サークル燃えないゴミ仮拠点

サークル代表師路射地の連絡所代わりのブログ

WEB_AUDIO_API (JavaScript) で 音量操作をする方法

こんにちは、シャチです。
前回までのサンプルで WEB_AUDIO_API における音声ファイル再生の基礎は解説しました。


ここからは基本的な機能の実装方法を書いていきたいと思います。
今回はWEB_AUDIO_API で音量操作をする方法についてです。

WEB_AUDIO_API では AudioContext と BufferSource を接続しないと音が出ない

今まで見せてきたサンプルにこのようなコードがあったかと思います。

// AudioContext と BufferSource を接続
source.connect(context.destination);


たった一行のコードですが、コレがないといくら頑張っても音は鳴りません。

AudioContext ってそもそも何?

私もよく分かりませんが、どこのサイトでも言われているのが以下の2点です。


色々読んで私なりに解釈した結果、
「音の生成や音声ファイルのデコード系の処理を行うオブジェクト」
という理解に及びました。


ティラノスクリプトで使う分にはそんぐらいの認識で構わないと思います。


BufferSource ってそもそも何?

私もよく分かりませんが、とにかくAudioContext.destination を BufferSource.connect の引数に指定すると音が出るようになると覚えておけば良いと思います。


私の中ではオーディオ機器におけるスピーカーみたいな存在として理解されています。
その考えで行くと、AudioContext は音源みたいな存在ですかね。


音源(AudioContext)とスピーカー(BufferSource)をつなぐ(connect)と音が鳴る。
こう覚えて問題は無いと思います。


どうやって音量操作を実現する?

さて、ここからが今回の本題です。
WEB_AUDIO_API では音源とスピーカーを繋いだだけでは音量操作ができません。


この2つの間に、「GainNode」というものを挟む必要があります。
GainNodeは音量操作を実現するための部品と理解しておけば問題ないでしょう。


「音源→GainNode→スピーカー」
このように繋いで、音量操作したいときにGainNodeに命令を出せば音量操作が実現できます。
(詳しいつなぎ方については今回のサンプルをご覧ください)


音源とスピーカーは最終的に繋がっていれば良いので、GainNodeを挟んでも音が出ます。


GainNode で音量操作するサンプル

では今回の内容を踏まえて、GainNodeで音量を操作するサンプルを載せておきます。

用意するもの

  • ティラノスクリプトの新しいゲームプロジェクト
  • 音声ファイル(名前を「sample.ogg」にしておきます)

準備

「sample.ogg」をゲームプロジェクトの「data/bgm/」フォルダーに配置したら準備完了です。
それではサンプルコードを「data/scenario/」フォルダーの「first.ks」に書いていきます。

data/scenario/first.ks

[iscript]
// 使い回した方が良い AudioContext. iscript をまたいで扱うため、一時変数に格納する
tf.context = new AudioContext();

// 以下、判りやすくするため事前に変数宣言しておきます。
var arrayBuffer;  // 読み込んだファイルの ArrayBuffer ・・・(1)
var audioBuffer;  // (1) を AudioBufferSourceNode に変換したもの
var source;       // BufferSource
tf.gain;           // GainNode. iscript をまたいで扱うため、一時変数に格納する

// url から音声ファイルの arrayBuffer を読み込む
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();
  });
}
// 読み込んだ arrayBuffer から audioBuffer を作る
function createAudioBuffer(){
  return new Promise(function(resolve){
    tf.context.decodeAudioData(arrayBuffer, function (buf) {
      audioBuffer = buf;
      resolve();
    });
  });
}
// 作った audioBuffer を BufferSource にオーディオソースとして登録する
function createBuffer(){
  return new Promise(function(resolve){
    source = tf.context.createBufferSource();
    // 新しい GainNode を作る
    tf.gain = tf.context.createGain();
    source.buffer = audioBuffer;
    // BufferSource と GainNode を接続
    source.connect(tf.gain);
    // GainNode と AudioContext を接続
    tf.gain.connect(tf.context.destination);
    resolve();
  });
}
// 再生する
function playOGG(){
  // 再生開始時の音量を指定。音量の幅は 0 ~ 1 の間。1 以上にすると音が割れる。
  tf.gain.gain.value = 1;
  source.start(0);
}

// ファイルのパス
var SOUND_URL = 'data/bgm/sample.ogg';
getArrayBuffer(SOUND_URL).then(createAudioBuffer).then(createBuffer).then(playOGG);
[endscript]
;十秒後、音量をいきなり十分の一にする
[wait time="10000"]
十秒経過。[r]
[emb exp="tf.context.currentTime"][r]
[iscript]
tf.gain.gain.setValueAtTime(0.1, tf.context.currentTime);
[endscript]
;そのまた十秒後、今度は十秒かけてゆっくりと音量を戻す
[wait time="10000"]
十秒経過。[r]
[emb exp="tf.context.currentTime"][r]
[iscript]
// フェード開始位置と開始時の音量を指定する。これがないとちゃんとしたフェードにならない。
tf.gain.gain.linearRampToValueAtTime(tf.gain.gain.value, tf.context.currentTime);
// フェード終了位置と終了時の音量を指定する。位置の指定は秒単位で行う。この場合は 10 秒。
tf.gain.gain.linearRampToValueAtTime(1, tf.context.currentTime + 10);
[endscript]
[wait time="10000"]
十秒経過。[r]
[emb exp="tf.context.currentTime"][r]


これで音量操作ができているはずです。


今回のサンプルは「フェードの基本的な方法」と「iscript をまたいで変数を使い回す方法」も使っています。
context と gain の前に tf. と付いているのを見落とさないでください。


次回はいよいよBGMのシームレスループの解説に入ります。
お楽しみに。それでは。

P.S.私が作った最新の動画です。シームレスなループも使われています。ぜひご覧ください。


【第十話】優しい騎士と小さな魔法使い【後編】―動画版