cancelScheduledValues(x) 后跟 setValueCurveAtTime(x) 抛出错误
cancelScheduledValues(x) followed by setValueCurveAtTime(x) throws error
我正在使用 WebAudio API 在多个源之间交叉淡入淡出。在排队新的淡入淡出之前,淡入淡出使用 setValueCurveAtTime(curve, time, duration). The WebAudio spec indicates that any subsequent call to setValueCurveAtTime with overlapping durations is not allowed. So I'm calling cancelScheduledValues(time) 排队。但是,Firefox v68 和 Chrome v77 都会在第二次 setValueCurveAtTime 调用时抛出错误。
附加的代码段包含在任一浏览器中触发错误的最少量代码。 单击“开始” 触发错误。请注意,它不会播放任何音频,因为抛出错误不需要它。 select 下拉列表允许控制两个函数的 time
参数。在 Chrome v77 time=0
中不会触发错误。
任何关于如何让它在两种浏览器中工作的想法都将不胜感激!
更新: 正如 指出的 cancelScheduledValues(t) 似乎取消了 在 t
开始 的自动化] 或之后(不只是在 t
期间有效)。通过使用 cancelScheduledValues(Math.max(t - duration, 0))
,代码现在似乎可以在 Chrome 中运行。然而,Firefox 仍然失败并出现 Operation is not supported
错误。
<!DOCTYPE html>
<html>
<body>
<button id="start">Start</button>
<select id="time">
<option value="0">time=0</option>
<option value="1">time=currentTime</option>
</select>
<pre id="log"></pre>
<script>
const select = document.querySelector('#time')
const log = document.querySelector('#log')
function start() {
const ctx = new AudioContext()
ctx.resume()
const gain = ctx.createGain()
gain.connect(ctx.destination)
// Fade in
gain.gain.setValueCurveAtTime(new Float32Array([0, 1]), 0, 1)
setTimeout(() => {
const time = select.options[select.selectedIndex].value === '0' ? 0 : ctx.currentTime
// Replace fade in with fade out
// THIS IS THE CALL THAT DOESN'T WORK =====
// Doesn't work in Firefox nor Chrome:
// gain.gain.cancelScheduledValues(time)
// Doesn't work in Firefox:
gain.gain.cancelScheduledValues(Math.max(time - 1 /* duration of previous fade */, 0))
try {
// ERROR IS THROWN HERE =================
gain.gain.setValueCurveAtTime(new Float32Array([0, 1]), time, 1)
} catch (error) {
log.prepend(error.message + '\n')
throw error
}
log.prepend('No error!\n')
}, 100)
}
document.querySelector('#start').addEventListener('click', start)
</script>
</body>
</html>
我对 cancelScheduledValues 的阅读表明这是按预期工作的。 setValueCurveAtTime(curve, time, duration)
的事件时间是 time
。 cancelScheduledValues(t2)
删除事件时间为 t2
或更长的所有事件。在您的测试用例中,time
= 0,而 t2
是大于 0 的 currentTime
。因此,不会从时间轴中删除任何内容。第二次调用 setValueCurveAtTime
会插入一个与前一个事件重叠的新事件。因此,你得到一个错误。
话虽如此,我觉得这有点出乎意料。这可能是 WebAudio 规范中的一个错误。
最后我放弃了 setValueCurveAtTime()
转而使用多个 setValueAtTime
调用:
function fade(gainNode, fadeIn, startTime = 0) {
const duration = 1 // seconds
const delta = 1 / 100 // number of volume changes per second
const targetVolume = fadeIn ? 1 : 0
const currentTime = audioContext.currentTime
if (!startTime) { startTime = currentTime }
// We can only read current volume for current startTime, so if queueing a fade
// we'll have to assume it starts in the other end
const startingVolume = startTime && startTime !== currentTime
? 1 - targetVolume
: gainNode.gain.value
// Offset to start at when startingVolume isn't 0 or 1
let tOffset = equalPowerEasingInverse(startingVolume)
if (fadeIn) tOffset = 1 - tOffset
let t = 0 // time iterator [0..1]
try {
// Cancel any potentially overlapping fade automations
gainNode.gain.cancelScheduledValues(Math.max(startTime - duration, 0))
for (; t + tOffset <= 1; t += delta) {
// Queue volume change
gainNode.gain.setValueAtTime(
equalPowerEasing(t + tOffset, fadeIn),
startTime + t*duration
)
}
// Ensure final value is exact
gainNode.gain.setValueAtTime(targetVolume, startTime + t*duration)
} catch (error) {
if (/Failed to execute 'setValueCurveAtTime'|Operation is not supported/.test(error.message)) {
// Ignore Chrome + Firefox errors
} else {
throw error
}
}
}
function equalPowerEasing(t, invert = true) {
if (invert) t = 1 - t
return Math.cos(t * 0.5 * Math.PI)
}
function equalPowerEasingInverse(x) {
return Math.acos(x) / 0.5 / Math.PI
}
我正在使用 WebAudio API 在多个源之间交叉淡入淡出。在排队新的淡入淡出之前,淡入淡出使用 setValueCurveAtTime(curve, time, duration). The WebAudio spec indicates that any subsequent call to setValueCurveAtTime with overlapping durations is not allowed. So I'm calling cancelScheduledValues(time) 排队。但是,Firefox v68 和 Chrome v77 都会在第二次 setValueCurveAtTime 调用时抛出错误。
附加的代码段包含在任一浏览器中触发错误的最少量代码。 单击“开始” 触发错误。请注意,它不会播放任何音频,因为抛出错误不需要它。 select 下拉列表允许控制两个函数的 time
参数。在 Chrome v77 time=0
中不会触发错误。
任何关于如何让它在两种浏览器中工作的想法都将不胜感激!
更新: 正如 t
开始 的自动化] 或之后(不只是在 t
期间有效)。通过使用 cancelScheduledValues(Math.max(t - duration, 0))
,代码现在似乎可以在 Chrome 中运行。然而,Firefox 仍然失败并出现 Operation is not supported
错误。
<!DOCTYPE html>
<html>
<body>
<button id="start">Start</button>
<select id="time">
<option value="0">time=0</option>
<option value="1">time=currentTime</option>
</select>
<pre id="log"></pre>
<script>
const select = document.querySelector('#time')
const log = document.querySelector('#log')
function start() {
const ctx = new AudioContext()
ctx.resume()
const gain = ctx.createGain()
gain.connect(ctx.destination)
// Fade in
gain.gain.setValueCurveAtTime(new Float32Array([0, 1]), 0, 1)
setTimeout(() => {
const time = select.options[select.selectedIndex].value === '0' ? 0 : ctx.currentTime
// Replace fade in with fade out
// THIS IS THE CALL THAT DOESN'T WORK =====
// Doesn't work in Firefox nor Chrome:
// gain.gain.cancelScheduledValues(time)
// Doesn't work in Firefox:
gain.gain.cancelScheduledValues(Math.max(time - 1 /* duration of previous fade */, 0))
try {
// ERROR IS THROWN HERE =================
gain.gain.setValueCurveAtTime(new Float32Array([0, 1]), time, 1)
} catch (error) {
log.prepend(error.message + '\n')
throw error
}
log.prepend('No error!\n')
}, 100)
}
document.querySelector('#start').addEventListener('click', start)
</script>
</body>
</html>
我对 cancelScheduledValues 的阅读表明这是按预期工作的。 setValueCurveAtTime(curve, time, duration)
的事件时间是 time
。 cancelScheduledValues(t2)
删除事件时间为 t2
或更长的所有事件。在您的测试用例中,time
= 0,而 t2
是大于 0 的 currentTime
。因此,不会从时间轴中删除任何内容。第二次调用 setValueCurveAtTime
会插入一个与前一个事件重叠的新事件。因此,你得到一个错误。
话虽如此,我觉得这有点出乎意料。这可能是 WebAudio 规范中的一个错误。
最后我放弃了 setValueCurveAtTime()
转而使用多个 setValueAtTime
调用:
function fade(gainNode, fadeIn, startTime = 0) {
const duration = 1 // seconds
const delta = 1 / 100 // number of volume changes per second
const targetVolume = fadeIn ? 1 : 0
const currentTime = audioContext.currentTime
if (!startTime) { startTime = currentTime }
// We can only read current volume for current startTime, so if queueing a fade
// we'll have to assume it starts in the other end
const startingVolume = startTime && startTime !== currentTime
? 1 - targetVolume
: gainNode.gain.value
// Offset to start at when startingVolume isn't 0 or 1
let tOffset = equalPowerEasingInverse(startingVolume)
if (fadeIn) tOffset = 1 - tOffset
let t = 0 // time iterator [0..1]
try {
// Cancel any potentially overlapping fade automations
gainNode.gain.cancelScheduledValues(Math.max(startTime - duration, 0))
for (; t + tOffset <= 1; t += delta) {
// Queue volume change
gainNode.gain.setValueAtTime(
equalPowerEasing(t + tOffset, fadeIn),
startTime + t*duration
)
}
// Ensure final value is exact
gainNode.gain.setValueAtTime(targetVolume, startTime + t*duration)
} catch (error) {
if (/Failed to execute 'setValueCurveAtTime'|Operation is not supported/.test(error.message)) {
// Ignore Chrome + Firefox errors
} else {
throw error
}
}
}
function equalPowerEasing(t, invert = true) {
if (invert) t = 1 - t
return Math.cos(t * 0.5 * Math.PI)
}
function equalPowerEasingInverse(x) {
return Math.acos(x) / 0.5 / Math.PI
}