概要
getUserMediaでブラウザから音声を拾い、画面に波形を出力します。
人間の声を分析して色々やりたいと思い、第一歩としてこちらを取り組みました。
環境
Windows 10 Home 64Bit
Chrome 63
超大まかな前提
- getUserMediaでマイクの音声を拾う
- onAudioProcessで1024バイトずつバッファを溜めていく(今回はバッファの内容は使わないが、wave化してサーバに送信などする予定)
- getChannelDataで返ってくるデータは生データなので、このままでは解析しにくい
- 離散フーリエ変換を行うことで周波数帯ごとの解析がしやすくなる(スペクトラム分析)
- audioContext.createAnalyser()でanalyserを作り、getByteFrequencyDataを利用すると上記フーリエ変換をした状態の値が取れる
ソースコード
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>音声解析</title>
<script
src="https://code.jquery.com/jquery-3.2.1.js"></script>
</head>
<body>
<button onclick="startRecording()">解析開始</button>
<button onclick="endRecording()">解析終了</button>
<hr>
<canvas id="canvas" width="500" height="500"></canvas>
<script src="voice_analyse.js"></script>
</body>
</html>
// クロスブラウザ定義
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
// 変数定義
var localMediaStream = null;
var localScriptProcessor = null;
var audioContext = new AudioContext();
var bufferSize = 1024;
var audioData = []; // 録音データ
var recordingFlg = false;
// キャンバス
var canvas = document.getElementById('canvas');
var canvasContext = canvas.getContext('2d');
// 音声解析
var audioAnalyser = null;
// 録音バッファ作成(録音中自動で繰り返し呼び出される)
var onAudioProcess = function(e) {
if (!recordingFlg) return;
// 音声のバッファを作成
var input = e.inputBuffer.getChannelData(0);
var bufferData = new Float32Array(bufferSize);
for (var i = 0; i < bufferSize; i++) {
bufferData[i] = input[i];
}
audioData.push(bufferData);
// 波形を解析
analyseVoice();
};
// 解析用処理
var analyseVoice = function() {
var fsDivN = audioContext.sampleRate / audioAnalyser.fftSize;
var spectrums = new Uint8Array(audioAnalyser.frequencyBinCount);
audioAnalyser.getByteFrequencyData(spectrums);
canvasContext.clearRect(0, 0, canvas.width, canvas.height);
canvasContext.beginPath();
for (var i = 0, len = spectrums.length; i < len; i++) {
//canvasにおさまるように線を描画
var x = (i / len) * canvas.width;
var y = (1 - (spectrums[i] / 255)) * canvas.height;
if (i === 0) {
canvasContext.moveTo(x, y);
} else {
canvasContext.lineTo(x, y);
}
var f = Math.floor(i * fsDivN); // index -> frequency;
// 500 Hz単位にy軸の線とラベル出力
if ((f % 500) === 0) {
var text = (f < 1000) ? (f + ' Hz') : ((f / 1000) + ' kHz');
// Draw grid (X)
canvasContext.fillRect(x, 0, 1, canvas.height);
// Draw text (X)
canvasContext.fillText(text, x, canvas.height);
}
}
canvasContext.stroke();
// x軸の線とラベル出力
var textYs = ['1.00', '0.50', '0.00'];
for (var i = 0, len = textYs.length; i < len; i++) {
var text = textYs[i];
var gy = (1 - parseFloat(text)) * canvas.height;
// Draw grid (Y)
canvasContext.fillRect(0, gy, canvas.width, 1);
// Draw text (Y)
canvasContext.fillText(text, 0, gy);
}
}
// 解析開始
var startRecording = function() {
recordingFlg = true;
navigator.getUserMedia({audio: true}, function(stream) {
// 録音関連
localMediaStream = stream;
var scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);
localScriptProcessor = scriptProcessor;
var mediastreamsource = audioContext.createMediaStreamSource(stream);
mediastreamsource.connect(scriptProcessor);
scriptProcessor.onaudioprocess = onAudioProcess;
scriptProcessor.connect(audioContext.destination);
// 音声解析関連
audioAnalyser = audioContext.createAnalyser();
audioAnalyser.fftSize = 2048;
frequencyData = new Uint8Array(audioAnalyser.frequencyBinCount);
timeDomainData = new Uint8Array(audioAnalyser.frequencyBinCount);
mediastreamsource.connect(audioAnalyser);
},
function(e) {
console.log(e);
});
};
// 解析終了
var endRecording = function() {
recordingFlg = false;
//audioDataをサーバに送信するなど終了処理
};
#今後
人の声の特性をとらえ、声の録音を行ったあとに自動で音声をサーバに送るなどしたいです。
#参考
##録音部分
https://developers.google.com/web/fundamentals/media/recording-audio/?hl=ja
http://var.blog.jp/archives/62330155.html
##音声解析部分
https://qiita.com/niisan-tokyo/items/764acfeec77d8092eb73
http://docolog.cocolog-nifty.com/papalog/2014/03/post-3de1.html
https://www.g200kg.com/jp/docs/webaudio/analyser.html
https://developer.mozilla.org/ja/docs/Web/API/AudioContext/createBufferSource
http://kagigotonet.hatenablog.com/entry/2014/09/16/124606
https://weblike-curtaincall.ssl-lolipop.jp/portfolio-web-sounder/webaudioapi-visualization/draw-wave