jevstrudel.git / packages / core / controls.mjs

controls.mjs - Registers audio controls for pattern manipulation and effects. Copyright (C) 2022 Strudel contributors - see https://codeberg.org/uzu/strudel/src/branch/main/packages/core/controls.mjs This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.

7import { logger } from './logger.mjs';
8import { Pattern, pure, register, reify } from './pattern.mjs';
10export function createParam(names) {
11  let isMulti = Array.isArray(names);
12  names = !isMulti ? [names] : names;
13  const name = names[0];

todo: make this less confusing

16  const withVal = (xs) => {
17    let bag;
18    // check if we have an object with an unnamed control (.value)
19    if (typeof xs === 'object' && xs.value !== undefined) {
20      bag = { ...xs }; // grab props that are already there
21      xs = xs.value; // grab the unnamed control for this one
22      delete bag.value;
23    }
24    if (isMulti && Array.isArray(xs)) {
25      const result = bag || {};
26      xs.forEach((x, i) => {
27        if (i < names.length) {
28          result[names[i]] = x;
29        }
30      });
31      return result;
32    } else if (bag) {
33      bag[name] = xs;
34      return bag;
35    } else {
36      return { [name]: xs };
37    }
38  };

todo: make this less confusing

41  const func = function (value, pat) {
42    if (!pat) {
43      return reify(value).withValue(withVal);
44    }
45    if (typeof value === 'undefined') {
46      return pat.fmap(withVal);
47    }
48    return pat.set(reify(value).withValue(withVal));
49  };
50  Pattern.prototype[name] = function (value) {
51    return func(value, this);
52  };
53  return func;
54}

maps control alias names to the "main" control name

57const controlAlias = new Map();
59export function isControlName(name) {
60  return controlAlias.has(name);
61}
62
63export function registerControl(names, ...aliases) {
64  const name = Array.isArray(names) ? names[0] : names;
65  let bag = {};
66  bag[name] = createParam(names);
67  controlAlias.set(name, name);
68  aliases.forEach((alias) => {
69    bag[alias] = bag[name];
70    controlAlias.set(alias, name);
71    Pattern.prototype[alias] = Pattern.prototype[name];
72  });
73  return bag;
74}
75
76export function registerMultiControl(names, maxControls, ...aliases) {
77  names = Array.isArray(names) ? names : [names];
78  let bag = {};
79  for (let i = 1; i <= maxControls; i++) {
80    let theseAliases = [...aliases];
81    let theseNames = [...names];
82    if (i === 1) {
83      // adds e.g. fm1 as an alias for fm
84      const aliases1 = theseAliases.map((a) => `${a}1`);
85      const names1 = theseNames.map((n) => `${n}1`);
86      theseAliases = theseAliases.concat(aliases1).concat(names1);
87    } else {
88      theseAliases = theseAliases.map((a) => `${a}${i}`);
89      theseNames = theseNames.map((n) => `${n}${i}`);
90    }
91    const subBag = registerControl(theseNames, ...theseAliases);
92    bag = { ...bag, ...subBag };
93  }
94  return bag;
95}

Select a sound / sample by name. When using mininotation, you can also optionally supply 'n' and 'gain' parameters separated by ':'.

@name s @tags superdough, samples @param {string | Pattern} sound The sound / pattern of sounds to pick @synonyms sound @example s("bd hh") @example s("bd:0 bd:1 bd:0:0.3 bd:1:1.4")

111export const { s, sound } = registerControl(['s', 'n', 'gain'], 'sound');

Position in the wavetable of the wavetable oscillator

@name wt @tags wavetable, superdough @param {number | Pattern} position Position in the wavetable from 0 to 1 @synonyms wavetablePosition @example s("squelch").bank("wt_digital").seg(8).note("F1").wt("0 0.25 0.5 0.75 1")

123export const { wt, wavetablePosition } = registerControl('wt', 'wavetablePosition');

Amount of envelope applied wavetable oscillator's position envelope

@name wtenv @tags wavetable, envelope, superdough @param {number | Pattern} amount between 0 and 1

132export const { wtenv } = registerControl('wtenv');

Attack time of the wavetable oscillator's position envelope

@name wtattack @tags wavetable, envelope, superdough @synonyms wtatt @param {number | Pattern} time attack time in seconds

141export const { wtattack, wtatt } = registerControl('wtattack', 'wtatt');

Decay time of the wavetable oscillator's position envelope

@name wtdecay @tags wavetable, envelope, superdough @synonyms wtdec @param {number | Pattern} time decay time in seconds

151export const { wtdecay, wtdec } = registerControl('wtdecay', 'wtdec');

Sustain time of the wavetable oscillator's position envelope

@name wtsustain @tags wavetable, envelope, superdough @synonyms wtsus @param {number | Pattern} gain sustain level (0 to 1)

161export const { wtsustain, wtsus } = registerControl('wtsustain', 'wtsus');

Release time of the wavetable oscillator's position envelope

@name wtrelease @tags wavetable, envelope, superdough @synonyms wtrel @param {number | Pattern} time release time in seconds

171export const { wtrelease, wtrel } = registerControl('wtrelease', 'wtrel');

Rate of the LFO for the wavetable oscillator's position

@name wtrate @tags wavetable, lfo, superdough @param {number | Pattern} rate rate in hertz

180export const { wtrate } = registerControl('wtrate');

cycle synced rate of the LFO for the wavetable oscillator's position

@name wtsync @tags wavetable, lfo, superdough @param {number | Pattern} rate rate in cycles

188export const { wtsync } = registerControl('wtsync');

Depth of the LFO for the wavetable oscillator's position

@name wtdepth @tags wavetable, lfo, superdough @param {number | Pattern} depth depth of modulation

197export const { wtdepth } = registerControl('wtdepth');

Shape of the LFO for the wavetable oscillator's position

@name wtshape @tags wavetable, lfo, superdough @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..)

206export const { wtshape } = registerControl('wtshape');

DC offset of the LFO for the wavetable oscillator's position

@name wtdc @tags wavetable, lfo, superdough @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar

215export const { wtdc } = registerControl('wtdc');

Skew of the LFO for the wavetable oscillator's position

@name wtskew @tags wavetable, lfo, superdough @param {number | Pattern} skew How much to bend the LFO shape

224export const { wtskew } = registerControl('wtskew');

Amount of warp (alteration of the waveform) to apply to the wavetable oscillator

@name warp @tags wavetable, superdough @param {number | Pattern} amount Warp of the wavetable from 0 to 1 @synonyms wavetableWarp @example s("basique").bank("wt_digital").seg(8).note("F1").warp("0 0.25 0.5 0.75 1") .warpmode("spin")

237export const { warp, wavetableWarp } = registerControl('warp', 'wavetableWarp');

Attack time of the wavetable oscillator's warp envelope

@name warpattack @tags wavetable, envelope, superdough @synonyms warpatt @param {number | Pattern} time attack time in seconds

247export const { warpattack, warpatt } = registerControl('warpattack', 'warpatt');

Decay time of the wavetable oscillator's warp envelope

@name warpdecay @tags wavetable, envelope, superdough @synonyms warpdec @param {number | Pattern} time decay time in seconds

257export const { warpdecay, warpdec } = registerControl('warpdecay', 'warpdec');

Sustain time of the wavetable oscillator's warp envelope

@name warpsustain @tags wavetable, envelope, superdough @synonyms warpsus @param {number | Pattern} gain sustain level (0 to 1)

267export const { warpsustain, warpsus } = registerControl('warpsustain', 'warpsus');

Release time of the wavetable oscillator's warp envelope

@name warprelease @tags wavetable, envelope, superdough @synonyms warprel @param {number | Pattern} time release time in seconds

277export const { warprelease, warprel } = registerControl('warprelease', 'warprel');

Rate of the LFO for the wavetable oscillator's warp

@name warprate @tags wavetable, lfo, superdough @param {number | Pattern} rate rate in hertz

286export const { warprate } = registerControl('warprate');

Depth of the LFO for the wavetable oscillator's warp

@name warpdepth @tags wavetable, lfo, superdough @param {number | Pattern} depth depth of modulation

295export const { warpdepth } = registerControl('warpdepth');

Shape of the LFO for the wavetable oscillator's warp

@name warpshape @tags wavetable, lfo, superdough @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..)

304export const { warpshape } = registerControl('warpshape');

DC offset of the LFO for the wavetable oscillator's warp

@name warpdc @tags wavetable, lfo, superdough @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar

313export const { warpdc } = registerControl('warpdc');

Skew of the LFO for the wavetable oscillator's warp

@name warpskew @tags wavetable, lfo, superdough @param {number | Pattern} skew How much to bend the LFO shape

322export const { warpskew } = registerControl('warpskew');

Type of warp (alteration of the waveform) to apply to the wavetable oscillator.

The current options are: none, asym, bendp, bendm, bendmp, sync, quant, fold, pwm, orbit, spin, chaos, primes, binary, brownian, reciprocal, wormhole, logistic, sigmoid, fractal, flip

@name warpmode @tags wavetable, superdough @param {number | string | Pattern} mode Warp mode @synonyms wavetableWarpMode @example s("morgana").bank("wt_digital").seg(8).note("F1").warp("0 0.25 0.5 0.75 1") .warpmode("<asym bendp spin logistic sync wormhole brownian>*2")

339export const { warpmode, wavetableWarpMode } = registerControl('warpmode', 'wavetableWarpMode');

Amount of randomness of the initial phase of the wavetable oscillator.

@name wtphaserand @tags wavetable, superdough @param {number | Pattern} amount Randomness of the initial phase. Between 0 (not random) and 1 (fully random) @synonyms wavetablePhaseRand @example s("basique").bank("wt_digital").seg(16).wtphaserand("<0 1>")

352export const { wtphaserand, wavetablePhaseRand } = registerControl('wtphaserand', 'wavetablePhaseRand');

Amount of envelope applied wavetable oscillator's position envelope

@name warpenv @tags wavetable, envelope, superdough @param {number | Pattern} amount between 0 and 1

361export const { warpenv } = registerControl('warpenv');

cycle synced rate of the LFO for the wavetable warp position

@name warpsync @tags wavetable, lfo, superdough @param {number | Pattern} rate rate in cycles

370export const { warpsync } = registerControl('warpsync');

Define a custom webaudio node to use as a sound source.

@name source @tags external_io, superdough @synonyms src @param {function} getSource @synonyms src

382export const { source, src } = registerControl('source', 'src');

Selects the given index:

  • for samples, it picks the sample by index, with wrap around
  • for scales, it picks the scale degree
  • for voicings, it picks the voice index

@name n @tags superdough, samples, tonal @param {number | Pattern} value sample index starting from 0 @example s("bd sd [~ bd] sd,hh*6").n("<0 1>")

also see https://codeberg.org/uzu/strudel/pulls/63

396export const { n } = registerControl('n');

Selects the given degree. Currently used in xen and tune:

@name i @tags tonal @param {number | Pattern} value @example i("0 1 2 3 4 5 6 7").xen("<5edo 10edo 15edo hexany15>")

407export const { i } = registerControl('i');

