From 755e24315e04223f75d22ac7680d05576b034c2f Mon Sep 17 00:00:00 2001 From: Vasilii Milovidov Date: Sun, 1 Oct 2023 15:31:10 +0400 Subject: [PATCH] use sample as an impulse response for the reverb --- packages/core/controls.mjs | 2384 ++++++++++++++-------------- packages/superdough/reverb.mjs | 29 +- packages/superdough/superdough.mjs | 11 +- 3 files changed, 1220 insertions(+), 1204 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 76a278a5f..bc06ecc2b 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -4,1231 +4,1233 @@ Copyright (C) 2022 Strudel contributors - see . */ -import {Pattern, register, sequence} from './pattern.mjs'; -import {zipWith} from './util.mjs'; +import { Pattern, register, sequence } from './pattern.mjs'; +import { zipWith } from './util.mjs'; const controls = {}; const generic_params = [ - /** - * Select a sound / sample by name. When using mininotation, you can also optionally supply 'n' and 'gain' parameters - * separated by ':'. - * - * @name s - * @param {string | Pattern} sound The sound / pattern of sounds to pick - * @synonyms sound - * @example - * s("bd hh") - * @example - * s("bd:0 bd:1 bd:0:0.3 bd:1:1.4") - * - */ - [['s', 'n', 'gain'], 'sound'], - /** - * Define a custom webaudio node to use as a sound source. - * - * @name source - * @param {function} getSource - * @synonyms src - * - */ - ['source', 'src'], - /** - * Selects the given index from the sample map. - * Numbers too high will wrap around. - * `n` can also be used to play midi numbers, but it is recommended to use `note` instead. - * - * @name n - * @param {number | Pattern} value sample index starting from 0 - * @example - * s("bd sd,hh*3").n("<0 1>") - */ - // also see https://github.com/tidalcycles/strudel/pull/63 - ['n'], - /** - * Plays the given note name or midi number. A note name consists of - * - * - a letter (a-g or A-G) - * - optional accidentals (b or #) - * - optional octave number (0-9). Defaults to 3 - * - * Examples of valid note names: `c`, `bb`, `Bb`, `f#`, `c3`, `A4`, `Eb2`, `c#5` - * - * You can also use midi numbers instead of note names, where 69 is mapped to A4 440Hz in 12EDO. - * - * @name note - * @example - * note("c a f e") - * @example - * note("c4 a4 f4 e4") - * @example - * note("60 69 65 64") - */ - [['note', 'n']], + /** + * Select a sound / sample by name. When using mininotation, you can also optionally supply 'n' and 'gain' parameters + * separated by ':'. + * + * @name s + * @param {string | Pattern} sound The sound / pattern of sounds to pick + * @synonyms sound + * @example + * s("bd hh") + * @example + * s("bd:0 bd:1 bd:0:0.3 bd:1:1.4") + * + */ + [['s', 'n', 'gain'], 'sound'], + /** + * Define a custom webaudio node to use as a sound source. + * + * @name source + * @param {function} getSource + * @synonyms src + * + */ + ['source', 'src'], + /** + * Selects the given index from the sample map. + * Numbers too high will wrap around. + * `n` can also be used to play midi numbers, but it is recommended to use `note` instead. + * + * @name n + * @param {number | Pattern} value sample index starting from 0 + * @example + * s("bd sd,hh*3").n("<0 1>") + */ + // also see https://github.com/tidalcycles/strudel/pull/63 + ['n'], + /** + * Plays the given note name or midi number. A note name consists of + * + * - a letter (a-g or A-G) + * - optional accidentals (b or #) + * - optional octave number (0-9). Defaults to 3 + * + * Examples of valid note names: `c`, `bb`, `Bb`, `f#`, `c3`, `A4`, `Eb2`, `c#5` + * + * You can also use midi numbers instead of note names, where 69 is mapped to A4 440Hz in 12EDO. + * + * @name note + * @example + * note("c a f e") + * @example + * note("c4 a4 f4 e4") + * @example + * note("60 69 65 64") + */ + [['note', 'n']], - /** - * A pattern of numbers that speed up (or slow down) samples while they play. Currently only supported by osc / superdirt. - * - * @name accelerate - * @param {number | Pattern} amount acceleration. - * @superdirtOnly - * @example - * s("sax").accelerate("<0 1 2 4 8 16>").slow(2).osc() - * - */ - ['accelerate'], - /** - * Controls the gain by an exponential amount. - * - * @name gain - * @param {number | Pattern} amount gain. - * @example - * s("hh*8").gain(".4!2 1 .4!2 1 .4 1") - * - */ - ['gain'], - /** - * Like {@link gain}, but linear. - * - * @name amp - * @param {number | Pattern} amount gain. - * @superdirtOnly - * @example - * s("bd*8").amp(".1*2 .5 .1*2 .5 .1 .5").osc() - * - */ - ['amp'], - /** - * Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset. - * - * @name attack - * @param {number | Pattern} attack time in seconds. - * @synonyms att - * @example - * note("c3 e3").attack("<0 .1 .5>") - * - */ - ['attack', 'att'], + /** + * A pattern of numbers that speed up (or slow down) samples while they play. Currently only supported by osc / superdirt. + * + * @name accelerate + * @param {number | Pattern} amount acceleration. + * @superdirtOnly + * @example + * s("sax").accelerate("<0 1 2 4 8 16>").slow(2).osc() + * + */ + ['accelerate'], + /** + * Controls the gain by an exponential amount. + * + * @name gain + * @param {number | Pattern} amount gain. + * @example + * s("hh*8").gain(".4!2 1 .4!2 1 .4 1") + * + */ + ['gain'], + /** + * Like {@link gain}, but linear. + * + * @name amp + * @param {number | Pattern} amount gain. + * @superdirtOnly + * @example + * s("bd*8").amp(".1*2 .5 .1*2 .5 .1 .5").osc() + * + */ + ['amp'], + /** + * Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset. + * + * @name attack + * @param {number | Pattern} attack time in seconds. + * @synonyms att + * @example + * note("c3 e3").attack("<0 .1 .5>") + * + */ + ['attack', 'att'], - /** - * Sets the Frequency Modulation Harmonicity Ratio. - * Controls the timbre of the sound. - * Whole numbers and simple ratios sound more natural, - * while decimal numbers and complex ratios sound metallic. - * - * @name fmh - * @param {number | Pattern} harmonicity - * @example - * note("c e g b") - * .fm(4) - * .fmh("<1 2 1.5 1.61>") - * .scope() - * - */ - [['fmh', 'fmi'], 'fmh'], - /** - * Sets the Frequency Modulation of the synth. - * Controls the modulation index, which defines the brightness of the sound. - * - * @name fm - * @param {number | Pattern} brightness modulation index - * @synonyms fmi - * @example - * note("c e g b") - * .fm("<0 1 2 8 32>") - * .scope() - * - */ - [['fmi', 'fmh'], 'fm'], - // fm envelope - /** - * Ramp type of fm envelope. Exp might be a bit broken.. - * - * @name fmenv - * @param {number | Pattern} type lin | exp - * @example - * note("c e g b") - * .fm(4) - * .fmdecay(.2) - * .fmsustain(0) - * .fmenv("") - * .scope() - * - */ - ['fmenv'], - /** - * Attack time for the FM envelope: time it takes to reach maximum modulation - * - * @name fmattack - * @param {number | Pattern} time attack time - * @example - * note("c e g b") - * .fm(4) - * .fmattack("<0 .05 .1 .2>") - * .scope() - * - */ - ['fmattack'], - /** - * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase. - * - * @name fmdecay - * @param {number | Pattern} time decay time - * @example - * note("c e g b") - * .fm(4) - * .fmdecay("<.01 .05 .1 .2>") - * .fmsustain(.4) - * .scope() - * - */ - ['fmdecay'], - /** - * Sustain level for the FM envelope: how much modulation is applied after the decay phase - * - * @name fmsustain - * @param {number | Pattern} level sustain level - * @example - * note("c e g b") - * .fm(4) - * .fmdecay(.1) - * .fmsustain("<1 .75 .5 0>") - * .scope() - * - */ - ['fmsustain'], - // these are not really useful... skipping for now - ['fmrelease'], - ['fmvelocity'], + /** + * Sets the Frequency Modulation Harmonicity Ratio. + * Controls the timbre of the sound. + * Whole numbers and simple ratios sound more natural, + * while decimal numbers and complex ratios sound metallic. + * + * @name fmh + * @param {number | Pattern} harmonicity + * @example + * note("c e g b") + * .fm(4) + * .fmh("<1 2 1.5 1.61>") + * .scope() + * + */ + [['fmh', 'fmi'], 'fmh'], + /** + * Sets the Frequency Modulation of the synth. + * Controls the modulation index, which defines the brightness of the sound. + * + * @name fm + * @param {number | Pattern} brightness modulation index + * @synonyms fmi + * @example + * note("c e g b") + * .fm("<0 1 2 8 32>") + * .scope() + * + */ + [['fmi', 'fmh'], 'fm'], + // fm envelope + /** + * Ramp type of fm envelope. Exp might be a bit broken.. + * + * @name fmenv + * @param {number | Pattern} type lin | exp + * @example + * note("c e g b") + * .fm(4) + * .fmdecay(.2) + * .fmsustain(0) + * .fmenv("") + * .scope() + * + */ + ['fmenv'], + /** + * Attack time for the FM envelope: time it takes to reach maximum modulation + * + * @name fmattack + * @param {number | Pattern} time attack time + * @example + * note("c e g b") + * .fm(4) + * .fmattack("<0 .05 .1 .2>") + * .scope() + * + */ + ['fmattack'], + /** + * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase. + * + * @name fmdecay + * @param {number | Pattern} time decay time + * @example + * note("c e g b") + * .fm(4) + * .fmdecay("<.01 .05 .1 .2>") + * .fmsustain(.4) + * .scope() + * + */ + ['fmdecay'], + /** + * Sustain level for the FM envelope: how much modulation is applied after the decay phase + * + * @name fmsustain + * @param {number | Pattern} level sustain level + * @example + * note("c e g b") + * .fm(4) + * .fmdecay(.1) + * .fmsustain("<1 .75 .5 0>") + * .scope() + * + */ + ['fmsustain'], + // these are not really useful... skipping for now + ['fmrelease'], + ['fmvelocity'], - /** - * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`. - * - * @name bank - * @param {string | Pattern} bank the name of the bank - * @example - * s("bd sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd") - * - */ - ['bank'], + /** + * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`. + * + * @name bank + * @param {string | Pattern} bank the name of the bank + * @example + * s("bd sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd") + * + */ + ['bank'], - ['analyze'], // analyser node send amount 0 - 1 (used by scope) - ['fft'], // fftSize of analyser + ['analyze'], // analyser node send amount 0 - 1 (used by scope) + ['fft'], // fftSize of analyser - /** - * Amplitude envelope decay time: the time it takes after the attack time to reach the sustain level. - * Note that the decay is only audible if the sustain value is lower than 1. - * - * @name decay - * @param {number | Pattern} time decay time in seconds - * @example - * note("c3 e3").decay("<.1 .2 .3 .4>").sustain(0) - * - */ - ['decay'], - /** - * Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset. - * - * @name sustain - * @param {number | Pattern} gain sustain level between 0 and 1 - * @synonyms sus - * @example - * note("c3 e3").decay(.2).sustain("<0 .1 .4 .6 1>") - * - */ - ['sustain', 'sus'], - /** - * Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero. - * - * @name release - * @param {number | Pattern} time release time in seconds - * @synonyms rel - * @example - * note("c3 e3 g3 c4").release("<0 .1 .4 .6 1>/2") - * - */ - ['release', 'rel'], - ['hold'], - // TODO: in tidal, it seems to be normalized - /** - * Sets the center frequency of the **b**and-**p**ass **f**ilter. When using mininotation, you - * can also optionally supply the 'bpq' parameter separated by ':'. - * - * @name bpf - * @param {number | Pattern} frequency center frequency - * @synonyms bandf, bp - * @example - * s("bd sd,hh*3").bpf("<1000 2000 4000 8000>") - * - */ - [['bandf', 'bandq'], 'bpf', 'bp'], - // TODO: in tidal, it seems to be normalized - /** - * Sets the **b**and-**p**ass **q**-factor (resonance). - * - * @name bpq - * @param {number | Pattern} q q factor - * @synonyms bandq - * @example - * s("bd sd").bpf(500).bpq("<0 1 2 3>") - * - */ - // currently an alias of 'bandq' https://github.com/tidalcycles/strudel/issues/496 - // ['bpq'], - ['bandq', 'bpq'], - /** - * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. - * - * @memberof Pattern - * @name begin - * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample - * @example - * samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/Dirt-Samples/master/') - * s("rave").begin("<0 .25 .5 .75>") - * - */ - ['begin'], - /** - * The same as .begin, but cuts off the end off each sample. - * - * @memberof Pattern - * @name end - * @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc.. - * @example - * s("bd*2,oh*4").end("<.1 .2 .5 1>") - * - */ - ['end'], - /** - * Loops the sample. - * Note that the tempo of the loop is not synced with the cycle tempo. - * To change the loop region, use loopBegin / loopEnd. - * - * @name loop - * @param {number | Pattern} on If 1, the sample is looped - * @example - * s("casio").loop(1) - * - */ - ['loop'], - /** - * Begin to loop at a specific point in the sample (inbetween `begin` and `end`). - * Note that the loop point must be inbetween `begin` and `end`, and before `loopEnd`! - * Note: Samples starting with wt_ will automatically loop! (wt = wavetable) - * - * @name loopBegin - * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample - * @synonyms loopb - * @example - * s("space").loop(1) - * .loopBegin("<0 .125 .25>").scope() - */ - ['loopBegin', 'loopb'], - /** - * - * End the looping section at a specific point in the sample (inbetween `begin` and `end`). - * Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`! - * - * @name loopEnd - * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample - * @synonyms loope - * @example - * s("space").loop(1) - * .loopEnd("<1 .75 .5 .25>").scope() - */ - ['loopEnd', 'loope'], - /** - * bit crusher effect. - * - * @name crush - * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). - * @example - * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") - * - */ - // TODO: currently duplicated with "native" legato - // TODO: superdirt legato will do more: https://youtu.be/dQPmE1WaD1k?t=419 - /** - * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. - * - * @name legato - * @param {number | Pattern} duration between 0 and 1, where 1 is the length of the whole hap time - * @noAutocomplete - * @example - * "c4 eb4 g4 bb4".legato("<0.125 .25 .5 .75 1 2 4>") - * - */ - // ['legato'], - // ['clhatdecay'], - ['crush'], - /** - * fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers - * - * @name coarse - * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. - * @example - * s("bd sd,hh*4").coarse("<1 4 8 16 32>") - * - */ - ['coarse'], - /** - * choose the channel the pattern is sent to in superdirt - * - * @name channel - * @param {number | Pattern} channel channel number - * - */ - ['channel'], - /** - * In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open. - * - * @name cut - * @param {number | Pattern} group cut group number - * @example - * s("rd*4").cut(1) - * - */ - ['cut'], - /** - * Applies the cutoff frequency of the **l**ow-**p**ass **f**ilter. - * - * When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'. - * - * @name lpf - * @param {number | Pattern} frequency audible between 0 and 20000 - * @synonyms cutoff, ctf, lp - * @example - * s("bd sd,hh*3").lpf("<4000 2000 1000 500 200 100>") - * @example - * s("bd*8").lpf("1000:0 1000:10 1000:20 1000:30") - * - */ - [['cutoff', 'resonance'], 'ctf', 'lpf', 'lp'], + /** + * Amplitude envelope decay time: the time it takes after the attack time to reach the sustain level. + * Note that the decay is only audible if the sustain value is lower than 1. + * + * @name decay + * @param {number | Pattern} time decay time in seconds + * @example + * note("c3 e3").decay("<.1 .2 .3 .4>").sustain(0) + * + */ + ['decay'], + /** + * Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset. + * + * @name sustain + * @param {number | Pattern} gain sustain level between 0 and 1 + * @synonyms sus + * @example + * note("c3 e3").decay(.2).sustain("<0 .1 .4 .6 1>") + * + */ + ['sustain', 'sus'], + /** + * Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero. + * + * @name release + * @param {number | Pattern} time release time in seconds + * @synonyms rel + * @example + * note("c3 e3 g3 c4").release("<0 .1 .4 .6 1>/2") + * + */ + ['release', 'rel'], + ['hold'], + // TODO: in tidal, it seems to be normalized + /** + * Sets the center frequency of the **b**and-**p**ass **f**ilter. When using mininotation, you + * can also optionally supply the 'bpq' parameter separated by ':'. + * + * @name bpf + * @param {number | Pattern} frequency center frequency + * @synonyms bandf, bp + * @example + * s("bd sd,hh*3").bpf("<1000 2000 4000 8000>") + * + */ + [['bandf', 'bandq'], 'bpf', 'bp'], + // TODO: in tidal, it seems to be normalized + /** + * Sets the **b**and-**p**ass **q**-factor (resonance). + * + * @name bpq + * @param {number | Pattern} q q factor + * @synonyms bandq + * @example + * s("bd sd").bpf(500).bpq("<0 1 2 3>") + * + */ + // currently an alias of 'bandq' https://github.com/tidalcycles/strudel/issues/496 + // ['bpq'], + ['bandq', 'bpq'], + /** + * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. + * + * @memberof Pattern + * @name begin + * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample + * @example + * samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/Dirt-Samples/master/') + * s("rave").begin("<0 .25 .5 .75>") + * + */ + ['begin'], + /** + * The same as .begin, but cuts off the end off each sample. + * + * @memberof Pattern + * @name end + * @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc.. + * @example + * s("bd*2,oh*4").end("<.1 .2 .5 1>") + * + */ + ['end'], + /** + * Loops the sample. + * Note that the tempo of the loop is not synced with the cycle tempo. + * To change the loop region, use loopBegin / loopEnd. + * + * @name loop + * @param {number | Pattern} on If 1, the sample is looped + * @example + * s("casio").loop(1) + * + */ + ['loop'], + /** + * Begin to loop at a specific point in the sample (inbetween `begin` and `end`). + * Note that the loop point must be inbetween `begin` and `end`, and before `loopEnd`! + * Note: Samples starting with wt_ will automatically loop! (wt = wavetable) + * + * @name loopBegin + * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample + * @synonyms loopb + * @example + * s("space").loop(1) + * .loopBegin("<0 .125 .25>").scope() + */ + ['loopBegin', 'loopb'], + /** + * + * End the looping section at a specific point in the sample (inbetween `begin` and `end`). + * Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`! + * + * @name loopEnd + * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample + * @synonyms loope + * @example + * s("space").loop(1) + * .loopEnd("<1 .75 .5 .25>").scope() + */ + ['loopEnd', 'loope'], + /** + * bit crusher effect. + * + * @name crush + * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). + * @example + * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") + * + */ + // TODO: currently duplicated with "native" legato + // TODO: superdirt legato will do more: https://youtu.be/dQPmE1WaD1k?t=419 + /** + * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. + * + * @name legato + * @param {number | Pattern} duration between 0 and 1, where 1 is the length of the whole hap time + * @noAutocomplete + * @example + * "c4 eb4 g4 bb4".legato("<0.125 .25 .5 .75 1 2 4>") + * + */ + // ['legato'], + // ['clhatdecay'], + ['crush'], + /** + * fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers + * + * @name coarse + * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. + * @example + * s("bd sd,hh*4").coarse("<1 4 8 16 32>") + * + */ + ['coarse'], + /** + * choose the channel the pattern is sent to in superdirt + * + * @name channel + * @param {number | Pattern} channel channel number + * + */ + ['channel'], + /** + * In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open. + * + * @name cut + * @param {number | Pattern} group cut group number + * @example + * s("rd*4").cut(1) + * + */ + ['cut'], + /** + * Applies the cutoff frequency of the **l**ow-**p**ass **f**ilter. + * + * When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'. + * + * @name lpf + * @param {number | Pattern} frequency audible between 0 and 20000 + * @synonyms cutoff, ctf, lp + * @example + * s("bd sd,hh*3").lpf("<4000 2000 1000 500 200 100>") + * @example + * s("bd*8").lpf("1000:0 1000:10 1000:20 1000:30") + * + */ + [['cutoff', 'resonance'], 'ctf', 'lpf', 'lp'], - /** - * Sets the lowpass filter envelope modulation depth. - * @name lpenv - * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_ - * @synonyms lpe - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .lpa(.5) - * .lpenv("<4 2 1 0 -1 -2 -4>/4") - */ - ['lpenv', 'lpe'], - /** - * Sets the highpass filter envelope modulation depth. - * @name hpenv - * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_ - * @synonyms hpe - * @example - * note("") - * .sound('sawtooth') - * .hpf(500) - * .hpa(.5) - * .hpenv("<4 2 1 0 -1 -2 -4>/4") - */ - ['hpenv', 'hpe'], - /** - * Sets the bandpass filter envelope modulation depth. - * @name bpenv - * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_ - * @synonyms bpe - * @example - * note("") - * .sound('sawtooth') - * .bpf(500) - * .bpa(.5) - * .bpenv("<4 2 1 0 -1 -2 -4>/4") - */ - ['bpenv', 'bpe'], - /** - * Sets the attack duration for the lowpass filter envelope. - * @name lpattack - * @param {number | Pattern} attack time of the filter envelope - * @synonyms lpa - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .lpa("<.5 .25 .1 .01>/4") - * .lpenv(4) - */ - ['lpattack', 'lpa'], - /** - * Sets the attack duration for the highpass filter envelope. - * @name hpattack - * @param {number | Pattern} attack time of the highpass filter envelope - * @synonyms hpa - * @example - * note("") - * .sound('sawtooth') - * .hpf(500) - * .hpa("<.5 .25 .1 .01>/4") - * .hpenv(4) - */ - ['hpattack', 'hpa'], - /** - * Sets the attack duration for the bandpass filter envelope. - * @name bpattack - * @param {number | Pattern} attack time of the bandpass filter envelope - * @synonyms bpa - * @example - * note("") - * .sound('sawtooth') - * .bpf(500) - * .bpa("<.5 .25 .1 .01>/4") - * .bpenv(4) - */ - ['bpattack', 'bpa'], - /** - * Sets the decay duration for the lowpass filter envelope. - * @name lpdecay - * @param {number | Pattern} decay time of the filter envelope - * @synonyms lpd - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .lpd("<.5 .25 .1 0>/4") - * .lps(0.2) - * .lpenv(4) - */ - ['lpdecay', 'lpd'], - /** - * Sets the decay duration for the highpass filter envelope. - * @name hpdecay - * @param {number | Pattern} decay time of the highpass filter envelope - * @synonyms hpd - * @example - * note("") - * .sound('sawtooth') - * .hpf(500) - * .hpd("<.5 .25 .1 0>/4") - * .hps(0.2) - * .hpenv(4) - */ - ['hpdecay', 'hpd'], - /** - * Sets the decay duration for the bandpass filter envelope. - * @name bpdecay - * @param {number | Pattern} decay time of the bandpass filter envelope - * @synonyms bpd - * @example - * note("") - * .sound('sawtooth') - * .bpf(500) - * .bpd("<.5 .25 .1 0>/4") - * .bps(0.2) - * .bpenv(4) - */ - ['bpdecay', 'bpd'], - /** - * Sets the sustain amplitude for the lowpass filter envelope. - * @name lpsustain - * @param {number | Pattern} sustain amplitude of the lowpass filter envelope - * @synonyms lps - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .lpd(.5) - * .lps("<0 .25 .5 1>/4") - * .lpenv(4) - */ - ['lpsustain', 'lps'], - /** - * Sets the sustain amplitude for the highpass filter envelope. - * @name hpsustain - * @param {number | Pattern} sustain amplitude of the highpass filter envelope - * @synonyms hps - * @example - * note("") - * .sound('sawtooth') - * .hpf(500) - * .hpd(.5) - * .hps("<0 .25 .5 1>/4") - * .hpenv(4) - */ - ['hpsustain', 'hps'], - /** - * Sets the sustain amplitude for the bandpass filter envelope. - * @name bpsustain - * @param {number | Pattern} sustain amplitude of the bandpass filter envelope - * @synonyms bps - * @example - * note("") - * .sound('sawtooth') - * .bpf(500) - * .bpd(.5) - * .bps("<0 .25 .5 1>/4") - * .bpenv(4) - */ - ['bpsustain', 'bps'], - /** - * Sets the release time for the lowpass filter envelope. - * @name lprelease - * @param {number | Pattern} release time of the filter envelope - * @synonyms lpr - * @example - * note("") - * .sound('sawtooth') - * .clip(.5) - * .lpf(500) - * .lpenv(4) - * .lpr("<.5 .25 .1 0>/4") - * .release(.5) - */ - ['lprelease', 'lpr'], - /** - * Sets the release time for the highpass filter envelope. - * @name hprelease - * @param {number | Pattern} release time of the highpass filter envelope - * @synonyms hpr - * @example - * note("") - * .sound('sawtooth') - * .clip(.5) - * .hpf(500) - * .hpenv(4) - * .hpr("<.5 .25 .1 0>/4") - * .release(.5) - */ - ['hprelease', 'hpr'], - /** - * Sets the release time for the bandpass filter envelope. - * @name bprelease - * @param {number | Pattern} release time of the bandpass filter envelope - * @synonyms bpr - * @example - * note("") - * .sound('sawtooth') - * .clip(.5) - * .bpf(500) - * .bpenv(4) - * .bpr("<.5 .25 .1 0>/4") - * .release(.5) - */ - ['bprelease', 'bpr'], - /** - * Sets the filter type. The 24db filter is more aggressive. More types might be added in the future. - * @name ftype - * @param {number | Pattern} type 12db (default) or 24db - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .bpenv(4) - * .ftype("<12db 24db>") - */ - ['ftype'], - ['fanchor'], - /** - * Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter. - * - * When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'. - * - * @name hpf - * @param {number | Pattern} frequency audible between 0 and 20000 - * @synonyms hp, hcutoff - * @example - * s("bd sd,hh*4").hpf("<4000 2000 1000 500 200 100>") - * @example - * s("bd sd,hh*4").hpf("<2000 2000:25>") - * - */ - // currently an alias of 'hcutoff' https://github.com/tidalcycles/strudel/issues/496 - // ['hpf'], - /** - * Applies a vibrato to the frequency of the oscillator. - * - * @name vib - * @synonyms vibrato, v - * @param {number | Pattern} frequency of the vibrato in hertz - * @example - * note("a") - * .vib("<.5 1 2 4 8 16>") - * @example - * // change the modulation depth with ":" - * note("a") - * .vib("<.5 1 2 4 8 16>:12") - */ - [['vib', 'vibmod'], 'vibrato', 'v'], - /** - * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set - * - * @name vibmod - * @synonyms vmod - * @param {number | Pattern} depth of vibrato (in semitones) - * @example - * note("a").vib(4) - * .vibmod("<.25 .5 1 2 12>") - * @example - * // change the vibrato frequency with ":" - * note("a") - * .vibmod("<.25 .5 1 2 12>:8") - */ - [['vibmod', 'vib'], 'vmod'], - [['hcutoff', 'hresonance'], 'hpf', 'hp'], - /** - * Controls the **h**igh-**p**ass **q**-value. - * - * @name hpq - * @param {number | Pattern} q resonance factor between 0 and 50 - * @synonyms hresonance - * @example - * s("bd sd,hh*4").hpf(2000).hpq("<0 10 20 30>") - * - */ - ['hresonance', 'hpq'], - /** - * Controls the **l**ow-**p**ass **q**-value. - * - * @name lpq - * @param {number | Pattern} q resonance factor between 0 and 50 - * @synonyms resonance - * @example - * s("bd sd,hh*4").lpf(2000).lpq("<0 10 20 30>") - * - */ - // currently an alias of 'resonance' https://github.com/tidalcycles/strudel/issues/496 - ['resonance', 'lpq'], - /** - * DJ filter, below 0.5 is low pass filter, above is high pass filter. - * - * @name djf - * @param {number | Pattern} cutoff below 0.5 is low pass filter, above is high pass filter - * @example - * n("0 3 7 [10,24]").s('superzow').octave(3).djf("<.5 .25 .5 .75>").osc() - * - */ - ['djf'], - // ['cutoffegint'], - // TODO: does not seem to work - /** - * Sets the level of the delay signal. - * - * When using mininotation, you can also optionally add the 'delaytime' and 'delayfeedback' parameter, - * separated by ':'. - * - * - * @name delay - * @param {number | Pattern} level between 0 and 1 - * @example - * s("bd").delay("<0 .25 .5 1>") - * @example - * s("bd bd").delay("0.65:0.25:0.9 0.65:0.125:0.7") - * - */ - [['delay', 'delaytime', 'delayfeedback']], - /** - * Sets the level of the signal that is fed back into the delay. - * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it - * - * @name delayfeedback - * @param {number | Pattern} feedback between 0 and 1 - * @synonyms delayfb, dfb - * @example - * s("bd").delay(.25).delayfeedback("<.25 .5 .75 1>").slow(2) - * - */ - ['delayfeedback', 'delayfb', 'dfb'], - /** - * Sets the time of the delay effect. - * - * @name delaytime - * @param {number | Pattern} seconds between 0 and Infinity - * @synonyms delayt, dt - * @example - * s("bd").delay(.25).delaytime("<.125 .25 .5 1>").slow(2) - * - */ - ['delaytime', 'delayt', 'dt'], - /* // TODO: test - * Specifies whether delaytime is calculated relative to cps. - * - * @name lock - * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. - * @example - * s("sd").delay().lock(1).osc() - * - */ - ['lock'], - /** - * Set detune of oscillators. Works only with some synths, see tidal doc - * - * @name detune - * @param {number | Pattern} amount between 0 and 1 - * @synonyms det - * @superdirtOnly - * @example - * n("0 3 7").s('superzow').octave(3).detune("<0 .25 .5 1 2>").osc() - * - */ - ['detune', 'det'], - /** - * Set dryness of reverb. See {@link room} and {@link size} for more information about reverb. - * - * @name dry - * @param {number | Pattern} dry 0 = wet, 1 = dry - * @example - * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() - * @superdirtOnly - * - */ - ['dry'], - // TODO: does not seem to do anything - /* - * Used when using {@link begin}/{@link end} or {@link chop}/{@link striate} and friends, to change the fade out time of the 'grain' envelope. - * - * @name fadeTime - * @param {number | Pattern} time between 0 and 1 - * @example - * s("oh*4").end(.1).fadeTime("<0 .2 .4 .8>").osc() - * - */ - ['fadeTime', 'fadeOutTime'], - // TODO: see above - ['fadeInTime'], - /** - * Set frequency of sound. - * - * @name freq - * @param {number | Pattern} frequency in Hz. the audible range is between 20 and 20000 Hz - * @example - * freq("220 110 440 110").s("superzow").osc() - * @example - * freq("110".mul.out(".5 1.5 .6 [2 3]")).s("superzow").osc() - * - */ - ['freq'], - // TODO: https://tidalcycles.org/docs/configuration/MIDIOSC/control-voltage/#gate - ['gate', 'gat'], - // ['hatgrain'], - // ['lagogo'], - // ['lclap'], - // ['lclaves'], - // ['lclhat'], - // ['lcrash'], - // TODO: - // https://tidalcycles.org/docs/reference/audio_effects/#leslie-1 - // https://tidalcycles.org/docs/reference/audio_effects/#leslie - /** - * Emulation of a Leslie speaker: speakers rotating in a wooden amplified cabinet. - * - * @name leslie - * @param {number | Pattern} wet between 0 and 1 - * @example - * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() - * @superdirtOnly - * - */ - ['leslie'], - /** - * Rate of modulation / rotation for leslie effect - * - * @name lrate - * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow - * @example - * n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() - * @superdirtOnly - * - */ - // TODO: the rate seems to "lag" (in the example, 1 will be fast) - ['lrate'], - /** - * Physical size of the cabinet in meters. Be careful, it might be slightly larger than your computer. Affects the Doppler amount (pitch warble) - * - * @name lsize - * @param {number | Pattern} meters somewhere between 0 and 1 - * @example - * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() - * @superdirtOnly - * - */ - ['lsize'], - // label for pianoroll - ['activeLabel'], - [['label', 'activeLabel']], - // ['lfo'], - // ['lfocutoffint'], - // ['lfodelay'], - // ['lfoint'], - // ['lfopitchint'], - // ['lfoshape'], - // ['lfosync'], - // ['lhitom'], - // ['lkick'], - // ['llotom'], - // ['lophat'], - // ['lsnare'], - ['degree'], // TODO: what is this? not found in tidal doc - ['mtranspose'], // TODO: what is this? not found in tidal doc - ['ctranspose'], // TODO: what is this? not found in tidal doc - ['harmonic'], // TODO: what is this? not found in tidal doc - ['stepsPerOctave'], // TODO: what is this? not found in tidal doc - ['octaveR'], // TODO: what is this? not found in tidal doc - // TODO: why is this needed? what's the difference to late / early? Answer: it's in seconds, and delays the message at - // OSC time (so can't be negative, at least not beyond the latency value) - ['nudge'], - // TODO: the following doc is just a guess, it's not documented in tidal doc. - /** - * Sets the default octave of a synth. - * - * @name octave - * @param {number | Pattern} octave octave number - * @example - * n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc() - * @superDirtOnly - */ - ['octave'], + /** + * Sets the lowpass filter envelope modulation depth. + * @name lpenv + * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_ + * @synonyms lpe + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpa(.5) + * .lpenv("<4 2 1 0 -1 -2 -4>/4") + */ + ['lpenv', 'lpe'], + /** + * Sets the highpass filter envelope modulation depth. + * @name hpenv + * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_ + * @synonyms hpe + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpa(.5) + * .hpenv("<4 2 1 0 -1 -2 -4>/4") + */ + ['hpenv', 'hpe'], + /** + * Sets the bandpass filter envelope modulation depth. + * @name bpenv + * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_ + * @synonyms bpe + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpa(.5) + * .bpenv("<4 2 1 0 -1 -2 -4>/4") + */ + ['bpenv', 'bpe'], + /** + * Sets the attack duration for the lowpass filter envelope. + * @name lpattack + * @param {number | Pattern} attack time of the filter envelope + * @synonyms lpa + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpa("<.5 .25 .1 .01>/4") + * .lpenv(4) + */ + ['lpattack', 'lpa'], + /** + * Sets the attack duration for the highpass filter envelope. + * @name hpattack + * @param {number | Pattern} attack time of the highpass filter envelope + * @synonyms hpa + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpa("<.5 .25 .1 .01>/4") + * .hpenv(4) + */ + ['hpattack', 'hpa'], + /** + * Sets the attack duration for the bandpass filter envelope. + * @name bpattack + * @param {number | Pattern} attack time of the bandpass filter envelope + * @synonyms bpa + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpa("<.5 .25 .1 .01>/4") + * .bpenv(4) + */ + ['bpattack', 'bpa'], + /** + * Sets the decay duration for the lowpass filter envelope. + * @name lpdecay + * @param {number | Pattern} decay time of the filter envelope + * @synonyms lpd + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpd("<.5 .25 .1 0>/4") + * .lps(0.2) + * .lpenv(4) + */ + ['lpdecay', 'lpd'], + /** + * Sets the decay duration for the highpass filter envelope. + * @name hpdecay + * @param {number | Pattern} decay time of the highpass filter envelope + * @synonyms hpd + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpd("<.5 .25 .1 0>/4") + * .hps(0.2) + * .hpenv(4) + */ + ['hpdecay', 'hpd'], + /** + * Sets the decay duration for the bandpass filter envelope. + * @name bpdecay + * @param {number | Pattern} decay time of the bandpass filter envelope + * @synonyms bpd + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpd("<.5 .25 .1 0>/4") + * .bps(0.2) + * .bpenv(4) + */ + ['bpdecay', 'bpd'], + /** + * Sets the sustain amplitude for the lowpass filter envelope. + * @name lpsustain + * @param {number | Pattern} sustain amplitude of the lowpass filter envelope + * @synonyms lps + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpd(.5) + * .lps("<0 .25 .5 1>/4") + * .lpenv(4) + */ + ['lpsustain', 'lps'], + /** + * Sets the sustain amplitude for the highpass filter envelope. + * @name hpsustain + * @param {number | Pattern} sustain amplitude of the highpass filter envelope + * @synonyms hps + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpd(.5) + * .hps("<0 .25 .5 1>/4") + * .hpenv(4) + */ + ['hpsustain', 'hps'], + /** + * Sets the sustain amplitude for the bandpass filter envelope. + * @name bpsustain + * @param {number | Pattern} sustain amplitude of the bandpass filter envelope + * @synonyms bps + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpd(.5) + * .bps("<0 .25 .5 1>/4") + * .bpenv(4) + */ + ['bpsustain', 'bps'], + /** + * Sets the release time for the lowpass filter envelope. + * @name lprelease + * @param {number | Pattern} release time of the filter envelope + * @synonyms lpr + * @example + * note("") + * .sound('sawtooth') + * .clip(.5) + * .lpf(500) + * .lpenv(4) + * .lpr("<.5 .25 .1 0>/4") + * .release(.5) + */ + ['lprelease', 'lpr'], + /** + * Sets the release time for the highpass filter envelope. + * @name hprelease + * @param {number | Pattern} release time of the highpass filter envelope + * @synonyms hpr + * @example + * note("") + * .sound('sawtooth') + * .clip(.5) + * .hpf(500) + * .hpenv(4) + * .hpr("<.5 .25 .1 0>/4") + * .release(.5) + */ + ['hprelease', 'hpr'], + /** + * Sets the release time for the bandpass filter envelope. + * @name bprelease + * @param {number | Pattern} release time of the bandpass filter envelope + * @synonyms bpr + * @example + * note("") + * .sound('sawtooth') + * .clip(.5) + * .bpf(500) + * .bpenv(4) + * .bpr("<.5 .25 .1 0>/4") + * .release(.5) + */ + ['bprelease', 'bpr'], + /** + * Sets the filter type. The 24db filter is more aggressive. More types might be added in the future. + * @name ftype + * @param {number | Pattern} type 12db (default) or 24db + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .bpenv(4) + * .ftype("<12db 24db>") + */ + ['ftype'], + ['fanchor'], + /** + * Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter. + * + * When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'. + * + * @name hpf + * @param {number | Pattern} frequency audible between 0 and 20000 + * @synonyms hp, hcutoff + * @example + * s("bd sd,hh*4").hpf("<4000 2000 1000 500 200 100>") + * @example + * s("bd sd,hh*4").hpf("<2000 2000:25>") + * + */ + // currently an alias of 'hcutoff' https://github.com/tidalcycles/strudel/issues/496 + // ['hpf'], + /** + * Applies a vibrato to the frequency of the oscillator. + * + * @name vib + * @synonyms vibrato, v + * @param {number | Pattern} frequency of the vibrato in hertz + * @example + * note("a") + * .vib("<.5 1 2 4 8 16>") + * @example + * // change the modulation depth with ":" + * note("a") + * .vib("<.5 1 2 4 8 16>:12") + */ + [['vib', 'vibmod'], 'vibrato', 'v'], + /** + * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set + * + * @name vibmod + * @synonyms vmod + * @param {number | Pattern} depth of vibrato (in semitones) + * @example + * note("a").vib(4) + * .vibmod("<.25 .5 1 2 12>") + * @example + * // change the vibrato frequency with ":" + * note("a") + * .vibmod("<.25 .5 1 2 12>:8") + */ + [['vibmod', 'vib'], 'vmod'], + [['hcutoff', 'hresonance'], 'hpf', 'hp'], + /** + * Controls the **h**igh-**p**ass **q**-value. + * + * @name hpq + * @param {number | Pattern} q resonance factor between 0 and 50 + * @synonyms hresonance + * @example + * s("bd sd,hh*4").hpf(2000).hpq("<0 10 20 30>") + * + */ + ['hresonance', 'hpq'], + /** + * Controls the **l**ow-**p**ass **q**-value. + * + * @name lpq + * @param {number | Pattern} q resonance factor between 0 and 50 + * @synonyms resonance + * @example + * s("bd sd,hh*4").lpf(2000).lpq("<0 10 20 30>") + * + */ + // currently an alias of 'resonance' https://github.com/tidalcycles/strudel/issues/496 + ['resonance', 'lpq'], + /** + * DJ filter, below 0.5 is low pass filter, above is high pass filter. + * + * @name djf + * @param {number | Pattern} cutoff below 0.5 is low pass filter, above is high pass filter + * @example + * n("0 3 7 [10,24]").s('superzow').octave(3).djf("<.5 .25 .5 .75>").osc() + * + */ + ['djf'], + // ['cutoffegint'], + // TODO: does not seem to work + /** + * Sets the level of the delay signal. + * + * When using mininotation, you can also optionally add the 'delaytime' and 'delayfeedback' parameter, + * separated by ':'. + * + * + * @name delay + * @param {number | Pattern} level between 0 and 1 + * @example + * s("bd").delay("<0 .25 .5 1>") + * @example + * s("bd bd").delay("0.65:0.25:0.9 0.65:0.125:0.7") + * + */ + [['delay', 'delaytime', 'delayfeedback']], + /** + * Sets the level of the signal that is fed back into the delay. + * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it + * + * @name delayfeedback + * @param {number | Pattern} feedback between 0 and 1 + * @synonyms delayfb, dfb + * @example + * s("bd").delay(.25).delayfeedback("<.25 .5 .75 1>").slow(2) + * + */ + ['delayfeedback', 'delayfb', 'dfb'], + /** + * Sets the time of the delay effect. + * + * @name delaytime + * @param {number | Pattern} seconds between 0 and Infinity + * @synonyms delayt, dt + * @example + * s("bd").delay(.25).delaytime("<.125 .25 .5 1>").slow(2) + * + */ + ['delaytime', 'delayt', 'dt'], + /* // TODO: test + * Specifies whether delaytime is calculated relative to cps. + * + * @name lock + * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. + * @example + * s("sd").delay().lock(1).osc() + * + */ + ['lock'], + /** + * Set detune of oscillators. Works only with some synths, see tidal doc + * + * @name detune + * @param {number | Pattern} amount between 0 and 1 + * @synonyms det + * @superdirtOnly + * @example + * n("0 3 7").s('superzow').octave(3).detune("<0 .25 .5 1 2>").osc() + * + */ + ['detune', 'det'], + /** + * Set dryness of reverb. See {@link room} and {@link size} for more information about reverb. + * + * @name dry + * @param {number | Pattern} dry 0 = wet, 1 = dry + * @example + * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() + * @superdirtOnly + * + */ + ['dry'], + // TODO: does not seem to do anything + /* + * Used when using {@link begin}/{@link end} or {@link chop}/{@link striate} and friends, to change the fade out time of the 'grain' envelope. + * + * @name fadeTime + * @param {number | Pattern} time between 0 and 1 + * @example + * s("oh*4").end(.1).fadeTime("<0 .2 .4 .8>").osc() + * + */ + ['fadeTime', 'fadeOutTime'], + // TODO: see above + ['fadeInTime'], + /** + * Set frequency of sound. + * + * @name freq + * @param {number | Pattern} frequency in Hz. the audible range is between 20 and 20000 Hz + * @example + * freq("220 110 440 110").s("superzow").osc() + * @example + * freq("110".mul.out(".5 1.5 .6 [2 3]")).s("superzow").osc() + * + */ + ['freq'], + // TODO: https://tidalcycles.org/docs/configuration/MIDIOSC/control-voltage/#gate + ['gate', 'gat'], + // ['hatgrain'], + // ['lagogo'], + // ['lclap'], + // ['lclaves'], + // ['lclhat'], + // ['lcrash'], + // TODO: + // https://tidalcycles.org/docs/reference/audio_effects/#leslie-1 + // https://tidalcycles.org/docs/reference/audio_effects/#leslie + /** + * Emulation of a Leslie speaker: speakers rotating in a wooden amplified cabinet. + * + * @name leslie + * @param {number | Pattern} wet between 0 and 1 + * @example + * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() + * @superdirtOnly + * + */ + ['leslie'], + /** + * Rate of modulation / rotation for leslie effect + * + * @name lrate + * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow + * @example + * n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() + * @superdirtOnly + * + */ + // TODO: the rate seems to "lag" (in the example, 1 will be fast) + ['lrate'], + /** + * Physical size of the cabinet in meters. Be careful, it might be slightly larger than your computer. Affects the Doppler amount (pitch warble) + * + * @name lsize + * @param {number | Pattern} meters somewhere between 0 and 1 + * @example + * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() + * @superdirtOnly + * + */ + ['lsize'], + // label for pianoroll + ['activeLabel'], + [['label', 'activeLabel']], + // ['lfo'], + // ['lfocutoffint'], + // ['lfodelay'], + // ['lfoint'], + // ['lfopitchint'], + // ['lfoshape'], + // ['lfosync'], + // ['lhitom'], + // ['lkick'], + // ['llotom'], + // ['lophat'], + // ['lsnare'], + ['degree'], // TODO: what is this? not found in tidal doc + ['mtranspose'], // TODO: what is this? not found in tidal doc + ['ctranspose'], // TODO: what is this? not found in tidal doc + ['harmonic'], // TODO: what is this? not found in tidal doc + ['stepsPerOctave'], // TODO: what is this? not found in tidal doc + ['octaveR'], // TODO: what is this? not found in tidal doc + // TODO: why is this needed? what's the difference to late / early? Answer: it's in seconds, and delays the message at + // OSC time (so can't be negative, at least not beyond the latency value) + ['nudge'], + // TODO: the following doc is just a guess, it's not documented in tidal doc. + /** + * Sets the default octave of a synth. + * + * @name octave + * @param {number | Pattern} octave octave number + * @example + * n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc() + * @superDirtOnly + */ + ['octave'], - // ['ophatdecay'], - // TODO: example - /** - * An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share the same global effects. - * - * @name orbit - * @param {number | Pattern} number - * @example - * stack( - * s("hh*3").delay(.5).delaytime(.25).orbit(1), - * s("~ sd").delay(.5).delaytime(.125).orbit(2) - * ) - */ - ['orbit'], - ['overgain'], // TODO: what is this? not found in tidal doc Answer: gain is limited to maximum of 2. This allows you to go over that - ['overshape'], // TODO: what is this? not found in tidal doc. Similar to above, but limited to 1 - /** - * Sets position in stereo. - * - * @name pan - * @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel) - * @example - * s("[bd hh]*2").pan("<.5 1 .5 0>") - * - */ - ['pan'], - // TODO: this has no effect (see example) - /* - * Controls how much multichannel output is fanned out - * - * @name panspan - * @param {number | Pattern} span between -inf and inf, negative is backwards ordering - * @example - * s("[bd hh]*2").pan("<.5 1 .5 0>").panspan("<0 .5 1>").osc() - * - */ - ['panspan'], - // TODO: this has no effect (see example) - /* - * Controls how much multichannel output is spread - * - * @name pansplay - * @param {number | Pattern} spread between 0 and 1 - * @example - * s("[bd hh]*2").pan("<.5 1 .5 0>").pansplay("<0 .5 1>").osc() - * - */ - ['pansplay'], - ['panwidth'], - ['panorient'], - // ['pitch1'], - // ['pitch2'], - // ['pitch3'], - // ['portamento'], - // TODO: LFO rate see https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare - ['rate'], - // TODO: slide param for certain synths - ['slide'], - // TODO: detune? https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare - ['semitone'], - // TODO: dedup with synth param, see https://tidalcycles.org/docs/reference/synthesizers/#superpiano - // ['velocity'], - ['voice'], // TODO: synth param + // ['ophatdecay'], + // TODO: example + /** + * An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share the same global effects. + * + * @name orbit + * @param {number | Pattern} number + * @example + * stack( + * s("hh*3").delay(.5).delaytime(.25).orbit(1), + * s("~ sd").delay(.5).delaytime(.125).orbit(2) + * ) + */ + ['orbit'], + ['overgain'], // TODO: what is this? not found in tidal doc Answer: gain is limited to maximum of 2. This allows you to go over that + ['overshape'], // TODO: what is this? not found in tidal doc. Similar to above, but limited to 1 + /** + * Sets position in stereo. + * + * @name pan + * @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel) + * @example + * s("[bd hh]*2").pan("<.5 1 .5 0>") + * + */ + ['pan'], + // TODO: this has no effect (see example) + /* + * Controls how much multichannel output is fanned out + * + * @name panspan + * @param {number | Pattern} span between -inf and inf, negative is backwards ordering + * @example + * s("[bd hh]*2").pan("<.5 1 .5 0>").panspan("<0 .5 1>").osc() + * + */ + ['panspan'], + // TODO: this has no effect (see example) + /* + * Controls how much multichannel output is spread + * + * @name pansplay + * @param {number | Pattern} spread between 0 and 1 + * @example + * s("[bd hh]*2").pan("<.5 1 .5 0>").pansplay("<0 .5 1>").osc() + * + */ + ['pansplay'], + ['panwidth'], + ['panorient'], + // ['pitch1'], + // ['pitch2'], + // ['pitch3'], + // ['portamento'], + // TODO: LFO rate see https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare + ['rate'], + // TODO: slide param for certain synths + ['slide'], + // TODO: detune? https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare + ['semitone'], + // TODO: dedup with synth param, see https://tidalcycles.org/docs/reference/synthesizers/#superpiano + // ['velocity'], + ['voice'], // TODO: synth param - // voicings // https://github.com/tidalcycles/strudel/issues/506 - ['chord'], // chord to voice, like C Eb Fm7 G7. the symbols can be defined via addVoicings - ['dictionary', 'dict'], // which dictionary to use for the voicings - ['anchor'], // the top note to align the voicing to, defaults to c5 - ['offset'], // how the voicing is offset from the anchored position - ['octaves'], // how many octaves are voicing steps spread apart, defaults to 1 - [['mode', 'anchor']], // below = anchor note will be removed from the voicing, useful for melody harmonization + // voicings // https://github.com/tidalcycles/strudel/issues/506 + ['chord'], // chord to voice, like C Eb Fm7 G7. the symbols can be defined via addVoicings + ['dictionary', 'dict'], // which dictionary to use for the voicings + ['anchor'], // the top note to align the voicing to, defaults to c5 + ['offset'], // how the voicing is offset from the anchored position + ['octaves'], // how many octaves are voicing steps spread apart, defaults to 1 + [['mode', 'anchor']], // below = anchor note will be removed from the voicing, useful for melody harmonization - /** - * Sets the level of reverb. - * - * When using mininotation, you can also optionally add the 'size' parameter, separated by ':'. - * - * @name room - * @param {number | Pattern} level between 0 and 1 - * @example - * s("bd sd").room("<0 .2 .4 .6 .8 1>") - * @example - * s("bd sd").room("<0.9:1 0.9:4>") - * - */ - [['room', 'size']], - /** - * Sets the room size of the reverb, see {@link room}. - * - * @name roomsize - * @param {number | Pattern} size between 0 and 10 - * @synonyms size, sz - * @example - * s("bd sd").room(.8).roomsize("<0 1 2 4 8>") - * - */ - // TODO: find out why : - // s("bd sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc() - // .. does not work. Is it because room is only one effect? - ['size', 'sz', 'roomsize'], - // ['sagogo'], - // ['sclap'], - // ['sclaves'], - // ['scrash'], + /** + * Sets the level of reverb. + * + * When using mininotation, you can also optionally add the 'size' parameter, separated by ':'. + * + * @name room + * @param {number | Pattern} level between 0 and 1 + * @example + * s("bd sd").room("<0 .2 .4 .6 .8 1>") + * @example + * s("bd sd").room("<0.9:1 0.9:4>") + * + */ + [['room', 'size']], + /** + * Sets the room size of the reverb, see {@link room}. + * + * @name roomsize + * @param {number | Pattern} size between 0 and 10 + * @synonyms size, sz + * @example + * s("bd sd").room(.8).roomsize("<0 1 2 4 8>") + * + */ - /** - * Sets the sample to use as an impulse response for the reverb. - * - * @name iresponse - * @param {string | Pattern} Sets the impulse response - * @example - * s("bd sd").room(.8).ir("") - * - */ - [['ir', 'i'], 'iresponse'], - /** - * Wave shaping distortion. CAUTION: it might get loud - * - * @name shape - * @param {number | Pattern} distortion between 0 and 1 - * @example - * s("bd sd,hh*4").shape("<0 .2 .4 .6 .8>") - * - */ - ['shape'], - /** - * Changes the speed of sample playback, i.e. a cheap way of changing pitch. - * - * @name speed - * @param {number | Pattern} speed -inf to inf, negative numbers play the sample backwards. - * @example - * s("bd").speed("<1 2 4 1 -2 -4>") - * @example - * speed("1 1.5*2 [2 1.1]").s("piano").clip(1) - * - */ - ['speed'], - /** - * Used in conjunction with {@link speed}, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`. - * - * @name unit - * @param {number | string | Pattern} unit see description above - * @example - * speed("1 2 .5 3").s("bd").unit("c").osc() - * @superdirtOnly - * - */ - ['unit'], - /** - * Made by Calum Gunn. Reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter. The SuperCollider manual defines Squiz as: - * - * "A simplistic pitch-raising algorithm. It's not meant to sound natural; its sound is reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter, depending on the input. The algorithm works by cutting the signal into fragments (delimited by upwards-going zero-crossings) and squeezing those fragments in the time domain (i.e. simply playing them back faster than they came in), leaving silences inbetween. All the parameters apart from memlen can be modulated." - * - * @name squiz - * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc. - * @example - * squiz("2 4/2 6 [8 16]").s("bd").osc() - * @superdirtOnly - * - */ - ['squiz'], - // ['stutterdepth'], // TODO: what is this? not found in tidal doc - // ['stuttertime'], // TODO: what is this? not found in tidal doc - // ['timescale'], // TODO: what is this? not found in tidal doc - // ['timescalewin'], // TODO: what is this? not found in tidal doc - // ['tomdecay'], - // ['vcfegint'], - // ['vcoegint'], - // TODO: Use a rest (~) to override the effect <- vowel - /** - * - * Formant filter to make things sound like vowels. - * - * @name vowel - * @param {string | Pattern} vowel You can use a e i o u. - * @example - * note("c2 >").s('sawtooth') - * .vowel(">") - * - */ - ['vowel'], - /* // TODO: find out how it works - * Made by Calum Gunn. Divides an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discards a fraction of them. Takes a number between 1 and 100, denoted the percentage of segments to drop. The SuperCollider manual describes the Waveloss effect this way: - * - * Divide an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discard a fraction of them (i.e. replace them with silence of the same length). The technique was described by Trevor Wishart in a lecture. Parameters: the filter drops drop out of out of chunks. mode can be 1 to drop chunks in a simple deterministic fashion (e.g. always dropping the first 30 out of a set of 40 segments), or 2 to drop chunks randomly but in an appropriate proportion.) - * - * mode: ? - * waveloss: ? - * - * @name waveloss - */ - ['waveloss'], - // TODO: midi effects? - ['dur'], - // ['modwheel'], - ['expression'], - ['sustainpedal'], - /* // TODO: doesn't seem to do anything - * - * Tremolo Audio DSP effect - * - * @name tremolodepth - * @param {number | Pattern} depth between 0 and 1 - * @example - * n("0,4,7").tremolodepth("<0 .3 .6 .9>").osc() - * - */ - ['tremolodepth', 'tremdp'], - ['tremolorate', 'tremr'], - // TODO: doesn't seem to do anything - ['phaserdepth', 'phasdp'], - ['phaserrate', 'phasr'], + // TODO: find out why : + // s("bd sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc() + // .. does not work. Is it because room is only one effect? + ['size', 'sz', 'roomsize'], + // ['sagogo'], + // ['sclap'], + // ['sclaves'], + // ['scrash'], - ['fshift'], - ['fshiftnote'], - ['fshiftphase'], + /** + * Sets the sample to use as an impulse response for the reverb. + * + * @name iresponse + * @param {string | Pattern} Sets the impulse response + * @example + * s("bd sd").room(.8).ir("") + * + */ + [['ir', 'i'], 'iresponse'], - ['triode'], - ['krush'], - ['kcutoff'], - ['octer'], - ['octersub'], - ['octersubsub'], - ['ring'], - ['ringf'], - ['ringdf'], - ['distort'], - ['freeze'], - ['xsdelay'], - ['tsdelay'], - ['real'], - ['imag'], - ['enhance'], - ['partials'], - ['comb'], - ['smear'], - ['scram'], - ['binshift'], - ['hbrick'], - ['lbrick'], - ['midichan'], - ['control'], - ['ccn'], - ['ccv'], - ['polyTouch'], - ['midibend'], - ['miditouch'], - ['ctlNum'], - ['frameRate'], - ['frames'], - ['hours'], - ['midicmd'], - ['minutes'], - ['progNum'], - ['seconds'], - ['songPtr'], - ['uid'], - ['val'], - ['cps'], - /** - * Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration. - * In tidal, this would be done with legato, [which has a complicated history in strudel](https://github.com/tidalcycles/strudel/issues/111). - * For now, if you're coming from tidal, just think clip = legato. - * - * @name clip - * @param {number | Pattern} factor >= 0 - * @example - * note("c a f e").s("piano").clip("<.5 1 2>") - * - */ - ['clip'], + /** + * Wave shaping distortion. CAUTION: it might get loud + * + * @name shape + * @param {number | Pattern} distortion between 0 and 1 + * @example + * s("bd sd,hh*4").shape("<0 .2 .4 .6 .8>") + * + */ + ['shape'], + /** + * Changes the speed of sample playback, i.e. a cheap way of changing pitch. + * + * @name speed + * @param {number | Pattern} speed -inf to inf, negative numbers play the sample backwards. + * @example + * s("bd").speed("<1 2 4 1 -2 -4>") + * @example + * speed("1 1.5*2 [2 1.1]").s("piano").clip(1) + * + */ + ['speed'], + /** + * Used in conjunction with {@link speed}, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`. + * + * @name unit + * @param {number | string | Pattern} unit see description above + * @example + * speed("1 2 .5 3").s("bd").unit("c").osc() + * @superdirtOnly + * + */ + ['unit'], + /** + * Made by Calum Gunn. Reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter. The SuperCollider manual defines Squiz as: + * + * "A simplistic pitch-raising algorithm. It's not meant to sound natural; its sound is reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter, depending on the input. The algorithm works by cutting the signal into fragments (delimited by upwards-going zero-crossings) and squeezing those fragments in the time domain (i.e. simply playing them back faster than they came in), leaving silences inbetween. All the parameters apart from memlen can be modulated." + * + * @name squiz + * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc. + * @example + * squiz("2 4/2 6 [8 16]").s("bd").osc() + * @superdirtOnly + * + */ + ['squiz'], + // ['stutterdepth'], // TODO: what is this? not found in tidal doc + // ['stuttertime'], // TODO: what is this? not found in tidal doc + // ['timescale'], // TODO: what is this? not found in tidal doc + // ['timescalewin'], // TODO: what is this? not found in tidal doc + // ['tomdecay'], + // ['vcfegint'], + // ['vcoegint'], + // TODO: Use a rest (~) to override the effect <- vowel + /** + * + * Formant filter to make things sound like vowels. + * + * @name vowel + * @param {string | Pattern} vowel You can use a e i o u. + * @example + * note("c2 >").s('sawtooth') + * .vowel(">") + * + */ + ['vowel'], + /* // TODO: find out how it works + * Made by Calum Gunn. Divides an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discards a fraction of them. Takes a number between 1 and 100, denoted the percentage of segments to drop. The SuperCollider manual describes the Waveloss effect this way: + * + * Divide an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discard a fraction of them (i.e. replace them with silence of the same length). The technique was described by Trevor Wishart in a lecture. Parameters: the filter drops drop out of out of chunks. mode can be 1 to drop chunks in a simple deterministic fashion (e.g. always dropping the first 30 out of a set of 40 segments), or 2 to drop chunks randomly but in an appropriate proportion.) + * + * mode: ? + * waveloss: ? + * + * @name waveloss + */ + ['waveloss'], + // TODO: midi effects? + ['dur'], + // ['modwheel'], + ['expression'], + ['sustainpedal'], + /* // TODO: doesn't seem to do anything + * + * Tremolo Audio DSP effect + * + * @name tremolodepth + * @param {number | Pattern} depth between 0 and 1 + * @example + * n("0,4,7").tremolodepth("<0 .3 .6 .9>").osc() + * + */ + ['tremolodepth', 'tremdp'], + ['tremolorate', 'tremr'], + // TODO: doesn't seem to do anything + ['phaserdepth', 'phasdp'], + ['phaserrate', 'phasr'], - // ZZFX - ['zrand'], - ['curve'], - ['slide'], // superdirt duplicate - ['deltaSlide'], - ['pitchJump'], - ['pitchJumpTime'], - ['lfo', 'repeatTime'], - ['noise'], - ['zmod'], - ['zcrush'], // like crush but scaled differently - ['zdelay'], - ['tremolo'], - ['zzfx'], + ['fshift'], + ['fshiftnote'], + ['fshiftphase'], + + ['triode'], + ['krush'], + ['kcutoff'], + ['octer'], + ['octersub'], + ['octersubsub'], + ['ring'], + ['ringf'], + ['ringdf'], + ['distort'], + ['freeze'], + ['xsdelay'], + ['tsdelay'], + ['real'], + ['imag'], + ['enhance'], + ['partials'], + ['comb'], + ['smear'], + ['scram'], + ['binshift'], + ['hbrick'], + ['lbrick'], + ['midichan'], + ['control'], + ['ccn'], + ['ccv'], + ['polyTouch'], + ['midibend'], + ['miditouch'], + ['ctlNum'], + ['frameRate'], + ['frames'], + ['hours'], + ['midicmd'], + ['minutes'], + ['progNum'], + ['seconds'], + ['songPtr'], + ['uid'], + ['val'], + ['cps'], + /** + * Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration. + * In tidal, this would be done with legato, [which has a complicated history in strudel](https://github.com/tidalcycles/strudel/issues/111). + * For now, if you're coming from tidal, just think clip = legato. + * + * @name clip + * @param {number | Pattern} factor >= 0 + * @example + * note("c a f e").s("piano").clip("<.5 1 2>") + * + */ + ['clip'], + + // ZZFX + ['zrand'], + ['curve'], + ['slide'], // superdirt duplicate + ['deltaSlide'], + ['pitchJump'], + ['pitchJumpTime'], + ['lfo', 'repeatTime'], + ['noise'], + ['zmod'], + ['zcrush'], // like crush but scaled differently + ['zdelay'], + ['tremolo'], + ['zzfx'], ]; // TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13 controls.createParam = function (names) { - const name = Array.isArray(names) ? names[0] : names; + const name = Array.isArray(names) ? names[0] : names; - var withVal; - if (Array.isArray(names)) { - withVal = (xs) => { - if (Array.isArray(xs)) { - const result = {}; - xs.forEach((x, i) => { - if (i < names.length) { - result[names[i]] = x; - } - }); - return result; - } else { - return {[name]: xs}; - } - }; - } else { - withVal = (x) => ({[name]: x}); - } + var withVal; + if (Array.isArray(names)) { + withVal = (xs) => { + if (Array.isArray(xs)) { + const result = {}; + xs.forEach((x, i) => { + if (i < names.length) { + result[names[i]] = x; + } + }); + return result; + } else { + return { [name]: xs }; + } + }; + } else { + withVal = (x) => ({ [name]: x }); + } - const func = (...pats) => sequence(...pats).withValue(withVal); + const func = (...pats) => sequence(...pats).withValue(withVal); - const setter = function (...pats) { - if (!pats.length) { - return this.fmap(withVal); - } - return this.set(func(...pats)); - }; - Pattern.prototype[name] = setter; - return func; + const setter = function (...pats) { + if (!pats.length) { + return this.fmap(withVal); + } + return this.set(func(...pats)); + }; + Pattern.prototype[name] = setter; + return func; }; generic_params.forEach(([names, ...aliases]) => { - const name = Array.isArray(names) ? names[0] : names; - controls[name] = controls.createParam(names); + const name = Array.isArray(names) ? names[0] : names; + controls[name] = controls.createParam(names); - aliases.forEach((alias) => { - controls[alias] = controls[name]; - Pattern.prototype[alias] = Pattern.prototype[name]; - }); + aliases.forEach((alias) => { + controls[alias] = controls[name]; + Pattern.prototype[alias] = Pattern.prototype[name]; + }); }); controls.createParams = (...names) => - names.reduce((acc, name) => Object.assign(acc, {[name]: controls.createParam(name)}), {}); + names.reduce((acc, name) => Object.assign(acc, { [name]: controls.createParam(name) }), {}); controls.adsr = register('adsr', (adsr, pat) => { - adsr = !Array.isArray(adsr) ? [adsr] : adsr; - const [attack, decay, sustain, release] = adsr; - return pat.set({attack, decay, sustain, release}); + adsr = !Array.isArray(adsr) ? [adsr] : adsr; + const [attack, decay, sustain, release] = adsr; + return pat.set({ attack, decay, sustain, release }); }); controls.ds = register('ds', (ds, pat) => { - ds = !Array.isArray(ds) ? [ds] : ds; - const [decay, sustain] = ds; - return pat.set({decay, sustain}); + ds = !Array.isArray(ds) ? [ds] : ds; + const [decay, sustain] = ds; + return pat.set({ decay, sustain }); }); export default controls; diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 727090852..e72af033b 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -7,22 +7,31 @@ if (typeof AudioContext !== 'undefined') { return impulse; }; + AudioContext.prototype.adjustLength = function (duration, buffer) { + const newLength = buffer.sampleRate * duration; + const newBuffer = this.createBuffer(buffer.numberOfChannels, buffer.length, buffer.sampleRate); + for (let channel = 0; channel < buffer.numberOfChannels; channel++) { + let oldData = buffer.getChannelData(channel); + let newData = newBuffer.getChannelData(channel); + + for (let i = 0; i < newLength; i++) { + newData[i] = oldData[i] || 0; + } + } + return newBuffer; + }; + AudioContext.prototype.createReverb = function (duration, buffer) { const convolver = this.createConvolver(); - convolver.setDuration = (d, i) => { - convolver.buffer = i !== undefined ? buffer : this.impulseResponse(d); - convolver.duration = d; + convolver.setDuration = (dur, imp) => { + convolver.buffer = imp ? this.adjustLength(dur, imp) : this.impulseResponse(dur); return convolver; }; - convolver.setIR = (i) => { - convolver.buffer = i; + convolver.setIR = (dur, imp) => { + convolver.buffer = imp ? this.adjustLength(dur, imp) : this.impulseResponse(dur); return convolver; }; - if (buffer !== undefined) { - convolver.setIR(buffer); - } else { - convolver.setDuration(duration); - } + convolver.setDuration(duration, buffer); return convolver; }; } diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 2b46ae3c3..b61c9568e 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -122,11 +122,11 @@ function getReverb(orbit, duration = 2, ir) { reverbs[orbit] = reverb; } if (reverbs[orbit].duration !== duration) { - reverbs[orbit] = reverbs[orbit].setDuration(duration); + reverbs[orbit] = reverbs[orbit].setDuration(duration, ir); reverbs[orbit].duration = duration; } if (reverbs[orbit].ir !== ir) { - reverbs[orbit] = reverbs[orbit].setIR(ir); + reverbs[orbit] = reverbs[orbit].setIR(duration, ir); reverbs[orbit].ir = ir; } return reverbs[orbit]; @@ -368,9 +368,14 @@ export const superdough = async (value, deadline, hapDuration) => { } // reverb let buffer; + let url; if (ir !== undefined) { let sample = getSound(ir); - let url = sample.data.samples[i % sample.data.samples.length]; + if (Array.isArray(sample)) { + url = sample.data.samples[i % sample.data.samples.length]; + } else if (typeof sample === 'object') { + url = Object.values(sample.data.samples)[i & Object.values(sample.data.samples).length]; + } buffer = await loadBuffer(url, ac, ir, 0); } let reverbSend;