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

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

WEB_AUDIO_API (JavaScript) で AudioBufferSourceNode を利用する際の注意点

少しご無沙汰しておりました。シャチです。
早速、前回の続きから書いていきたいと思います。


ちなみに前回↓
ティラノスクリプトで WEB_AUDIO_API (JavaScript) を利用する - ティラノスクリプトで小説動画を作る


BGMのシームレスなループは「 AudioBufferSourceNodeクラス 」一択!

WEB_AUDIO_APIには3種類の利用方法があり、
その中でシームレスなループ再生が可能なのは
「AudioBufferSourceNodeクラス」を利用する方法だけ

というのが、前回の結論でした。


ただし、この「AudioBufferSourceNodeクラス」というのがなかなかに曲者なのです。


「AudioBufferSourceNodeクラス」は、短い音声ファイル用

前回のサンプルの後に私はこう書きました。

もしうまく行かないようなら「sample.ogg」をもっと軽いファイルにして試してみてください。
(短い効果音のようなものがオススメです)


その理由は「AudioBufferSourceNodeクラス」が短い音声ファイル用のクラスだからです。
このクラスはファイルの読み込みが終わるまで、再生命令を実行できません。
(したところで、命令が空振りに終わるだけです)


ですから、読み込みの完了を待って再生命令を実行するなどの工夫が必要になってきます。
そして、実際に再生するタイミングで読み込みを始めたのでは間に合わないため、事前に読み込んでおく処理も必要になるでしょう。

「AudioBufferSourceNodeクラス」は、再生命令を一回しか受け付けない

困ったことに、「AudioBufferSourceNodeクラス」は再生開始命令を一回しか受け付けません。
一度再生されたデータはメモリから削除されてしまいます。
(公式のリファレンスでは「撃ちっぱなし」と表現していました)


そのため、同じファイルをもう一度再生したいと思った場合、再び同じファイルからAudioBufferSourceNodeを作る必要があります。
(大変面倒です)


この手間を抑えるために、一度読み込んだファイルのArrayBufferを別で取っておき、再生命令に応じてAudioBufferSourceNodeを生成する仕組みが必要になります。
(とっても面倒です)


私の目的としては、ティラノスクリプトの[playbgm]や[playse]と変わらない使用感でシームレスなループ再生を使えるようにすることだったので、これらの実装は不可欠なものでした。


ティラノスクリプトで「AudioBufferSourceNodeクラス」を利用するサンプル

では今回の内容を踏まえて、AudioBufferSourceNodeクラスを使って音声ファイルを再生するサンプルを載せておきます。

用意するもの

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

準備

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

data/scenario/first.ks

[iscript]
// 使い回した方が良い AudioContext
var context = new AudioContext();

// 以下、判りやすくするため事前に変数宣言しておきます。
var arrayBuffer;  // 読み込んだファイルの ArrayBuffer ・・・(1)
var audioBuffer;  // (1) を AudioBufferSourceNode に変換したもの
var source;       // BufferSource

// url から音声ファイルの arrayBuffer を読み込む
function getArrayBuffer(url){
  // 同期処理を Promise を使って実装する
  return new Promise(function(resolve){
    // XMLHttpRequest で sample.ogg を読み込む
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    // データを arraybuffer 形式で読み込む設定
    request.responseType = 'arraybuffer';
    // 読み込みが終わったときに行われる処理
    request.onload = function () {
      // 読み込んだデータを arrayBuffer に入れて保管
      arrayBuffer = request.response;
      // 終わったら次の処理へ
      resolve();
    };
    // 読み込み開始
    request.send();
  });
}
// 読み込んだ arrayBuffer から audioBuffer を作る
function createAudioBuffer(){
  return new Promise(function(resolve){
    // arrayBuffer を audioBuffer に変換する
    context.decodeAudioData(arrayBuffer, function (buf) {
      // 変換したものを audioBuffer に入れて保管
      audioBuffer = buf;
      // 終わったら次の処理へ
      resolve();
    });
  });
}
// 作った audioBuffer を BufferSource にオーディオソースとして登録する
function createBuffer(){
  return new Promise(function(resolve){
    // 新しい BufferSource を作る
    source = context.createBufferSource();
    // audioBuffer を BufferSource に登録する
    source.buffer = audioBuffer;
    // AudioContext と BufferSource を接続
    source.connect(context.destination);
    // 終わったら次の処理へ
    resolve();
  });
}
// 再生する
function playOGG(){
  source.start(0);
}

// ファイルのパス
var SOUND_URL = 'data/bgm/sample.ogg';
// Promise を使って順番に処理していく (今回の重要ポイント)
getArrayBuffer(SOUND_URL).then(createAudioBuffer).then(createBuffer).then(playOGG);
[endscript]


これで、長めの音声ファイルも再生することができるはずです。


次回は音量操作の方法について解説します。
お楽しみに。それでは。

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


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