Pages

Monday, January 14, 2013

Sounding Off

Hi there, Gentle Readers, it's Basics time again!  I've gotten a number of questions relating to sounds in the past few days, so, er...listen up!

Sound plays a big part in the immersive experience of virtual worlds like Second Life.  There are a number of sound "channels" used by the viewer, and you can see the volume sliders for them either by pulling them down from the little speaker icon in the upper right corner of your screen, or by looking in the Sound and Media tab of the Preferences window.


"Master Volume" is pretty self explanatory, so let's move down the list:
  • Buttons.  Some viewers call this User Interface or UI.  It's the beeps and dings that happen when you receive an IM, or click a button or other control.
  • Ambient.  Environmental sounds such as wind, the teleport whoosh, and the thonk when you bump your shins against steps that are a little too high.
  • Sound Effects.  Sounds played by in world objects.  Heel click sounds, gunshots, bird calls, wave noises, and so on.  We'll come back to this one in a minute.
  • Streaming Music.  Each land parcel can have its own music stream.  In a club, the DJ will set things up so the parcel music stream links to his Shoutcast stream.  In your home, your SL "radio" can be used to tune in any music stream on the internet.
  • Media.  Similar to the parcel music stream, but this is video content and its related sound track.
  • Voice Chat.  Plays local voice chat and voice IM calls.
The check boxes below the sliders are mostly self-explanatory, but I will mention two in particular.
  •  Allow Media to auto-play.  If checked, your viewer will automatically start playing the parcel's music and media streams when you arrive.  I recommend NOT checking this, for security reasons.  Any stream provider (all over the internet, not just in SL) gets your IP address -- they have to have it, or they could not send you the stream.  People who have your IP address have a significant clue as to your physical location.  With most streams, this is not a concern.  But it's possible for a stream provider to use this as a way to obtain people's IP addresses with Evil Intent.  So only play media streams from sources you choose.
  • Toggle speak on/off when I press <Designated Key>.  If this is checked, when you hit the specified key, your microphone is turned on...and it will stay live until you press the key again.  It is very easy to forget, and then everyone around you hears you muttering to yourself, scarfing down chips, and yelling at your kids.  If left unchecked, the mic will only be live as long as you hold the key down.
In contrast with the younger set, I tend to leave my music and media off, unless I am at a club.  I prefer to listen to the "natural" sounds of the world around me...the ones in the Sound Effects channel.  You can buy all sorts of sound effects for your Second Life home and garden...or space station, submarine, or other build.  Try browsing the Marketplace, and select Audio and Video/Nature and Relaxation Audio.  You'll find all sorts of sounds for sale, at prices ranging from free up to around $L500 or so.  You can also find a lot of free sounds in freebie stores like Yadni's Junkyard.

Or you can upload your own sounds.  There are some pretty strict limitations to be followed:
  • A sound clip cannot exceed 10 seconds in length
  • Sounds must be in .WAV format, 44.1 KHz mono or stereo.
This means you are not going to be uploading any symphonies, or even your favorite rock tunes.  There are plenty of basic audio editing programs out there that can resample sound files, manipulate them, and save the results in the required format.

There is a $L 10 upload charge, just as there is for textures.  I recommend uploading a test version onto the Beta Grid first...because you will want to test how it plays.  The most common problem is too low a volume.  The maximum volume you can play a sound at is 1.0, there is no "11" in Second Life.  You can always turn a sound down, but you can't increase the volume if it is too low to start with.  Your sound file should have its peaks near (but not over) 0 dB, and you may want to apply a Normalization filter in your audio editing program.

Uploaded sounds can be used in Gestures (HOOOOO!)  Yeck.  Or my favorite, a little girl whining "Mommy?  Mommy, Second Life won't work today!"  Or they can be used for environmental enhancement.

To do this, you need to put your sound(s) into the Content tab of a prim, along with a script to play the sound.  Here is one very simple sound-playing script:

//Play Sound in a Loop at Full Volume
default
{
    state_entry()
    {
//Either re-name your sound INVENTORY_SOUND, or change this text to your sound's name

//The last number, 1.0 is the volume.  Reduce volume by changing this to a value less than 1.0

        llLoopSound(llGetInventoryName(INVENTORY_SOUND,0), 1.0);
    }
}


