more
This commit is contained in:
parent
f696cd554f
commit
38cae8c905
2 changed files with 133 additions and 95 deletions
201
js/strudel.mjs
201
js/strudel.mjs
|
|
@ -9,6 +9,8 @@ function flatten(arr) {
|
||||||
return [].concat(...arr)
|
return [].concat(...arr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var id = a => a
|
||||||
|
|
||||||
// Returns the start of the cycle.
|
// Returns the start of the cycle.
|
||||||
Fraction.prototype.sam = function() {
|
Fraction.prototype.sam = function() {
|
||||||
return Fraction(Math.floor(this))
|
return Fraction(Math.floor(this))
|
||||||
|
|
@ -150,7 +152,7 @@ class Hap {
|
||||||
|
|
||||||
withSpan(func) {
|
withSpan(func) {
|
||||||
// Returns a new event with the function f applies to the event timespan.
|
// Returns a new event with the function f applies to the event timespan.
|
||||||
var whole = this.whole ? func(self.whole) : undefined
|
var whole = this.whole ? func(this.whole) : undefined
|
||||||
return new Hap(whole, func(this.part), this.value)
|
return new Hap(whole, func(this.part), this.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,7 +168,7 @@ class Hap {
|
||||||
}
|
}
|
||||||
|
|
||||||
spanEquals(other) {
|
spanEquals(other) {
|
||||||
return((this.whole === undefined && other.whole === undefined)
|
return((this.whole == undefined && other.whole == undefined)
|
||||||
|| this.whole.equals(other.whole)
|
|| this.whole.equals(other.whole)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +238,7 @@ class Pattern {
|
||||||
return new Pattern(span => this.query(span).filter(event_tespanCyclesst))
|
return new Pattern(span => this.query(span).filter(event_tespanCyclesst))
|
||||||
}
|
}
|
||||||
|
|
||||||
_filterValues(self, value_test) {
|
_filterValues(value_test) {
|
||||||
return new Pattern(span => this.query(span).filter(hap => value_test(hap.value)))
|
return new Pattern(span => this.query(span).filter(hap => value_test(hap.value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,7 +259,7 @@ class Pattern {
|
||||||
var event_vals = pat_val.query(span)
|
var event_vals = pat_val.query(span)
|
||||||
apply = function(event_func, event_val) {
|
apply = function(event_func, event_val) {
|
||||||
var s = event_func.part.intersection(event_val.part)
|
var s = event_func.part.intersection(event_val.part)
|
||||||
if (s === undefined) {
|
if (s == undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
return new Hap(whole_func(event_func.whole, event_val.whole), s, event_func.value(event_val.value))
|
return new Hap(whole_func(event_func.whole, event_val.whole), s, event_func.value(event_val.value))
|
||||||
|
|
@ -267,7 +269,7 @@ class Pattern {
|
||||||
return new Pattern(query)
|
return new Pattern(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
appBoth(self, pat_val) {
|
appBoth(pat_val) {
|
||||||
// Tidal's <*>
|
// Tidal's <*>
|
||||||
var whole_func = function(span_a, span_b) {
|
var whole_func = function(span_a, span_b) {
|
||||||
if (span_a == undefined || span_B == undefined) {
|
if (span_a == undefined || span_B == undefined) {
|
||||||
|
|
@ -318,83 +320,77 @@ class Pattern {
|
||||||
return new Pattern(query)
|
return new Pattern(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
add(other) {
|
|
||||||
// TODO - reify other
|
|
||||||
return this.fmap(x => y => x + y).appLeft(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
get firstCycle() {
|
get firstCycle() {
|
||||||
return this.query(new TimeSpan(Fraction(0), Fraction(1)))
|
return this.query(new TimeSpan(Fraction(0), Fraction(1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// def __radd__(self, other):
|
_opleft(other, func) {
|
||||||
// return self.__add__(other)
|
return this.fmap(func).appLeft(reify(other))
|
||||||
|
}
|
||||||
|
|
||||||
|
add(other) {
|
||||||
|
return this._opleft(other, a => b => a + b)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub(other) {
|
||||||
|
return this._opleft(other, a => b => a - b)
|
||||||
|
}
|
||||||
|
|
||||||
// def __sub__(self, other):
|
union(other) {
|
||||||
// return self.fmap(lambda x: lambda y: x - y).app_left(reify(other))
|
return this._opleft(other, a => b => Object.assign({}, a, b))
|
||||||
|
}
|
||||||
// def __rsub__(self, other):
|
|
||||||
// raise ValueError # or return NotImplemented?
|
|
||||||
|
|
||||||
// def union(self, other):
|
|
||||||
// return self.fmap(lambda x: lambda y: {**x, **y}).app_left(other)
|
|
||||||
|
|
||||||
// def __rshift__(self, other):
|
|
||||||
// """Overrides the >> operator to support combining patterns of
|
|
||||||
// dictionaries (AKA 'control patterns'). Produces the union of
|
|
||||||
// two patterns of dictionaries, with values from right replacing
|
|
||||||
// any with the same name from the left
|
|
||||||
// """
|
|
||||||
// return self.union(other)
|
|
||||||
|
|
||||||
// def __lshift__(self, other):
|
|
||||||
// """Like >>, but matching values from left replace those on the right"""
|
|
||||||
// return self.fmap(lambda x: lambda y: {**y, **x}).app_left(other)
|
|
||||||
|
|
||||||
// def _bind_whole(self, choose_whole, func):
|
_bind_whole(choose_whole, func) {
|
||||||
// pat_val = self
|
var pat_val = this
|
||||||
// def query(span):
|
var query = function(span) {
|
||||||
// def withWhole(a, b):
|
var withWhole = function(a, b) {
|
||||||
// return Event(choose_whole(a.whole, b.whole), b.part,
|
return new Hap(choose_whole(a.whole, b.whole), b.part,
|
||||||
// b.value
|
b.value
|
||||||
// )
|
)
|
||||||
// def match(a):
|
}
|
||||||
// return [withWhole(a, b) for b in func(a.value).query(a.part)]
|
var match = function (a) {
|
||||||
|
return func(a.value).query(a.part).map(b => withWhole(a, b))
|
||||||
|
}
|
||||||
|
return flatten(pat_val.query(span).map(match))
|
||||||
|
}
|
||||||
|
return new Pattern(query)
|
||||||
|
}
|
||||||
|
|
||||||
// return concat([match(ev) for ev in pat_val.query(span)])
|
bind(func) {
|
||||||
// return Pattern(query)
|
var whole_func = function(a, b) {
|
||||||
|
if (a == undefined || b == undefined) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return a.intersection_e(b)
|
||||||
|
}
|
||||||
|
return this._bind_whole(whole_func, func)
|
||||||
|
}
|
||||||
|
|
||||||
// def bind(self, func):
|
join() {
|
||||||
// def whole_func(a, b):
|
// Flattens a pattern of patterns into a pattern, where wholes are
|
||||||
// if a == None or b == None:
|
// the intersection of matched inner and outer events.
|
||||||
// return None
|
return this.bind(id)
|
||||||
// return a.intersection_e(b)
|
}
|
||||||
// return self._bind_whole(whole_func, func)
|
|
||||||
|
|
||||||
// def join(self):
|
inner_bind(func) {
|
||||||
// """Flattens a pattern of patterns into a pattern, where wholes are
|
return this._bind_whole((a, _) => a, func)
|
||||||
// the intersection of matched inner and outer events."""
|
}
|
||||||
// return self.bind(id)
|
|
||||||
|
|
||||||
// def inner_bind(self, func):
|
inner_join() {
|
||||||
// def whole_func(a, b):
|
// Flattens a pattern of patterns into a pattern, where wholes are
|
||||||
// return a
|
// taken from inner events.
|
||||||
// return self._bind_whole(whole_func, func)
|
return this.inner_bind(id)
|
||||||
|
}
|
||||||
// def inner_join(self):
|
|
||||||
// """Flattens a pattern of patterns into a pattern, where wholes are
|
|
||||||
// taken from inner events."""
|
|
||||||
// return self.inner_bind(id)
|
|
||||||
|
|
||||||
// def outer_bind(self, func):
|
outer_bind(func) {
|
||||||
// def whole_func(a, b):
|
return this._bind_whole((_, b) => b, func)
|
||||||
// return b
|
}
|
||||||
// return self._bind_whole(whole_func, func)
|
|
||||||
|
|
||||||
// def outer_join(self):
|
outer_join() {
|
||||||
// """Flattens a pattern of patterns into a pattern, where wholes are
|
// Flattens a pattern of patterns into a pattern, where wholes are
|
||||||
// taken from outer events."""
|
// taken from inner events.
|
||||||
// return self.outer_bind(id)
|
return this.outer_bind(id)
|
||||||
|
}
|
||||||
|
|
||||||
// def _patternify(method):
|
// def _patternify(method):
|
||||||
// def patterned(self, *args):
|
// def patterned(self, *args):
|
||||||
|
|
@ -402,36 +398,38 @@ class Pattern {
|
||||||
// return pat_arg.fmap(lambda arg: method(self,arg)).outer_join()
|
// return pat_arg.fmap(lambda arg: method(self,arg)).outer_join()
|
||||||
// return patterned
|
// return patterned
|
||||||
|
|
||||||
// def _fast(self, factor):
|
_fast(factor) {
|
||||||
// """ Speeds up a pattern by the given factor"""
|
var fastQuery = this.withQueryTime(t => t.mul(factor))
|
||||||
// fastQuery = self.with_query_time(lambda t: t*factor)
|
return fastQuery.withEventTime(t => t.div(factor))
|
||||||
// fastEvents = fastQuery.with_event_time(lambda t: t/factor)
|
}
|
||||||
// return fastEvents
|
|
||||||
// fast = _patternify(_fast)
|
// fast = _patternify(_fast)
|
||||||
|
|
||||||
// def _slow(self, factor):
|
_slow(factor) {
|
||||||
// """ Slow slows down a pattern """
|
return this._fast(1/factor)
|
||||||
// return self._fast(1/factor)
|
}
|
||||||
// slow = _patternify(_slow)
|
// slow = _patternify(_slow)
|
||||||
|
|
||||||
// def _early(self, offset):
|
_early(offset) {
|
||||||
// """ Equivalent of Tidal's <~ operator """
|
// Equivalent of Tidal's <~ operator
|
||||||
// offset = Fraction(offset)
|
offset = Fraction(offset)
|
||||||
// return self.with_query_time(lambda t: t+offset).with_event_time(lambda t: t-offset)
|
return this.withQueryTime(t => t.add(offset)).withEventTime(t => t.sub(offset))
|
||||||
|
}
|
||||||
// early = _patternify(_early)
|
// early = _patternify(_early)
|
||||||
|
|
||||||
// def _late(self, offset):
|
_late(offset) {
|
||||||
// """ Equivalent of Tidal's ~> operator """
|
// Equivalent of Tidal's ~> operator
|
||||||
// return self._early(0-offset)
|
return this._early(0-offset)
|
||||||
// late = _patternify(_late)
|
}
|
||||||
|
// late = _patternify(_late)
|
||||||
|
|
||||||
// def when(self, binary_pat, func):
|
when(binary_pat, func) {
|
||||||
// binary_pat = sequence(binary_pat)
|
//binary_pat = sequence(binary_pat)
|
||||||
// true_pat = binary_pat._filter_values(id)
|
var true_pat = binary_pat._filter_values(id)
|
||||||
// false_pat = binary_pat._filter_values(lambda val: not val)
|
var false_pat = binary_pat._filter_values(val => !val)
|
||||||
// with_pat = true_pat.fmap(lambda _: lambda y: y).app_right(func(self))
|
var with_pat = true_pat.fmap(_ => y => y).app_right(func(this))
|
||||||
// without_pat = false_pat.fmap(lambda _: lambda y: y).app_right(self)
|
var without_pat = false_pat.fmap(_ => y => y).app_right(this)
|
||||||
// return stack(with_pat, without_pat)
|
return stack(with_pat, without_pat)
|
||||||
|
}
|
||||||
|
|
||||||
// def off(self, time_pat, func):
|
// def off(self, time_pat, func):
|
||||||
// return stack(self, self.early(time_pat))
|
// return stack(self, self.early(time_pat))
|
||||||
|
|
@ -474,6 +472,13 @@ class Pattern {
|
||||||
// return str(self.first_cycle())
|
// return str(self.first_cycle())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reify(thing) {
|
||||||
|
if (thing.constructor.name == "Pattern") {
|
||||||
|
return thing
|
||||||
|
}
|
||||||
|
return pure(thing)
|
||||||
|
}
|
||||||
|
|
||||||
function pure(value) {
|
function pure(value) {
|
||||||
// Returns a pattern that repeats the given value once per cycle
|
// Returns a pattern that repeats the given value once per cycle
|
||||||
function query(span) {
|
function query(span) {
|
||||||
|
|
@ -482,5 +487,13 @@ function pure(value) {
|
||||||
return new Pattern(query)
|
return new Pattern(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {TimeSpan, Hap, Pattern, pure, Fraction}
|
function stack(pats) {
|
||||||
|
var pats = pats.map(reify)
|
||||||
|
var query = function(span) {
|
||||||
|
return flatten(pats.map(pat => pat.query(span)))
|
||||||
|
}
|
||||||
|
return new Pattern(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {Fraction, TimeSpan, Hap, Pattern, pure, stack}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import Fraction from 'fraction.js'
|
||||||
|
|
||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
|
|
||||||
import {TimeSpan, Hap, Pattern, pure} from "../js/strudel.mjs";
|
import {TimeSpan, Hap, Pattern, pure, stack} from "../js/strudel.mjs";
|
||||||
|
|
||||||
describe('TimeSpan', function() {
|
describe('TimeSpan', function() {
|
||||||
describe('equals()', function() {
|
describe('equals()', function() {
|
||||||
|
|
@ -66,4 +66,29 @@ describe('Pattern', function() {
|
||||||
assert.equal(pure(3).add(pure(4)).query(new TimeSpan(Fraction(0), Fraction(1)))[0].value, 7)
|
assert.equal(pure(3).add(pure(4)).query(new TimeSpan(Fraction(0), Fraction(1)))[0].value, 7)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
describe('sub()', function () {
|
||||||
|
it('Can subtract things', function() {
|
||||||
|
assert.equal(pure(3).sub(pure(4)).query(new TimeSpan(Fraction(0), Fraction(1)))[0].value, -1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('union()', function () {
|
||||||
|
it('Can union things', function () {
|
||||||
|
assert.deepStrictEqual(pure({a: 4, b: 6}).union(pure({c: 7})).firstCycle[0].value, {a: 4, b: 6, c: 7})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('stack()', function () {
|
||||||
|
it('Can stack things', function () {
|
||||||
|
assert.deepStrictEqual(stack([pure("a"), pure("b"), pure("c")]).firstCycle.map(h => h.value), ["a", "b", "c"])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('_fast()', function () {
|
||||||
|
it('Makes things faster', function () {
|
||||||
|
assert.equal(pure("a")._fast(2).firstCycle.length, 2)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('_slow()', 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"))
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
Loading…
Add table
Add a link
Reference in a new issue