You are currently in the Forum Archive. All content within this area is Read-Only and cannot be modified. Active Forums can be found here.
Reply
Recognized Resident
Cookie Bertone
Posts: 6

Free Full Featured Radio (Parcel Music Player) from Bertone Audio

I would like to present my radio script.  This has been slowly modified over that last few years into what it is today and was until recently sold for L$200 per item in my (now closed) store.

I would like to take this opportunity to say thank you to everybody who not only copies the script but pulls it apart for their own uses and then sells it to make money.  Good on you.  Congratulations.  I hope you make thousands.  I used to, then they changed the search rules.

Goodbye Second Life.  It was fun while it lasted.

In the object you need the script, a notecard called 'channels' which holds the stream URL's and an instructions notecard.  It was a two or three prim object and prim #2 would change colour depending on the status of the script.  I have provided a copy of all those items in this post.

It can retrieve track names if provided by the stream and it also referred to an update server that would check to see if it was the latest version and deliver an updated version if it wasn't.  That option was still not perfect as I wanted it to update the channels notecard or the script but not the whole device.  When I stopped developing the script this was still not added.

There are also references to a picture option.  I used this with a wall mounted radio that would cycle through textures included in the object.  I'll let you figure out how to activate that option in the menu.

// BAS PmP
// last modified 17 May 2010

// written by Cookie Bertone
// this script is PUBLIC DOMAIN

// if instructions are renamed make change in this script also!!

string version = "1.64"; // Version number of this item

string gName = "channels"; // name of the notecard that holds the channel list
integer gLine = 0; // current line number, set to 0 so it starts at the beginning
key gQueryID; // id used to identify dataserver queries
list GenreElementList = [];
integer populating_list = TRUE;
string nPlaying = "Unknown";
integer FloatText = TRUE;

// start 1.5 extras

list trackpulllist = [];
float gap = 5.0;
float counter = 0.0;
string nPlayingURL = "http://";
string TrackElementHold = "";
integer nPlayingSHOW = TRUE;

// end 1.5 extras

// start 1.6 extras

key gkOwner;
key serverID = "0acc64cf-adc2-ad08-7021-8548fd37ecb7@lsl.secondlife.com";
integer InstalledTextures = 0;

// end 1.6 extras

key menuitem; // id used to get menu selection
list MainMenu = ;
list OptionsMenu = ;
string tuner_dialog = "\nBAS Parcel Music Player\nBertone Audio Systems, Rossa (212, 98, 79)\n\nMake a selection or press 'ignore' to close this dialog box.";
string chatline = "------------------------------------------------------";

list first_menu;
list second_menu;
list third_menu;
list fourth_menu;
list fifth_menu;
list sixth_menu;
list seventh_menu;
list eigth_menu;
list ninth_menu;
string lastpage = "Page 9";

