読者です 読者をやめる 読者になる 読者になる

音の鳴るブログ

鳴らないこともある

Promise を返す decodeAudioData をつくった

draft Web Audio API shim ブームということで、先日つくった StereoPannerNode の shim に引き続いて Promise を返す decodeAudioData をつくった。

decodeAudioData というのはオーディオファイルのバイナリ(ArrayBuffer)をAudioBufferに変換するメソッドでサンプリングベースのなんかそういうのをあれこれするときに使います。

今はこういうインターフェースをしている。

callback DecodeSuccessCallback = void (AudioBuffer decodedData);
callback DecodeErrorCallback = void ();

interface AudioContext : EventTarget {
  void decodeAudioData(
    ArrayBuffer audioData,
    DecodeSuccessCallback successCallback,
    optional DecodeErrorCallback errorCallback
  );
}

で、将来的にはPromiseだろうという感じで、古いインターフェースとの互換性を保ちつつ戻り値をPromiseにして、ついでにエラーコールバックのときに引数がつくような変更が予定されています。

callback DecodeSuccessCallback = void (AudioBuffer decodedData);
callback DecodeErrorCallback = void (DOMException error);

interface AudioContext : EventTarget {
  Promise<AudioBuffer> decodeAudioData(
    ArrayBuffer audioData,
    optional DecodeSuccessCallback successCallback,
    optional DecodeErrorCallback errorCallback
  );
}

Promiseベースのインターフェースは古いインターフェースとの互換性があるので今と同じ使い方を続けるというのもありだけど、他の非同期処理と同期をとりたいとき(Promise.all)などPromiseの文脈で書きたいときに、いちいち調べたり自前でラッピングせずに便利に使えます。

使い方

<script src="/path/to/promise-decode-audio-data.js"></script>

これで AudioContext.prototype.decodeAudioData がPromiseベースに上書きされます。

仕組み

こういう感じ。単純にPromiseラッピングして後方互換用のコールバックを登録しているだけです。

var decodeAudioData = AudioContext.prototype.decodeAudioData;
AudioContext.prototype.decodeAudioData = function(audioData, success, error) {
  var audioContext = this;
  // Promiseでラッピング
  var promise = new Promise(function(resolve, reject) {
    decodeAudioData.call(audioContext, audioData, resolve, reject);
  });
  // 後方互換用のコールバックを登録
  promise.then(success, error);
  // Promiseを返す
  return promise;
};