Support list syntax in mininotation (#512)

fixes #504

* support list syntax in mininotation
* support compound controls
* remove redundant : splitting of s/n and note/n from webaudio
* patternable scale names
This commit is contained in:
Alex McLean 2023-03-04 18:06:18 +00:00 committed by GitHub
parent 4bfeaa47bf
commit bf72908dc9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 458 additions and 208 deletions

View file

@ -5,20 +5,24 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import { Pattern, sequence } from './pattern.mjs';
import { zipWith } from './util.mjs';
const controls = {};
const generic_params = [
/**
* Select a sound / sample by name.
* 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', 'sound'],
[['s', 'n', 'gain'], 'sound'],
/**
* Selects the given index from the sample map.
* Numbers too high will wrap around.
@ -50,7 +54,7 @@ const generic_params = [
* @example
* note("60 69 65 64")
*/
['note'],
[['note', 'n']],
/**
* A pattern of numbers that speed up (or slow down) samples while they play. Currently only supported by osc / superdirt.
@ -143,21 +147,20 @@ const generic_params = [
['hold'],
// TODO: in tidal, it seems to be normalized
/**
* Sets the center frequency of the **b**and-**p**ass **f**ilter.
* 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
* @synonyms bandf, bp
* @example
* s("bd sd,hh*3").bpf("<1000 2000 4000 8000>")
*
*/
// currently an alias of 'bandf' https://github.com/tidalcycles/strudel/issues/496
// ['bpf'],
['bandf', 'bpf'],
[['bandf', 'bandq'], 'bpf', 'bp'],
// TODO: in tidal, it seems to be normalized
/**
* Sets the **b**and-**p**ass **q**-factor (resonance)
* Sets the **b**and-**p**ass **q**-factor (resonance).
*
* @name bpq
* @param {number | Pattern} q q factor
@ -258,27 +261,35 @@ const generic_params = [
/**
* 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
* @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', 'ctf', 'lpf'],
[['cutoff', 'resonance'], 'ctf', 'lpf', 'lp'],
/**
* 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 hcutoff
* @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'],
['hcutoff', 'hpf'],
[['hcutoff', 'hresonance'], 'hpf', 'hp'],
/**
* Controls the **h**igh-**p**ass **q**-value.
*
@ -317,13 +328,19 @@ const generic_params = [
/**
* 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'],
[['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
@ -549,13 +566,17 @@ const generic_params = [
/**
* 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'],
[['room', 'size']],
/**
* Sets the room size of the reverb, see {@link room}.
*
@ -733,32 +754,50 @@ const generic_params = [
// TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13
const _name = (name, ...pats) => sequence(...pats).withValue((x) => ({ [name]: x }));
controls.createParam = function (names) {
const name = Array.isArray(names) ? names[0] : names;
const _setter = (func, name) =>
function (...pats) {
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 setter = function (...pats) {
if (!pats.length) {
return this.fmap((value) => ({ [name]: value }));
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);
generic_params.forEach(([name, ...aliases]) => {
controls[name] = (...pats) => _name(name, ...pats);
Pattern.prototype[name] = _setter(controls[name], name);
aliases.forEach((alias) => {
controls[alias] = controls[name];
Pattern.prototype[alias] = Pattern.prototype[name];
});
});
// create custom param
controls.createParam = (name) => {
const func = (...pats) => _name(name, ...pats);
Pattern.prototype[name] = _setter(func, name);
return (...pats) => _name(name, ...pats);
};
controls.createParams = (...names) =>
names.reduce((acc, name) => Object.assign(acc, { [name]: controls.createParam(name) }), {});