我有一个带有不同区域的3d空间,每个区域都有自己的声音。我的目标是当用户从一个区域移动到另一个区域时具有平滑的音频转换,基本上在音轨之间进行交叉淡化。此外,我想优雅地处理用户改变主意并突然返回到刚刚离开的区域时的情况,这意味着取消新的音频转换并逐渐恢复到之前的状态。
要做到这一点,我使用web audio api,特别是linearRampToValueAtTime和cancelScheduledValues来定义淡入/淡出函数:
const fadeIn = (sound, time = 4, gain = 1.0) => {
sound.gainNode.gain.cancelScheduledValues(sound.context.currentTime)
!sound.isPlaying && sound.play()
sound.gainNode.gain.linearRampToValueAtTime(gain, sound.context.currentTime + time)
}
const fadeOut = (sound, time = 4, gain = 0.0) => {
sound.gainNode.gain.cancelScheduledValues(sound.context.currentTime)
sound.gainNode.gain.linearRampToValueAtTime(gain, sound.context.currentTime + time)
}
我只在一个声音上测试它,按下键“I”触发fadeIn,键“O”触发fadeOut:
document.addEventListener("keyup", e => {
switch (e.keyCode) {
// I key
case 73:
fadeIn(mySound)
break
// O key
case 79:
fadeOut(mySound)
break
})
我得到了不一致的结果。有时音频会正确消失,有时会在fadeIn中突然开始或者在fadeOut中切换。似乎在转换尚未完成时调用cancelScheduledValues会导致不稳定的行为。即使在fadeIn尚未完成时触发fadeOut,我也希望这能顺利工作,反之亦然。我也试过用setTimeout推迟一些linearRamps,比如说
setTimeout(() => sound.gainNode.gain.linearRampToValueAtTime(gain, sound.context.currentTime + time), 0.1)
但得到了同样错误的结果。我是否错误地使用了api?有什么建议吗?
我认为这里发生的事情就是说,你已经消失了,而你还没有完成升级。然后你想要淡出,并取消事件,包括未完成的淡入渐变。然后,淡入渐变消失,时间线值恢复为上一个事件的值。
因此,正如HankMoody建议的那样,使用cancelAndHoldAtTime
来解决这个问题。但是,AFAIK,只有Chrome有这个。
为了便携,我认为你想要做的是计算你想要取消时斜坡的位置。假设值为v
。然后做
setValueAtTime(v, context.currentTime);
cancelScheduledValues(context.currentTime + eps);
其中eps
是1/context.sampleRate
或更多的一些小值。