default
{
    // when device is first rezzed it will reset the script
    on_rez(integer start_param)
    {
        llSay(0, "Player installed. Setting defaults.");
        llResetScript();
        llSay(0, "Defaults set.");
    }
   
    // when script is started or restarted it will display some details about the device,
    // tell it to read the data file, start the listen and set the text on the device
    state_entry()
    {
        llSetLinkColor(2, <0.0, 0.9, 0.0>, ALL_SIDES);
        llSay(978, "reset");
        llSay(0, "\n\n" +chatline+ "\nBAS PMP (Parcel Music Player) " +version+ "\nScript written by Cookie Bertone\nBertone Audio Systems, Rossa (212, 98, 79)\n" +chatline+ "\n\nLoading channels.\nPlease wait for 'Ready'.\n\n");
        llSetParcelMusicURL("");
        gQueryID = llGetNotecardLine(gName, gLine); // request first line
        llListen(97, "", NULL_KEY, ""); // listen for request on channel 97
        llSetText("Bertone Audio System " +version, <0.0,1.0,1.0>, .5);
        llSetTimerEvent(gap); // Activate the timer listener every X seconds
    }
   
    // when the device is touched it will display the main menu
    touch_start(integer touchNumber)
    {
        llDialog(llDetectedKey(0), tuner_dialog, MainMenu, 97);
    }
 
    listen(integer channel, string name, key id, string message)
    {
        integer index = llListFindList(GenreElementList, );

        if (index != -1)
        {
            llSay(0, "Found "+message+" in channel list. Setting channel.");
            llSetLinkColor(2, <0.0, 0.9, 0.0>, ALL_SIDES);
            gQueryID = llGetNotecardLine(gName, index);
        }
        else if (message=="list")
        {
            llSay(0, "Listing possible genres:");
            integer x;
            integer length=llGetListLength(GenreElementList);
            for (x=0; x<length; x++)
            {
                llSay(0,llList2String(GenreElementList,x));
            }
        }
        else if (message=="playing")
        {
            llSay(0, "Now playing: "+nPlaying);
        }
        else if (message=="texton")
        {
            FloatText = TRUE;
            nPlayingSHOW = TRUE;
            llSetText("Now playing: "+nPlaying+".", <0.0,1.0,1.0>, .5);
        }
        else if (message=="textoff")
        {
            FloatText = FALSE;
            nPlayingSHOW = FALSE;
            llSetText(" ", <0.0,1.0,1.0>, .5);
        }
        else if (message=="XXpictureXX")
        {
            integer InstalledTextures = llGetInventoryNumber(INVENTORY_TEXTURE);
            if (InstalledTextures == 1)
            {
                string TextureName = llGetInventoryName(INVENTORY_TEXTURE, 0);
                llSay (0,"Texture found: " +TextureName);
                if (name != "")
                    llSetTexture(TextureName, 5);
            }
            if (InstalledTextures > 1)
            {
                float RandTexture = llFrand(InstalledTextures);
                integer TextureChoice = (integer)RandTexture;
                string TextureName = llGetInventoryName(INVENTORY_TEXTURE, TextureChoice);
                llSay (0,"Choosing a random texture: "+TextureName);
                if (name != "")
                    llSetTexture(TextureName, 5);
            }
            if (InstalledTextures < 1)
            {
                llSay(0,"There are no textures installed.");
            }
        }
        else if (message=="instructions")
        {
            llGiveInventory(id, "radio instructions (16)");
        }
        else if (message=="reset")
        {
            llSay(0, "Resetting.");
            llResetScript();
        }
        else if (message=="options")
        {
            llDialog(id, tuner_dialog, OptionsMenu, 97);
        }
        else if (message=="update")
        {
            llOwnerSay("Contacting update server.");
            llEmail(serverID,"Update Request",llGetObjectName() + ":" + version + "|" + (string)llGetOwner());
        }
        else if (message=="off")
        {
            llSay(0, "System Off");
            llSetLinkColor(2, <1.0, 0.0, 0.0>, ALL_SIDES);
            llSay(978, "red");
            llSetParcelMusicURL("");
            //next two lines new for v1.5, these reset variables to stop it displaying track names when switched off
            nPlayingURL = "http://";
            TrackElementHold = "";
            if (FloatText = TRUE)
            {
                llSetText("Bertone Audio System " +version+ " OFF", <0.0,1.0,1.0>, .5);
            }
        }
        else if (message=="tuner")
        {
            if(llGetListLength(GenreElementList) > 80)
            {
                llSay(0, "Only the first 80 channels will be displayed in the menu.");
            }
            if(llGetListLength(GenreElementList) > 12)
            {
                // remember that a list starts at 0, not 1
                // all the menus except the first one are set here so the following if statements can change them if necessary
                second_menu = + llList2List(GenreElementList, 9, 17);
                third_menu = + llList2List(GenreElementList, 18, 26);
                fourth_menu = + llList2List(GenreElementList, 27, 35);
                fifth_menu = + llList2List(GenreElementList, 36, 44);
                sixth_menu = + llList2List(GenreElementList, 45, 53);
                seventh_menu = + llList2List(GenreElementList, 54, 62);
                eigth_menu = + llList2List(GenreElementList, 63, 71);
                ninth_menu = + llList2List(GenreElementList, 72, 80);

                // this bit sets which page will be the last so no empty menus are displayed
                if(llGetListLength(GenreElementList) < 81)
                {
                    ninth_menu = + llList2List(GenreElementList, 72, 80);
                    lastpage = "Page 9";
                }
                if(llGetListLength(GenreElementList) < 72)
                {
                    eigth_menu = + llList2List(GenreElementList, 63, 71);
                    lastpage = "Page 8";
                }
                if(llGetListLength(GenreElementList) < 63)
                {
                    seventh_menu = + llList2List(GenreElementList, 54, 62);
                    lastpage = "Page 7";
                }
                if(llGetListLength(GenreElementList) < 54)
                {
                    sixth_menu = + llList2List(GenreElementList, 45, 53);
                    lastpage = "Page 6";
                }
                if(llGetListLength(GenreElementList) < 45)
                {
                    fifth_menu = + llList2List(GenreElementList, 36, 44);
                    lastpage = "Page 5";
                }
                if(llGetListLength(GenreElementList) < 36)
                {
                    fourth_menu = + llList2List(GenreElementList, 27, 35);
                    lastpage = "Page 4";
                }
                if(llGetListLength(GenreElementList) < 27)
                {
                    third_menu = + llList2List(GenreElementList, 18, 26);
                    lastpage = "Page 3";
                }
                if(llGetListLength(GenreElementList) < 18)
                {
                    second_menu = + llList2List(GenreElementList, 9, 17);
                    lastpage = "Page 2";
                }

                // the first menu is set here 'after' the last page has been defined (above) then the first dialog is displayed
                first_menu = + llList2List(GenreElementList, 0, 8);
                llDialog(id, tuner_dialog, first_menu, 97);
            }
            else
            {
                // if there are less than 12 items in the menu it displays this dialog that doesn't need the page buttons
                llDialog(id, tuner_dialog, GenreElementList, 97);
            }
        }
        else if(message=="Page 1")
        {
            llDialog(id, tuner_dialog, first_menu, 97);
        }
        else if(message=="Page 2")
        {
            llDialog(id, tuner_dialog, second_menu, 97);
        }
        else if(message=="Page 3")
        {
            llDialog(id, tuner_dialog, third_menu, 97);
        }
        else if(message=="Page 4")
        {
            llDialog(id, tuner_dialog, fourth_menu, 97);
        }
        else if(message=="Page 5")
        {
            llDialog(id, tuner_dialog, fifth_menu, 97);
        }
        else if(message=="Page 6")
        {
            llDialog(id, tuner_dialog, sixth_menu, 97);
        }
        else if(message=="Page 7")
        {
            llDialog(id, tuner_dialog, seventh_menu, 97);
        }
        else if(message=="Page 8")
        {
            llDialog(id, tuner_dialog, eigth_menu, 97);
        }
        else if(message=="Page 9")
        {
            llDialog(id, tuner_dialog, ninth_menu, 97);
        }
        else if(message=="main menu")
        {
            llDialog(id, tuner_dialog, MainMenu, 97);
        }
        else
        {
            llSay(0, "Sorry. I couldn't find a station or command with the ID: "+message);
        }
    }

// timer section is new for version 1.5

    timer()
    {
        //llHTTPRequest(nPlayingURL+"/7.html HTTP/1.0\nUser-Agent: XML Getter (Mozilla Compatible)\n\n",[],"");
        llHTTPRequest(nPlayingURL+"/7.html HTTP/1.1\nUser-Agent:Mozilla\n\n",[],"");
       
    } 

// http_response section is new for version 1.5   
       
    http_response(key trackid, integer status, list metadata, string trackbody)
    {
        list TrackPullList = llParseString2List(trackbody,,[]); // takes the http result and turns it into a list
        string TrackElement = llList2String(TrackPullList,6); // takes the sixth item out of the http result list
       
        TrackElement = llDeleteSubString(TrackElement, -14, -1); // strips off the html junk text at the end

        if (nPlayingSHOW == TRUE)
        {
            if (TrackElementHold != TrackElement)
            {
                llSay(0, "Now playing: " + TrackElement + "."); // displays the result
                TrackElementHold = TrackElement;
            }
        }
    }

    dataserver(key query_id, string data)
    {
        if (populating_list == TRUE) // first pass through is to build up a list of Genre Names
        {
            if (data != EOF)
            {
                list StationList = llParseString2List(data,,[]);
                string GenreElement = llList2String(StationList,2);
                GenreElementList = GenreElementList + ;
                ++gLine; // increase line count by 1
                gQueryID = llGetNotecardLine(gName, gLine); // request next line
            }
            else
            {
                populating_list = FALSE;
                llSetLinkColor(2, <1.0, 0.0, 0.0>, ALL_SIDES);
                llSay(0, "\n\nReady.\nTouch player to begin...\n\n");
            }
        }
        else
        {
            list StationList = llParseString2List(data,,[]); // converts the line taken into a list
            string URLElement = llList2String(StationList,0); // grab the URL
            llSetParcelMusicURL(URLElement);
            llSetLinkColor(2, <0.9, 0.9, 0.9>, ALL_SIDES);
            llSay(978, "blue");
            string StationElement = llList2String(StationList,1);
            llSay(0, "Now playing: "+StationElement+" ("+URLElement+").");
            if (FloatText == TRUE)
            {
                //llSay(0,"DIAGNOSTIC: FloatText is true");
                llSetText("Now playing: "+StationElement+".", <0.0,1.0,1.0>, .5);
            }
            nPlaying = StationElement; // this holds the station name for use in other sections
            nPlayingURL = URLElement; // this holds the station URL for use in other sections
        }
    }
}


