diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 8841b4bc..1ca49bb6 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1296,6 +1296,17 @@ generic_params.forEach(([names, ...aliases]) => { controls.createParams = (...names) => names.reduce((acc, name) => Object.assign(acc, { [name]: controls.createParam(name) }), {}); +/** + * ADSR envelope: Combination of Attack, Decay, Sustain, and Release. + * + * @name adsr + * @param {number | Pattern} time attack time in seconds + * @param {number | Pattern} time decay time in seconds + * @param {number | Pattern} gain sustain level (0 to 1) + * @param {number | Pattern} time release time in seconds + * @example + * note("").sound("sawtooth").lpf(600).adsr(".1:.1:.5:.2") + */ controls.adsr = register('adsr', (adsr, pat) => { adsr = !Array.isArray(adsr) ? [adsr] : adsr; const [attack, decay, sustain, release] = adsr; diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index d3077e24..ae1b61af 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -633,6 +633,15 @@ exports[`runs examples > example "addVoicings" example index 0 1`] = ` ] `; +exports[`runs examples > example "adsr" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c3 s:sawtooth cutoff:600 ]", + "[ 1/1 → 2/1 | note:bb2 s:sawtooth cutoff:600 ]", + "[ 2/1 → 3/1 | note:f3 s:sawtooth cutoff:600 ]", + "[ 3/1 → 4/1 | note:eb3 s:sawtooth cutoff:600 ]", +] +`; + exports[`runs examples > example "almostAlways" example index 0 1`] = ` [ "[ 0/1 → 1/8 | s:hh speed:0.5 ]", diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index fb506aad..30804240 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -82,6 +82,10 @@ Strudel uses ADSR envelopes, which are probably the most common way to describe +## adsr + + + # Filter Envelope Each filter can receive an additional filter envelope controlling the cutoff value dynamically. It uses an ADSR envelope similar to the one used for amplitude. There is an additional parameter to control the depth of the filter modulation: `lpenv`|`hpenv`|`bpenv`. This allows you to play subtle or huge filter modulations just the same by only increasing or decreasing the depth.