Plays the given note name or midi number. A note name consists of

  • a letter (a-g or A-G)
  • optional accidentals (b or #)
  • optional (possibly negative) octave number (0-9). Defaults to 3

Examples of valid note names: c, bb, Bb, f#, c3, A4, Eb2, c#5

You can also use midi numbers instead of note names, where 69 is mapped to A4 440Hz in 12EDO.

@name note @tags tonal @example note("c a f e") @example note("c4 a4 f4 e4") @example note("60 69 65 64") @example note("fbb1 a#0 cbbb-1 e##-2").sound("saw")

431export const { note } = registerControl(['note', 'n']);

A pattern of numbers that speed up (or slow down) samples while they play. Currently only supported by osc / superdirt.

@name accelerate @tags samples, superdirt @param {number | Pattern} amount acceleration. @superdirtOnly @example s("sax").accelerate("<0 1 2 4 8 16>").slow(2).osc()

444export const { accelerate } = registerControl('accelerate');

Sets the velocity from 0 to 1. Is multiplied together with gain.

@name velocity @tags amplitude, superdough, supradough @synonyms vel @example s("hh*8") .gain(".4!2 1 .4!2 1 .4 1") .velocity(".4 1")

456export const { velocity, vel } = registerControl('velocity', 'vel');

Controls the gain by an exponential amount.

@name gain @tags amplitude, superdough, supradough @param {number | Pattern} amount gain. @example s("hh*8").gain(".4!2 1 .4!2 1 .4 1").fast(2)

467export const { gain } = registerControl('gain');

Gain applied after all effects have been processed.

@name postgain @tags amplitude, superdough, supradough @example s("bd sd [~ bd] sd,hh*8") .compressor("-20:20:10:.002:.02").postgain(1.5)

478export const { postgain } = registerControl('postgain');

Like gain, but linear.

@name amp @tags amplitude, superdirt @param {number | Pattern} amount gain. @superdirtOnly @example s("bd8").amp(".12 .5 .1*2 .5 .1 .5").osc()

490export const { amp } = registerControl('amp');

Sets the Frequency Modulation Harmonicity Ratio. Controls the timbre of the sound. Whole numbers and simple ratios sound more natural, while decimal numbers and complex ratios sound metallic.

A number may be added afterwards to control the harmonicity of any of the 8 individual FMs (e.g. fmh2)

@name fmh @tags fm, superdough, supradough @param {number | Pattern} harmonicity @example note("c e g b g e") .fm(4) .fmh("<1 2 1.5 1.61>") ._scope()

511export const { fmh, fmh1, fmh2, fmh3, fmh4, fmh5, fmh6, fmh7, fmh8 } = registerMultiControl(['fmh', 'fmi'], 8, 'fmh');

Sets the Frequency Modulation of the synth. Controls the modulation index, which defines the brightness of the sound.

A number may be added afterwards to control the modulation index of any of the 8 individual FMs (e.g. fm3). Also, FMs may be routed into each other with matrix commands like fm13, which would send fm1 back into fm3

@name fmi @tags fm, superdough, supradough @param {number | Pattern} brightness modulation index @synonyms fm @example note("c e g b g e") .fm("<0 1 2 8 32>") ._scope() @example s("sine").note("F1").seg(8) .fm(4).fm2(rand.mul(4)).fm3(saw.mul(8).slow(8)) .fmh(1.06).fmh2(10).fmh3(0.1)

536export const { fmi, fmi1, fmi2, fmi3, fmi4, fmi5, fmi6, fmi7, fmi8, fm, fm1, fm2, fm3, fm4, fm5, fm6, fm7, fm8 } =
537  registerMultiControl(['fmi', 'fmh'], 8, 'fm');

fm envelope

Ramp type of fm envelope. Exp might be a bit broken..

A number may be added afterwards to control the envelope of any of the 8 individual FMs (e.g. fmenv4)

@name fmenv @tags fm, envelope, superdough, supradough @param {number | Pattern} type lin | exp @synonyms fme @example note("c e g b g e") .fm(4) .fmdecay(.2) .fmsustain(0) .fmenv("<exp lin>") ._scope()

559export const { fmenv, fmenv1, fmenv2, fmenv3, fmenv4, fmenv5, fmenv6, fmenv7, fmenv8, fme } = registerMultiControl(
560  'fmenv',
561  8,
562  'fme',
563);

Attack time for the FM envelope: time it takes to reach maximum modulation

A number may be added afterwards to control the attack of the envelope of any of the 8 individual FMs (e.g. fmatt5)

@name fmattack @tags fm, envelope, superdough, supradough @synonyms fmatt @param {number | Pattern} time attack time @synonyms fmatt @example note("c e g b g e") .fm(4) .fmattack("<0 .05 .1 .2>") ._scope()

583export const {
584  fmattack,
585  fmattack1,
586  fmattack2,
587  fmattack3,
588  fmattack4,
589  fmattack5,
590  fmattack6,
591  fmattack7,
592  fmattack8,
593  fmatt,
594  fmatt1,
595  fmatt2,
596  fmatt3,
597  fmatt4,
598  fmatt5,
599  fmatt6,
600  fmatt7,
601  fmatt8,
602} = registerMultiControl('fmattack', 8, 'fmatt');

Waveform of the fm modulator

A number may be added afterwards to control the waveform any of the 8 individual FMs (e.g. fmwave6)

@name fmwave @tags fm, superdough, supradough @param {number | Pattern} wave waveform @example n("0 1 2 3".fast(4)).scale("d:minor").s("sine").fmwave("<sine square sawtooth crackle>").fm(4).fmh(2.01) @example n("0 1 2 3".fast(4)).chord("<Dm Am F G>").voicing().s("sawtooth").fmwave("brown").fm(.6)

619export const { fmwave, fmwave1, fmwave2, fmwave3, fmwave4, fmwave5, fmwave6, fmwave7, fmwave8 } = registerMultiControl(
620  'fmwave',
621  8,
622);

Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase.

A number may be added afterwards to control the decay of the envelope of any of the 8 individual FMs (e.g. fmdec6)

@name fmdecay @tags fm, envelope, superdough, supradough @synonyms fmdec @param {number | Pattern} time decay time @synonyms fmdec @example note("c e g b g e") .fm(4) .fmdecay("<.01 .05 .1 .2>") .fmsustain(.4) ._scope()

643export const {
644  fmdecay,
645  fmdecay1,
646  fmdecay2,
647  fmdecay3,
648  fmdecay4,
649  fmdecay5,
650  fmdecay6,
651  fmdecay7,
652  fmdecay8,
653  fmdec,
654  fmdec1,
655  fmdec2,
656  fmdec3,
657  fmdec4,
658  fmdec5,
659  fmdec6,
660  fmdec7,
661  fmdec8,
662} = registerMultiControl('fmdecay', 8, 'fmdec');

Sustain level for the FM envelope: how much modulation is applied after the decay phase

A number may be added afterwards to control the sustain of the envelope of any of the 8 individual FMs (e.g. fmsus7)

@name fmsustain @tags fm, envelope, superdough, supradough @synonyms fmsus @param {number | Pattern} level sustain level @synonyms fmsus @example note("c e g b g e") .fm(4) .fmdecay(.1) .fmsustain("<1 .75 .5 0>") ._scope()

683export const {
684  fmsustain,
685  fmsustain1,
686  fmsustain2,
687  fmsustain3,
688  fmsustain4,
689  fmsustain5,
690  fmsustain6,
691  fmsustain7,
692  fmsustain8,
693  fmsus,
694  fmsus1,
695  fmsus2,
696  fmsus3,
697  fmsus4,
698  fmsus5,
699  fmsus6,
700  fmsus7,
701  fmsus8,
702} = registerMultiControl('fmsustain', 8, 'fmsus');

Release time for the FM envelope: how much modulation is applied after the note is released

A number may be added afterwards to control the release of the envelope of any of the 8 individual FMs (e.g. fmrel8)

@name fmrelease @tags fm, envelope, superdough, supradough @synonyms fmrel @param {number | Pattern} time release time

716export const {
717  fmrelease,
718  fmrelease1,
719  fmrelease2,
720  fmrelease3,
721  fmrelease4,
722  fmrelease5,
723  fmrelease6,
724  fmrelease7,
725  fmrelease8,
726  fmrel,
727  fmrel1,
728  fmrel2,
729  fmrel3,
730  fmrel4,
731  fmrel5,
732  fmrel6,
733  fmrel7,
734  fmrel8,
735} = registerMultiControl('fmrelease', 8, 'fmrel');

FM Matrix Note: we do not declare top-level exports here since it would add ~162 more explicit exports. This is likely fine as the most common use-case would be to at least declare one other FM prior to utilizing the matrix functionality, but if we ever decide we need it, TODO to add it explicitly / go with the globalThis approach

742for (let i = 0; i <= 8; i++) {
743  for (let j = 0; j <= 8; j++) {
744    registerControl(`fmi${i}${j}`, `fm${i}${j}`);
745  }
746}

Select the sound bank to use. To be used together with s. The bank name (+ "_") will be prepended to the value of s.

@name bank @tags samples, superdough @param {string | Pattern} bank the name of the bank @example s("bd sd [~ bd] sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd")

758export const { bank } = registerControl('bank');

mix control for the chorus effect

@name chorus @tags pitch @param {string | Pattern} chorus mix amount between 0 and 1 @example note("d d a# a").s("sawtooth").chorus(.5)

770export const { chorus } = registerControl('chorus');

analyser node send amount 0 - 1 (used by scope)

773export const { analyze } = registerControl('analyze');

fftSize of analyser

775export const { fft } = registerControl('fft');

Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset.

@name attack @tags amplitude, envelope, superdough, supradough @param {number | Pattern} attack time in seconds. @synonyms att @example note("c3 e3 f3 g3").attack("<0 .1 .5>")

788export const { attack, att } = registerControl('attack', 'att');

Amplitude envelope decay time: the time it takes after the attack time to reach the sustain level. Note that the decay is only audible if the sustain value is lower than 1.

@name decay @tags amplitude, envelope, superdough, supradough @param {number | Pattern} time decay time in seconds @synonyms dec @example note("c3 e3 f3 g3").decay("<.1 .2 .3 .4>").sustain(0)

802export const { decay, dec } = registerControl('decay', 'dec');

Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset.

@name sustain @tags amplitude, envelope, superdough, supradough @param {number | Pattern} gain sustain level between 0 and 1 @synonyms sus @example note("c3 e3 f3 g3").decay(.2).sustain("<0 .1 .4 .6 1>")

814export const { sustain, sus } = registerControl('sustain', 'sus');

Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero.

@name release @tags amplitude, envelope, superdough, supradough @param {number | Pattern} time release time in seconds @synonyms rel @example note("c3 e3 g3 c4").release("<0 .1 .4 .6 1>/2")

826export const { release, rel } = registerControl('release', 'rel');
827export const { hold } = registerControl('hold');

TODO: in tidal, it seems to be normalized

Sets the center frequency of the band-pass filter. When using mininotation, you can also optionally supply the 'bpq' parameter separated by ':'.

@name bpf @tags filter, superdough, supradough @param {number | Pattern} frequency center frequency @synonyms bandf, bp @example s("bd sd [~ bd] sd,hh*6").bpf("<1000 2000 4000 8000>")

841export const { bandf, bpf, bp } = registerControl(['bandf', 'bandq', 'bpenv'], 'bpf', 'bp');

TODO: in tidal, it seems to be normalized

Sets the band-pass q-factor (resonance).

@name bpq @tags filter, superdough, supradough @param {number | Pattern} q q factor @synonyms bandq @example s("bd sd [~ bd] sd").bpf(500).bpq("<0 1 2 3>")

currently an alias of 'bandq' https://codeberg.org/uzu/strudel/issues/496 ['bpq'],

856export const { bandq, bpq } = registerControl('bandq', 'bpq');

A pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. 0.25 to cut off the first quarter from each sample.

@name begin @tags samples @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample @example samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/dirt-samples') s("rave").begin("<0 .25 .5 .75>").fast(2)

868export const { begin } = registerControl('begin');

The same as .begin, but cuts off the end off each sample.

@memberof Pattern @name end @tags samples @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc.. @example s("bd2,oh4").end("<.1 .2 .5 1>").fast(2)

880export const { end } = registerControl('end');

Loops the sample. Note that the tempo of the loop is not synced with the cycle tempo. To change the loop region, use loopBegin / loopEnd.

@name loop @tags samples @param {number | Pattern} on If 1, the sample is looped @example s("casio").loop(1)

893export const { loop } = registerControl('loop');

Begin to loop at a specific point in the sample (inbetween begin and end). Note that the loop point must be inbetween begin and end, and before loopEnd! Note: Samples starting with wt_ will automatically loop! (wt = wavetable)

@name loopBegin @tags samples @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample @synonyms loopb @example s("space").loop(1) .loopBegin("<0 .125 .25>")._scope()

907export const { loopBegin, loopb } = registerControl('loopBegin', 'loopb');

End the looping section at a specific point in the sample (inbetween begin and end). Note that the loop point must be inbetween begin and end, and after loopBegin!

@name loopEnd @tags samples @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample @synonyms loope @example s("space").loop(1) .loopEnd("<1 .75 .5 .25>")._scope()

921export const { loopEnd, loope } = registerControl('loopEnd', 'loope');

Bit crusher effect.

@name crush @tags superdough, supradough @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). @example s("<bd sd>,hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>")

