mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-26 04:58:27 +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;
|
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;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@ -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)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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 ]",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user