Now the channels notecard.  I have only kept a few but it will take upto 80 on the menu.  Each item is seperated by a comma.  It is important there is no space on either side of the comma.  You have the URL, the name that is displayed in chat and the name for the menu button.

http://207.200.96.228:8076,Groove Salad on Soma FM,groove salad
http://205.188.215.231:8014,Lounge on DI FM,lounge
http://205.188.215.225:8000,Datempo Lounge on Sky FM,datempo
http://205.188.215.230:8004,Vocal trance on DI FM,vocal trance
http://205.188.215.228:8008,Trance on DI FM,trance
http://205.188.215.226:8002,House on DI FM,house
http://205.188.215.231:8008,Progressive on DI FM,progressive
http://205.188.215.229:8026,#Musik Club,musik club
http://78.159.104.167:80,Technobase FM,techobase
http://89.238.146.142:7030,Ibiza Global Radio,ibiza global
http://207.200.96.231:8008,Absolutely Smooth Jazz on Sky FM,smooth jazz
http://64.71.144.178:8045,Otto's Baroque on 1 FM,1fm baroque
http://72.13.81.178:9030,Otto's Classical on 1 FM,1fm classical


And the instructions I wrote.

Welcome

This is the instruction guide for all Bertone Audio radios.

??

This document was last updated on the 30 December 2009 and is revision number 16.

