Jump to content

Stringing and Looping Sounds


Aylin Moonshadow
 Share

You are about to reply to a thread that has been inactive for 2926 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

My girlfriend lives by the water and made the comment that she would love to have ocean sounds in the background on her parcel.  So being that she is away for the week end and I needed something to keep me busy I decided to try and figure this out.  I watched Torley's video on making sounds and with some googling figured out how to put it in an object and make it play with a script on the parcel.  Torley is so cool.

Problem is that the 10 second loop starts to get annoying after a while.  No variety.  I have a recording of whales singing on the ocean I want to use but it is longer than 10 seconds.

So doing some more googling I figured out I would need to string the sounds and loop them.  And with some more googling I found this script.  http://forums-archive.secondlife.com/54/ee/194018/1.html  

It's hard finding scripts for what you need.  I couldn't find any where that they were organized.

What is killing me is I can't figure out in this script where I am supposed to put the name of sound files.  I will probably name them 'WhaleSounds01, WhaleSounds02, etc."

Can someone help me out with where the name of the sounds are supposed to go?

A million thanks and big hugsssss for whoever can help me!

------------------------------------------------------

//Psyke's Music Script v4.15.1 
//Song Player

//Modified by Psyke Phaeton... this script is free for anyone to use or copy.. please make it copyable from your "CD"
//Drop 9 second song clips in reverse order into the Inv of the obect, and touch to play.

//user variables

integer waves_to_preload = 3; //wave files to Preload ahead of the wav being played.
float preload_load_time = 0.5; // seconds pause between each preloaded wave file attempt b4 play comnences
//vector set_text_colour = <,0,0>; // colour of floating text label
float set_text_alpha = 1; //no idea what this is LOL
float timer_interval = 8.9; //time interval between requesting server to play the next 9 second wave
// times just below 9 seconds are suitable as we use sound queueing
integer timer_started;

// program variables
list invlist;
integer total_wave_files; //number of wave files
integer last_wave_file_number; //final wave sequence number (note: sequence starts at zero
//- so this is 1 less than total wave files)
integer i; //used by timer() player
integer c; //user by sound() bufferer
string preloading_wave_name; //the name of the wave file being preloaded
string playing_wave_name; //the name of the wave being played
integer play = FALSE; //toggle for music playing or stopped

sound()
{
total_wave_files = llGetInventoryNumber(INVENTORY_SOUND);
last_wave_file_number = total_wave_files - 1; // because wav files are numbered from zero
float length = total_wave_files*9.0;
//llSay(0, "preloading " + (string)tottrack + " wavs. Play length " + (string)llFloor(length) + "secs");
integer c = 0;
//do full preload of sound
llSetSoundQueueing(TRUE); //ONLY WORKS ON llPlaySound not llTriggerSound
/////////////////
//integer x = waves_to_preload;
//if ( total_wave_files < waves_to_preload ) { x = total_wave_files; }
////////////////
integer x = total_wave_files;
llSay(0, "(" + llGetScriptName() + ") " + llGetSubString((string)(x * preload_load_time),0,3) + " sec buffering..");
timer_started = FALSE;
for (c = 0 ; c <= (x - 1) ; c++) { //preload X wave files b4 we play
//since wavs are numbered from 0 we minus 1
preloading_wave_name = llGetInventoryName(INVENTORY_SOUND, c);
llSetText(llGetObjectName() +
"\n(preloading " + llGetSubString((string)((x - c)*preload_load_time),0,3) + " secs)"
//////debug lines
//+ "\n--debug--\n"
//+ "Preload wav: " + (string)c
//+ " name: " + preloading_wave_name
//+ "\nkey: " + (string)llGetInventoryKey(preloading_wave_name)
//////end debug line
+"\n.", <1,0,0>, set_text_alpha);

//Attempt to preload first x wave files to local machines cache!
llTriggerSound(preloading_wave_name, 0.0);
llPreloadSound(preloading_wave_name);
//llWhisper(0, (string)c + " of " + (string)tottrack);
//llWhisper(0,"Preloading wav: " + (string)c + (string)llGetInventoryKey(preloading_wave_name));

//start play sound timer in 'timer_interval' seconds when we are less than 'timer_interval' seconds from
// finishing preloading.
if ( ((total_wave_files - c) * preload_load_time) < timer_interval && !timer_started) {
//llWhisper(0, "Starting timer:" + (string)timer_interval);
llSetTimerEvent(timer_interval);
timer_started = TRUE;
}

llSleep(preload_load_time);
}
i=0; c=0;
llSay(0, "Done! Playing.. (" + (string)llFloor(length) + " secs) Click to stop.");
//llSetTimerEvent(8.85);
}

