こんにちは。飼育係の岡崎です。
今回はWebAudio APIついて。
以前少しこのAPIが必要になったのですが、色々と癖が強い印象。
あれこれ探しているうちにhowler.jsを発見し、こいつがめっぽう便利でして、
普段の案件で音を扱う場合、howler.jsでやりたいことが済んでしまうことが多いです。
スマホでネックになるユーザーの挙動起点でのロードも勝手に解消してくれ、
サウンドスプライトにも対応しているので、よほどのことがない限りこれで充分。
そんな感じでなかなか素の状態のWebAudio APIのことを知る機会がないのですが、
ふと「そういえばイコライザーってjsできるんだよね。どうやるんだろ?」
と今更ながら思ったのが、今回の記事のきっかけ。
どうせなら、もう少し深くWebAudio APIについて調べてみましょう。
とりあえず基本的なことは、この2つのサイトを参照してみます。
Getting Started with Web Audio API - HTML5 Rocks
Web Audio API - Web API インターフェイス | MDN
ふむふむ
まず普段のAudioタグと違うのはXMLHttpRequestをつかって、
ファイルをバイナリデータで取得する必要があるのですね。
・・・・この時点で来た道を引き返したくなりますが、進んでみましょう。
AudioContextなるものを作成します。
WebAudioにはこいつが不可欠で、最終的な出力(destination)もこいつが保持しており、
様々なNodeをつなげていく形になるようです。
ここからは、すべての処理がAudioContextのインスタンスが起点になります。
まずXMLHttpRequestで取得したデータをAudioContextの
decodeAudioDataメソッドでデコードします。
この処理は非同期で行われる点に注意。
デコードしたバッファをcreateBufferSourceメソッドで生成した
AudioBufferNodeのbufferプロパティにセット。
このAudioBufferNodeをコンテキストのdestinationに
接続すれば、最低限の音を再生する環境が整います。
あとはこのsourceとdestinationの間に、用意されているNodeを
接続していく形でWebAudio APIは設計されています。
つまりAudio Contextは最終的な再生先と、
各Nodeを生成するメソッドを保持しているのですね。
まずやりたくなるのが音量の調整ですが、これはAudio Contextから
createGainメソッドでGainNodeを生成し、
GainNodeとBufferNodeを接続し、
GainNodeをdestinationに接続します。
これで、入力と出力の間にGainNodeが挟まった形になりました。
音量の調整自体はGainNodeのgainプロパティをいじればOKです。
さて、いよいよ本題のイコライザーですが、解析担当のNodeが用意されています。
その名もAnalyserNode!
これにもGainNodeと同様、生成するメソッド"createAnalyser"メソッドが
contextに用意されているので、これを使います。
BufferNodeを生成したAnalyserNodeに接続し、
AnalyserNodeはGainNodeに接続します。
先程のGainとBufferの間にAnalyserが挟まった形になりました。
これで準備完了です。下準備はこんな感じのコードになりました。
this._buffer = buffer; //バッファノードの生成 this._source = this._ctx.createBufferSource(); //デコードしたバッファをセット this._source.buffer = buffer; //AnalyserNodeの生成 this._analyser = this._ctx.createAnalyser(); //BufferNodeをAnalyserNodeに接続 this._source.connect(this._analyser); //GainNodeの生成 this._gain = this._ctx.createGain(); //AnalyserNodeをGainNodeに接続 this._analyser.connect(this._gain); //GainNodeを出力に接続 this._gain.connect(this._ctx.destination);
ちなみに以前僕がハマった罠があり、BufferNodeは使い捨てになっています。
いちどstartメソッドが呼ばれたBufferNodeインスタンスは、再度startメソッドを呼び出すことが出来ません。
なので、その都度createBufferSourceメソッドでインスタンスを生成する必要があります。
あとは再生を開始し、毎フレームAnalyserNodeに仕事をさせます。
出来ることはここを参照するとして、
波形を可視化したければ、getByte(Float)TimeDomainData,
周波数データの場合はgetByte(Float)FrequencyDataを
使用してデータを取り出します。
//分析フレームの平均間隔 1に近ければ近いほどスムーズに。 this._analyser.smoothingTimeCount = 1; //高速フーリエ変換の周波数領域 this._analyser.fftSize = 2048; //周波数データをuintで配列にコピー this._analyser.getByteFrequencyData(this._times);
せっかくなので、しばらくご無沙汰しているWebGLを使って作ってみました。
もう少し工夫しても良かったですが、意外と綺麗でだらだら見てられるのでこのまま放出します。
余談ですが、ビジュアライゼーションってなぜか、テクノとかラウドな音をサンプルに使ってるのが多い気がします。
全ての周波数がいい感じに出力されているので、見た目が派手なのもあるのでしょうかね。
ピアノ曲だと、シンプルで綺麗な周波数データを見ることが出来て、これはこれで味わいがあると思います。
・・・というわけで、サンプルの曲は私の大好きなムソルグスキーのピアノ組曲「展覧会の絵」から、ラストを飾る2曲
「鶏の足の上に建つ小屋 - Baba Yaga」と「キエフの大門」をどうぞ。
本当は息子が作った曲にしようとしたけど自重。