Current radio version at time of writing is 1.62.

**** CONTENTS ****

1. Overview
2. Instructions

IMPORTANT: All visitors are advised to take one of the FREE basic radios for parcel compatibility testing before purchasing a full system.

**** OVERVIEW ****

I hope the Bertone Audio radio is exactly what you are looking for in a parcel music player.

All of the Bertone Audio radios contain a script, customisable channel list and a set of instructions just like these.  The radios use a touch to activate menu or simple chat based commands and can be operated by the owner from within the same parcel.  The speakers and stands are for aesthetic purposes only and serve no function other than to look good.

The channel list will be checked every few weeks for changes and is available free of charge by either selecting the 'update' option in the player menu or from the information post in the store.  Why not join the Bertone Audio - Update Group and get instant notification of any updates?  It's free to join.

To purchase an item simply touch the item or the black/green L$ cube above it.

Thank you for visiting.

Notes:

* For use on group owned land the receiver must be deeded to the group and group members must have the group tag activated.  For use in apartments or on shared group land the officers must deem the user qualified to change the url on behalf of the group.

* Should you already own a device that uses the same channel please let me know and I'll sort out another that uses a different channel to avoid conflicts.

**** INSTRUCTIONS ****

The following will attempt to describe installation, use and modification of a Bertone Audio radio.  If you have any problems with a system that you have purchased please PM me, Cookie Bertone, and I will attempt to help you overcome the problem.

