Web Audio API の仕様は日々 GitHub上で議論 されていて、ある程度同意が得られたものは ドラフトページ で公開されています。仕様のブラッシュアップのほかに追加される API や変更のある API があり、それらはちらほらとブラウザに実装もされてきているので、簡単な説明とサポート状況、ポリフィルについてまとめました。
この記事に書いてあるポリフィルは下記のリポジトリにまとめてあります。
また、各API対応状況は下記のページでも確認できます。
http://mohayonao.github.io/web-audio-api-shim/test/impl.html
http://compatibility.shwups-cms.ch/en/home
Microsoft Edge / Safari 9 の対応状況が調べられていないので、どなたか情報ください!!
新しく追加されるAPI
AnalyserNode#getFloatTimeDomainData
信号データを Float32Array で取得する。これまで波形データは getByteTimeDomainData
を使って Uint8Array で取得するしかできなかった。
Interface
AnalyserNode : AudioNode {
getFloatTimeDomainData(
array: Float32Array
): void;
}
-
array
結果を格納する配列
Support
Chrome | Opera | Firefox | Safari | Edge |
---|---|---|---|---|
37 | 22 | 30 | 8 |
Polyfill
getByteTimeDomainData
で uint8 型のデータを取得して変換した値を array に代入する。
var uint8 = new Uint8Array(2048);
function getFloatTimeDomainData(array) {
this.getByteTimeDomainData(uint8);
for (var i = 0, imax = array.length; i < imax; i++) {
array[i] = (uint8[i] - 128) * 0.0078125;
}
}
if (!AnalyserNode.prototype.hasOwnProperty("getFloatTimeDomainData")) {
AnalyserNode.prototype.getFloatTimeDomainData = getFloatTimeDomainData;
}
AudioBuffer#copyFromChannel / copyToChannel
AudioBuffer のデータを部分的に読み書きする。
以前の Firefox では getChannelData
を使うとバッファの全データをオーディオスレッドからコピーするため、例えば 1分 のバッファの場合 105864000 (= 44100 * 60 * 4) バイトのデータをコピーする必要があったり効率が悪かったのでこういうAPIを作った (という記憶がある) のだけど、今確認したらそうでもなさそうで良く分からないです。
とはいえ、AudioBuffer のデータを部分的に読み書きしたいケースはあまりないと思う。
Interface
AudioBuffer {
copyFromChannel(
destination: Float32Array,
channelNumber: number,
startInChannel: number = 0
): void;
}
-
destination
コピーした結果を格納する配列 -
channelNumber
コピー対象のチャネル -
startInChannel
コピー元の開始インデックス
AudioBuffer {
copyToChannel(
source: Float32Array,
channelNumber: number,
startInChannel: number = 0
): void;
}
-
source
コピーする配列 -
channelNumber
コピー対象のチャネル -
startInChannel
コピー先の開始インデックス
Support
Chrome | Opera | Firefox | Safari | Edge |
---|---|---|---|---|
43 | 30 | 27 | 8 |
Polyfill
function copyFromChannel(destination, channelNumber, startInChannel) {
var source = this.getChannelData(channelNumber|0).subarray(startInChannel|0);
var minLength = Math.min(source.length, destination.length);
destination.set(source.subarray(0, minLength));
}
function copyToChannel(source, channelNumber, startInChannel) {
var minLength= Math.min(source.length, this.length - (startInChannel|0));
var clipped = source.subarray(0, minLength);
this.getChannelData(channelNumber|0).set(source, startInChannel|0);
}
if (!AudioBuffer.prototype.hasOwnProperty("copyFromChannel")) {
AudioBuffer.prototype.copyFromChannel = copyFromChannel;
}
if (!AudioBuffer.prototype.hasOwnProperty("copyToChannel")) {
AudioBuffer.prototype.copyToChannel = copyToChannel;
}
AudioContext#createStereoPanner
ステレオパンナー。pan
属性で左右の位置を設定したりスケジューリングしたり、OscillatorNode と組み合わせてオートパンなどが簡単にできる。
いままではなぜか 3D空間上の定位 (空間のどこに音源があって、聴く人はどこにいるのか、どっちを向いているのか) しかなかった。
Interface
AudioContext {
createStereoPanner(): StereoPannerNode;
}
StereoPannerNode : AudioNode {
pan: AudioParam;
}
Support
Chrome | Opera | Firefox | Safari | Edge |
---|---|---|---|---|
41 | 28 | 37 | 8 |
Polyfill
var StereoPannerNode = require("stereo-panner-node");
function createStereoPanner() {
return new StereoPannerNode(this);
}
if (!AudioContext.prototype.hasOwnProperty("createStereoPanner")) {
AudioContext.prototype.createStereoPanner = createStereoPanner;
}
AudioContext#suspend / resume / close
オーディオコンテキストの 停止 / 再開 / 終了 をする。コンテキストの現在の状態は state
属性で取得でき、変更があった場合は onstatechange
が呼ばれる。
各コンテキストの状態遷移は以下のとおり。
new AudioContext()
|
V
+------------+ +------------+
| state: | -- suspend() -> | state: |
| running | <- resume() --- | suspended |
+------------+ +------------+
| |
| close() | close()
+------------------------------+
|
V
+-----------+
| state: |
| closed |
+-----------+
new OfflineAudioContext(...args);
|
V
+------------+
| state: |
| suspended |
+------------+
|
| startRendering()
V
+------------+
| state: |
| running |
+------------+
|
| < rendering completed >
V
+------------+
| state: |
| closed |
+------------+
Interface
AudioContext {
state: string;
onstatechange: EventHandler;
suspend(): Promise<void>;
resume(): Promise<void>;
close(): Promise<void>;
}
Support
API | Chrome | Opera | Firefox | Safari | Edge |
---|---|---|---|---|---|
suspend | 41 | 28 | 38 | 8 | |
resume | 41 | 28 | 38 | 8 | |
close | 42 | 29 | 38 | 8 |
Polyfill
複雑なため省略
変更のあったAPI
AudioContext#decodeAudioData
Promise ベースになる。今までのコールバックベースとの互換性はある。
API の変更とは関係ないけど Firefox の場合、audioData は transferred されて空になるので注意が必要です。
Interface
AudioContext {
decodeAudioData(
audioData: ArrayBuffer,
successCallback: function = null,
errorCallback: function = null
): Promise<AudioBuffer>;
}
-
audioData
オーディオデータ -
successCallback
成功したときのコールバック (後方互換用) -
errorCallback
失敗したときのコールバック (後方互換用)
Support
Chrome | Opera | Firefox | Safari | Edge |
---|---|---|---|---|
43 | 30 | 36 | 8 |
Polyfill
var originalDecodeAudioData = AudioContext.prototype.decodeAudioData;
var isPromiseBased = (function() {
var audioContext = new OfflineAudioContext(1, 2, 44100);
return !!audioContext.decodeAudioData(new Uint8Array(0).buffer, function() {});
})();
function decodeAudioData(audioData, successCallback, errorCallback) {
var _this = this;
var promise = new Promise(function(resolve, reject) {
originalDecodeAudioData.call(_this, audioData, resolve, reject);
});
promise.then(successCallback, errorCallback);
return promise;
};
if (!isPromiseBased) {
AudioContext.prototype.decodeAudioData = decodeAudioData;
}
OfflineAudioContext#startRendering
Promise ベースになる。今までのコールバックベースとの互換性はある。
Interface
OfflineAudioContext {
startRendering(): Promise<AudioBuffer>;
}
Support
Chrome | Opera | Firefox | Safari | Edge |
---|---|---|---|---|
42 | 29 | 37 | 8 |
Polyfill
var originalStartRendering = OfflineAudioContext.prototype.startRendering;
var isPromiseBased = (function() {
var audioContext = new OfflineAudioContext(1, 2, 44100);
return !!audioContext.startRendering();
})();
function startRendering () {
var _this = this;
return new Promise(function(resolve, reject) {
var oncomplete = _this.oncomplete;
_this.oncomplete = function(e) {
resolve(e.renderedBuffer);
if (typeof oncomplete === "function") {
oncomplete.call(_this, e);
}
};
originalStartRendering.call(_this);
});
}
if (!isPromiseBased) {
OfflineAudioContext.prototype.startRendering = startRendering;
}
AudioNode#disconnect
これまでは指定した出力チャネルの全ノードを切断するしかできなかったけど、特定のノードだけ切断するなど、より細かい制御が可能になる。
Interface
AudioNode {
// 全チャネルの全ノードを切断 (今までの disconenct() とは異なる)
disconnect(): void;
// 指定した出力チャネルの全ノードを切断 (今までと同じ)
disconnect(output: number): void;
// 指定したノードを切断
disconnect(destination: AudioNode|AudioParam): void;
// 指定した出力チャネルの指定したノードを切断
disconnect(destination: AudioNode|AudioParam, output: number): void;
// 指定した出力チャネルの指定したノードの指定した入力チャネルを切断
disconnect(destination: AudioNode, output: number, input: number): void;
}
Support
Chrome | Opera | Firefox | Safari | Edge |
---|---|---|---|---|
43 | 30 | 38 | 8 |
Polyfill
複雑なため省略
その他
-
AudioBufferSource
やConvolverNode
でbuffer
の再代入ができなくなる- 今は chrome だと warning が出るだけ (Convolverでは出ないけど)
-
ScriptProcessorNode
が廃止され Worker ベースのAudioWorkerNode
が導入される- 普通の
AudioNode
と同じように扱えるカスタムノードが生成できる
- 普通の