四月时帮朋友做毕设,用到过这个api。当时感觉很炫酷。可惜这个api的限制比较多。比如getUserMedia这个接口,在iOS中是完全不支持的(别问我为什么 我也是试验出来的)在安卓中也是有许多坑。anyway 一句话总结:这个api除了能处理声音,在移动端用处很小。
今天有点累,就不写点啥了 直接把我当初写的一个demo放上来。当然这个demo中的傅立叶快速变换不是我自己写的
autoCorrelate函数来自于微软一个开源的项目
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="full-screen" content="yes"/>
<meta content="telephone=no" name="format-detection" />
<meta name="screen-orientation" content="portrait"/>
<meta name="x5-fullscreen" content="true"/>
<meta name="360-fullscreen" content="true"/>
<title>监听声音</title>
<script src="js/get-float-time-domain-data.min.js"></script>
<style type="text/css">
.center {
margin-right: auto;margin-left: auto;
}
#button {
width: 30%;height: 50px;background-color: cornflowerblue;text-align: center;font-size: 18px;border-radius: 10px;line-height: 50px
}
#canvas {
margin-top: 10px;
}
</style>
</head>
<body style="text-align: center">
<h1>声音监控</h1>
<br>
<h2 id="hz">点击开始</h2>
<div οnclick="buttonClicked()" id="button" class="center">开始监听</div>
<canvas id="canvas" class="center"></canvas>
<script type="application/javascript">
var body = document.body,
canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
hz = document.getElementById("hz"),
width = 0,
height = 0,
button = document.getElementById("button");
var onRecord = false,//是否在录音
currentHz = 0, //当前频率
historyHz = [];//用来记录历史hz (为了canvas)
//这个是主角 WebAudio
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var audioContext = null,
sourceNode = null,
analyser = null,
theBuffer = null,
mediaStreamSource = null;
window.onload = function(){
canvas.width = body.clientWidth*0.5;
canvas.height = body.clientWidth*0.5;
width = canvas.width;
height = canvas.height;
ctx.fillStyle = "#B0E2FF"
ctx.fillRect(0,0,canvas.width,canvas.height);
initWebAudioApi();
}
function initWebAudioApi(){
try {
audioContext = new AudioContext();
}catch(e) {
alert("您的设备不支持Web Audi Api");
}
}
function error() {
alert('Stream generation failed.');
}
function buttonClicked(){
if (!onRecord){
onRecord = true;
button.innerText = "停止监听";
hz.innerText = "0Hz";
getUserMedia(
{
"audio": {
"mandatory": {
"googEchoCancellation": "false",
"googAutoGainControl": "false",
"googNoiseSuppression": "false",
"googHighpassFilter": "false"
},
"optional": []
},
}, gotStream);
}else{
onRecord = false;
button.innerText = "开始监听";
hz.innerText = "点击开始";
}
}
function getUserMedia(dictionary, callback) {
try {
navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
navigator.getUserMedia(dictionary, callback, error);
} catch (e) {
//常见错误原因是因为使用了http协议 (这个接口需要https)
console.log(e)
alert("抱歉GetUserMedia调用失败");
}
}
function gotStream(stream) {
mediaStreamSource = audioContext.createMediaStreamSource(stream);
analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
mediaStreamSource.connect( analyser );
updatePitch();
}
function autoCorrelate( buf, sampleRate ) {
var SIZE = buf.length;
var MAX_SAMPLES = Math.floor(SIZE/2);
var best_offset = -1;
var best_correlation = 0;
var rms = 0;
var foundGoodCorrelation = false;
var correlations = new Array(MAX_SAMPLES);
for (var i=0;i<SIZE;i++) {
var val = buf[i];
rms += val*val;
}
rms = Math.sqrt(rms/SIZE);
if (rms<0.01)
return -1;
var lastCorrelation=1;
for (var offset = 0; offset < MAX_SAMPLES; offset++) {
var correlation = 0;
for (var i=0; i<MAX_SAMPLES; i++) {
correlation += Math.abs((buf[i])-(buf[i+offset]));
}
correlation = 1 - (correlation/MAX_SAMPLES);
correlations[offset] = correlation; // store it, for the tweaking we need to do below.
if ((correlation>0.9) && (correlation > lastCorrelation)) {
foundGoodCorrelation = true;
if (correlation > best_correlation) {
best_correlation = correlation;
best_offset = offset;
}
} else if (foundGoodCorrelation) {
var shift = (correlations[best_offset+1] - correlations[best_offset-1])/correlations[best_offset];
return sampleRate/(best_offset+(8*shift));
}
lastCorrelation = correlation;
}
if (best_correlation > 0.01) {
return sampleRate/best_offset;
}
return -1;
}
var buflen = 1024,
frequency = 0;
var buf = new Float32Array(buflen);
function updatePitch(){
frequency++;
analyser.getFloatTimeDomainData(buf);
var ac = autoCorrelate(buf,audioContext.sampleRate);
var current_hz = 0;
if (ac != -1){
pitch = ac;
current_hz = (Math.round( pitch ));
}
if (historyHz.length>=canvas.width){
historyHz.shift();
}
currentHz = current_hz;
historyHz.push((current_hz>20000)?20000:current_hz);
hz.innerText = current_hz+"Hz";
animate()
loop = window.requestAnimationFrame( updatePitch );
}
function animate(){
//未完待续
}
</script>
</body>
</html>
注意:这个程序需要在https中打开才能使用(或者localhost:)
get-float-time-domain-data.min.js
这个js文件可以在网上下载到
https://www.npmjs.com/package/get-float-time-domain-data
不是必须的(只有部分不支持float time的浏览器需要添加)