オーディオ ワークレットがデフォルトで使用できるようになりました

Chrome 64 では、待望の Web Audio API の新機能が実装されます。 AudioWorklet。ここでは、Terraform ワークフローの カスタムオーディオプロセッサを 開発しています詳しくは、 ライブデモ シリーズの次の記事「オーディオ ワークレットのデザイン パターン」では、 高度なオーディオ アプリを作成するうえで興味深いドキュメントです。

バックグラウンド: ScriptProcessorNode

Web Audio API の音声処理はメインスレッドとは別のスレッドで実行される スムーズに動作します。カスタム音声処理を有効にするには、 JavaScript、Web Audio API は ScriptProcessorNode を提案しました。 イベント ハンドラを使用して、メイン UI スレッドでユーザー スクリプトを呼び出す。

この設計には 2 つの問題があります。イベント処理が非同期であることです。 メインスレッドで実行されます。前者 レイテンシを引き起こします。後者では、メインスレッドに さまざまな UI や DOM 関連のタスクで混雑していると、 「ジャンク」に「グリッチ」にできます。こうした根本的な設計上の欠陥により、 ScriptProcessorNode は仕様で非推奨となり、 AudioWorklet に置き換えられました。

コンセプト

オーディオ ワークレットは、ユーザーが指定した JavaScript コードをすべて やり取りできます。つまり、モジュールを使わずに、 やり取りできます。つまり、ユーザーが入力したスクリプト コードが実際に オーディオ レンダリング スレッド(AudioWorkletGlobalScope)で、 組み込みの AudioNodes。これにより、レイテンシの増加と同期を 説明します。

<ph type="x-smartling-placeholder">
</ph> メインのグローバル スコープとオーディオ ワークレット スコープの図 <ph type="x-smartling-placeholder">
</ph> Fig.1
をご覧ください。

登録とインスタンス化

オーディオ ワークレットの使用は、AudioWorkletProcessorAudioWorkletNode。これは、ScriptProcessorNode を使用するよりも カスタム オーディオの低レベルの機能をデベロッパーに提供するために あります。AudioWorkletProcessor は実際のオーディオ プロセッサを表します。 記述され、AudioWorkletGlobalScope にあります。 AudioWorkletNodeAudioWorkletProcessor と同等で、 メインスレッド内の他の AudioNodes との接続を管理します。これは、 メインのグローバル スコープで公開され、通常の AudioNode のように機能します。

こちらは 2 つのコード スニペットで、登録と インスタンス化できます。

// The code in the main global scope.
class MyWorkletNode extends AudioWorkletNode {
  constructor(context) {
    super(context, 'my-worklet-processor');
  }
}

let context = new AudioContext();

context.audioWorklet.addModule('processors.js').then(() => {
  let node = new MyWorkletNode(context);
});

AudioWorkletNode を作成するには、AudioContext を追加する必要があります。 プロセッサ名を文字列として格納します。プロセッサ定義は 新しいオーディオ ワークレット オブジェクトの addModule() 呼び出しによって読み込まれて登録されます。 音声ワークレットなどの Worklet API は、 安全なコンテキストを提供できるため、 それらを使用するページは HTTPS で配信する必要がありますが、http://localhost は 安全とみなされます。

AudioWorkletNode をサブクラス化して、 ワークレットで実行されているプロセッサを基盤とするカスタム ノードを作成します。

// This is the "processors.js" file, evaluated in AudioWorkletGlobalScope
// upon audioWorklet.addModule() call in the main global scope.
class MyWorkletProcessor extends AudioWorkletProcessor {
  constructor() {
    super();
  }

  process(inputs, outputs, parameters) {
    // audio processing code here.
  }
}

registerProcessor('my-worklet-processor', MyWorkletProcessor);

AudioWorkletGlobalScoperegisterProcessor() メソッドは、 登録するプロセッサの名前とクラス定義の文字列。 グローバル スコープでスクリプト コードの評価が完了すると、 AudioWorklet.addModule() からの Promise が解決され、ユーザーに通知されます。 クラス定義がメインのグローバル スコープで使用できる状態であることを確認します。

カスタム音声パラメータ

AudioNode の便利な点の一つは schedulable パラメータです。 AudioParam による自動化。AudioWorkletNode はこれらを使用して、 オーディオ レートで自動的に制御できる公開パラメータ。

<ph type="x-smartling-placeholder">
</ph> オーディオ ワークレット ノードとプロセッサの図 <ph type="x-smartling-placeholder">
</ph> Fig.2
をご覧ください。

ユーザー定義のオーディオ パラメータは AudioWorkletProcessor で宣言できる AudioParamDescriptor のセットをセットアップしてクラス定義を行います。「 基になる WebAudio エンジンが、この情報を AudioWorkletNode を作成してから、そのノードを AudioParam オブジェクトを適切にノードに追加します。

/* A separate script file, like "my-worklet-processor.js" */
class MyWorkletProcessor extends AudioWorkletProcessor {

  // Static getter to define AudioParam objects in this custom processor.
  static get parameterDescriptors() {
    return [{
      name: 'myParam',
      defaultValue: 0.707
    }];
  }

  constructor() { super(); }

  process(inputs, outputs, parameters) {
    // |myParamValues| is a Float32Array of either 1 or 128 audio samples
    // calculated by WebAudio engine from regular AudioParam operations.
    // (automation methods, setter) Without any AudioParam change, this array
    // would be a single value of 0.707.
    const myParamValues = parameters.myParam;

    if (myParamValues.length === 1) {
      // |myParam| has been a constant value for the current render quantum,
      // which can be accessed by |myParamValues[0]|.
    } else {
      // |myParam| has been changed and |myParamValues| has 128 values.
    }
  }
}

