Cal*_*mer 1 javascript audio web-audio-api
什么是实现一个正确的方法峰值表像那些在Logic Pro中使用的网络音频API AnalyserNode?
我知道会AnalyserNode.getFloatFrequencyData()返回分贝值,但是如何将这些值组合起来才能在仪表中显示呢?您是否像下面的代码示例一样取最大值(其中analyserData来自getFloatFrequencyData():
let peak = -Infinity;
for (let i = 0; i < analyserData.length; i++) {
const x = analyserData[i];
if (x > peak) {
peak = x;
}
}
Run Code Online (Sandbox Code Playgroud)
仅通过获取最大值来检查一些输出,就好像这不是正确的方法。我错了吗?
或者,使用a ScriptProcessorNode代替会更好吗?这种方法有何不同?
你只取最大值吗
对于峰值计来说,是的。对于 VU 仪表,测量功率以及模拟仪表的弹道学需要考虑各种因素。还有 RMS 功率计量。
在数字领域,您会发现峰值计对于许多任务最有用,而且迄今为止最容易计算。
任何给定样本组的峰值是该组中的最高绝对值。但首先,您需要该组样本。如果您调用getFloatFrequencyData(),您不会获得样本值,您会获得频谱。相反,你想要的是getFloatTimeDomainData(). 该数据是样本的低分辨率表示。也就是说,您的窗口中可能有 4096 个样本,但您的分析仪可能配置有 256 个存储桶...因此,这 4096 个样本将被重新采样为 256 个样本。这对于计量任务来说通常是可以接受的。
从那里开始,只需Math.max(-Math.min(samples), Math.max(samples))获取绝对值的最大值即可。
假设您想要一个更高分辨率的峰值计。为此,您需要可以获得所有原始样本。这就是 ScriptProcessorNode 派上用场的地方。您可以访问实际的示例数据。
基本上,对于此任务,AnalyserNode 速度更快,但分辨率稍低。ScriptProcessorNode 速度慢得多,但分辨率稍高。
如果您getFloatFrequencyData()在一帧中获取最大的,则您要测量的是单个频率上的音频功率(以功率最大的那个为准)。你真正想要什么来衡量是在峰值的任何频率-换句话说,你想不使用频率数据,但未经处理的样品没有分成频点。
问题是您必须自己计算分贝功率。这是相当简单的算法:您需要抽取一定数量的样本(一个或多个),对它们进行平方并取平均值。请注意,即使是一个“峰值”仪表也可能正在进行平均-只是时间短得多。
这是一个完整的例子。(警告:发出声音。)
const context = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = context.createOscillator();
oscillator.type = 'square';
oscillator.frequency.value = 440;
oscillator.start();
const gain1 = context.createGain();
const analyser = context.createAnalyser();
// Reduce output level to not hurt your ears.
const gain2 = context.createGain();
gain2.gain.value = 0.01;
oscillator.connect(gain1);
gain1.connect(analyser);
analyser.connect(gain2);
gain2.connect(context.destination);
function displayNumber(id, value) {
const meter = document.getElementById(id + '-level');
const text = document.getElementById(id + '-level-text');
text.textContent = value.toFixed(2);
meter.value = isFinite(value) ? value : meter.min;
}
// Time domain samples are always provided with the count of
// fftSize even though there is no FFT involved.
// (Note that fftSize can only have particular values, not an
// arbitrary integer.)
analyser.fftSize = 2048;
const sampleBuffer = new Float32Array(analyser.fftSize);
function loop() {
// Vary power of input to analyser. Linear in amplitude, so
// nonlinear in dB power.
gain1.gain.value = 0.5 * (1 + Math.sin(Date.now() / 4e2));
analyser.getFloatTimeDomainData(sampleBuffer);
// Compute average power over the interval.
let sumOfSquares = 0;
for (let i = 0; i < sampleBuffer.length; i++) {
sumOfSquares += sampleBuffer[i] ** 2;
}
const avgPowerDecibels = 10 * Math.log10(sumOfSquares / sampleBuffer.length);
// Compute peak instantaneous power over the interval.
let peakInstantaneousPower = 0;
for (let i = 0; i < sampleBuffer.length; i++) {
const power = sampleBuffer[i] ** 2;
peakInstantaneousPower = Math.max(power, peakInstantaneousPower);
}
const peakInstantaneousPowerDecibels = 10 * Math.log10(peakInstantaneousPower);
// Note that you should then add or subtract as appropriate to
// get the _reference level_ suitable for your application.
// Display value.
displayNumber('avg', avgPowerDecibels);
displayNumber('inst', peakInstantaneousPowerDecibels);
requestAnimationFrame(loop);
}
loop();Run Code Online (Sandbox Code Playgroud)
<p>
Short average
<meter id="avg-level" min="-100" max="10" value="-100"></meter>
<span id="avg-level-text">—</span> dB
<p>
Instantaneous
<meter id="inst-level" min="-100" max="10" value="-100"></meter>
<span id="inst-level-text">—</span> dBRun Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1383 次 |
| 最近记录: |