Don't click here unless you want to be banned.

LSL Wiki : TutorialJukebox

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are crawl338.us.archive.org

Jukebox Tutorial

By FoolishFrost

You want a device that plays a song, and when it's done, repeats. It does not give the user the option to stop the music. First, I can say that in practice, they will always have the play and stop buttons on the bottom of the screen. <shrugs> We'll ignore this for the moment. Let's go with this design: A device that will play a song, and when done, play the next one in a list. To do this, we will need a timer, a command to play the song, and the data containing what songs to play and for how long.

The timer is needed because SL cannot tell when a song has ended. It will just stop playing the mp3 and not continue.

First, let's make the base code:

default
{
     state_entry()
     {
          //code will go here.
     }
}

Above, you can see the most basic code in a script. The object 'default' is the state that LSL will always run first, and must be the starting point of any program. This means that when a program runs, it finds DEFAULT and runs its code first.

Inside default is 'state_entry()'. This is an EVENT. An event is something triggered by the program when something happens. In this case, 'state_entry()' happens with the state is first run. Since default is the first state run, and it runs 'state_entry()' first since it has just started running it, this means any code placed in the brackets {} of 'state_entry()' will run first.

Got all that? Don't be afraid of asking questions.


Next up:

default
{
     state_entry()
     {
          llSetParcelMusicURL("http://musicsite.com/song.mp3");
     }
}

We just added our first command to our program. In this case, the llSetParcelMusicURL command. It will play the song or stream listed inside of it ("http://musicsite.com/song.mp3"). In this case, we are using a fake site: http://musicsite.com/song.mp3

This means when we run this, it loads and plays the song above when run. LSL thinks like this: RUN DEFAULT -> RUN STATE_ENTRY -> RUN llSetParcelMusicURL -> NOTHING ELSE TO DO: END

Not quite there yet. We need to add more to this.




Let's continue with adding some more code:

string song = "http://musicsite.com/song.mp3";

default
{
     state_entry()
     {
          llSetParcelMusicURL(song);
     }
}

As you can see, we just added a line OUTSIDE of the default. In this case, the line:

string song = "http://musicsite.com/song.mp3";

What this line does is sets the variable song to equal the text string http:musicsite.com/song.mp3. The word 'string' as the begining tells LSL that the variable 'song' is a type of variable that can contain text. the '=' sign tells it to put the text "http:musicsite.com/song.mp3" inside of the variable 'song'. (minuse the quotes, that just define the edges of the data)

So, when you get down to the line that says:

llSetParcelMusicURL(song);

You see the work 'song' again. Notice it's not in quotes. That means instead of looking for "song", it looks for what 'song' stands for: "http://musicsite.com/song.mp3"

Did that make sense?

Oh, this is a good time to point out that commands always end in a ';'. The semicolon tells LSL that the command line is ended, and to go to the next line. It's really nothing more than a seperator.

Still, all we did is change the way it works, but it's doing the exact same thing as before...


list songs = ["song1.mp3", "song2.mp3", "song3.mp3", "song4.mp3"];
string path = "http://musicsite.com/";

default
{
     state_entry()
     {
          integer currentsong = 0;
          llSetParcelMusicURL(path + llList2String(songs, currentsong));
     }
}

Wow. This got a lot more complex. Did it not?

list songs = ["song1.mp3", "song2.mp3", "song3.mp3", "song4.mp3"];

What this lines does it make a variable that can store more than one piece of data. a list variable can store a series of things. This data is accessed by using the llList2String command.

If you want to get data out of a list, you can pull it out by the llList2String command. As you can see in the program, it looks like:

llList2String(songs, currentsong)

Notice the first variable 'songs' is the list that llList2String is looking to get data from. it then looks at the variable currentsong to figure out what item in the list to get.

Now, look at line:

integer currentsong = 0;

This is another variable. It means that currentsong is an integer, or in general terms, a whole number. Integers can be added, multiplied, and worked like most numbers. and integer variable (2) added to another integer variable (2) equals the sum of the two variables (4) just like in normal math.

since integer currentsong = 0, then it's looking for item '0' (zero) in the list. Lists are numbered starting with 0. This means it just found the text data 'song1.mp3' from the list and used it.

Let's look at that music player line of code again:

llSetParcelMusicURL(path + llList2String(songs, currentsong));

Notice that the variable path is added to the first item in the list songs. Both of these are strings, so instead of adding them like you do in math, it combines them like words. This means that since path = "http://musicsite.com/" and llList2String(songs, currentsong) = "song1.mp3", that the combined string = "http://musicsite.com/song1.mp3"!

Are we still following? More soon to come.




