Stepwise functions from Tidal (#1060)

* rename new stepwise functions to match tidal, adding s_expand and s_contract
* created a `stepJoin` for stepwise patternification
This commit is contained in:
Alex McLean 2024-04-21 21:17:07 +01:00 committed by GitHub
parent a194180130
commit 0a3694fb82
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 344 additions and 78 deletions

View File

@ -47,6 +47,10 @@ Fraction.prototype.eq = function (other) {
return this.compare(other) == 0; return this.compare(other) == 0;
}; };
Fraction.prototype.ne = function (other) {
return this.compare(other) != 0;
};
Fraction.prototype.max = function (other) { Fraction.prototype.max = function (other) {
return this.gt(other) ? this : other; return this.gt(other) ? this : other;
}; };

View File

@ -10,7 +10,18 @@ import Hap from './hap.mjs';
import State from './state.mjs'; import State from './state.mjs';
import { unionWithObj } from './value.mjs'; import { unionWithObj } from './value.mjs';
import { compose, removeUndefineds, flatten, id, listRange, curry, _mod, numeralArgs, parseNumeral } from './util.mjs'; import {
uniqsortr,
removeUndefineds,
flatten,
id,
listRange,
curry,
_mod,
numeralArgs,
parseNumeral,
pairs,
} from './util.mjs';
import drawLine from './drawLine.mjs'; import drawLine from './drawLine.mjs';
import { logger } from './logger.mjs'; import { logger } from './logger.mjs';
@ -48,6 +59,10 @@ export class Pattern {
return this; return this;
} }
withTactus(f) {
return new Pattern(this.query, this.tactus === undefined ? undefined : f(this.tactus));
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Haskell-style functor, applicative and monadic operations // Haskell-style functor, applicative and monadic operations
@ -1137,24 +1152,24 @@ function _composeOp(a, b, func) {
export const polyrhythm = stack; export const polyrhythm = stack;
export const pr = stack; export const pr = stack;
export const pm = polymeter; export const pm = s_polymeter;
// methods that create patterns, which are added to patternified Pattern methods // methods that create patterns, which are added to patternified Pattern methods
// TODO: remove? this is only used in old transpiler (shapeshifter) // TODO: remove? this is only used in old transpiler (shapeshifter)
Pattern.prototype.factories = { // Pattern.prototype.factories = {
pure, // pure,
stack, // stack,
slowcat, // slowcat,
fastcat, // fastcat,
cat, // cat,
timecat, // timecat,
sequence, // sequence,
seq, // seq,
polymeter, // polymeter,
pm, // pm,
polyrhythm, // polyrhythm,
pr, // pr,
}; // };
// the magic happens in Pattern constructor. Keeping this in prototype enables adding methods from the outside (e.g. see tonal.ts) // the magic happens in Pattern constructor. Keeping this in prototype enables adding methods from the outside (e.g. see tonal.ts)
// Elemental patterns // Elemental patterns
@ -1254,14 +1269,14 @@ function _stackWith(func, pats) {
export function stackLeft(...pats) { export function stackLeft(...pats) {
return _stackWith( return _stackWith(
(tactus, pats) => pats.map((pat) => (pat.tactus.eq(tactus) ? pat : timecat(pat, gap(tactus.sub(pat.tactus))))), (tactus, pats) => pats.map((pat) => (pat.tactus.eq(tactus) ? pat : s_cat(pat, gap(tactus.sub(pat.tactus))))),
pats, pats,
); );
} }
export function stackRight(...pats) { export function stackRight(...pats) {
return _stackWith( return _stackWith(
(tactus, pats) => pats.map((pat) => (pat.tactus.eq(tactus) ? pat : timecat(gap(tactus.sub(pat.tactus)), pat))), (tactus, pats) => pats.map((pat) => (pat.tactus.eq(tactus) ? pat : s_cat(gap(tactus.sub(pat.tactus)), pat))),
pats, pats,
); );
} }
@ -1274,7 +1289,7 @@ export function stackCentre(...pats) {
return pat; return pat;
} }
const g = gap(tactus.sub(pat.tactus).div(2)); const g = gap(tactus.sub(pat.tactus).div(2));
return timecat(g, pat, g); return s_cat(g, pat, g);
}), }),
pats, pats,
); );
@ -1288,7 +1303,7 @@ export function stackBy(by, ...pats) {
left: stackLeft, left: stackLeft,
right: stackRight, right: stackRight,
expand: stack, expand: stack,
repeat: (...args) => polymeterSteps(tactus, ...args), repeat: (...args) => s_polymeterSteps(tactus, ...args),
}; };
return by return by
.inhabit(lookup) .inhabit(lookup)
@ -1374,7 +1389,7 @@ export function cat(...pats) {
export function arrange(...sections) { export function arrange(...sections) {
const total = sections.reduce((sum, [cycles]) => sum + cycles, 0); const total = sections.reduce((sum, [cycles]) => sum + cycles, 0);
sections = sections.map(([cycles, section]) => [cycles, section.fast(cycles)]); sections = sections.map(([cycles, section]) => [cycles, section.fast(cycles)]);
return timecat(...sections).slow(total); return s_cat(...sections).slow(total);
} }
export function fastcat(...pats) { export function fastcat(...pats) {
@ -1454,7 +1469,7 @@ export const func = curry((a, b) => reify(b).func(a));
* @noAutocomplete * @noAutocomplete
* *
*/ */
export function register(name, func, patternify = true, preserveTactus = false) { export function register(name, func, patternify = true, preserveTactus = false, join = (x) => x.innerJoin()) {
if (Array.isArray(name)) { if (Array.isArray(name)) {
const result = {}; const result = {};
for (const name_item of name) { for (const name_item of name) {
@ -1491,7 +1506,7 @@ export function register(name, func, patternify = true, preserveTactus = false)
return func(...args, pat); return func(...args, pat);
}; };
mapFn = curry(mapFn, null, arity - 1); mapFn = curry(mapFn, null, arity - 1);
result = right.reduce((acc, p) => acc.appLeft(p), left.fmap(mapFn)).innerJoin(); result = join(right.reduce((acc, p) => acc.appLeft(p), left.fmap(mapFn)));
} }
} }
if (preserveTactus) { if (preserveTactus) {
@ -1535,6 +1550,11 @@ export function register(name, func, patternify = true, preserveTactus = false)
return curry(pfunc, null, arity); return curry(pfunc, null, arity);
} }
// Like register, but defaults to stepJoin
function stepRegister(name, func, patternify = true, preserveTactus = false, join = (x) => x.stepJoin()) {
return register(name, func, patternify, preserveTactus, join);
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Numerical transformations // Numerical transformations
@ -2375,13 +2395,62 @@ Pattern.prototype.tag = function (tag) {
// Tactus-related functions, i.e. ones that do stepwise // Tactus-related functions, i.e. ones that do stepwise
// transformations // transformations
Pattern.prototype.stepJoin = function () {
const pp = this;
const first_t = s_cat(..._retime(_slices(pp.queryArc(0, 1)))).tactus;
const q = function (state) {
const shifted = pp.early(state.span.begin.sam());
const haps = shifted.query(state.setSpan(new TimeSpan(Fraction(0), Fraction(1))));
const pat = s_cat(..._retime(_slices(haps)));
return pat.query(state);
};
return new Pattern(q, first_t);
};
export function _retime(timedHaps) {
const occupied_perc = timedHaps.filter((t, pat) => pat.tactus != undefined).reduce((a, b) => a.add(b), Fraction(0));
const occupied_tactus = removeUndefineds(timedHaps.map((t, pat) => pat.tactus)).reduce(
(a, b) => a.add(b),
Fraction(0),
);
const total_tactus = occupied_perc.eq(0) ? 0 : occupied_tactus.div(occupied_perc);
function adjust(dur, pat) {
if (pat.tactus === undefined) {
return [dur.mul(total_tactus), pat];
}
return [pat.tactus, pat];
}
return timedHaps.map((x) => adjust(...x));
}
export function _slices(haps) {
// slices evs = map (\s -> ((snd s - fst s), stack $ map value $ fit s evs))
// $ pairs $ sort $ nubOrd $ 0:1:concatMap (\ev -> start (part ev):stop (part ev):[]) evs
const breakpoints = flatten(haps.map((hap) => [hap.part.begin, hap.part.end]));
const unique = uniqsortr([Fraction(0), Fraction(1), ...breakpoints]);
const slicespans = pairs(unique);
return slicespans.map((s) => [s[1].sub(s[0]), stack(..._fitslice(new TimeSpan(...s), haps).map((x) => x.value))]);
}
export function _fitslice(span, haps) {
return removeUndefineds(haps.map((hap) => _match(span, hap)));
}
export function _match(span, hap_p) {
const subspan = span.intersection(hap_p.part);
if (subspan == undefined) {
return undefined;
}
return new Hap(hap_p.whole, subspan, hap_p.value, hap_p.context);
}
/** /**
* *EXPERIMENTAL* - Speeds a pattern up or down, to fit to the given metrical 'tactus'. * *EXPERIMENTAL* - Speeds a pattern up or down, to fit to the given number of steps per cycle (aka tactus).
* @example * @example
* s("bd sd cp").toTactus(4) * s("bd sd cp").steps(4)
* // The same as s("{bd sd cp}%4") * // The same as s("{bd sd cp}%4")
*/ */
export const toTactus = register('toTactus', function (targetTactus, pat) { export const steps = register('steps', function (targetTactus, pat) {
if (pat.tactus.eq(0)) { if (pat.tactus.eq(0)) {
// avoid divide by zero.. // avoid divide by zero..
return nothing; return nothing;
@ -2415,14 +2484,14 @@ export function _polymeterListSteps(steps, ...args) {
* Aligns one or more given patterns to the given number of steps per cycle. * Aligns one or more given patterns to the given number of steps per cycle.
* This relies on patterns having coherent number of steps per cycle, * This relies on patterns having coherent number of steps per cycle,
* *
* @name polymeterSteps * @name s_polymeterSteps
* @param {number} steps how many items are placed in one cycle * @param {number} steps how many items are placed in one cycle
* @param {any[]} patterns one or more patterns * @param {any[]} patterns one or more patterns
* @example * @example
* // the same as "{c d, e f g}%4" * // the same as "{c d, e f g}%4"
* polymeterSteps(4, "c d", "e f g") * s_polymeterSteps(4, "c d", "e f g")
*/ */
export function polymeterSteps(steps, ...args) { export function s_polymeterSteps(steps, ...args) {
if (args.length == 0) { if (args.length == 0) {
return silence; return silence;
} }
@ -2431,7 +2500,7 @@ export function polymeterSteps(steps, ...args) {
return _polymeterListSteps(steps, ...args); return _polymeterListSteps(steps, ...args);
} }
return polymeter(...args).toTactus(steps); return s_polymeter(...args).steps(steps);
} }
/** /**
@ -2439,10 +2508,10 @@ export function polymeterSteps(steps, ...args) {
* @synonyms pm * @synonyms pm
* @example * @example
* // The same as "{c eb g, c2 g2}" * // The same as "{c eb g, c2 g2}"
* polymeter("c eb g", "c2 g2") * s_polymeter("c eb g", "c2 g2")
* *
*/ */
export function polymeter(...args) { export function s_polymeter(...args) {
if (Array.isArray(args[0])) { if (Array.isArray(args[0])) {
// Support old behaviour // Support old behaviour
return _polymeterListSteps(0, ...args); return _polymeterListSteps(0, ...args);
@ -2461,16 +2530,16 @@ export function polymeter(...args) {
/** Sequences patterns like `seq`, but each pattern has a length, relative to the whole. /** Sequences patterns like `seq`, but each pattern has a length, relative to the whole.
* This length can either be provided as a [length, pattern] pair, or inferred from * This length can either be provided as a [length, pattern] pair, or inferred from
* the pattern's 'tactus', generally inferred by the mininotation. * the pattern's 'tactus', generally inferred by the mininotation. Has the alias `timecat`.
* @return {Pattern} * @return {Pattern}
* @example * @example
* timecat([3,"e3"],[1, "g3"]).note() * s_cat([3,"e3"],[1, "g3"]).note()
* // the same as "e3@3 g3".note() * // the same as "e3@3 g3".note()
* @example * @example
* timecat("bd sd cp","hh hh").sound() * s_cat("bd sd cp","hh hh").sound()
* // the same as "bd sd cp hh hh".sound() * // the same as "bd sd cp hh hh".sound()
*/ */
export function timecat(...timepats) { export function s_cat(...timepats) {
const findtactus = (x) => (Array.isArray(x) ? x : [x.tactus, x]); const findtactus = (x) => (Array.isArray(x) ? x : [x.tactus, x]);
timepats = timepats.map(findtactus); timepats = timepats.map(findtactus);
if (timepats.length == 1) { if (timepats.length == 1) {
@ -2495,18 +2564,19 @@ export function timecat(...timepats) {
return result; return result;
} }
/** Deprecated alias for `timecat` */ /** Aliases for `s_cat` */
export const timeCat = timecat; export const timecat = s_cat;
export const timeCat = s_cat;
/** /**
* *EXPERIMENTAL* - Concatenates patterns stepwise, according to their 'tactus'. * *EXPERIMENTAL* - Concatenates patterns stepwise, according to their 'tactus'.
* Similar to `timecat`, but if an argument is a list, the whole pattern will be repeated for each element in the list. * Similar to `s_cat`, but if an argument is a list, the whole pattern will alternate between the elements in the list.
* *
* @return {Pattern} * @return {Pattern}
* @example * @example
* stepcat(["bd cp", "mt"], "bd").sound() * s_alt(["bd cp", "mt"], "bd").sound()
*/ */
export function stepcat(...groups) { export function s_alt(...groups) {
groups = groups.map((a) => (Array.isArray(a) ? a.map(reify) : [reify(a)])); groups = groups.map((a) => (Array.isArray(a) ? a.map(reify) : [reify(a)]));
const cycles = lcm(...groups.map((x) => Fraction(x.length))); const cycles = lcm(...groups.map((x) => Fraction(x.length)));
@ -2517,7 +2587,7 @@ export function stepcat(...groups) {
} }
result = result.filter((x) => x.tactus > 0); result = result.filter((x) => x.tactus > 0);
const tactus = result.reduce((a, b) => a.add(b.tactus), Fraction(0)); const tactus = result.reduce((a, b) => a.add(b.tactus), Fraction(0));
result = timecat(...result); result = s_cat(...result);
result.tactus = tactus; result.tactus = tactus;
return result; return result;
} }
@ -2525,7 +2595,7 @@ export function stepcat(...groups) {
/** /**
* *EXPERIMENTAL* - Retains the given number of steps in a pattern (and dropping the rest), according to its 'tactus'. * *EXPERIMENTAL* - Retains the given number of steps in a pattern (and dropping the rest), according to its 'tactus'.
*/ */
export const stepwax = register('stepwax', function (i, pat) { export const s_add = stepRegister('s_add', function (i, pat) {
if (pat.tactus.lte(0)) { if (pat.tactus.lte(0)) {
return nothing; return nothing;
} }
@ -2553,18 +2623,26 @@ export const stepwax = register('stepwax', function (i, pat) {
/** /**
* *EXPERIMENTAL* - Removes the given number of steps from a pattern, according to its 'tactus'. * *EXPERIMENTAL* - Removes the given number of steps from a pattern, according to its 'tactus'.
*/ */
export const stepwane = register('stepwane', function (i, pat) { export const s_sub = stepRegister('s_sub', function (i, pat) {
i = Fraction(i); i = Fraction(i);
if (i.lt(0)) { if (i.lt(0)) {
return pat.stepwax(Fraction(0).sub(pat.tactus.add(i))); return pat.s_add(Fraction(0).sub(pat.tactus.add(i)));
} }
return pat.stepwax(pat.tactus.sub(i)); return pat.s_add(pat.tactus.sub(i));
});
export const s_expand = stepRegister('s_expand', function (factor, pat) {
return pat.withTactus((t) => t.mul(Fraction(factor)));
});
export const s_contract = stepRegister('s_contract', function (factor, pat) {
return pat.withTactus((t) => t.div(Fraction(factor)));
}); });
/** /**
* *EXPERIMENTAL* * *EXPERIMENTAL*
*/ */
Pattern.prototype.taperlist = function (amount, times) { Pattern.prototype.s_taperlist = function (amount, times) {
const pat = this; const pat = this;
times = times - 1; times = times - 1;
@ -2586,23 +2664,29 @@ Pattern.prototype.taperlist = function (amount, times) {
} }
return list; return list;
}; };
export const taperlist = (amount, times, pat) => pat.taperlist(amount, times); export const s_taperlist = (amount, times, pat) => pat.s_taperlist(amount, times);
/** /**
* *EXPERIMENTAL* * *EXPERIMENTAL*
*/ */
export const steptaper = register('steptaper', function (amount, times, pat) { export const s_taper = register(
const list = pat.taperlist(amount, times); 's_taper',
const result = timecat(...list); function (amount, times, pat) {
result.tactus = list.reduce((a, b) => a.add(b.tactus), Fraction(0)); const list = pat.s_taperlist(amount, times);
return result; const result = s_cat(...list);
}); result.tactus = list.reduce((a, b) => a.add(b.tactus), Fraction(0));
return result;
},
true,
false,
(x) => x.stepJoin(),
);
/** /**
* *EXPERIMENTAL* * *EXPERIMENTAL*
*/ */
Pattern.prototype.steptour = function (...many) { Pattern.prototype.s_tour = function (...many) {
return stepcat( return s_cat(
...[].concat( ...[].concat(
...many.map((x, i) => [...many.slice(0, many.length - i), this, ...many.slice(many.length - i)]), ...many.map((x, i) => [...many.slice(0, many.length - i), this, ...many.slice(many.length - i)]),
this, this,
@ -2611,8 +2695,8 @@ Pattern.prototype.steptour = function (...many) {
); );
}; };
export const steptour = function (pat, ...many) { export const s_tour = function (pat, ...many) {
return pat.steptour(...many); return pat.s_tour(...many);
}; };
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View File

@ -21,8 +21,8 @@ import {
cat, cat,
sequence, sequence,
palindrome, palindrome,
polymeter, s_polymeter,
polymeterSteps, s_polymeterSteps,
polyrhythm, polyrhythm,
silence, silence,
fast, fast,
@ -603,18 +603,18 @@ describe('Pattern', () => {
); );
}); });
}); });
describe('polymeter()', () => { describe('s_polymeter()', () => {
it('Can layer up cycles, stepwise, with lists', () => { it('Can layer up cycles, stepwise, with lists', () => {
expect(polymeterSteps(3, ['d', 'e']).firstCycle()).toStrictEqual( expect(s_polymeterSteps(3, ['d', 'e']).firstCycle()).toStrictEqual(
fastcat(pure('d'), pure('e'), pure('d')).firstCycle(), fastcat(pure('d'), pure('e'), pure('d')).firstCycle(),
); );
expect(polymeter(['a', 'b', 'c'], ['d', 'e']).fast(2).firstCycle()).toStrictEqual( expect(s_polymeter(['a', 'b', 'c'], ['d', 'e']).fast(2).firstCycle()).toStrictEqual(
stack(sequence('a', 'b', 'c', 'a', 'b', 'c'), sequence('d', 'e', 'd', 'e', 'd', 'e')).firstCycle(), stack(sequence('a', 'b', 'c', 'a', 'b', 'c'), sequence('d', 'e', 'd', 'e', 'd', 'e')).firstCycle(),
); );
}); });
it('Can layer up cycles, stepwise, with weighted patterns', () => { it('Can layer up cycles, stepwise, with weighted patterns', () => {
sameFirst(polymeterSteps(3, sequence('a', 'b')).fast(2), sequence('a', 'b', 'a', 'b', 'a', 'b')); sameFirst(s_polymeterSteps(3, sequence('a', 'b')).fast(2), sequence('a', 'b', 'a', 'b', 'a', 'b'));
}); });
}); });
@ -1138,28 +1138,26 @@ describe('Pattern', () => {
); );
}); });
}); });
describe('steptaper', () => { describe('s_taper', () => {
it('can taper', () => { it('can taper', () => {
expect(sameFirst(sequence(0, 1, 2, 3, 4).steptaper(1, 5), sequence(0, 1, 2, 3, 4, 0, 1, 2, 3, 0, 1, 2, 0, 1, 0))); expect(sameFirst(sequence(0, 1, 2, 3, 4).s_taper(1, 5), sequence(0, 1, 2, 3, 4, 0, 1, 2, 3, 0, 1, 2, 0, 1, 0)));
}); });
it('can taper backwards', () => { it('can taper backwards', () => {
expect( expect(sameFirst(sequence(0, 1, 2, 3, 4).s_taper(-1, 5), sequence(0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4)));
sameFirst(sequence(0, 1, 2, 3, 4).steptaper(-1, 5), sequence(0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4)),
);
}); });
}); });
describe('wax and wane, left', () => { describe('s_add and s_sub, left', () => {
it('can wax from the left', () => { it('can add from the left', () => {
expect(sameFirst(sequence(0, 1, 2, 3, 4).stepwax(2), sequence(0, 1))); expect(sameFirst(sequence(0, 1, 2, 3, 4).s_add(2), sequence(0, 1)));
}); });
it('can wane to the left', () => { it('can sub to the left', () => {
expect(sameFirst(sequence(0, 1, 2, 3, 4).stepwane(2), sequence(0, 1, 2))); expect(sameFirst(sequence(0, 1, 2, 3, 4).s_sub(2), sequence(0, 1, 2)));
}); });
it('can wax from the right', () => { it('can add from the right', () => {
expect(sameFirst(sequence(0, 1, 2, 3, 4).stepwax(-2), sequence(3, 4))); expect(sameFirst(sequence(0, 1, 2, 3, 4).s_add(-2), sequence(3, 4)));
}); });
it('can wane to the right', () => { it('can sub to the right', () => {
expect(sameFirst(sequence(0, 1, 2, 3, 4).stepwane(-2), sequence(2, 3, 4))); expect(sameFirst(sequence(0, 1, 2, 3, 4).s_sub(-2), sequence(2, 3, 4)));
}); });
}); });
}); });

View File

@ -231,6 +231,14 @@ export const splitAt = function (index, value) {
export const zipWith = (f, xs, ys) => xs.map((n, i) => f(n, ys[i])); export const zipWith = (f, xs, ys) => xs.map((n, i) => f(n, ys[i]));
export const pairs = function (xs) {
const result = [];
for (let i = 0; i < xs.length - 1; ++i) {
result.push([xs[i], xs[i + 1]]);
}
return result;
};
export const clamp = (num, min, max) => Math.min(Math.max(num, min), max); export const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
/* solmization, not used yet */ /* solmization, not used yet */
@ -289,6 +297,28 @@ export const sol2note = (n, notation = 'letters') => {
return note + oct; return note + oct;
}; };
// Remove duplicates from list
export function uniq(a) {
var seen = {};
return a.filter(function (item) {
return seen.hasOwn(item) ? false : (seen[item] = true);
});
}
// Remove duplicates from list, sorting in the process. Mutates argument!
export function uniqsort(a) {
return a.sort().filter(function (item, pos, ary) {
return !pos || item != ary[pos - 1];
});
}
// rational version
export function uniqsortr(a) {
return a.sort().filter(function (item, pos, ary) {
return !pos || item.ne(ary[pos - 1]);
});
}
// code hashing helpers // code hashing helpers
export function unicodeToBase64(text) { export function unicodeToBase64(text) {

View File

@ -5972,6 +5972,135 @@ exports[`runs examples > example "s" example index 1 1`] = `
] ]
`; `;
exports[`runs examples > example "s_alt" example index 0 1`] = `
[
"[ 0/1 → 1/5 | s:bd ]",
"[ 1/5 → 2/5 | s:cp ]",
"[ 2/5 → 3/5 | s:bd ]",
"[ 3/5 → 4/5 | s:mt ]",
"[ 4/5 → 1/1 | s:bd ]",
"[ 1/1 → 6/5 | s:bd ]",
"[ 6/5 → 7/5 | s:cp ]",
"[ 7/5 → 8/5 | s:bd ]",
"[ 8/5 → 9/5 | s:mt ]",
"[ 9/5 → 2/1 | s:bd ]",
"[ 2/1 → 11/5 | s:bd ]",
"[ 11/5 → 12/5 | s:cp ]",
"[ 12/5 → 13/5 | s:bd ]",
"[ 13/5 → 14/5 | s:mt ]",
"[ 14/5 → 3/1 | s:bd ]",
"[ 3/1 → 16/5 | s:bd ]",
"[ 16/5 → 17/5 | s:cp ]",
"[ 17/5 → 18/5 | s:bd ]",
"[ 18/5 → 19/5 | s:mt ]",
"[ 19/5 → 4/1 | s:bd ]",
]
`;
exports[`runs examples > example "s_cat" example index 0 1`] = `
[
"[ 0/1 → 3/4 | note:e3 ]",
"[ 3/4 → 1/1 | note:g3 ]",
"[ 1/1 → 7/4 | note:e3 ]",
"[ 7/4 → 2/1 | note:g3 ]",
"[ 2/1 → 11/4 | note:e3 ]",
"[ 11/4 → 3/1 | note:g3 ]",
"[ 3/1 → 15/4 | note:e3 ]",
"[ 15/4 → 4/1 | note:g3 ]",
]
`;
exports[`runs examples > example "s_cat" example index 1 1`] = `
[
"[ 0/1 → 1/5 | s:bd ]",
"[ 1/5 → 2/5 | s:sd ]",
"[ 2/5 → 3/5 | s:cp ]",
"[ 3/5 → 4/5 | s:hh ]",
"[ 4/5 → 1/1 | s:hh ]",
"[ 1/1 → 6/5 | s:bd ]",
"[ 6/5 → 7/5 | s:sd ]",
"[ 7/5 → 8/5 | s:cp ]",
"[ 8/5 → 9/5 | s:hh ]",
"[ 9/5 → 2/1 | s:hh ]",
"[ 2/1 → 11/5 | s:bd ]",
"[ 11/5 → 12/5 | s:sd ]",
"[ 12/5 → 13/5 | s:cp ]",
"[ 13/5 → 14/5 | s:hh ]",
"[ 14/5 → 3/1 | s:hh ]",
"[ 3/1 → 16/5 | s:bd ]",
"[ 16/5 → 17/5 | s:sd ]",
"[ 17/5 → 18/5 | s:cp ]",
"[ 18/5 → 19/5 | s:hh ]",
"[ 19/5 → 4/1 | s:hh ]",
]
`;
exports[`runs examples > example "s_polymeter" example index 0 1`] = `
[
"[ 0/1 → 1/3 | c ]",
"[ 0/1 → 1/3 | c2 ]",
"[ 1/3 → 2/3 | eb ]",
"[ 1/3 → 2/3 | g2 ]",
"[ 2/3 → 1/1 | g ]",
"[ 2/3 → 1/1 | c2 ]",
"[ 1/1 → 4/3 | c ]",
"[ 1/1 → 4/3 | g2 ]",
"[ 4/3 → 5/3 | eb ]",
"[ 4/3 → 5/3 | c2 ]",
"[ 5/3 → 2/1 | g ]",
"[ 5/3 → 2/1 | g2 ]",
"[ 2/1 → 7/3 | c ]",
"[ 2/1 → 7/3 | c2 ]",
"[ 7/3 → 8/3 | eb ]",
"[ 7/3 → 8/3 | g2 ]",
"[ 8/3 → 3/1 | g ]",
"[ 8/3 → 3/1 | c2 ]",
"[ 3/1 → 10/3 | c ]",
"[ 3/1 → 10/3 | g2 ]",
"[ 10/3 → 11/3 | eb ]",
"[ 10/3 → 11/3 | c2 ]",
"[ 11/3 → 4/1 | g ]",
"[ 11/3 → 4/1 | g2 ]",
]
`;
exports[`runs examples > example "s_polymeterSteps" example index 0 1`] = `
[
"[ 0/1 → 1/4 | c ]",
"[ 0/1 → 1/4 | e ]",
"[ 1/4 → 1/2 | d ]",
"[ 1/4 → 1/2 | f ]",
"[ 1/2 → 3/4 | c ]",
"[ 1/2 → 3/4 | g ]",
"[ 3/4 → 1/1 | d ]",
"[ 3/4 → 1/1 | e ]",
"[ 1/1 → 5/4 | c ]",
"[ 1/1 → 5/4 | f ]",
"[ 5/4 → 3/2 | d ]",
"[ 5/4 → 3/2 | g ]",
"[ 3/2 → 7/4 | c ]",
"[ 3/2 → 7/4 | e ]",
"[ 7/4 → 2/1 | d ]",
"[ 7/4 → 2/1 | f ]",
"[ 2/1 → 9/4 | c ]",
"[ 2/1 → 9/4 | g ]",
"[ 9/4 → 5/2 | d ]",
"[ 9/4 → 5/2 | e ]",
"[ 5/2 → 11/4 | c ]",
"[ 5/2 → 11/4 | f ]",
"[ 11/4 → 3/1 | d ]",
"[ 11/4 → 3/1 | g ]",
"[ 3/1 → 13/4 | c ]",
"[ 3/1 → 13/4 | e ]",
"[ 13/4 → 7/2 | d ]",
"[ 13/4 → 7/2 | f ]",
"[ 7/2 → 15/4 | c ]",
"[ 7/2 → 15/4 | g ]",
"[ 15/4 → 4/1 | d ]",
"[ 15/4 → 4/1 | e ]",
]
`;
exports[`runs examples > example "samples" example index 0 1`] = ` exports[`runs examples > example "samples" example index 0 1`] = `
[ [
"[ 0/1 → 1/4 | s:bd ]", "[ 0/1 → 1/4 | s:bd ]",
@ -7112,6 +7241,27 @@ exports[`runs examples > example "stepcat" example index 0 1`] = `
] ]
`; `;
exports[`runs examples > example "steps" example index 0 1`] = `
[
"[ 0/1 → 1/4 | s:bd ]",
"[ 1/4 → 1/2 | s:sd ]",
"[ 1/2 → 3/4 | s:cp ]",
"[ 3/4 → 1/1 | s:bd ]",
"[ 1/1 → 5/4 | s:sd ]",
"[ 5/4 → 3/2 | s:cp ]",
"[ 3/2 → 7/4 | s:bd ]",
"[ 7/4 → 2/1 | s:sd ]",
"[ 2/1 → 9/4 | s:cp ]",
"[ 9/4 → 5/2 | s:bd ]",
"[ 5/2 → 11/4 | s:sd ]",
"[ 11/4 → 3/1 | s:cp ]",
"[ 3/1 → 13/4 | s:bd ]",
"[ 13/4 → 7/2 | s:sd ]",
"[ 7/2 → 15/4 | s:cp ]",
"[ 15/4 → 4/1 | s:bd ]",
]
`;
exports[`runs examples > example "striate" example index 0 1`] = ` exports[`runs examples > example "striate" example index 0 1`] = `
[ [
"[ 0/1 → 1/6 | s:numbers n:0 begin:0 end:0.16666666666666666 ]", "[ 0/1 → 1/6 | s:numbers n:0 begin:0 end:0.16666666666666666 ]",