['clhatdecay'],

933export const { crush } = registerControl('crush');

Fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers

@name coarse @tags superdough, supradough @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. @example s("bd sd [~ bd] sd,hh*8").coarse("<1 4 8 16 32>")

944export const { coarse } = registerControl('coarse');

Modulate the amplitude of a sound with a continuous waveform

@name tremolo @tags amplitude, lfo, superdough @synonyms trem @param {number | Pattern} speed modulation speed in HZ @example note("d d d# d".fast(4)).s("supersaw").tremolo("<3 2 100> ").tremoloskew("<.5>")

957export const { tremolo, trem } = registerControl(['tremolo', 'tremolodepth', 'tremoloskew', 'tremolophase'], 'trem');

Modulate the amplitude of a sound with a continuous waveform

@name tremolosync @tags amplitude, lfo, superdough @synonyms tremsync @param {number | Pattern} cycles modulation speed in cycles @example note("d d d# d".fast(4)).s("supersaw").tremolosync("4").tremoloskew("<1 .5 0>")

970export const { tremolosync } = registerControl(
971  ['tremolosync', 'tremolodepth', 'tremoloskew', 'tremolophase'],
972  'tremsync',
973);

Depth of amplitude modulation

@name tremolodepth @tags amplitude, lfo, superdough @synonyms tremdepth @param {number | Pattern} depth @example note("a1 a1 a#1 a1".fast(4)).s("pulse").tremsync(4).tremolodepth("<1 2 .7>")

986export const { tremolodepth } = registerControl('tremolodepth', 'tremdepth');

Alter the shape of the modulation waveform

@name tremoloskew @tags amplitude, lfo, superdough @synonyms tremskew @param {number | Pattern} amount between 0 & 1, the shape of the waveform @example note("{f a c e}%16").s("sawtooth").tremsync(4).tremoloskew("<.5 0 1>")

998export const { tremoloskew } = registerControl('tremoloskew', 'tremskew');

Alter the phase of the modulation waveform

@name tremolophase @tags amplitude, lfo, superdough @synonyms tremphase @param {number | Pattern} offset the offset in cycles of the modulation @example note("{f a c e}%16").s("sawtooth").tremsync(4).tremolophase("<0 .25 .66>")

1011export const { tremolophase } = registerControl('tremolophase', 'tremphase');

Shape of amplitude modulation

@name tremoloshape @tags amplitude, lfo, superdough @synonyms tremshape @param {number | Pattern} shape tri | square | sine | saw | ramp @example note("{f g c d}%16").tremsync(4).tremoloshape("<sine tri square>").s("sawtooth")

1024export const { tremoloshape } = registerControl('tremoloshape', 'tremshape');

Filter overdrive for supported filter types

@name drive @tags filter, superdough @param {number | Pattern} amount @example note("{f g g c d a a#}%16".sub(17)).s("supersaw").lpenv(8).lpf(150).lpq(.8).ftype('ladder').drive("<.5 4>")

1035export const { drive } = registerControl('drive');

Modulate the amplitude of an orbit to create a "sidechain" like effect.

Can be applied to multiple orbits with the ':' mininotation, e.g. duckorbit("2:3")

@name duckorbit @tags amplitude, orbit, superdough @synonyms duck @param {number | Pattern} orbit target orbit @example $: n(run(16)).scale("c:minor:pentatonic").s("sawtooth").delay(.7).orbit(2) $: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack(0.2).duckdepth(1) @example $: n(run(16)).scale("c:minor:pentatonic").s("sawtooth").delay(.7).orbit(2) $: s("hh*16").orbit(3) $: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit("2:3").duckattack(0.2).duckdepth(1)

1055export const { duck } = registerControl('duckorbit', 'duck');

The amount of ducking applied to target orbit

Can vary across orbits with the ':' mininotation, e.g. duckdepth("0.3:0.1"). Note: this requires first applying the effect to multiple orbits with e.g. duckorbit("2:3").

@name duckdepth @tags amplitude, orbit, superdough @param {number | Pattern} depth depth of modulation from 0 to 1 @example stack( n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2), s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack(0.2).duckdepth("<1 .9 .6 0>")) @example $: n(run(16)).scale("c:minor:pentatonic").s("sawtooth").delay(.7).orbit(2) $: s("hh*16").orbit(3) $: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit("2:3").duckattack(0.2).duckdepth("1:0.5")

1074export const { duckdepth } = registerControl('duckdepth');

The time required for the ducked signal(s) to reach their lowest volume. Can be used to prevent clicking or for creative rhythmic effects.

Can vary across orbits with the ':' mininotation, e.g. duckonset("0:0.003"). Note: this requires first applying the effect to multiple orbits with e.g. duckorbit("2:3").

@name duckonset @tags amplitude, envelope, orbit, superdough @synonyms duckons

@param {number | Pattern} time The onset time in seconds @example // Clicks sound: freq("63.2388").s("sine").orbit(2).gain(4) duckerWithClick: s("bd4").duckorbit(2).duckattack(0.3).duckonset(0).postgain(0) @example // No clicks sound: freq("63.2388").s("sine").orbit(2).gain(4) duckerWithoutClick: s("bd4").duckorbit(2).duckattack(0.3).duckonset(0.01).postgain(0) @example // Rhythmic noise: s("pink").distort("2:1").orbit(4) // used rhythmically with 0.3 onset below hhat: s("hh16").orbit(7) ducker: s("bd4").bank("tr909").duckorbit("4:7").duckonset("0.3:0.003").duckattack(0.25)

1103export const { duckonset } = registerControl('duckonset', 'duckons');

The time required for the ducked signal(s) to return to their normal volume.

Can vary across orbits with the ':' mininotation, e.g. duckonset("0:0.003"). Note: this requires first applying the effect to multiple orbits with e.g. duckorbit("2:3").

@name duckattack @tags amplitude, envelope, orbit, superdough @synonyms duckatt, datt

@param {number | Pattern} time The attack time in seconds @example sound: n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2) ducker: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack("<0.2 0 0.4>").duckdepth(1) @example moreduck: n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2) lessduck: s("hh*16").orbit(5) ducker: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit("2:5").duckattack("0.4:0.1")

1125export const { duckattack } = registerControl('duckattack', 'duckatt', 'datt');

Create byte beats with custom expressions

@name byteBeatExpression @synonyms bbexpr, bb @tags superdough

@param {number | Pattern} byteBeatExpression bitwise expression for creating bytebeat @example s("bytebeat").bbexpr('t*(t>>15^t>>66)')

1139export const { byteBeatExpression, bbexpr } = registerControl('byteBeatExpression', 'bbexpr', 'bb');

Create byte beats with custom expressions

@name byteBeatStartTime @synonyms bbst @tags superdough

@param {number | Pattern} byteBeatStartTime in samples (t) @example note("c3!8".add("{0 0 12 0 7 5 3}%8")).s("bytebeat:5").bbst("<3 1>".mul(10000))._scope()

1153export const { byteBeatStartTime, bbst } = registerControl('byteBeatStartTime', 'bbst');

Allows you to set the output channels on the interface

@name channels @tags external_io, superdough @synonyms ch

@param {number | Pattern} channels pattern the output channels @example note("e a d b g").channels("3:4")

1167export const { channels, ch } = registerControl('channels', 'ch');

Controls the pulsewidth of the pulse oscillator

@name pw @tags superdough @param {number | Pattern} pulsewidth @example note("{f a c e}%16").s("pulse").pw(".8:1:.2") @example n(run(8)).scale("D:pentatonic").s("pulse").pw("0 .75 .5 1")

1180export const { pw } = registerControl(['pw', 'pwrate', 'pwsweep']);

Controls the lfo rate for the pulsewidth of the pulse oscillator

@name pwrate @synonyms pwr @tags superdough, lfo @param {number | Pattern} rate @example n(run(8)).scale("D:pentatonic").s("pulse").pw("0.5").pwrate("<5 .1 25>").pwsweep("<0.3 .8>")

1194export const { pwrate } = registerControl('pwrate', 'pwr');

Controls the lfo sweep for the pulsewidth of the pulse oscillator

@name pwsweep @synonyms pws @tags superdough, lfo @param {number | Pattern} sweep @example n(run(8)).scale("D:pentatonic").s("pulse").pw("0.5").pwrate("<5 .1 25>").pwsweep("<0.3 .8>")

1207export const { pwsweep } = registerControl('pwsweep', 'pws');

Phaser audio effect that approximates popular guitar pedals.

@name phaser @tags superdough @synonyms ph @param {number | Pattern} speed speed of modulation @example n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5) .phaser("<1 2 4 8>")

1221export const { phaserrate, ph, phaser } = registerControl(
1222  ['phaserrate', 'phaserdepth', 'phasercenter', 'phasersweep'],
1223  'ph',
1224  'phaser',
1225);

The frequency sweep range of the lfo for the phaser effect. Defaults to 2000

@name phasersweep @tags superdough, lfo @synonyms phs @param {number | Pattern} phasersweep most useful values are between 0 and 4000 @example n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5) .phaser(2).phasersweep("<800 2000 4000>")

1239export const { phasersweep, phs } = registerControl('phasersweep', 'phs');

The center frequency of the phaser in HZ. Defaults to 1000

@name phasercenter @tags superdough @synonyms phc @param {number | Pattern} centerfrequency in HZ @example n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5) .phaser(2).phasercenter("<800 2000 4000>")

1254export const { phasercenter, phc } = registerControl('phasercenter', 'phc');

The amount the signal is affected by the phaser effect. Defaults to 0.75

@name phaserdepth @tags superdough, superdirt @synonyms phd, phasdp @param {number | Pattern} depth number between 0 and 1 @example n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5) .phaser(2).phaserdepth("<0 .5 .75 1>")

also a superdirt control

1269export const { phaserdepth, phd, phasdp } = registerControl('phaserdepth', 'phd', 'phasdp');

Choose the channel the pattern is sent to

@name channel @tags superdough @param {number | Pattern} channel channel number

1279export const { channel } = registerControl('channel');

In the style of classic drum-machines, cut will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open.

@name cut @tags superdough @param {number | Pattern} group cut group number @example s("[oh hh]*4").cut(1)

1290export const { cut } = registerControl('cut');

Applies the cutoff frequency of the low-pass filter.

When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'.

@name lpf @tags filter, superdough, supradough @param {number | Pattern} frequency audible between 0 and 20000 @synonyms cutoff, ctf, lp @example s("bd sd [~ bd] sd,hh6").lpf("<4000 2000 1000 500 200 100>") @example s("bd16").lpf("1000:0 1000:10 1000:20 1000:30")

1306export const { cutoff, ctf, lpf, lp } = registerControl(['cutoff', 'resonance', 'lpenv'], 'ctf', 'lpf', 'lp');

Sets the lowpass filter envelope modulation depth. @name lpenv @tags filter, envelope, superdough, supradough @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and n @synonyms lpe @example note("c2 e2 f2 g2") .sound('sawtooth') .lpf(300) .lpa(.5) .lpenv("<4 2 1 0 -1 -2 -4>/4")

1321export const { lpenv, lpe } = registerControl('lpenv', 'lpe');

Sets the highpass filter envelope modulation depth. @name hpenv @tags filter, envelope, superdough, supradough @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and n @synonyms hpe @example note("c2 e2 f2 g2") .sound('sawtooth') .hpf(500) .hpa(.5) .hpenv("<4 2 1 0 -1 -2 -4>/4")

1335export const { hpenv, hpe } = registerControl('hpenv', 'hpe');

Sets the bandpass filter envelope modulation depth. @name bpenv @tags filter, envelope, superdough, supradough @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and n @synonyms bpe @example note("c2 e2 f2 g2") .sound('sawtooth') .bpf(500) .bpa(.5) .bpenv("<4 2 1 0 -1 -2 -4>/4")

1349export const { bpenv, bpe } = registerControl('bpenv', 'bpe');

