tidalcycles / Tidal

Pattern language
http://tidalcycles.org/
GNU General Public License v3.0
2.27k stars 255 forks source link

Rationalise Sound.Tidal.Params #763

Closed yaxu closed 3 years ago

yaxu commented 3 years ago

In solving #762 we create an extra function for each parameter to handle on-the-fly control busses. Sound.Tidal.Params is already unwieldy, so maybe this is a good time to rationalise this.

We could do this with code generation e.g. at compile time with Template Haskell, or at with an external script e.g. with perl or python. In the latter case this could maybe done automatically as part of the build process, or manually.

Template Haskell is tempting, but I think doesn't work with ghcjs, so wouldn't work for estuary (is that right @dktr0 ?)

One thing to bear in mind is that from this release we now have a two-way protocol developing between tidal and superdirt. So it could also be that an editor could somehow request a list of the available parameters from superdirt, which it then uses to create all the functions.

Following this thought, webdirt and indeed classic dirt don't have the same parameters as superdirt, so maybe they should have a different set of params.. But it's nice for patterns to run on both platforms even if some of the effects don't work on one of them.

A problem is that a superdirt synth might have a lot of parameters that then fill up the namespace and overwrite functions.

In cases like superfm it has a lot of individual parameters, and it's better to hand craft a function that modifies multiple parameters at once.

Logically speaking, parameters should have a namespace of their own, Params.pan etc, but even a shortened P.pan would be a real pain to type. There is already the possibility to do pF "pan".

So maybe for now we keep things as they are, not sure!

yaxu commented 3 years ago

I'm starting work on this, there's a lot of old parameters left over from the old tidal-midi implementation that should probably be removed. What's a good way to get a list of superdirt parameters? With osc type would be ideal.

yaxu commented 3 years ago

Here's what it's looking like so far:

genericParams = [
  ("s", "toArg", "for internal sound routing"),
  ("f", "from", "for internal sound routing"),
  ("f", "to", "for internal sound routing"),
  ("f", "accelerate", "a pattern of numbers that speed up (or slow down) samples while they play."),
  ("f", "amp", "like @gain@, but linear."),
  ("f", "attack", "a pattern of numbers to specify the attack time (in seconds) of an envelope applied to each sample. Only takes effect if `release` is also specified."),
  ("f", "bandf", "a pattern of numbers from 0 to 1. Sets the center frequency of the band-pass filter."),
  ("f", "bandq", "a pattern of anumbers from 0 to 1. Sets the q-factor of the band-pass filter."),
  ("f", "begin", "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."),
  ("f", "legato", "controls the amount of overlap between two adjacent sounds"),
  ("f", "clhatdecay", ""),
  ("f", "crush", "bit crushing, a pattern of numbers from 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction)."),
  ("f", "coarse", "fake-resampling, a pattern of numbers for lowering the sample rate, i.e. 1 for original 2 for half, 3 for a third and so on."),
  ("i", "channel", "choose the channel the pattern is sent to in superdirt"),
  ("i", "cut", "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."),
  ("f", "cutoff", "a pattern of numbers from 0 to 1. Applies the cutoff frequency of the low-pass filter."),
  ("f", "cutoffegint", ""),
  ("f", "decay", ""),
  ("f", "delay", "a pattern of numbers from 0 to 1. Sets the level of the delay signal."),
  ("f", "delayfeedback", "a pattern of numbers from 0 to 1. Sets the amount of delay feedback."),
  ("f", "delaytime", "a pattern of numbers from 0 to 1. Sets the length of the delay."),
  ("f", "detune", ""),
  ("f", "djf", "DJ filter, below 0.5 is low pass filter, above is high pass filter."),
  ("f", "dry", "when set to `1` will disable all reverb for this pattern. See `room` and `size` for more information about reverb."),
  ("f", "end", "the same as `begin`, but cuts the end off samples, shortening them; e.g. `0.75` to cut off the last quarter of each sample."),
  ("f", "freq", ""),
  ("f", "gain", "a pattern of numbers that specify volume. Values less than 1 make the sound quieter. Values greater than 1 make the sound louder. For the linear equivalent, see @amp@."),
  ("f", "gate", ""),
  ("f", "hatgrain", ""),
  ("f", "hcutoff", "a pattern of numbers from 0 to 1. Applies the cutoff frequency of the high-pass filter. Also has alias @hpf@"),
  ("f", "hold", "a pattern of numbers to specify the hold time (in seconds) of an envelope applied to each sample. Only takes effect if `attack` and `release` are also specified."),
  ("f", "hresonance", "a pattern of numbers from 0 to 1. Applies the resonance of the high-pass filter. Has alias @hpq@"),
  ("f", "lagogo", ""),
  ("f", "lclap", ""),
  ("f", "lclaves", ""),
  ("f", "lclhat", ""),
  ("f", "lcrash", ""),
  ("f", "leslie", ""),
  ("f", "lrate", ""),
  ("f", "lsize", ""),
  ("f", "lfo", ""),
  ("f", "lfocutoffint", ""),
  ("f", "lfodelay", ""),
  ("f", "lfoint", ""),
  ("f", "lfopitchint", ""),
  ("f", "lfoshape", ""),
  ("f", "lfosync", ""),
  ("f", "lhitom", ""),
  ("f", "lkick", ""),
  ("f", "llotom", ""),
  ("f", "lock", "A pattern of numbers. Specifies whether delaytime is calculated relative to cps. When set to 1, delaytime is a direct multiple of a cycle."),
  ("f", "loop", "loops the sample (from `begin` to `end`) the specified number of times."),
  ("f", "lophat", ""),
  ("f", "lsnare", ""),
  ("note", "n", "The note or sample number to choose for a synth or sampleset"),
  ("note", "note", "The note or pitch to play a sound or synth with"),
  ("f", "degree", ""),
  ("f", "mtranspose", ""),
  ("f", "ctranspose", ""),
  ("f", "harmonic", ""),
  ("f", "stepsPerOctave", ""),
  ("f", "octaveR", ""),
  ("f", "nudge", "Nudges events into the future by the specified number of seconds. Negative numbers work up to a point as well (due to internal latency)"),
  ("i", "octave", ""),
  ("f", "offset", ""),
  ("f", "ophatdecay", ""),
  ("i", "orbit", "a pattern of numbers. An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share hardware output bus offset and global effects, e.g. reverb and delay. The maximum number of orbits is specified in the superdirt startup, numbers higher than maximum will wrap around."),
  ("f", "overgain", ""),
  ("f", "overshape", ""),
  ("f", "pan", "a pattern of numbers between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel)"),
  ("f", "panspan", "a pattern of numbers between -inf and inf, which controls how much multichannel output is fanned out (negative is backwards ordering)"),
  ("f", "pansplay", "a pattern of numbers between 0.0 and 1.0, which controls the multichannel spread range (multichannel only)"),
  ("f", "panwidth", "a pattern of numbers between 0.0 and inf, which controls how much each channel is distributed over neighbours (multichannel only)"),
  ("f", "panorient", "a pattern of numbers between -1.0 and 1.0, which controls the relative position of the centre pan in a pair of adjacent speakers (multichannel only)"),
  ("f", "pitch1", ""),
  ("f", "pitch2", ""),
  ("f", "pitch3", ""),
  ("f", "portamento", ""),
  ("f", "rate", "used in SuperDirt softsynths as a control rate or 'speed'"),
  ("f", "release", "a pattern of numbers to specify the release time (in seconds) of an envelope applied to each sample. Only takes effect if `attack` is also specified."),
  ("f", "resonance", "a pattern of numbers from 0 to 1. Specifies the resonance of the low-pass filter."),
  ("f", "room", "a pattern of numbers from 0 to 1. Sets the level of reverb."),
  ("f", "sagogo", ""),
  ("f", "sclap", ""),
  ("f", "sclaves", ""),
  ("f", "scrash", ""),
  ("f", "semitone", ""),
  ("f", "shape", "wave shaping distortion, a pattern of numbers from 0 for no distortion up to 1 for loads of distortion."),
  ("f", "size", "a pattern of numbers from 0 to 1. Sets the perceptual size (reverb time) of the `room` to be used in reverb."),
  ("f", "slide", ""),
  ("f", "speed", "a pattern of numbers which changes the speed of sample playback, i.e. a cheap way of changing pitch. Negative values will play the sample backwards!"),
  ("f", "squiz", ""),
  ("f", "stutterdepth", ""),
  ("f", "stuttertime", ""),
  ("f", "sustain", ""),
  ("f", "tomdecay", ""),
  ("s", "unit", "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`."),
  ("f", "velocity", ""),
  ("f", "vcfegint", ""),
  ("f", "vcoegint", ""),
  ("f", "voice", ""),
  ("s", "vowel", "formant filter to make things sound like vowels, a pattern of either `a`, `e`, `i`, `o` or `u`. Use a rest (`~`) for no effect."),
  ("f", "waveloss", ""),
  ("f", "dur", ""),
  ("f", "modwheel", ""),
  ("f", "expression", ""),
  ("f", "sustainpedal", ""),
  ("f", "tremolodepth", "Tremolo Audio DSP effect | params are 'tremolorate' and 'tremolodepth'"),
  ("f", "tremolorate", "Tremolo Audio DSP effect | params are 'tremolorate' and 'tremolodepth'"),
  ("f", "phaserdepth", "Phaser Audio DSP effect | params are 'phaserrate' and 'phaserdepth'"),
  ("f", "phaserrate", "Phaser Audio DSP effect | params are 'phaserrate' and 'phaserdepth'"),
  ("f", "fshift", "frequency shifter"),
  ("f", "fshiftnote", "frequency shifter"),
  ("f", "fshiftphase", "frequency shifter"),
  ("f", "triode", "tube distortion"),
  ("f", "krush", "shape/bass enhancer"),
  ("f", "kcutoff", ""),
  ("f", "octer", "octaver effect"),
  ("f", "octersub", "octaver effect"),
  ("f", "octersubsub", "octaver effect"),
  ("f", "ringdf", "ring modulation"),
  ("f", "ring", "ring modulation"),
  ("f", "ringf", "ring modulation"),
  ("f", "ringdf", "ring modulation"),
  ("f", "distort", "noisy fuzzy distortion"),
  ("f", "freeze", "Spectral freeze"),
  ("f", "xsdelay", ""),
  ("f", "tsdelay", ""),
  ("f", "real", "Spectral conform"),
  ("f", "imag", ""),
  ("f", "enhance", "Spectral enhance"),
  ("f", "partials", ""),
  ("f", "comb", "Spectral comb"),
  ("f", "smear", "Spectral smear"),
  ("f", "scram", "Spectral scramble"),
  ("f", "binshift", "Spectral binshift"),
  ("f", "hbrick", "High pass sort of spectral filter"),
  ("f", "lbrick", "Low pass sort of spectral filter"),
  ("s", "drum", ""),
  -- SuperDirt MIDI Params
  ("word8", "array", ""),
  ("f", "midichan", ""),
  ("f", "control", ""),
  ("f", "ccn", ""),
  ("f", "ccv", ""),
  ("f", "polyTouch", ""),
  ("f", "midibend", ""),
  ("f", "miditouch", ""),
  ("i", "nrpnn", ""),
  ("i", "nrpnv", ""),
  ("f", "ctlNum", ""),
  ("f", "frameRate", ""),
  ("f", "frames", ""),
  ("f", "hours", ""),
  ("s", "midicmd", ""),
  ("f", "minutes", ""),
  ("f", "progNum", ""),
  ("f", "seconds", ""),
  ("f", "songPtr", ""),
  ("f", "uid", ""),
  ("f", "val", ""),
  ("f", "cps", "")
 ]
 ++ (map (\i -> ("f", "slider" ++ show i, "")) [0 .. 31])
 ++ (map (\i -> ("f", "button" ++ show i, "")) [0 .. 31])