VERY IMPORTANT: If you are installing the system on group or rented land please ensure you have read and understood the notes below regarding this.


Overview:
-------------------------------------------
The system consists of (a) the receiver (b) speakers and (c) stand/s.  The speakers and stands are simple basic prims and have no purpose except to look good.  The receiver contains the working parts of the system which are the script and the station notecard as well as a copy of these instructions and a location for the store.  The script sets the streaming audio URL for the parcel the receiver is on and it cannot be viewed, modified or copied.  The station notecard contains the station list and can be freely modified.


Installation:
-------------------------------------------
Place the receiver in the land parcel that you want to use it with.

In most circumstances the receiver must be owned by the owner of the parcel.  For use on group owned land the receiver must be deeded to the group and group members must have the group tag activated to be able to use it.  For use in apartments or on shared group land the officers must deem the user qualified to change the url on behalf of the group.

If the receiver is deeded to a group the channels notecard cannot be edited until ownership is returned to an individual.  That individual can then edit the notecard before returning ownership to its previous settings.


To select a channel:
-------------------------------------------
Touch the player to activate the menu, select 'tuner' and then select the station you would like to listen to.  To display more stations select one of the 'page' buttons (not available in free or basic player).

You can also control the player using chat commands.

In general chat enter /97 followed by a space and the button name of the station you want to listen to.  Eg: The button name for DI.fm's Lounge channel is di_lounge and for Sky FM's Best of the 80's channel it is sky_80s . To select either of those channels you would enter the following in chat: /97 di_lounge or /97 sky_80s .  To view a list of all channels enter /97 list .

From version 1.2.3 you can also display the current station in floating text above the receiver.  To display the floating text enter /97 texton.  To hide the floating text enter /97 textoff.


To view & modify available channels:
-------------------------------------------
With the 'full' version of the player the station notecard can be modified by the user without restrictions. To see the notecard right-click on the receiver and select 'Open' from the pie menu.  This will open the 'Object Contents' window.  You can now see the contents of the receiver which are the script, the channels notecard, the instructions notecard and a location.

If you double left-click on the channels notecard it will open in a new window.  You can now see all the channels that the receiver can use.  Each line of the channels notecard refers to one station.  Each line consists of three parts seperated by commas.  First is the stream URL, second is the description and third is the button name.

To add a new channel simply create a new line, enter the stream address, a description you understand and a button name, each seperated by commas.  You need to watch out for spaces on either side of the commas and to not use a button name that has already been used.  The button name is limited to twelve charachters.

To delete a channel just delete the line and to change a button name just overtype it.

Once finished save the notecard and then close the channels and Object Contents windows.  You then need to reset the script by selecting the device (right-click on it to open the pie menu) and then go to the 'Tools' menu (at the top of the Second Life window between 'World' and 'Help') and selecting 'Reset Scripts in Selection' for it to update.

* where applicable

If you ruin your notecard don't panic, new notecards are available from the dispenser in store.


To update your device:
-------------------------------------------
From version 1.62 the radios come with a built in update option.  With version 1.62 the radio will contact a distribution server and if a newer version is available will deliver it to your inventory almost instantly.  Later versions will perform seperate checks for the radio and the channels notecard.


And here is the server script.  This not my own work and sadly I have no idea where I got it from, it was probably somewhere in these forums...

It needs to be put in its own static object that can left forever without being deleted etc (this would wreck the UUID).  It needs a notecard in the same object called 'updates'.

// CTD Updater Script
// Server UUID:debccf52-3ec9-0428-4f4f-dcc533199b30
// NOTE: This is an area to keep the UUID in the notes for reference.
key serverID;
string cardName = "Updates"; // Name of the Notecard with all of the Item:Version info
string company = "Bertone Audio"; // Name of the company
key lineTotalID;
key locateID;
integer lineCurrent = 0; // Line Offset
integer lineTotal; // Line Offset
string noteLine;
string noteFront;
string noteBack;
string item;
string version;
key user;
float watchdog = 10.0;
integer InUse = FALSE;

