簡體   English   中英

使用 AnalyserNode 創建峰值計的 Web Audio API

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

使用 Web Audio API AnalyserNode實現像Logic Pro 中峰值計的正確方法是什么?

我知道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會更好嗎? 這種方法會有什么不同?

如果您在一幀中獲取getFloatFrequencyData()結果的最大值,那么您測量的是單個頻率下的音頻功率(以功率最大的為准)。 你真正想要什么來衡量是在任何頻率的峰值-換句話說,你想使用頻率數據,但未經處理的樣品沒有分成頻點。

問題是您必須自己計算分貝功率。 這是相當簡單的算術:取一定數量的樣本(一個或多個),對它們求平方,然后取平均值。 請注意,即使是“峰值”儀表也可能會進行平均——只是在更短的時間范圍內。

這是一個完整的例子。 (警告:產生聲音。)

 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>

你只是取最大值嗎

對於峰值表,是的。 對於 VU 儀表,在測量功率以及模擬儀表的彈道時需要考慮各種因素。 還有 RMS 功率計量。

在數字領域,您會發現峰值計對許多任務最有用,並且迄今為止最容易計算。

任何給定樣本組的峰值是該組中的最高絕對值。 但首先,您需要這組樣本。 如果您調用getFloatFrequencyData() ,您將不會獲得樣本值,而是獲得頻譜。 你想要的是getFloatTimeDomainData() 該數據是樣本的低分辨率表示。 也就是說,您的窗口中可能有 4096 個樣本,但您的分析器可能配置了 256 個桶……因此這 4096 個樣本將被重新采樣為 256 個樣本。 這對於計量任務通常是可以接受的。

從那里,它只是Math.max(-Math.min(samples), Math.max(samples))來獲得絕對值的最大值。

假設您想要一個更高分辨率的峰值表。 為此,您需要所有可以獲得的原始樣本。 這就是 ScriptProcessorNode 派上用場的地方。 您可以訪問實際的樣本數據。

基本上,對於此任務,AnalyserNode 快得多,但分辨率略低。 ScriptProcessorNode 慢得多,但分辨率略高。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM