656 lines
28 KiB
Text
656 lines
28 KiB
Text
// BGM.tjs - BGM管理
|
|
// Copyright (C)2001-2009, W.Dee and contributors 改変・配布は自由です
|
|
|
|
class KAGSoundBuffer
|
|
{
|
|
// 各 KAG 用サウンドバッファクラスに、この KAGSoundBuffer
|
|
// クラスを継承させる
|
|
|
|
var sbclass; // サウンドバッファのクラス
|
|
var owner; // オーナー
|
|
|
|
var inFadeAndStop = false; // フェード終了時に演奏を停止するか
|
|
var inFadeAndPause = false; // フェード終了時に演奏を一時停止するか
|
|
var inFading = false; // フェード中か
|
|
var prevstatus = "unload"; // 直前のステータス
|
|
|
|
function KAGSoundBuffer(owner, sbclass)
|
|
{
|
|
this.sbclass = sbclass;
|
|
this.owner = owner;
|
|
}
|
|
|
|
function finalize()
|
|
{
|
|
}
|
|
|
|
function fade()
|
|
{
|
|
// fade オーバーライド
|
|
inFading = true;
|
|
inFadeAndStop = false;
|
|
inFadeAndPause = false;
|
|
sbclass.fade(...);
|
|
}
|
|
|
|
function stopFade()
|
|
{
|
|
// stopFade オーバーライド
|
|
sbclass.stopFade(...);
|
|
inFadeAndStop = false;
|
|
inFadeAndPause = false;
|
|
}
|
|
|
|
function stop()
|
|
{
|
|
// stop オーバーライド
|
|
sbclass.stop(...);
|
|
inFadeAndStop = false;
|
|
inFadeAndPause = false;
|
|
}
|
|
|
|
function fadeOutAndStop(time, delay = 0)
|
|
{
|
|
// 時間 time, delay でフェードアウト後、演奏を停止する
|
|
inFading = true;
|
|
inFadeAndStop = true;
|
|
sbclass.fade(0, time, delay);
|
|
}
|
|
|
|
function fadeOutAndPause(time, delay = 0)
|
|
{
|
|
// 時間 time, delay でフェードアウト後、演奏を停止する
|
|
inFading = true;
|
|
inFadeAndPause = true;
|
|
sbclass.fade(0, time, delay);
|
|
}
|
|
|
|
function onFadeCompleted()
|
|
{
|
|
// フェードが終了した
|
|
inFading = false;
|
|
if(inFadeAndStop)
|
|
{
|
|
sbclass.stop(); // inFadeAndStop ならば停止
|
|
inFadeAndStop = false;
|
|
} else if (inFadeAndPause) {
|
|
sbclass.paused = true; // inFadeAndPause ならば一時停止
|
|
inFadeAndPause = 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(...);
|
|
}
|
|
|
|
function onLabel(label) {
|
|
owner.owner.onBGMLabel(label);
|
|
}
|
|
}
|
|
|
|
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, paused = false, start)
|
|
{
|
|
// 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;
|
|
@if(kirikiriz)
|
|
else if(test = storage + ".opus", Storages.isExistentStorage(test))
|
|
storage = test;
|
|
@endif
|
|
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.looping = loop; // 引数の "loop"
|
|
buf.open(storage);
|
|
// ごう追加 再生位置制御
|
|
if (start !== void &&
|
|
buf.labels !== void &&
|
|
(start = buf.labels[start]) !== void &&
|
|
(start = start.samplePosition) !== void) {
|
|
buf.samplePosition = start;
|
|
}
|
|
buf.paused = paused;
|
|
buf.play();
|
|
}
|
|
catch(e)
|
|
{
|
|
dm("BGM " + storage +" を再生できません : " + e.message);
|
|
return;
|
|
}
|
|
}
|
|
currentBuffer = buf;
|
|
}
|
|
|
|
function play(elm)
|
|
{
|
|
// elm.storage で与えられた BGM の演奏を開始
|
|
// elm.loop が true ならばループ再生を行う
|
|
// elm.paused が true ならばポーズされた状態で再生を開始する
|
|
// buf1 で演奏を行う
|
|
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, elm.paused ? true : false, elm.start);
|
|
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, false, elm.start);
|
|
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 fadePause(elm)
|
|
{
|
|
if(_enabled) currentBuffer.fadeOutAndPause(elm.time);
|
|
}
|
|
|
|
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, false, elm.start);
|
|
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;
|
|
if (sysvar.enable === void || sysvar.enable) {
|
|
buf1.volume2 = v2;
|
|
buf2.volume2 = v2 if doubleBuffered;
|
|
} else {
|
|
buf1.volume2 = 0;
|
|
buf2.volume2 = 0 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.paused = buf1.paused;
|
|
dic.volume = volume;
|
|
return dic;
|
|
}
|
|
|
|
function restore(dic)
|
|
{
|
|
// 辞書配列から現在の状態を復帰
|
|
stopFade();
|
|
setVolume(dic.volume);
|
|
if(dic.currentStorage != "") {
|
|
play(%[ storage : dic.currentStorage, loop : true, paused: dic.paused]);
|
|
} else
|
|
stop();
|
|
}
|
|
|
|
function restoreSystemState(dic)
|
|
{
|
|
// システム変数 dic から情報を設定する
|
|
// 大域ボリュームの情報を得る
|
|
var sysvar = dic.bgm;
|
|
if(sysvar !== void)
|
|
{
|
|
if (sysvar.enable === void || sysvar.enable) {
|
|
var v2 = sysvar.globalVolume;
|
|
if(v2 !== void) {
|
|
v2 = +v2;
|
|
buf1.volume2 = v2;
|
|
buf2.volume2 = v2 if doubleBuffered;
|
|
}
|
|
} else {
|
|
buf1.volume2 = 0;
|
|
buf2.volume2 = 0 if doubleBuffered;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// true の場合はボリューム補正する
|
|
var _voldown;
|
|
|
|
property voldown {
|
|
getter() {
|
|
return _voldown;
|
|
}
|
|
setter(v) {
|
|
_voldown = v;
|
|
if (_enabled && currentStorage != "") {
|
|
if (_voldown) {
|
|
currentBuffer.fade(volume * 0.5, 300);
|
|
} else {
|
|
currentBuffer.fade(volume, 300);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function setLoop(loop) {
|
|
if(_enabled) {
|
|
currentBuffer.looping = loop;
|
|
// ループするときはフラグ0を0にしておくルールに!
|
|
currentBuffer.flags[0] = loop ? 0 : 1;
|
|
}
|
|
looping = loop;
|
|
}
|
|
|
|
}
|
|
|