ブラウザ上でオーディオを再生して、そのスペクトラムを表示させたい。
一般的にはスペクトラムを表示するには、波形に対してフーリエ変換をしなければならないのですが、実はフーリエ変換を実装せずに「 Web Audio API 」を利用することで、スペクトラムを表示させることが可能です。今回、このAPIを利用して実際にコードを動かしてみます。
Web Audio APIとは
Web Audio API を利用することで、音声にエコーやリバーブなどのエフェクトをかけることはもちろん、リアルタイム音声処理やゲーム効果音生成、立体音響生成など、複雑な音声処理も簡単に実装できるようになります。
この Web Audio API の機能の一つに、オーディオソースから周波数、波形、その他のデータを抽出し、可視化する機能があります。この機能を利用してスペクトラム表示することができます。
https://developer.mozilla.org/ja/docs/Web/API/Web_Audio_API/Visualizations_with_Web_Audio_API
作成したプログラムの実行結果のイメージ
実装上のポイント
オーディオファイルの用意
まず音声ファイルが必要になります。WAV, MP3, AAC, OGGおよびその他複数のフォーマットの音声ファイルがサポートされていますが、ブラウザごとにサポートする音声フォーマットは異なります。 今回は、 audio.mp3
というオーディオファイルを用意し、後述するhtmlファイルと同ディレクトリに配置します。
オーディオファイルの読み込み
オーディオファイルは、ArrayBufferで取得します。ArrayBufferとは、音声や動画, 画像などのバイナリデータの配列です。Arrayインスタンスの配列との違いは、バイナリデータを直接扱うためアクセスがより高速になります。
request.responseType = 'arraybuffer';
request.open('GET', 'audio.mp3', true);
request.send();
データの流れ(AudioNode)
データの流れは、 source (入力) → analyzer → audio.destination (出力)となり、 AudioNode.connect()
で繋げていきます。これらはどれもAudioNodeを継承したクラスです。
https://developer.mozilla.org/en-US/docs/Web/API/AudioNode/connect
const audio: AudioContext = new (window.AudioContext || window.webkitAudioContext)();
const analyser: AnalyserNode = audio.createAnalyser();
const source: AudioBufferSourceNode = audio.createBufferSource();
// ===略===
source.connect(analyser);
analyser.connect(audio.destination);
コード
これを踏まえて実装してみましょう。
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <title>Play Audio</title>
</head>
<body>
<button>PLAY</button>
<canvas class="visualizer" width="1280" height="960"></canvas>
<script>
 
 const FFT_SIZE = 8192; // FFTのサイズ指定(大きくすれば棒の数が増える)

 document.querySelector('button').addEventListener('click', function () {
 // canvasの設定
 const canvas = document.querySelector('.visualizer');
 const canvasCtx = canvas.getContext("2d");

 // グラフ描画
 const visualize = function () {
 const WIDTH = canvas.width;
 const HEIGHT = canvas.height;

 analyser.fftSize = FFT_SIZE;
 const bufferLengthAlt = analyser.frequencyBinCount;
 const dataArrayAlt = new Uint8Array(bufferLengthAlt);

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

 const draw = function () {
 requestAnimationFrame(draw);
 
 //スペクトラムへ変換
 analyser.getByteFrequencyData(dataArrayAlt);

 canvasCtx.fillStyle = 'rgb(0, 0, 0)';
 canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);

 const barWidth = (WIDTH / bufferLengthAlt) * 2.5; // 適当な横幅の調整
 let barHeight;
 let x = 0;

 for (let i = 0; i !== bufferLengthAlt; ++i) {
 barHeight = dataArrayAlt[i];

 canvasCtx.fillStyle = 'rgb(' + (barHeight + 100) + ',50,50)'; // 色の変更
 canvasCtx.fillRect(x, HEIGHT - barHeight * (HEIGHT / 150), barWidth, barHeight * (HEIGHT / 150)); // 棒の描画

 x += barWidth + 1;
 }
 };

 draw();
 }

 // オーディオ取得・設定
 const audio = new (window.AudioContext || window.webkitAudioContext)();
 const analyser = audio.createAnalyser();
 analyser.minDecibels = -90;
 analyser.maxDecibels = -10;
 analyser.smoothingTimeConstant = 0.85;
 const request = new XMLHttpRequest();
 
 request.onload = function () {
 const buffer = request.response;
 audio.decodeAudioData(buffer, function (buffer) {
 const source = audio.createBufferSource();
 source.buffer = buffer;
 source.start = source.start || source.noteOn;
 source.stop = source.stop || source.noteOff;
 source.loop = false;
 source.loopStart = 0;
 source.loopEnd = buffer.duration;
 source.start(0);
 source.connect(analyser);
 analyser.connect(audio.destination);
 visualize();
 });
 };
 
 // オーディオファイルの読み込み
 request.responseType = 'arraybuffer';
 request.open('GET', 'audio.mp3', true);
 request.send();
 });
</script>
</body>
</html>
Web Audio API を使うことで、非常に簡単に実装することができますね。
以下は実際に動作するコードです。
音源は Ehren Starks が作曲したPaper Lightsです。
音が再生されるので、ご注意下さい。
http://magnatune.com/artists/ehren/
まとめ
今回は、 Web Audio API のAnalyserNode(analyser)を使うことによってフーリエ変換を実装せずに、周波数領域のスペクトラムを表示することが出来ました。 Web Audio API にはこの他にも様々なAPIが用意されています。上述したとおり、例えば、音源にエフェクトをかけたり、立体音響化するなど様々な表現が可能です。また機会があったら実装してみたいと思います。