default
{
on_rez(integer start_param)
{
//set text above object to the name of the object
llSetText(llGetObjectName() + "\n.", <0,1,0>, set_text_alpha);
//llSetSoundQueueing(FALSE); //ONLY WORKS ON llPlaySound not llTriggerSound
}
touch_start(integer total_number)
{
if (!play) {
//llTargetOmega(<0,0,1>,PI,1.0) ;
sound();
} else {
//llTargetOmega(<0,0,1>,0,0);
llSetTimerEvent(0.0);
llStopSound();
//llSetSoundQueueing(FALSE); //ONLY WORKS ON llPlaySound not llTriggerSound
llSay(0, "Stopping..");
llSetText(llGetObjectName() + "\n.", <0,1,0>, set_text_alpha);
}

play = !play;
}

timer()
{
if( i > last_wave_file_number )
{
sound();//This is what I changed or added to keep it looping till you touch it again and stop it. All the rest below was either already omitted or omitted by me where ie: // play = FALSE; was done by me and //llSay(0, "finished."); was omitted by the original maker that was prolly used for testing and such.

//llSay(0, "finished.");
// play = FALSE;
//llSetSoundQueueing(FALSE); //ONLY WORKS ON llPlaySound not llTriggerSound
// llSetText(llGetObjectName() + "\n.", <0,1,0>, set_text_alpha);
//llResetScript();
// llSetTimerEvent(0);
}
else
{
playing_wave_name = llGetInventoryName(INVENTORY_SOUND, i);
//llWhisper(0, "llPlaySound wav: " + (string)i + " " + (string)llGetInventoryKey(playing_wave_name) );
llPlaySound(playing_wave_name, 1.0);
llSetText( llGetObjectName() +
"\n(playing " + (string)i +" of "+ (string)last_wave_file_number +")\n."
, <0,1,1>, set_text_alpha);

if(i + waves_to_preload <= last_wave_file_number)
{
preloading_wave_name = llGetInventoryName(INVENTORY_SOUND, i + waves_to_preload);
//llSetText( llGetObjectName() +
// "\n(playing " + (string)i +" of "+ (string)last_wave_file_number +")"
//////debug line
//+ "\n--debug--\n"
//+ "Playing key: " + (string)llGetInventoryKey(playing_wave_name) + "\n"
//+ "Preloading sequence: " + (string)(i + waves_to_preload)
//+ " name: " + preloading_wave_name
//+ "\nPreloading key: " + (string)llGetInventoryKey(preloading_wave_name)
//////end debig line
// , set_text_colour, set_text_alpha);

//llWhisper(0, "Preloading wav:" + (string)(i + waves_to_preload) + " " +
// (string)llGetInventoryKey(preloading_wave_name) +" last = " + (string)last_wave_file_number);
llTriggerSound(llGetInventoryName(INVENTORY_SOUND, i + waves_to_preload), 0.0);
llPreloadSound(llGetInventoryName(INVENTORY_SOUND, i + waves_to_preload));
}
}

i++; //increment for next wave file in sequence!
}
}



 

 

  • Like 1
Link to comment
Share on other sites

