From fdb76867a7e66549190835c2da62c190e8107be1 Mon Sep 17 00:00:00 2001 From: Alex McLean Date: Sat, 18 Feb 2023 00:00:18 +0000 Subject: [PATCH] weave and weaveWith (#465) Added weave and weaveWith from tidal --- packages/core/euclid.mjs | 8 +---- packages/core/pattern.mjs | 31 ++++++++++++++++- packages/core/test/pattern.test.mjs | 5 +++ packages/core/util.mjs | 6 ++++ test/__snapshots__/examples.test.mjs.snap | 42 +++++++++++++++++++++++ 5 files changed, 84 insertions(+), 8 deletions(-) diff --git a/packages/core/euclid.mjs b/packages/core/euclid.mjs index d8560c8e..29539cf9 100644 --- a/packages/core/euclid.mjs +++ b/packages/core/euclid.mjs @@ -11,15 +11,9 @@ This program is free software: you can redistribute it and/or modify it under th */ import { Pattern, timeCat, register, silence } from './pattern.mjs'; -import { rotate, flatten } from './util.mjs'; +import { rotate, flatten, splitAt, zipWith } from './util.mjs'; import Fraction from './fraction.mjs'; -const splitAt = function (index, value) { - return [value.slice(0, index), value.slice(index)]; -}; - -const zipWith = (f, xs, ys) => xs.map((n, i) => f(n, ys[i])); - const left = function (n, x) { const [ons, offs] = n; const [xs, ys] = x; diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 1184e1a1..1274e760 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -977,7 +977,7 @@ export class Pattern { } ////////////////////////////////////////////////////////////////////// -// functions relating to chords/patterns of lists +// functions relating to chords/patterns of lists/lists of patterns // returns Array where each list of haps satisfies eq function groupHapsBy(eq, haps) { @@ -1026,6 +1026,35 @@ addToPrototype('arp', function (pat) { return this.arpWith((haps) => pat.fmap((i) => haps[i % haps.length])); }); +/** + * Takes a time duration followed by one or more patterns, and shifts the given patterns in time, so they are + * distributed equally over the given time duration. They are then combined with the pattern 'weave' is called on, after it has been stretched out (i.e. slowed down by) the time duration. + * @name weave + * @memberof Pattern + * @example pan(saw).weave(4, s("bd(3,8)"), s("~ sd")) + * @example n("0 1 2 3 4 5 6 7").weave(8, s("bd(3,8)"), s("~ sd")) + */ + +addToPrototype('weave', function (t, ...pats) { + return this.weaveWith(t, ...pats.map((x) => set.out(x))); +}); + +/** + * Like 'weave', but accepts functions rather than patterns, which are applied to the pattern. + * @name weaveWith + * @memberof Pattern + */ + +addToPrototype('weaveWith', function (t, ...funcs) { + const pat = this; + const l = funcs.length; + t = Fraction(t); + if (l == 0) { + return silence; + } + return stack(...funcs.map((func, i) => pat.inside(t, func).early(Fraction(i).div(l))))._slow(t); +}); + ////////////////////////////////////////////////////////////////////// // compose matrix functions diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 2142242d..3d71f529 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -981,4 +981,9 @@ describe('Pattern', () => { // sameFirst(s('bd').apply(set.squeeze.n(3).fast(2)), s('bd').set.squeeze.n(3).fast(2)); }); }); + describe('weave', () => { + it('Can distribute patterns along a pattern', () => { + sameFirst(n(0, 1).weave(2, s('bd', silence), s(silence, 'sd')), sequence(s('bd').n(0), s('sd').n(1))); + }); + }); }); diff --git a/packages/core/util.mjs b/packages/core/util.mjs index 6ba8f397..18586541 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -206,3 +206,9 @@ export function parseFractional(numOrString) { } export const fractionalArgs = (fn) => mapArgs(fn, parseFractional); + +export const splitAt = function (index, value) { + return [value.slice(0, index), value.slice(index)]; +}; + +export const zipWith = (f, xs, ys) => xs.map((n, i) => f(n, ys[i])); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index e061d105..e53cf9fa 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -4231,6 +4231,48 @@ exports[`runs examples > example "vowel" example index 0 1`] = ` ] `; +exports[`runs examples > example "weave" example index 0 1`] = ` +[ + "[ 0/1 → 1/8 | pan:0.015625 s:bd ]", + "[ 3/8 → 1/2 | pan:0.109375 s:bd ]", + "[ 3/4 → 7/8 | pan:0.203125 s:bd ]", + "[ 1/1 → 9/8 | pan:0.265625 s:bd ]", + "[ 11/8 → 3/2 | pan:0.359375 s:bd ]", + "[ 7/4 → 15/8 | pan:0.453125 s:bd ]", + "[ 2/1 → 17/8 | pan:0.515625 s:bd ]", + "[ 19/8 → 5/2 | pan:0.609375 s:bd ]", + "[ 11/4 → 23/8 | pan:0.703125 s:bd ]", + "[ 3/1 → 25/8 | pan:0.765625 s:bd ]", + "[ 27/8 → 7/2 | pan:0.859375 s:bd ]", + "[ 15/4 → 31/8 | pan:0.953125 s:bd ]", + "[ 1/2 → 1/1 | pan:0.6875 s:sd ]", + "[ 3/2 → 2/1 | pan:0.9375 s:sd ]", + "[ 5/2 → 3/1 | pan:0.1875 s:sd ]", + "[ 7/2 → 4/1 | pan:0.4375 s:sd ]", +] +`; + +exports[`runs examples > example "weave" example index 1 1`] = ` +[ + "[ 0/1 → 1/8 | n:0 s:bd ]", + "[ 3/8 → 1/2 | n:0 s:bd ]", + "[ 3/4 → 7/8 | n:0 s:bd ]", + "[ 1/1 → 9/8 | n:1 s:bd ]", + "[ 11/8 → 3/2 | n:1 s:bd ]", + "[ 7/4 → 15/8 | n:1 s:bd ]", + "[ 2/1 → 17/8 | n:2 s:bd ]", + "[ 19/8 → 5/2 | n:2 s:bd ]", + "[ 11/4 → 23/8 | n:2 s:bd ]", + "[ 3/1 → 25/8 | n:3 s:bd ]", + "[ 27/8 → 7/2 | n:3 s:bd ]", + "[ 15/4 → 31/8 | n:3 s:bd ]", + "[ 1/2 → 1/1 | n:4 s:sd ]", + "[ 3/2 → 2/1 | n:5 s:sd ]", + "[ 5/2 → 3/1 | n:6 s:sd ]", + "[ 7/2 → 4/1 | n:7 s:sd ]", +] +`; + exports[`runs examples > example "webdirt" example index 0 1`] = ` [ "[ 0/1 → 1/8 | s:bd n:0 ]",