【发布时间】:2022-03-14 21:20:52
【问题描述】:
我最近开始研究网络音频 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>
所以,阅读这些问题后,我发现有几件事发生了:
-
当音频的音量 1 时重载目的地 Web Audio API glitching/distortions issues
-
突然打开和关闭振荡器会引起咔嗒声Web Audio: the ugly click and the human ear
因此,第一个链接建议我们通过增益节点控制音量,并通过动态压缩器路由音乐,而不是直接链接到 AudioContext 目标。我也读过将增益值降低十倍
为了减少“难看的咔哒声”,建议我们上下调整振荡器,而不是突然启动和停止它们。
本文How feasible is it to use the Oscillator.connect() and Oscillator.disconnect() methods to turn on/off sounds in an app built with the Web Audio API? 中的想法建议您可以在需要时动态创建振荡器。
使用上面的信息我想出了这个。
// 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>
我现在的问题是,我这样做是否正确,我是否可以做得更多,因为咔哒声和失真已显着减少,但如果我在键盘上有点疯狂,仍然可以检测到!
非常感谢并就此提供反馈,因此,提前致谢。
【问题讨论】:
标签: javascript web-audio-api game-development