Tidy up composers, supporting function patterns

This commit is contained in:
alex 2022-04-24 11:27:52 +01:00
parent a194b908e7
commit df5ffb4736

View File

@ -2,6 +2,7 @@ import TimeSpan from './timespan.mjs';
import Fraction from './fraction.mjs'; import Fraction from './fraction.mjs';
import Hap from './hap.mjs'; import Hap from './hap.mjs';
import State from './state.mjs'; import State from './state.mjs';
import { unionWithObj } from './value.mjs';
import { isNote, toMidi, compose, removeUndefineds, flatten, id, listRange, curry, mod } from './util.mjs'; import { isNote, toMidi, compose, removeUndefineds, flatten, id, listRange, curry, mod } from './util.mjs';
@ -395,26 +396,36 @@ export class Pattern {
return this.innerBind(id); return this.innerBind(id);
} }
_resetJoin(restart=false) { // Flatterns patterns of patterns, by resetting inner patterns at onsets of outer pattern events
_resetJoin(restart = false) {
const pat_of_pats = this; const pat_of_pats = this;
return new Pattern((state) => { return new Pattern((state) => {
return pat_of_pats return (
.discreteOnly() pat_of_pats
.query(state) // drop continuous events from the outer pattern.
.map((outer_hap) => { .discreteOnly()
return outer_hap.value .query(state)
.late(restart ? outer_hap.whole.begin : outer_hap.whole.begin.cyclePos()) .map((outer_hap) => {
.query(state) return (
.map((inner_hap) => outer_hap.value
new Hap( // reset = align the inner pattern cycle start to outer pattern events
inner_hap.whole ? inner_hap.whole.intersection(outer_hap.whole) : undefined, // restart = align the inner pattern cycle zero to outer pattern events
inner_hap.part.intersection(outer_hap.part), .late(restart ? outer_hap.whole.begin : outer_hap.whole.begin.cyclePos())
inner_hap.value, .query(state)
).setContext(outer_hap.combineContext(inner_hap)), .map((inner_hap) =>
) new Hap(
.filter((hap) => hap.part); // Supports continuous events in the inner pattern
}) inner_hap.whole ? inner_hap.whole.intersection(outer_hap.whole) : undefined,
.flat(); inner_hap.part.intersection(outer_hap.part),
inner_hap.value,
).setContext(outer_hap.combineContext(inner_hap)),
)
// Drop events that didn't intersect
.filter((hap) => hap.part)
);
})
.flat()
);
}); });
} }
@ -425,7 +436,7 @@ export class Pattern {
_squeezeJoin() { _squeezeJoin() {
const pat_of_pats = this; const pat_of_pats = this;
function query(state) { function query(state) {
const haps = pat_of_pats.query(state); const haps = pat_of_pats.discreteOnly().query(state);
function flatHap(outerHap) { function flatHap(outerHap) {
const pat = outerHap.value._compressSpan(outerHap.wholeOrPart().cycleArc()); const pat = outerHap.value._compressSpan(outerHap.wholeOrPart().cycleArc());
const innerHaps = pat.query(state.setSpan(outerHap.part)); const innerHaps = pat.query(state.setSpan(outerHap.part));
@ -763,54 +774,41 @@ export class Pattern {
} }
} }
// TODO - adopt value.mjs fully..
function _composeOp(a, b, func) {
function _nonFunctionObject(x) {
return x instanceof Object && (!(x instanceof Function))
}
if (_nonFunctionObject(a) || _nonFunctionObject(b)) {
if (!_nonFunctionObject(a)) {
a = { value: a };
}
if (!_nonFunctionObject(b)) {
b = { value: b };
}
return unionWithObj(a, b, func);
}
return func(a, b);
}
// pattern composers // pattern composers
const composers = { const composers = {
set: [ set: (a, b) => b,
(a) => (b) => { const: id,
// If an object is involved, do a union, discarding matching keys from a. add: (a, b) => a + b,
// Otherwise, just return b. sub: (a, b) => a - b,
if (a instanceof Object || b instanceof Object) { mul: (a, b) => a * b,
if (!a instanceof Object) { div: (a, b) => a / b,
a = { value: a }; mod: mod,
} func: (a, b) => b(a)
if (!b instanceof Object) {
b = { value: b };
}
return Object.assign({}, a, b);
}
return b;
},
id,
],
const: [
(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({}, b, a);
}
return a;
},
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)) { for (const [name, op] of Object.entries(composers)) {
for (const opType of ['', 'Flip', 'Sect', 'Squeeze', 'SqueezeFlip', 'Reset', 'Restart']) { for (const opType of ['', 'Flip', 'Sect', 'Squeeze', 'SqueezeFlip', 'Reset', 'Restart']) {
Pattern.prototype[name + opType] = function (...other) { Pattern.prototype[name + opType] = function (...other) {
return op[1](this)['_op' + opType](sequence(other), op[0]); return this['_op' + opType](sequence(other), (a) => (b) => _composeOp(a, b, op));
}; };
if (name === "set" && opType !== '') { if (name === 'set' && opType !== '') {
Pattern.prototype[opType.toLowerCase()] = Pattern.prototype[name + opType]; Pattern.prototype[opType.toLowerCase()] = Pattern.prototype[name + opType];
} }
} }