jevstrudel.git / packages / core / controls.mjs
1/*
2controls.mjs - Registers audio controls for pattern manipulation and effects.
3Copyright (C) 2022 Strudel contributors - see <https://codeberg.org/uzu/strudel/src/branch/main/packages/core/controls.mjs>
4This 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/>.
5*/
6
7import { logger } from './logger.mjs';
8import { Pattern, pure, register, reify } from './pattern.mjs';
9
10export function createParam(names) {
11  let isMulti = Array.isArray(names);
12  names = !isMulti ? [names] : names;
13  const name = names[0];
14
15  // 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  };
39
40  // 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}
55
56// maps control alias names to the "main" control name
57const controlAlias = new Map();
58
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}
96
97/**
98 * Select a sound / sample by name. When using mininotation, you can also optionally supply 'n' and 'gain' parameters
99 * separated by ':'.
100 *
101 * @name s
102 * @tags superdough, samples
103 * @param {string | Pattern} sound The sound / pattern of sounds to pick
104 * @synonyms sound
105 * @example
106 * s("bd hh")
107 * @example
108 * s("bd:0 bd:1 bd:0:0.3 bd:1:1.4")
109 *
110 */
111export const { s, sound } = registerControl(['s', 'n', 'gain'], 'sound');
112
113/**
114 * Position in the wavetable of the wavetable oscillator
115 *
116 * @name wt
117 * @tags wavetable, superdough
118 * @param {number | Pattern} position Position in the wavetable from 0 to 1
119 * @synonyms wavetablePosition
120 * @example
121 * s("squelch").bank("wt_digital").seg(8).note("F1").wt("0 0.25 0.5 0.75 1")
122 */
123export const { wt, wavetablePosition } = registerControl('wt', 'wavetablePosition');
124
125/**
126 * Amount of envelope applied wavetable oscillator's position envelope
127 *
128 * @name wtenv
129 * @tags wavetable, envelope, superdough
130 * @param {number | Pattern} amount between 0 and 1
131 */
132export const { wtenv } = registerControl('wtenv');
133/**
134 * Attack time of the wavetable oscillator's position envelope
135 *
136 * @name wtattack
137 * @tags wavetable, envelope, superdough
138 * @synonyms wtatt
139 * @param {number | Pattern} time attack time in seconds
140 */
141export const { wtattack, wtatt } = registerControl('wtattack', 'wtatt');
142
143/**
144 * Decay time of the wavetable oscillator's position envelope
145 *
146 * @name wtdecay
147 * @tags wavetable, envelope, superdough
148 * @synonyms wtdec
149 * @param {number | Pattern} time decay time in seconds
150 */
151export const { wtdecay, wtdec } = registerControl('wtdecay', 'wtdec');
152
153/**
154 * Sustain time of the wavetable oscillator's position envelope
155 *
156 * @name wtsustain
157 * @tags wavetable, envelope, superdough
158 * @synonyms wtsus
159 * @param {number | Pattern} gain sustain level (0 to 1)
160 */
161export const { wtsustain, wtsus } = registerControl('wtsustain', 'wtsus');
162
163/**
164 * Release time of the wavetable oscillator's position envelope
165 *
166 * @name wtrelease
167 * @tags wavetable, envelope, superdough
168 * @synonyms wtrel
169 * @param {number | Pattern} time release time in seconds
170 */
171export const { wtrelease, wtrel } = registerControl('wtrelease', 'wtrel');
172
173/**
174 * Rate of the LFO for the wavetable oscillator's position
175 *
176 * @name wtrate
177 * @tags wavetable, lfo, superdough
178 * @param {number | Pattern} rate rate in hertz
179 */
180export const { wtrate } = registerControl('wtrate');
181/**
182 * cycle synced rate of the LFO for the wavetable oscillator's position
183 *
184 * @name wtsync
185 * @tags wavetable, lfo, superdough
186 * @param {number | Pattern} rate rate in cycles
187 */
188export const { wtsync } = registerControl('wtsync');
189
190/**
191 * Depth of the LFO for the wavetable oscillator's position
192 *
193 * @name wtdepth
194 * @tags wavetable, lfo, superdough
195 * @param {number | Pattern} depth depth of modulation
196 */
197export const { wtdepth } = registerControl('wtdepth');
198
199/**
200 * Shape of the LFO for the wavetable oscillator's position
201 *
202 * @name wtshape
203 * @tags wavetable, lfo, superdough
204 * @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..)
205 */
206export const { wtshape } = registerControl('wtshape');
207
208/**
209 * DC offset of the LFO for the wavetable oscillator's position
210 *
211 * @name wtdc
212 * @tags wavetable, lfo, superdough
213 * @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar
214 */
215export const { wtdc } = registerControl('wtdc');
216
217/**
218 * Skew of the LFO for the wavetable oscillator's position
219 *
220 * @name wtskew
221 * @tags wavetable, lfo, superdough
222 * @param {number | Pattern} skew How much to bend the LFO shape
223 */
224export const { wtskew } = registerControl('wtskew');
225
226/**
227 * Amount of warp (alteration of the waveform) to apply to the wavetable oscillator
228 *
229 * @name warp
230 * @tags wavetable, superdough
231 * @param {number | Pattern} amount Warp of the wavetable from 0 to 1
232 * @synonyms wavetableWarp
233 * @example
234 * s("basique").bank("wt_digital").seg(8).note("F1").warp("0 0.25 0.5 0.75 1")
235 *   .warpmode("spin")
236 */
237export const { warp, wavetableWarp } = registerControl('warp', 'wavetableWarp');
238
239/**
240 * Attack time of the wavetable oscillator's warp envelope
241 *
242 * @name warpattack
243 * @tags wavetable, envelope, superdough
244 * @synonyms warpatt
245 * @param {number | Pattern} time attack time in seconds
246 */
247export const { warpattack, warpatt } = registerControl('warpattack', 'warpatt');
248
249/**
250 * Decay time of the wavetable oscillator's warp envelope
251 *
252 * @name warpdecay
253 * @tags wavetable, envelope, superdough
254 * @synonyms warpdec
255 * @param {number | Pattern} time decay time in seconds
256 */
257export const { warpdecay, warpdec } = registerControl('warpdecay', 'warpdec');
258
259/**
260 * Sustain time of the wavetable oscillator's warp envelope
261 *
262 * @name warpsustain
263 * @tags wavetable, envelope, superdough
264 * @synonyms warpsus
265 * @param {number | Pattern} gain sustain level (0 to 1)
266 */
267export const { warpsustain, warpsus } = registerControl('warpsustain', 'warpsus');
268
269/**
270 * Release time of the wavetable oscillator's warp envelope
271 *
272 * @name warprelease
273 * @tags wavetable, envelope, superdough
274 * @synonyms warprel
275 * @param {number | Pattern} time release time in seconds
276 */
277export const { warprelease, warprel } = registerControl('warprelease', 'warprel');
278
279/**
280 * Rate of the LFO for the wavetable oscillator's warp
281 *
282 * @name warprate
283 * @tags wavetable, lfo, superdough
284 * @param {number | Pattern} rate rate in hertz
285 */
286export const { warprate } = registerControl('warprate');
287
288/**
289 * Depth of the LFO for the wavetable oscillator's warp
290 *
291 * @name warpdepth
292 * @tags wavetable, lfo, superdough
293 * @param {number | Pattern} depth depth of modulation
294 */
295export const { warpdepth } = registerControl('warpdepth');
296
297/**
298 * Shape of the LFO for the wavetable oscillator's warp
299 *
300 * @name warpshape
301 * @tags wavetable, lfo, superdough
302 * @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..)
303 */
304export const { warpshape } = registerControl('warpshape');
305
306/**
307 * DC offset of the LFO for the wavetable oscillator's warp
308 *
309 * @name warpdc
310 * @tags wavetable, lfo, superdough
311 * @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar
312 */
313export const { warpdc } = registerControl('warpdc');
314
315/**
316 * Skew of the LFO for the wavetable oscillator's warp
317 *
318 * @name warpskew
319 * @tags wavetable, lfo, superdough
320 * @param {number | Pattern} skew How much to bend the LFO shape
321 */
322export const { warpskew } = registerControl('warpskew');
323
324/**
325 * Type of warp (alteration of the waveform) to apply to the wavetable oscillator.
326 *
327 * The current options are: none, asym, bendp, bendm, bendmp, sync, quant, fold, pwm, orbit,
328 * spin, chaos, primes, binary, brownian, reciprocal, wormhole, logistic, sigmoid, fractal, flip
329 *
330 * @name warpmode
331 * @tags wavetable, superdough
332 * @param {number | string | Pattern} mode Warp mode
333 * @synonyms wavetableWarpMode
334 * @example
335 * s("morgana").bank("wt_digital").seg(8).note("F1").warp("0 0.25 0.5 0.75 1")
336 *   .warpmode("<asym bendp spin logistic sync wormhole brownian>*2")
337 *
338 */
339export const { warpmode, wavetableWarpMode } = registerControl('warpmode', 'wavetableWarpMode');
340
341/**
342 * Amount of randomness of the initial phase of the wavetable oscillator.
343 *
344 * @name wtphaserand
345 * @tags wavetable, superdough
346 * @param {number | Pattern} amount Randomness of the initial phase. Between 0 (not random) and 1 (fully random)
347 * @synonyms wavetablePhaseRand
348 * @example
349 * s("basique").bank("wt_digital").seg(16).wtphaserand("<0 1>")
350 *
351 */
352export const { wtphaserand, wavetablePhaseRand } = registerControl('wtphaserand', 'wavetablePhaseRand');
353
354/**
355 * Amount of envelope applied wavetable oscillator's position envelope
356 *
357 * @name warpenv
358 * @tags wavetable, envelope, superdough
359 * @param {number | Pattern} amount between 0 and 1
360 */
361export const { warpenv } = registerControl('warpenv');
362
363/**
364 * cycle synced rate of the LFO for the wavetable warp position
365 *
366 * @name warpsync
367 * @tags wavetable, lfo, superdough
368 * @param {number | Pattern} rate rate in cycles
369 */
370export const { warpsync } = registerControl('warpsync');
371
372/**
373 * Define a custom webaudio node to use as a sound source.
374 *
375 * @name source
376 * @tags external_io, superdough
377 * @synonyms src
378 * @param {function} getSource
379 * @synonyms src
380 *
381 */
382export const { source, src } = registerControl('source', 'src');
383/**
384 * Selects the given index:
385 *  - for samples, it picks the sample by index, with wrap around
386 *  - for scales, it picks the scale degree
387 *  - for voicings, it picks the voice index
388 *
389 * @name n
390 * @tags superdough, samples, tonal
391 * @param {number | Pattern} value sample index starting from 0
392 * @example
393 * s("bd sd [~ bd] sd,hh*6").n("<0 1>")
394 */
395// also see https://codeberg.org/uzu/strudel/pulls/63
396export const { n } = registerControl('n');
397
398/**
399 * Selects the given degree. Currently used in `xen` and `tune`:
400 *
401 * @name i
402 * @tags tonal
403 * @param {number | Pattern} value
404 * @example
405 * i("0 1 2 3 4 5 6 7").xen("<5edo 10edo 15edo hexany15>")
406 */
407export const { i } = registerControl('i');
408
409/**
410 * Plays the given note name or midi number. A note name consists of
411 *
412 * - a letter (a-g or A-G)
413 * - optional accidentals (b or #)
414 * - optional (possibly negative) octave number (0-9). Defaults to 3
415 *
416 * Examples of valid note names: `c`, `bb`, `Bb`, `f#`, `c3`, `A4`, `Eb2`, `c#5`
417 *
418 * You can also use midi numbers instead of note names, where 69 is mapped to A4 440Hz in 12EDO.
419 *
420 * @name note
421 * @tags tonal
422 * @example
423 * note("c a f e")
424 * @example
425 * note("c4 a4 f4 e4")
426 * @example
427 * note("60 69 65 64")
428 * @example
429 * note("fbb1 a#0 cbbb-1 e##-2").sound("saw")
430 */
431export const { note } = registerControl(['note', 'n']);
432
433/**
434 * A pattern of numbers that speed up (or slow down) samples while they play. Currently only supported by osc / superdirt.
435 *
436 * @name accelerate
437 * @tags samples, superdirt
438 * @param {number | Pattern} amount acceleration.
439 * @superdirtOnly
440 * @example
441 * s("sax").accelerate("<0 1 2 4 8 16>").slow(2).osc()
442 *
443 */
444export const { accelerate } = registerControl('accelerate');
445/**
446 * Sets the velocity from 0 to 1. Is multiplied together with gain.
447 *
448 * @name velocity
449 * @tags amplitude, superdough, supradough
450 * @synonyms vel
451 * @example
452 * s("hh*8")
453 * .gain(".4!2 1 .4!2 1 .4 1")
454 * .velocity(".4 1")
455 */
456export const { velocity, vel } = registerControl('velocity', 'vel');
457/**
458 * Controls the gain by an exponential amount.
459 *
460 * @name gain
461 * @tags amplitude, superdough, supradough
462 * @param {number | Pattern} amount gain.
463 * @example
464 * s("hh*8").gain(".4!2 1 .4!2 1 .4 1").fast(2)
465 *
466 */
467export const { gain } = registerControl('gain');
468/**
469 * Gain applied after all effects have been processed.
470 *
471 * @name postgain
472 * @tags amplitude, superdough, supradough
473 * @example
474 * s("bd sd [~ bd] sd,hh*8")
475 * .compressor("-20:20:10:.002:.02").postgain(1.5)
476 *
477 */
478export const { postgain } = registerControl('postgain');
479/**
480 * Like `gain`, but linear.
481 *
482 * @name amp
483 * @tags amplitude, superdirt
484 * @param {number | Pattern} amount gain.
485 * @superdirtOnly
486 * @example
487 * s("bd*8").amp(".1*2 .5 .1*2 .5 .1 .5").osc()
488 *
489 */
490export const { amp } = registerControl('amp');
491
492/**
493 * Sets the Frequency Modulation Harmonicity Ratio.
494 * Controls the timbre of the sound.
495 * Whole numbers and simple ratios sound more natural,
496 * while decimal numbers and complex ratios sound metallic.
497 *
498 * A number may be added afterwards to control the harmonicity of
499 * any of the 8 individual FMs (e.g. `fmh2`)
500 *
501 * @name fmh
502 * @tags fm, superdough, supradough
503 * @param {number | Pattern} harmonicity
504 * @example
505 * note("c e g b g e")
506 * .fm(4)
507 * .fmh("<1 2 1.5 1.61>")
508 * ._scope()
509 *
510 */
511export const { fmh, fmh1, fmh2, fmh3, fmh4, fmh5, fmh6, fmh7, fmh8 } = registerMultiControl(['fmh', 'fmi'], 8, 'fmh');
512
513/**
514 * Sets the Frequency Modulation of the synth.
515 * Controls the modulation index, which defines the brightness of the sound.
516 *
517 * A number may be added afterwards to control the modulation index of
518 * any of the 8 individual FMs (e.g. `fm3`). Also, FMs may be routed into
519 * each other with matrix commands like `fm13`, which would send `fm1` back into
520 * `fm3`
521 *
522 * @name fmi
523 * @tags fm, superdough, supradough
524 * @param {number | Pattern} brightness modulation index
525 * @synonyms fm
526 * @example
527 * note("c e g b g e")
528 * .fm("<0 1 2 8 32>")
529 * ._scope()
530 * @example
531 * s("sine").note("F1").seg(8)
532 *  .fm(4).fm2(rand.mul(4)).fm3(saw.mul(8).slow(8))
533 *  .fmh(1.06).fmh2(10).fmh3(0.1)
534 *
535 */
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');
538
539// fm envelope
540/**
541 * Ramp type of fm envelope. Exp might be a bit broken..
542 *
543 * A number may be added afterwards to control the envelope of
544 * any of the 8 individual FMs (e.g. `fmenv4`)
545 *
546 * @name fmenv
547 * @tags fm, envelope, superdough, supradough
548 * @param {number | Pattern} type lin | exp
549 * @synonyms fme
550 * @example
551 * note("c e g b g e")
552 * .fm(4)
553 * .fmdecay(.2)
554 * .fmsustain(0)
555 * .fmenv("<exp lin>")
556 * ._scope()
557 *
558 */
559export const { fmenv, fmenv1, fmenv2, fmenv3, fmenv4, fmenv5, fmenv6, fmenv7, fmenv8, fme } = registerMultiControl(
560  'fmenv',
561  8,
562  'fme',
563);
564
565/**
566 * Attack time for the FM envelope: time it takes to reach maximum modulation
567 *
568 * A number may be added afterwards to control the attack of the envelope of
569 * any of the 8 individual FMs (e.g. `fmatt5`)
570 *
571 * @name fmattack
572 * @tags fm, envelope, superdough, supradough
573 * @synonyms fmatt
574 * @param {number | Pattern} time attack time
575 * @synonyms fmatt
576 * @example
577 * note("c e g b g e")
578 * .fm(4)
579 * .fmattack("<0 .05 .1 .2>")
580 * ._scope()
581 *
582 */
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');
603
604/**
605 * Waveform of the fm modulator
606 *
607 * A number may be added afterwards to control the waveform
608 * any of the 8 individual FMs (e.g. `fmwave6`)
609 *
610 * @name fmwave
611 * @tags fm, superdough, supradough
612 * @param {number | Pattern} wave waveform
613 * @example
614 * n("0 1 2 3".fast(4)).scale("d:minor").s("sine").fmwave("<sine square sawtooth crackle>").fm(4).fmh(2.01)
615 * @example
616 * n("0 1 2 3".fast(4)).chord("<Dm Am F G>").voicing().s("sawtooth").fmwave("brown").fm(.6)
617 *
618 */
619export const { fmwave, fmwave1, fmwave2, fmwave3, fmwave4, fmwave5, fmwave6, fmwave7, fmwave8 } = registerMultiControl(
620  'fmwave',
621  8,
622);
623
624/**
625 * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase.
626 *
627 * A number may be added afterwards to control the decay of the envelope of
628 * any of the 8 individual FMs (e.g. `fmdec6`)
629 *
630 * @name fmdecay
631 * @tags fm, envelope, superdough, supradough
632 * @synonyms fmdec
633 * @param {number | Pattern} time decay time
634 * @synonyms fmdec
635 * @example
636 * note("c e g b g e")
637 * .fm(4)
638 * .fmdecay("<.01 .05 .1 .2>")
639 * .fmsustain(.4)
640 * ._scope()
641 *
642 */
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');
663
664/**
665 * Sustain level for the FM envelope: how much modulation is applied after the decay phase
666 *
667 * A number may be added afterwards to control the sustain of the envelope of
668 * any of the 8 individual FMs (e.g. `fmsus7`)
669 *
670 * @name fmsustain
671 * @tags fm, envelope, superdough, supradough
672 * @synonyms fmsus
673 * @param {number | Pattern} level sustain level
674 * @synonyms fmsus
675 * @example
676 * note("c e g b g e")
677 * .fm(4)
678 * .fmdecay(.1)
679 * .fmsustain("<1 .75 .5 0>")
680 * ._scope()
681 *
682 */
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');
703
704/**
705 * Release time for the FM envelope: how much modulation is applied after the note is released
706 *
707 * A number may be added afterwards to control the release of the envelope of
708 * any of the 8 individual FMs (e.g. `fmrel8`)
709 *
710 * @name fmrelease
711 * @tags fm, envelope, superdough, supradough
712 * @synonyms fmrel
713 * @param {number | Pattern} time release time
714 *
715 */
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');
736
737// FM Matrix
738// Note: we do not declare top-level exports here since it would add
739// ~162 more explicit exports. This is likely fine as the most common use-case would be to at least
740// declare one other FM prior to utilizing the matrix functionality, but if we ever decide we need it,
741// 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}
747
748/**
749 * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`.
750 *
751 * @name bank
752 * @tags samples, superdough
753 * @param {string | Pattern} bank the name of the bank
754 * @example
755 * s("bd sd [~ bd] sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd")
756 *
757 */
758export const { bank } = registerControl('bank');
759
760/**
761 * mix control for the chorus effect
762 *
763 * @name chorus
764 * @tags pitch
765 * @param {string | Pattern} chorus mix amount between 0 and 1
766 * @example
767 * note("d d a# a").s("sawtooth").chorus(.5)
768 *
769 */
770export const { chorus } = registerControl('chorus');
771
772// analyser node send amount 0 - 1 (used by scope)
773export const { analyze } = registerControl('analyze');
774// fftSize of analyser
775export const { fft } = registerControl('fft');
776
777/**
778 * Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset.
779 *
780 * @name attack
781 * @tags amplitude, envelope, superdough, supradough
782 * @param {number | Pattern} attack time in seconds.
783 * @synonyms att
784 * @example
785 * note("c3 e3 f3 g3").attack("<0 .1 .5>")
786 *
787 */
788export const { attack, att } = registerControl('attack', 'att');
789
790/**
791 * Amplitude envelope decay time: the time it takes after the attack time to reach the sustain level.
792 * Note that the decay is only audible if the sustain value is lower than 1.
793 *
794 * @name decay
795 * @tags amplitude, envelope, superdough, supradough
796 * @param {number | Pattern} time decay time in seconds
797 * @synonyms dec
798 * @example
799 * note("c3 e3 f3 g3").decay("<.1 .2 .3 .4>").sustain(0)
800 *
801 */
802export const { decay, dec } = registerControl('decay', 'dec');
803/**
804 * Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset.
805 *
806 * @name sustain
807 * @tags amplitude, envelope, superdough, supradough
808 * @param {number | Pattern} gain sustain level between 0 and 1
809 * @synonyms sus
810 * @example
811 * note("c3 e3 f3 g3").decay(.2).sustain("<0 .1 .4 .6 1>")
812 *
813 */
814export const { sustain, sus } = registerControl('sustain', 'sus');
815/**
816 * Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero.
817 *
818 * @name release
819 * @tags amplitude, envelope, superdough, supradough
820 * @param {number | Pattern} time release time in seconds
821 * @synonyms rel
822 * @example
823 * note("c3 e3 g3 c4").release("<0 .1 .4 .6 1>/2")
824 *
825 */
826export const { release, rel } = registerControl('release', 'rel');
827export const { hold } = registerControl('hold');
828// TODO: in tidal, it seems to be normalized
829/**
830 * Sets the center frequency of the **b**and-**p**ass **f**ilter. When using mininotation, you
831 * can also optionally supply the 'bpq' parameter separated by ':'.
832 *
833 * @name bpf
834 * @tags filter, superdough, supradough
835 * @param {number | Pattern} frequency center frequency
836 * @synonyms bandf, bp
837 * @example
838 * s("bd sd [~ bd] sd,hh*6").bpf("<1000 2000 4000 8000>")
839 *
840 */
841export const { bandf, bpf, bp } = registerControl(['bandf', 'bandq', 'bpenv'], 'bpf', 'bp');
842// TODO: in tidal, it seems to be normalized
843/**
844 * Sets the **b**and-**p**ass **q**-factor (resonance).
845 *
846 * @name bpq
847 * @tags filter, superdough, supradough
848 * @param {number | Pattern} q q factor
849 * @synonyms bandq
850 * @example
851 * s("bd sd [~ bd] sd").bpf(500).bpq("<0 1 2 3>")
852 *
853 */
854// currently an alias of 'bandq' https://codeberg.org/uzu/strudel/issues/496
855// ['bpq'],
856export const { bandq, bpq } = registerControl('bandq', 'bpq');
857/**
858 * 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.
859 *
860 * @name begin
861 * @tags samples
862 * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample
863 * @example
864 * samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/dirt-samples')
865 * s("rave").begin("<0 .25 .5 .75>").fast(2)
866 *
867 */
868export const { begin } = registerControl('begin');
869/**
870 * The same as .begin, but cuts off the end off each sample.
871 *
872 * @memberof Pattern
873 * @name end
874 * @tags samples
875 * @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc..
876 * @example
877 * s("bd*2,oh*4").end("<.1 .2 .5 1>").fast(2)
878 *
879 */
880export const { end } = registerControl('end');
881/**
882 * Loops the sample.
883 * Note that the tempo of the loop is not synced with the cycle tempo.
884 * To change the loop region, use loopBegin / loopEnd.
885 *
886 * @name loop
887 * @tags samples
888 * @param {number | Pattern} on If 1, the sample is looped
889 * @example
890 * s("casio").loop(1)
891 *
892 */
893export const { loop } = registerControl('loop');
894/**
895 * Begin to loop at a specific point in the sample (inbetween `begin` and `end`).
896 * Note that the loop point must be inbetween `begin` and `end`, and before `loopEnd`!
897 * Note: Samples starting with wt_ will automatically loop! (wt = wavetable)
898 *
899 * @name loopBegin
900 * @tags samples
901 * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample
902 * @synonyms loopb
903 * @example
904 * s("space").loop(1)
905 * .loopBegin("<0 .125 .25>")._scope()
906 */
907export const { loopBegin, loopb } = registerControl('loopBegin', 'loopb');
908/**
909 *
910 * End the looping section at a specific point in the sample (inbetween `begin` and `end`).
911 * Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`!
912 *
913 * @name loopEnd
914 * @tags samples
915 * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample
916 * @synonyms loope
917 * @example
918 * s("space").loop(1)
919 * .loopEnd("<1 .75 .5 .25>")._scope()
920 */
921export const { loopEnd, loope } = registerControl('loopEnd', 'loope');
922/**
923 * Bit crusher effect.
924 *
925 * @name crush
926 * @tags superdough, supradough
927 * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction).
928 * @example
929 * s("<bd sd>,hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>")
930 *
931 */
932// ['clhatdecay'],
933export const { crush } = registerControl('crush');
934/**
935 * Fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers
936 *
937 * @name coarse
938 * @tags superdough, supradough
939 * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on.
940 * @example
941 * s("bd sd [~ bd] sd,hh*8").coarse("<1 4 8 16 32>")
942 *
943 */
944export const { coarse } = registerControl('coarse');
945
946/**
947 * Modulate the amplitude of a sound with a continuous waveform
948 *
949 * @name tremolo
950 * @tags amplitude, lfo, superdough
951 * @synonyms trem
952 * @param {number | Pattern} speed modulation speed in HZ
953 * @example
954 * note("d d d# d".fast(4)).s("supersaw").tremolo("<3 2 100> ").tremoloskew("<.5>")
955 *
956 */
957export const { tremolo, trem } = registerControl(['tremolo', 'tremolodepth', 'tremoloskew', 'tremolophase'], 'trem');
958
959/**
960 * Modulate the amplitude of a sound with a continuous waveform
961 *
962 * @name tremolosync
963 * @tags amplitude, lfo, superdough
964 * @synonyms tremsync
965 * @param {number | Pattern} cycles modulation speed in cycles
966 * @example
967 * note("d d d# d".fast(4)).s("supersaw").tremolosync("4").tremoloskew("<1 .5 0>")
968 *
969 */
970export const { tremolosync } = registerControl(
971  ['tremolosync', 'tremolodepth', 'tremoloskew', 'tremolophase'],
972  'tremsync',
973);
974
975/**
976 * Depth of amplitude modulation
977 *
978 * @name tremolodepth
979 * @tags amplitude, lfo, superdough
980 * @synonyms tremdepth
981 * @param {number | Pattern} depth
982 * @example
983 * note("a1 a1 a#1 a1".fast(4)).s("pulse").tremsync(4).tremolodepth("<1 2 .7>")
984 *
985 */
986export const { tremolodepth } = registerControl('tremolodepth', 'tremdepth');
987/**
988 * Alter the shape of the modulation waveform
989 *
990 * @name tremoloskew
991 * @tags amplitude, lfo, superdough
992 * @synonyms tremskew
993 * @param {number | Pattern} amount between 0 & 1, the shape of the waveform
994 * @example
995 * note("{f a c e}%16").s("sawtooth").tremsync(4).tremoloskew("<.5 0 1>")
996 *
997 */
998export const { tremoloskew } = registerControl('tremoloskew', 'tremskew');
999
1000/**
1001 * Alter the phase of the modulation waveform
1002 *
1003 * @name tremolophase
1004 * @tags amplitude, lfo, superdough
1005 * @synonyms tremphase
1006 * @param {number | Pattern} offset the offset in cycles of the modulation
1007 * @example
1008 * note("{f a c e}%16").s("sawtooth").tremsync(4).tremolophase("<0 .25 .66>")
1009 *
1010 */
1011export const { tremolophase } = registerControl('tremolophase', 'tremphase');
1012
1013/**
1014 * Shape of amplitude modulation
1015 *
1016 * @name tremoloshape
1017 * @tags amplitude, lfo, superdough
1018 * @synonyms tremshape
1019 * @param {number | Pattern} shape tri | square | sine | saw | ramp
1020 * @example
1021 * note("{f g c d}%16").tremsync(4).tremoloshape("<sine tri square>").s("sawtooth")
1022 *
1023 */
1024export const { tremoloshape } = registerControl('tremoloshape', 'tremshape');
1025/**
1026 * Filter overdrive for supported filter types
1027 *
1028 * @name drive
1029 * @tags filter, superdough
1030 * @param {number | Pattern} amount
1031 * @example
1032 * note("{f g g c d a a#}%16".sub(17)).s("supersaw").lpenv(8).lpf(150).lpq(.8).ftype('ladder').drive("<.5 4>")
1033 *
1034 */
1035export const { drive } = registerControl('drive');
1036
1037/**
1038 * Modulate the amplitude of an orbit to create a "sidechain" like effect.
1039 *
1040 * Can be applied to multiple orbits with the ':' mininotation, e.g. `duckorbit("2:3")`
1041 *
1042 * @name duckorbit
1043 * @tags amplitude, orbit, superdough
1044 * @synonyms duck
1045 * @param {number | Pattern} orbit target orbit
1046 * @example
1047 * $: n(run(16)).scale("c:minor:pentatonic").s("sawtooth").delay(.7).orbit(2)
1048 * $: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack(0.2).duckdepth(1)
1049 * @example
1050 * $: n(run(16)).scale("c:minor:pentatonic").s("sawtooth").delay(.7).orbit(2)
1051 * $: s("hh*16").orbit(3)
1052 * $: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit("2:3").duckattack(0.2).duckdepth(1)
1053 *
1054 */
1055export const { duck } = registerControl('duckorbit', 'duck');
1056
1057/**
1058 * The amount of ducking applied to target orbit
1059 *
1060 * Can vary across orbits with the ':' mininotation, e.g. `duckdepth("0.3:0.1")`.
1061 * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`.
1062 *
1063 * @name duckdepth
1064 * @tags amplitude, orbit, superdough
1065 * @param {number | Pattern} depth depth of modulation from 0 to 1
1066 * @example
1067 * 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>"))
1068 * @example
1069 * $: n(run(16)).scale("c:minor:pentatonic").s("sawtooth").delay(.7).orbit(2)
1070 * $: s("hh*16").orbit(3)
1071 * $: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit("2:3").duckattack(0.2).duckdepth("1:0.5")
1072 *
1073 */
1074export const { duckdepth } = registerControl('duckdepth');
1075
1076/**
1077 * The time required for the ducked signal(s) to reach their lowest volume.
1078 * Can be used to prevent clicking or for creative rhythmic effects.
1079 *
1080 * Can vary across orbits with the ':' mininotation, e.g. `duckonset("0:0.003")`.
1081 * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`.
1082 *
1083 * @name duckonset
1084 * @tags amplitude, envelope, orbit, superdough
1085 * @synonyms duckons
1086 *
1087 * @param {number | Pattern} time The onset time in seconds
1088 * @example
1089 * // Clicks
1090 * sound: freq("63.2388").s("sine").orbit(2).gain(4)
1091 * duckerWithClick: s("bd*4").duckorbit(2).duckattack(0.3).duckonset(0).postgain(0)
1092 * @example
1093 * // No clicks
1094 * sound: freq("63.2388").s("sine").orbit(2).gain(4)
1095 * duckerWithoutClick: s("bd*4").duckorbit(2).duckattack(0.3).duckonset(0.01).postgain(0)
1096 * @example
1097 * // Rhythmic
1098 * noise: s("pink").distort("2:1").orbit(4) // used rhythmically with 0.3 onset below
1099 * hhat: s("hh*16").orbit(7)
1100 * ducker: s("bd*4").bank("tr909").duckorbit("4:7").duckonset("0.3:0.003").duckattack(0.25)
1101 *
1102 */
1103export const { duckonset } = registerControl('duckonset', 'duckons');
1104
1105/**
1106 * The time required for the ducked signal(s) to return to their normal volume.
1107 *
1108 * Can vary across orbits with the ':' mininotation, e.g. `duckonset("0:0.003")`.
1109 * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`.
1110 *
1111 * @name duckattack
1112 * @tags amplitude, envelope, orbit, superdough
1113 * @synonyms duckatt, datt
1114 *
1115 * @param {number | Pattern} time The attack time in seconds
1116 * @example
1117 * sound: n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2)
1118 * ducker: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack("<0.2 0 0.4>").duckdepth(1)
1119 * @example
1120 * moreduck: n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2)
1121 * lessduck: s("hh*16").orbit(5)
1122 * ducker: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit("2:5").duckattack("0.4:0.1")
1123 *
1124 */
1125export const { duckattack } = registerControl('duckattack', 'duckatt', 'datt');
1126
1127/**
1128 * Create byte beats with custom expressions
1129 *
1130 * @name byteBeatExpression
1131 * @synonyms bbexpr, bb
1132 * @tags superdough
1133 *
1134 * @param {number | Pattern} byteBeatExpression bitwise expression for creating bytebeat
1135 * @example
1136 * s("bytebeat").bbexpr('t*(t>>15^t>>66)')
1137 *
1138 */
1139export const { byteBeatExpression, bbexpr } = registerControl('byteBeatExpression', 'bbexpr', 'bb');
1140
1141/**
1142 * Create byte beats with custom expressions
1143 *
1144 * @name byteBeatStartTime
1145 * @synonyms bbst
1146 * @tags superdough
1147 *
1148 * @param {number | Pattern} byteBeatStartTime in samples (t)
1149 * @example
1150 * note("c3!8".add("{0 0 12 0 7 5 3}%8")).s("bytebeat:5").bbst("<3 1>".mul(10000))._scope()
1151 *
1152 */
1153export const { byteBeatStartTime, bbst } = registerControl('byteBeatStartTime', 'bbst');
1154
1155/**
1156 * Allows you to set the output channels on the interface
1157 *
1158 * @name channels
1159 * @tags external_io, superdough
1160 * @synonyms ch
1161 *
1162 * @param {number | Pattern} channels pattern the output channels
1163 * @example
1164 * note("e a d b g").channels("3:4")
1165 *
1166 */
1167export const { channels, ch } = registerControl('channels', 'ch');
1168
1169/**
1170 * Controls the pulsewidth of the pulse oscillator
1171 *
1172 * @name pw
1173 * @tags superdough
1174 * @param {number | Pattern} pulsewidth
1175 * @example
1176 * note("{f a c e}%16").s("pulse").pw(".8:1:.2")
1177 * @example
1178 * n(run(8)).scale("D:pentatonic").s("pulse").pw("0 .75 .5 1")
1179 */
1180export const { pw } = registerControl(['pw', 'pwrate', 'pwsweep']);
1181
1182/**
1183 * Controls the lfo rate for the pulsewidth of the pulse oscillator
1184 *
1185 * @name pwrate
1186 * @synonyms pwr
1187 * @tags superdough, lfo
1188 * @param {number | Pattern} rate
1189 * @example
1190 * n(run(8)).scale("D:pentatonic").s("pulse").pw("0.5").pwrate("<5 .1 25>").pwsweep("<0.3 .8>")
1191
1192 *
1193 */
1194export const { pwrate } = registerControl('pwrate', 'pwr');
1195
1196/**
1197 * Controls the lfo sweep for the pulsewidth of the pulse oscillator
1198 *
1199 * @name pwsweep
1200 * @synonyms pws
1201 * @tags superdough, lfo
1202 * @param {number | Pattern} sweep
1203 * @example
1204 * n(run(8)).scale("D:pentatonic").s("pulse").pw("0.5").pwrate("<5 .1 25>").pwsweep("<0.3 .8>")
1205 *
1206 */
1207export const { pwsweep } = registerControl('pwsweep', 'pws');
1208
1209/**
1210 * Phaser audio effect that approximates popular guitar pedals.
1211 *
1212 * @name phaser
1213 * @tags superdough
1214 * @synonyms ph
1215 * @param {number | Pattern} speed speed of modulation
1216 * @example
1217 * n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5)
1218 * .phaser("<1 2 4 8>")
1219 *
1220 */
1221export const { phaserrate, ph, phaser } = registerControl(
1222  ['phaserrate', 'phaserdepth', 'phasercenter', 'phasersweep'],
1223  'ph',
1224  'phaser',
1225);
1226
1227/**
1228 * The frequency sweep range of the lfo for the phaser effect. Defaults to 2000
1229 *
1230 * @name phasersweep
1231 * @tags superdough, lfo
1232 * @synonyms phs
1233 * @param {number | Pattern} phasersweep most useful values are between 0 and 4000
1234 * @example
1235 * n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5)
1236 * .phaser(2).phasersweep("<800 2000 4000>")
1237 *
1238 */
1239export const { phasersweep, phs } = registerControl('phasersweep', 'phs');
1240
1241/**
1242 * The center frequency of the phaser in HZ. Defaults to 1000
1243 *
1244 * @name phasercenter
1245 * @tags superdough
1246 * @synonyms phc
1247 * @param {number | Pattern} centerfrequency in HZ
1248 * @example
1249 * n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5)
1250 * .phaser(2).phasercenter("<800 2000 4000>")
1251 *
1252 */
1253
1254export const { phasercenter, phc } = registerControl('phasercenter', 'phc');
1255
1256/**
1257 * The amount the signal is affected by the phaser effect. Defaults to 0.75
1258 *
1259 * @name phaserdepth
1260 * @tags superdough, superdirt
1261 * @synonyms phd, phasdp
1262 * @param {number | Pattern} depth number between 0 and 1
1263 * @example
1264 * n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5)
1265 * .phaser(2).phaserdepth("<0 .5 .75 1>")
1266 *
1267 */
1268// also a superdirt control
1269export const { phaserdepth, phd, phasdp } = registerControl('phaserdepth', 'phd', 'phasdp');
1270
1271/**
1272 * Choose the channel the pattern is sent to
1273 *
1274 * @name channel
1275 * @tags superdough
1276 * @param {number | Pattern} channel channel number
1277 *
1278 */
1279export const { channel } = registerControl('channel');
1280/**
1281 * 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.
1282 *
1283 * @name cut
1284 * @tags superdough
1285 * @param {number | Pattern} group cut group number
1286 * @example
1287 * s("[oh hh]*4").cut(1)
1288 *
1289 */
1290export const { cut } = registerControl('cut');
1291/**
1292 * Applies the cutoff frequency of the **l**ow-**p**ass **f**ilter.
1293 *
1294 * When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'.
1295 *
1296 * @name lpf
1297 * @tags filter, superdough, supradough
1298 * @param {number | Pattern} frequency audible between 0 and 20000
1299 * @synonyms cutoff, ctf, lp
1300 * @example
1301 * s("bd sd [~ bd] sd,hh*6").lpf("<4000 2000 1000 500 200 100>")
1302 * @example
1303 * s("bd*16").lpf("1000:0 1000:10 1000:20 1000:30")
1304 *
1305 */
1306export const { cutoff, ctf, lpf, lp } = registerControl(['cutoff', 'resonance', 'lpenv'], 'ctf', 'lpf', 'lp');
1307
1308/**
1309 * Sets the lowpass filter envelope modulation depth.
1310 * @name lpenv
1311 * @tags filter, envelope, superdough, supradough
1312 * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_
1313 * @synonyms lpe
1314 * @example
1315 * note("c2 e2 f2 g2")
1316 * .sound('sawtooth')
1317 * .lpf(300)
1318 * .lpa(.5)
1319 * .lpenv("<4 2 1 0 -1 -2 -4>/4")
1320 */
1321export const { lpenv, lpe } = registerControl('lpenv', 'lpe');
1322/**
1323 * Sets the highpass filter envelope modulation depth.
1324 * @name hpenv
1325 * @tags filter, envelope, superdough, supradough
1326 * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_
1327 * @synonyms hpe
1328 * @example
1329 * note("c2 e2 f2 g2")
1330 * .sound('sawtooth')
1331 * .hpf(500)
1332 * .hpa(.5)
1333 * .hpenv("<4 2 1 0 -1 -2 -4>/4")
1334 */
1335export const { hpenv, hpe } = registerControl('hpenv', 'hpe');
1336/**
1337 * Sets the bandpass filter envelope modulation depth.
1338 * @name bpenv
1339 * @tags filter, envelope, superdough, supradough
1340 * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_
1341 * @synonyms bpe
1342 * @example
1343 * note("c2 e2 f2 g2")
1344 * .sound('sawtooth')
1345 * .bpf(500)
1346 * .bpa(.5)
1347 * .bpenv("<4 2 1 0 -1 -2 -4>/4")
1348 */
1349export const { bpenv, bpe } = registerControl('bpenv', 'bpe');
1350/**
1351 * Sets the attack duration for the lowpass filter envelope.
1352 * @name lpattack
1353 * @tags filter, envelope, superdough, supradough
1354 * @param {number | Pattern} attack time of the filter envelope
1355 * @synonyms lpa
1356 * @example
1357 * note("c2 e2 f2 g2")
1358 * .sound('sawtooth')
1359 * .lpf(300)
1360 * .lpa("<.5 .25 .1 .01>/4")
1361 * .lpenv(4)
1362 */
1363export const { lpattack, lpa } = registerControl('lpattack', 'lpa');
1364/**
1365 * Sets the attack duration for the highpass filter envelope.
1366 * @name hpattack
1367 * @tags filter, envelope, superdough, supradough
1368 * @param {number | Pattern} attack time of the highpass filter envelope
1369 * @synonyms hpa
1370 * @example
1371 * note("c2 e2 f2 g2")
1372 * .sound('sawtooth')
1373 * .hpf(500)
1374 * .hpa("<.5 .25 .1 .01>/4")
1375 * .hpenv(4)
1376 */
1377export const { hpattack, hpa } = registerControl('hpattack', 'hpa');
1378/**
1379 * Sets the attack duration for the bandpass filter envelope.
1380 * @name bpattack
1381 * @tags filter, envelope, superdough, supradough
1382 * @param {number | Pattern} attack time of the bandpass filter envelope
1383 * @synonyms bpa
1384 * @example
1385 * note("c2 e2 f2 g2")
1386 * .sound('sawtooth')
1387 * .bpf(500)
1388 * .bpa("<.5 .25 .1 .01>/4")
1389 * .bpenv(4)
1390 */
1391export const { bpattack, bpa } = registerControl('bpattack', 'bpa');
1392/**
1393 * Sets the decay duration for the lowpass filter envelope.
1394 * @name lpdecay
1395 * @tags filter, envelope, superdough, supradough
1396 * @param {number | Pattern} decay time of the filter envelope
1397 * @synonyms lpd
1398 * @example
1399 * note("c2 e2 f2 g2")
1400 * .sound('sawtooth')
1401 * .lpf(300)
1402 * .lpd("<.5 .25 .1 0>/4")
1403 * .lpenv(4)
1404 */
1405export const { lpdecay, lpd } = registerControl('lpdecay', 'lpd');
1406/**
1407 * Sets the decay duration for the highpass filter envelope.
1408 * @name hpdecay
1409 * @tags filter, envelope, superdough, supradough
1410 * @param {number | Pattern} decay time of the highpass filter envelope
1411 * @synonyms hpd
1412 * @example
1413 * note("c2 e2 f2 g2")
1414 * .sound('sawtooth')
1415 * .hpf(500)
1416 * .hpd("<.5 .25 .1 0>/4")
1417 * .hps(0.2)
1418 * .hpenv(4)
1419 */
1420export const { hpdecay, hpd } = registerControl('hpdecay', 'hpd');
1421/**
1422 * Sets the decay duration for the bandpass filter envelope.
1423 * @name bpdecay
1424 * @tags filter, envelope, superdough, supradough
1425 * @param {number | Pattern} decay time of the bandpass filter envelope
1426 * @synonyms bpd
1427 * @example
1428 * note("c2 e2 f2 g2")
1429 * .sound('sawtooth')
1430 * .bpf(500)
1431 * .bpd("<.5 .25 .1 0>/4")
1432 * .bps(0.2)
1433 * .bpenv(4)
1434 */
1435export const { bpdecay, bpd } = registerControl('bpdecay', 'bpd');
1436/**
1437 * Sets the sustain amplitude for the lowpass filter envelope.
1438 * @name lpsustain
1439 * @tags filter, envelope, superdough, supradough
1440 * @param {number | Pattern} sustain amplitude of the lowpass filter envelope
1441 * @synonyms lps
1442 * @example
1443 * note("c2 e2 f2 g2")
1444 * .sound('sawtooth')
1445 * .lpf(300)
1446 * .lpd(.5)
1447 * .lps("<0 .25 .5 1>/4")
1448 * .lpenv(4)
1449 */
1450export const { lpsustain, lps } = registerControl('lpsustain', 'lps');
1451/**
1452 * Sets the sustain amplitude for the highpass filter envelope.
1453 * @name hpsustain
1454 * @tags filter, envelope, superdough, supradough
1455 * @param {number | Pattern} sustain amplitude of the highpass filter envelope
1456 * @synonyms hps
1457 * @example
1458 * note("c2 e2 f2 g2")
1459 * .sound('sawtooth')
1460 * .hpf(500)
1461 * .hpd(.5)
1462 * .hps("<0 .25 .5 1>/4")
1463 * .hpenv(4)
1464 */
1465export const { hpsustain, hps } = registerControl('hpsustain', 'hps');
1466/**
1467 * Sets the sustain amplitude for the bandpass filter envelope.
1468 * @name bpsustain
1469 * @tags filter, envelope, superdough, supradough
1470 * @param {number | Pattern} sustain amplitude of the bandpass filter envelope
1471 * @synonyms bps
1472 * @example
1473 * note("c2 e2 f2 g2")
1474 * .sound('sawtooth')
1475 * .bpf(500)
1476 * .bpd(.5)
1477 * .bps("<0 .25 .5 1>/4")
1478 * .bpenv(4)
1479 */
1480export const { bpsustain, bps } = registerControl('bpsustain', 'bps');
1481/**
1482 * Sets the release time for the lowpass filter envelope.
1483 * @name lprelease
1484 * @tags filter, envelope, superdough, supradough
1485 * @param {number | Pattern} release time of the filter envelope
1486 * @synonyms lpr
1487 * @example
1488 * note("c2 e2 f2 g2")
1489 * .sound('sawtooth')
1490 * .clip(.5)
1491 * .lpf(300)
1492 * .lpenv(4)
1493 * .lpr("<.5 .25 .1 0>/4")
1494 * .release(.5)
1495 */
1496export const { lprelease, lpr } = registerControl('lprelease', 'lpr');
1497/**
1498 * Sets the release time for the highpass filter envelope.
1499 * @name hprelease
1500 * @tags filter, envelope, superdough, supradough
1501 * @param {number | Pattern} release time of the highpass filter envelope
1502 * @synonyms hpr
1503 * @example
1504 * note("c2 e2 f2 g2")
1505 * .sound('sawtooth')
1506 * .clip(.5)
1507 * .hpf(500)
1508 * .hpenv(4)
1509 * .hpr("<.5 .25 .1 0>/4")
1510 * .release(.5)
1511 */
1512export const { hprelease, hpr } = registerControl('hprelease', 'hpr');
1513/**
1514 * Sets the release time for the bandpass filter envelope.
1515 * @name bprelease
1516 * @tags filter, envelope, superdough, supradough
1517 * @param {number | Pattern} release time of the bandpass filter envelope
1518 * @synonyms bpr
1519 * @example
1520 * note("c2 e2 f2 g2")
1521 * .sound('sawtooth')
1522 * .clip(.5)
1523 * .bpf(500)
1524 * .bpenv(4)
1525 * .bpr("<.5 .25 .1 0>/4")
1526 * .release(.5)
1527 */
1528export const { bprelease, bpr } = registerControl('bprelease', 'bpr');
1529/**
1530 * Sets the filter type. The ladder filter is more aggressive. More types might be added in the future.
1531 * @name ftype
1532 * @tags filter, superdough
1533 * @param {number | Pattern} type 12db (0), ladder (1), or 24db (2)
1534 * @example
1535 * note("{f g g c d a a#}%8").s("sawtooth").lpenv(4).lpf(500).ftype("<0 1 2>").lpq(1)
1536 * @example
1537 * note("c f g g a c d4").fast(2)
1538 * .sound('sawtooth')
1539 * .lpf(200).fanchor(0)
1540 * .lpenv(3).lpq(1)
1541 * .ftype("<ladder 12db 24db>")
1542 */
1543export const { ftype } = registerControl('ftype');
1544
1545/**
1546 * controls the center of the filter envelope. 0 is unipolar positive, .5 is bipolar, 1 is unipolar negative
1547 * @name fanchor
1548 * @tags filter, envelope, superdough
1549 * @param {number | Pattern} center 0 to 1
1550 * @example
1551 * note("{f g g c d a a#}%8").s("sawtooth").lpf("{1000}%2")
1552 * .lpenv(8).fanchor("<0 .5 1>")
1553 */
1554export const { fanchor } = registerControl('fanchor');
1555/**
1556 * Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter.
1557 *
1558 * When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'.
1559 *
1560 * @name hpf
1561 * @tags filter, superdough, supradough
1562 * @param {number | Pattern} frequency audible between 0 and 20000
1563 * @synonyms hp, hcutoff
1564 * @example
1565 * s("bd sd [~ bd] sd,hh*8").hpf("<4000 2000 1000 500 200 100>")
1566 * @example
1567 * s("bd sd [~ bd] sd,hh*8").hpf("<2000 2000:25>")
1568 *
1569 */
1570// currently an alias of 'hcutoff' https://codeberg.org/uzu/strudel/issues/496
1571// ['hpf'],
1572
1573/**
1574 * Rate of the LFO for the lowpass filter
1575 *
1576 * @name lprate
1577 * @tags filter, lfo, superdough
1578 * @param {number | Pattern} rate rate in hertz
1579 * @example
1580 * note("<c c c# c c c4>*16").s("sawtooth").lpf(600).lprate("<4 8 2 1>")
1581 */
1582export const { lprate } = registerControl('lprate');
1583
1584/**
1585 * Cycle-synced rate of the LFO for the lowpass filter
1586 *
1587 * @name lpsync
1588 * @tags filter, lfo, superdough
1589 * @param {number | Pattern} rate rate in cycles
1590 * @example
1591 * note("<c c c# c c c4>*16").s("sawtooth").lpf(600).lpsync("<4 8 2 1>")
1592 */
1593export const { lpsync } = registerControl('lpsync');
1594
1595/**
1596 * Depth of the LFO for the lowpass filter
1597 *
1598 * @name lpdepth
1599 * @tags filter, lfo, superdough
1600 * @param {number | Pattern} depth depth of modulation
1601 * @example
1602 * note("<c c c# c c c4>*16").s("sawtooth").lpf(600).lpdepth("<1 .5 1.8 0>")
1603 */
1604
1605export const { lpdepth } = registerControl('lpdepth');
1606/**
1607 * Depth of the LFO for the lowpass filter, in HZ
1608 *
1609 * @name lpdepthfrequency
1610 * @tags filter, lfo, superdough
1611 * @synonyms lpdepthfreq
1612 * @param {number | Pattern} depth depth of modulation
1613 * @example
1614 * note("<c c c# c c c4>*16").s("sawtooth").lpf(600).lpdepthfrequency("<200 500 100 0>")
1615 */
1616
1617export const { lpdepthfrequency, lpdepthfreq } = registerControl('lpdepthfrequency', 'lpdepthfreq');
1618
1619/**
1620 * Shape of the LFO for the lowpass filter
1621 *
1622 * @name lpshape
1623 * @tags filter, lfo, superdough
1624 * @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..)
1625 */
1626export const { lpshape } = registerControl('lpshape');
1627
1628/**
1629 * DC offset of the LFO for the lowpass filter
1630 *
1631 * @name lpdc
1632 * @tags filter, lfo, superdough
1633 * @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar
1634 */
1635export const { lpdc } = registerControl('lpdc');
1636
1637/**
1638 * Skew of the LFO for the lowpass filter
1639 *
1640 * @name lpskew
1641 * @tags filter, lfo, superdough
1642 * @param {number | Pattern} skew How much to bend the LFO shape
1643 */
1644export const { lpskew } = registerControl('lpskew');
1645
1646/**
1647 * Rate of the LFO for the bandpass filter
1648 *
1649 * @name bprate
1650 * @tags filter, lfo, superdough
1651 * @param {number | Pattern} rate rate in hertz
1652 */
1653export const { bprate } = registerControl('bprate');
1654
1655/**
1656 * Cycle-synced rate of the LFO for the bandpass filter
1657 *
1658 * @name bpsync
1659 * @tags filter, lfo, superdough
1660 * @param {number | Pattern} rate rate in cycles
1661 */
1662export const { bpsync } = registerControl('bpsync');
1663
1664/**
1665 * Depth of the LFO for the bandpass filter
1666 *
1667 * @name bpdepth
1668 * @tags filter, lfo, superdough
1669 * @param {number | Pattern} depth depth of modulation
1670 */
1671export const { bpdepth } = registerControl('bpdepth');
1672
1673/**
1674 * Depth of the LFO for the bandpass filter, in HZ
1675 *
1676 * @name bpdepthfrequency
1677 * @tags filter, lfo, superdough
1678 * @synonyms bpdepthfreq
1679 * @param {number | Pattern} depth depth of modulation
1680 * @example
1681 * note("<c c c# c c c4>*16").s("sawtooth").lpf(600).bpdepthfrequency("<200 500 100 0>")
1682 */
1683
1684export const { bpdepthfrequency, bpdepthfreq } = registerControl('bpdepthfrequency', 'bpdepthfreq');
1685
1686/**
1687 * Shape of the LFO for the bandpass filter
1688 *
1689 * @name bpshape
1690 * @tags filter, lfo, superdough
1691 * @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..)
1692 */
1693export const { bpshape } = registerControl('bpshape');
1694
1695/**
1696 * DC offset of the LFO for the bandpass filter
1697 *
1698 * @name bpdc
1699 * @tags filter, lfo, superdough
1700 * @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar
1701 */
1702export const { bpdc } = registerControl('bpdc');
1703
1704/**
1705 * Skew of the LFO for the bandpass filter
1706 *
1707 * @name bpskew
1708 * @tags filter, lfo, superdough
1709 * @param {number | Pattern} skew How much to bend the LFO shape
1710 */
1711export const { bpskew } = registerControl('bpskew');
1712
1713/**
1714 * Rate of the LFO for the highpass filter
1715 *
1716 * @name hprate
1717 * @tags filter, lfo, superdough
1718 * @param {number | Pattern} rate rate in hertz
1719 */
1720export const { hprate } = registerControl('hprate');
1721
1722/**
1723 * Cycle-synced rate of the LFO for the highpass filter
1724 *
1725 * @name hpsync
1726 * @tags filter, lfo, superdough
1727 * @param {number | Pattern} rate rate in cycles
1728 */
1729export const { hpsync } = registerControl('hpsync');
1730
1731/**
1732 * Depth of the LFO for the highpass filter
1733 *
1734 * @name hpdepth
1735 * @tags filter, lfo, superdough
1736 * @param {number | Pattern} depth depth of modulation
1737 */
1738export const { hpdepth } = registerControl('hpdepth');
1739
1740/**
1741 * Depth of the LFO for the hipass filter, in hz
1742 *
1743 * @name hpdepthfrequency
1744 * @tags filter, lfo, superdough
1745 * @synonyms hpdepthfreq
1746 * @param {number | Pattern} depth depth of modulation
1747 * @example
1748 * note("<c c c# c c c4>*16").s("sawtooth").lpf(600).hpdepthfrequency("<200 500 100 0>")
1749 */
1750
1751export const { hpdepthfrequency, hpdepthfreq } = registerControl('hpdepthfrequency', 'hpdepthfreq');
1752
1753/**
1754 * Shape of the LFO for the highpass filter
1755 *
1756 * @name hpshape
1757 * @tags filter, lfo, superdough
1758 * @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..)
1759 */
1760export const { hpshape } = registerControl('hpshape');
1761
1762/**
1763 * DC offset of the LFO for the highpass filter
1764 *
1765 * @name hpdc
1766 * @tags filter, lfo, superdough
1767 * @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar
1768 */
1769export const { hpdc } = registerControl('hpdc');
1770
1771/**
1772 * Skew of the LFO for the highpass filter
1773 *
1774 * @name hpskew
1775 * @tags filter, lfo, superdough
1776 * @param {number | Pattern} skew How much to bend the LFO shape
1777 */
1778export const { hpskew } = registerControl('hpskew');
1779
1780/**
1781 * Applies a vibrato to the frequency of the oscillator.
1782 *
1783 * @name vib
1784 * @tags pitch, lfo, superdough, supradough
1785 * @synonyms vibrato, v
1786 * @param {number | Pattern} frequency of the vibrato in hertz
1787 * @example
1788 * note("a e")
1789 * .vib("<.5 1 2 4 8 16>")
1790 * ._scope()
1791 * @example
1792 * // change the modulation depth with ":"
1793 * note("a e")
1794 * .vib("<.5 1 2 4 8 16>:12")
1795 * ._scope()
1796 */
1797export const { vib, vibrato, v } = registerControl(['vib', 'vibmod'], 'vibrato', 'v');
1798/**
1799 * Adds pink noise to the mix
1800 *
1801 * @name noise
1802 * @tags generators, superdough, supradough
1803 * @param {number | Pattern} wet wet amount
1804 * @example
1805 * sound("<white pink brown>/2")
1806 */
1807export const { noise } = registerControl('noise');
1808/**
1809 * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set
1810 *
1811 * @name vibmod
1812 * @tags pitch, lfo, superdough, supradough
1813 * @synonyms vmod
1814 * @param {number | Pattern} depth of vibrato (in semitones)
1815 * @example
1816 * note("a e").vib(4)
1817 * .vibmod("<.25 .5 1 2 12>")
1818 * ._scope()
1819 * @example
1820 * // change the vibrato frequency with ":"
1821 * note("a e")
1822 * .vibmod("<.25 .5 1 2 12>:8")
1823 * ._scope()
1824 */
1825export const { vibmod, vmod } = registerControl(['vibmod', 'vib'], 'vmod');
1826export const { hcutoff, hpf, hp } = registerControl(['hcutoff', 'hresonance', 'hpenv'], 'hpf', 'hp');
1827/**
1828 * Controls the **h**igh-**p**ass **q**-value.
1829 *
1830 * @name hpq
1831 * @tags filter, superdough, supradough
1832 * @param {number | Pattern} q resonance factor between 0 and 50
1833 * @synonyms hresonance
1834 * @example
1835 * s("bd sd [~ bd] sd,hh*8").hpf(2000).hpq("<0 10 20 30>")
1836 *
1837 */
1838export const { hresonance, hpq } = registerControl('hresonance', 'hpq');
1839/**
1840 * Controls the **l**ow-**p**ass **q**-value.
1841 *
1842 * @name lpq
1843 * @tags filter, superdough, supradough
1844 * @param {number | Pattern} q resonance factor between 0 and 50
1845 * @synonyms resonance
1846 * @example
1847 * s("bd sd [~ bd] sd,hh*8").lpf(2000).lpq("<0 10 20 30>")
1848 *
1849 */
1850// currently an alias of 'resonance' https://codeberg.org/uzu/strudel/issues/496
1851export const { resonance, lpq } = registerControl('resonance', 'lpq');
1852/**
1853 * DJ filter, below 0.5 is low pass filter, above is high pass filter.
1854 *
1855 * @name djf
1856 * @tags filter, superdough
1857 * @param {number | Pattern} cutoff below 0.5 is low pass filter, above is high pass filter
1858 * @example
1859 * n(irand(16).seg(8)).scale("d:phrygian").s("supersaw").djf("<.5 .3 .2 .75>")
1860 *
1861 */
1862export const { djf } = registerControl('djf');
1863// ['cutoffegint'],
1864// TODO: does not seem to work
1865/**
1866 * Sets the level of the delay signal.
1867 *
1868 * When using mininotation, you can also optionally add the 'delaytime' and 'delayfeedback' parameter,
1869 * separated by ':'.
1870 *
1871 *
1872 * @name delay
1873 * @tags orbit, superdough, supradough
1874 * @param {number | Pattern} level between 0 and 1
1875 * @example
1876 * s("bd bd").delay("<0 .25 .5 1>")
1877 * @example
1878 * s("bd bd").delay("0.65:0.25:0.9 0.65:0.125:0.7")
1879 *
1880 */
1881export const { delay } = registerControl(['delay', 'delaytime', 'delayfeedback']);
1882/**
1883 * Sets the level of the signal that is fed back into the delay.
1884 * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it
1885 *
1886 * @name delayfeedback
1887 * @tags orbit, superdough, supradough
1888 * @param {number | Pattern} feedback between 0 and 1
1889 * @synonyms delayfb, dfb
1890 * @example
1891 * s("bd").delay(.25).delayfeedback("<.25 .5 .75 1>")
1892 *
1893 */
1894export const { delayfeedback, delayfb, dfb } = registerControl('delayfeedback', 'delayfb', 'dfb');
1895
1896/**
1897 * Sets the time of the delay effect.
1898 *
1899 * @name delayspeed
1900 * @tags supradough
1901 * @param {number | Pattern} delayspeed controls the pitch of the delay feedback
1902 * @synonyms delayt, dt
1903 * @example
1904 * note("d d a# a".fast(2)).s("sawtooth").delay(.8).delaytime(1/2).delayspeed("<2 .5 -1 -2>")
1905 *
1906 */
1907export const { delayspeed } = registerControl('delayspeed');
1908
1909/**
1910 * Sets the time of the delay effect in seconds.
1911 *
1912 * @name delaytime
1913 * @tags orbit, superdough, supradough
1914 * @param {number | Pattern} delay in seconds
1915 * @synonyms delayt, dt
1916 * @example
1917 * note("d d a# a".fast(2))
1918 * .s("sawtooth")
1919 * .delay(.8)
1920 * .delaytime(1/2)
1921 * .delayspeed("<2 .5 -1 -2>")
1922 */
1923export const { delaytime, delayt, dt } = registerControl('delaytime', 'delayt', 'dt');
1924
1925/**
1926 * Sets the time of the delay effect in cycles.
1927 *
1928 * @name delaysync
1929 * @tags orbit, superdough
1930 * @param {number | Pattern} cycles delay length in cycles
1931 * @synonyms delays, ds
1932 * @example
1933 * s("bd bd").delay(.25).delaysync("<1 2 3 5>".div(8))
1934 *
1935 */
1936export const { delaysync } = registerControl('delaysync', 'delays', 'ds');
1937
1938/**
1939 * Specifies whether delaytime is calculated relative to cps.
1940 *
1941 * @name lock
1942 * @tags superdirt
1943 * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle.
1944 * @superdirtOnly
1945 * @example
1946 * s("sd").delay().lock(1).osc()
1947 *
1948 *
1949 */
1950
1951export const { lock } = registerControl('lock');
1952/**
1953 * Set detune for stacked voices of supported oscillators.
1954 *
1955 * @name detune
1956 * @tags pitch, superdough
1957 * @param {number | Pattern} amount
1958 * @synonyms det
1959 * @example
1960 * note("d f a a# a d3").fast(2).s("supersaw").detune("<.1 .2 .5 24.1>")
1961 *
1962 */
1963export const { detune, det } = registerControl('detune', 'det');
1964/**
1965 * Set number of stacked voices for supported oscillators.
1966 *
1967 * @name unison
1968 * @tags superdough
1969 * @param {number | Pattern} numvoices
1970 * @example
1971 * note("d f a a# a d3").fast(2).s("supersaw").unison("<1 2 7>")
1972 *
1973 */
1974export const { unison } = registerControl('unison');
1975
1976/**
1977 * Set the stereo pan spread for supported oscillators
1978 *
1979 * @name spread
1980 * @tags superdough
1981 * @param {number | Pattern} spread between 0 and 1
1982 * @example
1983 * note("d f a a# a d3").fast(2).s("supersaw").spread("<0 .3 1>")
1984 *
1985 */
1986export const { spread } = registerControl('spread');
1987/**
1988 * Set dryness of reverb. See `room` and `size` for more information about reverb.
1989 *
1990 * @name dry
1991 * @tags superdirt
1992 * @param {number | Pattern} dry 0 = wet, 1 = dry
1993 * @example
1994 * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc()
1995 * @superdirtOnly
1996 *
1997 */
1998export const { dry } = registerControl('dry');
1999/**
2000 * Used when using `begin`/`end` or `chop`/`striate` and friends, to change the fade out time of the 'grain' envelope.
2001 *
2002 * @name fadeTime
2003 * @tags superdirt
2004 * @synonyms fadeOutTime
2005 * @param {number | Pattern} time between 0 and 1
2006 * @example
2007 * s("oh*4").end(.1).fadeTime("<0 .2 .4 .8>").osc()
2008 *
2009 */
2010export const { fadeTime, fadeOutTime } = registerControl('fadeTime', 'fadeOutTime');
2011export const { fadeInTime } = registerControl('fadeInTime');
2012/**
2013 * Set frequency of sound.
2014 *
2015 * @name freq
2016 * @tags pitch, superdough
2017 * @param {number | Pattern} frequency in Hz. the audible range is between 20 and 20000 Hz
2018 * @example
2019 * freq("220 110 440 110").s("superzow").osc()
2020 * @example
2021 * freq("110".mul.out(".5 1.5 .6 [2 3]")).s("superzow").osc()
2022 *
2023 */
2024export const { freq } = registerControl('freq');
2025// pitch envelope
2026/**
2027 * Attack time of pitch envelope.
2028 *
2029 * @name pattack
2030 * @tags pitch, envelope, superdough, supradough
2031 * @synonyms patt
2032 * @param {number | Pattern} time time in seconds
2033 * @example
2034 * note("c eb g bb").pattack("0 .1 .25 .5").slow(2)
2035 *
2036 */
2037export const { pattack, patt } = registerControl('pattack', 'patt');
2038/**
2039 * Decay time of pitch envelope.
2040 *
2041 * @name pdecay
2042 * @tags pitch, envelope, superdough, supradough
2043 * @synonyms pdec
2044 * @param {number | Pattern} time time in seconds
2045 * @example
2046 * note("<c eb g bb>").pdecay("<0 .1 .25 .5>")
2047 *
2048 */
2049export const { pdecay, pdec } = registerControl('pdecay', 'pdec');
2050// TODO: how to use psustain?!
2051export const { psustain, psus } = registerControl('psustain', 'psus');
2052/**
2053 * Release time of pitch envelope
2054 *
2055 * @name prelease
2056 * @tags pitch, envelope, superdough, supradough
2057 * @synonyms prel
2058 * @param {number | Pattern} time time in seconds
2059 * @example
2060 * note("<c eb g bb> ~")
2061 * .release(.5) // to hear the pitch release
2062 * .prelease("<0 .1 .25 .5>")
2063 *
2064 */
2065export const { prelease, prel } = registerControl('prelease', 'prel');
2066/**
2067 * Amount of pitch envelope. Negative values will flip the envelope.
2068 * If you don't set other pitch envelope controls, `pattack:.2` will be the default.
2069 *
2070 * @name penv
2071 * @tags pitch, envelope, superdough, supradough
2072 * @param {number | Pattern} semitones change in semitones
2073 * @example
2074 * note("c")
2075 * .penv("<12 7 1 .5 0 -1 -7 -12>")
2076 *
2077 */
2078export const { penv } = registerControl('penv');
2079/**
2080 * Curve of envelope. Defaults to linear. exponential is good for kicks
2081 *
2082 * @name pcurve
2083 * @tags pitch, envelope, superdough
2084 * @param {number | Pattern} type 0 = linear, 1 = exponential
2085 * @example
2086 * note("g1*4")
2087 * .s("sine").pdec(.5)
2088 * .penv(32)
2089 * .pcurve("<0 1>")
2090 *
2091 */
2092export const { pcurve } = registerControl('pcurve');
2093/**
2094 * Sets the range anchor of the envelope:
2095 * - anchor 0: range = [note, note + penv]
2096 * - anchor 1: range = [note - penv, note]
2097 * If you don't set an anchor, the value will default to the psustain value.
2098 *
2099 * @name panchor
2100 * @tags pitch, envelope, superdough
2101 * @param {number | Pattern} anchor anchor offset
2102 * @example
2103 * note("c c4").penv(12).panchor("<0 .5 1 .5>")
2104 *
2105 */
2106export const { panchor } = registerControl('panchor');
2107// TODO: https://tidalcycles.org/docs/configuration/MIDIOSC/control-voltage/#gate
2108export const { gate, gat } = registerControl('gate', 'gat');
2109// ['hatgrain'],
2110// ['lagogo'],
2111// ['lclap'],
2112// ['lclaves'],
2113// ['lclhat'],
2114// ['lcrash'],
2115// TODO:
2116// https://tidalcycles.org/docs/reference/audio_effects/#leslie-1
2117// https://tidalcycles.org/docs/reference/audio_effects/#leslie
2118/**
2119 * Emulation of a Leslie speaker: speakers rotating in a wooden amplified cabinet.
2120 *
2121 * @name leslie
2122 * @tags superdirt
2123 * @param {number | Pattern} wet between 0 and 1
2124 * @example
2125 * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc()
2126 * @superdirtOnly
2127 *
2128 */
2129export const { leslie } = registerControl('leslie');
2130/**
2131 * Rate of modulation / rotation for leslie effect
2132 *
2133 * @name lrate
2134 * @tags superdirt
2135 * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow
2136 * @example
2137 * n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc()
2138 * @superdirtOnly
2139 *
2140 */
2141// TODO: the rate seems to "lag" (in the example, 1 will be fast)
2142export const { lrate } = registerControl('lrate');
2143/**
2144 * Physical size of the cabinet in meters. Be careful, it might be slightly larger than your computer. Affects the Doppler amount (pitch warble)
2145 *
2146 * @name lsize
2147 * @tags superdirt
2148 * @param {number | Pattern} meters somewhere between 0 and 1
2149 * @example
2150 * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc()
2151 * @superdirtOnly
2152 *
2153 */
2154export const { lsize } = registerControl('lsize');
2155/**
2156 * Sets the displayed text for an event on the pianoroll
2157 *
2158 * @name label
2159 * @tags visualization
2160 * @param {string} label text to display
2161 */
2162export const { activeLabel } = registerControl('activeLabel');
2163export const { label } = registerControl(['label', 'activeLabel']);
2164// ['lfo'],
2165// ['lfocutoffint'],
2166// ['lfodelay'],
2167// ['lfoint'],
2168// ['lfopitchint'],
2169// ['lfoshape'],
2170// ['lfosync'],
2171// ['lhitom'],
2172// ['lkick'],
2173// ['llotom'],
2174// ['lophat'],
2175// ['lsnare'],
2176// TODO: what is this? not found in tidal doc
2177export const { degree } = registerControl('degree');
2178// TODO: what is this? not found in tidal doc
2179export const { mtranspose } = registerControl('mtranspose');
2180// TODO: what is this? not found in tidal doc
2181export const { ctranspose } = registerControl('ctranspose');
2182// TODO: what is this? not found in tidal doc
2183export const { harmonic } = registerControl('harmonic');
2184// TODO: what is this? not found in tidal doc
2185export const { stepsPerOctave } = registerControl('stepsPerOctave');
2186// TODO: what is this? not found in tidal doc
2187export const { octaveR } = registerControl('octaveR');
2188// TODO: why is this needed? what's the difference to late / early? Answer: it's in seconds, and delays the message at
2189// OSC time (so can't be negative, at least not beyond the latency value)
2190export const { nudge } = registerControl('nudge');
2191// TODO: the following doc is just a guess, it's not documented in tidal doc.
2192/**
2193 * Sets the default octave of a synth.
2194 *
2195 * @name octave
2196 * @tags superdirt
2197 * @synonyms oct
2198 * @param {number | Pattern} octave octave number
2199 * @example
2200 * n("0,4,7").scale("F:minor").s('supersaw').octave("<0 1 2 3>")
2201 */
2202export const { octave, oct } = registerControl('octave', 'oct');
2203
2204// ['ophatdecay'],
2205// TODO: example
2206/**
2207 * An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share the same global effects.
2208 *
2209 * @name orbit
2210 * @tags superdough
2211 * @synonyms o
2212 * @param {number | Pattern} number
2213 * @example
2214 * stack(
2215 *   s("hh*6").delay(.5).delaytime(.25).orbit(1),
2216 *   s("~ sd ~ sd").delay(.5).delaytime(.125).orbit(2)
2217 * )
2218 */
2219export const { orbit } = registerControl('orbit', 'o');
2220
2221/**
2222 * A `bus` is a send which can be used for mixing patterns. It combines with..
2223 *   s("bus") to play that bus through another pattern (for, say, applying non-linear
2224 *   effects like distortion to multiple signals)
2225 *
2226 *   otherPat.bmod(..) (to modulate another pattern with the bus)
2227 *
2228 * @name bus
2229 * @tags superdirt
2230 * @param {number | Pattern} number
2231 */
2232export const { bus } = registerControl('bus');
2233
2234/**
2235 * Postgain multiplier prior to sending the signal to the audio bus.
2236 *
2237 * @name busgain
2238 * @tags superdirt
2239 * @synonyms bgain
2240 * @param {number | Pattern} number
2241 */
2242export const { busgain, bgain } = registerControl('busgain', 'bgain');
2243
2244// 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');
2246// TODO: what is this? not found in tidal doc. Similar to above, but limited to 1
2247export const { overshape } = registerControl('overshape');
2248/**
2249 * Sets position in stereo.
2250 *
2251 * @name pan
2252 * @tags superdough, supradough
2253 * @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel)
2254 * @example
2255 * s("[bd hh]*2").pan("<.5 1 .5 0>")
2256 * @example
2257 * s("bd rim sd rim bd ~ cp rim").pan(sine.slow(2))
2258 *
2259 */
2260export const { pan } = registerControl('pan');
2261/**
2262 * Controls how much multichannel output is fanned out
2263 *
2264 * @name panspan
2265 * @tags superdirt
2266 * @param {number | Pattern} span between -inf and inf, negative is backwards ordering
2267 * @example
2268 * s("[bd hh]*2").pan("<.5 1 .5 0>").panspan("<0 .5 1>").osc()
2269 *
2270 */
2271export const { panspan } = registerControl('panspan');
2272/**
2273 * Controls how much multichannel output is spread
2274 *
2275 * @name pansplay
2276 * @tags superdirt
2277 * @param {number | Pattern} spread between 0 and 1
2278 * @example
2279 * s("[bd hh]*2").pan("<.5 1 .5 0>").pansplay("<0 .5 1>").osc()
2280 *
2281 */
2282export const { pansplay } = registerControl('pansplay');
2283export const { panwidth } = registerControl('panwidth');
2284export const { panorient } = registerControl('panorient');
2285// ['pitch1'],
2286// ['pitch2'],
2287// ['pitch3'],
2288// ['portamento'],
2289
2290// TODO: slide param for certain synths
2291export const { slide } = registerControl('slide');
2292// TODO: detune? https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare
2293export const { semitone } = registerControl('semitone');
2294
2295// TODO: synth param
2296export const { voice } = registerControl('voice');
2297// voicings // https://codeberg.org/uzu/strudel/issues/506
2298/**
2299 * The chord to voice
2300 * @name chord
2301 * @tags tonal
2302 * @param {string | Pattern} symbols chord symbols to voice e.g., C, Eb, Fm7, G7. The symbols can be defined via addVoicings
2303 * @example
2304 * chord("<Am C D F Am E Am E>").voicing()
2305 **/
2306export const { chord } = registerControl('chord');
2307/**
2308 * Which dictionary to use for the voicings. This falls back to the default dictionary if not provided
2309 *
2310 * @name dictionary
2311 * @tags tonal
2312 * @param {string} dictionaryName which dictionary (having been defined with `addVoicings`) to use
2313 * @example
2314 * addVoicings('house', {
2315'': ['7 12 16', '0 7 16', '4 7 12'],
2316'm': ['0 3 7']
2317})
2318chord("<Am C D F Am E Am E>")
2319.dict('house').anchor(66)
2320.voicing().room(.5)
2321 **/
2322export const { dictionary, dict } = registerControl('dictionary', 'dict');
2323/** The top note to align the voicing to. Defaults to c5
2324 *
2325 * @name anchor
2326 * @tags tonal
2327 * @param {string | Pattern} anchorNote the note to align the voicing or scale to
2328 * @example
2329 * anchor("<c4 g4 c5 g5>").chord("C").voicing()
2330 * @example
2331 * n("0 .. 7").anchor("<c4 g4 c5 g5>").scale("<C:major F:minor>")
2332 **/
2333export const { anchor } = registerControl('anchor');
2334/**
2335 * Sets how the voicing is offset from the anchored position
2336 *
2337 * @name offset
2338 * @tags tonal
2339 * @param {number | Pattern} shift the amount to shift the voicing up or down
2340 * @example
2341 * chord("<Am C D F Am E Am E>").offset("<0 1 2 3 4 5>") // alter the voicing each time
2342 **/
2343export const { offset } = registerControl('offset');
2344/**
2345 *  How many octaves are voicing steps spread apart, defaults to 1
2346 *
2347 *  @name octaves
2348 *  @tags tonal
2349 *  @param {number | Pattern} count the number of octaves
2350 *  @example
2351 *  chord("<Am C D F Am E Am E>").octaves("<2 4>").voicing()
2352 **/
2353export const { octaves } = registerControl('octaves');
2354/**
2355 *  How the voicing is aligned to the anchor
2356 *   - `below`: top note <= anchor
2357 *   - `duck`: top note <= anchor, anchor excluded
2358 *   - `above`: bottom note >= anchor
2359 *   - `root`: bottom note is the lowest root of the chord >= anchor
2360 *
2361 *   - `oldabove` : old (buggy) behavior of above, kept for legacy reason
2362 *   - `oldroot` : old (buggy) behavior of root, kept for legacy reason
2363 *
2364 * @name mode
2365 * @tags tonal
2366 * @param {string | Pattern} modeName one of {below | above | duck | root | oldabove | oldroot}
2367 * @example
2368 * mode("<below above duck root>").chord("C").voicing()
2369 *
2370 **/
2371export const { mode } = registerControl(['mode', 'anchor']);
2372
2373/**
2374 * Sets the level of reverb.
2375 *
2376 * When using mininotation, you can also optionally add the 'size' parameter, separated by ':'.
2377 *
2378 * @name room
2379 * @tags orbit, superdough
2380 * @param {number | Pattern} level between 0 and 1
2381 * @example
2382 * s("bd sd [~ bd] sd").room("<0 .2 .4 .6 .8 1>")
2383 * @example
2384 * s("bd sd [~ bd] sd").room("<0.9:1 0.9:4>")
2385 *
2386 */
2387export const { room } = registerControl(['room', 'size']);
2388/**
2389 * Reverb lowpass starting frequency (in hertz).
2390 * When this property is changed, the reverb will be recaculated, so only change this sparsely..
2391 *
2392 * @name roomlp
2393 * @tags orbit, superdough
2394 * @synonyms rlp
2395 * @param {number} frequency between 0 and 20000hz
2396 * @example
2397 * s("bd sd [~ bd] sd").room(0.5).rlp(10000)
2398 * @example
2399 * s("bd sd [~ bd] sd").room(0.5).rlp(5000)
2400 */
2401export const { roomlp, rlp } = registerControl('roomlp', 'rlp');
2402/**
2403 * Reverb lowpass frequency at -60dB (in hertz).
2404 * When this property is changed, the reverb will be recaculated, so only change this sparsely..
2405 *
2406 * @name roomdim
2407 * @tags orbit, superdough
2408 * @synonyms rdim
2409 * @param {number} frequency between 0 and 20000hz
2410 * @example
2411 * s("bd sd [~ bd] sd").room(0.5).rlp(10000).rdim(8000)
2412 * @example
2413 * s("bd sd [~ bd] sd").room(0.5).rlp(5000).rdim(400)
2414 *
2415 */
2416export const { roomdim, rdim } = registerControl('roomdim', 'rdim');
2417/**
2418 * Reverb fade time (in seconds).
2419 * When this property is changed, the reverb will be recaculated, so only change this sparsely..
2420 *
2421 * @name roomfade
2422 * @tags orbit, superdough
2423 * @synonyms rfade
2424 * @param {number} seconds for the reverb to fade
2425 * @example
2426 * s("bd sd [~ bd] sd").room(0.5).rlp(10000).rfade(0.5)
2427 * @example
2428 * s("bd sd [~ bd] sd").room(0.5).rlp(5000).rfade(4)
2429 *
2430 */
2431export const { roomfade, rfade } = registerControl('roomfade', 'rfade');
2432/**
2433 * Sets the sample to use as an impulse response for the reverb.
2434 * @name iresponse
2435 * @tags orbit, superdough
2436 * @param {string | Pattern} sample to use as an impulse response
2437 * @synonyms ir
2438 * @example
2439 * s("bd sd [~ bd] sd").room(.8).ir("<shaker_large:0 shaker_large:2>")
2440 *
2441 */
2442export const { ir, iresponse } = registerControl(['ir', 'i'], 'iresponse');
2443
2444/**
2445 * Sets speed of the sample for the impulse response.
2446 * @name irspeed
2447 * @tags orbit, superdough
2448 * @param {string | Pattern} speed
2449 * @example
2450 * samples('github:switchangel/pad')
2451 * $: 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)
2452 *
2453 */
2454export const { irspeed } = registerControl('irspeed');
2455
2456/**
2457 * Sets the beginning of the IR response sample
2458 * @name irbegin
2459 * @tags orbit, superdough
2460 * @param {string | Pattern} begin between 0 and 1
2461 * @synonyms ir
2462 * @example
2463 * samples('github:switchangel/pad')
2464 * $: 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)
2465 *
2466 */
2467export const { irbegin } = registerControl('irbegin');
2468/**
2469 * Sets the room size of the reverb, see `room`.
2470 * When this property is changed, the reverb will be recaculated, so only change this sparsely..
2471 *
2472 * @name roomsize
2473 * @tags orbit, superdough
2474 * @param {number | Pattern} size between 0 and 10
2475 * @synonyms rsize, sz, size
2476 * @example
2477 * s("bd sd [~ bd] sd").room(.8).rsize(1)
2478 * @example
2479 * s("bd sd [~ bd] sd").room(.8).rsize(4)
2480 *
2481 */
2482// TODO: find out why :
2483// s("bd sd [~ bd] sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc()
2484// .. does not work. Is it because room is only one effect?
2485export const { roomsize, size, sz, rsize } = registerControl('roomsize', 'size', 'sz', 'rsize');
2486// ['sagogo'],
2487// ['sclap'],
2488// ['sclaves'],
2489// ['scrash'],
2490/**
2491 * (Deprecated) Wave shaping distortion. WARNING: can suddenly get unpredictably loud.
2492 * Please use distort instead, which has a more predictable response curve
2493 * second option in optional array syntax (ex: ".9:.5") applies a postgain to the output
2494 *
2495 *
2496 * @name shape
2497 * @tags distortion, superdough
2498 * @param {number | Pattern} distortion between 0 and 1
2499 * @example
2500 * s("bd sd [~ bd] sd,hh*8").shape("<0 .2 .4 .6 .8>")
2501 *
2502 */
2503export const { shape } = registerControl(['shape', 'shapevol']);
2504/**
2505 * Wave shaping distortion. CAUTION: it can get loud.
2506 * Second option in optional array syntax (ex: ".9:.5") applies a postgain to the output. Third option sets the waveshaping type.
2507 * 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 ;)
2508 *
2509 * @name distort
2510 * @tags distortion, superdough, supradough
2511 * @synonyms dist
2512 * @param {number | Pattern} distortion amount of distortion to apply
2513 * @param {number | Pattern} volume linear postgain of the distortion
2514 * @param {number | string | Pattern} type type of distortion to apply
2515 * @example
2516 * s("bd sd [~ bd] sd,hh*8").distort("<0 2 3 10:.5>")
2517 * @example
2518 * note("d1!8").s("sine").penv(36).pdecay(.12).decay(.23).distort("8:.4")
2519 * @example
2520 * s("bd:4*4").bank("tr808").distort("3:0.5:diode")
2521 *
2522 */
2523export const { distort, dist } = registerControl(['distort', 'distortvol', 'distorttype'], 'dist');
2524
2525/**
2526 * Postgain for waveshaping distortion.
2527 *
2528 * @name distortvol
2529 * @synonyms distortion, distvol
2530 * @tags superdough, supradough
2531 * @param {number | Pattern} volume linear postgain of the distortion
2532 * @example
2533 * s("bd*4").bank("tr909").distort(2).distortvol(0.8)
2534 */
2535export const { distortvol } = registerControl('distortvol', 'distvol');
2536
2537/**
2538 * Type of waveshaping distortion to apply.
2539 *
2540 * @name distorttype
2541 * @tags distortion, superdough, supradough
2542 * @synonyms disttype
2543 * @param {number | string | Pattern} type type of distortion to apply
2544 * @example
2545 * s("bd*4").bank("tr909").distort(2).distorttype("<0 1 2>")
2546 *
2547 * @example
2548 * s("sine").note("F1*2").release(1)
2549 *   .penv(24).pdecay(0.05)
2550 *   .distort(rand.range(1, 8))
2551 *   .distorttype("<fold chebyshev scurve diode asym sinefold>")
2552 */
2553export const { distorttype } = registerControl('distorttype', 'disttype');
2554
2555/**
2556 * Dynamics Compressor. The params are `compressor("threshold:ratio:knee:attack:release")`
2557 * More info [here](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode?retiredLocale=de#instance_properties)
2558 *
2559 * @name compressor
2560 * @tags superdough
2561 * @example
2562 * s("bd sd [~ bd] sd,hh*8")
2563 * .compressor("-20:20:10:.002:.02")
2564 *
2565 */
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');
2577/**
2578 * Changes the speed of sample playback, i.e. a cheap way of changing pitch.
2579 *
2580 * @name speed
2581 * @tags pitch, samples
2582 * @param {number | Pattern} speed -inf to inf, negative numbers play the sample backwards.
2583 * @example
2584 * s("bd*6").speed("1 2 4 1 -2 -4")
2585 * @example
2586 * speed("1 1.5*2 [2 1.1]").s("piano").clip(1)
2587 *
2588 */
2589export const { speed } = registerControl('speed');
2590
2591/**
2592 * Changes the pitch of the sample without changing its speed.
2593 * The frequencies are multiplied by (factor + 1) for positive numbers
2594 * and by max(factor / 4 + 1, 0) for negative numbers.
2595 * So tuning up by octaves can be done with 1, 3, 7, ...
2596 * and tuning down by octaves with -2, -3, -3.5...
2597 *
2598 * @name stretch
2599 * @tags pitch, samples
2600 * @param {number | Pattern} factor between `-4` and `inf`. Positive increases pitch, 0 does nothing, negative decreases the pitch.
2601 * @example
2602 * s("gm_flute").stretch("<2 1 0 -2>")
2603 *
2604 */
2605export const { stretch } = registerControl('stretch');
2606/**
2607 * 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`.
2608 *
2609 * @name unit
2610 * @tags superdirt
2611 * @param {number | string | Pattern} unit see description above
2612 * @example
2613 * speed("1 2 .5 3").s("bd").unit("c").osc()
2614 * @superdirtOnly
2615 *
2616 */
2617
2618export const { unit } = registerControl('unit');
2619/**
2620 * Made by Calum Gunn. Reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter. The SuperCollider manual defines Squiz as:
2621 *
2622 * "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."
2623 *
2624 * @name squiz
2625 * @tags superdirt
2626 * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc.
2627 * @example
2628 * squiz("2 4/2 6 [8 16]").s("bd").osc()
2629 * @superdirtOnly
2630 *
2631 */
2632export const { squiz } = registerControl('squiz');
2633// TODO: what is this? not found in tidal doc
2634// ['stutterdepth'],
2635// TODO: what is this? not found in tidal doc
2636// ['stuttertime'],
2637// TODO: what is this? not found in tidal doc
2638// ['timescale'],
2639// TODO: what is this? not found in tidal doc
2640// ['timescalewin'],
2641// ['tomdecay'],
2642// ['vcfegint'],
2643// ['vcoegint'],
2644// TODO: Use a rest (~) to override the effect <- vowel
2645/**
2646 *
2647 * Formant filter to make things sound like vowels.
2648 *
2649 * @name vowel
2650 * @tags superdough
2651 * @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 = æ.
2652 * @example
2653 * note("[c2 <eb2 <g2 g1>>]*2").s('sawtooth')
2654 * .vowel("<a e i <o u>>")
2655 * @example
2656 * s("bd sd mt ht bd [~ cp] ht lt").vowel("[a|e|i|o|u]")
2657 *
2658 */
2659export const { vowel } = registerControl('vowel');
2660/* // TODO: find out how it works
2661 * 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:
2662 *
2663 * 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.)
2664 *
2665 * mode: ?
2666 * waveloss: ?
2667 *
2668 * @name waveloss
2669 */
2670export const { waveloss } = registerControl('waveloss');
2671/**
2672 * crackle noise density
2673 *
2674 * @name density
2675 * @tags superdough
2676 * @param {number | Pattern} density between 0 and x
2677 * @example
2678 * s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(4))
2679 *
2680 */
2681export const { density } = registerControl('density');
2682// ['modwheel'],
2683export const { expression } = registerControl('expression');
2684export const { sustainpedal } = registerControl('sustainpedal');
2685
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');
2721/**
2722 * Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration.
2723 *
2724 * @name clip
2725 * @tags superdough
2726 * @synonyms legato
2727 * @param {number | Pattern} factor >= 0
2728 * @example
2729 * note("c a f e").s("piano").clip("<.5 1 2>")
2730 *
2731 */
2732export const { clip, legato } = registerControl('clip', 'legato');
2733
2734/**
2735 * 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.
2736 *
2737 * @name duration
2738 * @tags superdough
2739 * @synonyms dur
2740 * @param {number | Pattern} seconds >= 0
2741 * @example
2742 * note("c a f e").s("piano").dur("<.5 1 2>")
2743 *
2744 */
2745export const { duration, dur } = registerControl('duration', 'dur');
2746
2747// ZZFX
2748export const { zrand } = registerControl('zrand');
2749export const { curve } = registerControl('curve');
2750// superdirt duplicate
2751// export const {slide]} = registerControl('slide']);
2752export const { deltaSlide } = registerControl('deltaSlide');
2753export const { pitchJump } = registerControl('pitchJump');
2754export const { pitchJumpTime } = registerControl('pitchJumpTime');
2755// noise on the frequency or as bubo calls it "frequency fog" :)
2756export const { znoise } = registerControl('znoise');
2757export const { zmod } = registerControl('zmod');
2758// like crush but scaled differently
2759export const { zcrush } = registerControl('zcrush');
2760export const { zdelay } = registerControl('zdelay');
2761export const { zzfx } = registerControl('zzfx');
2762
2763/**
2764 * Sets the color of the hap in visualizations like pianoroll or highlighting.
2765 * @name color
2766 * @tags visualization
2767 * @synonyms colour
2768 * @param {string} color Hexadecimal or CSS color name
2769 */
2770export const { color, colour } = registerControl(['color', 'colour']);
2771
2772// TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13
2773
2774export let createParams = (...names) =>
2775  names.reduce((acc, name) => Object.assign(acc, { [name]: createParam(name) }), {});
2776
2777/**
2778 * ADSR envelope: Combination of Attack, Decay, Sustain, and Release.
2779 *
2780 * @name adsr
2781 * @tags envelope, amplitude
2782 * @param {number | Pattern} time attack time in seconds
2783 * @param {number | Pattern} time decay time in seconds
2784 * @param {number | Pattern} gain sustain level (0 to 1)
2785 * @param {number | Pattern} time release time in seconds
2786 * @example
2787 * note("[c3 bb2 f3 eb3]*2").sound("sawtooth").lpf(600).adsr(".1:.1:.5:.2")
2788 */
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});
2809
2810//MIDI
2811
2812/**
2813 * MIDI channel: Sets the MIDI channel for the event.
2814 *
2815 * @name midichan
2816 * @tags external_io, midi
2817 * @param {number | Pattern} channel MIDI channel number (0-15)
2818 * @example
2819 * note("c4").midichan(1).midi()
2820 */
2821export const { midichan } = registerControl('midichan');
2822
2823export const { midimap } = registerControl('midimap');
2824
2825/**
2826 * MIDI port: Sets the MIDI port for the event.
2827 *
2828 * @name midiport
2829 * @tags external_io, midi
2830 * @param {number | Pattern} port MIDI port
2831 * @example
2832 * note("c a f e").midiport("<0 1 2 3>").midi()
2833 */
2834export const { midiport } = registerControl('midiport');
2835
2836/**
2837 * MIDI command: Sends a MIDI command message.
2838 *
2839 * @name midicmd
2840 * @tags external_io, midi
2841 * @param {number | Pattern} command MIDI command
2842 * @example
2843 * midicmd("clock*48,<start stop>/2").midi()
2844 */
2845export const { midicmd } = registerControl('midicmd');
2846
2847/**
2848 * MIDI control: Sends a MIDI control change message.
2849 *
2850 * @name control
2851 * @tags external_io, midi
2852 * @param {number | Pattern}  MIDI control number (0-127)
2853 * @param {number | Pattern}  MIDI controller value (0-127)
2854 */
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});
2862
2863/**
2864 * MIDI control number: Sends a MIDI control change message.
2865 *
2866 * @name ccn
2867 * @tags external_io, midi
2868 * @param {number | Pattern}  MIDI control number (0-127)
2869 */
2870export const { ccn } = registerControl('ccn');
2871/**
2872 * MIDI control value: Sends a MIDI control change message.
2873 *
2874 * @name ccv
2875 * @tags external_io, midi
2876 * @param {number | Pattern}  MIDI control value (0-127)
2877 */
2878export const { ccv } = registerControl('ccv');
2879export const { ctlNum } = registerControl('ctlNum');
2880// TODO: ctlVal?
2881
2882/**
2883 * MIDI NRPN non-registered parameter number: Sends a MIDI NRPN non-registered parameter number message.
2884 * @name nrpnn
2885 * @tags external_io, midi
2886 * @param {number | Pattern} nrpnn MIDI NRPN non-registered parameter number (0-127)
2887 * @example
2888 * note("c4").nrpnn("1:8").nrpv("123").midichan(1).midi()
2889 */
2890export const { nrpnn } = registerControl('nrpnn');
2891/**
2892 * MIDI NRPN non-registered parameter value: Sends a MIDI NRPN non-registered parameter value message.
2893 * @name nrpv
2894 * @tags external_io, midi
2895 * @param {number | Pattern} nrpv MIDI NRPN non-registered parameter value (0-127)
2896 * @example
2897 * note("c4").nrpnn("1:8").nrpv("123").midichan(1).midi()
2898 */
2899export const { nrpv } = registerControl('nrpv');
2900
2901/**
2902 * MIDI program number: Sends a MIDI program change message.
2903 *
2904 * @name progNum
2905 * @tags external_io
2906 * @param {number | Pattern} program MIDI program number (0-127)
2907 * @example
2908 * note("c4").progNum(10).midichan(1).midi()
2909 */
2910export const { progNum } = registerControl('progNum');
2911
2912/**
2913 * MIDI sysex: Sends a MIDI sysex message.
2914 * @name sysex
2915 * @tags external_io, midi
2916 * @param {number | Pattern} id Sysex ID
2917 * @param {number | Pattern} data Sysex data
2918 * @example
2919 * note("c4").sysex(["0x77", "0x01:0x02:0x03:0x04"]).midichan(1).midi()
2920 */
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});
2928/**
2929 * MIDI sysex ID: Sends a MIDI sysex identifier message.
2930 * @name sysexid
2931 * @tags external_io, midi
2932 * @param {number | Pattern} id Sysex ID
2933 * @example
2934 * note("c4").sysexid("0x77").sysexdata("0x01:0x02:0x03:0x04").midichan(1).midi()
2935 */
2936export const { sysexid } = registerControl('sysexid');
2937/**
2938 * MIDI sysex data: Sends a MIDI sysex message.
2939 * @name sysexdata
2940 * @tags external_io, midi
2941 * @param {number | Pattern} data Sysex data
2942 * @example
2943 * note("c4").sysexid("0x77").sysexdata("0x01:0x02:0x03:0x04").midichan(1).midi()
2944 */
2945export const { sysexdata } = registerControl('sysexdata');
2946
2947/**
2948 * MIDI pitch bend: Sends a MIDI pitch bend message.
2949 * @name midibend
2950 * @tags external_io, midi
2951 * @param {number | Pattern} midibend MIDI pitch bend (-1 - 1)
2952 * @example
2953 * note("c4").midibend(sine.slow(4).range(-0.4,0.4)).midi()
2954 */
2955export const { midibend } = registerControl('midibend');
2956/**
2957 * MIDI key after touch: Sends a MIDI key after touch message.
2958 * @name miditouch
2959 * @tags external_io, midi
2960 * @param {number | Pattern} miditouch MIDI key after touch (0-1)
2961 * @example
2962 * note("c4").miditouch(sine.slow(4).range(0,1)).midi()
2963 */
2964export const { miditouch } = registerControl('miditouch');
2965
2966// TODO: what is this?
2967export const { polyTouch } = registerControl('polyTouch');
2968
2969/**
2970 * The host to send open sound control messages to. Requires running the OSC bridge.
2971 * @name oschost
2972 * @tags external_io
2973 * @param {string | Pattern} oschost e.g. 'localhost'
2974 * @example
2975 * note("c4").oschost('127.0.0.1').oscport(57120).osc();
2976 */
2977export const { oschost } = registerControl('oschost');
2978
2979/**
2980 * The port to send open sound control messages to. Requires running the OSC bridge.
2981 * @name oscport
2982 * @tags external_io
2983 * @param {number | Pattern} oscport e.g. 57120
2984 * @example
2985 * note("c4").oschost('127.0.0.1').oscport(57120).osc();
2986 */
2987export const { oscport } = registerControl('oscport');
2988
2989export const getControlName = (alias) => {
2990  if (controlAlias.has(alias)) {
2991    return controlAlias.get(alias);
2992  }
2993  return alias;
2994};
2995
2996/**
2997 * Sets properties in a batch.
2998 *
2999 * @name as
3000 * @tags combiners
3001 * @param {String | Array} mapping the control names that are set
3002 * @example
3003 * "c:.5 a:1 f:.25 e:.8".as("note:clip")
3004 * @example
3005 * "{0@2 0.25 0 0.5 .3 .5}%8".as("begin").s("sax_vib").clip(1)
3006 */
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});
3020
3021/**
3022 * Allows you to scrub an audio file like a tape loop by passing values that represents the position in the audio file
3023 * in the optional array syntax ex: "0.5:2", the second value controls the speed of playback
3024 * @name scrub
3025 * @tags samples
3026 * @memberof Pattern
3027 * @returns Pattern
3028 * @example
3029 * samples('github:switchangel/pad')
3030 * s("swpad:0").scrub("{0.1!2 .25@3 0.7!2 <0.8:1.5>}%8")
3031 * @example
3032 * samples('github:yaxu/clean-breaks/main');
3033 * s("amen/4").fit().scrub("{0@3 0@2 4@3}%8".div(16))
3034 */
3035
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);
3050
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};
3154
3155/**
3156 * Configures an LFO. Can be called in sequence like pat.lfo(...).lfo(...) to set up multiple LFOs.
3157 * There are two ways to declare which control will be modulated:
3158 * 1. Explicitly put `control` in the config (e.g. `lfo({ c: "lpf" })`)
3159 * 2. If the control parameter is absent, the control _immediately before_ the `lfo` call will be used
3160 *   (e.g. `s("saw").lpf(500).lfo()` to modulate `lpf`)
3161 *
3162 * Modulators can be referred to by `id` so that they can be updated later e.g. inside
3163 * a `sometimes`. See example below.
3164 *
3165 * @name lfo
3166 * @tags lfo, superdough
3167 * @param {Object} config LFO configuration.
3168 * @param {string | Pattern} [config.control] Node to modulate. Aliases: c
3169 * @param {string | Pattern} [config.subControl] Sub-control name to append to the control key. Aliases: sc
3170 * @param {number | Pattern} [config.rate] Modulation rate. Aliases: r
3171 * @param {number | Pattern} [config.sync] Tempo-synced modulation rate. Aliases: s
3172 * @param {number | Pattern} [config.depth] Relative modulation depth. Aliases: dep, dr
3173 * @param {number | Pattern} [config.depthabs] Absolute modulation depth. Aliases: da
3174 * @param {number | Pattern} [config.dcoffset] DC offset / bias for the waveform. Aliases: dc
3175 * @param {number | Pattern} [config.shape] Shape index. Aliases: sh
3176 * @param {number | Pattern} [config.skew] Skew amount. Aliases: sk
3177 * @param {number | Pattern} [config.curve] Exponential curve amount. Aliases: cu
3178 * @param {number | Pattern} [config.retrig] If > 0.5, the LFO will retrigger on each event. Aliases: rt
3179 * @param {number | Pattern} [config.fxi] FX index to target
3180 * @param {string | Pattern} id ID to use for this modulator
3181 * @returns Pattern
3182 *
3183 * @example
3184 * s("saw").note("F1").lpf(500).lfo()
3185 *
3186 * @example
3187 * s("saw").lfo().lpf(500).lfo({ s: 0.3 })
3188 *
3189 * @example
3190 * s("saw").lpf(500).diode(0.3)
3191 *   .lfo({ c: "lpf" })
3192 *
3193 * @example
3194 * s("pulse").lpf(500).lfo()
3195 *   .lfo({ c: "s" })
3196 *   .diode(0.3)
3197 *   .sometimes(x => x.lfo({ s: "8" }, 1)) // lfo #1 (0-indexed)
3198 *
3199 * @example
3200 * s("pulse").lpf(500).lfo({ depth: 4 }, 'lpf_mod')
3201 *   .lfo({ c: "s" })
3202 *   .diode(0.3)
3203 *   .sometimes(x => x.lfo({ s: "8" }, 'lpf_mod'))
3204 */
3205Pattern.prototype.lfo = function (config, id) {
3206  return this.modulate('lfo', config, id);
3207};
3208export const lfo = (config) => pure({}).lfo(config);
3209
3210/**
3211 * Configures an envelope. Can be called in sequence like pat.env(...).env(...) to set up multiple envelopes
3212 * There are two ways to declare which control will be modulated:
3213 * 1. Explicitly put `control` in the config (e.g. `env({ c: "lpf" })`)
3214 * 2. If the control parameter is absent, the control _immediately before_ the `env` call will be used
3215 *   (e.g. `s("saw").lpf(500).env({ a: 1 })` to modulate `lpf`)
3216 *
3217 * Modulators can be referred to by `id` so that they can be updated later e.g. inside
3218 * a `sometimes`. See example below.
3219 *
3220 * @name env
3221 * @tags envelope, superdough
3222 * @param {Object} config Envelope configuration.
3223 * @param {string | Pattern} [config.control] Node to modulate. Aliases: c
3224 * @param {string | Pattern} [config.subControl] Sub-control name to append to the control key. Aliases: sc
3225 * @param {number | Pattern} [config.depth] Relative modulation depth. Aliases: dep, dr
3226 * @param {number | Pattern} [config.depthabs] Absolute modulation depth. Aliases: da
3227 * @param {number | Pattern} [config.attack] Time to reach depth. Aliases: att, a
3228 * @param {number | Pattern} [config.decay] Time to reach sustain. Aliases: dec, d
3229 * @param {number | Pattern} [config.sustain] Sustain depth. Aliases: sus, s
3230 * @param {number | Pattern} [config.release] Time to return to nominal value. Aliases: rel, r
3231 * @param {number | Pattern} [config.acurve] Snappiness of attack curve (-1 = relaxed, 1 = snappy). Aliases: ac
3232 * @param {number | Pattern} [config.dcurve] Snappiness of decay curve (-1 = relaxed, 1 = snappy). Aliases: dc
3233 * @param {number | Pattern} [config.rcurve] Snappiness of release curve (-1 = relaxed, 1 = snappy). Aliases: rc
3234 * @param {number | Pattern} [config.fxi] FX index to target
3235 * @param {string | Pattern} id ID to use for this modulator
3236 * @returns Pattern
3237 *
3238 * @example
3239 * s("saw").note("F1").lpf(500).env({ a: 1 })
3240 *
3241 * @example
3242 * s("saw").env({ d: 1 }).note("F1")
3243 *   .lpq(4).lpf(50)
3244 *   .env({ a: 0.1, d: 1, ac: 0.8, dc: 0.3, depth: 50 })
3245 *
3246 * @example
3247 * s("saw").lpf(500).diode(0.3)
3248 *   .env({ c: "lpf", a: 0.5, d: 0.5 })
3249 *
3250 * @example
3251 * s("pulse").lpf(500).env({ a: 1 })
3252 *   .env({ c: "s", a: 1 })
3253 *   .diode(0.3)
3254 *   .sometimes(x => x.env({ a: "0.5" }, 1)) // envelope #1 (0-indexed)
3255 *
3256 * @example
3257 * s("pulse").lpf(500).env({ a: 1 }, 'lpf_mod')
3258 *   .env({ c: "s", a: 1 })
3259 *   .diode(0.3)
3260 *   .sometimes(x => x.env({ a: "0.5" }, 'lpf_mod'))
3261 */
3262Pattern.prototype.env = function (config, id) {
3263  return this.modulate('env', config, id);
3264};
3265export const env = (config) => pure({}).env(config);
3266
3267/**
3268 * Modulates with the output from a given `bus`.
3269 * Can be called in sequence like pat.bmod(...).bmod(...) to set up multiple modulators
3270 *
3271 * Send to an audio bus with `otherPat.bus(..)`.
3272 *
3273 * There are two ways to declare which control will be modulated:
3274 * 1. Explicitly put `control` in the config (e.g. `bmod({ id: 2, c: "lpf" })`)
3275 * 2. If the control parameter is absent, the control _immediately before_ the `bmod` call will be used
3276 *   (e.g. `s("saw").lpf(500).bmod({ id: 2 })` to modulate `lpf`)
3277 *
3278 * Modulators can be referred to by `id` so that they can be updated later e.g. inside
3279 * a `sometimes`. See example below.
3280 *
3281 * @name bmod
3282 * @tags superdough
3283 * @param {Object} config Bus modulation configuration.
3284 * @param {string | Pattern} [config.bus] Bus to get modulation signal from
3285 * @param {string | Pattern} [config.control] Node to modulate. Aliases: c
3286 * @param {string | Pattern} [config.subControl] Sub-control name to append to the control key. Aliases: sc
3287 * @param {number | Pattern} [config.depth] Relative modulation depth. Aliases: dep, dr
3288 * @param {number | Pattern} [config.depthabs] Absolute modulation depth. Aliases: da
3289 * @param {number | Pattern} [config.dc] DC offset prior to application
3290 * @param {number | Pattern} [config.fxi] FX index to target
3291 * @param {string | Pattern} id ID to use for this modulator
3292 * @returns Pattern
3293 *
3294 * @example
3295 * modulator: s("one").seg(64).gain(slider(0, 0, 1)).bus(1).dry(0)
3296 * carrier: s("saw").bmod({ b: 1 })
3297 *
3298 */
3299Pattern.prototype.bmod = function (config, id) {
3300  return this.modulate('bmod', config, id);
3301};
3302export const bmod = (config) => pure({}).bmod(config);
3303
3304/**
3305 * Transient shaper. Gives independent control over the emphasis on transients
3306 * and sustains
3307 *
3308 * @name transient
3309 * @tags superdough
3310 * @param {number | Pattern} attack Emphasis on transients; between -1 (deaccentuate) and 1 (accentuate)
3311 * @param {number | Pattern} sustain Emphasis on the sustains; between -1 (deaccentuate) and 1 (accentuate)
3312 * @example
3313 * s("bd").transient("<-1 -0.5 0 0.5 1>")
3314 * @example
3315 * s("hh*16").bank("tr909").transient("<-1:1 1:-1>")
3316 */
3317export const { transient } = registerControl(['transient', 'transsustain']);
3318
3319export const { FXrelease, FXrel, FXr, fxr } = registerControl('FXrelease', 'FXrel', 'FXr', 'fxr');