diff --git a/index.html b/index.html index 1baabcb6..6bf19cf2 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,6 @@ - + Bingo diff --git a/js/strudel.mjs b/js/strudel.mjs index d794f070..b2f0e7b0 100644 --- a/js/strudel.mjs +++ b/js/strudel.mjs @@ -191,16 +191,18 @@ class Pattern { this.query = query } - splitQueries() { + _splitQueries() { // Splits queries at cycle boundaries. This makes some calculations // easier to express, as all events are then constrained to happen within // a cycle. - var query = function(span) { - return flatten(span.spanCycles.map(subspan => this.query(subspan))) + var pat = this + var q = function(span) { + return flatten(span.spanCycles.map(subspan => pat.query(subspan))) } - return new Pattern(query) + return new Pattern(q) } + withQuerySpan(func) { return new Pattern(span => this.query(func(span))) } @@ -235,7 +237,7 @@ class Pattern { } _filterEvents(event_test) { - return new Pattern(span => this.query(span).filter(event_tespanCyclesst)) + return new Pattern(span => this.query(span).filter(event_test)) } _filterValues(value_test) { @@ -424,11 +426,15 @@ class Pattern { when(binary_pat, func) { //binary_pat = sequence(binary_pat) - var true_pat = binary_pat._filter_values(id) - var false_pat = binary_pat._filter_values(val => !val) - var with_pat = true_pat.fmap(_ => y => y).app_right(func(this)) - var without_pat = false_pat.fmap(_ => y => y).app_right(this) - return stack(with_pat, without_pat) + var true_pat = binary_pat._filterValues(id) + var false_pat = binary_pat._filterValues(val => !val) + var with_pat = true_pat.fmap(_ => y => y).appRight(func(this)) + var without_pat = false_pat.fmap(_ => y => y).appRight(this) + return stack([with_pat, without_pat]) + } + + off(time_pat, func) { + return stack([this, func(this._early(time_pat))]) } off(time_pat, func) { @@ -504,5 +510,27 @@ function stack(pats) { return new Pattern(query) } -export {Fraction, TimeSpan, Hap, Pattern, pure, stack} +function slowcat(pats) { + // Concatenation: combines a list of patterns, switching between them + // successively, one per cycle. + // (currently behaves slightly differently from Tidal) + //var pats = pats.map(reify) + var query = function(span) { + var pat = pats[Math.floor(span.begin) % pats.length] + return pat.query(span) + } + return new Pattern(query)._splitQueries() +} + +function fastcat(pats) { + // Concatenation: as with slowcat, but squashes a cycle from each + // pattern into one cycle + return slowcat(pats)._fast(pats.length) +} + +function cat(pats) { + return fastcat(pats) +} + +export {Fraction, TimeSpan, Hap, Pattern, pure, stack, slowcat, fastcat, cat} diff --git a/test/pattern.test.mjs b/test/pattern.test.mjs index 34628c7e..ddb2e981 100644 --- a/test/pattern.test.mjs +++ b/test/pattern.test.mjs @@ -2,7 +2,7 @@ import Fraction from 'fraction.js' import { strict as assert } from 'assert'; -import {TimeSpan, Hap, Pattern, pure, stack} from "../js/strudel.mjs"; +import {TimeSpan, Hap, Pattern, pure, stack, fastcat, slowcat, cat} from "../js/strudel.mjs"; describe('TimeSpan', function() { describe('equals()', function() { @@ -90,5 +90,29 @@ describe('Pattern', function() { it('Makes things slower', function () { assert.deepStrictEqual(pure("a")._slow(2).firstCycle[0], new Hap(new TimeSpan(Fraction(0),Fraction(2)), new TimeSpan(Fraction(0), Fraction(1)), "a")) }) + }) + describe('_filterValues()', function () { + it('Filters true', function () { + assert.equal(pure(true)._filterValues(x => x).firstCycle.length, 1) + }) + }) + describe('when()', function () { + it('Always faster', function () { + assert.equal(pure("a").when(pure(true), x => x._fast(2)).firstCycle.length, 2) + }) + it('Never faster', function () { + assert.equal(pure("a").when(pure(false), x => x._fast(2)).firstCycle.length, 1) + }) + }) + describe('fastcat()', function () { + it('Can concatenate two things', function () { + assert.deepStrictEqual(fastcat([pure("a"), pure("b")]).firstCycle.map(x => x.value), ["a", "b"]) + }) + }) + describe('slowcat()', function () { + it('Can concatenate things slowly', function () { + assert.deepStrictEqual(slowcat([pure("a"), pure("b")]).firstCycle.map(x => x.value), ["a"]) + assert.deepStrictEqual(slowcat([pure("a"), pure("b")])._early(1).firstCycle.map(x => x.value), ["b"]) + }) }) }) \ No newline at end of file