Web Audio API

Web Audio APIはWeb上で音声を扱うための強力で多機能なシステムを提供します。これにより開発者はオーディオソースを選択したり、エフェクトを加えたり、ビジュアライゼーションを加えたり、パンニングなどの特殊効果を適用したり、他にもたくさんのいろいろなことができるようになります。

Web audio の概念と利用方法

Web Audio API は音声操作をオーディオコンテキスト内の操作として実現し、モジュラールーティングできるようにデザインされています。基本的な操作は オーディオノードとして表現されています。これを接続することで、オーディオグラフを作成します。 チャンネル構成の異なる複数の音源も 1 つのコンテキスト内で扱えます。この構成によって、複雑で動的な音声操作を実現できるようになっています。

オーディオノードは入力と出力を接続され、一つや多数のソースからいくつかのノードを辿ってdestinationに到達するチェインを形成します。(音声の可視化を行う場合など、 destination へは到達しないこともあります) 通常のWeb Audioの使い方は次のようになります:

  1. オーディオコンテキストを作成する
  2. コンテキストの中で、<audio>,オシレーター,ストリームなどのソースを作成する
  3. リバーブ・フィルター・パンナー・コンプレッサーなどのエフェクトノードを作成する
  4. 最終的な音声の到達先を選ぶ(例えばスピーカー)
  5. ソースをエフェクトに繋げ、エフェクトを到達先(destination)に繋げる

A simple box diagram with an outer box labeled Audio context, and three inner boxes labeled Sources, Effects and Destination. The three inner boxes have arrow between them pointing from left to right, indicating the flow of audio information.

タイミングは高精度で低遅延に制御されます。正確にイベントに反応したり特定の音声サンプルにアクセスしたりすることができます。ドラムマシンやシーケンサーのようなアプリケーションを作ることができます。

Web Audio API は立体音響も扱えます。 source-listener モデル に基づいたシステムを利用し、パンニングモデルをコントロールできます。また距離に基づく音の減衰や、ドップラー効果も扱えます。

付記: Web Audio API の理論に関する詳細は Basic concepts behind Web Audio API をご覧ください。

Web Audio API インタフェース

Web Audio API は全部で28のインタフェースと関連するイベントを持ちます。それらは機能的に9個のカテゴリに分けられます。

一般的なオーディオグラフの定義

Web Audio API で利用するオーディオグラフのコンテナと、その構成要素は以下の通りです。

AudioContext
音声モジュールを組み合わせて作成される、音声処理のグラフを表します。グラフ内の各モジュールは AudioNode として表現されています。オーディオコンテキストは、コンテキスト内での処理を担当するノードの作成を行います。
AudioNode
AudioNode インタフェースは音声処理のモジュールの表現しています。これには<audio>要素や<video> 要素のような音源、音声の出力先、BiquadFilterNodeGainNode) のようなフィルタなどが含まれます。
AudioParam
AudioParam インタフェースは AudioNode の持つような、音声に関するパラメータを表現しています。値をセットするだけでなく、差分を指定することも可能です。また指定した時間やパターンで、値を変更をすることもできます。
ended (event)
ended イベントは、再生が終了した際に発火するイベントです。

音源

Web Audio API 内で利用できる音源は以下の通りです。

OscillatorNode
OscillatorNode はサイン波を出力する AudioNode です。出力する波形の周波数を指定できます。
AudioBuffer
 AudioBuffer はメモリ上に展開された短い音声データを表します。AudioContext.createBuffer() メソッドによって、音声ファイルから作成されます。このデータは AudioBufferSourceNode を利用して再生されます。
AudioBufferSourceNode
AudioBufferSourceNodeAudioNode の一種で、メモリ上の音声データを利用した音源です。音声データ自身は AudioBuffer として保存されています。
MediaElementAudioSourceNode
MediaElementAudioSourceNodeAudioNode の一種で、<audio> 要素や <video> 要素を利用する音源です。
MediaStreamAudioSourceNode
MediaStreamAudioSourceNodeAudioNode の一種で、マイクやWebカメラといった WebRTC MediaStream からの入力を扱える音源です。

オーディオエフェクトフィルター

これらを利用すると、音源からの音声にエフェクトをかけられます。

BiquadFilterNode
BiquadFilterNode AudioNode の一種で、単純な低次フィルタです。フィルタやトーンコントロール、グラフィックイコライザで利用されます。BiquadFilterNode の入力と出力はともに 1 つです。
ConvolverNode
ConvolverNode AudioNode の一種で、Audiobuffer に対して線形畳み込みを行います。リバーブの実現に利用されます。
DelayNode
DelayNode AudioNode の一種で、delay-line を表します。入力された音声を、遅らせて出力します。
DynamicsCompressorNode
DynamicsCompressorNode はコンプレッサとして働きます。大きな音の音量を絞ることで、複数の音を同時に再生した時に起きがちな、音のクリッピングや歪みを回避します。
GainNode
GainNode AudioNode の一種で、入力された音の音量を指定されたものに変更して出力します。
WaveShaperNode
WaveShaperNode AudioNode の一種で、非線形のディストーションエフェクトを実現します。curve 属性に指定された関数を用いて、入力を歪ませます。音を歪ませ、温かみを与えるために用いられます。
PeriodicWaveNode
OscillatorNode の出力の波形を変えるために用いられます。

