From b370c405da0116b725fa2ca82364ff6fe3704128 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 21 Apr 2022 14:13:26 +0100 Subject: [PATCH 1/6] Add pattern composers, implements #82 --- packages/core/controls.mjs | 6 +-- packages/core/pattern.mjs | 79 +++++++++++++++++++++++++++----------- 2 files changed, 60 insertions(+), 25 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 039457a4..acce1acd 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -275,14 +275,14 @@ const generic_params = [ const _name = (name, ...pats) => sequence(...pats).withValue((x) => ({ [name]: x })); -const _unionise = (func) => +const _setter = (func) => function (...pats) { - return this.union(func(...pats)); + return this.set(func(...pats)); }; generic_params.forEach(([type, name, description]) => { controls[name] = (...pats) => _name(name, ...pats); - Pattern.prototype[name] = _unionise(controls[name]); + Pattern.prototype[name] = _setter(controls[name]); }); export default controls; diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index b8239c17..a3798488 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -247,9 +247,24 @@ export class Pattern { ); } - _opleft(other, func) { + _opLeft(other, func) { return this.fmap(func).appLeft(reify(other)); } + _opRight(other, func) { + return this.fmap(func).appRight(reify(other)); + } + _opBoth(other, func) { + return this.fmap(func).appBoth(reify(other)); + } + _opSqueeze(other, func) { + const otherPat = reify(other); + return this.fmap((a) => otherPat.fmap((b) => func(a)(b)))._squeezeJoin(); + } + _opSqueezeFlip(other, func) { + const thisPat = this; + const otherPat = reify(other); + return otherPat.fmap((a) => thisPat.fmap((b) => func(a)(b)))._squeezeJoin(); + } _asNumber(silent = false) { return this._withEvent((event) => { @@ -275,22 +290,6 @@ export class Pattern { })._removeUndefineds(); } - add(other) { - return this._asNumber()._opleft(other, (a) => (b) => a + b); - } - - sub(other) { - return this._asNumber()._opleft(other, (a) => (b) => a - b); - } - - mul(other) { - return this._asNumber()._opleft(other, (a) => (b) => a * b); - } - - div(other) { - return this._asNumber()._opleft(other, (a) => (b) => a / b); - } - round() { return this._asNumber().fmap((v) => Math.round(v)); } @@ -324,10 +323,6 @@ export class Pattern { return this._fromBipolar().range(min, max); } - union(other) { - return this._opleft(other, (a) => (b) => Object.assign({}, a, b)); - } - _bindWhole(choose_whole, func) { const pat_val = this; const query = function (state) { @@ -719,6 +714,46 @@ export class Pattern { _velocity(velocity) { return this._withContext((context) => ({ ...context, velocity: (context.velocity || 1) * velocity })); } + + add(other) { + return this._asNumber()._opLeft(other, (a) => (b) => a + b); + } + + sub(other) { + return this._asNumber()._opLeft(other, (a) => (b) => a - b); + } + mul(other) { + return this._asNumber()._opLeft(other, (a) => (b) => a * b); + } + + div(other) { + return this._asNumber()._opLeft(other, (a) => (b) => a / b); + } +} + +// pattern composers +const composers = { + set: [(a) => (b) => Object.assign({}, a, b), id], + add: [(a) => (b) => a + b, (x) => x._asNumber()], + sub: [(a) => (b) => a - b, (x) => x._asNumber()], + mul: [(a) => (b) => a * b, (x) => x._asNumber()], + div: [(a) => (b) => a / b, (x) => x._asNumber()], +}; + +for (const [name, op] of Object.entries(composers)) { + console.log(`Adding ${name}`); + Pattern.prototype[name] = function (other) { + return op[1](this)._opLeft(other, op[0]); + }; + Pattern.prototype[name + 'Flip'] = function (other) { + return op[1](this)._opRight(other, op[0]); + }; + Pattern.prototype[name + 'Sect'] = function (other) { + return op[1](this)._opBoth(other, op[0]); + }; + Pattern.prototype[name + 'Squeeze'] = function (other) { + return op[1](this)._opSqueeze(other, op[0]); + }; } // methods of Pattern that get callable factories @@ -922,7 +957,7 @@ export const slow = curry((a, pat) => pat.slow(a)); export const struct = curry((a, pat) => pat.struct(a)); export const sub = curry((a, pat) => pat.sub(a)); export const superimpose = curry((array, pat) => pat.superimpose(...array)); -export const union = curry((a, pat) => pat.union(a)); +export const set = curry((a, pat) => pat.set(a)); export const when = curry((binary, f, pat) => pat.when(binary, f)); // problem: curried functions with spread arguments must have pat at the beginning From 3ae6443c59a1e57531b994ee4cf5bb0cba5901c1 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 21 Apr 2022 14:17:28 +0100 Subject: [PATCH 2/6] Tidy test and console logs --- packages/core/pattern.mjs | 16 ---------------- packages/core/test/pattern.test.mjs | 6 +++--- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index a3798488..fc788751 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -714,21 +714,6 @@ export class Pattern { _velocity(velocity) { return this._withContext((context) => ({ ...context, velocity: (context.velocity || 1) * velocity })); } - - add(other) { - return this._asNumber()._opLeft(other, (a) => (b) => a + b); - } - - sub(other) { - return this._asNumber()._opLeft(other, (a) => (b) => a - b); - } - mul(other) { - return this._asNumber()._opLeft(other, (a) => (b) => a * b); - } - - div(other) { - return this._asNumber()._opLeft(other, (a) => (b) => a / b); - } } // pattern composers @@ -741,7 +726,6 @@ const composers = { }; for (const [name, op] of Object.entries(composers)) { - console.log(`Adding ${name}`); Pattern.prototype[name] = function (other) { return op[1](this)._opLeft(other, op[0]); }; diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index f2b255d2..e8e0ff0f 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -161,11 +161,11 @@ describe('Pattern', function () { assert.equal(pure(3).div(pure(2)).firstCycle()[0].value, 1.5); }); }); - describe('union()', function () { - it('Can union things', function () { + describe('set()', function () { + it('Can set things', function () { assert.deepStrictEqual( pure({ a: 4, b: 6 }) - .union(pure({ c: 7 })) + .set(pure({ c: 7 })) .firstCycle()[0].value, { a: 4, b: 6, c: 7 }, ); From b510ab03718244711d8e0ef466b22595a4af3895 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 22 Apr 2022 09:26:39 +0100 Subject: [PATCH 3/6] Make set work for primative patterns, allow ops accept sequence --- packages/core/pattern.mjs | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index fc788751..4f482205 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -718,7 +718,23 @@ export class Pattern { // pattern composers const composers = { - set: [(a) => (b) => Object.assign({}, a, b), id], + set: [ + (a) => (b) => { + // If an object is involved, do a union, discarding matching keys from a. + // Otherwise, just return b. + if (a instanceof Object || b instanceof Object) { + if (!a instanceof Object) { + a = { value: a }; + } + if (!b instanceof Object) { + b = { value: b }; + } + return Object.assign({}, a, b); + } + return b; + }, + id, + ], add: [(a) => (b) => a + b, (x) => x._asNumber()], sub: [(a) => (b) => a - b, (x) => x._asNumber()], mul: [(a) => (b) => a * b, (x) => x._asNumber()], @@ -726,17 +742,17 @@ const composers = { }; for (const [name, op] of Object.entries(composers)) { - Pattern.prototype[name] = function (other) { - return op[1](this)._opLeft(other, op[0]); + Pattern.prototype[name] = function (...other) { + return op[1](this)._opLeft(sequence(other), op[0]); }; - Pattern.prototype[name + 'Flip'] = function (other) { - return op[1](this)._opRight(other, op[0]); + Pattern.prototype[name + 'Flip'] = function (...other) { + return op[1](this)._opRight(sequence(other), op[0]); }; - Pattern.prototype[name + 'Sect'] = function (other) { - return op[1](this)._opBoth(other, op[0]); + Pattern.prototype[name + 'Sect'] = function (...other) { + return op[1](this)._opBoth(sequence(other), op[0]); }; - Pattern.prototype[name + 'Squeeze'] = function (other) { - return op[1](this)._opSqueeze(other, op[0]); + Pattern.prototype[name + 'Squeeze'] = function (...other) { + return op[1](this)._opSqueeze(sequence(other), op[0]); }; } From d9e3ff3256f11f449307e18e482ecb8d629862c7 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 22 Apr 2022 09:27:23 +0100 Subject: [PATCH 4/6] Tests for set, setFlip and setSqueeze --- packages/core/test/pattern.test.mjs | 47 +++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index e8e0ff0f..562037c7 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -50,6 +50,10 @@ const hap = (whole, part, value, context = {}) => new Hap(whole, part, value, co const third = Fraction(1, 3); const twothirds = Fraction(2, 3); +const sameFirst = (a, b) => { + return assert.deepStrictEqual(a.firstCycle(), b.firstCycle()); +}; + describe('TimeSpan', function () { describe('equals()', function () { it('Should be equal to the same value', function () { @@ -162,13 +166,49 @@ describe('Pattern', function () { }); }); describe('set()', function () { - it('Can set things', function () { + it('Can set things in objects', function () { assert.deepStrictEqual( pure({ a: 4, b: 6 }) .set(pure({ c: 7 })) .firstCycle()[0].value, { a: 4, b: 6, c: 7 }, ); + sameFirst( + sequence({ a: 1, b: 2 }, { a: 2, b: 2 }, { a: 3, b: 2 }).set({ a: 4, c: 5 }), + sequence({ a: 4, b: 2, c: 5 }).fast(3), + ); + }); + it('Can set things with plain values', function () { + sameFirst(sequence(1, 2, 3).set(4), sequence(4).fast(3)); + }); + describe('setFlip()', () => { + it('Can set things with structure from second pattern', () => { + sameFirst(sequence(1, 2).setFlip(4), pure(4).mask(true, true)); + }); + }); + describe('setSqueeze()', () => { + it('Can squeeze one pattern inside the events of another', () => { + sameFirst( + sequence(1, [2, 3]).setSqueeze(sequence('a', 'b', 'c')), + sequence( + ['a', 'b', 'c'], + [ + ['a', 'b', 'c'], + ['a', 'b', 'c'], + ], + ), + ); + sameFirst( + sequence(1, [2, 3]).setSqueeze('a', 'b', 'c'), + sequence( + ['a', 'b', 'c'], + [ + ['a', 'b', 'c'], + ['a', 'b', 'c'], + ], + ), + ); + }); }); }); describe('stack()', function () { @@ -441,7 +481,10 @@ describe('Pattern', function () { ); }); it('Can structure a continuous pattern', () => { - assert.deepStrictEqual(steady('a').struct(true, [true, true]).firstCycle(), sequence('a', ['a', 'a']).firstCycle()); + assert.deepStrictEqual( + steady('a').struct(true, [true, true]).firstCycle(), + sequence('a', ['a', 'a']).firstCycle(), + ); }); }); describe('mask()', function () { From 974503160d75e8116a0af4b4284f83558a6f8527 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 22 Apr 2022 09:51:44 +0100 Subject: [PATCH 5/6] squeezeflip, tests for add including failing one.. --- packages/core/pattern.mjs | 5 ++++- packages/core/test/pattern.test.mjs | 29 ++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 4f482205..9bf45725 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -263,7 +263,7 @@ export class Pattern { _opSqueezeFlip(other, func) { const thisPat = this; const otherPat = reify(other); - return otherPat.fmap((a) => thisPat.fmap((b) => func(a)(b)))._squeezeJoin(); + return otherPat.fmap((a) => thisPat.fmap((b) => func(b)(a)))._squeezeJoin(); } _asNumber(silent = false) { @@ -754,6 +754,9 @@ for (const [name, op] of Object.entries(composers)) { Pattern.prototype[name + 'Squeeze'] = function (...other) { return op[1](this)._opSqueeze(sequence(other), op[0]); }; + Pattern.prototype[name + 'SqueezeFlip'] = function (...other) { + return op[1](this)._opSqueezeFlip(sequence(other), op[0]); + }; } // methods of Pattern that get callable factories diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 562037c7..fd9163b0 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -51,7 +51,7 @@ const third = Fraction(1, 3); const twothirds = Fraction(2, 3); const sameFirst = (a, b) => { - return assert.deepStrictEqual(a.firstCycle(), b.firstCycle()); + return assert.deepStrictEqual(a._sortEventsByPart().firstCycle(), b._sortEventsByPart().firstCycle()); }; describe('TimeSpan', function () { @@ -150,6 +150,33 @@ describe('Pattern', function () { assert.equal(pure(3).add(pure(4)).query(st(0, 1))[0].value, 7); }); }); + describe('addFlip()', () => { + it('Can add things with structure from second pattern', () => { + sameFirst(sequence(1, 2).addFlip(4), sequence(5, 6).struct(true)); + }); + }); + describe('addSqueeze()', () => { + it('Can add while squeezing the second pattern inside the events of the first', () => { + sameFirst( + sequence(1, [2, 3]).addSqueeze(sequence(10, 20, 30)), + sequence( + [11, 21, 31], + [ + [12, 22, 32], + [13, 23, 33], + ], + ), + ); + }); + }); + describe('addSqueezeFlip()', () => { + it('Can add while squeezing the first pattern inside the events of the second', () => { + sameFirst( + sequence(1, [2, 3]).addSqueezeFlip(10, 20, 30), + sequence([11, [12, 13]], [21, [22, 23]], [31, [32, 33]]), + ); + }); + }); describe('sub()', function () { it('Can subtract things', function () { assert.equal(pure(3).sub(pure(4)).query(st(0, 1))[0].value, -1); From 8e441ecb1947a5af92992ab66f4b5134d329165b Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 22 Apr 2022 12:07:23 +0100 Subject: [PATCH 6/6] fix striate: union -> set --- packages/core/pattern.mjs | 2 +- packages/core/test/pattern.test.mjs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 6f405b48..a194c81d 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -499,7 +499,7 @@ export class Pattern { const slices = Array.from({ length: n }, (x, i) => i); const slice_objects = slices.map((i) => ({ begin: i / n, end: (i + 1) / n })); const slicePat = slowcat(...slice_objects); - return this.union(slicePat)._fast(n); + return this.set(slicePat)._fast(n); } // cpm = cycles per minute diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 641f81e4..06bd2b34 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -709,6 +709,14 @@ describe('Pattern', function () { assert.equal(sequence(1, 2, 3).ply(2).early(8).firstCycle().length, 6); }); }); + describe('striate', () => { + it('Can striate(2)', () => { + sameFirst( + sequence({ sound: 'a' }).striate(2), + sequence({ sound: 'a', begin: 0, end: 0.5 }, { sound: 'a', begin: 0.5, end: 1 }), + ); + }); + }); describe('chop', () => { it('Can _chop(2)', () => { assert.deepStrictEqual(