Playing Music files in OpenSim

From OpenSimulator

Revision as of 01:58, 2 June 2023 by JeffKelley (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Introduction

Web-hosted audio files are the simplest way to play Music in OpenSimulator. Entering the url of an audio file in About Land > Sound > Music instructs the viewer to play this file. The only software you need is a web server.

Unlike streaming, where the content is choosen by the broadcaster, the songs to play may be choosen in-world using a script.

Making a jukebox

First, prepare a set of music files on a web directory. Make this directory listable (Apache directive Options +Indexes). The result is as follow :

apache_directory_list.jpg


Next, write a script that will read and parse this page to build a list of songs. Finally, present this list to the user (this example use dynamic textures) and provide a selection mechanism (click on the song name). One the selected song name is obtained, call llSetParcelMusicURL

A more sophisticated script can browse multiple web directories, allowing for a unlimited number of songs.

//
// Sample jukebox script
// 
 
string musicDirectoryUrl = "http://my.web.server/music/wildfire/";
list Songs;
 
//
// Get song list from the HTTP server
//
 
QueryMusicDirectory (string url)
{
    // Default MAXLENGTH of 2048 probably too small
    // Extend to max OpenSim default of 16384
    // This can be further extended in the [Network]
    // section of OpenSim.ini editing HttpBodyMaxLenMAX
 
    llHTTPRequest (url, [HTTP_BODY_MAXLENGTH, 16384], "");
}
 
list ParseMusicDirectory (string html)
{
    list result;
    list lines = llParseString2List (html, ["\n"], [ ]);
    for (integer l=0; l<llGetListLength(lines); l++)
    {
        string line = llList2String (lines, l);
 
        // Crude HTML parsing
 
        integer p = llSubStringIndex (line, "<a href=");
        if (p != -1) line = llDeleteSubString (line, 0, p+7);
 
        p = llSubStringIndex (line, ">");
        if (p != -1) line = llDeleteSubString (line, p, -1);
 
        // Remove leading and trailing quotes
 
        if (llGetSubString (line, 0,0) == "\"")
            line = llDeleteSubString (line, 0,0);
 
        if (llGetSubString (line, -1,-1) == "\"")
            line = llDeleteSubString (line, -1,-1);
 
        if (llGetSubString (line, -4,-1) == ".mp3")
        {
            line = llDeleteSubString (line, -4,-1);
            result += [ line ];
        }
    }
 
    return result;
}
 
//
// Matrix drawing stuff
//
 
integer DISPLAY_SIDE = 1;   // Active side
integer TEXTURE_SIZE = 512;
integer FONT_SIZE = 10;
integer COLUMNS = 3;    // Holds 3x16 items
integer ROWS = 16;      // Extend if necessary
 
string cellBorderColor      = "FFFFFFFF";
string backgroundColor      = "FF555555";
string cellBackgroundColor  = "FF000055";
string cellForegroundColor  = "FFFFFFFF";
 
string drawList;
 
displayBegin() {
    drawList = "";
}
 
displayEnd() {
    osSetDynamicTextureDataFace ( "", "vector", drawList,
        "width:"+(string)TEXTURE_SIZE+",height:"+(string)TEXTURE_SIZE, 0, DISPLAY_SIDE);
}
 
drawCell (integer x, integer y, string text) {
    integer CELL_HEIGHT = TEXTURE_SIZE / ROWS;
    integer CELL_WIDHT  = TEXTURE_SIZE / COLUMNS;
    integer xTopLeft    = x*CELL_WIDHT;
    integer yTopLeft    = y*CELL_HEIGHT;
 
    // Draw border
 
    drawList = osSetPenColor   (drawList, cellBorderColor);
    drawList = osMovePen       (drawList, xTopLeft, yTopLeft);
    drawList = osDrawRectangle (drawList, CELL_WIDHT, CELL_HEIGHT);
 
    // Fill background
 
    drawList = osSetPenColor         (drawList, cellBackgroundColor);
    drawList = osMovePen             (drawList, xTopLeft+2, yTopLeft+2);
    drawList = osDrawFilledRectangle (drawList, CELL_WIDHT-3, CELL_HEIGHT-3);
 
    // Draw text
 
    xTopLeft += 10;  // Center text in cell
    yTopLeft += 08;  // Center text in cell
    drawList = osSetPenColor (drawList, cellForegroundColor);
    drawList = osMovePen     (drawList, xTopLeft, yTopLeft);
    drawList = osDrawText    (drawList, text);
}
 
drawTable(list items) {
    displayBegin();
 
    drawList = osSetPenSize  (drawList, 1);
    drawList = osSetFontSize (drawList, FONT_SIZE);
 
    drawList = osMovePen     (drawList, 0, 0);
    drawList = osSetPenColor (drawList, backgroundColor);
    drawList = osDrawFilledRectangle (drawList, TEXTURE_SIZE, TEXTURE_SIZE);
 
    integer x; integer y;
    for (x=0; x<COLUMNS; x++)
        for (y=0; y<ROWS; y++)
            drawCell (x, y, llList2String (items, y+x*ROWS));
    displayEnd();
}
 
integer getCellIndex(vector point) {
    integer y = (ROWS-1) - llFloor(point.y*ROWS); // Top to bottom
    integer x = llFloor(point.x*COLUMNS);         // Left to right
    integer index = (y+x*ROWS);
    return index;
}
 
//
// Click action
//
//  To use llSetParcelMusicURL, you need to own the parcel
//  The object owner must also be the land owner. If the land
//  is deeded to a group the object will need to be deeded to
//  the same group for this function to work.
 
HandleClick (vector point) {
    integer index = getCellIndex(point);
    MoveIndex (index);
 
    string song = llList2String (Songs, index);
    if (song == "") return; // Blank cell clicked
 
    if (song == "STOP")
    {
        llWhisper (0, "Stopping music");
        llSetParcelMusicURL ("");
    }
    else
    {
        llWhisper (0, "Now playing "+song);
        llSetParcelMusicURL (musicDirectoryUrl+song+".mp3");
    }
}
 
integer INDEX_PRIM = 2; // Optional colored, semi-transparent prim
                        // to place in front of selection
 
MoveIndex(integer index)
{
    integer x = index / ROWS;
    integer y = index % ROWS;
 
    vector size = llGetScale();
    float xIndex = - (size.x/2) + (x+0.5) * size.x / COLUMNS;
    float yIndex =   (size.y/2) - (y+0.5) * size.y / ROWS;
 
    vector indexPos = < xIndex, yIndex, size.z/2 >;
    llSetLinkPrimitiveParamsFast (INDEX_PRIM, [PRIM_POS_LOCAL, indexPos]);
}
 
 
default
{
    state_entry()
    {
        QueryMusicDirectory (musicDirectoryUrl);
    }
 
    http_response (key request_id, integer status, list metadata, string body)
    {
        if (status == 200)
        {
            Songs = ParseMusicDirectory (body);
            Songs += [ "STOP" ];
            drawTable (Songs);
        }
        else
            llOwnerSay ("HTTP error "+(string)status);
    }
 
 
    touch_start (integer n) {
        vector point = llDetectedTouchST(0);
        integer face = llDetectedTouchFace(0);
        integer link = llDetectedLinkNumber(0);
 
        if (face != DISPLAY_SIDE) return;
        if (point == TOUCH_INVALID_TEXCOORD) return;
 
        HandleClick (point);
    }
}


See also

Streaming_Media_in_OpenSim

Personal tools
General
About This Wiki