// BGM.tjs - BGM管理 // Copyright (C)2001-2003, W.Dee 改変・配布は自由です class KAGSoundBuffer { // 各 KAG 用サウンドバッファクラスに、この KAGSoundBuffer // クラスを継承させる var sbclass; // サウンドバッファのクラス var owner; // オーナー var inFadeAndStop = false; // フェード終了時に演奏を停止するか var inFading = false; // フェード中か var prevstatus = "unload"; // 直前のステータス function KAGSoundBuffer(owner, sbclass) { this.sbclass = sbclass; this.owner = owner; } function finalize() { } function fade() { // fade オーバーライド inFading = true; sbclass.fade(...); } function stopFade() { // stopFade オーバーライド sbclass.stopFade(...); inFadeAndStop = false; } function stop() { // stop オーバーライド sbclass.stop(...); inFadeAndStop = false; } function fadeOutAndStop(time, delay = 0) { // 時間 time, delay でフェードアウト後、演奏を停止する inFading = true; inFadeAndStop = true; sbclass.fade(0, time, delay); } function onFadeCompleted() { // フェードが終了した inFading = false; if(inFadeAndStop) { sbclass.stop(); // inFadeAndStop ならば停止 inFadeAndStop = false; } sbclass.onFadeCompleted(...); owner.onSoundBufferFadeCompleted(this); } function onStatusChanged() { sbclass.onStatusChanged(...); var ps = prevstatus; // prev. status var cs = sbclass.status; // current status prevstatus = cs; if(ps == "play" && cs == "stop") owner.onSoundBufferStop(this); // play => stop : 演奏が停止した } } class KAGWaveSoundBuffer extends WaveSoundBuffer, KAGSoundBuffer { function KAGWaveSoundBuffer(owner) { WaveSoundBuffer(owner); KAGSoundBuffer(owner, global.WaveSoundBuffer); } function finalize() { global.KAGSoundBuffer.finalize(...); global.WaveSoundBuffer.finalize(...); } } class KAGMIDISoundBuffer extends MIDISoundBuffer, KAGSoundBuffer { function KAGMIDISoundBuffer(owner) { MIDISoundBuffer(owner); KAGSoundBuffer(owner, global.MIDISoundBuffer); } function finalize() { global.KAGSoundBuffer.finalize(...); global.MIDISoundBuffer.finalize(...); } } class KAGCDDASoundBuffer extends CDDASoundBuffer, KAGSoundBuffer { function KAGCDDASoundBuffer(owner) { CDDASoundBuffer(owner); KAGSoundBuffer(owner, global.CDDASoundBuffer); } function finalize() { global.KAGSoundBuffer.finalize(...); global.CDDASoundBuffer.finalize(...); } } class BGM { var owner; // このオブジェクトを保持する KAGWindow クラスのオブジェクト var type = "Wave"; // BGM タイプ var cdVolume = "xxxx"; // 演奏に使用する CD の ボリュームラベル var doubleBuffered = true; // クロスフェードを行うか var midiInitialMessage = <% %>; // MIDI 初期化メッセージ var buf1; // バッファ 1 var buf2; // バッファ 2 var currentBuffer; // buf1 or buf2 var looping = true; // ループ中かどうか var volume = 100000; // ボリューム var playingStorage; // 演奏中のストレージ var currentStorage; // 見かけの演奏中のストレージ var _enabled; // 有効かどうか function BGM(owner) { this.owner = owner; (BGM_config incontextof this)(); (BGM_config_override incontextof this)() if typeof global.BGM_config_override != "undefined"; createBuffers(); // サウンドバッファを作成 currentStorage = ''; playingStorage = ''; currentBuffer = buf1; _enabled = true; } function finalize() { invalidate buf1 if buf1 !== void; invalidate buf2 if buf2 !== void; // super.finalize(...); } function createBuffers() { // バッファを作成する if(type == "Wave") { buf1 = new KAGWaveSoundBuffer(this); buf2 = new KAGWaveSoundBuffer(this) if doubleBuffered; } else if(type == "MIDI") { buf1 = new KAGMIDISoundBuffer(this); buf2 = new KAGMIDISoundBuffer(this) if doubleBuffered; } else if(type == "CDDA") { buf1 = new KAGCDDASoundBuffer(this); if(doubleBuffered) { throw new Exception("CDDA ではクロスフェードを行えません。" "doubleBuffered = false に設定してください。"); } } buf1.looping = looping; buf2.looping = looping if buf2 !== void; } function freeBuffers() { // バッファを解放する invalidate buf1 if buf1 !== void; invalidate buf2 if buf2 !== void; } function changeDevice(newtype) { // デバイスの変更 var flags = %[]; store(flags); // いったん状態を保存 freeBuffers(); // バッファの解放 type = newtype; createBuffers(); // バッファの作成 restore(flags); // 状態を復帰 } function playBuffer(buf, storage, loop) { // buf に対して演奏を開始させる // 拡張子やドライブ文字が省略された場合、検索を行う var found = true; if(type == "CDDA") { if(storage[1] != ':') { var drive = Storages.searchCD(cdVolume); if(drive == '') { // 使用可能な CD が見つからない dm("ボリュームラベル " + cdVolume + " を持った CD がドライブに入っていません"); return; //------ } else { storage = drive + ':' + storage; } } } else if(type == "Wave") { var test; if(!Storages.isExistentStorage(storage)) { if(test = storage + ".wav", Storages.isExistentStorage(test)) storage = test; else if(test = storage + ".ogg", Storages.isExistentStorage(test)) storage = test; else if(test = storage + ".tcw", Storages.isExistentStorage(test)) storage = test; else found = false; } } else if(type == "MIDI") { var test; if(!Storages.isExistentStorage(storage)) { if(test = storage + ".mid", Storages.isExistentStorage(test)) storage = test; else if(test = storage + ".smf", Storages.isExistentStorage(test)) storage = test; else if(test = storage + ".mdi", Storages.isExistentStorage(test)) storage = test; else found = false; } if(midiInitialMessage.length) MIDISoundBuffer.midiOut(midiInitialMessage); // 初期化メッセージなど } if(_enabled) { if(!found) { dm("BGM " + storage + " が見つかりません"); return; } try { buf.paused = false; buf.looping = loop; // 引数の "loop" buf.open(storage); buf.play(); } catch(e) { dm("BGM " + storage +" を再生できません : " + e.message); return; } } currentBuffer = buf; } function play(elm) { // elm.storage で与えられた BGM の演奏を開始 // elm.loop が true ならばループ再生を行う // buf1 で演奏を行う if(elm.storage == currentStorage) return; // 同じ曲は再演奏しない var loop = elm.loop === void ? true : +elm.loop; try { buf1.stop(); buf2.stop() if buf2 !== void; } catch(e) { dm("BGM の停止に失敗しました(実行は続行できます) : " + e.message); } stopFade(); // フェーディングは終了 buf1.volume = volume; playBuffer(buf1, elm.storage, loop); if(loop) currentStorage = elm.storage; // ループ演奏の場合 else currentStorage = ""; // 単発演奏の場合 playingStorage = elm.storage; looping = loop; } function fadeIn(elm) { // 音量 0 から演奏を開始し、フェードインする var loop = elm.loop === void ? true : +elm.loop; var time = elm.time === void ? 5000 : +elm.time; // stopFade(); try { buf1.stop(); buf2.stop() if buf2 !== void; } catch(e) { dm("BGM の停止に失敗しました(実行は続行できます) : " + e.message); } if(time != 0) buf1.volume = 0; else buf1.volume = volume; playBuffer(buf1, elm.storage, loop); if(_enabled) if(time !=0 ) currentBuffer.fade(volume, time); looping = loop; if(loop) currentStorage = elm.storage; else currentStorage = ""; playingStorage = elm.storage; } function stop() { // 演奏を停止 try { buf1.stop(); buf2.stop() if buf2 !== void; } catch(e) { dm("BGM の停止に失敗しました(実行は続行できます) : " + e.message); } // stopFade(); currentStorage = ""; playingStorage = ""; } function pause() { // 演奏を一時停止 buf1.paused = true; buf2.paused = true if buf2 !== void; } function resume() { // 演奏を再開 buf1.paused = false; buf2.paused = false if buf2 !== void; } function fadeOut(elm) { // フェードアウト // stopFade(); if(_enabled) currentBuffer.fadeOutAndStop(elm.time); currentStorage = ""; } function fade(elm) { // 指定音量までフェード var time = elm.time === void ? 5000 : +elm.time; var vol = +elm.volume * 1000; // stopFade(); if(_enabled) { currentBuffer.fade(vol, time); } volume = vol; } function stopFade() { buf1.stopFade(); buf2.stopFade() if buf2 !== void; } function exchange(elm) { // 曲を入れ替える // 現在演奏中の曲は time で指定された時間をかけてフェードアウトされ、 // storage で指定された曲が time で指定された時間をかけてフェードイン // 再生される。 // time が指定されていなくて、intime と outtime が指定されている場合は、 // intime でフェードイン時間、outime でフェードアウト時間を指定したこと // になる。 // overlap には、現在演奏中の曲がフェードアウトされてから新しい曲が // フェードインされるまでの待ち時間(ただし与える値の正負は逆)で、 // 正の値を指定するとクロスフェードになる。 // storage は、このメソッドが呼ばれた時点で演奏を開始する if(elm.storage == playingStorage) return; // stopFade(); var loop = elm.loop === void ? true : +elm.loop; var intime, outtime; if(elm.time === void) { intime = elm.intime === void ? 5000:+elm.intime; outtime = elm.outtime === void ? 5000:+elm.outtime; } else { intime = outtime = elm.time === void ? 5000 : +elm.time; } var overlap = elm.overlap === void ? 0 : +elm.overlap; if(!_enabled) { // 無効になっている場合は 一応 play を呼んでからそのまま返る play(elm); return; } if(playingStorage == '') { // 現在演奏中の曲がない場合は、単なるフェードインと同じ動作になる elm.time = intime; fadeIn(elm); return; } volume = int(+elm.volume * 1000) if elm.volume !== void; if(doubleBuffered) { // ダブルバッファリングの場合 if(outtime != 0) currentBuffer.fadeOutAndStop(outtime); // 現在演奏中の BGM をフェードアウト else currentBuffer.stop(); var nextbuffer; // 次の演奏バッファ nextbuffer = (currentBuffer == buf1) ? buf2 : buf1; if(intime != 0) nextbuffer.volume = 0; else nextbuffer.volume = volume; playBuffer(nextbuffer, elm.storage, loop); if(intime != 0) nextbuffer.fade(volume, intime, outtime - overlap); } else { // シングルバッファリングの場合 play(elm); // 単に次の曲を演奏するだけ } if(loop) currentStorage = elm.storage; else currentStorage = ""; looping = loop; playingStorage = elm.storage; } function setVolume(vol) { volume = +vol; stopFade(); buf1.volume = volume; buf2.volume = volume if doubleBuffered; } function setOptions(elm) { if(elm.device !== void) { changeDevice(elm.device); } if(elm.volume !== void) { setVolume(int(+elm.volume * 1000)); } if(elm.gvolume !== void) { // 大域ボリューム var sysvar = owner.scflags; // システム(コア)変数 if(sysvar.bgm === void) sysvar.bgm = %[]; sysvar = sysvar.bgm; var v2 = +elm.gvolume; v2 = int(v2 * 1000); sysvar.globalVolume = v2; buf1.volume2 = v2; buf2.volume2 = v2 if doubleBuffered; } } property inFading // フェード中かどうかを返す { getter { return currentBuffer.inFading; } } property canWaitStop // BGM の終了を待てるかどうかを返す { getter { return currentBuffer.status == "play" && !currentBuffer.looping; } } function onSoundBufferFadeCompleted(source) { if(currentBuffer == source) { // フェードが終了した owner.onBGMFadeCompleted(); } } function onSoundBufferStop(source) { if(currentBuffer == source) { // 演奏が終了した playingStorage = ""; currentStorage = ""; owner.onBGMStop(); } } function store() { // 辞書配列に現在の状態を待避 var dic = %[]; dic.currentStorage = currentStorage; dic.volume = volume; return dic; } function restore(dic) { // 辞書配列から現在の状態を復帰 stopFade(); setVolume(dic.volume); if(dic.currentStorage != "") play(%[ storage : dic.currentStorage, loop : true]); else stop(); } function restoreSystemState(dic) { // システム変数 dic から情報を設定する // 大域ボリュームの情報を得る var sysvar = dic.bgm; if(sysvar !== void) { var v2 = sysvar.globalVolume; if(v2 !== void) { v2 = +v2; buf1.volume2 = v2; buf2.volume2 = v2 if doubleBuffered; } } } }