mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-11 13:48:40 +00:00
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:
parent
a194180130
commit
0a3694fb82
@ -47,6 +47,10 @@ Fraction.prototype.eq = function (other) {
|
||||
return this.compare(other) == 0;
|
||||
};
|
||||
|
||||
Fraction.prototype.ne = function (other) {
|
||||
return this.compare(other) != 0;
|
||||
};
|
||||
|
||||
Fraction.prototype.max = function (other) {
|
||||
return this.gt(other) ? this : other;
|
||||
};
|
||||
|
||||
@ -10,7 +10,18 @@ import Hap from './hap.mjs';
|
||||
import State from './state.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 { logger } from './logger.mjs';
|
||||
|
||||
@ -48,6 +59,10 @@ export class Pattern {
|
||||
return this;
|
||||
}
|
||||
|
||||
withTactus(f) {
|
||||
return new Pattern(this.query, this.tactus === undefined ? undefined : f(this.tactus));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Haskell-style functor, applicative and monadic operations
|
||||
|
||||
@ -1137,24 +1152,24 @@ function _composeOp(a, b, func) {
|
||||
export const polyrhythm = 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
|
||||
// TODO: remove? this is only used in old transpiler (shapeshifter)
|
||||
Pattern.prototype.factories = {
|
||||
pure,
|
||||
stack,
|
||||
slowcat,
|
||||
fastcat,
|
||||
cat,
|
||||
timecat,
|
||||
sequence,
|
||||
seq,
|
||||
polymeter,
|
||||
pm,
|
||||
polyrhythm,
|
||||
pr,
|
||||
};
|
||||
// Pattern.prototype.factories = {
|
||||
// pure,
|
||||
// stack,
|
||||
// slowcat,
|
||||
// fastcat,
|
||||
// cat,
|
||||
// timecat,
|
||||
// sequence,
|
||||
// seq,
|
||||
// polymeter,
|
||||
// pm,
|
||||
// polyrhythm,
|
||||
// pr,
|
||||
// };
|
||||
// the magic happens in Pattern constructor. Keeping this in prototype enables adding methods from the outside (e.g. see tonal.ts)
|
||||
|
||||
// Elemental patterns
|
||||
@ -1254,14 +1269,14 @@ function _stackWith(func, pats) {
|
||||
|
||||
export function stackLeft(...pats) {
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
export function stackRight(...pats) {
|
||||
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,
|
||||
);
|
||||
}
|
||||
@ -1274,7 +1289,7 @@ export function stackCentre(...pats) {
|
||||
return pat;
|
||||
}
|
||||
const g = gap(tactus.sub(pat.tactus).div(2));
|
||||
return timecat(g, pat, g);
|
||||
return s_cat(g, pat, g);
|
||||
}),
|
||||
pats,
|
||||
);
|
||||
@ -1288,7 +1303,7 @@ export function stackBy(by, ...pats) {
|
||||
left: stackLeft,
|
||||
right: stackRight,
|
||||
expand: stack,
|
||||
repeat: (...args) => polymeterSteps(tactus, ...args),
|
||||
repeat: (...args) => s_polymeterSteps(tactus, ...args),
|
||||
};
|
||||
return by
|
||||
.inhabit(lookup)
|
||||
@ -1374,7 +1389,7 @@ export function cat(...pats) {
|
||||
export function arrange(...sections) {
|
||||
const total = sections.reduce((sum, [cycles]) => sum + cycles, 0);
|
||||
sections = sections.map(([cycles, section]) => [cycles, section.fast(cycles)]);
|
||||
return timecat(...sections).slow(total);
|
||||
return s_cat(...sections).slow(total);
|
||||
}
|
||||
|
||||
export function fastcat(...pats) {
|
||||
@ -1454,7 +1469,7 @@ export const func = curry((a, b) => reify(b).func(a));
|
||||
* @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)) {
|
||||
const result = {};
|
||||
for (const name_item of name) {
|
||||
@ -1491,7 +1506,7 @@ export function register(name, func, patternify = true, preserveTactus = false)
|
||||
return func(...args, pat);
|
||||
};
|
||||
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) {
|
||||
@ -1535,6 +1550,11 @@ export function register(name, func, patternify = true, preserveTactus = false)
|
||||
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
|
||||
|
||||
@ -2375,13 +2395,62 @@ Pattern.prototype.tag = function (tag) {
|
||||
// Tactus-related functions, i.e. ones that do stepwise
|
||||
// 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
|
||||
* s("bd sd cp").toTactus(4)
|
||||
* s("bd sd cp").steps(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)) {
|
||||
// avoid divide by zero..
|
||||
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.
|
||||
* 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 {any[]} patterns one or more patterns
|
||||
* @example
|
||||
* // 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) {
|
||||
return silence;
|
||||
}
|
||||
@ -2431,7 +2500,7 @@ export function polymeterSteps(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
|
||||
* @example
|
||||
* // 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])) {
|
||||
// Support old behaviour
|
||||
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.
|
||||
* 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}
|
||||
* @example
|
||||
* timecat([3,"e3"],[1, "g3"]).note()
|
||||
* s_cat([3,"e3"],[1, "g3"]).note()
|
||||
* // the same as "e3@3 g3".note()
|
||||
* @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()
|
||||
*/
|
||||
export function timecat(...timepats) {
|
||||
export function s_cat(...timepats) {
|
||||
const findtactus = (x) => (Array.isArray(x) ? x : [x.tactus, x]);
|
||||
timepats = timepats.map(findtactus);
|
||||
if (timepats.length == 1) {
|
||||
@ -2495,18 +2564,19 @@ export function timecat(...timepats) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Deprecated alias for `timecat` */
|
||||
export const timeCat = timecat;
|
||||
/** Aliases for `s_cat` */
|
||||
export const timecat = s_cat;
|
||||
export const timeCat = s_cat;
|
||||
|
||||
/**
|
||||
* *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}
|
||||
* @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)]));
|
||||
|
||||
const cycles = lcm(...groups.map((x) => Fraction(x.length)));
|
||||
@ -2517,7 +2587,7 @@ export function stepcat(...groups) {
|
||||
}
|
||||
result = result.filter((x) => x.tactus > 0);
|
||||
const tactus = result.reduce((a, b) => a.add(b.tactus), Fraction(0));
|
||||
result = timecat(...result);
|
||||
result = s_cat(...result);
|
||||
result.tactus = tactus;
|
||||
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'.
|
||||
*/
|
||||
export const stepwax = register('stepwax', function (i, pat) {
|
||||
export const s_add = stepRegister('s_add', function (i, pat) {
|
||||
if (pat.tactus.lte(0)) {
|
||||
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'.
|
||||
*/
|
||||
export const stepwane = register('stepwane', function (i, pat) {
|
||||
export const s_sub = stepRegister('s_sub', function (i, pat) {
|
||||
i = Fraction(i);
|
||||
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*
|
||||
*/
|
||||
Pattern.prototype.taperlist = function (amount, times) {
|
||||
Pattern.prototype.s_taperlist = function (amount, times) {
|
||||
const pat = this;
|
||||
times = times - 1;
|
||||
|
||||
@ -2586,23 +2664,29 @@ Pattern.prototype.taperlist = function (amount, times) {
|
||||
}
|
||||
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*
|
||||
*/
|
||||
export const steptaper = register('steptaper', function (amount, times, pat) {
|
||||
const list = pat.taperlist(amount, times);
|
||||
const result = timecat(...list);
|
||||
result.tactus = list.reduce((a, b) => a.add(b.tactus), Fraction(0));
|
||||
return result;
|
||||
});
|
||||
export const s_taper = register(
|
||||
's_taper',
|
||||
function (amount, times, pat) {
|
||||
const list = pat.s_taperlist(amount, times);
|
||||
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*
|
||||
*/
|
||||
Pattern.prototype.steptour = function (...many) {
|
||||
return stepcat(
|
||||
Pattern.prototype.s_tour = function (...many) {
|
||||
return s_cat(
|
||||
...[].concat(
|
||||
...many.map((x, i) => [...many.slice(0, many.length - i), this, ...many.slice(many.length - i)]),
|
||||
this,
|
||||
@ -2611,8 +2695,8 @@ Pattern.prototype.steptour = function (...many) {
|
||||
);
|
||||
};
|
||||
|
||||
export const steptour = function (pat, ...many) {
|
||||
return pat.steptour(...many);
|
||||
export const s_tour = function (pat, ...many) {
|
||||
return pat.s_tour(...many);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -21,8 +21,8 @@ import {
|
||||
cat,
|
||||
sequence,
|
||||
palindrome,
|
||||
polymeter,
|
||||
polymeterSteps,
|
||||
s_polymeter,
|
||||
s_polymeterSteps,
|
||||
polyrhythm,
|
||||
silence,
|
||||
fast,
|
||||
@ -603,18 +603,18 @@ describe('Pattern', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('polymeter()', () => {
|
||||
describe('s_polymeter()', () => {
|
||||
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(),
|
||||
);
|
||||
|
||||
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(),
|
||||
);
|
||||
});
|
||||
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', () => {
|
||||
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', () => {
|
||||
expect(
|
||||
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)),
|
||||
);
|
||||
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)));
|
||||
});
|
||||
});
|
||||
describe('wax and wane, left', () => {
|
||||
it('can wax from the left', () => {
|
||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).stepwax(2), sequence(0, 1)));
|
||||
describe('s_add and s_sub, left', () => {
|
||||
it('can add from the left', () => {
|
||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).s_add(2), sequence(0, 1)));
|
||||
});
|
||||
it('can wane to the left', () => {
|
||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).stepwane(2), sequence(0, 1, 2)));
|
||||
it('can sub to the left', () => {
|
||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).s_sub(2), sequence(0, 1, 2)));
|
||||
});
|
||||
it('can wax from the right', () => {
|
||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).stepwax(-2), sequence(3, 4)));
|
||||
it('can add from the right', () => {
|
||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).s_add(-2), sequence(3, 4)));
|
||||
});
|
||||
it('can wane to the right', () => {
|
||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).stepwane(-2), sequence(2, 3, 4)));
|
||||
it('can sub to the right', () => {
|
||||
expect(sameFirst(sequence(0, 1, 2, 3, 4).s_sub(-2), sequence(2, 3, 4)));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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 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);
|
||||
|
||||
/* solmization, not used yet */
|
||||
@ -289,6 +297,28 @@ export const sol2note = (n, notation = 'letters') => {
|
||||
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
|
||||
|
||||
export function unicodeToBase64(text) {
|
||||
|
||||
@ -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`] = `
|
||||
[
|
||||
"[ 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`] = `
|
||||
[
|
||||
"[ 0/1 → 1/6 | s:numbers n:0 begin:0 end:0.16666666666666666 ]",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user