diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 5f38d393..ff5da74d 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -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) }), {}); diff --git a/packages/core/test/controls.test.mjs b/packages/core/test/controls.test.mjs new file mode 100644 index 00000000..49aa6a4f --- /dev/null +++ b/packages/core/test/controls.test.mjs @@ -0,0 +1,28 @@ +/* +controls.test.mjs - +Copyright (C) 2023 Strudel contributors - see +This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ + +import controls from '../controls.mjs'; +import { mini } from '../../mini/mini.mjs'; +import { describe, it, expect } from 'vitest'; + +describe('controls', () => { + it('should support controls', () => { + expect(controls.s('bd').firstCycleValues).toEqual([{ s: 'bd' }]); + }); + it('should support compound controls', () => { + expect(controls.s(mini('bd:3')).firstCycleValues).toEqual([{ s: 'bd', n: 3 }]); + expect(controls.s(mini('bd:3 sd:4:1.4')).firstCycleValues).toEqual([ + { s: 'bd', n: 3 }, + { s: 'sd', n: 4, gain: 1.4 }, + ]); + }); + it('should support ignore extra elements in compound controls', () => { + expect(controls.s(mini('bd:3:0.4 sd:4:0.5:3:17')).firstCycleValues).toEqual([ + { s: 'bd', n: 3, gain: 0.4 }, + { s: 'sd', n: 4, gain: 0.5 }, + ]); + }); +}); diff --git a/packages/mini/krill-parser.js b/packages/mini/krill-parser.js index e37a3d05..34e3843c 100644 --- a/packages/mini/krill-parser.js +++ b/packages/mini/krill-parser.js @@ -182,21 +182,21 @@ function peg$parse(input, options) { var peg$c8 = "#"; var peg$c9 = "^"; var peg$c10 = "_"; - var peg$c11 = ":"; - var peg$c12 = "["; - var peg$c13 = "]"; - var peg$c14 = "{"; - var peg$c15 = "}"; - var peg$c16 = "%"; - var peg$c17 = "<"; - var peg$c18 = ">"; - var peg$c19 = "@"; - var peg$c20 = "!"; - var peg$c21 = "("; - var peg$c22 = ")"; - var peg$c23 = "/"; - var peg$c24 = "*"; - var peg$c25 = "?"; + var peg$c11 = "["; + var peg$c12 = "]"; + var peg$c13 = "{"; + var peg$c14 = "}"; + var peg$c15 = "%"; + var peg$c16 = "<"; + var peg$c17 = ">"; + var peg$c18 = "@"; + var peg$c19 = "!"; + var peg$c20 = "("; + var peg$c21 = ")"; + var peg$c22 = "/"; + var peg$c23 = "*"; + var peg$c24 = "?"; + var peg$c25 = ":"; var peg$c26 = "struct"; var peg$c27 = "target"; var peg$c28 = "euclid"; @@ -237,21 +237,21 @@ function peg$parse(input, options) { var peg$e15 = peg$literalExpectation("#", false); var peg$e16 = peg$literalExpectation("^", false); var peg$e17 = peg$literalExpectation("_", false); - var peg$e18 = peg$literalExpectation(":", false); - var peg$e19 = peg$literalExpectation("[", false); - var peg$e20 = peg$literalExpectation("]", false); - var peg$e21 = peg$literalExpectation("{", false); - var peg$e22 = peg$literalExpectation("}", false); - var peg$e23 = peg$literalExpectation("%", false); - var peg$e24 = peg$literalExpectation("<", false); - var peg$e25 = peg$literalExpectation(">", false); - var peg$e26 = peg$literalExpectation("@", false); - var peg$e27 = peg$literalExpectation("!", false); - var peg$e28 = peg$literalExpectation("(", false); - var peg$e29 = peg$literalExpectation(")", false); - var peg$e30 = peg$literalExpectation("/", false); - var peg$e31 = peg$literalExpectation("*", false); - var peg$e32 = peg$literalExpectation("?", false); + var peg$e18 = peg$literalExpectation("[", false); + var peg$e19 = peg$literalExpectation("]", false); + var peg$e20 = peg$literalExpectation("{", false); + var peg$e21 = peg$literalExpectation("}", false); + var peg$e22 = peg$literalExpectation("%", false); + var peg$e23 = peg$literalExpectation("<", false); + var peg$e24 = peg$literalExpectation(">", false); + var peg$e25 = peg$literalExpectation("@", false); + var peg$e26 = peg$literalExpectation("!", false); + var peg$e27 = peg$literalExpectation("(", false); + var peg$e28 = peg$literalExpectation(")", false); + var peg$e29 = peg$literalExpectation("/", false); + var peg$e30 = peg$literalExpectation("*", false); + var peg$e31 = peg$literalExpectation("?", false); + var peg$e32 = peg$literalExpectation(":", false); var peg$e33 = peg$literalExpectation("struct", false); var peg$e34 = peg$literalExpectation("target", false); var peg$e35 = peg$literalExpectation("euclid", false); @@ -280,35 +280,36 @@ function peg$parse(input, options) { var peg$f9 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) }; var peg$f10 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) }; var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a } }) }; - var peg$f12 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1}); + var peg$f12 = function(s) { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) }; + var peg$f13 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1}); for (const op of ops) { op(result); } return result; }; - var peg$f13 = function(s) { return new PatternStub(s, 'fastcat'); }; - var peg$f14 = function(tail) { return { alignment: 'stack', list: tail }; }; - var peg$f15 = function(tail) { return { alignment: 'rand', list: tail }; }; - var peg$f16 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment); } else { return head; } }; - var peg$f17 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); }; - var peg$f18 = function(sc) { return sc; }; - var peg$f19 = function(s) { return { name: "struct", args: { mini:s }}}; - var peg$f20 = function(s) { return { name: "target", args : { name:s}}}; - var peg$f21 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}}; - var peg$f22 = function(a) { return { name: "stretch", args :{ amount: a}}}; - var peg$f23 = function(a) { return { name: "shift", args :{ amount: "-"+a}}}; - var peg$f24 = function(a) { return { name: "shift", args :{ amount: a}}}; - var peg$f25 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}}; - var peg$f26 = function(s) { return { name: "scale", args :{ scale: s.join("")}}}; - var peg$f27 = function(s, v) { return v}; - var peg$f28 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); }; - var peg$f29 = function(sg) {return sg}; - var peg$f30 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)}; - var peg$f31 = function(sc) { return sc }; - var peg$f32 = function(c) { return c }; - var peg$f33 = function(v) { return new CommandStub("setcps", { value: v})}; - var peg$f34 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})}; - var peg$f35 = function() { return new CommandStub("hush")}; + var peg$f14 = function(s) { return new PatternStub(s, 'fastcat'); }; + var peg$f15 = function(tail) { return { alignment: 'stack', list: tail }; }; + var peg$f16 = function(tail) { return { alignment: 'rand', list: tail }; }; + var peg$f17 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment); } else { return head; } }; + var peg$f18 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); }; + var peg$f19 = function(sc) { return sc; }; + var peg$f20 = function(s) { return { name: "struct", args: { mini:s }}}; + var peg$f21 = function(s) { return { name: "target", args : { name:s}}}; + var peg$f22 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}}; + var peg$f23 = function(a) { return { name: "stretch", args :{ amount: a}}}; + var peg$f24 = function(a) { return { name: "shift", args :{ amount: "-"+a}}}; + var peg$f25 = function(a) { return { name: "shift", args :{ amount: a}}}; + var peg$f26 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}}; + var peg$f27 = function(s) { return { name: "scale", args :{ scale: s.join("")}}}; + var peg$f28 = function(s, v) { return v}; + var peg$f29 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); }; + var peg$f30 = function(sg) {return sg}; + var peg$f31 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)}; + var peg$f32 = function(sc) { return sc }; + var peg$f33 = function(c) { return c }; + var peg$f34 = function(v) { return new CommandStub("setcps", { value: v})}; + var peg$f35 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})}; + var peg$f36 = function() { return new CommandStub("hush")}; var peg$currPos = 0; var peg$savedPos = 0; var peg$posDetailsCache = [{ line: 1, column: 1 }]; @@ -848,15 +849,6 @@ function peg$parse(input, options) { s0 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e17); } } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 58) { - s0 = peg$c11; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e18); } - } - } } } } @@ -899,11 +891,11 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = peg$parsews(); if (input.charCodeAt(peg$currPos) === 91) { - s2 = peg$c12; + s2 = peg$c11; peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e19); } + if (peg$silentFails === 0) { peg$fail(peg$e18); } } if (s2 !== peg$FAILED) { s3 = peg$parsews(); @@ -911,11 +903,11 @@ function peg$parse(input, options) { if (s4 !== peg$FAILED) { s5 = peg$parsews(); if (input.charCodeAt(peg$currPos) === 93) { - s6 = peg$c13; + s6 = peg$c12; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e20); } + if (peg$silentFails === 0) { peg$fail(peg$e19); } } if (s6 !== peg$FAILED) { s7 = peg$parsews(); @@ -943,11 +935,11 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = peg$parsews(); if (input.charCodeAt(peg$currPos) === 123) { - s2 = peg$c14; + s2 = peg$c13; peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e21); } + if (peg$silentFails === 0) { peg$fail(peg$e20); } } if (s2 !== peg$FAILED) { s3 = peg$parsews(); @@ -955,11 +947,11 @@ function peg$parse(input, options) { if (s4 !== peg$FAILED) { s5 = peg$parsews(); if (input.charCodeAt(peg$currPos) === 125) { - s6 = peg$c15; + s6 = peg$c14; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e22); } + if (peg$silentFails === 0) { peg$fail(peg$e21); } } if (s6 !== peg$FAILED) { s7 = peg$parsepolymeter_steps(); @@ -990,11 +982,11 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 37) { - s1 = peg$c16; + s1 = peg$c15; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e23); } + if (peg$silentFails === 0) { peg$fail(peg$e22); } } if (s1 !== peg$FAILED) { s2 = peg$parseslice(); @@ -1019,11 +1011,11 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = peg$parsews(); if (input.charCodeAt(peg$currPos) === 60) { - s2 = peg$c17; + s2 = peg$c16; peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e24); } + if (peg$silentFails === 0) { peg$fail(peg$e23); } } if (s2 !== peg$FAILED) { s3 = peg$parsews(); @@ -1031,11 +1023,11 @@ function peg$parse(input, options) { if (s4 !== peg$FAILED) { s5 = peg$parsews(); if (input.charCodeAt(peg$currPos) === 62) { - s6 = peg$c18; + s6 = peg$c17; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e25); } + if (peg$silentFails === 0) { peg$fail(peg$e24); } } if (s6 !== peg$FAILED) { s7 = peg$parsews(); @@ -1088,6 +1080,9 @@ function peg$parse(input, options) { s0 = peg$parseop_replicate(); if (s0 === peg$FAILED) { s0 = peg$parseop_degrade(); + if (s0 === peg$FAILED) { + s0 = peg$parseop_tail(); + } } } } @@ -1102,11 +1097,11 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 64) { - s1 = peg$c19; + s1 = peg$c18; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e26); } + if (peg$silentFails === 0) { peg$fail(peg$e25); } } if (s1 !== peg$FAILED) { s2 = peg$parsenumber(); @@ -1130,11 +1125,11 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 33) { - s1 = peg$c20; + s1 = peg$c19; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e27); } + if (peg$silentFails === 0) { peg$fail(peg$e26); } } if (s1 !== peg$FAILED) { s2 = peg$parsenumber(); @@ -1158,11 +1153,11 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 40) { - s1 = peg$c21; + s1 = peg$c20; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e28); } + if (peg$silentFails === 0) { peg$fail(peg$e27); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); @@ -1186,11 +1181,11 @@ function peg$parse(input, options) { } s12 = peg$parsews(); if (input.charCodeAt(peg$currPos) === 41) { - s13 = peg$c22; + s13 = peg$c21; peg$currPos++; } else { s13 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e29); } + if (peg$silentFails === 0) { peg$fail(peg$e28); } } if (s13 !== peg$FAILED) { peg$savedPos = s0; @@ -1224,11 +1219,11 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 47) { - s1 = peg$c23; + s1 = peg$c22; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e30); } + if (peg$silentFails === 0) { peg$fail(peg$e29); } } if (s1 !== peg$FAILED) { s2 = peg$parseslice(); @@ -1252,11 +1247,11 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 42) { - s1 = peg$c24; + s1 = peg$c23; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e31); } + if (peg$silentFails === 0) { peg$fail(peg$e30); } } if (s1 !== peg$FAILED) { s2 = peg$parseslice(); @@ -1280,11 +1275,11 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 63) { - s1 = peg$c25; + s1 = peg$c24; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e32); } + if (peg$silentFails === 0) { peg$fail(peg$e31); } } if (s1 !== peg$FAILED) { s2 = peg$parsenumber(); @@ -1301,6 +1296,34 @@ function peg$parse(input, options) { return s0; } + function peg$parseop_tail() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 58) { + s1 = peg$c25; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e32); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseslice(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f12(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + function peg$parseslice_with_ops() { var s0, s1, s2, s3; @@ -1314,7 +1337,7 @@ function peg$parse(input, options) { s3 = peg$parseslice_op(); } peg$savedPos = s0; - s0 = peg$f12(s1, s2); + s0 = peg$f13(s1, s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1339,7 +1362,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f13(s1); + s1 = peg$f14(s1); } s0 = s1; @@ -1388,7 +1411,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f14(s1); + s1 = peg$f15(s1); } s0 = s1; @@ -1437,7 +1460,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f15(s1); + s1 = peg$f16(s1); } s0 = s1; @@ -1458,7 +1481,7 @@ function peg$parse(input, options) { s2 = null; } peg$savedPos = s0; - s0 = peg$f16(s1, s2); + s0 = peg$f17(s1, s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1478,7 +1501,7 @@ function peg$parse(input, options) { s2 = null; } peg$savedPos = s0; - s0 = peg$f17(s1, s2); + s0 = peg$f18(s1, s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1499,7 +1522,7 @@ function peg$parse(input, options) { s4 = peg$parsequote(); if (s4 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f18(s3); + s0 = peg$f19(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1561,7 +1584,7 @@ function peg$parse(input, options) { s3 = peg$parsemini_or_operator(); if (s3 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f19(s3); + s0 = peg$f20(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1594,7 +1617,7 @@ function peg$parse(input, options) { s5 = peg$parsequote(); if (s5 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f20(s4); + s0 = peg$f21(s4); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1639,7 +1662,7 @@ function peg$parse(input, options) { s7 = null; } peg$savedPos = s0; - s0 = peg$f21(s3, s5, s7); + s0 = peg$f22(s3, s5, s7); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1672,7 +1695,7 @@ function peg$parse(input, options) { s3 = peg$parsenumber(); if (s3 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f22(s3); + s0 = peg$f23(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1701,7 +1724,7 @@ function peg$parse(input, options) { s3 = peg$parsenumber(); if (s3 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f23(s3); + s0 = peg$f24(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1730,7 +1753,7 @@ function peg$parse(input, options) { s3 = peg$parsenumber(); if (s3 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f24(s3); + s0 = peg$f25(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1759,7 +1782,7 @@ function peg$parse(input, options) { s3 = peg$parsenumber(); if (s3 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f25(s3); + s0 = peg$f26(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1801,7 +1824,7 @@ function peg$parse(input, options) { s5 = peg$parsequote(); if (s5 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f26(s4); + s0 = peg$f27(s4); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1876,11 +1899,11 @@ function peg$parse(input, options) { if (s1 !== peg$FAILED) { s2 = peg$parsews(); if (input.charCodeAt(peg$currPos) === 91) { - s3 = peg$c12; + s3 = peg$c11; peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e19); } + if (peg$silentFails === 0) { peg$fail(peg$e18); } } if (s3 !== peg$FAILED) { s4 = peg$parsews(); @@ -1893,7 +1916,7 @@ function peg$parse(input, options) { s9 = peg$parsemini_or_operator(); if (s9 !== peg$FAILED) { peg$savedPos = s7; - s7 = peg$f27(s5, s9); + s7 = peg$f28(s5, s9); } else { peg$currPos = s7; s7 = peg$FAILED; @@ -1910,7 +1933,7 @@ function peg$parse(input, options) { s9 = peg$parsemini_or_operator(); if (s9 !== peg$FAILED) { peg$savedPos = s7; - s7 = peg$f27(s5, s9); + s7 = peg$f28(s5, s9); } else { peg$currPos = s7; s7 = peg$FAILED; @@ -1922,15 +1945,15 @@ function peg$parse(input, options) { } s7 = peg$parsews(); if (input.charCodeAt(peg$currPos) === 93) { - s8 = peg$c13; + s8 = peg$c12; peg$currPos++; } else { s8 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e20); } + if (peg$silentFails === 0) { peg$fail(peg$e19); } } if (s8 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f28(s5, s6); + s0 = peg$f29(s5, s6); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1976,7 +1999,7 @@ function peg$parse(input, options) { s4 = peg$parsecomment(); } peg$savedPos = s0; - s0 = peg$f29(s1); + s0 = peg$f30(s1); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1998,7 +2021,7 @@ function peg$parse(input, options) { s5 = peg$parsemini_or_operator(); if (s5 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f30(s1, s5); + s0 = peg$f31(s1, s5); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -2023,7 +2046,7 @@ function peg$parse(input, options) { s1 = peg$parsemini_or_operator(); if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f31(s1); + s1 = peg$f32(s1); } s0 = s1; if (s0 === peg$FAILED) { @@ -2056,7 +2079,7 @@ function peg$parse(input, options) { if (s2 !== peg$FAILED) { s3 = peg$parsews(); peg$savedPos = s0; - s0 = peg$f32(s2); + s0 = peg$f33(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -2081,7 +2104,7 @@ function peg$parse(input, options) { s3 = peg$parsenumber(); if (s3 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f33(s3); + s0 = peg$f34(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -2110,7 +2133,7 @@ function peg$parse(input, options) { s3 = peg$parsenumber(); if (s3 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f34(s3); + s0 = peg$f35(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -2136,7 +2159,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f35(); + s1 = peg$f36(); } s0 = s1; diff --git a/packages/mini/krill.pegjs b/packages/mini/krill.pegjs index 12047f8f..f4fd7772 100644 --- a/packages/mini/krill.pegjs +++ b/packages/mini/krill.pegjs @@ -96,7 +96,7 @@ quote = '"' / "'" // ------------------ steps and cycles --------------------------- // single step definition (e.g bd) -step_char = [0-9a-zA-Z~] / "-" / "#" / "." / "^" / "_" / ":" +step_char = [0-9a-zA-Z~] / "-" / "#" / "." / "^" / "_" step = ws chars:step_char+ ws { return new AtomStub(chars.join("")) } // define a sub cycle e.g. [1 2, 3 [4]] @@ -119,7 +119,7 @@ slice = step / sub_cycle / polymeter / slow_sequence // slice modifier affects the timing/size of a slice (e.g. [a b c]@3) // at this point, we assume we can represent them as regular sequence operators -slice_op = op_weight / op_bjorklund / op_slow / op_fast / op_replicate / op_degrade +slice_op = op_weight / op_bjorklund / op_slow / op_fast / op_replicate / op_degrade / op_tail op_weight = "@" a:number { return x => x.options_['weight'] = a } @@ -139,6 +139,9 @@ op_fast = "*"a:slice op_degrade = "?"a:number? { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a } }) } +op_tail = ":" s:slice + { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) } + // a slice with an modifier applied i.e [bd@4 sd@3]@2 hh] slice_with_ops = s:slice ops:slice_op* { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1}); diff --git a/packages/mini/mini.mjs b/packages/mini/mini.mjs index a78451f2..af6549cf 100644 --- a/packages/mini/mini.mjs +++ b/packages/mini/mini.mjs @@ -18,6 +18,7 @@ const applyOptions = (parent, code) => (pat, i) => { const ast = parent.source_[i]; const options = ast.options_; const ops = options?.ops; + if (ops) { for (const op of ops) { switch (op.type_) { @@ -66,6 +67,11 @@ const applyOptions = (parent, code) => (pat, i) => { pat = strudel.reify(pat).degradeBy(op.arguments_.amount === null ? 0.5 : op.arguments_.amount); break; } + case 'tail': { + const friend = patternifyAST(op.arguments_.element, code); + pat = pat.fmap((a) => (b) => Array.isArray(a) ? [...a, b] : [a, b]).appLeft(friend); + break; + } default: { console.warn(`operator "${op.type_}" not implemented`); } diff --git a/packages/mini/test/mini.test.mjs b/packages/mini/test/mini.test.mjs index e5d6b4f0..1e18dd58 100644 --- a/packages/mini/test/mini.test.mjs +++ b/packages/mini/test/mini.test.mjs @@ -140,6 +140,9 @@ describe('mini', () => { expect(haps.length < 230).toBe(true); // 'Had too many cycles remaining after degradeBy 0.8'); }); + it('supports lists', () => { + expect(minV('a:b c:d:[e:f] g')).toEqual([['a', 'b'], ['c', 'd', ['e', 'f']], 'g']); + }); /*it('supports the random choice operator ("|") with nesting', () => { const numCycles = 900; const haps = mini('a | [b | c] | [d | e | f]').queryArc(0, numCycles); diff --git a/packages/tonal/test/tonal.test.mjs b/packages/tonal/test/tonal.test.mjs index 0db68404..7c32989a 100644 --- a/packages/tonal/test/tonal.test.mjs +++ b/packages/tonal/test/tonal.test.mjs @@ -9,6 +9,7 @@ This program is free software: you can redistribute it and/or modify it under th import '../tonal.mjs'; // need to import this to add prototypes import { pure, controls, seq } from '@strudel.cycles/core'; import { describe, it, expect } from 'vitest'; +import { mini } from '../../mini/mini.mjs'; const { n } = controls; describe('tonal', () => { @@ -37,4 +38,11 @@ describe('tonal', () => { .firstCycleValues.map((h) => h.note), ).toEqual(['C3', 'D3', 'E3']); }); + it('scale with mininotation colon', () => { + expect( + n(0, 1, 2) + .scale(mini('C:major')) + .firstCycleValues.map((h) => h.note), + ).toEqual(['C3', 'D3', 'E3']); + }); }); diff --git a/packages/tonal/tonal.mjs b/packages/tonal/tonal.mjs index 9a6c16bb..854fd1b6 100644 --- a/packages/tonal/tonal.mjs +++ b/packages/tonal/tonal.mjs @@ -123,29 +123,36 @@ export const scaleTranspose = register('scaleTranspose', function (offset /* : n /** * Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like {@link Pattern#scaleTranspose}. * - * A scale consists of a root note (e.g. `c4`, `c`, `f#`, `bb4`) followed by a [scale type](https://github.com/tonaljs/tonal/blob/main/packages/scale-type/data.ts). + * A scale consists of a root note (e.g. `c4`, `c`, `f#`, `bb4`) followed by semicolon (':') and then a [scale type](https://github.com/tonaljs/tonal/blob/main/packages/scale-type/data.ts). + * * The root note defaults to octave 3, if no octave number is given. - * Note that you currently cannot pattern `scale` with the mini notation, because the scale name includes a space. - * This will be improved in the future. Until then, use a sequence function like `cat` or `seq`. * * @memberof Pattern * @name scale * @param {string} scale Name of scale * @returns Pattern * @example - * "0 2 4 6 4 2".scale('C2 major').note() + * "0 2 4 6 4 2".scale("C2:major").note() * @example * "0 2 4 6 4 2" - * .scale(seq('C2 major', 'C2 minor').slow(2)) + * .scale("C2:") * .note() + * @example + * "0 1 2 3 4 5 6 7".rev().scale("C2:").note() + * .s("folkharp") */ -export const scale = register('scale', function (scale /* : string */, pat) { +export const scale = register('scale', function (scale, pat) { + // Supports ':' list syntax in mininotation + if (Array.isArray(scale)) { + scale = scale.join(' '); + } return pat.withHap((hap) => { const isObject = typeof hap.value === 'object'; let note = isObject ? hap.value.n : hap.value; const asNumber = Number(note); if (!isNaN(asNumber)) { + // TODO: worth keeping for supporting ':' in (non-mininotation) strings? scale = scale.replaceAll(':', ' '); let [tonic, scaleName] = Scale.tokenize(scale); const { pc, oct = 3 } = Note.get(tonic); diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 1f4e3375..50436d09 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -97,17 +97,6 @@ const getSoundfontKey = (s) => { return; }; -const splitSN = (s, n) => { - if (!s.includes(':')) { - return [s, n]; - } - let [s2, n2] = s.split(':'); - if (isNaN(Number(n2))) { - return [s, n]; - } - return [s2, n2]; -}; - let workletsLoading; function loadWorklets() { if (workletsLoading) { @@ -243,12 +232,6 @@ export const webaudioOutput = async (hap, deadline, hapDuration, cps) => { if (bank && s) { s = `${bank}_${s}`; } - if (typeof s === 'string') { - [s, n] = splitSN(s, n); - } - if (typeof note === 'string') { - [note, n] = splitSN(note, n); - } if (!s || ['sine', 'square', 'triangle', 'sawtooth'].includes(s)) { // destructure adsr here, because the default should be different for synths and samples const { attack = 0.001, decay = 0.05, sustain = 0.6, release = 0.01 } = hap.value; diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 6bd9e7f0..e72fd284 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1399,6 +1399,19 @@ exports[`runs examples > example "delay" example index 0 1`] = ` ] `; +exports[`runs examples > example "delay" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd delay:0.65 delaytime:0.25 delayfeedback:0.9 ]", + "[ 1/2 → 1/1 | s:bd delay:0.65 delaytime:0.125 delayfeedback:0.7 ]", + "[ 1/1 → 3/2 | s:bd delay:0.65 delaytime:0.25 delayfeedback:0.9 ]", + "[ 3/2 → 2/1 | s:bd delay:0.65 delaytime:0.125 delayfeedback:0.7 ]", + "[ 2/1 → 5/2 | s:bd delay:0.65 delaytime:0.25 delayfeedback:0.9 ]", + "[ 5/2 → 3/1 | s:bd delay:0.65 delaytime:0.125 delayfeedback:0.7 ]", + "[ 3/1 → 7/2 | s:bd delay:0.65 delaytime:0.25 delayfeedback:0.9 ]", + "[ 7/2 → 4/1 | s:bd delay:0.65 delaytime:0.125 delayfeedback:0.7 ]", +] +`; + exports[`runs examples > example "delayfeedback" example index 0 1`] = ` [ "[ (0/1 → 1/1) ⇝ 2/1 | s:bd delay:0.25 delayfeedback:0.25 ]", @@ -1923,6 +1936,35 @@ exports[`runs examples > example "hpf" example index 0 1`] = ` ] `; +exports[`runs examples > example "hpf" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd hcutoff:2000 ]", + "[ 1/2 → 1/1 | s:sd hcutoff:2000 ]", + "[ 0/1 → 1/4 | s:hh hcutoff:2000 ]", + "[ 1/4 → 1/2 | s:hh hcutoff:2000 ]", + "[ 1/2 → 3/4 | s:hh hcutoff:2000 ]", + "[ 3/4 → 1/1 | s:hh hcutoff:2000 ]", + "[ 1/1 → 3/2 | s:bd hcutoff:2000 hresonance:25 ]", + "[ 3/2 → 2/1 | s:sd hcutoff:2000 hresonance:25 ]", + "[ 1/1 → 5/4 | s:hh hcutoff:2000 hresonance:25 ]", + "[ 5/4 → 3/2 | s:hh hcutoff:2000 hresonance:25 ]", + "[ 3/2 → 7/4 | s:hh hcutoff:2000 hresonance:25 ]", + "[ 7/4 → 2/1 | s:hh hcutoff:2000 hresonance:25 ]", + "[ 2/1 → 5/2 | s:bd hcutoff:2000 ]", + "[ 5/2 → 3/1 | s:sd hcutoff:2000 ]", + "[ 2/1 → 9/4 | s:hh hcutoff:2000 ]", + "[ 9/4 → 5/2 | s:hh hcutoff:2000 ]", + "[ 5/2 → 11/4 | s:hh hcutoff:2000 ]", + "[ 11/4 → 3/1 | s:hh hcutoff:2000 ]", + "[ 3/1 → 7/2 | s:bd hcutoff:2000 hresonance:25 ]", + "[ 7/2 → 4/1 | s:sd hcutoff:2000 hresonance:25 ]", + "[ 3/1 → 13/4 | s:hh hcutoff:2000 hresonance:25 ]", + "[ 13/4 → 7/2 | s:hh hcutoff:2000 hresonance:25 ]", + "[ 7/2 → 15/4 | s:hh hcutoff:2000 hresonance:25 ]", + "[ 15/4 → 4/1 | s:hh hcutoff:2000 hresonance:25 ]", +] +`; + exports[`runs examples > example "hpq" example index 0 1`] = ` [ "[ 0/1 → 1/2 | s:bd hcutoff:2000 hresonance:0 ]", @@ -1955,19 +1997,19 @@ exports[`runs examples > example "hpq" example index 0 1`] = ` exports[`runs examples > example "hurry" example index 0 1`] = ` [ "[ 0/1 → 3/4 | s:bd speed:1 ]", - "[ (3/4 → 1/1) ⇝ 3/2 | s:sd:2 speed:1 ]", - "[ 3/4 ⇜ (1/1 → 3/2) | s:sd:2 speed:1 ]", + "[ (3/4 → 1/1) ⇝ 3/2 | s:sd n:2 speed:1 ]", + "[ 3/4 ⇜ (1/1 → 3/2) | s:sd n:2 speed:1 ]", "[ 3/2 → 15/8 | s:bd speed:2 ]", - "[ (15/8 → 2/1) ⇝ 9/4 | s:sd:2 speed:2 ]", - "[ 15/8 ⇜ (2/1 → 9/4) | s:sd:2 speed:2 ]", + "[ (15/8 → 2/1) ⇝ 9/4 | s:sd n:2 speed:2 ]", + "[ 15/8 ⇜ (2/1 → 9/4) | s:sd n:2 speed:2 ]", "[ 9/4 → 21/8 | s:bd speed:2 ]", - "[ 21/8 → 3/1 | s:sd:2 speed:2 ]", + "[ 21/8 → 3/1 | s:sd n:2 speed:2 ]", "[ 3/1 → 51/16 | s:bd speed:4 ]", - "[ 51/16 → 27/8 | s:sd:2 speed:4 ]", + "[ 51/16 → 27/8 | s:sd n:2 speed:4 ]", "[ 27/8 → 57/16 | s:bd speed:4 ]", - "[ 57/16 → 15/4 | s:sd:2 speed:4 ]", + "[ 57/16 → 15/4 | s:sd n:2 speed:4 ]", "[ 15/4 → 63/16 | s:bd speed:4 ]", - "[ (63/16 → 4/1) ⇝ 33/8 | s:sd:2 speed:4 ]", + "[ (63/16 → 4/1) ⇝ 33/8 | s:sd n:2 speed:4 ]", ] `; @@ -2406,6 +2448,43 @@ exports[`runs examples > example "lpf" example index 0 1`] = ` ] `; +exports[`runs examples > example "lpf" example index 1 1`] = ` +[ + "[ 0/1 → 1/8 | s:bd cutoff:1000 resonance:0 ]", + "[ 1/8 → 1/4 | s:bd cutoff:1000 resonance:0 ]", + "[ 1/4 → 3/8 | s:bd cutoff:1000 resonance:10 ]", + "[ 3/8 → 1/2 | s:bd cutoff:1000 resonance:10 ]", + "[ 1/2 → 5/8 | s:bd cutoff:1000 resonance:20 ]", + "[ 5/8 → 3/4 | s:bd cutoff:1000 resonance:20 ]", + "[ 3/4 → 7/8 | s:bd cutoff:1000 resonance:30 ]", + "[ 7/8 → 1/1 | s:bd cutoff:1000 resonance:30 ]", + "[ 1/1 → 9/8 | s:bd cutoff:1000 resonance:0 ]", + "[ 9/8 → 5/4 | s:bd cutoff:1000 resonance:0 ]", + "[ 5/4 → 11/8 | s:bd cutoff:1000 resonance:10 ]", + "[ 11/8 → 3/2 | s:bd cutoff:1000 resonance:10 ]", + "[ 3/2 → 13/8 | s:bd cutoff:1000 resonance:20 ]", + "[ 13/8 → 7/4 | s:bd cutoff:1000 resonance:20 ]", + "[ 7/4 → 15/8 | s:bd cutoff:1000 resonance:30 ]", + "[ 15/8 → 2/1 | s:bd cutoff:1000 resonance:30 ]", + "[ 2/1 → 17/8 | s:bd cutoff:1000 resonance:0 ]", + "[ 17/8 → 9/4 | s:bd cutoff:1000 resonance:0 ]", + "[ 9/4 → 19/8 | s:bd cutoff:1000 resonance:10 ]", + "[ 19/8 → 5/2 | s:bd cutoff:1000 resonance:10 ]", + "[ 5/2 → 21/8 | s:bd cutoff:1000 resonance:20 ]", + "[ 21/8 → 11/4 | s:bd cutoff:1000 resonance:20 ]", + "[ 11/4 → 23/8 | s:bd cutoff:1000 resonance:30 ]", + "[ 23/8 → 3/1 | s:bd cutoff:1000 resonance:30 ]", + "[ 3/1 → 25/8 | s:bd cutoff:1000 resonance:0 ]", + "[ 25/8 → 13/4 | s:bd cutoff:1000 resonance:0 ]", + "[ 13/4 → 27/8 | s:bd cutoff:1000 resonance:10 ]", + "[ 27/8 → 7/2 | s:bd cutoff:1000 resonance:10 ]", + "[ 7/2 → 29/8 | s:bd cutoff:1000 resonance:20 ]", + "[ 29/8 → 15/4 | s:bd cutoff:1000 resonance:20 ]", + "[ 15/4 → 31/8 | s:bd cutoff:1000 resonance:30 ]", + "[ 31/8 → 4/1 | s:bd cutoff:1000 resonance:30 ]", +] +`; + exports[`runs examples > example "lpq" example index 0 1`] = ` [ "[ 0/1 → 1/2 | s:bd cutoff:2000 resonance:0 ]", @@ -3231,6 +3310,19 @@ exports[`runs examples > example "room" example index 0 1`] = ` ] `; +exports[`runs examples > example "room" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.9 size:1 ]", + "[ 1/2 → 1/1 | s:sd room:0.9 size:1 ]", + "[ 1/1 → 3/2 | s:bd room:0.9 size:4 ]", + "[ 3/2 → 2/1 | s:sd room:0.9 size:4 ]", + "[ 2/1 → 5/2 | s:bd room:0.9 size:1 ]", + "[ 5/2 → 3/1 | s:sd room:0.9 size:1 ]", + "[ 3/1 → 7/2 | s:bd room:0.9 size:4 ]", + "[ 7/2 → 4/1 | s:sd room:0.9 size:4 ]", +] +`; + exports[`runs examples > example "roomsize" example index 0 1`] = ` [ "[ 0/1 → 1/2 | s:bd room:0.8 size:0 ]", @@ -3304,6 +3396,27 @@ exports[`runs examples > example "s" example index 0 1`] = ` ] `; +exports[`runs examples > example "s" example index 1 1`] = ` +[ + "[ 0/1 → 1/4 | s:bd n:0 ]", + "[ 1/4 → 1/2 | s:bd n:1 ]", + "[ 1/2 → 3/4 | s:bd n:0 gain:0.3 ]", + "[ 3/4 → 1/1 | s:bd n:1 gain:1.4 ]", + "[ 1/1 → 5/4 | s:bd n:0 ]", + "[ 5/4 → 3/2 | s:bd n:1 ]", + "[ 3/2 → 7/4 | s:bd n:0 gain:0.3 ]", + "[ 7/4 → 2/1 | s:bd n:1 gain:1.4 ]", + "[ 2/1 → 9/4 | s:bd n:0 ]", + "[ 9/4 → 5/2 | s:bd n:1 ]", + "[ 5/2 → 11/4 | s:bd n:0 gain:0.3 ]", + "[ 11/4 → 3/1 | s:bd n:1 gain:1.4 ]", + "[ 3/1 → 13/4 | s:bd n:0 ]", + "[ 13/4 → 7/2 | s:bd n:1 ]", + "[ 7/2 → 15/4 | s:bd n:0 gain:0.3 ]", + "[ 15/4 → 4/1 | s:bd n:1 gain:1.4 ]", +] +`; + exports[`runs examples > example "samples" example index 0 1`] = ` [ "[ 0/1 → 1/4 | s:bd ]", @@ -3454,6 +3567,43 @@ exports[`runs examples > example "scale" example index 1 1`] = ` ] `; +exports[`runs examples > example "scale" example index 2 1`] = ` +[ + "[ 7/8 → 1/1 | note:C2 s:folkharp ]", + "[ 3/4 → 7/8 | note:D2 s:folkharp ]", + "[ 5/8 → 3/4 | note:E2 s:folkharp ]", + "[ 1/2 → 5/8 | note:F2 s:folkharp ]", + "[ 3/8 → 1/2 | note:G2 s:folkharp ]", + "[ 1/4 → 3/8 | note:A2 s:folkharp ]", + "[ 1/8 → 1/4 | note:B2 s:folkharp ]", + "[ 0/1 → 1/8 | note:C3 s:folkharp ]", + "[ 15/8 → 2/1 | note:C2 s:folkharp ]", + "[ 7/4 → 15/8 | note:D2 s:folkharp ]", + "[ 13/8 → 7/4 | note:Eb2 s:folkharp ]", + "[ 3/2 → 13/8 | note:F2 s:folkharp ]", + "[ 11/8 → 3/2 | note:G2 s:folkharp ]", + "[ 5/4 → 11/8 | note:Ab2 s:folkharp ]", + "[ 9/8 → 5/4 | note:Bb2 s:folkharp ]", + "[ 1/1 → 9/8 | note:C3 s:folkharp ]", + "[ 23/8 → 3/1 | note:C2 s:folkharp ]", + "[ 11/4 → 23/8 | note:D2 s:folkharp ]", + "[ 21/8 → 11/4 | note:E2 s:folkharp ]", + "[ 5/2 → 21/8 | note:F2 s:folkharp ]", + "[ 19/8 → 5/2 | note:G2 s:folkharp ]", + "[ 9/4 → 19/8 | note:A2 s:folkharp ]", + "[ 17/8 → 9/4 | note:B2 s:folkharp ]", + "[ 2/1 → 17/8 | note:C3 s:folkharp ]", + "[ 31/8 → 4/1 | note:C2 s:folkharp ]", + "[ 15/4 → 31/8 | note:D2 s:folkharp ]", + "[ 29/8 → 15/4 | note:Eb2 s:folkharp ]", + "[ 7/2 → 29/8 | note:F2 s:folkharp ]", + "[ 27/8 → 7/2 | note:G2 s:folkharp ]", + "[ 13/4 → 27/8 | note:Ab2 s:folkharp ]", + "[ 25/8 → 13/4 | note:Bb2 s:folkharp ]", + "[ 3/1 → 25/8 | note:C3 s:folkharp ]", +] +`; + exports[`runs examples > example "scaleTranspose" example index 0 1`] = ` [ "[ 0/1 → 1/2 | note:C3 ]", diff --git a/test/__snapshots__/tunes.test.mjs.snap b/test/__snapshots__/tunes.test.mjs.snap index 5a0bb2db..0675938f 100644 --- a/test/__snapshots__/tunes.test.mjs.snap +++ b/test/__snapshots__/tunes.test.mjs.snap @@ -298,11 +298,11 @@ exports[`renders tunes > tune: bassFuge 1`] = ` "[ -3/4 ⇜ (0/1 → 3/4) ⇝ 5/4 | note:C5 s:flbass n:0 gain:0.3 cutoff:2924.3791043233605 resonance:10 clip:1 ]", "[ -3/4 ⇜ (3/4 → 1/1) ⇝ 5/4 | note:A4 s:flbass n:0 gain:0.3 cutoff:2924.3791043233605 resonance:10 clip:1 ]", "[ -3/4 ⇜ (3/4 → 1/1) ⇝ 5/4 | note:C5 s:flbass n:0 gain:0.3 cutoff:2924.3791043233605 resonance:10 clip:1 ]", - "[ 0/1 → 1/2 | s:bd:1 ]", - "[ 1/2 → 1/1 | s:bd:1 ]", - "[ 1/2 → 1/1 | s:sd:0 ]", - "[ 1/4 → 1/2 | s:hh:0 ]", - "[ 3/4 → 1/1 | s:hh:0 ]", + "[ 0/1 → 1/2 | s:bd n:1 ]", + "[ 1/2 → 1/1 | s:bd n:1 ]", + "[ 1/2 → 1/1 | s:sd n:0 ]", + "[ 1/4 → 1/2 | s:hh n:0 ]", + "[ 3/4 → 1/1 | s:hh n:0 ]", ] `; @@ -313,7 +313,7 @@ exports[`renders tunes > tune: belldub 1`] = ` "[ (5/8 → 1/1) ⇝ 5/4 | s:hh room:0 end:0.04483079938329212 ]", "[ 0/1 → 5/16 | s:mt gain:0.5 room:0.5 ]", "[ (15/16 → 1/1) ⇝ 5/4 | s:lt gain:0.5 room:0.5 ]", - "[ (0/1 → 1/1) ⇝ 5/1 | s:misc:2 speed:1 delay:0.5 delaytime:0.3333333333333333 gain:0.4 ]", + "[ (0/1 → 1/1) ⇝ 5/1 | s:misc n:2 speed:1 delay:0.5 delaytime:0.3333333333333333 gain:0.4 ]", "[ (5/8 → 1/1) ⇝ 5/4 | note:F3 s:sawtooth gain:0.5 cutoff:400.16785462816676 decay:0.05380063255866716 sustain:0 delay:0.9 room:1 ]", "[ (5/8 → 1/1) ⇝ 5/4 | note:A3 s:sawtooth gain:0.5 cutoff:400.16785462816676 decay:0.05380063255866716 sustain:0 delay:0.9 room:1 ]", "[ (5/8 → 1/1) ⇝ 5/4 | note:Bb3 s:sawtooth gain:0.5 cutoff:400.16785462816676 decay:0.05380063255866716 sustain:0 delay:0.9 room:1 ]", @@ -6974,16 +6974,16 @@ exports[`renders tunes > tune: flatrave 1`] = ` "[ 1/2 → 1/1 | s:bd bank:RolandTR909 ]", "[ 1/2 → 1/1 | s:cp bank:RolandTR909 ]", "[ 1/2 → 1/1 | s:sd bank:RolandTR909 ]", - "[ 0/1 → 1/4 | s:hh:1 end:0.02000058072071123 bank:RolandTR909 room:0.5 gain:0.4 ]", - "[ 0/1 ⇜ (1/8 → 1/4) | s:hh:1 end:0.02000058072071123 bank:RolandTR909 room:0.5 gain:0.4 ]", - "[ 1/4 → 3/8 | s:hh:1 end:0.02000875429921906 bank:RolandTR909 room:0.5 gain:0.4 ]", - "[ 1/4 → 3/8 | s:hh:1 end:0.02000875429921906 bank:RolandTR909 room:0.5 gain:0.4 ]", - "[ 3/8 → 1/2 | s:hh:1 end:0.020023446730265706 bank:RolandTR909 room:0.5 gain:0.4 ]", - "[ 5/8 → 3/4 | s:hh:1 end:0.020086608138500644 bank:RolandTR909 room:0.5 gain:0.4 ]", - "[ 5/8 → 3/4 | s:hh:1 end:0.020086608138500644 bank:RolandTR909 room:0.5 gain:0.4 ]", - "[ 3/4 → 7/8 | s:hh:1 end:0.02013941880355398 bank:RolandTR909 room:0.5 gain:0.4 ]", - "[ 1/8 → 1/4 | s:hh:1 speed:0.5 delay:0.5 end:0.020001936784171157 bank:RolandTR909 room:0.5 gain:0.4 ]", - "[ 1/8 → 1/4 | s:hh:1 speed:0.5 delay:0.5 end:0.020001936784171157 bank:RolandTR909 room:0.5 gain:0.4 ]", + "[ 0/1 → 1/4 | s:hh n:1 end:0.02000058072071123 bank:RolandTR909 room:0.5 gain:0.4 ]", + "[ 0/1 ⇜ (1/8 → 1/4) | s:hh n:1 end:0.02000058072071123 bank:RolandTR909 room:0.5 gain:0.4 ]", + "[ 1/4 → 3/8 | s:hh n:1 end:0.02000875429921906 bank:RolandTR909 room:0.5 gain:0.4 ]", + "[ 1/4 → 3/8 | s:hh n:1 end:0.02000875429921906 bank:RolandTR909 room:0.5 gain:0.4 ]", + "[ 3/8 → 1/2 | s:hh n:1 end:0.020023446730265706 bank:RolandTR909 room:0.5 gain:0.4 ]", + "[ 5/8 → 3/4 | s:hh n:1 end:0.020086608138500644 bank:RolandTR909 room:0.5 gain:0.4 ]", + "[ 5/8 → 3/4 | s:hh n:1 end:0.020086608138500644 bank:RolandTR909 room:0.5 gain:0.4 ]", + "[ 3/4 → 7/8 | s:hh n:1 end:0.02013941880355398 bank:RolandTR909 room:0.5 gain:0.4 ]", + "[ 1/8 → 1/4 | s:hh n:1 speed:0.5 delay:0.5 end:0.020001936784171157 bank:RolandTR909 room:0.5 gain:0.4 ]", + "[ 1/8 → 1/4 | s:hh n:1 speed:0.5 delay:0.5 end:0.020001936784171157 bank:RolandTR909 room:0.5 gain:0.4 ]", "[ 1/8 → 1/4 | note:G1 s:sawtooth decay:0.1 sustain:0 ]", "[ 1/4 → 3/8 | note:G1 s:sawtooth decay:0.1 sustain:0 ]", "[ 1/2 → 5/8 | note:G1 s:sawtooth decay:0.1 sustain:0 ]", @@ -8127,8 +8127,8 @@ exports[`renders tunes > tune: loungeSponge 1`] = ` exports[`renders tunes > tune: meltingsubmarine 1`] = ` [ - "[ (0/1 → 1/1) ⇝ 3/2 | s:bd:5 speed:0.7519542165100574 ]", - "[ (3/4 → 1/1) ⇝ 3/2 | s:sd:1 speed:0.7931522866332671 ]", + "[ (0/1 → 1/1) ⇝ 3/2 | s:bd n:5 speed:0.7519542165100574 ]", + "[ (3/4 → 1/1) ⇝ 3/2 | s:sd n:1 speed:0.7931522866332671 ]", "[ 3/8 → 3/4 | s:hh27 speed:0.7285963821098448 ]", "[ (3/4 → 1/1) ⇝ 9/8 | s:hh27 speed:0.77531205091027 ]", "[ (0/1 → 1/1) ⇝ 3/2 | note:33.129885541275144 decay:0.15 sustain:0 s:sawtooth gain:0.4 cutoff:3669.6267869262615 ]", @@ -8364,15 +8364,15 @@ exports[`renders tunes > tune: randomBells 1`] = ` exports[`renders tunes > tune: sampleDemo 1`] = ` [ - "[ 0/1 → 1/4 | s:woodblock:1 ]", - "[ 1/4 → 3/8 | s:woodblock:2 ]", - "[ 0/1 → 1/8 | s:brakedrum:1 ]", - "[ 3/4 → 7/8 | s:brakedrum:1 ]", - "[ 3/8 → 1/2 | s:woodblock:2 speed:2 ]", - "[ 1/2 → 1/1 | s:snare_rim:0 speed:2 ]", + "[ 0/1 → 1/4 | s:woodblock n:1 ]", + "[ 1/4 → 3/8 | s:woodblock n:2 ]", + "[ 0/1 → 1/8 | s:brakedrum n:1 ]", + "[ 3/4 → 7/8 | s:brakedrum n:1 ]", + "[ 3/8 → 1/2 | s:woodblock n:2 speed:2 ]", + "[ 1/2 → 1/1 | s:snare_rim n:0 speed:2 ]", "[ (0/1 → 1/1) ⇝ 8/1 | s:gong speed:2 ]", - "[ 3/8 → 1/2 | s:brakedrum:1 speed:2 ]", - "[ 3/4 → 1/1 | s:cowbell:3 speed:2 ]", + "[ 3/8 → 1/2 | s:brakedrum n:1 speed:2 ]", + "[ 3/4 → 1/1 | s:cowbell n:3 speed:2 ]", "[ -3/4 ⇜ (0/1 → 1/4) | note:Bb3 s:clavisynth gain:0.2 delay:0.25 pan:0 ]", "[ (3/4 → 1/1) ⇝ 7/4 | note:Bb3 s:clavisynth gain:0.2 delay:0.25 pan:1 ]", "[ -1/4 ⇜ (0/1 → 3/4) | note:F3 s:clavisynth gain:0.2 delay:0.25 pan:1 ]",