default
{
state_entry()
{
llWhisper(0,llGetKey());

llSetTimerEvent(5.0);

lineTotalID = llGetNumberOfNotecardLines(cardName);
}

touch_start(integer total_number)
{
llResetScript();
}

timer()
{
llGetNextEmail("","");
}

email(string time, string address, string subject, string body, integer remaining)
{
llWhisper(0,"Received Request...");

if (InUse == FALSE)
{
InUse = TRUE;

llSetText("In Use...",<1,1,1>,1.0);

string message = llDeleteSubString(body, 0, llSubStringIndex(body, "\n\n") + 1);

integer itemMark = llSubStringIndex(message,":");
integer verMark = llSubStringIndex(message,"|");

item = llGetSubString(message,0,(itemMark - 1));
version = llGetSubString(message,(itemMark + 1),(verMark - 1));
user = (key)llGetSubString(message,(verMark + 1), -1);

llWhisper(0,"Item: " + item);
llWhisper(0,"Version: " + version);
llWhisper(0,"User: " + llKey2Name(user));
lineCurrent = 0;

locateID = llGetNotecardLine(cardName,0);
}
}

dataserver(key queryID, string data)
{
if (queryID == lineTotalID)
{
lineTotal = (integer)data;
llInstantMessage(llGetOwner(),"Found " + (string)lineTotal + " items on update server.");
}

if (locateID == queryID)
{
if (data != EOF)
{
noteLine = data;

integer marker = llSubStringIndex(noteLine,":");

if (marker != -1)
{
lineCurrent += 1;
noteFront = llGetSubString(noteLine,0,( marker - 1 ));
noteBack = llGetSubString(noteLine,( marker + 1 ), -1);
}
if (marker == -1)
{
llInstantMessage(llGetOwner(),"Error with Update Server Inventory line: " + (string)lineCurrent);
}

if (noteFront != item)
locateID = llGetNotecardLine(cardName,lineCurrent);

if (noteFront == item)
{
if ((float)noteBack > (float)version)
{
llGiveInventory(user,item);
llInstantMessage(user,"Sending you the updated version of: " + item);
}

if ((float)noteBack < (float)version)

llInstantMessage(user,"There was an error with the update of your " + item + ", please contact " + llKey2Name(llGetOwner()));

if ((float)noteBack == (float)version)

llInstantMessage(user,"Your " + item + " by " + company + " is up to date.");

InUse = FALSE;

llSetText("",<1,1,1>,0.0);
}
}
}
if (data == EOF)
{
InUse = FALSE;

llSetText("",<1,1,1>,0.0);
llInstantMessage(llGetOwner(),"Update Server Hit EOF on Update List");
}
}
}


This was what I had in my 'updates' notecard.

BAS Cube:1.60
BAS Tube:1.60
BAS Pyramid:1.60
BAS Hidden:1.60
BAS Classic:1.60
BAS PMP-A:1.60
BAS PMP-B:160
BAS F80:1.60
BAS El Arco:1.60
BAS Wast Water:1.60

Chelsea Malibu
Posts: 1,712
Registered: ‎10-05-2009

Re: Free Full Featured Radio (Parcel Music Player) from Bertone Audio

Reply to Cookie Bertone - view message

Sorry to see you throw in the towel as good scripters are hard to find and you are among the better ones out there.  However, i do thank you for the script and always and greatful for the contributions you have to our wacky virtual world.

Lilah Munster
Posts: 125
Registered: ‎01-31-2010

Re: Free Full Featured Radio (Parcel Music Player) from Bertone Audio

Reply to Cookie Bertone - view message

Sorry you had a bad experience with people who don't respect the wishes of content creators.  But thank you for releasing this script.  It should save me some prims and make the radio control on my little parcel of land much more streamlined.

What if the Hokey Pokey really IS what it's all about?