3 or 4 hundred inventory items (times 10s per wav file) is about the limit to which contents have been stretched, according to a discussion we had a while ago here.  Rolig Loon was having trouble with suffix numbers (eg; you Whale 0, Whale 1, ...) where the suffix was over 32 - so you may need WhaleA 31, WhaleB 0, etc. - but otherwise we have no definite limit on object contents.

The biggest problem you'll have is that there is no way for a script to know when a sound has finished playing and so to start the next one at just the right time.  For ambient/intermittent sounds such as a beach that shouldn't be a problem (fingers crossed)

Link to comment
Share on other sites

Preloading refers to giving advance notice of what the sound will be to the avatar who will be listening .  If the listening avatar hasn't already cached the sound file locally on his/her own machine, it has to be downloaded before the viewer can play it.  The delay that you're talking about occurs because it takes time to download the file.  So, preloading simply means sending the file to the avatar before it's needed.  You can do that by using llPreloadSound.  Since you'll be looping the sound indefinitely, though, it's easier to just let the listener deal with a delay the first time the file is called. That first time will load it into cache, where it will be for all subsequent calls.

Link to comment
Share on other sites

OK, I have built the silly thing.  And it works.  Kind of.

So two problems.

1.  It takes almost 9 seconds for it to preload each time it completes its cycle.  That's a bit long.  So I am wondering if the preload is even needed.  And how I would get rid of it.

2.  It randomly skips sound files each time it cycles.  Or doesn't play them.  It's not the same sound files it skips each time.  There will be a 9 second gap (the length of each file) this happens.  I see the text above the box saying it is playing the file but there is no sound coming out.

I used 17 nine second sound files.  I guess that is the one other odd thing.  It only ever says it sees 16.

The parts of it that are working are sounding really really good when they work.  It was a lot of fun making the files once I figured out how to convince Audacity that they needed to be 16 bit.  The break between sounds I don't think you'd notice if you didn't know it was there except when it skips a file.

Thanks,

Aylin

 

p.s.  right now I am feeling brain frazzled so it may be tomorrow before I come back to this.  but thank you for the help.  when it is finished I will give you all a copy if you'd like.

 

Link to comment
Share on other sites

