Web 音频 API "clicks" "crackles" "pops"和失真噪声消除。我还能做更多吗?



我最近开始研究网络音频api,以便将声音和音乐引入基于画布的游戏中。没过多久,我就注意到同时播放声音(例如,拍摄声音的快速重复在几秒钟内衰减,或者,在背景音乐上播放的拍摄声音会迅速导致可怕的失真

为了弄清楚产生不必要噪音的原因,我制作了一种非常简单的键盘乐器来演奏音符。当播放一个音符时,当声音结束时,你会听到一声难看的咔嗒声。当连续快速按下多个键时,情况会变得更糟。

// create the context
const actx = new AudioContext();
function playNoteUgh(freq = 261.63, type = "sine", decay = 0.5) {
// create oscillator and gain nodes
let osc = actx.createOscillator();
let vol = actx.createGain();
// set the supplied values
osc.frequency.value = freq;
osc.type = type;
vol.gain.value = 0.1;
//create the audio graph
osc.connect(vol).connect(actx.destination);
osc.start(actx.currentTime);
osc.stop(actx.currentTime + decay);
}
function playNote(freq = 261.63, type = "sine", decay = 2) {
// Create a new oscillator and audio graph for each keypress
createOsc(freq, type, decay);
}
function createOsc(freq, type, decay) {
console.log(freq, type, decay);
// create oscillator, gain and compressor nodes
let osc = actx.createOscillator();
let vol = actx.createGain();
let compressor = actx.createDynamicsCompressor();
// set the supplied values
osc.frequency.value = freq;
osc.type = type;
// set the volume value so that we do not overload the destination
// when multiple voices are played simmultaneously
vol.gain.value = 0.1;
//create the audio graph
osc.connect(vol).connect(compressor).connect(actx.destination);
// ramp up to volume so that we minimise the
// ugly "click" when the key is pressed
vol.gain.exponentialRampToValueAtTime(
vol.gain.value,
actx.currentTime + 0.03
);
// ramp down to minimise the ugly click when the oscillator stops
vol.gain.exponentialRampToValueAtTime(0.0001, actx.currentTime + decay);
osc.start(actx.currentTime);
osc.stop(actx.currentTime + decay + 0.03);
}
window.addEventListener("keydown", keyDown, { passive: false });
// Some musical note values:
let C4 = 261.63,
D4 = 293.66,
E4 = 329.63,
F4 = 349.23,
G4 = 392,
A5 = 440,
B5 = 493.88,
C5 = 523.25,
D5 = 587.33,
E5 = 659.25;
function keyDown(event) {
let key = event.key;
if (key === "q") playNoteUgh(C4);
if (key === "w") playNoteUgh(D4);
if (key === "e") playNoteUgh(E4);
if (key === "r") playNoteUgh(F4);
if (key === "t") playNoteUgh(G4);
if (key === "y") playNoteUgh(A5);
if (key === "u") playNoteUgh(B5);
if (key === "i") playNoteUgh(C5);
if (key === "o") playNoteUgh(D5);
if (key === "p") playNoteUgh(E5);
}
<p>Keys Q through P play C4 through E4</p>

所以,读到这些问题,我发现有几件事正在发生:

  • 当音频音量&lt-1或&gt;1.网络音频API故障/失真问题

  • 突然打开和关闭振荡器会引起可听见的点击Web音频:丑陋的点击和人耳

因此,第一个链接建议我们通过增益节点控制音量,并通过动力学压缩器路由音乐,而不是直接链接到AudioContext目的地。我还读到,将的增益值降低十倍

为了减少"丑陋的点击",我们建议向上和向下倾斜振荡器,而不是突然启动和停止它们。

这篇文章中的想法在使用Web Audio API构建的应用程序中,使用Oscillator.connect((和Oscillator.dedisconnect((方法打开/关闭声音的可行性如何?建议您可以根据需要随时创建振荡器。

利用以上信息,我想出了这个。

// create the context
const actx = new AudioContext();
function playNote(freq = 261.63, type = "sine", decay = 2) {
// Create a new oscillator and audio graph for each keypress
createOsc(freq, type, decay);
}
function createOsc(freq, type, decay) {

// create oscillator, gain and compressor nodes
let osc = actx.createOscillator();
let vol = actx.createGain();
let compressor = actx.createDynamicsCompressor();
// set the supplied values
osc.frequency.value = freq;
osc.type = type;
// set the volume value so that we do not overload the destination
// when multiple voices are played simmultaneously
vol.gain.value = 0.1;
//create the audio graph
osc.connect(vol).connect(compressor).connect(actx.destination);
// ramp up to volume so that we minimise the
// ugly "click" when the key is pressed
vol.gain.exponentialRampToValueAtTime(
vol.gain.value,
actx.currentTime + 0.03
);
// ramp down to minimise the ugly click when the oscillator stops
vol.gain.exponentialRampToValueAtTime(0.0001, actx.currentTime + decay);
osc.start(actx.currentTime);
osc.stop(actx.currentTime + decay + 0.03);
}
window.addEventListener("keydown", keyDown, { passive: false });
// Some musical note values:
let C4 = 261.63,
D4 = 293.66,
E4 = 329.63,
F4 = 349.23,
G4 = 392,
A5 = 440,
B5 = 493.88,
C5 = 523.25,
D5 = 587.33,
E5 = 659.25;
function keyDown(event) {
let key = event.key;
if (key === "1") playNote(C4);
if (key === "2") playNote(D4);
if (key === "3") playNote(E4);
if (key === "4") playNote(F4);
if (key === "5") playNote(G4);
if (key === "6") playNote(A5);
if (key === "7") playNote(B5);
if (key === "8") playNote(C5);
if (key === "9") playNote(D5);
if (key === "0") playNote(E5);
}
<p>Key 1 to 0 play C4 through to E5</p>

我现在的问题是,我做得对吗?我能做得更多吗?因为点击和失真已经显著减少,但如果我在键盘上有点疯狂,仍然可以检测到!

我真的很感激并对此提供反馈,所以,提前谢谢。

当前形式的卷自动化不应该有任何影响。

您先设置音量。

vol.gain.value = 0.1;

稍后定义一个渐变,该渐变应渐变为相同的值。

vol.gain.exponentialRampToValueAtTime(
vol.gain.value,
actx.currentTime + 0.03
);

结果是0.1的常数值。

假设您希望最初设置音量,并希望将音量保持恒定,直到actx.currentTime + decay,然后再淡出0.03秒。其代码如下所示。

const currentTime = actx.currentTime;
// This defines the initial value.
vol.gain.setValueAtTime(0.1, currentTime);
// This sets the starting point for the ramp.
vol.gain.setValueAtTime(0.1, currentTime + decay);
// Finally this schedules the fade out.
vol.gain.exponentialRampToValueAtTime(
0.001, 
currentTime + decay + 0.03
);

指数渐变不能在0处结束。因此,出现故障的风险仍然很小。可以通过在末端添加另一个线性渐变来避免这种情况。但我想没有必要。

最新更新