Sets the attack duration for the lowpass filter envelope. @name lpattack @tags filter, envelope, superdough, supradough @param {number | Pattern} attack time of the filter envelope @synonyms lpa @example note("c2 e2 f2 g2") .sound('sawtooth') .lpf(300) .lpa("<.5 .25 .1 .01>/4") .lpenv(4)

1363export const { lpattack, lpa } = registerControl('lpattack', 'lpa');

Sets the attack duration for the highpass filter envelope. @name hpattack @tags filter, envelope, superdough, supradough @param {number | Pattern} attack time of the highpass filter envelope @synonyms hpa @example note("c2 e2 f2 g2") .sound('sawtooth') .hpf(500) .hpa("<.5 .25 .1 .01>/4") .hpenv(4)

1377export const { hpattack, hpa } = registerControl('hpattack', 'hpa');

Sets the attack duration for the bandpass filter envelope. @name bpattack @tags filter, envelope, superdough, supradough @param {number | Pattern} attack time of the bandpass filter envelope @synonyms bpa @example note("c2 e2 f2 g2") .sound('sawtooth') .bpf(500) .bpa("<.5 .25 .1 .01>/4") .bpenv(4)

1391export const { bpattack, bpa } = registerControl('bpattack', 'bpa');

Sets the decay duration for the lowpass filter envelope. @name lpdecay @tags filter, envelope, superdough, supradough @param {number | Pattern} decay time of the filter envelope @synonyms lpd @example note("c2 e2 f2 g2") .sound('sawtooth') .lpf(300) .lpd("<.5 .25 .1 0>/4") .lpenv(4)

1405export const { lpdecay, lpd } = registerControl('lpdecay', 'lpd');

Sets the decay duration for the highpass filter envelope. @name hpdecay @tags filter, envelope, superdough, supradough @param {number | Pattern} decay time of the highpass filter envelope @synonyms hpd @example note("c2 e2 f2 g2") .sound('sawtooth') .hpf(500) .hpd("<.5 .25 .1 0>/4") .hps(0.2) .hpenv(4)

1420export const { hpdecay, hpd } = registerControl('hpdecay', 'hpd');

Sets the decay duration for the bandpass filter envelope. @name bpdecay @tags filter, envelope, superdough, supradough @param {number | Pattern} decay time of the bandpass filter envelope @synonyms bpd @example note("c2 e2 f2 g2") .sound('sawtooth') .bpf(500) .bpd("<.5 .25 .1 0>/4") .bps(0.2) .bpenv(4)

1435export const { bpdecay, bpd } = registerControl('bpdecay', 'bpd');

Sets the sustain amplitude for the lowpass filter envelope. @name lpsustain @tags filter, envelope, superdough, supradough @param {number | Pattern} sustain amplitude of the lowpass filter envelope @synonyms lps @example note("c2 e2 f2 g2") .sound('sawtooth') .lpf(300) .lpd(.5) .lps("<0 .25 .5 1>/4") .lpenv(4)

1450export const { lpsustain, lps } = registerControl('lpsustain', 'lps');

Sets the sustain amplitude for the highpass filter envelope. @name hpsustain @tags filter, envelope, superdough, supradough @param {number | Pattern} sustain amplitude of the highpass filter envelope @synonyms hps @example note("c2 e2 f2 g2") .sound('sawtooth') .hpf(500) .hpd(.5) .hps("<0 .25 .5 1>/4") .hpenv(4)

1465export const { hpsustain, hps } = registerControl('hpsustain', 'hps');

Sets the sustain amplitude for the bandpass filter envelope. @name bpsustain @tags filter, envelope, superdough, supradough @param {number | Pattern} sustain amplitude of the bandpass filter envelope @synonyms bps @example note("c2 e2 f2 g2") .sound('sawtooth') .bpf(500) .bpd(.5) .bps("<0 .25 .5 1>/4") .bpenv(4)

1480export const { bpsustain, bps } = registerControl('bpsustain', 'bps');

Sets the release time for the lowpass filter envelope. @name lprelease @tags filter, envelope, superdough, supradough @param {number | Pattern} release time of the filter envelope @synonyms lpr @example note("c2 e2 f2 g2") .sound('sawtooth') .clip(.5) .lpf(300) .lpenv(4) .lpr("<.5 .25 .1 0>/4") .release(.5)

1496export const { lprelease, lpr } = registerControl('lprelease', 'lpr');

Sets the release time for the highpass filter envelope. @name hprelease @tags filter, envelope, superdough, supradough @param {number | Pattern} release time of the highpass filter envelope @synonyms hpr @example note("c2 e2 f2 g2") .sound('sawtooth') .clip(.5) .hpf(500) .hpenv(4) .hpr("<.5 .25 .1 0>/4") .release(.5)

1512export const { hprelease, hpr } = registerControl('hprelease', 'hpr');

Sets the release time for the bandpass filter envelope. @name bprelease @tags filter, envelope, superdough, supradough @param {number | Pattern} release time of the bandpass filter envelope @synonyms bpr @example note("c2 e2 f2 g2") .sound('sawtooth') .clip(.5) .bpf(500) .bpenv(4) .bpr("<.5 .25 .1 0>/4") .release(.5)

1528export const { bprelease, bpr } = registerControl('bprelease', 'bpr');

Sets the filter type. The ladder filter is more aggressive. More types might be added in the future. @name ftype @tags filter, superdough @param {number | Pattern} type 12db (0), ladder (1), or 24db (2) @example note("{f g g c d a a#}%8").s("sawtooth").lpenv(4).lpf(500).ftype("<0 1 2>").lpq(1) @example note("c f g g a c d4").fast(2) .sound('sawtooth') .lpf(200).fanchor(0) .lpenv(3).lpq(1) .ftype("<ladder 12db 24db>")

1543export const { ftype } = registerControl('ftype');

controls the center of the filter envelope. 0 is unipolar positive, .5 is bipolar, 1 is unipolar negative @name fanchor @tags filter, envelope, superdough @param {number | Pattern} center 0 to 1 @example note("{f g g c d a a#}%8").s("sawtooth").lpf("{1000}%2") .lpenv(8).fanchor("<0 .5 1>")

1554export const { fanchor } = registerControl('fanchor');

Applies the cutoff frequency of the high-pass filter.

When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'.

@name hpf @tags filter, superdough, supradough @param {number | Pattern} frequency audible between 0 and 20000 @synonyms hp, hcutoff @example s("bd sd [~ bd] sd,hh8").hpf("<4000 2000 1000 500 200 100>") @example s("bd sd [~ bd] sd,hh8").hpf("<2000 2000:25>")

currently an alias of 'hcutoff' https://codeberg.org/uzu/strudel/issues/496 ['hpf'],

Rate of the LFO for the lowpass filter

@name lprate @tags filter, lfo, superdough @param {number | Pattern} rate rate in hertz @example note("<c c c# c c c4>*16").s("sawtooth").lpf(600).lprate("<4 8 2 1>")

1582export const { lprate } = registerControl('lprate');

Cycle-synced rate of the LFO for the lowpass filter

@name lpsync @tags filter, lfo, superdough @param {number | Pattern} rate rate in cycles @example note("<c c c# c c c4>*16").s("sawtooth").lpf(600).lpsync("<4 8 2 1>")

1593export const { lpsync } = registerControl('lpsync');

Depth of the LFO for the lowpass filter

@name lpdepth @tags filter, lfo, superdough @param {number | Pattern} depth depth of modulation @example note("<c c c# c c c4>*16").s("sawtooth").lpf(600).lpdepth("<1 .5 1.8 0>")

1605export const { lpdepth } = registerControl('lpdepth');

Depth of the LFO for the lowpass filter, in HZ

@name lpdepthfrequency @tags filter, lfo, superdough @synonyms lpdepthfreq @param {number | Pattern} depth depth of modulation @example note("<c c c# c c c4>*16").s("sawtooth").lpf(600).lpdepthfrequency("<200 500 100 0>")

1617export const { lpdepthfrequency, lpdepthfreq } = registerControl('lpdepthfrequency', 'lpdepthfreq');

Shape of the LFO for the lowpass filter

@name lpshape @tags filter, lfo, superdough @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..)

1626export const { lpshape } = registerControl('lpshape');

DC offset of the LFO for the lowpass filter

@name lpdc @tags filter, lfo, superdough @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar

1635export const { lpdc } = registerControl('lpdc');

Skew of the LFO for the lowpass filter

@name lpskew @tags filter, lfo, superdough @param {number | Pattern} skew How much to bend the LFO shape

1644export const { lpskew } = registerControl('lpskew');

Rate of the LFO for the bandpass filter

@name bprate @tags filter, lfo, superdough @param {number | Pattern} rate rate in hertz

1653export const { bprate } = registerControl('bprate');

Cycle-synced rate of the LFO for the bandpass filter

@name bpsync @tags filter, lfo, superdough @param {number | Pattern} rate rate in cycles

1662export const { bpsync } = registerControl('bpsync');

Depth of the LFO for the bandpass filter

@name bpdepth @tags filter, lfo, superdough @param {number | Pattern} depth depth of modulation

1671export const { bpdepth } = registerControl('bpdepth');

Depth of the LFO for the bandpass filter, in HZ

@name bpdepthfrequency @tags filter, lfo, superdough @synonyms bpdepthfreq @param {number | Pattern} depth depth of modulation @example note("<c c c# c c c4>*16").s("sawtooth").lpf(600).bpdepthfrequency("<200 500 100 0>")

1684export const { bpdepthfrequency, bpdepthfreq } = registerControl('bpdepthfrequency', 'bpdepthfreq');

Shape of the LFO for the bandpass filter

@name bpshape @tags filter, lfo, superdough @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..)

1693export const { bpshape } = registerControl('bpshape');

DC offset of the LFO for the bandpass filter

@name bpdc @tags filter, lfo, superdough @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar

1702export const { bpdc } = registerControl('bpdc');

Skew of the LFO for the bandpass filter

@name bpskew @tags filter, lfo, superdough @param {number | Pattern} skew How much to bend the LFO shape

1711export const { bpskew } = registerControl('bpskew');

Rate of the LFO for the highpass filter

@name hprate @tags filter, lfo, superdough @param {number | Pattern} rate rate in hertz

1720export const { hprate } = registerControl('hprate');

Cycle-synced rate of the LFO for the highpass filter

@name hpsync @tags filter, lfo, superdough @param {number | Pattern} rate rate in cycles

1729export const { hpsync } = registerControl('hpsync');

Depth of the LFO for the highpass filter

@name hpdepth @tags filter, lfo, superdough @param {number | Pattern} depth depth of modulation

1738export const { hpdepth } = registerControl('hpdepth');

Depth of the LFO for the hipass filter, in hz

@name hpdepthfrequency @tags filter, lfo, superdough @synonyms hpdepthfreq @param {number | Pattern} depth depth of modulation @example note("<c c c# c c c4>*16").s("sawtooth").lpf(600).hpdepthfrequency("<200 500 100 0>")

1751export const { hpdepthfrequency, hpdepthfreq } = registerControl('hpdepthfrequency', 'hpdepthfreq');

Shape of the LFO for the highpass filter

@name hpshape @tags filter, lfo, superdough @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..)

1760export const { hpshape } = registerControl('hpshape');

DC offset of the LFO for the highpass filter

@name hpdc @tags filter, lfo, superdough @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar

1769export const { hpdc } = registerControl('hpdc');

Skew of the LFO for the highpass filter

@name hpskew @tags filter, lfo, superdough @param {number | Pattern} skew How much to bend the LFO shape

1778export const { hpskew } = registerControl('hpskew');

Applies a vibrato to the frequency of the oscillator.

@name vib @tags pitch, lfo, superdough, supradough @synonyms vibrato, v @param {number | Pattern} frequency of the vibrato in hertz @example note("a e") .vib("<.5 1 2 4 8 16>") ._scope() @example // change the modulation depth with ":" note("a e") .vib("<.5 1 2 4 8 16>:12") ._scope()

1797export const { vib, vibrato, v } = registerControl(['vib', 'vibmod'], 'vibrato', 'v');

Adds pink noise to the mix

@name noise @tags generators, superdough, supradough @param {number | Pattern} wet wet amount @example sound("<white pink brown>/2")

1807export const { noise } = registerControl('noise');