音声の出力先

処理した音声の出力先を、以下のもので定めます。

AudioDestinationNode
AudioDestinationNode はコンテキスト内での出力先を表します。通常はスピーカとなっています。
MediaStreamAudioDestinationNode
MediaElementAudioSourceNode は音声の出力先となる AudioNode の一種で、WebRTC MediaStream と1 つの AudioMediaStreamTrack から構成されます。Navigator.getUserMedia で取得された MediaStream と同様に扱えます。

分析と可視化

音声の時間領域 / 周波数領域分析には、AnalyserNode を利用します。

AnalyserNode
AnalyserNode を利用すると、音声のリアルタイムに時間領域分析と周波数領域分析が行えます。これを利用すると、音声の可視化が行えます。

オーディオチャンネルの分岐と合成

オーディオチャンネルを分岐したり合成したりするのにこれらのインターフェースを使います。

ChannelSplitterNode
The ChannelSplitterNode はオーディオソースの複数のチャンネルを別々のモノラル出力へ分離します。
ChannelMergerNode
ChannelMergerNode は異なるモノラルの入力を、1 つの出力へとまとめます。それぞれの入力は、出力内のチャンネルとなります。

立体音響

以下を利用すると、立体音響を実現できます。

AudioListener
AudioListener は聴者の向きと位置を表します。
PannerNode
PannerNode AudioNode の一種で、空間内での音の振る舞いを規定します。位置はカルテシアンの右手座標系で表され、速度ベクトルで動きを表します。向きはコーンの向きで表現します。

JavaScript による音声処理

スクリプトを利用して音声を処理する場合は、以下のノードとイベントが利用できます。

ScriptProcessorNode
ScriptProcessorNode を利用すると、JavaScript から音声データの生成、処理、分析を行えます。このノードは AudioNode の一種で、入力と出力の二つのバッファとリンクしています。入力バッファに新しいデータがセットされる度に AudioProcessingEvent インタフェースを実装したイベントが生起します。イベントハンドラは出力バッファにデータをセットして処理を終了します。
audioprocess (event)
audioprocess イベントは ScriptProcessorNode の処理が可能になった際に発火します。
AudioProcessingEvent
AudioProcessingEventScriptProcessorNode の入力バッファが処理可能になったことを表すイベントです。

オフライン / バックグラウンドでの処理

以下を利用すると、音声を出力することなく処理できます。

OfflineAudioContext
OfflineAudioContextAudioContext の一種で、AudioNode を組み合わせて、音声処理を行うグラフを表現しています。通常の AudioContext と異なりOfflineAudioContext は音声を出力せず、バッファ内で高速に処理を行います。
complete (event)
complete イベントは OfflineAudioContext の処理が終了した時に発火します。
OfflineAudioCompletionEvent
OfflineAudioCompletionEventOfflineAudioContext の処理が終了したことを表します。complete イベントは、これを実装しています。

Audio Workers

Audio workers を利用すると web worker のコンテキストで音声処理をおこなえます。Audio Workers は比較的新しいいくつかのインタフェース (2014 年 8 月 29 日に定義)によって定義されているため、これを実装したブラウザはまだありません。実装が完了すると、 ScriptProcessorNode, と JavaScript による音声処理 で述べた機能を置き換えることとなります。

AudioWorkerNode
AudioWorkerNode は AudioNode の一種で、ワーカースレッド内で動作し、音声の生成、処理、分析を行います。
AudioWorkerGlobalScope
AudioWorkerGlobalScopeDedicatedWorkerGlobalScope 由来のオブジェクトで、音声処理のスクリプトが動作するワーカーコンテキストを定義します。音声の生成、処理、分析がワーカースレッド内で行えるように設計されています。
AudioProcessEvent
AudioWorkerGlobalScope オブジェクトへ送信されるイベントオブジェクトです。

廃止されたインタフェース

以下のものは、 Web Audio API の古い仕様には存在しましたが、現在は廃止され、別のものに置き換えられています。

JavaScriptNode
JavaScript で音声を直接処理できます。廃止され、 ScriptProcessorNode に置き換えられています。
WaveTableNode
定期的な波形変換を行います。廃止され PeriodicWaveNode に置き換えられています。

次の例は Web Audio API で定められている様々な機能を利用して実装したボイスチェンジャです。動作しているデモは Voice-change-o-matic でご覧になれます。また全ソースコードは Github でご覧になれます。スピーカの音量を絞ってデモを起動してください。

Web Audio API を利用している行がハイライトされています。各メソッドの詳細については、インタフェースのページを探してください。

var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); // define audio context
// Webkit/blink browsers need prefix, Safari won't work without window.

