Use currentTime for exact scheduling

This commit is contained in:
Aria 2025-09-06 10:09:44 -05:00
parent 9c108862ef
commit 798eb22d9a
3 changed files with 47 additions and 46 deletions

View file

@ -590,12 +590,12 @@ export const { duckdepth } = registerControl('duckdepth');
* @param {number | Pattern} time The onset time in seconds * @param {number | Pattern} time The onset time in seconds
* @example * @example
* // Clicks * // Clicks
* sound: n(run(8)).scale("c:minor").s("sawtooth").lpf(200).delay(.7).orbit(2) * sound: freq("63.2388").s("sine").orbit(2).gain(4)
* duckerWithClick: s("bd*4").duckorbit(2).duckonset(0).postgain(0) * duckerWithClick: s("bd*4").duckorbit(2).duckattack(0.3).duckonset(0).postgain(0)
* @example * @example
* // No clicks * // No clicks
* sound: n(run(8)).scale("c:minor").s("sawtooth").lpf(200).delay(.7).orbit(2) * sound: freq("63.2388").s("sine").orbit(2).gain(4)
* duckerWithoutClick: s("bd*4").duckorbit(2).duckonset(0.003).postgain(0) * duckerWithoutClick: s("bd*4").duckorbit(2).duckattack(0.3).duckonset(0.01).postgain(0)
* @example * @example
* // Rhythmic * // Rhythmic
* noise: s("pink").distort("2:1").orbit(4) // used rhythmically with 0.3 onset below * noise: s("pink").distort("2:1").orbit(4) // used rhythmically with 0.3 onset below

View file

@ -420,7 +420,7 @@ function setOrbit(audioContext, orbit, channels) {
} }
} }
function duckOrbit(audioContext, targetOrbit, t, onsettime = 0.003, attacktime = 0.1, duckdepth = 1) { function duckOrbit(audioContext, targetOrbit, t, onsettime = 0, attacktime = 0.1, duckdepth = 1) {
const targetArr = [targetOrbit].flat(); const targetArr = [targetOrbit].flat();
const onsetArr = [onsettime].flat(); const onsetArr = [onsettime].flat();
const attackArr = [attacktime].flat(); const attackArr = [attacktime].flat();
@ -438,17 +438,18 @@ function duckOrbit(audioContext, targetOrbit, t, onsettime = 0.003, attacktime =
webAudioTimeout( webAudioTimeout(
audioContext, audioContext,
() => { () => {
gainParam.cancelScheduledValues(t); const now = audioContext.currentTime;
// cancelScheduledValues and setValueAtTime together emulate cancelAndHoldAtTime
// on browsers which lack that method
const currVal = gainParam.value; const currVal = gainParam.value;
gainParam.cancelScheduledValues(now);
gainParam.setValueAtTime(currVal, now);
const t0 = Math.max(t, now); // guard against now > t
const duckedVal = clamp(1 - Math.sqrt(depth), 0.01, currVal); const duckedVal = clamp(1 - Math.sqrt(depth), 0.01, currVal);
gainParam.exponentialRampToValueAtTime(duckedVal, t0 + onset);
// Guarantees the value is set to currVal at time t. This in conjunction with gainParam.exponentialRampToValueAtTime(1, t0 + onset + attack);
// cancelScheduledValues above emulates cancelAndHoldAtTime on browsers which lack
// that method
gainParam.setValueAtTime(currVal, t);
gainParam.exponentialRampToValueAtTime(duckedVal, t + onset);
gainParam.exponentialRampToValueAtTime(1, t + onset + attack);
}, },
0, 0,
t - 0.01, t - 0.01,

View file

@ -3170,43 +3170,43 @@ exports[`runs examples > example "duckdepth" example index 1 1`] = `
exports[`runs examples > example "duckonset" example index 0 1`] = ` exports[`runs examples > example "duckonset" example index 0 1`] = `
[ [
"[ 0/1 → 1/4 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 0/1 → 1/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 1/4 → 1/2 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 1/4 → 1/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 1/2 → 3/4 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 1/2 → 3/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 3/4 → 1/1 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 3/4 → 1/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 1/1 → 5/4 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 1/1 → 5/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 5/4 → 3/2 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 5/4 → 3/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 3/2 → 7/4 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 3/2 → 7/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 7/4 → 2/1 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 7/4 → 2/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 2/1 → 9/4 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 2/1 → 9/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 9/4 → 5/2 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 9/4 → 5/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 5/2 → 11/4 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 5/2 → 11/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 11/4 → 3/1 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 11/4 → 3/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 3/1 → 13/4 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 3/1 → 13/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 13/4 → 7/2 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 13/4 → 7/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 7/2 → 15/4 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 7/2 → 15/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
"[ 15/4 → 4/1 | s:bd duckorbit:2 duckonset:0 postgain:0 ]", "[ 15/4 → 4/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
] ]
`; `;
exports[`runs examples > example "duckonset" example index 1 1`] = ` exports[`runs examples > example "duckonset" example index 1 1`] = `
[ [
"[ 0/1 → 1/4 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 0/1 → 1/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 1/4 → 1/2 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 1/4 → 1/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 1/2 → 3/4 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 1/2 → 3/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 3/4 → 1/1 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 3/4 → 1/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 1/1 → 5/4 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 1/1 → 5/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 5/4 → 3/2 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 5/4 → 3/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 3/2 → 7/4 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 3/2 → 7/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 7/4 → 2/1 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 7/4 → 2/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 2/1 → 9/4 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 2/1 → 9/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 9/4 → 5/2 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 9/4 → 5/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 5/2 → 11/4 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 5/2 → 11/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 11/4 → 3/1 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 11/4 → 3/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 3/1 → 13/4 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 3/1 → 13/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 13/4 → 7/2 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 13/4 → 7/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 7/2 → 15/4 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 7/2 → 15/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
"[ 15/4 → 4/1 | s:bd duckorbit:2 duckonset:0.003 postgain:0 ]", "[ 15/4 → 4/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
] ]
`; `;