Here is another one, with options to play different sounds during the day or night, and to randomly select from multiple sound files.  It's menu-controlled and has plenty of other options, too:

// ----------------------------------------------------------------
// Script Title:    Advanced Sound Player
// Created by:      WhiteStar Magic
// Creation Date:   December 21,2009
// Platforms:
//    OpenSim:      Tested & operational on OpenSim 0.6.9 (Dev) ba75526 - r11721
//
// Revision:        0.5
// Revision History:
//     Evolved from basic DayTime Sound Module
//     Now does Day, Night or Always sound emination with Volume control and more,
//     All Menu driven and no need to edit script for functionality.
//
// Revision Contributors:
//
//
//
// Conditions:
// Please maintain this header. If you modify the script indicate your
// revision details / enhancements
//
// Support:
//  If & When needed as Time Permits
//
// Licensing:  Creative Commons 2.5 (Canada)
//
// http://creativecommons.org/licenses/by/2.5/ca/
// http://creativecommons.org/licenses/by/2.5/ca/legalcode.en
// ================================================================
// ** SCRIPT NOTES **
//  This is  Multi Mode Sound player Script.  It can play sounds during Daytime, Nightime or Always
//  Plays sounds for a Random Period of Time between 15 to 150 seconds in duration
//  Menu Driven with Settings Status Information
//  Menu can only be accessed by Owner
//  Debugging can be enabled from Menu for extended details of operation
//  No Lag Chat & Listen functions (uses Good Housekeeping)
//  No llSleep Functions, uses active timer
//
//  ** INSTALLATION **
//    Install script into a prim of your choice
//    Copy in your desired sound files into same prim
//    Touch for OWNER Menu to activate Options
//
//  ** MENU OPTIONS **
//    ON    = Player ON
//    OFF   = Player OFF
//    Day   = Play Sounds Daytime Only
//    Night = Play Sounds Nightime Only
//    Allways = Play Sounds always
//    Looping = Loops Sound during Cycle
//    Single  = Plays Sound once in Cycle
//    Volume = Volume/Loudness menu
//      Volume menu gives option of 1/4, 1/3, 1/2, 2/3, 3/4, full
//    RESET = RESET SCRIPT
//    DEBUG = On/Off (Chats info to Owner)
//    DONE  = EXIT MENU SYSTEM
//
//================================================================
// === Variables Used ===
//==================================
// === DO NOT CHANGE LINES BELOW ===
//==================================
//
// Smaller Numbers will generally result in faster switching / cycling of sounds.
float SHORTEST = 11.0;  // allows for Short Sound Burst cycle = More Randomness
float LONGEST = 165.0;  // 2.5 minutes Max Sound Play value with correction for llFrand
//
string  WhenToPlay = "Day"; // Day, Night, Allways
integer On_Off = TRUE;      // Device Active On or Off
integer MENU_ACTIVE = FALSE;
string  MENU;
string  MyVol = "1/2";
integer sounds;
float   timing = 10.0;
integer CHANNEL;            // Channel for Menus
key     OWNER;              // Owners UUID
integer DEBUG = FALSE;      // Debugging Switch
string  MyStatus;
float   LOUDNESS = 0.5;     // 0.0 = silent - 1.0 = Loudest
integer LOOPING = FALSE;
// =====================================
// === Day Night Check ===
DayOrNight()
{
    vector sun_point = llGetSunDirection();
    if ( sun_point.z <= 0.0 ) // It's Night Time as SUN is Below Horizon
    {
        if(DEBUG) llOwnerSay("Currently Night time");
        if(WhenToPlay == "Night")
        {
            SoundPlay();
        }
        else
        {
            llSetTimerEvent(300); // 5 minutes till next cycle & test
            return;
        }
    }
    else    // It's daytime as SUN is above Horizon
    {
        if(DEBUG) llOwnerSay("Currently Day time");
        if(WhenToPlay == "Day")
        {
            SoundPlay();
        }
        else
        {
            llSetTimerEvent(300); // 5 minutes till next cycle & test
            return;
        }
    }
}
// === PLAY SOUND ROUTINE ===
SoundPlay()
{
    // Sets timer event to trigger again @ random time causing sound cycle
    // Little bit of tom foolery here to compensate & rationalize values
    timing = (float)(llFloor(llFrand(LONGEST - SHORTEST)) + 1);
    if(timing < 10.0) timing = SHORTEST;
    llSetTimerEvent(timing);
    //       
    if ( sounds <= 0 ) llOwnerSay("There are no sounds in me, Please put sounds files into this prim for me to play");
    integer randomnum = llFloor(llFrand(sounds));
    string soundname = llGetInventoryName( INVENTORY_SOUND, randomnum );
    if ( soundname != "" )
    {
        if(DEBUG) llOwnerSay("Sounds Found = ["+(string)sounds+"]: File Being Played = [# "+(string)randomnum+" / "+soundname+"] for ["+llGetSubString((string)timing, 0,llSubStringIndex((string)timing,".")+1)+"] Seconds");
        if(LOOPING) llLoopSound( soundname, LOUDNESS );
        else llPlaySound( soundname, LOUDNESS );
    }
}
//
// ==============
// MENU FUNCTIONS
// ==============
// Simple Button Ordering for regular functions (MAX 12 BUTTONS)
//===== Order the Buttons into right sequence for DIALOG MENUS
list ButtonSort(list btns)
{
    return llList2List(btns, -3, -1) + llList2List(btns, -6, -4)
        + llList2List(btns, -9, -7) + llList2List(btns, -12, -10);
}
//
OPEN_Comms()  // Open Communications Channel only when needed
{
    llListenRemove(CHANNEL);                         // SAFETY Kill CHANNEL incase left over
    CHANNEL = (integer)(llFrand(-1000.0) - 1000.0);  // RANDOM Negative Channel
    llListen(CHANNEL, "", "", "");                   // listen for dialog answers
    llSetTimerEvent(45.0);                           // to AutoKill CHANNEL
}
//
MENU_Admin(key id)  // Admin Menu
{
    OPEN_Comms();
    MENU_ACTIVE = TRUE;
    MENU = "ADM";
    llSetTimerEvent(60.0);
    //
    status();
    //
    string MA_data;
    list MA_list;
    //
    MA_data = "-=[Multi-Mode Sound Player]=-"+MyStatus+
        "\n=== Select the Option ===\n"+
        "ON    = Player ON\n"+
        "OFF   = Player OFF\n"+
        "Day   = Play Sounds Daytime Only\n"+
        "Night = Play Sounds Nightime Only\n"+
        "Allways = Play Sounds always\n"+
        "Looping = Loops Sound during Cycle\n"+
        "Single  = Plays Sound once in Cycle\n"+
        "Volume  = Volume/Loudness Menu\n"+
        "RESET = RESET SCRIPT\n"+
        "DEBUG =  On/Off (Chats info to Owner)\n"+
        "DONE  = EXIT MENU SYSTEM";
    MA_list = ["ON","OFF","Day","Night","Allways","Looping","Single","Volume","RESET","DEBUG","DONE"];
    llDialog(id, MA_data, ButtonSort(MA_list), CHANNEL);
}
//
MENU_Volume(key id)  // Volume Menu
{
    OPEN_Comms();
    MENU_ACTIVE = TRUE;
    MENU = "VOL";
    llSetTimerEvent(60.0);
    //
    status();
    //
    string MA_data;
    list MA_list;
    //
    MA_data = "-=[Multi-Mode Sound Player]=-"+MyStatus+
        "\n=== Volume/Loudness ===\n"+
        "Select your Volumen Prefference\n"+
        "NB: Sound is Centered ON the prim containing this script\n"+
        "DONE  = EXIT MENU SYSTEM";
    MA_list = ["1/4","1/3","1/2","2/3","3/4","FULL","DONE"];
    llDialog(id, MA_data, ButtonSort(MA_list), CHANNEL);
}
//
status()
{
    string statusA =    "Debug OFF";
    if(DEBUG) statusA = "Debug ON";
    string statusB =     "Device OFF";
    if(On_Off) statusB = "Device ON";
    string statusC =    "OFF";
    if(LOOPING) statusC = "ON";

    MyStatus = "\nSTATUS: ["+statusA+"] ["+statusB+"]\n"+
        "Play Mode:["+WhenToPlay+"] Vol:["+(string)MyVol+"] Loop:["+statusC+"]\n"+
        "Sounds Available: ["+(string)sounds+"]";
}
// === MAIN APP STATE ===
default
{
    state_entry()
    {
        OWNER = llGetOwner();
        sounds = llGetInventoryNumber(INVENTORY_SOUND);
        status();
        llOwnerSay(MyStatus);
        llStopSound();
        llSetTimerEvent(10.0);  // kick the timer in 10 seconds and act on defaults
    }
   
    on_rez(integer start_param)
    {
        llResetScript();
    }
   
    changed(integer change)     // something changed, take action
    {
        if(change & CHANGED_OWNER)
        {
            llResetScript();
        }
        else if (change & CHANGED_INVENTORY)
        {
            sounds = llGetInventoryNumber(INVENTORY_SOUND);
            llOwnerSay("Sound Inventory Changed, resetting Sound Inventory Count "+(string)sounds+" Sounds Found");
        }
    }
   
    touch_start(integer num_detected)
    {
        key id_key = llDetectedKey(0);
        if(id_key == OWNER) MENU_Admin(id_key);
    }
   
    timer()
    {
        if (MENU_ACTIVE)
        {
            MENU_ACTIVE = FALSE;
            MENU = "";
            llOwnerSay("Menu Deactivated Touch to get New Menu");
            llListenRemove(CHANNEL);
            llSetTimerEvent(timing);
        }
        if(On_Off) // it's ON so act accordingly
        {
            if (WhenToPlay == "Allways") SoundPlay();
            else DayOrNight();
        }
    }
    // ==== Listening & acting on Menu Response ====
    listen( integer channel, string name, key id, string msg )
    {
        if(DEBUG) llOwnerSay("\nListen Heard on Channel " + (string)channel+
            "\nName: "+name+
            "\nID: "+(string)id+
            "\nMSG: "+msg);
        // housekeping - Kill Listen reduce Lag
        llListenRemove(CHANNEL);
        // Take Action
        if(msg == "ON")
        {
            On_Off = TRUE;
            llSetTimerEvent(timing);
        }
        else if(msg == "OFF")
        {
            On_Off = FALSE;
            llStopSound();
        }
        else if(msg == "DEBUG")
        {
            if(DEBUG) DEBUG = FALSE;
            else DEBUG = TRUE;
        }
        else if(msg == "Day") WhenToPlay = msg;
        else if(msg == "Night") WhenToPlay = msg;
        else if(msg == "Allways") WhenToPlay = msg;
        else if(msg == "Looping") LOOPING = TRUE;
        else if(msg == "Single") LOOPING = FALSE;
        // volume selections
        else if(msg == "Volume")
        {
            MENU_Volume(id);
            return;
        }
        else if(msg == "1/4") { LOUDNESS = 0.25; MyVol = msg;}
        else if(msg == "1/3") { LOUDNESS = 0.33; MyVol = msg;}
        else if(msg == "1/2") { LOUDNESS = 0.50; MyVol = msg;}
        else if(msg == "2/3") { LOUDNESS = 0.66; MyVol = msg;}
        else if(msg == "3/4") { LOUDNESS = 0.75; MyVol = msg;}
        else if(msg == "FULL") { LOUDNESS = 1.00; MyVol = msg;}
        //
        else if(msg == "RESET") llResetScript();
        if(msg == "DONE")
        {
            MENU_ACTIVE = FALSE;
            MENU = "";
            llSetTimerEvent(timing); //set timing back to the random value already generated OR default
            llOwnerSay(MyStatus);
            return;
        }
        else if(MENU == "ADM") MENU_Admin(id);
        else if(MENU == "VOL") MENU_Volume(id);
    }
}


Thanks to WhiteStar Magic of OSGrid for the above script!

1 comment: