From 4fd843f8cf98940708680494a8f220269455b78b Mon Sep 17 00:00:00 2001 From: alex Date: Mon, 9 May 2022 16:56:08 +0200 Subject: [PATCH 1/3] Implement brak() --- packages/core/pattern.mjs | 10 ++++++++++ packages/core/test/pattern.test.mjs | 13 +++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 6c4335a3..0080ff83 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -822,6 +822,16 @@ export class Pattern { return slowcatPrime(...pats); } + /** + * Returns a new pattern where every other cycle is played once, twice as + * fast, and offset in time by one quarter of a cycle. Creates a kind of + * breakbeat feel. + * @returns Pattern + */ + brak() { + return this.every(2, x => fastcat(x, silence)._late(0.25)) + } + rev() { const pat = this; const query = function (state) { diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index aa4a4ab0..25088ecf 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -277,10 +277,7 @@ describe('Pattern', function () { ); }); it('can SqueezeOut() structure', () => { - sameFirst( - sequence(1, [2, 3]).keepifSqueezeOut(true, true, false), - sequence([1, [2, 3]], [1, [2, 3]], silence), - ); + sameFirst(sequence(1, [2, 3]).keepifSqueezeOut(true, true, false), sequence([1, [2, 3]], [1, [2, 3]], silence)); }); }); describe('sub()', function () { @@ -589,6 +586,14 @@ describe('Pattern', function () { ); }); }); + describe('brak()', () => { + it('Can make something a bit breakbeaty', () => { + sameFirst( + sequence('a', 'b').brak()._fast(2), + sequence('a', 'b', fastcat(silence, 'a'), fastcat('b', silence)) + ) + }); + }); describe('timeCat()', function () { it('Can concatenate patterns with different relative durations', function () { assert.deepStrictEqual( From a8d676641b2b571b7e4a8b7847c925cfa97a2f50 Mon Sep 17 00:00:00 2001 From: alex Date: Mon, 9 May 2022 17:24:49 +0200 Subject: [PATCH 2/3] brak() fix --- packages/core/pattern.mjs | 46 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 0080ff83..9f4910a9 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -67,7 +67,7 @@ export class Pattern { * @param {Function} func the function to apply * @returns Pattern */ - withQueryTime(func) { + withQueryTime(func) { return new Pattern((state) => this.query(state.withSpan((span) => span.withTime(func)))); } @@ -75,7 +75,7 @@ export class Pattern { * Similar to {@link Pattern#withQuerySpan|withQuerySpan}, but the function is applied to the timespans * of all haps returned by pattern queries (both `part` timespans, and where * present, `whole` timespans). - * @param {Function} func + * @param {Function} func * @returns Pattern */ withHapSpan(func) { @@ -88,31 +88,31 @@ export class Pattern { * @param {Function} func the function to apply * @returns Pattern */ - withHapTime(func) { + withHapTime(func) { return this.withHapSpan((span) => span.withTime(func)); } /** * Returns a new pattern with the given function applied to the list of haps returned by every query. - * @param {Function} func + * @param {Function} func * @returns Pattern */ - _withHaps(func) { + _withHaps(func) { return new Pattern((state) => func(this.query(state))); } /** * As with {@link Pattern#_withHaps}, but applies the function to every hap, rather than every list of haps. - * @param {Function} func + * @param {Function} func * @returns Pattern */ - _withHap(func) { + _withHap(func) { return this._withHaps((haps) => haps.map(func)); } /** * Returns a new pattern with the context field set to every hap set to the given value. - * @param {*} context + * @param {*} context * @returns Pattern */ _setContext(context) { @@ -121,7 +121,7 @@ export class Pattern { /** * Returns a new pattern with the given function applied to the context field of every hap. - * @param {Function} func + * @param {Function} func * @returns Pattern */ _withContext(func) { @@ -129,7 +129,7 @@ export class Pattern { } /** - * Returns a new pattern with the context field of every hap set to an empty object. + * Returns a new pattern with the context field of every hap set to an empty object. * @returns Pattern */ _stripContext() { @@ -139,8 +139,8 @@ export class Pattern { /** * Returns a new pattern with the given location information added to the * context of every hap. - * @param {Number} start - * @param {Number} end + * @param {Number} start + * @param {Number} end * @returns Pattern */ withLocation(start, end) { @@ -183,7 +183,7 @@ export class Pattern { /** * Returns a new pattern, with the function applied to the value of * each hap. It has the alias {@link Pattern#fmap|fmap}. - * @param {Function} func + * @param {Function} func * @returns Pattern */ withValue(func) { @@ -193,7 +193,7 @@ export class Pattern { /** * see {@link Pattern#withValue|withValue} */ - fmap(func) { + fmap(func) { return this.withValue(func); } @@ -209,7 +209,7 @@ export class Pattern { /** * As with {@link Pattern#_filterHaps}, but the function is applied to values * inside haps. - * @param {Function} value_test + * @param {Function} value_test * @returns Pattern */ _filterValues(value_test) { @@ -231,7 +231,7 @@ export class Pattern { * as its `part` timespan. * @returns Pattern */ - onsetsOnly() { + onsetsOnly() { // Returns a new pattern that will only return haps where the start // of the 'whole' timespan matches the start of the 'part' // timespan, i.e. the haps that include their 'onset'. @@ -278,12 +278,12 @@ export class Pattern { /** * When this method is called on a pattern of functions, it matches its haps * with those in the given pattern of values. A new pattern is returned, with - * each matching value applied to the corresponding function. + * each matching value applied to the corresponding function. * * In this `appBoth` variant, where timespans of the function and value haps * are not the same but do intersect, the resulting hap has a timespan of the * intersection. This applies to both the part and the whole timespan. - * @param {Pattern} pat_val + * @param {Pattern} pat_val * @returns Pattern */ appBoth(pat_val) { @@ -303,7 +303,7 @@ export class Pattern { * on. In practice, this means that the pattern structure, including onsets, * are preserved from the pattern of functions (often referred to as the left * hand or inner pattern). - * @param {Pattern} pat_val + * @param {Pattern} pat_val * @returns Pattern */ appLeft(pat_val) { @@ -333,7 +333,7 @@ export class Pattern { * As with {@link Pattern#appLeft|appLeft}, but `whole` timespans are instead taken from the * pattern of values, i.e. structure is preserved from the right hand/outer * pattern. - * @param {Pattern} pat_val + * @param {Pattern} pat_val * @returns Pattern */ appRight(pat_val) { @@ -824,12 +824,12 @@ export class Pattern { /** * Returns a new pattern where every other cycle is played once, twice as - * fast, and offset in time by one quarter of a cycle. Creates a kind of + * fast, and offset in time by one quarter of a cycle. Creates a kind of * breakbeat feel. * @returns Pattern */ brak() { - return this.every(2, x => fastcat(x, silence)._late(0.25)) + return this.when(slowcat(false, true), (x) => fastcat(x, silence)._late(0.25)); } rev() { @@ -989,7 +989,7 @@ function _composeOp(a, b, func) { } // Make composers -(function() { +(function () { const num = (pat) => pat._asNumber(); const numOrString = (pat) => pat._asNumber(false, true); From aca3bea56b7398900fa88cba9bd8bf9d5da121ea Mon Sep 17 00:00:00 2001 From: alex Date: Mon, 9 May 2022 18:43:02 +0100 Subject: [PATCH 3/3] Implement inside/outside --- packages/core/pattern.mjs | 16 ++++++++++++++++ packages/core/test/pattern.test.mjs | 20 ++++++++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 9f4910a9..6b7fb7a7 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -696,6 +696,14 @@ export class Pattern { return this._fast(Fraction(1).div(factor)); } + _inside(factor, f) { + return f(this._slow(factor))._fast(factor); + } + + _outside(factor, f) { + return f(this._fast(factor))._slow(factor); + } + _ply(factor) { return this.fmap((x) => pure(x)._fast(factor))._squeezeJoin(); } @@ -1400,6 +1408,14 @@ Pattern.prototype.compress = function (...args) { args = args.map(reify); return patternify2(Pattern.prototype._compress)(...args, this); }; +Pattern.prototype.outside = function (...args) { + args = args.map(reify); + return patternify2(Pattern.prototype._outside)(...args, this); +}; +Pattern.prototype.inside = function (...args) { + args = args.map(reify); + return patternify2(Pattern.prototype._inside)(...args, this); +}; // call this after all Patter.prototype.define calls have been executed! (right before evaluate) Pattern.prototype.bootstrap = function () { diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 25088ecf..b37ee286 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -41,6 +41,7 @@ import { tri2, id, ply, + rev } from '../index.mjs'; import { steady } from '../signal.mjs'; @@ -436,6 +437,20 @@ describe('Pattern', function () { // mini('eb3 [c3 g3]/2 ') always plays [c3 g3] }); }); + describe('inside', () => { + it('can rev inside a cycle', () => { + sameFirst(sequence('a', 'b', 'c', 'd').inside(2, rev), + sequence('b', 'a', 'd', 'c') + ); + }); + }); + describe('outside', () => { + it('can rev outside a cycle', () => { + sameFirst(sequence('a', 'b', 'c', 'd')._slow(2).outside(2, rev), + sequence('d', 'c') + ); + }); + }); describe('_filterValues()', function () { it('Filters true', function () { assert.equal( @@ -588,10 +603,7 @@ describe('Pattern', function () { }); describe('brak()', () => { it('Can make something a bit breakbeaty', () => { - sameFirst( - sequence('a', 'b').brak()._fast(2), - sequence('a', 'b', fastcat(silence, 'a'), fastcat('b', silence)) - ) + sameFirst(sequence('a', 'b').brak()._fast(2), sequence('a', 'b', fastcat(silence, 'a'), fastcat('b', silence))); }); }); describe('timeCat()', function () {