It's a little hard to follow the logic of this script, but it looks as if it's running slowly because you added that user-defined function, sound().  You really shouldn't need that. I think you can do with removing that function and the top part of the timer event.....

        if( i > last_wave_file_number )        {            sound();//This is what I changed or added to keep it looping till you touch it again and stop it. All the rest below was either already omitted or omitted by me where ie: // play = FALSE; was done by me and //llSay(0, "finished."); was omitted by the original maker that was prolly used for testing and such. //llSay(0, "finished."); // play = FALSE; //llSetSoundQueueing(FALSE); //ONLY WORKS ON llPlaySound not llTriggerSound // llSetText(llGetObjectName() + "\n.", <0,1,0>, set_text_alpha); //llResetScript(); // llSetTimerEvent(0);        }         else         {

 so that the remainder looks like this ....

    timer()    {            playing_wave_name = llGetInventoryName(INVENTORY_SOUND, i%total_wave_files);            //llWhisper(0, "llPlaySound wav: " + (string)i + " " + (string)llGetInventoryKey(playing_wave_name) );            llPlaySound(playing_wave_name, 1.0);            llSetText(  llGetObjectName() +                         "\n(playing " + (string)(i%total_wave_files) +" of "+ (string)last_wave_file_number +")\n."                            , <0,1,1>, set_text_alpha);                    if(i + waves_to_preload <= last_wave_file_number)              {                preloading_wave_name = llGetInventoryName(INVENTORY_SOUND, i + waves_to_preload);                 llTriggerSound(llGetInventoryName(INVENTORY_SOUND, i + waves_to_preload), 0.0);                 llPreloadSound(llGetInventoryName(INVENTORY_SOUND, i + waves_to_preload));            }        }    i++;     //increment for next wave file in sequence!    }

 

 The function i%total_wave_files  just means "divide i by total_wave_files and use whatever is the remainder".  So if total_wave_files = 17 and i = 23, for example, i%total_wave_files = 6.

The if test will only make a difference the first time around the cycle, since i will increment past it by the second time around.

Link to comment
Share on other sites

I think I understood what you were saying to do.  But I wound up getting an error code,

(2,25)  :  Error   :   Name not defined within scope.

This is what I changed the code to based on what you said, but it is very possible I have not understood something here:

 

sound()
{
        total_wave_files = llGetInventoryNumber(INVENTORY_SOUND);
        last_wave_file_number = total_wave_files - 1;  // because wav files are numbered from zero
        float length = total_wave_files*9.0;
        //llSay(0, "preloading " + (string)tottrack + " wavs. Play length " + (string)llFloor(length) + "secs");
        integer c = 0;
        //do full preload of sound
        llSetSoundQueueing(TRUE); //ONLY WORKS ON llPlaySound not llTriggerSound
        /////////////////
        //integer x = waves_to_preload;
        //if ( total_wave_files < waves_to_preload ) { x = total_wave_files; }
        ////////////////
        integer x = total_wave_files;
        llSay(0, "(" + llGetScriptName() + ") " + llGetSubString((string)(x * preload_load_time),0,3) + " sec buffering..");
        timer_started = FALSE;
        for (c = 0 ; c <= (x - 1) ; c++) { //preload X wave files b4 we play
                                                          //since wavs are numbered from 0 we minus 1
            preloading_wave_name = llGetInventoryName(INVENTORY_SOUND, c);
            llSetText(llGetObjectName() +
                    "\n(preloading " + llGetSubString((string)((x - c)*preload_load_time),0,3) + " secs)"
                    //////debug lines
                    //+ "\n--debug--\n"
                    //+ "Preload wav: " + (string)c
                    //+ " name: " + preloading_wave_name
                    //+ "\nkey: " + (string)llGetInventoryKey(preloading_wave_name)
                    //////end debug line
                        +"\n.", <1,0,0>, set_text_alpha);

            //Attempt to preload first x wave files to local machines cache!                                             
            llTriggerSound(preloading_wave_name, 0.0);
            llPreloadSound(preloading_wave_name);
            //llWhisper(0, (string)c + " of " + (string)tottrack);
            //llWhisper(0,"Preloading wav: " + (string)c + (string)llGetInventoryKey(preloading_wave_name));
           
            //start play sound timer in 'timer_interval' seconds when we are less than 'timer_interval' seconds from
            // finishing preloading.
            if ( ((total_wave_files - c) * preload_load_time) < timer_interval && !timer_started) {
                //llWhisper(0, "Starting timer:" + (string)timer_interval);
                llSetTimerEvent(timer_interval);
                timer_started = TRUE;
            }
           
            llSleep(preload_load_time);
        }
        i=0; c=0;
        llSay(0, "Done! Playing..  (" + (string)llFloor(length) + " secs) Click to stop.");
        //llSetTimerEvent(8.85);
}

default
{
    on_rez(integer start_param)
    {
        //set text above object to the name of the object
        llSetText(llGetObjectName() + "\n.", <0,1,0>, set_text_alpha);
        //llSetSoundQueueing(FALSE); //ONLY WORKS ON llPlaySound not llTriggerSound
    }
    touch_start(integer total_number)
    {
        if (!play) {
            //llTargetOmega(<0,0,1>,PI,1.0) ;
            sound();
        } else {
            //llTargetOmega(<0,0,1>,0,0);
            llSetTimerEvent(0.0);
            llStopSound();
            //llSetSoundQueueing(FALSE); //ONLY WORKS ON llPlaySound not llTriggerSound
            llSay(0, "Stopping..");
            llSetText(llGetObjectName() + "\n.", <0,1,0>, set_text_alpha);
        }
       
        play = !play;
    }
   
    timer()
    {
       
        {
            playing_wave_name = llGetInventoryName(INVENTORY_SOUND, i%total_wave_files);
            //llWhisper(0, "llPlaySound wav: " + (string)i + " " + (string)llGetInventoryKey(playing_wave_name) );
            llPlaySound(playing_wave_name, 1.0);
            llSetText(  llGetObjectName() +
                        "\n(playing " + (string)i +" of "+ (string)last_wave_file_number +")\n."
                            , <0,1,1>, set_text_alpha);
       
            if(i + waves_to_preload <= last_wave_file_number) 
            {
                preloading_wave_name = llGetInventoryName(INVENTORY_SOUND, i + waves_to_preload);
                //llSetText(   llGetObjectName() +
                //             "\n(playing " + (string)(i%total_wave_files) +" of "+ (string)last_wave_file_number +")"
                             //////debug line
                             //+ "\n--debug--\n"
                             //+ "Playing key: " + (string)llGetInventoryKey(playing_wave_name) + "\n"
                             //+ "Preloading sequence: " + (string)(i + waves_to_preload)
                             //+ " name: " + preloading_wave_name
                             //+ "\nPreloading key: " + (string)llGetInventoryKey(preloading_wave_name)
                             //////end debig line
                //                , set_text_colour, set_text_alpha);
               
                //llWhisper(0, "Preloading wav:" + (string)(i + waves_to_preload) + " " +
                //                (string)llGetInventoryKey(preloading_wave_name) +" last = " + (string)last_wave_file_number);
                 llTriggerSound(llGetInventoryName(INVENTORY_SOUND, i + waves_to_preload), 0.0);
                 llPreloadSound(llGetInventoryName(INVENTORY_SOUND, i + waves_to_preload));
            }
        }

    i++;     //increment for next wave file in sequence!
    }
}

Link to comment
Share on other sites

Noooo.... I was agreeing with you that you can get rid of almost all of the sound preloading, because most of the time the same person will be hearing all of your sound files in a continuous loop. You really only need to preload files the first time that person hears them.  So, I suggested getting rid of the sound() function entirely, leaving only a vestige of the preloading business at the tail end of the timer event. 

Here's a version of what I was suggesting. I have removed all of the global functions that were only used in the sound() function and have tightened up the remaining script a lot too.  There's more that could be done.  For example, I left the annoying hover text in (although I commented it out here), but I doubt that you really want it in your object.  What's left is short, simple, and ought to do the job.

//user variablesfloat timer_interval = 8.9;        //time interval between requesting server to play the next 9 second wave // times just below 9 seconds are suitable as we use sound queueing // program variablesinteger total_wave_files;      //number of wave filesinteger i; //used by timer() playerinteger play = FALSE;        //toggle for music playing or stoppeddefault{    state_entry()    {        total_wave_files = llGetInventoryNumber(INVENTORY_SOUND);  //Count the number of sound files in inventory        llSetSoundQueueing(TRUE);  //Allow sound to finish before playing the next one    }    on_rez(integer start_param)    { // llSetText(llGetObjectName() + "\n.", <0,1,0>, 1.0);    }    touch_start(integer total_number)    {        if (!play)        {            llSay(0,"Starting ... ");            i=0;  //Start the cycle counter            llSetTimerEvent(1.0); // Set the timer for a short interval to jump start sounds        }        else        {            llSetTimerEvent(0.0);            llStopSound();            llSay(0, "Stopping.."); // llSetText(llGetObjectName() + "\n.", <0,1,0>, 1.0);        }        play = !play;  //Toggle switch    }    timer()    {        llSetTimerEvent(timer_interval); //Set timer to its "playing" interval        llPlaySound(llGetInventoryName(INVENTORY_SOUND, i%total_wave_files), 1.0);  //Play the current sound file 
// llSetText( llGetObjectName() + "\n(playing " + (string)((i+1)%total_wave_files) +" of "+ (string)(total_wave_files) +")\n." , <0,1,1>, 1.0);
i++; //increment for next wave file in sequence! if(i < total_wave_files) //Only preload sound files the first time around the cycle { llPreloadSound(llGetInventoryName(INVENTORY_SOUND, i)); //Preload the next sound file } }}

 The first time you play it, your very first sound file will not have been preloaded, so there will be a delay. After that, it should be fine.  If your sound files are much shorter than 9 seconds, there will be a gap between them.  The way to beat that, if you can, is to make all your sound files as close to the same length as possible and then set the timer_interval varible to a hair shorter than that common length.

  • Like 2
Link to comment
Share on other sites

That is working better.  Kind of.  But I am still getting "skipped" sounds.

I did a stripped down version with fewer sound files trying to find a pattern.

I have several sounds I am using.  So what is happenning as best as I can make sense of it is this.  I have a sound, Ocean waves that I wanted to repeat 3 times.  So I named the files Ocean waves 01, 02, & 03.  The I have a file, Seagulls, I wanted to play twice, so I named them Seagulls 01, 02.   The same with two other files. 

So I hear Ocean Waves 01.  Then a 9 second gap.  Seagulls 01 plays.  Then a gap where Seagulls 02 should be.  On to my next two sounds and then a gap where the next Ocean Waves file should be. 

All my sound files are between 8.6 and 8.9 seconds.  I tried taking the timer down in 1/10 increments starting at 8.5 down to 7 but this doesn't  seem to change anything.

Seems very strange

 

Again, a million thanks!

 

EDIT TO ADD:

I just made one more.  I have 4 sound files I am using.  I just loaded the 4 with no repetitions and it plays without skipping a beat.  So it appears the problem I am having is when I am trying to get a sound to repeat.

Link to comment
Share on other sites

Well, for one thing, the sound files will be playing in alphabetical order, because that's the way they are always stored in inventory.  If you want to change that order, you'll need to rename some of the files.  

The gaps, though, don't have anything to do with the script, as far as I can see.  After all, you can play your four files in a continuous loop, as I can when I test it with four sound files here.  It sounds like the dead sound files themselves -- the numbered copies that you made --- are borked, but I'm momentarily at a loss to explain how.

Link to comment
Share on other sites


Void Singer wrote:

there was something screwy going on with renaming items while they were stil in contents a while back in the vaniLLa viewer... I found it was much more stable to rename them in inventory and then drop them into object contents.

I notice you said "Viewer."  I am renaming them in inventory then dropping them into contents.  I am however doing all my work using Firestorm.  Am going to give this a try in Phoenix.

Link to comment
Share on other sites


Rolig Loon wrote:

BTW, I assume you know that you don't need to rename the files at all......  If you drop two or more copies of the same item in an object's inventory, the extra ones will automatically have a number appended to them (
sound, sound1, sound2, sound3...
.). 

giggle snort

i didn't realize this.  but still need to do the manual renaming because i have three sounds files with different bird calls I am trying to disperse  between the sounds of just the waves.

Link to comment
Share on other sites

  • 7 months later...

That's easy.  Instead of continuing around the timer cycle indefinitely with i%total_wave_files, just stop the timer when i = total_wave_files.  The problem of not hearing the first sound file would remain, of course, because it won't be preloaded until you are supposed to hear it for the second time --- which will never happen if you stop the timer when i = total_wave_files.  The way to beat that is to preload it as soon as the user clicks to trigger the touch_start event and then play it as the first selection when the timer event executes.  You'll probably have to do something to distract the user for those few seconds (or disable the touch_start event) to keep him from a bout of frustrated and counterproductive clicking. ("Loading .........")  :smileytongue:

  • Haha 1
Link to comment
Share on other sites

  • 1 year later...

You mentioned a way of getting rid of the hovertext in this script. While I've had success with it in general in terms of sound, I cannot for the life of me get rid of the hovertext and it's driving me batty! I know this is an old thread but IF you see this, could you possibly post an ammendment to your cleaner version of the script with hovertext removed please?

*fingers crossed*

 

Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 2926 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...