javascript - 在浏览器中检测实时音频输入的音高
问题描述
如何检测浏览器中实时音频输入的音高?
下面的代码将为您提供 1,024 个频率值。但是我不知道如何从这个到实际的音高(例如 A#)。
const audioContext = new window.AudioContext();
const analyser = audioContext.createAnalyser();
navigator.getUserMedia(
{ audio: true },
stream => {
audioContext.createMediaStreamSource(stream).connect(analyser);
const dataArray = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteTimeDomainData(dataArray);
// Log the contents of the analyzer ever 500ms.
setInterval(() => {
console.log(dataArray.length);
}, 500);
},
err => console.log(err)
);
解决方案
您当前访问的是时域数据,不能用于检索笔记(这似乎是您想要的)。
您想要的是频域,使用AnalyserNode.get[XXX]FrequencyData,您可以从中获得哪些频率更大或更安静。
但是,由于大多数声音是由和声组成的,因此您无法从麦克风中检索播放的音符,此外,我们只能访问有限的分辨率,而且您不仅无法从麦克风中检索音符,但也不是来自虚拟振荡器。
以下示例来自此 Q/A和来自 MDN 的示例;
const canvasCtx = canvas.getContext('2d');
const WIDTH = canvas.width = 500;
const HEIGHT = canvas.height = 150;
const audioCtx = new (window.AudioContext || window.webkitAudioContext);
const analyser = audioCtx.createAnalyser();
const nyquist = audioCtx.sampleRate / 2;
// highest precision
analyser.fftSize = 32768;
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
const osc = audioCtx.createOscillator();
osc.frequency.value = 400;
osc.connect(analyser);
osc.connect(audioCtx.destination);
range.oninput = e => {
osc.frequency.value = range.value;
};
if(!audioCtx.state || audioCtx.state === 'running') {
begin();
}
else {
log.textContent = 'click anywhere to begin';
onclick = e => {
onclick = null;
begin()
}
}
function begin() {
osc.start(0);
draw();
}
function draw() {
requestAnimationFrame(draw);
// get the Frequency Domain
analyser.getByteFrequencyData(dataArray);
canvasCtx.fillStyle = 'rgb(0, 0, 0)';
canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
const barWidth = (WIDTH / bufferLength) * 2.5;
let max_val = -Infinity;
let max_index = -1;
let x = 0;
for(let i = 0; i < bufferLength; i++) {
let barHeight = dataArray[i];
if(barHeight > max_val) {
max_val = barHeight;
max_index = i;
}
canvasCtx.fillStyle = 'rgb(' + (barHeight+100) + ',50,50)';
canvasCtx.fillRect(x,HEIGHT-barHeight/2,barWidth,barHeight/2);
x += barWidth;
}
log.textContent = `loudest freq: ${max_index * (nyquist / bufferLength)}
real value: ${range.value}`;
}
#log{display: inline-block; margin:0 12px}
#canvas{display: block;}
<input id="range" type="range" min="0" max="1000" value="400"><pre id="log"></pre>
<canvas id="canvas"></canvas>
推荐阅读
- angular - 如何在本机脚本角度的图标上显示文本
- django - 为什么这个 django 查询返回一个元组而不是对象?
- forms - symfony,我如何在基本模板上显示表单
- python - 如何使用 Python 和 joblib 并行提交多个 Spark 作业?
- font-size - 如何计算jsPDF字体大小比例因子?
- reactjs - 尝试在数组中使用 React 的 useRef 来获取音频的持续时间
- java - 从范围列表中查找重叠范围
- ruby-on-rails - `env:Client` SOAP Fault 带有错误字符串`Internal Error (from client)` 在 Savon 2.0 SOAP 请求上具有正确的参数
- .net-core - 当前线程与 Dispatcher 没有关联。触发渲染时使用 InvokeAsync() 将执行切换到 Dispatcher
- css - 我希望我的搜索框保持不透明度:当它处于活动状态时为 100%