aliases =
  [("note", "up", "note"),
  ("f", "att", "attack"),
  ("f", "bpf", "bandf"),
  ("f", "bpq", "bandq"),
  ("f", "chdecay", "clhatdecay"),
  ("f", "ctf", "cutoff"),
  ("f", "ctfg", "cutoffegint"),
  ("f", "delayfb", "delayfeedback"),
  ("f", "dfb", "delayfeedback"),
  ("f", "delayt", "delaytime"),
  ("f", "dt", "delaytime"),
  ("f", "det", "detune"),
  ("f", "gat", "gate"),
  ("f", "hg", "hatgrain"),
  ("f", "hpf", "hcutoff"),
  ("f", "hpq", "hresonance"),
  ("f", "lag", "lagogo"),
  ("f", "lbd", "lkick"),
  ("f", "lch", "lclhat"),
  ("f", "lcl", "lclaves"),
  ("f", "lcp", "lclap"),
  ("f", "lcr", "lcrash"),
  ("f", "lfoc", "lfocutoffint"),
  ("f", "lfoi", "lfoint"),
  ("f", "lfop", "lfopitchint"),
  ("f", "lht", "lhitom"),
  ("f", "llt", "llotom"),
  ("f", "loh", "lophat"),
  ("f", "lpf", "cutoff"),
  ("f", "lpq", "resonance"),
  ("f", "lsn", "lsnare"),
  ("f", "ohdecay", "ophatdecay"),
  ("f", "phasdp", "phaserdepth"),
  ("f", "phasr", "phaserrate"),
  ("f", "pit1", "pitch1"),
  ("f", "pit2", "pitch2"),
  ("f", "pit3", "pitch3"),
  ("f", "por", "portamento"),
  ("f", "rel", "release"),
  ("f", "sag", "sagogo"),
  ("f", "scl", "sclaves"),
  ("f", "scp", "sclap"),
  ("f", "scr", "scrash"),
  ("f", "sz", "size"),
  ("f", "sld", "slide"),
  ("f", "std", "stutterdepth"),
  ("f", "stt", "stuttertime"),
  ("f", "sus", "sustain"),
  ("f", "tdecay", "tomdecay"),
  ("f", "tremdp", "tremolodepth"),
  ("f", "tremr", "tremolorate"),
  ("f", "vcf", "vcfegint"),
  ("f", "vco", "vcoegint"),
  ("f", "voi", "voice"),
  ]
