简体   繁体   English

使用 AnalyserNode 创建峰值计的 Web Audio API

[英]Web Audio API creating a Peak Meter with AnalyserNode

What is the correct way to implement a Peak Meter like those in Logic Pro with the Web Audio API AnalyserNode ?使用 Web Audio API AnalyserNode实现像Logic Pro 中峰值计的正确方法是什么?

I know AnalyserNode.getFloatFrequencyData() returns decibel values, but how do you combine those values to get the one to be displayed in the meter?我知道AnalyserNode.getFloatFrequencyData()返回分贝值,但是如何组合这些值以在仪表中显示该值? Do you just take the maximum value like in the following code sample (where analyserData comes from getFloatFrequencyData() :您是否只采用以下代码示例中的最大值(其中analyserData来自getFloatFrequencyData()

let peak = -Infinity;
for (let i = 0; i < analyserData.length; i++) {
  const x = analyserData[i];
  if (x > peak) {
    peak = x;
  }
}

Inspecting some output from just taking the max makes it look like this is not the correct approach.检查仅取最大值的一些输出会使这看起来不是正确的方法。 Am I wrong?我错了吗?

Alternatively, would it be a better idea to use a ScriptProcessorNode instead?或者,改用ScriptProcessorNode会更好吗? How would that approach differ?这种方法会有什么不同?

If you take the maximum of getFloatFrequencyData() 's results in one frame, then what you are measuring is the audio power at a single frequency (whichever one has the most power).如果您在一帧中获取getFloatFrequencyData()结果的最大值,那么您测量的是单个频率下的音频功率(以功率最大的为准)。 What you actually want to measure is the peak at any frequency — in other words, you want to not use the frequency data, but the unprocessed samples not separated into frequency bins.你真正想要什么来衡量是在任何频率的峰值-换句话说,你想使用频率数据,但未经处理的样品没有分成频点。

The catch is that you'll have to compute the decibels power yourself.问题是您必须自己计算分贝功率。 This is fairly simple arithmetic: you take some number of samples (one or more), square them, and average them.这是相当简单的算术:取一定数量的样本(一个或多个),对它们求平方,然后取平均值。 Note that even a “peak” meter may be doing averaging — just on a much shorter time scale.请注意,即使是“峰值”仪表也可能会进行平均——只是在更短的时间范围内。

Here's a complete example.这是一个完整的例子。 (Warning: produces sound.) (警告:产生声音。)

 document.getElementById('start').addEventListener('click', () => { 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(); });
 <button id="start">Start</button> <p> Short average <meter id="avg-level" min="-100" max="10" value="-100"></meter> <span id="avg-level-text">—</span> dB </p> <p> Instantaneous <meter id="inst-level" min="-100" max="10" value="-100"></meter> <span id="inst-level-text">—</span> dB </p>

Do you just take the maximum value你只是取最大值吗

For a peak meter, yes.对于峰值表,是的。 For a VU meter, there's all sorts of considerations in measuring the power, as well as the ballistics of an analog meter.对于 VU 仪表,在测量功率以及模拟仪表的弹道时需要考虑各种因素。 There's also RMS power metering.还有 RMS 功率计量。

In digital land, you'll find a peak meter to be most useful for many tasks, and by far the easiest to compute.在数字领域,您会发现峰值计对许多任务最有用,并且迄今为止最容易计算。

A peak for any given set of samples is the highest absolute value in the set.任何给定样本组的峰值是该组中的最高绝对值。 First though, you need that set of samples.但首先,您需要这组样本。 If you call getFloatFrequencyData() , you're not getting sample values, you're getting the spectrum.如果您调用getFloatFrequencyData() ,您将不会获得样本值,而是获得频谱。 What you want instead is getFloatTimeDomainData() .你想要的是getFloatTimeDomainData() This data is a low resolution representation of the samples.该数据是样本的低分辨率表示。 That is, you might have 4096 samples in your window, but your analyser might be configured with 256 buckets... so those 4096 samples will be resampled down to 256 samples.也就是说,您的窗口中可能有 4096 个样本,但您的分析器可能配置了 256 个桶……因此这 4096 个样本将被重新采样为 256 个样本。 This is generally acceptable for a metering task.这对于计量任务通常是可以接受的。

From there, it's just Math.max(-Math.min(samples), Math.max(samples)) to get the max of the absolute value.从那里,它只是Math.max(-Math.min(samples), Math.max(samples))来获得绝对值的最大值。

Suppose you wanted a higher resolution peak meter.假设您想要一个更高分辨率的峰值表。 For that, you need all the raw samples you can get.为此,您需要所有可以获得的原始样本。 That's where a ScriptProcessorNode comes in handy.这就是 ScriptProcessorNode 派上用场的地方。 You get access to the actual sample data.您可以访问实际的样本数据。

Basically, for this task, AnalyserNode is much faster, but slightly lower resolution.基本上,对于此任务,AnalyserNode 快得多,但分辨率略低。 ScriptProcessorNode is much slower, but slightly higher resolution. ScriptProcessorNode 慢得多,但分辨率略高。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM