2017-06-05 49 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個採樣。」 - 這是不正確的。您將以相同的速率獲得更少的樣本,代表更短的時間段,而不是重新採樣。 –