var voiceSelect = document.getElementById("voice"); // select box for selecting voice effect options
var visualSelect = document.getElementById("visual"); // select box for selecting audio visualization options
var mute = document.querySelector('.mute'); // mute button
var drawVisual; // requestAnimationFrame

var analyser = audioCtx.createAnalyser();
var distortion = audioCtx.createWaveShaper();
var gainNode = audioCtx.createGain();
var biquadFilter = audioCtx.createBiquadFilter();

function makeDistortionCurve(amount) { // function to make curve shape for distortion/wave shaper node to use
  var k = typeof amount === 'number' ? amount : 50,
    n_samples = 44100,
    curve = new Float32Array(n_samples),
    deg = Math.PI / 180,
    i = 0,
    x;
  for ( ; i < n_samples; ++i ) {
    x = i * 2 / n_samples - 1;
    curve[i] = ( 3 + k ) * x * 20 * deg / ( Math.PI + k * Math.abs(x) );
  }
  return curve;
};

navigator.getUserMedia (
  // constraints - only audio needed for this app
  {
    audio: true
  },

  // Success callback
  function(stream) {
    source = audioCtx.createMediaStreamSource(stream);
    source.connect(analyser);
    analyser.connect(distortion);
    distortion.connect(biquadFilter);
    biquadFilter.connect(gainNode);
    gainNode.connect(audioCtx.destination); // connecting the different audio graph nodes together

    visualize(stream);
    voiceChange();

  },

  // Error callback
  function(err) {
    console.log('The following gUM error occured: ' + err);
  }
);

function visualize(stream) {
  WIDTH = canvas.width;
  HEIGHT = canvas.height;

  var visualSetting = visualSelect.value;
  console.log(visualSetting);

  if(visualSetting == "sinewave") {
    analyser.fftSize = 2048;
    var bufferLength = analyser.frequencyBinCount; // half the FFT value
    var dataArray = new Uint8Array(bufferLength); // create an array to store the data

    canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);

    function draw() {

      drawVisual = requestAnimationFrame(draw);

      analyser.getByteTimeDomainData(dataArray); // get waveform data and put it into the array created above

      canvasCtx.fillStyle = 'rgb(200, 200, 200)'; // draw wave with canvas
      canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);

      canvasCtx.lineWidth = 2;
      canvasCtx.strokeStyle = 'rgb(0, 0, 0)';

      canvasCtx.beginPath();

      var sliceWidth = WIDTH * 1.0 / bufferLength;
      var x = 0;

      for(var i = 0; i < bufferLength; i++) {

        var v = dataArray[i] / 128.0;
        var y = v * HEIGHT/2;

        if(i === 0) {
          canvasCtx.moveTo(x, y);
        } else {
          canvasCtx.lineTo(x, y);
        }

        x += sliceWidth;
      }

      canvasCtx.lineTo(canvas.width, canvas.height/2);
      canvasCtx.stroke();
    };

    draw();

  } else if(visualSetting == "off") {
    canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
    canvasCtx.fillStyle = "red";
    canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
  }

}

function voiceChange() {
  distortion.curve = new Float32Array;
  biquadFilter.gain.value = 0; // reset the effects each time the voiceChange function is run

  var voiceSetting = voiceSelect.value;
  console.log(voiceSetting);

  if(voiceSetting == "distortion") {
    distortion.curve = makeDistortionCurve(400); // apply distortion to sound using waveshaper node
  } else if(voiceSetting == "biquad") {
    biquadFilter.type = "lowshelf";
    biquadFilter.frequency.value = 1000;
    biquadFilter.gain.value = 25; // apply lowshelf filter to sounds using biquad
  } else if(voiceSetting == "off") {
    console.log("Voice settings turned off"); // do nothing, as off option was chosen
  }

}

// event listeners to change visualize and voice settings

visualSelect.onchange = function() {
  window.cancelAnimationFrame(drawVisual);
  visualize(stream);
}

voiceSelect.onchange = function() {
  voiceChange();
}

mute.onclick = voiceMute;

function voiceMute() { // toggle to mute and unmute sound
  if(mute.id == "") {
    gainNode.gain.value = 0; // gain set to 0 to mute sound
    mute.id = "activated";
    mute.innerHTML = "Unmute";
  } else {
    gainNode.gain.value = 1; // gain set to 1 to unmute sound
    mute.id = "";    
    mute.innerHTML = "Mute";
  }
}

仕様

Specification Status Comment
Web Audio API 草案  

ブラウザ互換性

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
Basic support 14 webkit 23 未サポート 15 webkit
22 (unprefixed)
6 webkit
Feature Android Chrome Firefox Mobile (Gecko) Firefox OS IE Phone Opera Mobile Safari Mobile
Basic support 未サポート 28 webkit 25 1.2 未サポート 未サポート webkit

関連情報

 

ドキュメントのタグと貢献者

 このページの貢献者: chikoski, niusounds, hirontweet, nshimizu
 最終更新者: chikoski,