AudioWorkletProcessor.process() メソッド

実際の音声処理は、process() コールバック メソッドで行われます。 AudioWorkletProcessor。これはクラスのユーザーが実装する必要があります。 定義します。WebAudio エンジンはこの関数をアイソクロナス レイヤで呼び出し、 入力とパラメータをフィードし、出力をフェッチします。

/* AudioWorkletProcessor.process() method */
process(inputs, outputs, parameters) {
  // The processor may have multiple inputs and outputs. Get the first input and
  // output.
  const input = inputs[0];
  const output = outputs[0];

  // Each input or output may have multiple channels. Get the first channel.
  const inputChannel0 = input[0];
  const outputChannel0 = output[0];

  // Get the parameter value array.
  const myParamValues = parameters.myParam;

  // if |myParam| has been a constant value during this render quantum, the
  // length of the array would be 1.
  if (myParamValues.length === 1) {
    // Simple gain (multiplication) processing over a render quantum
    // (128 samples). This processor only supports the mono channel.
    for (let i = 0; i < inputChannel0.length; ++i) {
      outputChannel0[i] = inputChannel0[i] * myParamValues[0];
    }
  } else {
    for (let i = 0; i < inputChannel0.length; ++i) {
      outputChannel0[i] = inputChannel0[i] * myParamValues[i];
    }
  }

  // To keep this processor alive.
  return true;
}

さらに、process() メソッドの戻り値を使用して、 AudioWorkletNode の存続期間を制御して、デベロッパーが管理できるようにする 削減できますprocess() メソッドマークから false を返す プロセッサが非アクティブになり、WebAudio エンジンが メソッドを呼び出します。プロセッサを存続させるため、メソッドは true を返す必要があります。 それ以外の場合、ノードとプロセッサのペアがシステムによってガベージ コレクションされます あります。

MessagePort による双方向通信

カスタムの AudioWorkletNode が、そうでないコントロールを公開しようとすることがあります。 AudioParam にマッピング(文字列ベースの type 属性など) カスタムフィルタの制御に使用できますこの目的とその後のために AudioWorkletNodeAudioWorkletProcessor は以下を搭載しています: MessagePort: 双方向通信に使用します。あらゆる種類のカスタムデータ このチャネルで交換できます

<ph type="x-smartling-placeholder">
</ph> Fig.2 <ph type="x-smartling-placeholder">
</ph> Fig.2
をご覧ください。

MessagePort には、ノードと Pod の両方で .port 属性を使用してアクセスできます 提供します。ノードの port.postMessage() メソッドは、メッセージを 関連付けられているプロセッサの port.onmessage ハンドラで、逆にその逆になります。

/* The code in the main global scope. */
context.audioWorklet.addModule('processors.js').then(() => {
  let node = new AudioWorkletNode(context, 'port-processor');
  node.port.onmessage = (event) => {
    // Handling data from the processor.
    console.log(event.data);
  };

  node.port.postMessage('Hello!');
});
/* "processors.js" file. */
class PortProcessor extends AudioWorkletProcessor {
  constructor() {
    super();
    this.port.onmessage = (event) => {
      // Handling data from the node.
      console.log(event.data);
    };

    this.port.postMessage('Hi!');
  }

  process(inputs, outputs, parameters) {
    // Do nothing, producing silent output.
    return true;
  }
}

registerProcessor('port-processor', PortProcessor);

MessagePort は Transferable に対応しているため、次のことを行えます。 WASM モジュールです開く Audio Worklet システムの使い方は無限に広がっています。

チュートリアル: ゲインノードを作成する

このスライドは、Google Cloud 上に構築された AudioWorkletNodeAudioWorkletProcessor

index.html ファイル:

<!doctype html>
<html>
<script>
  const context = new AudioContext();

  // Loads module script with AudioWorklet.
  context.audioWorklet.addModule('gain-processor.js').then(() => {
    let oscillator = new OscillatorNode(context);

    // After the resolution of module loading, an AudioWorkletNode can be
    // constructed.
    let gainWorkletNode = new AudioWorkletNode(context, 'gain-processor');

    // AudioWorkletNode can be interoperable with other native AudioNodes.
    oscillator.connect(gainWorkletNode).connect(context.destination);
    oscillator.start();
  });
</script>
</html>

gain-processor.js ファイル:

class GainProcessor extends AudioWorkletProcessor {

  // Custom AudioParams can be defined with this static getter.
  static get parameterDescriptors() {
    return [{ name: 'gain', defaultValue: 1 }];
  }

  constructor() {
    // The super constructor call is required.
    super();
  }

  process(inputs, outputs, parameters) {
    const input = inputs[0];
    const output = outputs[0];
    const gain = parameters.gain;
    for (let channel = 0; channel < input.length; ++channel) {
      const inputChannel = input[channel];
      const outputChannel = output[channel];
      if (gain.length === 1) {
        for (let i = 0; i < inputChannel.length; ++i)
          outputChannel[i] = inputChannel[i] * gain[0];
      } else {
        for (let i = 0; i < inputChannel.length; ++i)
          outputChannel[i] = inputChannel[i] * gain[i];
      }
    }

    return true;
  }
}

registerProcessor('gain-processor', GainProcessor);

オーディオ ワークレット システムの基本について説明します。ライブデモを利用できます Chrome WebAudio チームの GitHub リポジトリで確認できます。

機能の移行: 試験運用版から安定版へ

Chrome 66 以降では、オーディオ ワークレットがデフォルトで有効になっています。Chrome 64 と 65 では、この機能は試験運用版のフラグを使用していました。