ソラマメブログ

2007年11月21日

自分の土地で音楽を流そう

 だいたいの方は、SHOUTcastなどからインターネットラジオのURLを抜いてきて自分の土地に設定している、どこかで配布しているSLラジオを置いて、自分の土地で音楽を流していると思います。
 でも、自分の選曲で流したいとなると、サーバー用のPCを調達してサーバーソフトを導入しないと流せないと思っていました。
 スクリプト的なところで、自分の土地で音楽を設定するためにllSetParcelMusicURL(string url)というのがあるんですが、リファレンスをみてもURLと書いてあって、サーバーのアドレスを指定しないとならないと思いこんでいました。
 が、このURLの部分に直接MP3ファイルを指定しても、土地で音楽を流せることが分かりました。
 ということは、自分の契約しているプロバイダのホームページ用エリアにMP3をアップロードして、それをSLで流せるということになります。

 そんなことから、これはスクリプトでジュークボックス的なものを作ってみようと思いまして、いろいろと機能を入れて作ってみました。
 ちょっと面倒なのはNotecardでプレイリスト(らしきもの)を作ってオブジェクトの中に入れておかなければならないことで、書式は後述します。
 面倒ですが、プレイリストにサーバー名もMP3ファイル名も入っているので、お友達にNotecardを渡せばそのまま聞くことが出来るわけです。

【お約束】
 著作物の取り扱いには注意が必要です

<機能>
 ・指定のURL上にあるMP3ファイルを連続で再生する
 ・サーバーとMP3のデータはNotecardに記述する(まぁ、Playlistみたいなもの)
 ・サーバー、MP3のデータを書いたNotecardは複数でも可
 ・オブジェクトにNotecardをドロップして追加可能(ただし追加した場合、スクリプトはリセットされる)
 ・オブジェクトの上に現在流れているMP3の曲名/アーティスト名を表示する
 ・全てのNotecardに書かれている全てのMP3を順に再生し、全て再生し終わったら最初に戻る
 ・オブジェクトをTouchすると、ダイアログを表示し、次の曲の情報を表示する
 ・ダイアログにはPlay、Repeat、Stop、OK、Next、Resetボタンを表示する。
 ・Playボタン:停止状態の場合は再生を開始する。再生中の場合は次の曲へ
 ・Repeatボタン:再生されているMP3を繰り返す(1回だけ)
 ・Stopボタン:土地のミュージックを停止する
 ・OKボタン:ダイアログを閉じる
 ・Nextボタン:次の曲を再生する
 ・Resetボタン:スクリプトのリセットを行う(最初から再生し直す)

