AudioContext
で音声データをいい感じに表示する。
やること
- 音声ファイルのバイナリデータを取得
- バイナリデータをAudioBufferに変換する
- AudioNode作成し、音声出力先とデータ解析用のインターフェースを紐付ける
- 再生を開始し、解析されたデータを取得してcanvasに描画する
音声ファイルのバイナリデータを取得
音声ファイルの指定方法はいくつかあるが、今回は2種類紹介。
input[type=file]
でファイルを指定する方法と、パスを直書き(現実的なところだとinput[type=text]
で指定)する方法。
ファイルを指定してバイナリデータを取得
var input = document.getElementById('file');
input.addEventListener('change', function () {
var file = input.files[0];
var fr = new FileReader();
fr.onload = function() {
// バイナリデータ
var arrayBuffer = fr.result;
console.log(arrayBuffer);
};
fr.readAsArrayBuffer(file);
});
パスを指定してバイナリデータを取得
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.open('GET', 'path/to/audio', true);
xhr.onload = function() {
// バイナリデータ
var arrayBuffer = xhr.response;
console.log(arrayBuffer);
};
xhr.send();
これでarrayBuffer
に音声ファイルのバイナリデータが入る。
バイナリデータをAudioBufferに変換する
バイナリデータをAudioBufferに変換するには、AudioContext
の持つdecodeAudioData
を使う。
AudioBufferに変換
// AudioContextを作成
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();
// decode処理
audioCtx.decodeAudioData(arrayBuffer, function(audioBuffer) {
console.log(audioBuffer);
});
decodeAudioData
メソッドのコールバック関数の引数に、変換後のAudioBufferが渡される。
AudioNode作成し、音声出力先とデータ解析用のインターフェースを紐付ける
音声データをAudioBufferに変換したことで扱いやすくなったので、音声の再生と描画をする準備をする。
AudioNode
を作成し、変換したAudioBuffer、をbuffer
プロパティに指定する。
AudioNodeの作成と関連付け
// AnalyserNodeを作成
var analyser = audioCtx.createAnalyser();
analyser.fftSize = 2048;
// AudioNodeを作成
var source = audioCtx.createBufferSource();
// bufferプロパティにAudioBufferを指定
source.buffer = audioBuffer;
// 音声出力先を指定
source.connect(audioCtx.destination);
// AnalyserNodeを指定
source.connect(analyser);
再生を開始し、解析されたデータを取得してcanvasに描画する
音声を再生し、requestAnimationFrame
ごとに解析されたデータを取得してcanvasに描画する。
再生開始
source.start(0);
解析されたデータをcanvasに描画
// canvasを取得
var canvas = document.getElementById('canvas');
canvas.width = 512;
canvas.height = 288;
// canvasのcontextを取得
var canvasCtx = canvas.getContext('2d');
canvasCtx.fillStyle = 'rgb(16, 16, 24)';
canvasCtx.lineWidth = 2;
canvasCtx.strokeStyle = 'rgb(124, 224, 255)';
var dataArray = new Uint8Array(analyser.fftSize);
// dataArrayが解析されたデータで満たされる
analyser.getByteTimeDomainData(dataArray);
var sliceWidth = canvas.width / analyser.fftSize;
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
canvasCtx.beginPath();
canvasCtx.moveTo(0, canvas.height / 2);
for (var i = 0; i < analyser.fftSize; i++) {
var x = sliceWidth * i;
// dataArrayの中身は0から255の数値
var v = dataArray[i] / 128;
var y = v * canvas.height / 2;
canvasCtx.lineTo(x, y);
}
canvasCtx.lineTo(canvas.width, canvas.height / 2);
canvasCtx.stroke();
整理すると
こんな感じ。
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Language" content="ja">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0,minimal-ui">
<title>Audio Context</title>
</head>
<body>
<p><input type="file" id="file" /></p>
<canvas id="canvas"></canvas>
<script type="text/javascript" src="./script.js"></script>
</body>
</html>
script.js
(function () {
// AudioContextを作成
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();
// AnalyserNodeを作成
var analyser = audioCtx.createAnalyser();
analyser.fftSize = 2048;
// canvasを取得
var canvas = document.getElementById('canvas');
canvas.width = 512;
canvas.height = 288;
// canvasのcontextを取得
var canvasCtx = canvas.getContext('2d');
canvasCtx.fillStyle = 'rgb(16, 16, 24)';
canvasCtx.lineWidth = 2;
canvasCtx.strokeStyle = 'rgb(124, 224, 255)';
// 初起表示
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
canvasCtx.beginPath();
canvasCtx.moveTo(0, canvas.height / 2);
canvasCtx.lineTo(canvas.width, canvas.height / 2);
canvasCtx.stroke();
// ファイルが選択されたらdecodeする
var input = document.getElementById('file');
input.addEventListener('change', function () {
var file = input.files[0];
var fr = new FileReader();
fr.onload = function() {
var arrayBuffer = fr.result;
decode(arrayBuffer);
};
fr.readAsArrayBuffer(file);
});
/*
// パスを指定するパターン
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.open('GET', 'path/to/audio', true);
xhr.onload = function() {
var arrayBuffer = xhr.response;
decode(arrayBuffer);
};
xhr.send();
*/
// decode処理
var decode = function (arrayBuffer) {
audioCtx.decodeAudioData(arrayBuffer, function(audioBuffer) {
// AudioNodeを作成
var source = audioCtx.createBufferSource();
// bufferプロパティにAudioBufferを指定
source.buffer = audioBuffer;
// 音声出力先を指定
source.connect(audioCtx.destination);
// AnalyserNodeを指定
source.connect(analyser);
// 再生開始
source.start(0);
draw();
});
};
// 描画
var draw = function () {
var dataArray = new Uint8Array(analyser.fftSize);
// dataArrayが解析されたデータで満たされる
analyser.getByteTimeDomainData(dataArray);
var sliceWidth = canvas.width / analyser.fftSize;
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
canvasCtx.beginPath();
canvasCtx.moveTo(0, canvas.height / 2);
for (var i = 0; i < analyser.fftSize; i++) {
var x = sliceWidth * i;
// dataArrayの中身は0から255の数値
var v = dataArray[i] / 128;
var y = v * canvas.height / 2;
canvasCtx.lineTo(x, y);
}
canvasCtx.lineTo(canvas.width, canvas.height / 2);
canvasCtx.stroke();
requestAnimationFrame(draw);
};
})();
demo