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');