From 357b0ee4678e3e96d7d7f326e098399e38ac44d9 Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 12 Apr 2022 10:53:22 +0100 Subject: [PATCH 1/3] First run at squeezeBind, ref #32 --- packages/core/fraction.mjs | 5 +++ packages/core/strudel.mjs | 79 +++++++++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/packages/core/fraction.mjs b/packages/core/fraction.mjs index f95ee90a..9d6605fc 100644 --- a/packages/core/fraction.mjs +++ b/packages/core/fraction.mjs @@ -16,6 +16,11 @@ Fraction.prototype.wholeCycle = function () { return new TimeSpan(this.sam(), this.nextSam()); }; +// The position of a time value relative to the start of its cycle. +Fraction.prototype.cyclePos = function () { + return this.sub(this.sam()); +}; + Fraction.prototype.lt = function (other) { return this.compare(other) < 0; }; diff --git a/packages/core/strudel.mjs b/packages/core/strudel.mjs index 4f98eb5e..42d974ec 100644 --- a/packages/core/strudel.mjs +++ b/packages/core/strudel.mjs @@ -59,6 +59,15 @@ class TimeSpan { return spans; } + cycleArc() { + // Shifts a timespan to one of equal duration that starts within cycle zero. + // (Note that the output timespan probably does not start *at* Time 0 -- + // that only happens when the input Arc starts at an integral Time.) + const b = this.begin.cyclePos(); + const e = b + (this.end - this.begin); + return new TimeSpan(b, e); + } + withTime(func_time) { // Applies given function to both the begin and end time value of the timespan""" return new TimeSpan(func_time(this.begin), func_time(this.end)); @@ -140,6 +149,10 @@ class Hap { return this.whole.end.sub(this.whole.begin).valueOf(); } + wholeOrPart() { + return this.whole ? this.whole : this.part; + } + withSpan(func) { // Returns a new event with the function f applies to the event timespan. const whole = this.whole ? func(this.whole) : undefined; @@ -186,6 +199,11 @@ class Hap { ); } + combineContext(b) { + const a = this; + return { ...a.context, ...b.context, locations: (a.context.locations || []).concat(b.context.locations || []) }; + } + setContext(context) { return new Hap(this.whole, this.part, this.value, context); } @@ -351,12 +369,11 @@ class Pattern { if (s == undefined) { return undefined; } - // TODO: is it right to add event_val.context here? return new Hap( whole_func(event_func.whole, event_val.whole), s, event_func.value(event_val.value), - event_val.context, + event_val.combineContext(event_func), ); }; return flatten( @@ -388,11 +405,8 @@ class Pattern { const new_whole = hap_func.whole; const new_part = hap_func.part.intersection_e(hap_val.part); const new_value = hap_func.value(hap_val.value); - const hap = new Hap(new_whole, new_part, new_value, { - ...hap_val.context, - ...hap_func.context, - locations: (hap_val.context.locations || []).concat(hap_func.context.locations || []), - }); + const new_context = hap_val.combineContext(hap_func); + const hap = new Hap(new_whole, new_part, new_value, new_context); haps.push(hap); } } @@ -412,11 +426,8 @@ class Pattern { const new_whole = hap_val.whole; const new_part = hap_func.part.intersection_e(hap_val.part); const new_value = hap_func.value(hap_val.value); - const hap = new Hap(new_whole, new_part, new_value, { - ...hap_func.context, - ...hap_val.context, - locations: (hap_val.context.locations || []).concat(hap_func.context.locations || []), - }); + const new_context = hap_val.combineContext(hap_func); + const hap = new Hap(new_whole, new_part, new_value, new_context); haps.push(hap); } } @@ -572,6 +583,49 @@ class Pattern { return this.outerBind(id); } + squeezeJoin() { + const pat_of_pats = this; + function query(state) { + const haps = pat_of_pats.query(state); + function flatHap(outerHap) { + const pat = outerHap.value.compressArc(outerHap.wholeOrPart().cycleArc()); + const innerHaps = pat.query(state.setSpan(outerHap.part)); + function munge(outer, inner) { + let whole = undefined; + if (inner.whole && outer.whole) { + whole = inner.whole.intersection(outer.whole); + if (!whole) { + // The wholes are present, but don't intersect + return undefined; + } + } + const part = inner.part.intersection(outer_part); + if (!part) { + // The parts don't intersect + return undefined; + } + const context = inner.combineContext(outer.context); + return new Hap(whole, part, inner.value, context); + } + innerHaps.map(innerHap => munge(outerHap,innerHap)) + } + const flattened = haps.map((x) => x.withEvent(flatHap)); + return flattened.filter(x => x); + } + } + + // squeezeJoin :: Pattern (Pattern a) -> Pattern a + // squeezeJoin pp = pp {query = q} + // where q st = concatMap + // (\e@(Event c w p v) -> + // mapMaybe (munge c w p) $ query (compressArc (cycleArc $ wholeOrPart e) v) st {arc = p} + // ) + // (query pp st) + // munge oContext oWhole oPart (Event iContext iWhole iPart v) = + // do w' <- subMaybeArc oWhole iWhole + // p' <- subArc oPart iPart + // return (Event (combineContexts [iContext, oContext]) w' p' v) + _apply(func) { return func(this); } @@ -995,7 +1049,6 @@ function polymeterSteps(steps, ...args) { if (steps == seq[1]) { pats.push(seq[0]); } else { - console.log("aha"); pats.push(seq[0]._fast(Fraction(steps).div(Fraction(seq[1])))); } } From c2cc6b6dc8762521e8ee329cb581942ed0c8bc0e Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 12 Apr 2022 11:22:53 +0100 Subject: [PATCH 2/3] Working squeezeBind --- packages/core/strudel.mjs | 14 ++++++++------ packages/core/test/pattern.test.mjs | 9 +++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/core/strudel.mjs b/packages/core/strudel.mjs index 42d974ec..42dd4189 100644 --- a/packages/core/strudel.mjs +++ b/packages/core/strudel.mjs @@ -588,7 +588,7 @@ class Pattern { function query(state) { const haps = pat_of_pats.query(state); function flatHap(outerHap) { - const pat = outerHap.value.compressArc(outerHap.wholeOrPart().cycleArc()); + const pat = outerHap.value._compressSpan(outerHap.wholeOrPart().cycleArc()); const innerHaps = pat.query(state.setSpan(outerHap.part)); function munge(outer, inner) { let whole = undefined; @@ -599,19 +599,21 @@ class Pattern { return undefined; } } - const part = inner.part.intersection(outer_part); + const part = inner.part.intersection(outer.part); if (!part) { // The parts don't intersect return undefined; } - const context = inner.combineContext(outer.context); + const context = inner.combineContext(outer); return new Hap(whole, part, inner.value, context); } - innerHaps.map(innerHap => munge(outerHap,innerHap)) + return innerHaps.map(innerHap => munge(outerHap, innerHap)) } - const flattened = haps.map((x) => x.withEvent(flatHap)); - return flattened.filter(x => x); + const result = flatten(haps.map(flatHap)); + // remove undefineds + return result.filter(x => x); } + return new Pattern(query); } // squeezeJoin :: Pattern (Pattern a) -> Pattern a diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 13eff589..76a6e418 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -33,6 +33,7 @@ import { square2, tri, tri2, + id, } from '../strudel.mjs'; //import { Time } from 'tone'; import pkg from 'tone'; @@ -489,4 +490,12 @@ describe('Pattern', function() { ) }) }) + describe("squeezeJoin", () => { + it("Can squeeze", () => { + assert.deepStrictEqual( + sequence("a", ["a","a"]).fmap(a => fastcat("b", "c")).squeezeJoin().firstCycle(), + sequence(["b", "c"],[["b", "c"],["b", "c"]]).firstCycle() + ) + }) + }) }) From b10e0c3a1578d7d1b16a767846c971ede82672d3 Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 12 Apr 2022 11:31:20 +0100 Subject: [PATCH 3/3] Add ply() --- packages/core/strudel.mjs | 21 ++++++++------------- packages/core/test/pattern.test.mjs | 9 +++++++++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/core/strudel.mjs b/packages/core/strudel.mjs index 42dd4189..7a3d8685 100644 --- a/packages/core/strudel.mjs +++ b/packages/core/strudel.mjs @@ -616,18 +616,6 @@ class Pattern { return new Pattern(query); } - // squeezeJoin :: Pattern (Pattern a) -> Pattern a - // squeezeJoin pp = pp {query = q} - // where q st = concatMap - // (\e@(Event c w p v) -> - // mapMaybe (munge c w p) $ query (compressArc (cycleArc $ wholeOrPart e) v) st {arc = p} - // ) - // (query pp st) - // munge oContext oWhole oPart (Event iContext iWhole iPart v) = - // do w' <- subMaybeArc oWhole iWhole - // p' <- subArc oPart iPart - // return (Event (combineContexts [iContext, oContext]) w' p' v) - _apply(func) { return func(this); } @@ -689,6 +677,10 @@ class Pattern { return this._fast(Fraction(1).div(factor)); } + _ply(factor) { + return this.fmap(x => pure(x)._fast(factor)).squeezeJoin() + } + // cpm = cycles per minute _cpm(cpm) { return this._fast(cpm / 60); @@ -885,6 +877,7 @@ Pattern.prototype.patternified = [ 'apply', 'fast', 'slow', + 'ply', 'cpm', 'early', 'late', @@ -892,7 +885,7 @@ Pattern.prototype.patternified = [ 'legato', 'velocity', 'segment', - 'color', + 'color' ]; // methods that create patterns, which are added to patternified Pattern methods Pattern.prototype.factories = { pure, stack, slowcat, fastcat, cat, timeCat, sequence, polymeter, pm, polyrhythm, pr }; @@ -1105,6 +1098,7 @@ const iter = curry((a, pat) => pat.iter(a)); const iterBack = curry((a, pat) => pat.iter(a)); const chunk = curry((a, pat) => pat.chunk(a)); const chunkBack = curry((a, pat) => pat.chunkBack(a)); +const ply = curry((a, pat) => pat.ply(a)); // problem: curried functions with spread arguments must have pat at the beginning // with this, we cannot keep the pattern open at the end.. solution for now: use array to keep using pat as last arg @@ -1262,4 +1256,5 @@ export { iterBack, chunk, chunkBack, + ply, }; diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 76a6e418..88d18f32 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -34,6 +34,7 @@ import { tri, tri2, id, + ply, } from '../strudel.mjs'; //import { Time } from 'tone'; import pkg from 'tone'; @@ -498,4 +499,12 @@ describe('Pattern', function() { ) }) }) + describe("ply", () => { + it("Can ply(3)", () => { + assert.deepStrictEqual( + sequence("a", ["b","c"]).ply(3).firstCycle(), + sequence(pure("a").fast(3), [pure("b").fast(3), pure("c").fast(3)]).firstCycle() + ) + }) + }) })