What’s new in Web Audio?
この記事は “What’s new in Web Audio?” の抄訳です。
Web Audio API は以前開発中です。つまり新しいメソッドや属性の追加、名称の変更、入れ替え、そして削除もありうるということです。
この記事では 2015 年初頭の時点からの Web Audio と、その Firefox における実装との変更点についてまとめます。参照しているデモは Firefox の Nightly 版で動作します。リリース版や Developer Edition では対応していない変更点もありますから、ぜひ Nightly 版で試してください。
API の変更
破壊的なもの
DynamicsCompressorNode
の reduction
属性は AudioParam
ではなく float
となりました。その結果、compressor.reduction.value
ではなく compressor.reduction を参照することで、値を読み出せるようになりました。
この値は入力シグナルに対して適用される減衰量を表します。以前よりリードオンリーの値であり、スケジュールされた値の変更が必要ないため、AudioPArame
ではなく float
への変更は妥当なものでしょう。
ブラウザのサポートするのが AudioParam なのか、それとも float なのかは、reduction の value 属性を参照することで判断できます。
if(compressor.reduction.value !== undefined) { // old style } else { // new style }
こちらでは、ドラムループが再生され、それに応じて reduction 属性の値が変化する様子がみられます。この値はトラックの音の大きさに合わせて変化しています。またブラウザがサポートする API のバージョンの判定方法も、併せて確認できます。
新しい属性とメソッド
AudioContext へ追加されたライフサイクル管理用メソッド
AudioContext の生成のコストは高かいため、suspend()
、resume()
、close()
の 3 メソッドが追加されました。
これらのメソッドを利用することで、音声処理を必要になるまでサスペンドしたり、AudioContext
が必要なくなった場合に close()
を呼んでリソースを解放するといったことが可能になりました。
AudioContext
がサスペンドされた場合、音声は再生されません。レジューム時にはサスペンドされた位置から再生されます。詳細については、suspend()
の解説を参照してください。
これらのメソッドの利用例は、こちらのデモで見られます。
正確になった AudioNode の disconnect()
disconnect()
を呼ぶと、接続されている全てのノードから切り離されてしまったため、特定のノードだけを選択的に切断することはできませんでした。
望まれる切断方法が多様化したため、使い方にあわせて disconnect() メソッドをオーバロードできるようになりました。
disconnect()
:全てのノードへの接続を切る(既存の機能)disconnect(outputNumber)
:指定された出力チャンネルから、ノードへの接続を全て切るdisconnect(anotherNode)
:指定されたノードへの接続を全て切るdisconnect(anotherNode, outputNumber)
:このノードのoutputNumber
で指定されたチャンネルから anotherNode への接続を全て切るdisconnect(anotherNode, outputNumber, inputNumber)
:このノードのoutputNumber
で指定されたチャンネルから、anotherNode
のinputNumber
で指定されるチャンネルへの接続を全て切るdisconnect(audioParam)
:このノードから指定されたaudioParam
への接続を全て切るdisconnect(audioParam, outputNumber)
:このノードのoutputNumber
で指定されたチャンネルから、audioParam
への接続を全て切る
これらの切断に関する処理について理解するには、AudioNode
の仕様を読むことを強くお勧めします。また変更が行われた理由については、こちらの議論を参照してください。
OfflineAudioContext への length 属性の追加
この新しい属性の値は、OfflineAudioContext の初期化時に実行されるコンストラクタの引数によって決まります。そのため、その値を別の変数に保存する必要がなくなりました。
var oac = new OfflineAudioContext(1, 1000, 44100); console.log(oac.length); >> 1000
こちらのデモではこの属性の使用例と、ゲインエンベロープを利用した音声の生成例が見られます。
AudioBufferSourceNode
への detune
属性の追加
これは OscillatorNode
の detune
属性と同様のものです。既存の playbackRate
属性よりも正確にサンプリングした音声を調整できます。
PannerNode に AudioParam 型の属性 position と orientation の追加
AudioParam
型の属性が追加されました。これらを利用することで、setPoistion()
や setOrientation()
を呼び続けなくても、それらの変化を自動化することができます。
StereoPannerNode
の pan
属性はすでに AudioParam
になっています。そのため音声のパンニングを扱うノードのは全て、自動的にその位置に関する属性を変更できるようになりました。これはモジュラーシンセを作るのに、とても便利なものたちです。
しかし AudioListener
の位置と向きに関する属性を自動的に変化させることは、まだできません。つまりこれらの値を時間に応じて変化させるには setPosition()
と setOrientation()
を定期的に呼び出す必要があるのです。この問題は Bug #1283029 にファイルされています。
PeriodicWave
の初期値指定
PeriodicWave
オブジェクトを作成する際に、オプションをオブジェクトで渡せるようになりました。
var wave = audioContext.createPeriodicWave(real, imag, { disableNormalization: false });
比較のため、以前の書き方を載せておきます:
var wave = audioContext.createPeriodicWave(real, imag); wave.disableNormalization = false;
将来的には、ノードを作成する全てのメソッドで初期値を渡せるようになります。またコンストラクタも利用できるようになるため、GainNode
の作成もこのように行えます:new GainNode(anAudioContext, {gain: 0.5});
。 これで初期化が必要な Web Audio のコードをより簡潔に書けるようになります。メンテナンスするコード量が減ることは、常にいいことですよね!
新しいノードの追加:IIRFilterNode
BiquadFilterNode
では力不足と感じた場合、IIRFilterNode
を使ってカスタムフィルタを作成すると良いでしょう。
AudioContext
の createIIFilter()
メソッドに、フィードフォワードとフィードバックの二つの係数を配列で与えることで、フィルタを作成できます。
var customFilter = audioContext.createIIRFilter([ 0.1, 0.2, ...], [0.4, 0.3, ...]);
このフィルタノードのパラメータを自動的に変化させることはできません。つまり一度作成したら、その値は変更できません。そのような変化をさせたいのであれば、BiquadFilter
を利用し、AudioParam
で指定できる Q
/ detune
/ frequency
/ gain
の各属性を適切に設定することになります。
これらの違いについては仕様を読むと、より理解できるでしょう。また Digital Filter Design も便利なツールです。ここではフィルタのデザインとフィルタの可視化が可能で、feedforward
と feedback
を含む使用可能なコードが生成されます。
メソッドチェーン
書きやすさのために、いくつかのシンタックスシュガーも追加されました。
connect()
メソッドは接続先のノードを返すようになったため、複数のノードの接続を簡単に書けるようになりました。
以前:
node0.connect(node1); node1.connect(node2); node2.connect(node3);
今:
node0.connect(node1).connect(node2).connect(node3);
また AudioParam
の自動化メソッドも呼び出し先のノードを返すようになったため、メソッドチェーンを行えるようになりました。
以前:
gain.setValueAtTime(0, ac.currentTime); gain.linearRampToValueAtTime(1, ac.currentTime + attackTime);
今:
gain.setValueAtTime(0, ac.currentTime) .linearRampToValueAtTime(1, ac.currentTime + attackTime);
これから
Web Audio ワーキンググループは AudioWorklet
に関する仕様の記述をほぼ終えています。これは AudioWorker
の新しい名前です。これは ScriptProcessorNode
を置き換えることになっています。これは ScriptProcessorNode
が開発者が定義した任意の音声処理を UI スレッドで行うため、パフォーマンスの面で改善の余地があったためです。
AudioWorklet と関連するオブジェクトを定義し、仕様に追加するプルリクエストのマージが最初に行われるでしょう。その後、各ブラウザベンダが AudioWorklet の実装に入ります。
Firefox:パフォーマンスとデバッグにおける改善
3 人のエンジニア(Karl Tomlinson, Daniel Minor and Paul Adenot)の 6 ヶ月以上の奮闘により、Firefox の Web Audio に関するパフォーマンスは改善されました。実践的な面から見ると、音声に関するコードは Chrome よりも同等以上のスピードで動作するようになりました。唯一の例外が AudioParam に関連するもので、そのパフォーマンスはまだ良いとは言えません。
同様にメインスレッドが重たい時によく起きていた ScriptProcessorNode の遅延も少なくなくなりました。これはコンソールエミュレータのようなアプリケーションには大きな改善です。低遅延はエミュレーションの信頼性を高め、その結果として多くのゲームを楽しめるようになるからです。
より深い話としては、DSP カーネルの計算にアセンブラレベルでの最適化が行われました。ARM と x86 の SIMD 命令の長所を利用して、パンニングやゲインの調整といった簡単な計算で、複数の値を並行に計算するようになりました。これによってコードの実行が高速かつ効率的になり、モバイル環境では特に重要なバッテリー消費もより少なくなりました。
また MediaElement ノードで発生する cross-origin エラーが開発ツールのコンソールへ表示されるようにもなりました。以前はこのエラーが報告されないまま失敗するため、コードが止まる理由を考えるのが大変でした。この変更によって、その原因コードの特定が簡単になりました。
これら以外にも多くのバグが修正されました。ここに書くには多すぎるくらいですが、こちらのリストでそれらを確認できます。
Soledad Penadés について
Sole は Mozilla の Developer Relation チームの一員で、Web で素晴らしいものが作られるのを助けています。なかでもリアルタイムに関するものが好みです。irc.mozilla.org の #devrel でコンタクトできます。
- Web サイト:soledadpenades.com
- Twitter:@supersole