Sets the vibrato depth in semitones. Only has an effect if vibrato | vib | v is is also set

@name vibmod @tags pitch, lfo, superdough, supradough @synonyms vmod @param {number | Pattern} depth of vibrato (in semitones) @example note("a e").vib(4) .vibmod("<.25 .5 1 2 12>") ._scope() @example // change the vibrato frequency with ":" note("a e") .vibmod("<.25 .5 1 2 12>:8") ._scope()

1825export const { vibmod, vmod } = registerControl(['vibmod', 'vib'], 'vmod');
1826export const { hcutoff, hpf, hp } = registerControl(['hcutoff', 'hresonance', 'hpenv'], 'hpf', 'hp');

Controls the high-pass q-value.

@name hpq @tags filter, superdough, supradough @param {number | Pattern} q resonance factor between 0 and 50 @synonyms hresonance @example s("bd sd [~ bd] sd,hh*8").hpf(2000).hpq("<0 10 20 30>")

1838export const { hresonance, hpq } = registerControl('hresonance', 'hpq');

Controls the low-pass q-value.

@name lpq @tags filter, superdough, supradough @param {number | Pattern} q resonance factor between 0 and 50 @synonyms resonance @example s("bd sd [~ bd] sd,hh*8").lpf(2000).lpq("<0 10 20 30>")

currently an alias of 'resonance' https://codeberg.org/uzu/strudel/issues/496

1851export const { resonance, lpq } = registerControl('resonance', 'lpq');

DJ filter, below 0.5 is low pass filter, above is high pass filter.

@name djf @tags filter, superdough @param {number | Pattern} cutoff below 0.5 is low pass filter, above is high pass filter @example n(irand(16).seg(8)).scale("d:phrygian").s("supersaw").djf("<.5 .3 .2 .75>")

1862export const { djf } = registerControl('djf');

['cutoffegint'], TODO: does not seem to work

Sets the level of the delay signal.

When using mininotation, you can also optionally add the 'delaytime' and 'delayfeedback' parameter, separated by ':'.

@name delay @tags orbit, superdough, supradough @param {number | Pattern} level between 0 and 1 @example s("bd bd").delay("<0 .25 .5 1>") @example s("bd bd").delay("0.65:0.25:0.9 0.65:0.125:0.7")

1881export const { delay } = registerControl(['delay', 'delaytime', 'delayfeedback']);

Sets the level of the signal that is fed back into the delay. Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it

@name delayfeedback @tags orbit, superdough, supradough @param {number | Pattern} feedback between 0 and 1 @synonyms delayfb, dfb @example s("bd").delay(.25).delayfeedback("<.25 .5 .75 1>")

1894export const { delayfeedback, delayfb, dfb } = registerControl('delayfeedback', 'delayfb', 'dfb');

Sets the time of the delay effect.

@name delayspeed @tags supradough @param {number | Pattern} delayspeed controls the pitch of the delay feedback @synonyms delayt, dt @example note("d d a# a".fast(2)).s("sawtooth").delay(.8).delaytime(1/2).delayspeed("<2 .5 -1 -2>")

1907export const { delayspeed } = registerControl('delayspeed');

Sets the time of the delay effect in seconds.

@name delaytime @tags orbit, superdough, supradough @param {number | Pattern} delay in seconds @synonyms delayt, dt @example note("d d a# a".fast(2)) .s("sawtooth") .delay(.8) .delaytime(1/2) .delayspeed("<2 .5 -1 -2>")

1923export const { delaytime, delayt, dt } = registerControl('delaytime', 'delayt', 'dt');

Sets the time of the delay effect in cycles.

@name delaysync @tags orbit, superdough @param {number | Pattern} cycles delay length in cycles @synonyms delays, ds @example s("bd bd").delay(.25).delaysync("<1 2 3 5>".div(8))

1936export const { delaysync } = registerControl('delaysync', 'delays', 'ds');

Specifies whether delaytime is calculated relative to cps.

@name lock @tags superdirt @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. @superdirtOnly @example s("sd").delay().lock(1).osc()

1951export const { lock } = registerControl('lock');

Set detune for stacked voices of supported oscillators.

@name detune @tags pitch, superdough @param {number | Pattern} amount @synonyms det @example note("d f a a# a d3").fast(2).s("supersaw").detune("<.1 .2 .5 24.1>")

1963export const { detune, det } = registerControl('detune', 'det');

Set number of stacked voices for supported oscillators.

@name unison @tags superdough @param {number | Pattern} numvoices @example note("d f a a# a d3").fast(2).s("supersaw").unison("<1 2 7>")

1974export const { unison } = registerControl('unison');

Set the stereo pan spread for supported oscillators

@name spread @tags superdough @param {number | Pattern} spread between 0 and 1 @example note("d f a a# a d3").fast(2).s("supersaw").spread("<0 .3 1>")

1986export const { spread } = registerControl('spread');

Set dryness of reverb. See room and size for more information about reverb.

@name dry @tags superdirt @param {number | Pattern} dry 0 = wet, 1 = dry @example n("0,3,7").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() @superdirtOnly

1998export const { dry } = registerControl('dry');

Used when using begin/end or chop/striate and friends, to change the fade out time of the 'grain' envelope.

@name fadeTime @tags superdirt @synonyms fadeOutTime @param {number | Pattern} time between 0 and 1 @example s("oh*4").end(.1).fadeTime("<0 .2 .4 .8>").osc()

2010export const { fadeTime, fadeOutTime } = registerControl('fadeTime', 'fadeOutTime');
2011export const { fadeInTime } = registerControl('fadeInTime');

Set frequency of sound.

@name freq @tags pitch, superdough @param {number | Pattern} frequency in Hz. the audible range is between 20 and 20000 Hz @example freq("220 110 440 110").s("superzow").osc() @example freq("110".mul.out(".5 1.5 .6 [2 3]")).s("superzow").osc()

2024export const { freq } = registerControl('freq');

pitch envelope

Attack time of pitch envelope.

@name pattack @tags pitch, envelope, superdough, supradough @synonyms patt @param {number | Pattern} time time in seconds @example note("c eb g bb").pattack("0 .1 .25 .5").slow(2)

2037export const { pattack, patt } = registerControl('pattack', 'patt');

Decay time of pitch envelope.

@name pdecay @tags pitch, envelope, superdough, supradough @synonyms pdec @param {number | Pattern} time time in seconds @example note("<c eb g bb>").pdecay("<0 .1 .25 .5>")

2049export const { pdecay, pdec } = registerControl('pdecay', 'pdec');

TODO: how to use psustain?!

2051export const { psustain, psus } = registerControl('psustain', 'psus');

Release time of pitch envelope

@name prelease @tags pitch, envelope, superdough, supradough @synonyms prel @param {number | Pattern} time time in seconds @example note("<c eb g bb> ~") .release(.5) // to hear the pitch release .prelease("<0 .1 .25 .5>")

2065export const { prelease, prel } = registerControl('prelease', 'prel');

Amount of pitch envelope. Negative values will flip the envelope. If you don't set other pitch envelope controls, pattack:.2 will be the default.

@name penv @tags pitch, envelope, superdough, supradough @param {number | Pattern} semitones change in semitones @example note("c") .penv("<12 7 1 .5 0 -1 -7 -12>")

2078export const { penv } = registerControl('penv');

Curve of envelope. Defaults to linear. exponential is good for kicks

@name pcurve @tags pitch, envelope, superdough @param {number | Pattern} type 0 = linear, 1 = exponential @example note("g1*4") .s("sine").pdec(.5) .penv(32) .pcurve("<0 1>")

2092export const { pcurve } = registerControl('pcurve');

Sets the range anchor of the envelope:

  • anchor 0: range = [note, note + penv]
  • anchor 1: range = [note - penv, note] If you don't set an anchor, the value will default to the psustain value.

@name panchor @tags pitch, envelope, superdough @param {number | Pattern} anchor anchor offset @example note("c c4").penv(12).panchor("<0 .5 1 .5>")

2106export const { panchor } = registerControl('panchor');

TODO: https://tidalcycles.org/docs/configuration/MIDIOSC/control-voltage/#gate

2108export const { gate, gat } = registerControl('gate', 'gat');

['hatgrain'], ['lagogo'], ['lclap'], ['lclaves'], ['lclhat'], ['lcrash'], TODO: https://tidalcycles.org/docs/reference/audio_effects/#leslie-1 https://tidalcycles.org/docs/reference/audio_effects/#leslie

Emulation of a Leslie speaker: speakers rotating in a wooden amplified cabinet.

@name leslie @tags superdirt @param {number | Pattern} wet between 0 and 1 @example n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() @superdirtOnly

2129export const { leslie } = registerControl('leslie');

Rate of modulation / rotation for leslie effect

@name lrate @tags superdirt @param {number | Pattern} rate 6.7 for fast, 0.7 for slow @example n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() @superdirtOnly

TODO: the rate seems to "lag" (in the example, 1 will be fast)

2142export const { lrate } = registerControl('lrate');

Physical size of the cabinet in meters. Be careful, it might be slightly larger than your computer. Affects the Doppler amount (pitch warble)

@name lsize @tags superdirt @param {number | Pattern} meters somewhere between 0 and 1 @example n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() @superdirtOnly

2154export const { lsize } = registerControl('lsize');

Sets the displayed text for an event on the pianoroll

@name label @tags visualization @param {string} label text to display

2162export const { activeLabel } = registerControl('activeLabel');
2163export const { label } = registerControl(['label', 'activeLabel']);

['lfo'], ['lfocutoffint'], ['lfodelay'], ['lfoint'], ['lfopitchint'], ['lfoshape'], ['lfosync'], ['lhitom'], ['lkick'], ['llotom'], ['lophat'], ['lsnare'], TODO: what is this? not found in tidal doc

2177export const { degree } = registerControl('degree');

TODO: what is this? not found in tidal doc

2179export const { mtranspose } = registerControl('mtranspose');

TODO: what is this? not found in tidal doc

2181export const { ctranspose } = registerControl('ctranspose');

TODO: what is this? not found in tidal doc

2183export const { harmonic } = registerControl('harmonic');

TODO: what is this? not found in tidal doc

2185export const { stepsPerOctave } = registerControl('stepsPerOctave');

TODO: what is this? not found in tidal doc

2187export const { octaveR } = registerControl('octaveR');

TODO: why is this needed? what's the difference to late / early? Answer: it's in seconds, and delays the message at OSC time (so can't be negative, at least not beyond the latency value)

2190export const { nudge } = registerControl('nudge');

TODO: the following doc is just a guess, it's not documented in tidal doc.

Sets the default octave of a synth.

@name octave @tags superdirt @synonyms oct @param {number | Pattern} octave octave number @example n("0,4,7").scale("F:minor").s('supersaw').octave("<0 1 2 3>")

2202export const { octave, oct } = registerControl('octave', 'oct');

['ophatdecay'], TODO: example

An orbit is a global parameter context for patterns. Patterns with the same orbit will share the same global effects.

@name orbit @tags superdough @synonyms o @param {number | Pattern} number @example stack( s("hh*6").delay(.5).delaytime(.25).orbit(1), s("~ sd ~ sd").delay(.5).delaytime(.125).orbit(2) )

2219export const { orbit } = registerControl('orbit', 'o');

A bus is a send which can be used for mixing patterns. It combines with.. s("bus") to play that bus through another pattern (for, say, applying non-linear effects like distortion to multiple signals)

otherPat.bmod(..) (to modulate another pattern with the bus)

@name bus @tags superdirt @param {number | Pattern} number

2232export const { bus } = registerControl('bus');

Postgain multiplier prior to sending the signal to the audio bus.

@name busgain @tags superdirt @synonyms bgain @param {number | Pattern} number

2242export const { busgain, bgain } = registerControl('busgain', 'bgain');

TODO: what is this? not found in tidal doc Answer: gain is limited to maximum of 2. This allows you to go over that

2245export const { overgain } = registerControl('overgain');

TODO: what is this? not found in tidal doc. Similar to above, but limited to 1

2247export const { overshape } = registerControl('overshape');

Sets position in stereo.

@name pan @tags superdough, supradough @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel) @example s("[bd hh]*2").pan("<.5 1 .5 0>") @example s("bd rim sd rim bd ~ cp rim").pan(sine.slow(2))

