diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index e6169399a..f9c1852df 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -3729,6 +3729,22 @@ export const phases = (list) => { * @name FX * @memberof Pattern * @returns Pattern + * @example + * $: s("[sbd ]*4").dec(.4) + * .FX( + * phaser(0.5).gain(2), + * bpf(800), + * distort(1.3), + * room(0.2), + * delay(0.5).gain(1.25), + * distort(0.3), + * ).fxr(1.7) // sets release time of effects (like delay) + * @example + * $: s("saw").fm(0.5) + * .delay(0.3) // outer effects are applied *last* + * .FX(coarse(4)) // first coarse + * .FX(lpf(500).lpe(4).lpa(1).lpd(2)) // then lpf + * .FX(distort(1)) // then distort */ Pattern.prototype.FX = function (...effects) { effects = effects.map(reify); diff --git a/packages/superdough/modulators.mjs b/packages/superdough/modulators.mjs index 2b6fcf334..ea9067102 100644 --- a/packages/superdough/modulators.mjs +++ b/packages/superdough/modulators.mjs @@ -112,7 +112,7 @@ export const connectLFO = (id, params, nodeTracker) => { max, }; const lfoNode = getLfo(getAudioContext(), modParams); - nodeTracker[0][`lfo_${id}`] = [lfoNode]; + nodeTracker.main[`lfo_${id}`] = [lfoNode]; targetParams.forEach((t) => lfoNode.connect(t)); return lfoNode; }; @@ -134,7 +134,7 @@ export const connectEnvelope = (id, params, nodeTracker) => { decayCurve: dcurve, releaseCurve: rcurve, }); - nodeTracker[0][`env_${id}`] = [envNode]; + nodeTracker.main[`env_${id}`] = [envNode]; targetParams.forEach((t) => envNode.connect(t)); return envNode; }; diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 9e9ffa12d..7546bfab4 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -196,6 +196,7 @@ let defaultDefaultValues = { fft: 8, tremolodepth: 1, tremolophase: 0, + release: 0.01, }; const defaultDefaultDefaultValues = Object.freeze({ ...defaultDefaultValues }); @@ -473,7 +474,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) duckattack, duckdepth, djf, - release = 0.01, + release = getDefaultValue('release'), dry, delay = getDefaultValue('delay'), delayfeedback = getDefaultValue('delayfeedback'), @@ -544,7 +545,18 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) nodes.main['source'] = [sourceNode]; } else if (getSound(s)) { const { onTrigger } = getSound(s); - const onEnded = () => {}; + // We have to use onEnded because some sources (e.g. `sampler`) have + // an internal duration which is longer than `value.duration` + const onEnded = () => + webAudioTimeout( + ac, + () => { + chain.releaseNodes(); + activeSoundSources.delete(chainID); + }, + 0, + endWithRelease, + ); const soundHandle = await onTrigger(t, value, onEnded, cps); if (soundHandle) { @@ -566,7 +578,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) return; } - const chain = new Chain(sourceNode); // audio nodes that will be connected to each other sequentially + const chain = new Chain(sourceNode); // connection manager which tracks audio nodes for releasing FX = [...FX, value]; // run through the FX chain and then run through all FX outside of it as well for (let [idx, fx] of Object.entries(FX)) { const key = idx == FX.length - 1 ? 'main' : idx; @@ -990,15 +1002,6 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) } } }); - webAudioTimeout( - ac, - () => { - chain.releaseNodes(); - activeSoundSources.delete(chainID); - }, - 0, - endWithRelease, - ); }; export const superdoughTrigger = (t, hap, ct, cps) => { diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 61ab11866..f6547bc61 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -565,6 +565,52 @@ exports[`runs examples > example "_euclidRot" example index 20 1`] = ` ] `; +exports[`runs examples > example "FX" example index 0 1`] = ` +[ + "[ 0/1 → 1/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 1/8 → 1/4 | s:hh decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 1/4 → 3/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 3/8 → 1/2 | s:bd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 1/2 → 5/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 5/8 → 3/4 | s:hh decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 3/4 → 7/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 7/8 → 1/1 | s:lt decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 1/1 → 9/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 9/8 → 5/4 | s:hh decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 5/4 → 11/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 11/8 → 3/2 | s:oh decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 3/2 → 13/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 13/8 → 7/4 | s:hh decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 7/4 → 15/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 15/8 → 2/1 | s:bd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 2/1 → 17/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 17/8 → 9/4 | s:hh decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 9/4 → 19/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 19/8 → 5/2 | s:bd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 5/2 → 21/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 21/8 → 11/4 | s:hh decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 11/4 → 23/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 23/8 → 3/1 | s:bd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 3/1 → 25/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 25/8 → 13/4 | s:hh decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 13/4 → 27/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 27/8 → 7/2 | s:lt decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 7/2 → 29/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 29/8 → 15/4 | s:hh decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 15/4 → 31/8 | s:sbd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", + "[ 31/8 → 4/1 | s:bd decay:0.4 FX:[{phaserrate:0.5 gain:2} {bandf:800} {distort:1.3} {room:0.2} {delay:0.5 gain:1.25} {distort:0.3}] FXrelease:1.7 ]", +] +`; + +exports[`runs examples > example "FX" example index 1 1`] = ` +[ + "[ 0/1 → 1/1 | s:saw fmi:0.5 delay:0.3 FX:[{coarse:4} {cutoff:500 lpenv:4 lpattack:1 lpdecay:2} {distort:1}] ]", + "[ 1/1 → 2/1 | s:saw fmi:0.5 delay:0.3 FX:[{coarse:4} {cutoff:500 lpenv:4 lpattack:1 lpdecay:2} {distort:1}] ]", + "[ 2/1 → 3/1 | s:saw fmi:0.5 delay:0.3 FX:[{coarse:4} {cutoff:500 lpenv:4 lpattack:1 lpdecay:2} {distort:1}] ]", + "[ 3/1 → 4/1 | s:saw fmi:0.5 delay:0.3 FX:[{coarse:4} {cutoff:500 lpenv:4 lpattack:1 lpdecay:2} {distort:1}] ]", +] +`; + exports[`runs examples > example "accelerate" example index 0 1`] = ` [ "[ 0/1 → 2/1 | s:sax accelerate:0 ]",