我正在开发一个 3D Web 应用程序,我想在其中使用位置 3D 音频。
我开始注意到,当声源改变位置时,输出上会出现噼啪声。 最初我认为这可能是编程问题或库问题(我使用的是 howler.js)。
我基于纯 JS 和 Webaudio API 制作了一个非常基本的示例,如下所示
let params={
"xPosition":0,
"zPosition":-1
};
let gui=new dat.GUI( { autoPlace: true, width: 500 });
const AudioContext = window.AudioContext || window.webkitAudioContext;
let audioCtx;
let panner;
let listener;
let source;
let osc;
function initWebAudio(){
audioCtx = new AudioContext();
panner = audioCtx.createPanner();
listener = audioCtx.listener;
osc = audioCtx.createOscillator();
osc.frequency.value = 70;
osc.connect(panner);
osc.start(0);
panner.connect(audioCtx.destination);
panner.panningModel = 'HRTF';
panner.distanceModel = 'linear';
panner.maxDistance = 60;
panner.refDistance = 1;
panner.rolloffFactor = 1;
panner.coneInnerAngle = 360;
panner.coneOuterAngle = 360;
panner.coneOuterGain = 0;
panner.positionX.setValueAtTime(0,audioCtx.currentTime);
panner.positionY.setValueAtTime(1,audioCtx.currentTime);
panner.positionZ.setValueAtTime(1,audioCtx.currentTime);
if(panner.orientationX) {
panner.orientationX.value = 1;
panner.orientationY.value = 0;
panner.orientationZ.value = 0;
} else {
panner.setOrientation(1,0,0);
}
if(listener.forwardX) {
listener.forwardX.value = 0;
listener.forwardY.value = 0;
listener.forwardZ.value = -1;
listener.upX.value = 0;
listener.upY.value = 1;
listener.upZ.value = 0;
} else {
listener.setOrientation(0,0,-1,0,1,0);
}
if(listener.positionX) {
listener.positionX.value = 0;
listener.positionY.value = 0;
listener.positionZ.value = 0;
} else {
listener.setPosition(0,0,0);
}
}
function positionPanner() {
if(panner.positionX) {
panner.positionX.setValueAtTime(params.xPosition, audioCtx.currentTime);
} else {
panner.setPosition(params.xPosition,0,params.zPosition);
}
}
function tick(){
positionPanner();
}
function onClickStart(){
initWebAudio();
gui.add(osc.frequency,"value",50,220).name("frequency");
setInterval(tick,50);
}
function buildMenu(){
gui.add(params,"xPosition",-3,3).step(0.001);
gui.add(window,"onClickStart").name("start");
}
buildMenu();
https://jsfiddle.net/fedeM75/t9vpm8so/23/
按开始,然后当您更改 xPosition 滑块时会出现噼啪声。 使用耳机时尤其明显。
我在google上搜索了一下,有人说和仓位变化率有关。但我尝试了不同的值范围,但它仍然会发生微小的变化。
顺便说一句,如果使用平移器的条件是位置变化速度非常慢,那么它在现实情况下似乎没有用。
我使用计时器来更新位置,但使用 requestAnimationFrame() 也会发生同样的情况
有谁知道为什么会发生这种情况以及如何解决它?
我用一个简单的 GainNode 得到了这种噼啪作响的音频。这确实可能是一个错误,因为 ScriptProcessor 中的简单实现确实可以工作。或者事实上我/我们只是不知道应该如何处理 audioCtx.currentTime 的计时?
对我来说,解决方案是放弃增益节点并使用 ScriptProcessor(已弃用:您可以/应该使用 AudioWorkletNode),并将我自己的增益直接应用于音频信号:
let myGain = 1.0; // change this as you like without crackling noises
let whiteNoise = audioCtx.createScriptProcessor(4096, 0, 1);
whiteNoise.onaudioprocess = function(e) {
let output = e.outputBuffer.getChannelData(0);
for (let i = 0; i < output.length; i++) {
output[i] = (Math.random() * 2 - 1) * myGain;
}
}
这不会直接解决您的问题,因为您在 3D 声像器方面遇到了这个问题,但也许您可以找到此类声像器实现的源代码/示例并将其移植到 AudioWorkletNode?希望这有帮助。
panner.positionX.setTargetAtTime(0, audioCtx.currentTime, 0.005);
panner.positionY.setTargetAtTime(1, audioCtx.currentTime, 0.005);
panner.positionZ.setTargetAtTime(1, audioCtx.currentTime, 0.005);
等等。大约 0.005,这里 Chris Wilson 怀特关于时间常数:
WebAudio:setTargetAtTime 中的 timeConstant 如何工作?
根据我的经验,0.005 是“瞬时”值变化的最小稳定值。一般来说,绝对不应该使用 setTargetAtTime (恕我直言)来更改值,在使用此方法时我总是听到声音中的伪影。它可能只适合初始设置值,但我不会冒险。