list songs = ["song1.mp3", "song2.mp3", "song3.mp3", "song4.mp3"];
string path = "http://musicsite.com/";

default
{
     state_entry()
     {
          integer currentsong = 0;
          llSetTimerEvent(0.01);
     }

     timer()
     {
          llSetParcelMusicURL(path + llList2String(songs, currentsong));
          currentsong = currentsong + 1;
          llSetTimerEvent(30.0);
     }

}

Well. That changed alot!

You now notice that a new event is present, and that the event state_entry() was changed.

Notice that integer currentsong = 0; still sets the variable currentsong to be the number 0. The lext line, though, seems to be a new command!

llSetTimerEvent(0.01);

This line tells LSL to set a timer. This timer is set in seconds, so in this case, it's saying trigger a Timer event in about .01 seconds. <grins> This is a cheap way to make LSL do something more or less immediatly.

So when it reaches the above line, it sets the timer and ends the program. Then when the timer goes off, it started running the code in the timer() event!

timer()
     {
          llSetParcelMusicURL(path + llList2String(songs, currentsong));
          currentsong = currentsong + 1;
          llSetTimerEvent(30.0);
     }

Above you see the llSetParcelMusicURL again. We moved it from the state_entry() event to this one so that when the timer goes off, it plays a song. The next line:

currentsong = currentsong + 1;

tells LSL to make the variable currentsong equal itself plus one. This means that currentsong was increased by one.

Last, you see a new llSetTimerEvent command. This resets the timer to go off in 30 seconds (more or less, lag can make this take longer).

And then the program ends until the timer goes off again. When it does, it runs the llSetParcelMusicURL command again. This time though, currentsong has been increased, and so it equals 1 instead of 0. This means that it will use the 2nd item in the list! So it will play: "http://musicsite.com/song2.mp3"

Whew. Are you getting this? Cause it's taking FOREVER to type out.

Wait!

We just found our first BUG! A bug is a programming error that leads to problems in the program running properly.

In this case, the variable currentsong increases by one eachtime the timer goes off, but what happens when it get's to 4? There is no 5th item in the list!

Well, even if the program does not crash from the error, it's not going to give the data from the list we want. What we need to do is teach it to start over at zero again!

list songs = ["song1.mp3", "song2.mp3", "song3.mp3", "song4.mp3"];
string path = "http://musicsite.com/";

default
{
     state_entry()
     {
          integer currentsong = 0;
          llSetTimerEvent(0.01);
     }

     timer()
     {
          llSetParcelMusicURL(path + llList2String(songs, currentsong));
          currentsong = currentsong + 1;
          if (currentsong > 3)
          {
              currentsong = 0;
          }
          llSetTimerEvent(30.0);
     }

}

Now we just added a flow control. In this case, an IF decision!

if (currentsong > 3)
          {
              currentsong = 0;
          }

So, the line if (currentsong > 3) seems simple enough. IF currentsong is greater than 3, then the variable currentsong needs to equal zero again! Notice the commands to run if the IF decision is true are held in brackets again {}.

Now, if the program increases currentsong to higher than 3, it makes it equal 0 again and starts withthe first song in the list again!

Woot!

Any of this making sense yet?


Now, on with the show:

list songs = ["song1.mp3", "song2.mp3", "song3.mp3", "song4.mp3"];
list songlengths = [300,200,234,98];
string path = "http://musicsite.com/";

default
{
     state_entry()
     {
          integer currentsong = 0;
          llSetTimerEvent(0.01);
     }

     timer()
     {
          llSetParcelMusicURL(path + llList2String(songs, currentsong));
          llSetTimerEvent(llList2Float(songlengths, currentsong));
          currentsong = currentsong + 1;
          if (currentsong > 3)
          {
              currentsong = 0;
          }
     }

}

Well. In the above, we added a new list called 'songlengths' and changed the llSetTimerEvent in the 'timer()' event to read it.

Notice that the songlengths is a number, and in this case represents the number of seconds that the song will play. Each of the 4 entries of the list are placed to match the 4 songs in the list 'songs'.

The line:
llSetTimerEvent(llList2Float(songlengths, currentsong));

Sets the timer based on the list 'songlengths'. Note that the command to pull the date from the list is now llList2Float. This means it reads the data as a float variable. A float is like an integer, but can deal with numbers that are not whole. While an Integer might be 1, or 100, or 3920102; a float can be 1.4, or 45.1, or even 0.1

Since the llSetTimerEvent MUST use a float number, the llList2Float converts the data in the songlengths list to something it can understand and read. So when it places song 0, it will play the song 'song1.mp3' for about 300.0 seconds.

Are we all following still?


Tutorials
There are 3 comments on this page. [Display comments/form]