React で Audio Worklet を使いこなしたい

この記事は『CRESCO Advent Calendar 2021』 3日目の記事です。

ずいぶんとマニアックな記事になってしまいました。

突然ですが、皆さんは Web Audio API の Audio Worklet という機能をご存じでしょうか。

Audio Worklet は Web Audio API に定義されていないユーザー任意の音声処理を行う機能になります。

もちろん音声処理そのものは手動で実装する必要があるのですが、Audio Worklet を用いればそういった手動の実装を緩和しつつ、Web Audio APIの機能(Audio Node)の1つとして登録・使用することができ非常に便利です。

この記事では、Audio Worklet が内部でどのような処理を行っているのか、実装例を見ながら紹介していきたいと思います。

 

目標:Audio Worklet で VUメーターを作成する

今回の記事の目標です。

Audio Workletの具体的な使用方法を知るための実装例として、W3Cのドキュメント(日本語訳)にあるVUメーターを参考にしました。普段 React をよく利用しているので、ReactアプリとしてこのVUメーターを動かしてみたいと思います。

必要そうな前提知識

  • Web Audio API
    • そこそこ
    • 音声ファイルを読み込んで再生する程度の内容
  • React
    • そこそこ
    • useState, useEffect, useRef あたりのフックが理解できれば

以前書いた記事「React.jsでWeb Audio APIを使ってみる」の内容をそのまま流用している点があるので、先に読んでもらえるとより理解しやすいかもしれません。

目次

  1. Reactでの実装例概要
    1. 開発環境
    2. ファイル構成
    3. イメージ画像
  2. 実際のコード
    1. 「vuMeterProcessor.js」
    2. 「VUMeterNode.js 」
    3. 「main.js」

React での実装例概要

上記でも紹介したように、この記事の実装は、W3CのドキュメントにあるVUメーターを参考にしています。VUとはVolume Unit のことです。再生時点の音声の平均的な音量を示すメーターで、単位はアナログの音量単位「dBu」です。デジタルの音量単位「dBFS」との関係が色々あるようなのですが、ここでの説明は省きたいと思います。

開発環境

  • OS:windows 10
  • Node:v14.16.1
  • npm:6.13.4

ファイル構成

「create-react-app」で自動生成したディレクトリにいくつかファイルを追加しています。ディレクトリ名は「audio-app」になります。

  • audio-app
    • public
      • audioWorklet(新規作成ディレクトリ)
        • vuMeterProcessor.js(追加ファイル)
      • index.html
      • その他自動生成ファイル
    • src
      • volumeUnitMeter(新規作成ディレクトリ)
        • Main.js(追加ファイル)
        • VUMeterNode.js(追加ファイル)
      • index.js
      • その他自動生成ファイル

イメージ画像

アプリの全体像ですが、実際に動かすと次の画像のようになります。

音声ファイルを選択後、再生ボタンを押下することで音量の測定を開始します。

VUメーター

実際のコード

今回 Audio Worklet を利用したVUメーターを作成するにあたって、新しく作成したファイルは3つです。

それぞれ「vuMeterProcessor.js」「VUMeterNode.js」「main.js」になります。

以下簡単な概要です。

  • vuMeterProcessor.js:任意の音声処理の作成・登録を行う
  • VUMeterNode.js:登録した音声処理を利用する新クラスを作成する
  • main.js:新クラスを React コンポーネント内で利用する

vuMeterProcessor.js

利用したい音声処理(今回は音量の計測)を AudioWorkletProcessor として Web Audio API に登録します。

今回の登録名は「vumeter」とします。

コンストラクターでは、音声処理に利用したい変数を定義しておきます。引数の options は後述するVUMeterNode クラスで指定したものが取得できます。options は使用しなくても特に問題ありません。

具体的な音声処理は、関数 process() 内に記述します。js のメインスレッドとは別のスレッドで動作するため、画面描画の遅延等とは関係なく動作します。サクサク動いて快適です。入力を検知しなくなるまで自動で繰り返し実行されます。

音量が極端に小さいときは、入力がない状態と変わらないので、返り値に false を返して process() の動作を手動で中断させましょう。

音声処理した結果(今回は計測した音量)をメインスレッドに伝えたい場合は、後述するVUMeterNodeクラスのオブジェクトにメッセージとして送信することで実現できます。

逆にメッセージを受け取ることも可能です。このコードでは「音量の更新頻度の指示」をメインスレッドから受け取れるようになっています。

VUMeterNode.js

登録した音声処理を Web Audio API の機能の1つとして利用するためのクラスを作成します。

AudioWorkletNode を継承することで作成できます。クラス名は「VUMeterNode」としました。

親クラスのコンストラクターには、「AudioContext」「利用したい音声処理の登録名」「オプション」を引き渡しましょう。

プロセッサーとのメッセージ送信・受信処理。メインスレッドで使用したい情報(音量 [dBu] など)のゲッターなどはここで作成しておきます。

main.js

VUMeterNode クラスを利用した React コンポーネントを作成します。コンポーネント名は「VUMain」にしました。

音声ファイルの指定や再生するまでの処理は、以前書いた記事「React.jsでWeb Audio APIを使ってみる」の内容を流用しています。

異なる点は、VUMeterNode の取得部分とソースノードとの接続部分です。

コードを見て分かるように Audio Worklet で作成した VUMeterNode は、取得する際に非同期の処理が必要になります。しかし、それ以外は通常の Web Audio API のAudioNode と同じように扱うことができます。

今回は特にフィルターなどは追加せず、単純に sourceNode を入力先として接続してみます。

以上になります。

VUMain コンポーネントを index.js にインポートして配置すると、上で紹介したイメージ画像のようになります。

とても高速に動作するので、更新頻度を指定する入力欄を作っても良かったかもしれません。

まとめ

今回は、VUメーターを Audio Worklet を利用して作成しました。

自由度の高い機能なので、まだ全てを理解できたわけでないですが、これで Audio Worklet の大まかな使い方は確認できたかなと思っています。

Web Audio API の通常機能では実現できない音声処理があれば、Audio Worklet を使ってみる選択肢を考えるようにしたいですね。

VUメーターのようなリアルタイムな音声処理でなければ、再生前の audioBuffer を直接いじった方が簡単な場合もあるのでその点には注意したいです。

  • このエントリーをはてなブックマークに追加