2017-06-05 51 views
0

使用Web Audio API AnalyserNode实现Peak Meter类似于Logic Pro的正确方法是什么?Web Audio API创建分析器节点的峰值表

我知道AnalyserNode.getFloatFrequencyData()会返回分贝值,但是如何将这些值组合起来才能得到要显示的值?你只需要像最大值以下代码示例中(其中analyserData来自getFloatFrequencyData()

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

检查从刚取最大值使它看起来像这不是正确的做法一些输出难道我错了?

或者,那会是一个更好的主意,用一个ScriptProcessorNode呢?如何将这种做法有什么不同?

+1

@Kaiido AudioWorkers现已实现?根据MDN,他们不是......同时,ScriptProcessorNode没有任何问题。 – Brad

回答

3

如果你把最大的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();
<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> dB

2

你只取最大值

对于峰值计,是的。对于VU仪表,在测量功率和模拟仪表的弹道性能方面有各种考虑因素。还有RMS功率计量。

在数字领域,您会发现峰值计对于许多任务最有用,而且迄今为止最容易计算。

任何给定样本集的峰值是该集合中最高的绝对值。首先,你需要那套样品。如果你打电话getFloatFrequencyData(),你没有得到样本值,你正在获取频谱。你想要的是getFloatTimeDomainData()。这些数据是样本的低分辨率表示。也就是说,你的窗口中可能有4096个样本,但是你的分析器可能配置了256个桶......所以这4096个样本将被重新抽样到256个样本。这通常可以用于测量任务。

从那里,它只是Math.max(-Math.min(samples), Math.max(samples))获得绝对值的最大值。

假设您想要更高分辨率的峰值流量计。为此,您需要获得所有原始样本。这就是ScriptProcessorNode派上用场的地方。您可以访问实际的样本数据。

基本上,对于这个任务,AnalyserNode速度要快得多,但分辨率稍低。 ScriptProcessorNode速度较慢,但​​分辨率稍高。

+0

“,这样那些4096个采样将被重采样到256个采样。” - 这是不正确的。您将以相同的速率获得更少的样本,代表更短的时间段,而不是重新采样。 –