Needed: simple audio streaming script
|
Robby Mission
Registered User
Join date: 31 Jan 2006
Posts: 6
|
06-16-2006 22:41
Hi this is my first time posting in forums, so i hope i dont brek to many rules or anything.
Im writing today because i need a very basic script that will set a stream or mp3.
this is the project... im making a simple kiosk to promote a RL band for a freind the kiosk will have buttons for the tracks (mp3's) when you press one track it will set that URL it would be nice if i could display the track playing but not necessary.
Currently the one i have has a stop feature too but i dont want them to stop. I have found other scripts but they give you menus, but i dont want a menu.
I cannot script but i understand enough to make adjustments.
so nothing fancy just a simple touch object, set url.
I really didnt want to impose on anyone but i have tried over and over on my own and im just not getting it myself.
Thanks in advance Robby Mission
|
Foolish Frost
Grand Technomancer
Join date: 7 Mar 2005
Posts: 1,433
|
06-17-2006 06:12
Ok. You're not too clear on what you want, let me try and confirm what you are requesting: 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 LSL program. 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 it's 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.mp3This 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.
|
Foolish Frost
Grand Technomancer
Join date: 7 Mar 2005
Posts: 1,433
|
06-17-2006 06:37
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.mp3Did 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.
|
Rigmyster Slichter
Registered User
Join date: 9 Jun 2006
Posts: 3
|
06-17-2006 06:46
yep, got all that so far. cheers
|
Foolish Frost
Grand Technomancer
Join date: 7 Mar 2005
Posts: 1,433
|
06-17-2006 06:59
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 to 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! 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?
|
Foolish Frost
Grand Technomancer
Join date: 7 Mar 2005
Posts: 1,433
|
06-17-2006 07:00
Hullo there! See your still about. What else are you wanting this to do?
|
Haravikk Mistral
Registered User
Join date: 8 Oct 2005
Posts: 2,482
|
06-17-2006 07:20
Won't you need to store the times in the list as well? I realise you're trying to do it from basics, but not all the songs are likely to be the same length I think?
_____________________
Computer (Mac Pro): 2 x Quad Core 3.2ghz Xeon 10gb DDR2 800mhz FB-DIMMS 4 x 750gb, 32mb cache hard-drives (RAID-0/striped) NVidia GeForce 8800GT (512mb)
|
Foolish Frost
Grand Technomancer
Join date: 7 Mar 2005
Posts: 1,433
|
06-17-2006 08:45
Good point. I'm also adding this to the LSLWiki at: http://secondlife.com/badgeo/wakka.php?wakka=StepNow, 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 line 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?
|
Foolish Frost
Grand Technomancer
Join date: 7 Mar 2005
Posts: 1,433
|
06-17-2006 08:51
All right. Question time now! I want to see some good questions before I move on. LSL gurus will NOT count, as this is supposed to be a beginner class. Er. Should I copy this to a new thread? 
|
Trurl Hicks
Registered User
Join date: 19 Apr 2006
Posts: 6
|
06-17-2006 09:48
I just noticed this thread, and as an SL newbie with programming experience - I really appreciate the work you've put into this - thank you. A couple of questions:
No way to upload and store MP3s in the game database, right? (Probably not desirable anyway)
Your code snippets are labeled as PHP but that's an artifact of the forum software, right?
I'm going to tinker around with this (saved it to my growing library of snippets) - and I was wondering if you might point me to a thread or wiki page to learn about streaming?
|
Foolish Frost
Grand Technomancer
Join date: 7 Mar 2005
Posts: 1,433
|
06-17-2006 09:58
I just noticed this thread, and as an SL newbie with programming experience - I really appreciate the work you've put into this - thank you. A couple of questions:
Thanks. Go ahead.
No way to upload and store MP3s in the game database, right? (Probably not desirable anyway)
No GOOD method. You CAN upload wav files in 9.9 second long clips and use the sound player commands to make them play in series. It's twitchy and fault filled, but works after a fashion.
Otherwise, you have to have a website you can store MP3s and then stream them to listeners. It's a good idea to make them a lot lower than 128 bit MP3s, something more like 32 bit.
Your code snippets are labeled as PHP but that's an artifact of the forum software, right?
Ayup.
I'm going to tinker around with this (saved it to my growing library of snippets) - and I was wondering if you might point me to a thread or wiki page to learn about streaming?
Learn about what part of streaming? I spent a month researching all of the aspects needed to understand the basics of streaming audio/video over the web. It's not exactly all in one place. Shoutcast.com was a starter place, but it's a sure bet you will need to use Google and hunt about.
|
Foolish Frost
Grand Technomancer
Join date: 7 Mar 2005
Posts: 1,433
|
06-17-2006 10:36
<tsk> C'mon guys. Unless I get some beginner questions about scripting, I'm going to wander off and do something more interesting. Like watch paint dry! And yes, bordom DOES make me impatient. The petulance just comes naturally whne I'm stuck at work and hungry.
|
Trurl Hicks
Registered User
Join date: 19 Apr 2006
Posts: 6
|
06-17-2006 11:06
Interesting! My comments interleaved below. No GOOD method. You CAN upload wav files in 9.9 second long clips and use the sound player commands to make them play in series. It's twitchy and fault filled, but works after a fashion.Possibly not designed for playing music then - but intended perhaps for sound effects like doors and machines or pre-recorded messages? I just did some quick reading and see the crafty work-arounds to use it to play SL-served music, but... Otherwise, you have to have a website you can store MP3s and then stream them to listeners. It's a good idea to make them a lot lower than 128 bit MP3s, something more like 32 bit....I do have access to a web server, so your script looks more logical for playing MP3s, for me at least. Learn about what part of streaming?Yes, that was pretty vague. I meant "broadcasting" an existing stream onto my parcel from a source like perhaps Shoutcast or KCRW.COM ( http://www.kcrw.com/pls/kcrwmusic.pls). Not setting up my own streaming media server. So a more precise question would be around the mechanism, perhaps llSetParcelMusicURL, for handing a stream URL to a client. This would presuppose the availabilty of the necessary player / codec / etc. on each client, right? Also, I think I understand that there's a way to have a stream associated with a parcel, but is there a way to play a stream to an individual on a parcel, like a radio or ipod? More than one stream to a parcel at a time? Thanks much!
|
Foolish Frost
Grand Technomancer
Join date: 7 Mar 2005
Posts: 1,433
|
06-17-2006 11:36
No GOOD method. You CAN upload wav files in 9.9 second long clips and use the sound player commands to make them play in series. It's twitchy and fault filled, but works after a fashion.Possibly not designed for playing music then - but intended perhaps for sound effects like doors and machines or pre-recorded messages? I just did some quick reading and see the crafty work-arounds to use it to play SL-served music, but...Well, you can find the sound commands in the Wiki: http://secondlife.com/badgeo/wakka.php?wakka=soundYes, the sound commands work VERY well for sound effects. Otherwise, you have to have a website you can store MP3s and then stream them to listeners. It's a good idea to make them a lot lower than 128 bit MP3s, something more like 32 bit....I do have access to a web server, so your script looks more logical for playing MP3s, for me at least.Then you just have to upload the files to the server and use the above script, or an enhanced version, to play back the songs. It will need the timer set for each song, since it cannot tell when a song ends. Learn about what part of streaming?Yes, that was pretty vague. I meant "broadcasting" an existing stream onto my parcel from a source like perhaps Shoutcast or KCRW.COM (http://www.kcrw.com/pls/kcrwmusic.pls). Not setting up my own streaming media server.Well, Shoutcast is a software, not a server. While you could serve to SL from your home computer using Shoutcast, you would only be able to feed a few people befeor your bandwidth choaked and it failed. Also, you can't use Playlists in the land Music stream. It either has to be a 'real' stream, or an individual MP3. So a more precise question would be around the mechanism, perhaps llSetParcelMusicURL, for handing a stream URL to a client. This would presuppose the availabilty of the necessary player / codec / etc. on each client, right?SL has the MP3 and OGG codecs built-in. Also, I think I understand that there's a way to have a stream associated with a parcel, but is there a way to play a stream to an individual on a parcel, like a radio or ipod? More than one stream to a parcel at a time?Per land section. And the scripted device MUST belong to the user/group of the land. In addition, while a stream does play for everyone at near the same time (give or take seconds for net lag), MP3 files are NOT synched aside from the fact that all of the residents of a land section have the play and music info sent at the same time. if they hit stop and start again, it starts over from the beginning in that case. Thanks much!You're welcome!
|
Trurl Hicks
Registered User
Join date: 19 Apr 2006
Posts: 6
|
06-17-2006 13:29
So here's what I've got so far: - see the wiki for LSL sound commands http://secondlife.com/badgeo/wakka.php?wakka=sound (wikis are great!)
- a streaming server is necessary, if I don't want to work from a list of MP3s
- a stream is required, not a playlist. My example was flawed because it pointed to a .PLS playlist file. Instead I could use the stream it referenced, in this case http://64.236.34.97:80/stream/1045. This is working great BTW, since I can just paste that into About Land -> Media -> Music URL because...
- the SL client is a streaming media player (you mentioned OGG and MP3 support). So apparently the player is exposed to LSL... neat.
That gives me enough to listen to tunes while I'm building or visiting with friends, and to explore and try out what we've discussed. THANKS
|
Foolish Frost
Grand Technomancer
Join date: 7 Mar 2005
Posts: 1,433
|
06-17-2006 15:28
And now you know, because knowing is half the battl... OW! NO! HALP! GETITOFFMEILLBEGOODOHGODDONTBENDTHATARRRRRRRGGGGG!
<Foolish is currently being punished for pop culture referance abuse - The Management>
|
Robby Mission
Registered User
Join date: 31 Jan 2006
Posts: 6
|
Ok guys, sorry i didnt respond...
06-17-2006 15:48
I checked this many times yesterday, then gave up and went to bed. lol today im like... WOW! So sorry everyone was just sleeping.
I will try to be a little more clear about this because no one actaully got it and I totally appreciate the lesson. I fear it will take me longer to get ahold of scripting i was never good at programming. I will give it a try because i want to learn ..i dont like feeling left because i cant scipt.
Ok let me explain the idea again and like i said before.... so so easy.
Im making a music Kiosk, the buttons for each track will all be individual prims. Inside each prim will be a simple "Touch" script that will set one single mp3 URL.
This will activate the music track but allow the person to press another tangable button to choose another track to hear whatever song they like and that will activate and set another mp3 URL by touching another button.
There will MP3's stored online for this to happen.
If anyone would like to actaully see an example of this i even have a "incorrectly"working model goin already. the current script in it will stop the tracks because it has a stop feature in it.
|
Robby Mission
Registered User
Join date: 31 Jan 2006
Posts: 6
|
I did it!!! And thank everyone that contributed....
06-18-2006 01:03
This is what i wanted and i did it myself with your help ty mr Frost  YAY default { touch_start(integer total_number) { llSetParcelMusicURL("URL HERE"  ; llWhisper(0, "TRACK NAME HERE"  ; } } i know its simple but it does exactly what i want!!!! ty ty ty ~Robby Mission~
|
Ron Overdrive
Registered User
Join date: 10 Jul 2005
Posts: 1,002
|
06-19-2006 22:20
Congrats you found what you were looking for Robby. I'd make one recomendation though, since you're trying to promote a band in SL it might be a good idea to use OggVorbis files (*.ogg) instead of MP3's since they'll have higher quality at low bitrates and you'd want to make the band sound the best they can get over streaming audio.
|