<Notecardの書式>
 ・//で始まる行はコメント行とする
 ・空行(改行コードのみの行」を入れることは可能
 ・サーバーの定義を「url,http://www.test.ne.jp/」で記述する
 ・サーバーの定義はそのつど記述しても良い
 ・実際のMP3の定義は「ファイル名,曲名,アーティスト名,時間」と,(カンマ)区切りとし、それぞれの項目は""(ダブルクォーテーション)で囲む必要はない
 ・ファイル名は半角英数字のみ(スペース不可)(サーバーにアップロードできる名前)
 ・曲目、アーティスト名は普通の文字列。スペース等可。日本語可
 ・時間は半角数字と半角:(コロン)で「分:秒」とする。(3:54や12:34など)

 それではサンプルのNotecardを2つとスクリプトです。あいかわらず長いです。
 スクリプトはそのままオブジェクトに入れればOKで、特に変更するような場所はありません。
 あえて自分の環境に合わせて変更するとすれば、オブジェクトの上に表示されるテキストの色(現在は赤)と、混信防止のためにチャットのチャンネル番号(現在は1120)ぐらいです。
 ノートカードは適当に記述してますから、自分の環境に合わせて書き直すなり、新たに作るなりしてください。時間を記述しないとならないのが面倒ですが、MP3のタグエディターなどで必要な項目をCSV形式(カンマ区切りデータ)で書き出せば楽だと思います。

//----------------------------------------------
// Notecard sample 1
// ノートカード・サンプル

// IV / Red Zeppelin
url,http://www.sasapy.gov/mp3/red/
stairway.mp3,Stairway to 2nd Floor,Red Zeppelin,6:52
white.mp3,White Dog,Red Zeppelin,3:43

// No Title / The Beatloose
url,http://www.sasapy.gov/mp3/beat/
while.mp3,While My Ukulele Gently Weeps,The Beatloose,4:18
nyaa.mp3,Nyaaa Blues,The Beatloose,5:12
//----------------------------------------------

//----------------------------------------------
// Notecard sample 2
// ノートカード・サンプル

// Machine Bed / Deep Yellow
url,http://www.sasapy.com/
smoke.mp3,Smoke on the Fire,Deep Yellow,3:23

// 天国 / 氷
tengoku.mp3,天国,氷,4:20
//----------------------------------------------

//----------------------------------------------
// sasa_mp3_player
// 1.00.00
// 2007.11.19
// created by sasapy Beck

// Define
    key owner_key;

    string url; // サーバー名
    string file_name; // ファイル名
    string song_name; // 曲名
    string artist_name; // アーティスト名
    float song_time; // 曲の時間
    float time_first = 10.0; // スクリプトの最初にNotecardを読み込むための時間(秒)
    float time_lag = 5.0; // 曲間のタイムラグを補正する時間(秒)

    integer repeat = FALSE; // リピート指定された時にTRUE
    string now_url; // 現在のサーバー名
    string now_file_name; // 現在のファイル名
    string now_song_name; // 現在の曲名
    string now_artist_name; // 現在のアーティスト名
    float now_song_time; // 現在の曲の時間

    key query_key; // データサーバーからデータを受け取るためのキー
    integer notecard_num = 0; // ノートカード番号
    integer notecard_max; // ノートカード数
    string notecard_name; // ノートカード名
    integer notecard_line = 0; // 読み込む行番号
    vector text_color = <1.0, 0.0, 0.0>; // テキスト表示色(赤)

    integer listen_handle; // listen用のハンドル
    list button = ["OK", "Next", "Reset", "Play", "Repeat", "Stop"]; // ダイアログボタン定義
    integer channel = 1120; // ダイアログからの通信用チャンネル番号

// Main
    default
    {
        state_entry()
        {
            owner_key = llGetOwner();

// ドロップを可能にする
            llAllowInventoryDrop(TRUE);

// ノートカード数取得
            notecard_max = llGetInventoryNumber(INVENTORY_NOTECARD);

// 最初の読み込み処理
            if(notecard_max != 0)
            {
                notecard_name = llGetInventoryName(INVENTORY_NOTECARD, notecard_num);
                query_key = llGetNotecardLine(notecard_name, notecard_line);

// 土地のミュージックを設定する処理は全てタイマーイベントで行う
// 最初にノートカードを読み込んでからタイマー処理をしたいため、ダミーでタイマー起動

                llSetTimerEvent(time_first);
            }
            else
            {
                llOwnerSay("Can't find Notecard.");
            }
        }

// Rez Event
        on_rez(integer start_param)
        {
            llResetScript();
        }

// Data Server Event
// ノートカード読み込み処理
        dataserver(key queryid, string data)
        {
            if(queryid == query_key)
            {
// End of Fileでないとき
                if(data != EOF)
                {
// コメント行と空行を読み飛ばす
                    if((llGetSubString(data, 0, 1) == "//") || (data == ""))
                    {
                        notecard_line ++;
                        query_key = llGetNotecardLine(notecard_name, notecard_line);
                    }
// サーバー定義行の場合
                    else if(llGetSubString(data, 0, 3) == "url,")
                    {
                        url = llDeleteSubString(data, 0, 3);
                    }
// それ以外=MP3の定義行
                    else
                    {
// カンマ区切りの行をリストに変換
                        list separate_string = llParseString2List(data, [","], []);

// 各パラメーターをセット
                        file_name = llList2String(separate_string, 0);
                        song_name = llList2String(separate_string, 1);
                        artist_name = llList2String(separate_string, 2);

// 時間(分:秒)をコロン区切りのリストにし、秒に変換する
                        list string_time = llParseString2List(llList2String(separate_string, 3), [":"], []);
                        song_time = llList2Float(string_time, 0) * 60 + llList2Float(string_time, 1);
                    }
                }
// EOFの場合、次のノートカードの0行目を読む
                else
                {
                    notecard_num ++;
// ノートカード番号が最大数になったら0に戻す
                    if(notecard_num >= notecard_max)
                    {
                        notecard_num = 0;
                    }
                    notecard_name = llGetInventoryName(INVENTORY_NOTECARD, notecard_num);
                    notecard_line = 0;
                    query_key = llGetNotecardLine(notecard_name, notecard_line);
                }
            }
        }

// Timer Event
// 土地のミュージック設定処理
// 初回以外は、曲の時間でタイマーイベント起動する
        timer()
        {
// 通常の処理(リピート設定されていない場合)
            if(!repeat)
            {
// サーバー名+ファイル名を土地のミュージックに設定
                llSetParcelMusicURL(url + file_name);
// オブジェクトの上に曲名、アーティスト名表示
                llSetText("Now playing\n" + song_name + " / " + artist_name, text_color, 1.0);
// 次のタイマーイベント発生の時間設定(曲の時間)
                llSetTimerEvent(song_time - time_lag);

// 次の曲のパラメータを読み込む
                notecard_line ++;
                query_key = llGetNotecardLine(notecard_name, notecard_line);

// 現在の曲のパラメータの保持
                now_url = url;
                now_file_name = file_name;
                now_song_name = song_name;
                now_artist_name = artist_name;
                now_song_time = song_time;
            }

// リピート指定されていた場合
            else
            {
// 現在の曲のパラメータを再設定する
                llSetParcelMusicURL(now_url + now_file_name);
                llSetText("Now playing\n" + now_song_name + " / " + now_artist_name, text_color, 1.0);
                llSetTimerEvent(now_song_time - time_lag);
                repeat = FALSE;
            }
        }

// Touch Event
// タッチされたらダイアログを表示
        touch_start(integer num_detected)
        {
            string dialog_message = "sasa mp3 player.\nNext song is\n";
            key touch_key = llDetectedKey(0);
            listen_handle = llListen(channel, "", touch_key, "");
            llDialog(touch_key, dialog_message + song_name + " / " + artist_name, button, channel);
        }

// Listen Event
// ダイアログからの入力を取得する処理
        listen(integer channel, string name, key id, string message)
        {
            if (message == llList2String(button, 0)) // OK
            {
                llListenRemove(listen_handle);
            }
            else if(message == llList2String(button, 1)) // Next
            {
// 次の曲に飛ばす処理はタイマーを即時起動する
                llListenRemove(listen_handle);
                llSetTimerEvent(0.1);
            }
            else if(message == llList2String(button, 2)) // Reset
            {
                llListenRemove(listen_handle);
                llResetScript();
            }
            else if(message == llList2String(button, 3)) // Play
            {
                llListenRemove(listen_handle);
                llSetTimerEvent(0.1);
            }
            else if(message == llList2String(button, 4)) // Repeaet
            {
                llListenRemove(listen_handle);
                repeat = TRUE;
            }
            else if(message == llList2String(button, 5)) // Stop
            {
                llListenRemove(listen_handle);
                llSetParcelMusicURL("");
                llSetText("sasa mp3 player Stopped", text_color, 1.0);
                llSetTimerEvent(0.0);
            }
        }

// Change Event
// オブジェクトにドロップされたときの処理
        changed(integer change)
        {
// MOD権限を持つ人がドロップした場合
            if(change & CHANGED_INVENTORY)
            {
// ノートカードが増えていればスクリプトをリセット
                integer new_notecard = llGetInventoryNumber(INVENTORY_NOTECARD);
                if(notecard_max < new_notecard)
                {
                    llResetScript();
                }
// ノートカード以外がドロップされた場合
                else
                {
                    llWhisper(PUBLIC_CHANNEL, "This is not a Notecard.");
                }
            }
// MOD権限を持たない人がドロップした場合
            else if(change & CHANGED_ALLOWED_DROP)
            {
                llWhisper(PUBLIC_CHANNEL, "You can't modify.");
            }
        }
    }
//----------------------------------------------

<お願い>ファイルからペーストしたので、インデント(字下げ)が無くなってしまい見づらくなってしまいました。ここからコピーした際は、ご自身でインデントしていただけると助かります。


この記事へのトラックバックURL

http://sasapy.slmame.com/t73173
この記事へのコメント
こんにちは、たまたまブログを読み漁っていたら、ささぴーさんを発見w

土地に音楽を流す方法…こんな方法があったとは…凄い参考になりました!
といっても、土地無しな私…裕福になりたい!!ヽ(`Д´)ノ
Posted by redzaku at 2007年12月05日 12:07
いちいちサーバーソフト(とかプラグイン)をいれたりせずに、自分の好きな順番で再生できるというのがいいですよね。

モノを作ったり、スクリプトを試したりというのは、土地がないと出来ないですし、サンドボックスは重かったりしますから、自分の土地があるといいですよぉ。
私のところは作りかけのモノがあったりで、雑然としていますけどね。
Posted by ささぴささぴ at 2007年12月05日 14:02
はじめまして~

このスクリとノートを利用して自分の土地で、好きな曲を流せるようになりました~
どうもありがとうございます(≧∇≦)b

ただ、スクリとノートを組み入れたオブジェクトをほかの土地の所有者に試してもらったら、うまく再生できませんでした^^;
もしかして土地の設定とか関係しているのでしょうか?
Posted by Robby Little at 2007年12月20日 09:46
Robby Littleさん、こんにちは。

ただ、スクリとノートを組み入れたオブジェクトをほかの土地の所有者に試してもらったら、うまく再生できませんでした^^;
もしかして土地の設定とか関係しているのでしょうか?

他の土地の所有者ではうまく再生できなかったとのことですが、私は他の誰かに渡して再生してもらったことがないので、確実なところは分かりませんが、考えられることとして、

・土地の所有者が土地の音楽を設定できる権限を持っていない
(土地の所有者に自分の土地情報を見てもらってミュージックのURLが設定できているか確認してもらう)

もし、お時間があるようでしたら、私のところに来て、私にRobby Littleさんの作ったオブジェクトを渡してもらって再生できるか試してみたいですね。
Posted by ささぴささぴ at 2007年12月20日 14:06
昨日、引越し先に行ってみましたが、誰もいなかったので、またお邪魔させていただきますね^^
Posted by Robby Little at 2007年12月21日 08:03
やりました!無事、SL内で流すことに成功しましたー♪
といっても、結局ライブストリーミングを選択しました。
やっぱり、いつかは自分のライブを自分の土地で…まぁ、曲がないで
無理ですけどね(-_-;)

icecastとDDNS、winampとoddcastを、放送用のPCで動かせば
OKでした(^.^)b URLの設定が間違ってたんですねぇ…
今度は、ささぴーさんの方法も試してみようと思います!!
Posted by redzaku at 2007年12月21日 12:20