From fe9a91d1e4d9933d21f9aa028851a62f348ebe1e Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 24 Jun 2025 23:22:42 +0100 Subject: [PATCH 1/6] add unjoin, into and chunkinto --- packages/core/pattern.mjs | 41 ++++++++++++++ test/__snapshots__/examples.test.mjs.snap | 69 +++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index beefc5971..0ad1f77c9 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -869,6 +869,47 @@ export class Pattern { console.log(drawLine(this)); return this; } + + ////////////////////////////////////////////////////////////////////// + // methods relating to breaking patterns into subcycles + + // Breaks a pattern into a pattern of patterns, according to the structure of the given binary pattern. + unjoin(pieces, func = id) { + return pieces.withHap((hap) => + hap.withValue((v) => (v ? func(this.ribbon(hap.whole.begin, hap.whole.duration)) : this)), + ); + } + + /** + * Breaks a pattern into pieces according to the structure of a given pattern. + * True values in the given pattern cause the corresponding subcycle of the + * source pattern to be looped, and for an (optional) given function to be + * applied. False values result in the corresponding part of the source pattern + * to be played unchanged. + * @name into + * @memberof Pattern + * @example + * sound("bd sd ht lt").into("1 0", hurry(2)) + */ + into(pieces, func) { + return this.unjoin(pieces, func).innerJoin(); + } + + /** + * Like `chunk`, but the function is applied to a looped subcycle of the source pattern. + * @name chunkInto + * @synonym chunkinto + * @memberof Pattern + * @example + * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) + * .bank("tr909") + */ + chunkInto(n, func) { + return this.into(fastcat(true, ...Array(n - 1).fill(false)).iterback(4), func); + } + chunkinto(n, func) { + return this.chunkInto(n, func); + } } ////////////////////////////////////////////////////////////////////// diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index f3607cb48..d0a622940 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1889,6 +1889,46 @@ exports[`runs examples > example "chunkBack" example index 0 1`] = ` ] `; +exports[`runs examples > example "chunkInto" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | s:bd speed:2 bank:tr909 ]", + "[ 1/16 → 1/8 | s:sd speed:2 bank:tr909 ]", + "[ 1/8 → 3/16 | s:bd speed:2 bank:tr909 ]", + "[ 3/16 → 1/4 | s:sd speed:2 bank:tr909 ]", + "[ 1/4 → 3/8 | s:ht bank:tr909 ]", + "[ 3/8 → 1/2 | s:lt bank:tr909 ]", + "[ 1/2 → 5/8 | s:bd bank:tr909 ]", + "[ 3/4 → 7/8 | s:cp bank:tr909 ]", + "[ 7/8 → 1/1 | s:lt bank:tr909 ]", + "[ 1/1 → 9/8 | s:bd bank:tr909 ]", + "[ 9/8 → 5/4 | s:sd bank:tr909 ]", + "[ 5/4 → 21/16 | s:ht speed:2 bank:tr909 ]", + "[ 21/16 → 11/8 | s:lt speed:2 bank:tr909 ]", + "[ 11/8 → 23/16 | s:ht speed:2 bank:tr909 ]", + "[ 23/16 → 3/2 | s:lt speed:2 bank:tr909 ]", + "[ 3/2 → 13/8 | s:bd bank:tr909 ]", + "[ 7/4 → 15/8 | s:cp bank:tr909 ]", + "[ 15/8 → 2/1 | s:lt bank:tr909 ]", + "[ 2/1 → 17/8 | s:bd bank:tr909 ]", + "[ 17/8 → 9/4 | s:sd bank:tr909 ]", + "[ 9/4 → 19/8 | s:ht bank:tr909 ]", + "[ 19/8 → 5/2 | s:lt bank:tr909 ]", + "[ 5/2 → 41/16 | s:bd speed:2 bank:tr909 ]", + "[ 21/8 → 43/16 | s:bd speed:2 bank:tr909 ]", + "[ 11/4 → 23/8 | s:cp bank:tr909 ]", + "[ 23/8 → 3/1 | s:lt bank:tr909 ]", + "[ 3/1 → 25/8 | s:bd bank:tr909 ]", + "[ 25/8 → 13/4 | s:sd bank:tr909 ]", + "[ 13/4 → 27/8 | s:ht bank:tr909 ]", + "[ 27/8 → 7/2 | s:lt bank:tr909 ]", + "[ 7/2 → 29/8 | s:bd bank:tr909 ]", + "[ 15/4 → 61/16 | s:cp speed:2 bank:tr909 ]", + "[ 61/16 → 31/8 | s:lt speed:2 bank:tr909 ]", + "[ 31/8 → 63/16 | s:cp speed:2 bank:tr909 ]", + "[ 63/16 → 4/1 | s:lt speed:2 bank:tr909 ]", +] +`; + exports[`runs examples > example "clip" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:c s:piano clip:0.5 ]", @@ -4391,6 +4431,35 @@ exports[`runs examples > example "inside" example index 0 1`] = ` ] `; +exports[`runs examples > example "into" example index 0 1`] = ` +[ + "[ 0/1 → 1/8 | s:bd speed:2 ]", + "[ 1/8 → 1/4 | s:sd speed:2 ]", + "[ 1/4 → 3/8 | s:bd speed:2 ]", + "[ 3/8 → 1/2 | s:sd speed:2 ]", + "[ 1/2 → 3/4 | s:ht ]", + "[ 3/4 → 1/1 | s:lt ]", + "[ 1/1 → 9/8 | s:bd speed:2 ]", + "[ 9/8 → 5/4 | s:sd speed:2 ]", + "[ 5/4 → 11/8 | s:bd speed:2 ]", + "[ 11/8 → 3/2 | s:sd speed:2 ]", + "[ 3/2 → 7/4 | s:ht ]", + "[ 7/4 → 2/1 | s:lt ]", + "[ 2/1 → 17/8 | s:bd speed:2 ]", + "[ 17/8 → 9/4 | s:sd speed:2 ]", + "[ 9/4 → 19/8 | s:bd speed:2 ]", + "[ 19/8 → 5/2 | s:sd speed:2 ]", + "[ 5/2 → 11/4 | s:ht ]", + "[ 11/4 → 3/1 | s:lt ]", + "[ 3/1 → 25/8 | s:bd speed:2 ]", + "[ 25/8 → 13/4 | s:sd speed:2 ]", + "[ 13/4 → 27/8 | s:bd speed:2 ]", + "[ 27/8 → 7/2 | s:sd speed:2 ]", + "[ 7/2 → 15/4 | s:ht ]", + "[ 15/4 → 4/1 | s:lt ]", +] +`; + exports[`runs examples > example "invert" example index 0 1`] = ` [ "[ 0/1 → 1/8 | s:bd ]", From e19fd2444704b1767f415d68c897d3da0058eb6a Mon Sep 17 00:00:00 2001 From: Khalid Date: Wed, 25 Jun 2025 08:44:12 -0400 Subject: [PATCH 2/6] Case insensitive search in the reference tab --- website/src/repl/components/panel/Reference.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index ab925eb91..42a03a02e 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -21,7 +21,8 @@ export function Reference() { return true; } - return entry.name.includes(search) || (entry.synonyms?.some((s) => s.includes(search)) ?? false); + const lowCaseSearch = search.toLowerCase(); + return entry.name.toLowerCase().includes(lowCaseSearch) || (entry.synonyms?.some((s) => s.includes(lowCaseSearch)) ?? false); }); }, [search]); From ff5b11f5edee691e5cb126f6a88d0fc4471bc4d0 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 27 Jun 2025 22:44:15 +0100 Subject: [PATCH 3/6] test + bugfix for chunkinto --- packages/core/pattern.mjs | 36 ++++++++++++++--------------- packages/core/test/pattern.test.mjs | 23 ++++++++++++++++++ 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 10527a1a8..c8c744628 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -894,22 +894,6 @@ export class Pattern { into(pieces, func) { return this.unjoin(pieces, func).innerJoin(); } - - /** - * Like `chunk`, but the function is applied to a looped subcycle of the source pattern. - * @name chunkInto - * @synonym chunkinto - * @memberof Pattern - * @example - * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) - * .bank("tr909") - */ - chunkInto(n, func) { - return this.into(fastcat(true, ...Array(n - 1).fill(false)).iterback(4), func); - } - chunkinto(n, func) { - return this.chunkInto(n, func); - } } ////////////////////////////////////////////////////////////////////// @@ -1858,9 +1842,9 @@ export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (f const newWhole = !hap.whole ? undefined : new TimeSpan( - newPart.begin.sub(begin.sub(hap.whole.begin).div(factor)), - newPart.end.add(hap.whole.end.sub(end).div(factor)), - ); + newPart.begin.sub(begin.sub(hap.whole.begin).div(factor)), + newPart.end.add(hap.whole.end.sub(end).div(factor)), + ); return new Hap(newWhole, newPart, hap.value, hap.context); }; return pat.withQuerySpanMaybe(qf).withHap(ef).splitQueries(); @@ -2535,6 +2519,20 @@ export const { fastchunk, fastChunk } = register( true, ); +/** + * Like `chunk`, but the function is applied to a looped subcycle of the source pattern. + * @name chunkInto + * @synonym chunkinto + * @memberof Pattern + * @example + * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) + * .bank("tr909") + */ +export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], + function (n, func, pat) { + return pat.into(fastcat(true, ...Array(n - 1).fill(false)).iterback(n), func); + }); + // TODO - redefine elsewhere in terms of mask export const bypass = register( 'bypass', diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 874d45132..8d26a990c 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -1271,4 +1271,27 @@ describe('Pattern', () => { ); }); }); + describe('unjoin', () => { + it('destructures a pattern into subcycles', () => { + sameFirst( + fastcat('a', 'b', 'c', 'd').unjoin(fastcat(true, fastcat(true, true))).fmap(fast(2)).join(), + fastcat('a', 'b', 'a', 'b', 'c', 'c', 'd', 'd') + ) + }) + }) + describe('into', () => { + it('applies a function to subcycles of a pattern', () => { + sameFirst( + fastcat('a', 'b', 'c', 'd').into(fastcat(fastcat('true', 'true'), 'true'), fast(2)), + fastcat('a', 'a', 'b', 'b', 'c', 'd', 'c', 'd') + ) + }) + }), + describe('chunkinto', () => { + it('chunks into subcycles', () => { + sameFirst(fastcat('a', 'b', 'c').chunkInto(3, fast(2)).fast(3), + fastcat(fastcat('a', 'a'), 'b', 'c', 'a', fastcat('b', 'b'), 'c', 'a', 'b', fastcat('c', 'c')) + ) + }) + }) }); From f44caf90966e34271f155f366721a64b060c1f97 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 27 Jun 2025 22:51:47 +0100 Subject: [PATCH 4/6] tests and tweaks, and add chunkBackInto --- packages/core/pattern.mjs | 16 +++++++++++++++- packages/core/test/pattern.test.mjs | 23 +++++++++++++++-------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index c8c744628..6d42c51fd 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2530,7 +2530,21 @@ export const { fastchunk, fastChunk } = register( */ export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], function (n, func, pat) { - return pat.into(fastcat(true, ...Array(n - 1).fill(false)).iterback(n), func); + return pat.into(fastcat(true, ...Array(n - 1).fill(false))._iterback(n), func); + }); + +/** + * Like `chunkInto`, but moves backwards through the chunks. + * @name chunkBackInto + * @synonym chunkbackinto + * @memberof Pattern + * @example + * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) + * .bank("tr909") + */ +export const { chunkbackinto, chunkBackInto } = register(['chunkbackinto', 'chunkBackInto'], + function (n, func, pat) { + return pat.into(fastcat(true, ...Array(n - 1).fill(false))._iter(n)._early(1), func); }); // TODO - redefine elsewhere in terms of mask diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 8d26a990c..4ab519a75 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -1278,7 +1278,7 @@ describe('Pattern', () => { fastcat('a', 'b', 'a', 'b', 'c', 'c', 'd', 'd') ) }) - }) + }); describe('into', () => { it('applies a function to subcycles of a pattern', () => { sameFirst( @@ -1286,12 +1286,19 @@ describe('Pattern', () => { fastcat('a', 'a', 'b', 'b', 'c', 'd', 'c', 'd') ) }) - }), - describe('chunkinto', () => { - it('chunks into subcycles', () => { - sameFirst(fastcat('a', 'b', 'c').chunkInto(3, fast(2)).fast(3), - fastcat(fastcat('a', 'a'), 'b', 'c', 'a', fastcat('b', 'b'), 'c', 'a', 'b', fastcat('c', 'c')) - ) - }) + }); + describe('chunkinto', () => { + it('chunks into subcycles', () => { + sameFirst(fastcat('a', 'b', 'c').chunkInto(3, fast(2)).fast(3), + fastcat(fastcat('a', 'a'), 'b', 'c', 'a', fastcat('b', 'b'), 'c', 'a', 'b', fastcat('c', 'c')) + ) }) + }); + describe('chunkbackinto', () => { + it('chunks into subcycles backwards', () => { + sameFirst(fastcat('a', 'b', 'c').chunkBackInto(3, fast(2)).fast(3), + fastcat('a', 'b', fastcat('c', 'c'), 'a', fastcat('b', 'b'), 'c', fastcat('a', 'a'), 'b', 'c') + ) + }) + }); }); From 27073d6c17264dd3218102bd72bcb96d6a0186d6 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 27 Jun 2025 22:56:19 +0100 Subject: [PATCH 5/6] format --- packages/core/pattern.mjs | 25 ++++++++++++--------- packages/core/test/pattern.test.mjs | 35 ++++++++++++++++------------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 6d42c51fd..cbaf8a8a2 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1842,9 +1842,9 @@ export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (f const newWhole = !hap.whole ? undefined : new TimeSpan( - newPart.begin.sub(begin.sub(hap.whole.begin).div(factor)), - newPart.end.add(hap.whole.end.sub(end).div(factor)), - ); + newPart.begin.sub(begin.sub(hap.whole.begin).div(factor)), + newPart.end.add(hap.whole.end.sub(end).div(factor)), + ); return new Hap(newWhole, newPart, hap.value, hap.context); }; return pat.withQuerySpanMaybe(qf).withHap(ef).splitQueries(); @@ -2528,10 +2528,9 @@ export const { fastchunk, fastChunk } = register( * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) * .bank("tr909") */ -export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], - function (n, func, pat) { - return pat.into(fastcat(true, ...Array(n - 1).fill(false))._iterback(n), func); - }); +export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], function (n, func, pat) { + return pat.into(fastcat(true, ...Array(n - 1).fill(false))._iterback(n), func); +}); /** * Like `chunkInto`, but moves backwards through the chunks. @@ -2542,10 +2541,14 @@ export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) * .bank("tr909") */ -export const { chunkbackinto, chunkBackInto } = register(['chunkbackinto', 'chunkBackInto'], - function (n, func, pat) { - return pat.into(fastcat(true, ...Array(n - 1).fill(false))._iter(n)._early(1), func); - }); +export const { chunkbackinto, chunkBackInto } = register(['chunkbackinto', 'chunkBackInto'], function (n, func, pat) { + return pat.into( + fastcat(true, ...Array(n - 1).fill(false)) + ._iter(n) + ._early(1), + func, + ); +}); // TODO - redefine elsewhere in terms of mask export const bypass = register( diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 4ab519a75..93c4168c9 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -1274,31 +1274,36 @@ describe('Pattern', () => { describe('unjoin', () => { it('destructures a pattern into subcycles', () => { sameFirst( - fastcat('a', 'b', 'c', 'd').unjoin(fastcat(true, fastcat(true, true))).fmap(fast(2)).join(), - fastcat('a', 'b', 'a', 'b', 'c', 'c', 'd', 'd') - ) - }) + fastcat('a', 'b', 'c', 'd') + .unjoin(fastcat(true, fastcat(true, true))) + .fmap(fast(2)) + .join(), + fastcat('a', 'b', 'a', 'b', 'c', 'c', 'd', 'd'), + ); + }); }); describe('into', () => { it('applies a function to subcycles of a pattern', () => { sameFirst( fastcat('a', 'b', 'c', 'd').into(fastcat(fastcat('true', 'true'), 'true'), fast(2)), - fastcat('a', 'a', 'b', 'b', 'c', 'd', 'c', 'd') - ) - }) + fastcat('a', 'a', 'b', 'b', 'c', 'd', 'c', 'd'), + ); + }); }); describe('chunkinto', () => { it('chunks into subcycles', () => { - sameFirst(fastcat('a', 'b', 'c').chunkInto(3, fast(2)).fast(3), - fastcat(fastcat('a', 'a'), 'b', 'c', 'a', fastcat('b', 'b'), 'c', 'a', 'b', fastcat('c', 'c')) - ) - }) + sameFirst( + fastcat('a', 'b', 'c').chunkInto(3, fast(2)).fast(3), + fastcat(fastcat('a', 'a'), 'b', 'c', 'a', fastcat('b', 'b'), 'c', 'a', 'b', fastcat('c', 'c')), + ); + }); }); describe('chunkbackinto', () => { it('chunks into subcycles backwards', () => { - sameFirst(fastcat('a', 'b', 'c').chunkBackInto(3, fast(2)).fast(3), - fastcat('a', 'b', fastcat('c', 'c'), 'a', fastcat('b', 'b'), 'c', fastcat('a', 'a'), 'b', 'c') - ) - }) + sameFirst( + fastcat('a', 'b', 'c').chunkBackInto(3, fast(2)).fast(3), + fastcat('a', 'b', fastcat('c', 'c'), 'a', fastcat('b', 'b'), 'c', fastcat('a', 'a'), 'b', 'c'), + ); + }); }); }); From df06248c54709e68d39d4f4ae607af4509742842 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 27 Jun 2025 22:59:01 +0100 Subject: [PATCH 6/6] snapshot --- test/__snapshots__/examples.test.mjs.snap | 40 +++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index d0a622940..2d20cff4c 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1889,6 +1889,46 @@ exports[`runs examples > example "chunkBack" example index 0 1`] = ` ] `; +exports[`runs examples > example "chunkBackInto" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | s:bd speed:2 bank:tr909 ]", + "[ 1/16 → 1/8 | s:sd speed:2 bank:tr909 ]", + "[ 1/8 → 3/16 | s:bd speed:2 bank:tr909 ]", + "[ 3/16 → 1/4 | s:sd speed:2 bank:tr909 ]", + "[ 1/4 → 3/8 | s:ht bank:tr909 ]", + "[ 3/8 → 1/2 | s:lt bank:tr909 ]", + "[ 1/2 → 5/8 | s:bd bank:tr909 ]", + "[ 3/4 → 7/8 | s:cp bank:tr909 ]", + "[ 7/8 → 1/1 | s:lt bank:tr909 ]", + "[ 1/1 → 9/8 | s:bd bank:tr909 ]", + "[ 9/8 → 5/4 | s:sd bank:tr909 ]", + "[ 5/4 → 21/16 | s:ht speed:2 bank:tr909 ]", + "[ 21/16 → 11/8 | s:lt speed:2 bank:tr909 ]", + "[ 11/8 → 23/16 | s:ht speed:2 bank:tr909 ]", + "[ 23/16 → 3/2 | s:lt speed:2 bank:tr909 ]", + "[ 3/2 → 13/8 | s:bd bank:tr909 ]", + "[ 7/4 → 15/8 | s:cp bank:tr909 ]", + "[ 15/8 → 2/1 | s:lt bank:tr909 ]", + "[ 2/1 → 17/8 | s:bd bank:tr909 ]", + "[ 17/8 → 9/4 | s:sd bank:tr909 ]", + "[ 9/4 → 19/8 | s:ht bank:tr909 ]", + "[ 19/8 → 5/2 | s:lt bank:tr909 ]", + "[ 5/2 → 41/16 | s:bd speed:2 bank:tr909 ]", + "[ 21/8 → 43/16 | s:bd speed:2 bank:tr909 ]", + "[ 11/4 → 23/8 | s:cp bank:tr909 ]", + "[ 23/8 → 3/1 | s:lt bank:tr909 ]", + "[ 3/1 → 25/8 | s:bd bank:tr909 ]", + "[ 25/8 → 13/4 | s:sd bank:tr909 ]", + "[ 13/4 → 27/8 | s:ht bank:tr909 ]", + "[ 27/8 → 7/2 | s:lt bank:tr909 ]", + "[ 7/2 → 29/8 | s:bd bank:tr909 ]", + "[ 15/4 → 61/16 | s:cp speed:2 bank:tr909 ]", + "[ 61/16 → 31/8 | s:lt speed:2 bank:tr909 ]", + "[ 31/8 → 63/16 | s:cp speed:2 bank:tr909 ]", + "[ 63/16 → 4/1 | s:lt speed:2 bank:tr909 ]", +] +`; + exports[`runs examples > example "chunkInto" example index 0 1`] = ` [ "[ 0/1 → 1/16 | s:bd speed:2 bank:tr909 ]",