WebAudio:立即触发 setTargetAtTime() 事件,而不是在预定时间触发
WebAudio: setTargetAtTime() event is triggered Immediately, not at Scheduled Time
我有一个 振荡器 连接到 增益节点,用于创建音量包络线,随着时间的推移慢慢减少增益。 在开始后 1.5 秒左右,我想在第 2 秒执行以下操作:
- 取消所有预定的(未来)包络增益更改。
- 创建一个新的时间表 setTargetAtTime 到 "fade out" 声音。
问题是 "fade out" setTargetAtTime() 似乎立即被触发,这使得音量突然增加。是的,如果第二个参数 (startTime) 小于或等于 audioContext.currentTime,这是预期的。但这里不是这样。
function startSound() {
//Audio context
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var currentTime = audioCtx.currentTime;
//Create Gain Node And Oscillator
var gainNode = audioCtx.createGain();
var oscillator = audioCtx.createOscillator();
oscillator.type = "sine";
oscillator.frequency.setValueAtTime(440, currentTime);
//Connect Oscillator to Gain Node and Gain Node to audioCtx.destination
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
oscillator.start(currentTime);
//Create Gain (Volume Envelope)
gainNode.gain.setValueAtTime(0.5, currentTime);
gainNode.gain.linearRampToValueAtTime(0.5, currentTime + 0.5);
gainNode.gain.linearRampToValueAtTime(0.25, currentTime + 2);
gainNode.gain.linearRampToValueAtTime(0.000001, currentTime + 5);
gainNode.gain.linearRampToValueAtTime(0.000001, currentTime + 999);
//Approximately 1.5 seconds later, Cancel ALL Scheduled Envelope changes from second 2 and up, and create a FAST
//"Fade Out" that lasts 0.1 seconds.
var cancelAt = currentTime + 2;
setTimeout(function () {
StopEnvelope(audioCtx, gainNode, cancelAt);
}, 1500);
}
function StopEnvelope(audioCtx, gainNode, cancelAt) {
//This is never TRUE
if (cancelAt <= audioCtx.currentTime) {
console.log("cancelScheduledValues will happen immediately!");
}
gainNode.gain.cancelScheduledValues(cancelAt);
gainNode.gain.setTargetAtTime(0.00001, cancelAt, 0.1);
}
<input type="button" value="Start Oscillator" onclick="startSound();" />
<p>
You can hear a JUMP in Gain (Volume) at around 1.5 seconds. That is when <strong>gainNode.gain.setTargetAtTime(0.00001, cancelAt, 0.1);</strong> is executed.
It's is like it is executed Immedately instead at cancelAt (at second 2).
</p>
<p style="color: red; font-weight: bold;">
NOTE: this would be expected if cancelAt <= audioCtx.currentTime. But that is NOT the case here. </p>
非常奇怪的是,如果我在计划的事件中添加一点时间,它似乎会按预期运行。参见行 cancelAt += 0.0005;
function startSound() {
//Audio context
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var currentTime = audioCtx.currentTime;
//Create Gain Node And Oscillator
var gainNode = audioCtx.createGain();
var oscillator = audioCtx.createOscillator();
oscillator.type = "sine";
oscillator.frequency.setValueAtTime(440, currentTime);
//Connect Oscillator to Gain Node and Gain Node to audioCtx.destination
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
oscillator.start(currentTime);
//Create Gain (Volume Envelope)
gainNode.gain.setValueAtTime(0.5, currentTime);
gainNode.gain.linearRampToValueAtTime(0.5, currentTime + 0.5);
gainNode.gain.linearRampToValueAtTime(0.25, currentTime + 2);
gainNode.gain.linearRampToValueAtTime(0.000001, currentTime + 5);
gainNode.gain.linearRampToValueAtTime(0.000001, currentTime + 999);
//Approximately 1.5 seconds later, Cancel ALL Scheduled Envelope changes from second 2 and up, and create a FAST
//"Fade Out" that lasts 0.1 seconds.
var cancelAt = currentTime + 2;
setTimeout(function () {
StopEnvelope(audioCtx, gainNode, cancelAt);
}, 1500);
}
function StopEnvelope(audioCtx, gainNode, cancelAt) {
cancelAt += 0.0005;
//This is never TRUE
if (cancelAt <= audioCtx.currentTime) {
console.log("cancelScheduledValues will happen immediately!");
}
gainNode.gain.cancelScheduledValues(cancelAt);
gainNode.gain.setTargetAtTime(0.00001, cancelAt, 0.1);
}
<input type="button" value="Start Oscillator" onclick="startSound();" />
<p>
Now the "Fade Out" works as expected. The only change is that 0.0005 seconds is added to the <strong>cancelAt</strong> paramenter.
</p>
<p style="color: red; font-weight: bold;">
NOTE: As before, cancelAt is NEVER less than or equal to audioCtx.currentTime. </p>
你知道这种奇怪行为的根源是什么吗?如果没有那个时间黑客可以解决这个问题吗?
非常感谢!
丹尼布洛
问题是 cancelScheduledValues()
将删除在 cancelTime
尚未完成的所有自动化。在您的示例中,尽管第一个斜坡实际上没有做任何事情,但它包括所有斜坡,因为它将信号从 0.5
斜坡到 0.5
。第二个斜坡被删除,因为它在秒 2
结束,因此在秒 2
还没有完成。这意味着一旦您调用 cancelScheduledValues()
,增益就会迅速回到 0.5
,因为那是最后一个剩余动画的结束值。
这也是为什么如果你增加 cancelTime
一点点它似乎有效的原因。这样做时,第二个斜坡不会受到对 cancelScheduledValues()
的调用的影响,并且效果更小。
您可以使用 cancelAndHoldAtTime()
而不是 cancelScheduledValues()
来实现所需的行为。它将保留最后一个值,下一个自动化将从那里开始。
我有一个 振荡器 连接到 增益节点,用于创建音量包络线,随着时间的推移慢慢减少增益。 在开始后 1.5 秒左右,我想在第 2 秒执行以下操作:
- 取消所有预定的(未来)包络增益更改。
- 创建一个新的时间表 setTargetAtTime 到 "fade out" 声音。
问题是 "fade out" setTargetAtTime() 似乎立即被触发,这使得音量突然增加。是的,如果第二个参数 (startTime) 小于或等于 audioContext.currentTime,这是预期的。但这里不是这样。
function startSound() {
//Audio context
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var currentTime = audioCtx.currentTime;
//Create Gain Node And Oscillator
var gainNode = audioCtx.createGain();
var oscillator = audioCtx.createOscillator();
oscillator.type = "sine";
oscillator.frequency.setValueAtTime(440, currentTime);
//Connect Oscillator to Gain Node and Gain Node to audioCtx.destination
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
oscillator.start(currentTime);
//Create Gain (Volume Envelope)
gainNode.gain.setValueAtTime(0.5, currentTime);
gainNode.gain.linearRampToValueAtTime(0.5, currentTime + 0.5);
gainNode.gain.linearRampToValueAtTime(0.25, currentTime + 2);
gainNode.gain.linearRampToValueAtTime(0.000001, currentTime + 5);
gainNode.gain.linearRampToValueAtTime(0.000001, currentTime + 999);
//Approximately 1.5 seconds later, Cancel ALL Scheduled Envelope changes from second 2 and up, and create a FAST
//"Fade Out" that lasts 0.1 seconds.
var cancelAt = currentTime + 2;
setTimeout(function () {
StopEnvelope(audioCtx, gainNode, cancelAt);
}, 1500);
}
function StopEnvelope(audioCtx, gainNode, cancelAt) {
//This is never TRUE
if (cancelAt <= audioCtx.currentTime) {
console.log("cancelScheduledValues will happen immediately!");
}
gainNode.gain.cancelScheduledValues(cancelAt);
gainNode.gain.setTargetAtTime(0.00001, cancelAt, 0.1);
}
<input type="button" value="Start Oscillator" onclick="startSound();" />
<p>
You can hear a JUMP in Gain (Volume) at around 1.5 seconds. That is when <strong>gainNode.gain.setTargetAtTime(0.00001, cancelAt, 0.1);</strong> is executed.
It's is like it is executed Immedately instead at cancelAt (at second 2).
</p>
<p style="color: red; font-weight: bold;">
NOTE: this would be expected if cancelAt <= audioCtx.currentTime. But that is NOT the case here. </p>
非常奇怪的是,如果我在计划的事件中添加一点时间,它似乎会按预期运行。参见行 cancelAt += 0.0005;
function startSound() {
//Audio context
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var currentTime = audioCtx.currentTime;
//Create Gain Node And Oscillator
var gainNode = audioCtx.createGain();
var oscillator = audioCtx.createOscillator();
oscillator.type = "sine";
oscillator.frequency.setValueAtTime(440, currentTime);
//Connect Oscillator to Gain Node and Gain Node to audioCtx.destination
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
oscillator.start(currentTime);
//Create Gain (Volume Envelope)
gainNode.gain.setValueAtTime(0.5, currentTime);
gainNode.gain.linearRampToValueAtTime(0.5, currentTime + 0.5);
gainNode.gain.linearRampToValueAtTime(0.25, currentTime + 2);
gainNode.gain.linearRampToValueAtTime(0.000001, currentTime + 5);
gainNode.gain.linearRampToValueAtTime(0.000001, currentTime + 999);
//Approximately 1.5 seconds later, Cancel ALL Scheduled Envelope changes from second 2 and up, and create a FAST
//"Fade Out" that lasts 0.1 seconds.
var cancelAt = currentTime + 2;
setTimeout(function () {
StopEnvelope(audioCtx, gainNode, cancelAt);
}, 1500);
}
function StopEnvelope(audioCtx, gainNode, cancelAt) {
cancelAt += 0.0005;
//This is never TRUE
if (cancelAt <= audioCtx.currentTime) {
console.log("cancelScheduledValues will happen immediately!");
}
gainNode.gain.cancelScheduledValues(cancelAt);
gainNode.gain.setTargetAtTime(0.00001, cancelAt, 0.1);
}
<input type="button" value="Start Oscillator" onclick="startSound();" />
<p>
Now the "Fade Out" works as expected. The only change is that 0.0005 seconds is added to the <strong>cancelAt</strong> paramenter.
</p>
<p style="color: red; font-weight: bold;">
NOTE: As before, cancelAt is NEVER less than or equal to audioCtx.currentTime. </p>
你知道这种奇怪行为的根源是什么吗?如果没有那个时间黑客可以解决这个问题吗?
非常感谢! 丹尼布洛
问题是 cancelScheduledValues()
将删除在 cancelTime
尚未完成的所有自动化。在您的示例中,尽管第一个斜坡实际上没有做任何事情,但它包括所有斜坡,因为它将信号从 0.5
斜坡到 0.5
。第二个斜坡被删除,因为它在秒 2
结束,因此在秒 2
还没有完成。这意味着一旦您调用 cancelScheduledValues()
,增益就会迅速回到 0.5
,因为那是最后一个剩余动画的结束值。
这也是为什么如果你增加 cancelTime
一点点它似乎有效的原因。这样做时,第二个斜坡不会受到对 cancelScheduledValues()
的调用的影响,并且效果更小。
您可以使用 cancelAndHoldAtTime()
而不是 cancelScheduledValues()
来实现所需的行为。它将保留最后一个值,下一个自动化将从那里开始。