2260export const { pan } = registerControl('pan');

Controls how much multichannel output is fanned out

@name panspan @tags superdirt @param {number | Pattern} span between -inf and inf, negative is backwards ordering @example s("[bd hh]*2").pan("<.5 1 .5 0>").panspan("<0 .5 1>").osc()

2271export const { panspan } = registerControl('panspan');

Controls how much multichannel output is spread

@name pansplay @tags superdirt @param {number | Pattern} spread between 0 and 1 @example s("[bd hh]*2").pan("<.5 1 .5 0>").pansplay("<0 .5 1>").osc()

2282export const { pansplay } = registerControl('pansplay');
2283export const { panwidth } = registerControl('panwidth');
2284export const { panorient } = registerControl('panorient');

['pitch1'], ['pitch2'], ['pitch3'], ['portamento'],

TODO: slide param for certain synths

2291export const { slide } = registerControl('slide');

TODO: detune? https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare

2293export const { semitone } = registerControl('semitone');

TODO: synth param

2296export const { voice } = registerControl('voice');

voicings // https://codeberg.org/uzu/strudel/issues/506

The chord to voice @name chord @tags tonal @param {string | Pattern} symbols chord symbols to voice e.g., C, Eb, Fm7, G7. The symbols can be defined via addVoicings @example chord("<Am C D F Am E Am E>").voicing()

2306export const { chord } = registerControl('chord');

Which dictionary to use for the voicings. This falls back to the default dictionary if not provided

@name dictionary @tags tonal @param {string} dictionaryName which dictionary (having been defined with addVoicings) to use @example addVoicings('house', { '': ['7 12 16', '0 7 16', '4 7 12'], 'm': ['0 3 7'] }) chord("<Am C D F Am E Am E>") .dict('house').anchor(66) .voicing().room(.5)

2322export const { dictionary, dict } = registerControl('dictionary', 'dict');

The top note to align the voicing to. Defaults to c5

@name anchor @tags tonal @param {string | Pattern} anchorNote the note to align the voicing or scale to @example anchor("<c4 g4 c5 g5>").chord("C").voicing() @example n("0 .. 7").anchor("<c4 g4 c5 g5>").scale("<C:major F:minor>")

2333export const { anchor } = registerControl('anchor');

Sets how the voicing is offset from the anchored position

@name offset @tags tonal @param {number | Pattern} shift the amount to shift the voicing up or down @example chord("<Am C D F Am E Am E>").offset("<0 1 2 3 4 5>") // alter the voicing each time

2343export const { offset } = registerControl('offset');

How many octaves are voicing steps spread apart, defaults to 1

@name octaves @tags tonal @param {number | Pattern} count the number of octaves @example chord("<Am C D F Am E Am E>").octaves("<2 4>").voicing()

2353export const { octaves } = registerControl('octaves');

How the voicing is aligned to the anchor

  • below: top note <= anchor

  • duck: top note <= anchor, anchor excluded

  • above: bottom note >= anchor

  • root: bottom note is the lowest root of the chord >= anchor

  • oldabove : old (buggy) behavior of above, kept for legacy reason

  • oldroot : old (buggy) behavior of root, kept for legacy reason

@name mode @tags tonal @param {string | Pattern} modeName one of {below | above | duck | root | oldabove | oldroot} @example mode("<below above duck root>").chord("C").voicing()

2371export const { mode } = registerControl(['mode', 'anchor']);

Sets the level of reverb.

When using mininotation, you can also optionally add the 'size' parameter, separated by ':'.

@name room @tags orbit, superdough @param {number | Pattern} level between 0 and 1 @example s("bd sd [~ bd] sd").room("<0 .2 .4 .6 .8 1>") @example s("bd sd [~ bd] sd").room("<0.9:1 0.9:4>")

2387export const { room } = registerControl(['room', 'size']);

Reverb lowpass starting frequency (in hertz). When this property is changed, the reverb will be recaculated, so only change this sparsely..

@name roomlp @tags orbit, superdough @synonyms rlp @param {number} frequency between 0 and 20000hz @example s("bd sd [~ bd] sd").room(0.5).rlp(10000) @example s("bd sd [~ bd] sd").room(0.5).rlp(5000)

2401export const { roomlp, rlp } = registerControl('roomlp', 'rlp');

Reverb lowpass frequency at -60dB (in hertz). When this property is changed, the reverb will be recaculated, so only change this sparsely..

@name roomdim @tags orbit, superdough @synonyms rdim @param {number} frequency between 0 and 20000hz @example s("bd sd [~ bd] sd").room(0.5).rlp(10000).rdim(8000) @example s("bd sd [~ bd] sd").room(0.5).rlp(5000).rdim(400)

2416export const { roomdim, rdim } = registerControl('roomdim', 'rdim');

Reverb fade time (in seconds). When this property is changed, the reverb will be recaculated, so only change this sparsely..

@name roomfade @tags orbit, superdough @synonyms rfade @param {number} seconds for the reverb to fade @example s("bd sd [~ bd] sd").room(0.5).rlp(10000).rfade(0.5) @example s("bd sd [~ bd] sd").room(0.5).rlp(5000).rfade(4)

2431export const { roomfade, rfade } = registerControl('roomfade', 'rfade');

Sets the sample to use as an impulse response for the reverb. @name iresponse @tags orbit, superdough @param {string | Pattern} sample to use as an impulse response @synonyms ir @example s("bd sd [~ bd] sd").room(.8).ir("<shaker_large:0 shaker_large:2>")

2442export const { ir, iresponse } = registerControl(['ir', 'i'], 'iresponse');

Sets speed of the sample for the impulse response. @name irspeed @tags orbit, superdough @param {string | Pattern} speed @example samples('github:switchangel/pad') $: s("brk/2").fit().scrub(irand(16).div(16).seg(8)).ir("swpad:4").room(.2).irspeed("<2 1 .5>/2").irbegin(.5).roomsize(.5)

2454export const { irspeed } = registerControl('irspeed');

Sets the beginning of the IR response sample @name irbegin @tags orbit, superdough @param {string | Pattern} begin between 0 and 1 @synonyms ir @example samples('github:switchangel/pad') $: s("brk/2").fit().scrub(irand(16).div(16).seg(8)).ir("swpad:4").room(.65).irspeed("-2").irbegin("<0 .5 .75>/2").roomsize(.6)

2467export const { irbegin } = registerControl('irbegin');

Sets the room size of the reverb, see room. When this property is changed, the reverb will be recaculated, so only change this sparsely..

@name roomsize @tags orbit, superdough @param {number | Pattern} size between 0 and 10 @synonyms rsize, sz, size @example s("bd sd [~ bd] sd").room(.8).rsize(1) @example s("bd sd [~ bd] sd").room(.8).rsize(4)

TODO: find out why : s("bd sd [~ bd] sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc() .. does not work. Is it because room is only one effect?

2485export const { roomsize, size, sz, rsize } = registerControl('roomsize', 'size', 'sz', 'rsize');

['sagogo'], ['sclap'], ['sclaves'], ['scrash'],

(Deprecated) Wave shaping distortion. WARNING: can suddenly get unpredictably loud. Please use distort instead, which has a more predictable response curve second option in optional array syntax (ex: ".9:.5") applies a postgain to the output

@name shape @tags distortion, superdough @param {number | Pattern} distortion between 0 and 1 @example s("bd sd [~ bd] sd,hh*8").shape("<0 .2 .4 .6 .8>")

2503export const { shape } = registerControl(['shape', 'shapevol']);

Wave shaping distortion. CAUTION: it can get loud. Second option in optional array syntax (ex: ".9:.5") applies a postgain to the output. Third option sets the waveshaping type. Most useful values are usually between 0 and 10 (depending on source gain). If you are feeling adventurous, you can turn it up to 11 and beyond ;)

@name distort @tags distortion, superdough, supradough @synonyms dist @param {number | Pattern} distortion amount of distortion to apply @param {number | Pattern} volume linear postgain of the distortion @param {number | string | Pattern} type type of distortion to apply @example s("bd sd [~ bd] sd,hh8").distort("<0 2 3 10:.5>") @example note("d1!8").s("sine").penv(36).pdecay(.12).decay(.23).distort("8:.4") @example s("bd:44").bank("tr808").distort("3:0.5:diode")

2523export const { distort, dist } = registerControl(['distort', 'distortvol', 'distorttype'], 'dist');

Postgain for waveshaping distortion.

@name distortvol @synonyms distortion, distvol @tags superdough, supradough @param {number | Pattern} volume linear postgain of the distortion @example s("bd*4").bank("tr909").distort(2).distortvol(0.8)

2535export const { distortvol } = registerControl('distortvol', 'distvol');

Type of waveshaping distortion to apply.

@name distorttype @tags distortion, superdough, supradough @synonyms disttype @param {number | string | Pattern} type type of distortion to apply @example s("bd*4").bank("tr909").distort(2).distorttype("<0 1 2>")

@example s("sine").note("F1*2").release(1) .penv(24).pdecay(0.05) .distort(rand.range(1, 8)) .distorttype("<fold chebyshev scurve diode asym sinefold>")

2553export const { distorttype } = registerControl('distorttype', 'disttype');

Dynamics Compressor. The params are compressor("threshold:ratio:knee:attack:release") More info here

@name compressor @tags superdough @example s("bd sd [~ bd] sd,hh*8") .compressor("-20:20:10:.002:.02")

2566export const { compressor } = registerControl([
2567  'compressor',
2568  'compressorRatio',
2569  'compressorKnee',
2570  'compressorAttack',
2571  'compressorRelease',
2572]);
2573export const { compressorKnee } = registerControl('compressorKnee');
2574export const { compressorRatio } = registerControl('compressorRatio');
2575export const { compressorAttack } = registerControl('compressorAttack');
2576export const { compressorRelease } = registerControl('compressorRelease');

Changes the speed of sample playback, i.e. a cheap way of changing pitch.

@name speed @tags pitch, samples @param {number | Pattern} speed -inf to inf, negative numbers play the sample backwards. @example s("bd6").speed("1 2 4 1 -2 -4") @example speed("1 1.52 [2 1.1]").s("piano").clip(1)

2589export const { speed } = registerControl('speed');

Changes the pitch of the sample without changing its speed. The frequencies are multiplied by (factor + 1) for positive numbers and by max(factor / 4 + 1, 0) for negative numbers. So tuning up by octaves can be done with 1, 3, 7, ... and tuning down by octaves with -2, -3, -3.5...

@name stretch @tags pitch, samples @param {number | Pattern} factor between -4 and inf. Positive increases pitch, 0 does nothing, negative decreases the pitch. @example s("gm_flute").stretch("<2 1 0 -2>")

2605export const { stretch } = registerControl('stretch');

Used in conjunction with speed, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using unit "c" means speed will be interpreted in units of cycles, e.g. speed "1" means samples will be stretched to fill a cycle. Using unit "s" means the playback speed will be adjusted so that the duration is the number of seconds specified by speed.

@name unit @tags superdirt @param {number | string | Pattern} unit see description above @example speed("1 2 .5 3").s("bd").unit("c").osc() @superdirtOnly

2618export const { unit } = registerControl('unit');

Made by Calum Gunn. Reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter. The SuperCollider manual defines Squiz as:

"A simplistic pitch-raising algorithm. It's not meant to sound natural; its sound is reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter, depending on the input. The algorithm works by cutting the signal into fragments (delimited by upwards-going zero-crossings) and squeezing those fragments in the time domain (i.e. simply playing them back faster than they came in), leaving silences inbetween. All the parameters apart from memlen can be modulated."

@name squiz @tags superdirt @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc. @example squiz("2 4/2 6 [8 16]").s("bd").osc() @superdirtOnly

2632export const { squiz } = registerControl('squiz');

TODO: what is this? not found in tidal doc ['stutterdepth'], TODO: what is this? not found in tidal doc ['stuttertime'], TODO: what is this? not found in tidal doc ['timescale'], TODO: what is this? not found in tidal doc ['timescalewin'], ['tomdecay'], ['vcfegint'], ['vcoegint'], TODO: Use a rest (~) to override the effect <- vowel