dktr0 commented 3 years ago

@yaxu As I understand it, incorporating template haskell into what tidal-parse/MiniTidal recognizes is not impossible (template haskell constructs are part of the AST returned by haskell-src-exts, for which tidal-parse then uses haskellish to parse further into Tidal constructs, so...), but it would be a massive increase in the complexity of tidal-parse. Definitely not something I'd be able to take on... (to say this a different way: GHCJS absolutely does support template Haskell - however GHCJS is only what is used to build Estuary, it is not available as a service in the run-time environment - tidal-parse is what is used to parse things at run-time)

Trying to follow this issue and the previous one it is connected to, I feel like I don't have a full grasp of what the goal is. My basic thought is that sending pan messages once and sending them continuously are such different scenarios that there's no need to abstract them into each other. Using "pan" for one kind of panning and "panbus n" for the other already seems very workable to me. Making all uses of pan involve bombarding the sound server with messages, even when the pan position doesn't change, seems strange. But possibly I have just misunderstood something about what the goal is! ;)

dktr0 commented 3 years ago

In any case, I do very much hope as many parameters as possible stay in common between the different implementations - inevitably diverging, but with as large an area of commonality as possible... since all three Dirts have panning with basically the exact same semantics, it would be pretty weird for them to have different parameters for that, for example.

yaxu commented 3 years ago

@dktr0 scsynth only gets messages if the end-user says so, by explicitly routing a parameter to a bus, so I think we're in agreement there. A lot of the comments on the other issue are just me thinking through how to make a nice UI for it.

Yes lets try to keep the dirts talking the same language as much as possible. I want to keep classic dirt going too although probably won't find implement these control busses.

Sound.Tidal.Params is a bit of a mess, and putting everything into the same namespace feels a bit messy too, but I can't think of a single time when it's been a problem.. I guess in general pattern transformation names are verbs and synth parameter names are adjectives and this works well enough namespace-wise.

yaxu commented 3 years ago

As for TH I got something working but it wasn't making things less complicated.. Easier to just generate the code as text then compile it as per https://github.com/tidalcycles/Tidal/commit/54430b7ce4fc37e8f04cffbb97f41833a4ca60d1