WEB_AUDIO_API (Javascript) で フェードイン・フェードアウトする方法
今回はWEB_AUDIO_APIでフェードイン・フェードアウトをする方法についてお話しします。
WEB_AUDIO_API でフェードイン・フェードアウトを行うコツ
WEB_AUDIO_APIのGainNodeを使ったフェード操作にはコツがあります。
それは必ず以下の手順を守ることです。
- 再生スケジュールのキャンセル
- 再生スケジュールにフェード終了位置と目標音量を指定
WEB_AUDIO_APIは再生スケジュールというタイムラインを使って演奏を制御しています。
よってフェードインにしろアウトにしろ再生スケジュールに予定を書き込むことで実現するしかありません。
その際、前に入れた予定をキャンセルしておかないとフェードがうまく機能しません。
とても重要なのでそういうものとしてよく覚えておいてください。
サンプル
以上を踏まえてWEB_AUDIO_APIでフェードするサンプルを載せておきます。
準備
「sample.ogg」をゲームプロジェクトの「data/bgm/」フォルダーに配置したら準備完了です。
それではサンプルコードを「data/scenario/」フォルダーの「first.ks」に書いていきます。
data/scenario/first.ks
[iscript] // https://syachi.hatenablog.jp/entry/2019/07/07/174015 に載っけたサンプルを流用しています。 tf.context = new AudioContext(); var arrayBuffer; var audioBuffer; var source; tf.gain; function getArrayBuffer(url){ return new Promise(function(resolve){ var request = new XMLHttpRequest(); request.open('GET', url, true); request.responseType = 'arraybuffer'; request.onload = function () { arrayBuffer = request.response; resolve(); }; request.send(); }); } function createAudioBuffer(){ return new Promise(function(resolve){ tf.context.decodeAudioData(arrayBuffer, function (buf) { audioBuffer = buf; resolve(); }); }); } function createBuffer(){ return new Promise(function(resolve){ source = tf.context.createBufferSource(); tf.gain = tf.context.createGain(); source.buffer = audioBuffer; source.connect(tf.gain); tf.gain.connect(tf.context.destination); resolve(); }); } function playOGG(){ // 冒頭のフェードイン(10秒) // 開始時の音量は 0, 開始位置はタイムラインの現在位置(未再生なら0)を示す context.currentTime tf.gain.gain.linearRampToValueAtTime(0, tf.context.currentTime); // context.currentTime から10秒後に音量 1 となるようにフェードイン tf.gain.gain.linearRampToValueAtTime(1, tf.context.currentTime + 10); // ファイルの先頭から再生開始 source.start(0); } // ファイルのパス var SOUND_URL = 'data/bgm/sample.ogg'; getArrayBuffer(SOUND_URL).then(createAudioBuffer).then(createBuffer).then(playOGG); [endscript] ファイル先頭から10秒かけてフェードイン。[r] [wait time="10000"] 10秒経過。音量[emb exp="tf.gain.gain.value"][r] [iscript] // 予定のキャンセル tf.gain.gain.cancelScheduledValues(0); // context.currentTime から20秒後に音量 0 となるようにフェードアウト tf.gain.gain.linearRampToValueAtTime(0, tf.context.currentTime + 20); [endscript] 次は20秒かけてフェードアウト。音量[emb exp="tf.gain.gain.value"][r] [wait time="14000"] 14秒経過、やっぱりフェードアウトはやめる。音量[emb exp="tf.gain.gain.value"][r] [iscript] // 予定のキャンセル tf.gain.gain.cancelScheduledValues(0); // context.currentTime から5秒後に音量 1 となるようにフェードイン tf.gain.gain.linearRampToValueAtTime(1, tf.context.currentTime + 5); [endscript] (1) 5秒かけてフェードイン。音量[emb exp="tf.gain.gain.value"][r] [wait time="5000"] 5秒経過。音量[emb exp="tf.gain.gain.value"][r] [iscript] // 予定のキャンセル tf.gain.gain.cancelScheduledValues(0); // context.currentTime から20秒後に音量 0 となるようにフェードアウト tf.gain.gain.linearRampToValueAtTime(0, tf.context.currentTime + 20); [endscript] もう一度20秒かけてフェードアウト。音量[emb exp="tf.gain.gain.value"][r] 今度は予定のキャンセルを入れなかったバージョン。[r] [wait time="14000"] 14秒経過、やっぱりフェードアウトはやめる。音量[emb exp="tf.gain.gain.value"][r] [iscript] // 予定のキャンセルは入れない //tf.gain.gain.cancelScheduledValues(0); // context.currentTime から5秒後に音量 1 となるようにフェードイン tf.gain.gain.linearRampToValueAtTime(1, tf.context.currentTime + 5); [endscript] (2) 5秒かけてフェードイン。音量[emb exp="tf.gain.gain.value"][r] [wait time="5000"] 5秒経過。音量[emb exp="tf.gain.gain.value"]
実行時は (1) と (2) の音量に注目してください。
予定をキャンセルしなかった場合だと、フェードインの始まりが遅くなっているのです。
まとめ
最後に今回の要点をまとめましょう。
WEB_AUDIO_APIでフェードを行う際には以下の手順を守ること。
- 再生スケジュールのキャンセル
- 再生スケジュールにフェード終了位置と目標音量を指定
なお、ファイル冒頭のフェードインだけは再生スケジュールのキャンセルが必要ありません。
その代わり、音量を0から始める必要があるのでその設定を先に記述してください。
以上を理解すれば、今日からあなたも音量操作の魔術師です。
本能の赴くままフェードイン・フェードアウトを使いまくってください。
2020/12/09追記
現在の ティラノビルダーver1.86 Chroniumではサンプルがうまく機能しない場合があります。
その際は以下のようにサンプルを修正してみて下さい。
[iscript] // 予定のキャンセル tf.gain.gain.cancelScheduledValues(0); // フェード操作の直前に、現在の音量と再生位置を与える tf.gain.gain.linearRampToValueAtTime(tf.gain.gain.value, tf.context.currentTime); // context.currentTime から20秒後に音量 0 となるようにフェードアウト tf.gain.gain.linearRampToValueAtTime(0, tf.context.currentTime + 20); [endscript]
これでうまくフェードするはずです。