Formant filter to make things sound like vowels.

@name vowel @tags superdough @param {string | Pattern} vowel You can use a e i o u ae aa oe ue y uh un en an on, corresponding to [a] [e] [i] [o] [u] [æ] [ɑ] [ø] [y] [ɯ] [ʌ] [œ̃] [ɛ̃] [ɑ̃] [ɔ̃]. Aliases: aa = å = ɑ, oe = ø = ö, y = ı, ae = æ. @example note("[c2 <eb2 <g2 g1>>]*2").s('sawtooth') .vowel("<a e i <o u>>") @example s("bd sd mt ht bd [~ cp] ht lt").vowel("[a|e|i|o|u]")

2659export const { vowel } = registerControl('vowel');

// TODO: find out how it works Made by Calum Gunn. Divides an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discards a fraction of them. Takes a number between 1 and 100, denoted the percentage of segments to drop. The SuperCollider manual describes the Waveloss effect this way:

Divide an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discard a fraction of them (i.e. replace them with silence of the same length). The technique was described by Trevor Wishart in a lecture. Parameters: the filter drops drop out of out of chunks. mode can be 1 to drop chunks in a simple deterministic fashion (e.g. always dropping the first 30 out of a set of 40 segments), or 2 to drop chunks randomly but in an appropriate proportion.)

mode: ? waveloss: ?

@name waveloss

2670export const { waveloss } = registerControl('waveloss');

crackle noise density

@name density @tags superdough @param {number | Pattern} density between 0 and x @example s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(4))

2681export const { density } = registerControl('density');

['modwheel'],

2683export const { expression } = registerControl('expression');
2684export const { sustainpedal } = registerControl('sustainpedal');
2686export const { fshift } = registerControl('fshift');
2687export const { fshiftnote } = registerControl('fshiftnote');
2688export const { fshiftphase } = registerControl('fshiftphase');
2689
2690export const { triode } = registerControl('triode');
2691export const { krush } = registerControl('krush');
2692export const { kcutoff } = registerControl('kcutoff');
2693export const { octer } = registerControl('octer');
2694export const { octersub } = registerControl('octersub');
2695export const { octersubsub } = registerControl('octersubsub');
2696export const { ring } = registerControl('ring');
2697export const { ringf } = registerControl('ringf');
2698export const { ringdf } = registerControl('ringdf');
2699export const { freeze } = registerControl('freeze');
2700export const { xsdelay } = registerControl('xsdelay');
2701export const { tsdelay } = registerControl('tsdelay');
2702export const { real } = registerControl('real');
2703export const { imag } = registerControl('imag');
2704export const { enhance } = registerControl('enhance');
2705export const { comb } = registerControl('comb');
2706export const { smear } = registerControl('smear');
2707export const { scram } = registerControl('scram');
2708export const { binshift } = registerControl('binshift');
2709export const { hbrick } = registerControl('hbrick');
2710export const { lbrick } = registerControl('lbrick');
2711
2712export const { frameRate } = registerControl('frameRate');
2713export const { frames } = registerControl('frames');
2714export const { hours } = registerControl('hours');
2715export const { minutes } = registerControl('minutes');
2716export const { seconds } = registerControl('seconds');
2717export const { songPtr } = registerControl('songPtr');
2718export const { uid } = registerControl('uid');
2719export const { val } = registerControl('val');
2720export const { cps } = registerControl('cps');

Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration.

@name clip @tags superdough @synonyms legato @param {number | Pattern} factor >= 0 @example note("c a f e").s("piano").clip("<.5 1 2>")

2732export const { clip, legato } = registerControl('clip', 'legato');

Sets the duration of the event in cycles. Similar to clip / legato, it also cuts samples off at the end if they exceed the duration.

@name duration @tags superdough @synonyms dur @param {number | Pattern} seconds >= 0 @example note("c a f e").s("piano").dur("<.5 1 2>")

2745export const { duration, dur } = registerControl('duration', 'dur');

ZZFX

2748export const { zrand } = registerControl('zrand');
2749export const { curve } = registerControl('curve');

superdirt duplicate export const {slide]} = registerControl('slide']);

2752export const { deltaSlide } = registerControl('deltaSlide');
2753export const { pitchJump } = registerControl('pitchJump');
2754export const { pitchJumpTime } = registerControl('pitchJumpTime');

noise on the frequency or as bubo calls it "frequency fog" :)

2756export const { znoise } = registerControl('znoise');
2757export const { zmod } = registerControl('zmod');

like crush but scaled differently

2759export const { zcrush } = registerControl('zcrush');
2760export const { zdelay } = registerControl('zdelay');
2761export const { zzfx } = registerControl('zzfx');

Sets the color of the hap in visualizations like pianoroll or highlighting. @name color @tags visualization @synonyms colour @param {string} color Hexadecimal or CSS color name

2770export const { color, colour } = registerControl(['color', 'colour']);

TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13

2774export let createParams = (...names) =>
2775  names.reduce((acc, name) => Object.assign(acc, { [name]: createParam(name) }), {});

ADSR envelope: Combination of Attack, Decay, Sustain, and Release.

@name adsr @tags envelope, amplitude @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("[c3 bb2 f3 eb3]*2").sound("sawtooth").lpf(600).adsr(".1:.1:.5:.2")

2789export const adsr = register('adsr', (adsr, pat) => {
2790  adsr = !Array.isArray(adsr) ? [adsr] : adsr;
2791  const [attack, decay, sustain, release] = adsr;
2792  return pat.set({ attack, decay, sustain, release });
2793});
2794export const ad = register('ad', (t, pat) => {
2795  t = !Array.isArray(t) ? [t] : t;
2796  const [attack, decay = attack] = t;
2797  return pat.attack(attack).decay(decay);
2798});
2799export const ds = register('ds', (t, pat) => {
2800  t = !Array.isArray(t) ? [t] : t;
2801  const [decay, sustain = 0] = t;
2802  return pat.set({ decay, sustain });
2803});
2804export const ar = register('ar', (t, pat) => {
2805  t = !Array.isArray(t) ? [t] : t;
2806  const [attack, release = attack] = t;
2807  return pat.set({ attack, release });
2808});

MIDI

MIDI channel: Sets the MIDI channel for the event.

@name midichan @tags external_io, midi @param {number | Pattern} channel MIDI channel number (0-15) @example note("c4").midichan(1).midi()

2821export const { midichan } = registerControl('midichan');
2823export const { midimap } = registerControl('midimap');

MIDI port: Sets the MIDI port for the event.

@name midiport @tags external_io, midi @param {number | Pattern} port MIDI port @example note("c a f e").midiport("<0 1 2 3>").midi()

2834export const { midiport } = registerControl('midiport');

MIDI command: Sends a MIDI command message.

@name midicmd @tags external_io, midi @param {number | Pattern} command MIDI command @example midicmd("clock*48,<start stop>/2").midi()

2845export const { midicmd } = registerControl('midicmd');

MIDI control: Sends a MIDI control change message.

@name control @tags external_io, midi @param {number | Pattern} MIDI control number (0-127) @param {number | Pattern} MIDI controller value (0-127)

2855export const control = register('control', (args, pat) => {
2856  if (!Array.isArray(args)) {
2857    throw new Error('control expects an array of [ccn, ccv]');
2858  }
2859  const [_ccn, _ccv] = args;
2860  return pat.ccn(_ccn).ccv(_ccv);
2861});

MIDI control number: Sends a MIDI control change message.

@name ccn @tags external_io, midi @param {number | Pattern} MIDI control number (0-127)

2870export const { ccn } = registerControl('ccn');

MIDI control value: Sends a MIDI control change message.

@name ccv @tags external_io, midi @param {number | Pattern} MIDI control value (0-127)

2878export const { ccv } = registerControl('ccv');
2879export const { ctlNum } = registerControl('ctlNum');

TODO: ctlVal?

MIDI NRPN non-registered parameter number: Sends a MIDI NRPN non-registered parameter number message. @name nrpnn @tags external_io, midi @param {number | Pattern} nrpnn MIDI NRPN non-registered parameter number (0-127) @example note("c4").nrpnn("1:8").nrpv("123").midichan(1).midi()

2890export const { nrpnn } = registerControl('nrpnn');

MIDI NRPN non-registered parameter value: Sends a MIDI NRPN non-registered parameter value message. @name nrpv @tags external_io, midi @param {number | Pattern} nrpv MIDI NRPN non-registered parameter value (0-127) @example note("c4").nrpnn("1:8").nrpv("123").midichan(1).midi()

2899export const { nrpv } = registerControl('nrpv');

MIDI program number: Sends a MIDI program change message.

@name progNum @tags external_io @param {number | Pattern} program MIDI program number (0-127) @example note("c4").progNum(10).midichan(1).midi()

2910export const { progNum } = registerControl('progNum');

MIDI sysex: Sends a MIDI sysex message. @name sysex @tags external_io, midi @param {number | Pattern} id Sysex ID @param {number | Pattern} data Sysex data @example note("c4").sysex(["0x77", "0x01:0x02:0x03:0x04"]).midichan(1).midi()

2921export const sysex = register('sysex', (args, pat) => {
2922  if (!Array.isArray(args)) {
2923    throw new Error('sysex expects an array of [id, data]');
2924  }
2925  const [id, data] = args;
2926  return pat.sysexid(id).sysexdata(data);
2927});

MIDI sysex ID: Sends a MIDI sysex identifier message. @name sysexid @tags external_io, midi @param {number | Pattern} id Sysex ID @example note("c4").sysexid("0x77").sysexdata("0x01:0x02:0x03:0x04").midichan(1).midi()

2936export const { sysexid } = registerControl('sysexid');

MIDI sysex data: Sends a MIDI sysex message. @name sysexdata @tags external_io, midi @param {number | Pattern} data Sysex data @example note("c4").sysexid("0x77").sysexdata("0x01:0x02:0x03:0x04").midichan(1).midi()

2945export const { sysexdata } = registerControl('sysexdata');

MIDI pitch bend: Sends a MIDI pitch bend message. @name midibend @tags external_io, midi @param {number | Pattern} midibend MIDI pitch bend (-1 - 1) @example note("c4").midibend(sine.slow(4).range(-0.4,0.4)).midi()

2955export const { midibend } = registerControl('midibend');

MIDI key after touch: Sends a MIDI key after touch message. @name miditouch @tags external_io, midi @param {number | Pattern} miditouch MIDI key after touch (0-1) @example note("c4").miditouch(sine.slow(4).range(0,1)).midi()

2964export const { miditouch } = registerControl('miditouch');

TODO: what is this?

2967export const { polyTouch } = registerControl('polyTouch');

The host to send open sound control messages to. Requires running the OSC bridge. @name oschost @tags external_io @param {string | Pattern} oschost e.g. 'localhost' @example note("c4").oschost('127.0.0.1').oscport(57120).osc();

2977export const { oschost } = registerControl('oschost');

The port to send open sound control messages to. Requires running the OSC bridge. @name oscport @tags external_io @param {number | Pattern} oscport e.g. 57120 @example note("c4").oschost('127.0.0.1').oscport(57120).osc();

2987export const { oscport } = registerControl('oscport');
2989export const getControlName = (alias) => {
2990  if (controlAlias.has(alias)) {
2991    return controlAlias.get(alias);
2992  }
2993  return alias;
2994};

Sets properties in a batch.

@name as @tags combiners @param {String | Array} mapping the control names that are set @example "c:.5 a:1 f:.25 e:.8".as("note:clip") @example "{0@2 0.25 0 0.5 .3 .5}%8".as("begin").s("sax_vib").clip(1)

3007export const as = register('as', (mapping, pat) => {
3008  mapping = Array.isArray(mapping) ? mapping : [mapping];
3009  return pat.fmap((v) => {
3010    v = Array.isArray(v) ? v : [v];
3011    const entries = [];
3012    for (let i = 0; i < mapping.length; ++i) {
3013      if (v[i] !== undefined) {
3014        entries.push([getControlName(mapping[i]), v[i]]);
3015      }
3016    }
3017    return Object.fromEntries(entries);
3018  });
3019});

Allows you to scrub an audio file like a tape loop by passing values that represents the position in the audio file in the optional array syntax ex: "0.5:2", the second value controls the speed of playback @name scrub @tags samples @memberof Pattern @returns Pattern @example samples('github:switchangel/pad') s("swpad:0").scrub("{0.1!2 .25@3 0.7!2 <0.8:1.5>}%8") @example samples('github:yaxu/clean-breaks/main'); s("amen/4").fit().scrub("{0@3 0@2 4@3}%8".div(16))

3036export const scrub = register(
3037  'scrub',
3038  (beginPat, pat) => {
3039    return beginPat.outerBind((v) => {
3040      if (!Array.isArray(v)) {
3041        v = [v];
3042      }
3043      const [beginVal, speedMultiplier = 1] = v;
3044
3045      return pat.begin(beginVal).mul(speed(speedMultiplier)).clip(1);
3046    });
3047  },
3048  false,
3049);
3051const subControlAliases = new Map();
3052const registerSubControl = (control, subControl, ...aliases) => {
3053  const aliasMap = subControlAliases.get(control) ?? new Map();
3054  const allKeys = new Set([subControl, ...aliases]);
3055  for (const alias of allKeys) {
3056    aliasMap.set(String(alias).toLowerCase(), subControl);
3057  }
3058  subControlAliases.set(control, aliasMap);
3059};
3060
3061const registerSubControls = (control, subControlAliases = []) => {
3062  for (const [subControl, ...aliases] of subControlAliases) {
3063    registerSubControl(control, subControl, ...aliases);
3064  }
3065};
3066
3067const getMainSubcontrolName = (control, subKey) => {
3068  const aliasMap = subControlAliases.get(control);
3069  if (!aliasMap) return subKey;
3070  return aliasMap.get(String(subKey).toLowerCase()) ?? subKey;
3071};
3072
3073registerSubControls('lfo', [
3074  ['control', 'c'],
3075  ['subControl', 'sc'],
3076  ['rate', 'r'],
3077  ['depth', 'dep', 'dr'],
3078  ['depthabs', 'da'],
3079  ['dcoffset', 'dc'],
3080  ['shape', 'sh'],
3081  ['skew', 'sk'],
3082  ['curve', 'cu'],
3083  ['sync', 's'],
3084  ['retrig', 'rt'],
3085  ['fxi'],
3086]);
3087registerSubControls('env', [
3088  ['control', 'c'],
3089  ['subControl', 'sc'],
3090  ['attack', 'att', 'a'],
3091  ['decay', 'dec', 'd'],
3092  ['sustain', 'sus', 's'],
3093  ['release', 'rel', 'r'],
3094  ['depth', 'dep', 'dr'],
3095  ['depthabs', 'da'],
3096  ['acurve', 'ac'],
3097  ['dcurve', 'dc'],
3098  ['rcurve', 'rc'],
3099  ['fxi'],
3100]);
3101registerSubControls('bmod', [
3102  ['bus', 'b'],
3103  ['control', 'c'],
3104  ['subControl', 'sc'],
3105  ['depth', 'dep', 'dr'],
3106  ['depthabs', 'da'],
3107  ['dc'],
3108  ['fxi'],
3109]);
3110
3111Pattern.prototype.modulate = function (type, config, idPat) {
3112  config = { control: undefined, ...config };
3113  const modulatorKeys = ['lfo', 'env', 'bmod'];
3114  if (!modulatorKeys.includes(type)) {
3115    logger(`[core] Modulation type ${type} not found. Please use one of 'lfo', 'env', 'bmod'`);
3116    return this;
3117  }
3118  let output = this;
3119  let defaultValue = undefined;
3120  // Copy value into a temporary `v` container and attach a single `id` (to be shared across
3121  // each config entry). At the output we destructure and throw away the id
3122  output = output.fmap((v) => (id) => ({ v, id })).appLeft(reify(idPat));
3123  for (const [rawKey, value] of Object.entries(config)) {
3124    const key = getMainSubcontrolName(type, rawKey);
3125    const valuePat = reify(value);
3126    output = output
3127      .fmap(({ v, id }) => (c) => {
3128        if (defaultValue === undefined) {
3129          // default control to the control set just before this in the chain
3130          // e.g. pat.gain(0.5).lfo({..}) will be a gain-LFO
3131          let control = getControlName(Object.keys(v).at(-1));
3132          if (modulatorKeys.includes(control)) {
3133            control = `${control}_${[...v[control].__ids].at(-1)}`;
3134          }
3135          defaultValue = control;
3136        }
3137        v[type] ??= { __ids: new Set() };
3138        const t = v[type];
3139        id ??= t.__ids.size;
3140        t[id] ??= { control: defaultValue };
3141        t.__ids.add(id); // keeps track of insertion order
3142        if (c === undefined) return { v, id };
3143        if (key === 'control' || key === 'subControl') {
3144          t[id][key] = getControlName(c);
3145        } else {
3146          t[id][key] = c;
3147        }
3148        return { v, id };
3149      })
3150      .appLeft(valuePat);
3151  }
3152  return output.fmap(({ v }) => v);
3153};

Configures an LFO. Can be called in sequence like pat.lfo(...).lfo(...) to set up multiple LFOs. There are two ways to declare which control will be modulated:

  1. Explicitly put control in the config (e.g. lfo({ c: "lpf" }))
  2. If the control parameter is absent, the control immediately before the lfo call will be used (e.g. s("saw").lpf(500).lfo() to modulate lpf)

Modulators can be referred to by id so that they can be updated later e.g. inside a sometimes. See example below.

@name lfo @tags lfo, superdough @param {Object} config LFO configuration. @param {string | Pattern} [config.control] Node to modulate. Aliases: c @param {string | Pattern} [config.subControl] Sub-control name to append to the control key. Aliases: sc @param {number | Pattern} [config.rate] Modulation rate. Aliases: r @param {number | Pattern} [config.sync] Tempo-synced modulation rate. Aliases: s @param {number | Pattern} [config.depth] Relative modulation depth. Aliases: dep, dr @param {number | Pattern} [config.depthabs] Absolute modulation depth. Aliases: da @param {number | Pattern} [config.dcoffset] DC offset / bias for the waveform. Aliases: dc @param {number | Pattern} [config.shape] Shape index. Aliases: sh @param {number | Pattern} [config.skew] Skew amount. Aliases: sk @param {number | Pattern} [config.curve] Exponential curve amount. Aliases: cu @param {number | Pattern} [config.retrig] If > 0.5, the LFO will retrigger on each event. Aliases: rt @param {number | Pattern} [config.fxi] FX index to target @param {string | Pattern} id ID to use for this modulator @returns Pattern

@example s("saw").note("F1").lpf(500).lfo()

@example s("saw").lfo().lpf(500).lfo({ s: 0.3 })

@example s("saw").lpf(500).diode(0.3) .lfo({ c: "lpf" })

@example s("pulse").lpf(500).lfo() .lfo({ c: "s" }) .diode(0.3) .sometimes(x => x.lfo({ s: "8" }, 1)) // lfo #1 (0-indexed)

@example s("pulse").lpf(500).lfo({ depth: 4 }, 'lpf_mod') .lfo({ c: "s" }) .diode(0.3) .sometimes(x => x.lfo({ s: "8" }, 'lpf_mod'))

3205Pattern.prototype.lfo = function (config, id) {
3206  return this.modulate('lfo', config, id);
3207};
3208export const lfo = (config) => pure({}).lfo(config);

Configures an envelope. Can be called in sequence like pat.env(...).env(...) to set up multiple envelopes There are two ways to declare which control will be modulated:

  1. Explicitly put control in the config (e.g. env({ c: "lpf" }))
  2. If the control parameter is absent, the control immediately before the env call will be used (e.g. s("saw").lpf(500).env({ a: 1 }) to modulate lpf)

Modulators can be referred to by id so that they can be updated later e.g. inside a sometimes. See example below.

@name env @tags envelope, superdough @param {Object} config Envelope configuration. @param {string | Pattern} [config.control] Node to modulate. Aliases: c @param {string | Pattern} [config.subControl] Sub-control name to append to the control key. Aliases: sc @param {number | Pattern} [config.depth] Relative modulation depth. Aliases: dep, dr @param {number | Pattern} [config.depthabs] Absolute modulation depth. Aliases: da @param {number | Pattern} [config.attack] Time to reach depth. Aliases: att, a @param {number | Pattern} [config.decay] Time to reach sustain. Aliases: dec, d @param {number | Pattern} [config.sustain] Sustain depth. Aliases: sus, s @param {number | Pattern} [config.release] Time to return to nominal value. Aliases: rel, r @param {number | Pattern} [config.acurve] Snappiness of attack curve (-1 = relaxed, 1 = snappy). Aliases: ac @param {number | Pattern} [config.dcurve] Snappiness of decay curve (-1 = relaxed, 1 = snappy). Aliases: dc @param {number | Pattern} [config.rcurve] Snappiness of release curve (-1 = relaxed, 1 = snappy). Aliases: rc @param {number | Pattern} [config.fxi] FX index to target @param {string | Pattern} id ID to use for this modulator @returns Pattern

@example s("saw").note("F1").lpf(500).env({ a: 1 })

@example s("saw").env({ d: 1 }).note("F1") .lpq(4).lpf(50) .env({ a: 0.1, d: 1, ac: 0.8, dc: 0.3, depth: 50 })

@example s("saw").lpf(500).diode(0.3) .env({ c: "lpf", a: 0.5, d: 0.5 })

@example s("pulse").lpf(500).env({ a: 1 }) .env({ c: "s", a: 1 }) .diode(0.3) .sometimes(x => x.env({ a: "0.5" }, 1)) // envelope #1 (0-indexed)

@example s("pulse").lpf(500).env({ a: 1 }, 'lpf_mod') .env({ c: "s", a: 1 }) .diode(0.3) .sometimes(x => x.env({ a: "0.5" }, 'lpf_mod'))

3262Pattern.prototype.env = function (config, id) {
3263  return this.modulate('env', config, id);
3264};
3265export const env = (config) => pure({}).env(config);

Modulates with the output from a given bus. Can be called in sequence like pat.bmod(...).bmod(...) to set up multiple modulators

Send to an audio bus with otherPat.bus(..).

There are two ways to declare which control will be modulated:

  1. Explicitly put control in the config (e.g. bmod({ id: 2, c: "lpf" }))
  2. If the control parameter is absent, the control immediately before the bmod call will be used (e.g. s("saw").lpf(500).bmod({ id: 2 }) to modulate lpf)

Modulators can be referred to by id so that they can be updated later e.g. inside a sometimes. See example below.

@name bmod @tags superdough @param {Object} config Bus modulation configuration. @param {string | Pattern} [config.bus] Bus to get modulation signal from @param {string | Pattern} [config.control] Node to modulate. Aliases: c @param {string | Pattern} [config.subControl] Sub-control name to append to the control key. Aliases: sc @param {number | Pattern} [config.depth] Relative modulation depth. Aliases: dep, dr @param {number | Pattern} [config.depthabs] Absolute modulation depth. Aliases: da @param {number | Pattern} [config.dc] DC offset prior to application @param {number | Pattern} [config.fxi] FX index to target @param {string | Pattern} id ID to use for this modulator @returns Pattern

@example modulator: s("one").seg(64).gain(slider(0, 0, 1)).bus(1).dry(0) carrier: s("saw").bmod({ b: 1 })

3299Pattern.prototype.bmod = function (config, id) {
3300  return this.modulate('bmod', config, id);
3301};
3302export const bmod = (config) => pure({}).bmod(config);

Transient shaper. Gives independent control over the emphasis on transients and sustains

@name transient @tags superdough @param {number | Pattern} attack Emphasis on transients; between -1 (deaccentuate) and 1 (accentuate) @param {number | Pattern} sustain Emphasis on the sustains; between -1 (deaccentuate) and 1 (accentuate) @example s("bd").transient("<-1 -0.5 0 0.5 1>") @example s("hh*16").bank("tr909").transient("<-1:1 1:-1>")

3317export const { transient } = registerControl(['transient', 'transsustain']);
3319export const { FXrelease, FXrel, FXr, fxr } = registerControl('FXrelease', 'FXrel', 'FXr', 'fxr');