Tidying up core (#256)

* remove _ prefixes except for functions to be patternified
* categorise pattern methods
* experimental support for `.add.squeeze` and friends as alternative to `.addSqueeze`
* `every` is now an alias for `firstOf` with additional `lastOf` (which every will become an alias for next)
This commit is contained in:
Alex McLean 2022-11-22 08:51:25 +00:00 committed by GitHub
parent 4cf412b93d
commit e1a532500e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 738 additions and 603 deletions

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,7 @@ export const signal = (func) => {
}; };
export const isaw = signal((t) => 1 - (t % 1)); export const isaw = signal((t) => 1 - (t % 1));
export const isaw2 = isaw._toBipolar(); export const isaw2 = isaw.toBipolar();
/** /**
* A sawtooth signal between 0 and 1. * A sawtooth signal between 0 and 1.
@ -33,7 +33,7 @@ export const isaw2 = isaw._toBipolar();
* *
*/ */
export const saw = signal((t) => t % 1); export const saw = signal((t) => t % 1);
export const saw2 = saw._toBipolar(); export const saw2 = saw.toBipolar();
export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t)); export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t));
@ -45,7 +45,7 @@ export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t));
* sine.segment(16).range(0,15).slow(2).scale('C minor').note() * sine.segment(16).range(0,15).slow(2).scale('C minor').note()
* *
*/ */
export const sine = sine2._fromBipolar(); export const sine = sine2.fromBipolar();
/** /**
* A cosine signal between 0 and 1. * A cosine signal between 0 and 1.
@ -67,7 +67,7 @@ export const cosine2 = sine2._early(Fraction(1).div(4));
* *
*/ */
export const square = signal((t) => Math.floor((t * 2) % 2)); export const square = signal((t) => Math.floor((t * 2) % 2));
export const square2 = square._toBipolar(); export const square2 = square.toBipolar();
/** /**
* A triangle signal between 0 and 1. * A triangle signal between 0 and 1.
@ -127,7 +127,7 @@ export const rand = signal(timeToRand);
/** /**
* A continuous pattern of random numbers, between -1 and 1 * A continuous pattern of random numbers, between -1 and 1
*/ */
export const rand2 = rand._toBipolar(); export const rand2 = rand.toBipolar();
export const _brandBy = (p) => rand.fmap((x) => x < p); export const _brandBy = (p) => rand.fmap((x) => x < p);
export const brandBy = (pPat) => reify(pPat).fmap(_brandBy).innerJoin(); export const brandBy = (pPat) => reify(pPat).fmap(_brandBy).innerJoin();
@ -201,7 +201,7 @@ Pattern.prototype.choose = function (...xs) {
* @returns {Pattern} * @returns {Pattern}
*/ */
Pattern.prototype.choose2 = function (...xs) { Pattern.prototype.choose2 = function (...xs) {
return chooseWith(this._fromBipolar(), xs); return chooseWith(this.fromBipolar(), xs);
}; };
/** /**
@ -259,7 +259,7 @@ export const perlinWith = (pat) => {
export const perlin = perlinWith(time.fmap((v) => Number(v))); export const perlin = perlinWith(time.fmap((v) => Number(v)));
Pattern.prototype._degradeByWith = function (withPat, x) { Pattern.prototype._degradeByWith = function (withPat, x) {
return this.fmap((a) => (_) => a).appLeft(withPat._filterValues((v) => v > x)); return this.fmap((a) => (_) => a).appLeft(withPat.filterValues((v) => v > x));
}; };
/** /**

View File

@ -49,7 +49,7 @@ import { steady } from '../signal.mjs';
import controls from '../controls.mjs'; import controls from '../controls.mjs';
const { n } = controls; const { n, s } = controls;
const st = (begin, end) => new State(ts(begin, end)); const st = (begin, end) => new State(ts(begin, end));
const ts = (begin, end) => new TimeSpan(Fraction(begin), Fraction(end)); const ts = (begin, end) => new TimeSpan(Fraction(begin), Fraction(end));
const hap = (whole, part, value, context = {}) => new Hap(whole, part, value, context); const hap = (whole, part, value, context = {}) => new Hap(whole, part, value, context);
@ -58,7 +58,7 @@ const third = Fraction(1, 3);
const twothirds = Fraction(2, 3); const twothirds = Fraction(2, 3);
const sameFirst = (a, b) => { const sameFirst = (a, b) => {
return expect(a._sortHapsByPart().firstCycle()).toStrictEqual(b._sortHapsByPart().firstCycle()); return expect(a.sortHapsByPart().firstCycle()).toStrictEqual(b.sortHapsByPart().firstCycle());
}; };
describe('TimeSpan', () => { describe('TimeSpan', () => {
@ -382,7 +382,7 @@ describe('Pattern', () => {
); );
}); });
it('copes with breaking up events across cycles', () => { it('copes with breaking up events across cycles', () => {
expect(pure('a').slow(2)._fastGap(2)._setContext({}).query(st(0, 2))).toStrictEqual([ expect(pure('a').slow(2)._fastGap(2).setContext({}).query(st(0, 2))).toStrictEqual([
hap(ts(0, 1), ts(0, 0.5), 'a'), hap(ts(0, 1), ts(0, 0.5), 'a'),
hap(ts(0.5, 1.5), ts(1, 1.5), 'a'), hap(ts(0.5, 1.5), ts(1, 1.5), 'a'),
]); ]);
@ -446,8 +446,8 @@ describe('Pattern', () => {
}); });
describe('slow()', () => { describe('slow()', () => {
it('Supports zero-length queries', () => { it('Supports zero-length queries', () => {
expect(steady('a').slow(1)._setContext({}).queryArc(0, 0)).toStrictEqual( expect(steady('a').slow(1).setContext({}).queryArc(0, 0)).toStrictEqual(
steady('a')._setContext({}).queryArc(0, 0), steady('a').setContext({}).queryArc(0, 0),
); );
}); });
}); });
@ -465,7 +465,7 @@ describe('Pattern', () => {
it('Filters true', () => { it('Filters true', () => {
expect( expect(
pure(true) pure(true)
._filterValues((x) => x) .filterValues((x) => x)
.firstCycle().length, .firstCycle().length,
).toBe(1); ).toBe(1);
}); });
@ -490,7 +490,7 @@ describe('Pattern', () => {
pure(10) pure(10)
.when(slowcat(true, false), (x) => x.add(3)) .when(slowcat(true, false), (x) => x.add(3))
.fast(4) .fast(4)
._sortHapsByPart() .sortHapsByPart()
.firstCycle(), .firstCycle(),
).toStrictEqual(fastcat(13, 10, 13, 10).firstCycle()); ).toStrictEqual(fastcat(13, 10, 13, 10).firstCycle());
}); });
@ -577,26 +577,26 @@ describe('Pattern', () => {
}); });
}); });
describe('every()', () => { describe('firstOf()', () => {
it('Can apply a function every 3rd time', () => { it('Can apply a function every 3rd time', () => {
expect( expect(
pure('a') pure('a')
.every(3, (x) => x._fast(2)) .firstOf(3, (x) => x._fast(2))
._fast(3) ._fast(3)
.firstCycle(), .firstCycle(),
).toStrictEqual(sequence(sequence('a', 'a'), 'a', 'a').firstCycle()); ).toStrictEqual(sequence(sequence('a', 'a'), 'a', 'a').firstCycle());
}); });
it('works with currying', () => { it('works with currying', () => {
expect(pure('a').every(3, fast(2))._fast(3).firstCycle()).toStrictEqual( expect(pure('a').firstOf(3, fast(2))._fast(3).firstCycle()).toStrictEqual(
sequence(sequence('a', 'a'), 'a', 'a').firstCycle(), sequence(sequence('a', 'a'), 'a', 'a').firstCycle(),
); );
expect(sequence(3, 4, 5).every(3, add(3)).fast(5).firstCycle()).toStrictEqual( expect(sequence(3, 4, 5).firstOf(3, add(3)).fast(5).firstCycle()).toStrictEqual(
sequence(6, 7, 8, 3, 4, 5, 3, 4, 5, 6, 7, 8, 3, 4, 5).firstCycle(), sequence(6, 7, 8, 3, 4, 5, 3, 4, 5, 6, 7, 8, 3, 4, 5).firstCycle(),
); );
expect(sequence(3, 4, 5).every(2, sub(1)).fast(5).firstCycle()).toStrictEqual( expect(sequence(3, 4, 5).firstOf(2, sub(1)).fast(5).firstCycle()).toStrictEqual(
sequence(2, 3, 4, 3, 4, 5, 2, 3, 4, 3, 4, 5, 2, 3, 4).firstCycle(), sequence(2, 3, 4, 3, 4, 5, 2, 3, 4, 3, 4, 5, 2, 3, 4).firstCycle(),
); );
expect(sequence(3, 4, 5).every(3, add(3)).every(2, sub(1)).fast(2).firstCycle()).toStrictEqual( expect(sequence(3, 4, 5).firstOf(3, add(3)).firstOf(2, sub(1)).fast(2).firstCycle()).toStrictEqual(
sequence(5, 6, 7, 3, 4, 5).firstCycle(), sequence(5, 6, 7, 3, 4, 5).firstCycle(),
); );
}); });
@ -692,7 +692,7 @@ describe('Pattern', () => {
it('Can set the hap context', () => { it('Can set the hap context', () => {
expect( expect(
pure('a') pure('a')
._setContext([ .setContext([
[ [
[0, 1], [0, 1],
[1, 2], [1, 2],
@ -713,13 +713,13 @@ describe('Pattern', () => {
it('Can update the hap context', () => { it('Can update the hap context', () => {
expect( expect(
pure('a') pure('a')
._setContext([ .setContext([
[ [
[0, 1], [0, 1],
[1, 2], [1, 2],
], ],
]) ])
._withContext((c) => [ .withContext((c) => [
...c, ...c,
[ [
[3, 4], [3, 4],
@ -743,7 +743,7 @@ describe('Pattern', () => {
}); });
describe('apply', () => { describe('apply', () => {
it('Can apply a function', () => { it('Can apply a function', () => {
expect(sequence('a', 'b')._apply(fast(2)).firstCycle()).toStrictEqual(sequence('a', 'b').fast(2).firstCycle()); expect(sequence('a', 'b').apply(fast(2)).firstCycle()).toStrictEqual(sequence('a', 'b').fast(2).firstCycle());
}), }),
it('Can apply a pattern of functions', () => { it('Can apply a pattern of functions', () => {
expect(sequence('a', 'b').apply(fast(2)).firstCycle()).toStrictEqual(sequence('a', 'b').fast(2).firstCycle()); expect(sequence('a', 'b').apply(fast(2)).firstCycle()).toStrictEqual(sequence('a', 'b').fast(2).firstCycle());
@ -784,18 +784,18 @@ describe('Pattern', () => {
}); });
describe('jux', () => { describe('jux', () => {
it('Can juxtapose', () => { it('Can juxtapose', () => {
expect(pure({ a: 1 }).jux(fast(2))._sortHapsByPart().firstCycle()).toStrictEqual( expect(pure({ a: 1 }).jux(fast(2)).sortHapsByPart().firstCycle()).toStrictEqual(
stack(pure({ a: 1, pan: 0 }), pure({ a: 1, pan: 1 }).fast(2)) stack(pure({ a: 1, pan: 0 }), pure({ a: 1, pan: 1 }).fast(2))
._sortHapsByPart() .sortHapsByPart()
.firstCycle(), .firstCycle(),
); );
}); });
}); });
describe('juxBy', () => { describe('juxBy', () => {
it('Can juxtapose by half', () => { it('Can juxtapose by half', () => {
expect(pure({ a: 1 }).juxBy(0.5, fast(2))._sortHapsByPart().firstCycle()).toStrictEqual( expect(pure({ a: 1 }).juxBy(0.5, fast(2)).sortHapsByPart().firstCycle()).toStrictEqual(
stack(pure({ a: 1, pan: 0.25 }), pure({ a: 1, pan: 0.75 }).fast(2)) stack(pure({ a: 1, pan: 0.25 }), pure({ a: 1, pan: 0.75 }).fast(2))
._sortHapsByPart() .sortHapsByPart()
.firstCycle(), .firstCycle(),
); );
}); });
@ -805,7 +805,7 @@ describe('Pattern', () => {
expect( expect(
sequence('a', ['a', 'a']) sequence('a', ['a', 'a'])
.fmap((a) => fastcat('b', 'c')) .fmap((a) => fastcat('b', 'c'))
._squeezeJoin() .squeezeJoin()
.firstCycle(), .firstCycle(),
).toStrictEqual( ).toStrictEqual(
sequence( sequence(
@ -820,7 +820,7 @@ describe('Pattern', () => {
it('Squeezes to the correct cycle', () => { it('Squeezes to the correct cycle', () => {
expect( expect(
pure(time.struct(true)) pure(time.struct(true))
._squeezeJoin() .squeezeJoin()
.queryArc(3, 4) .queryArc(3, 4)
.map((x) => x.value), .map((x) => x.value),
).toStrictEqual([Fraction(3.5)]); ).toStrictEqual([Fraction(3.5)]);
@ -857,7 +857,7 @@ describe('Pattern', () => {
); );
}); });
it('Can chop(2,3)', () => { it('Can chop(2,3)', () => {
expect(pure({ sound: 'a' }).fast(2).chop(2, 3)._sortHapsByPart().firstCycle()).toStrictEqual( expect(pure({ sound: 'a' }).fast(2).chop(2, 3).sortHapsByPart().firstCycle()).toStrictEqual(
sequence( sequence(
[ [
{ sound: 'a', begin: 0, end: 0.5 }, { sound: 'a', begin: 0, end: 0.5 },
@ -869,7 +869,7 @@ describe('Pattern', () => {
{ sound: 'a', begin: 2 / 3, end: 1 }, { sound: 'a', begin: 2 / 3, end: 1 },
], ],
) )
._sortHapsByPart() .sortHapsByPart()
.firstCycle(), .firstCycle(),
); );
}); });
@ -893,4 +893,11 @@ describe('Pattern', () => {
); );
}); });
}); });
describe('alignments', () => {
it('Can squeeze arguments', () => {
expect(sequence(1, 2).add.squeeze(4, 5).firstCycle()).toStrictEqual(
sequence(5, 6, 6, 7).firstCycle()
);
});
});
}); });

View File

@ -13,7 +13,7 @@ const { fastcat, evalScope } = strudel;
describe('evaluate', async () => { describe('evaluate', async () => {
await evalScope({ mini }, strudel); await evalScope({ mini }, strudel);
const ev = async (code) => (await evaluate(code)).pattern._firstCycleValues; const ev = async (code) => (await evaluate(code)).pattern.firstCycleValues;
it('Should evaluate strudel functions', async () => { it('Should evaluate strudel functions', async () => {
expect(await ev('pure("c3")')).toEqual(['c3']); expect(await ev('pure("c3")')).toEqual(['c3']);
expect(await ev('cat("c3")')).toEqual(['c3']); expect(await ev('cat("c3")')).toEqual(['c3']);

View File

@ -9,8 +9,8 @@ import '@strudel.cycles/core/euclid.mjs';
import { describe, expect, it } from 'vitest'; import { describe, expect, it } from 'vitest';
describe('mini', () => { describe('mini', () => {
const minV = (v) => mini(v)._firstCycleValues; const minV = (v) => mini(v).firstCycleValues;
const minS = (v) => mini(v)._showFirstCycle; const minS = (v) => mini(v).showFirstCycle;
it('supports single elements', () => { it('supports single elements', () => {
expect(minV('a')).toEqual(['a']); expect(minV('a')).toEqual(['a']);
}); });

View File

@ -12,6 +12,6 @@ import { describe, it, expect } from 'vitest';
describe('tonal', () => { describe('tonal', () => {
it('Should run tonal functions ', () => { it('Should run tonal functions ', () => {
expect(pure('c3').scale('C major').scaleTranspose(1)._firstCycleValues).toEqual(['D3']); expect(pure('c3').scale('C major').scaleTranspose(1).firstCycleValues).toEqual(['D3']);
}); });
}); });

View File

@ -75,7 +75,7 @@ function scaleOffset(scale, offset, note) {
*/ */
Pattern.prototype._transpose = function (intervalOrSemitones) { Pattern.prototype._transpose = function (intervalOrSemitones) {
return this._withHap((hap) => { return this.withHap((hap) => {
const interval = !isNaN(Number(intervalOrSemitones)) const interval = !isNaN(Number(intervalOrSemitones))
? Interval.fromSemitones(intervalOrSemitones /* as number */) ? Interval.fromSemitones(intervalOrSemitones /* as number */)
: String(intervalOrSemitones); : String(intervalOrSemitones);
@ -111,7 +111,7 @@ Pattern.prototype._transpose = function (intervalOrSemitones) {
*/ */
Pattern.prototype._scaleTranspose = function (offset /* : number | string */) { Pattern.prototype._scaleTranspose = function (offset /* : number | string */) {
return this._withHap((hap) => { return this.withHap((hap) => {
if (!hap.context.scale) { if (!hap.context.scale) {
throw new Error('can only use scaleTranspose after .scale'); throw new Error('can only use scaleTranspose after .scale');
} }
@ -142,7 +142,7 @@ Pattern.prototype._scaleTranspose = function (offset /* : number | string */) {
*/ */
Pattern.prototype._scale = function (scale /* : string */) { Pattern.prototype._scale = function (scale /* : string */) {
return this._withHap((hap) => { return this.withHap((hap) => {
let note = hap.value; let note = hap.value;
const asNumber = Number(note); const asNumber = Number(note);
if (!isNaN(asNumber)) { if (!isNaN(asNumber)) {

View File

@ -51,7 +51,7 @@ Pattern.prototype.voicings = function (range) {
} }
return this.fmapNested((event) => { return this.fmapNested((event) => {
lastVoicing = getVoicing(event.value, lastVoicing, range); lastVoicing = getVoicing(event.value, lastVoicing, range);
return stack(...lastVoicing)._withContext(() => ({ return stack(...lastVoicing).withContext(() => ({
locations: event.context.locations || [], locations: event.context.locations || [],
})); }));
}); });

View File

@ -13,6 +13,6 @@ describe('tone', () => {
// const s = synth().chain(out()); // TODO: mock audio context? // const s = synth().chain(out()); // TODO: mock audio context?
// assert.deepStrictEqual(s, new Tone.Synth().chain(out())); // assert.deepStrictEqual(s, new Tone.Synth().chain(out()));
const s = {}; const s = {};
expect(pure('c3').tone(s)._firstCycleValues).toEqual(['c3']); expect(pure('c3').tone(s).firstCycleValues).toEqual(['c3']);
}); });
}); });

View File

@ -1,5 +1,22 @@
// Vitest Snapshot v1 // Vitest Snapshot v1
exports[`runs examples > example "_apply" example index 0 1`] = `
[
"0/1 -> 1/1: {\\"note\\":\\"C3\\"}",
"0/1 -> 1/1: {\\"note\\":\\"Eb3\\"}",
"0/1 -> 1/1: {\\"note\\":\\"G3\\"}",
"1/1 -> 2/1: {\\"note\\":\\"Eb3\\"}",
"1/1 -> 2/1: {\\"note\\":\\"G3\\"}",
"1/1 -> 2/1: {\\"note\\":\\"Bb3\\"}",
"2/1 -> 3/1: {\\"note\\":\\"G3\\"}",
"2/1 -> 3/1: {\\"note\\":\\"Bb3\\"}",
"2/1 -> 3/1: {\\"note\\":\\"D4\\"}",
"3/1 -> 4/1: {\\"note\\":\\"C3\\"}",
"3/1 -> 4/1: {\\"note\\":\\"Eb3\\"}",
"3/1 -> 4/1: {\\"note\\":\\"G3\\"}",
]
`;
exports[`runs examples > example "accelerate" example index 0 1`] = ` exports[`runs examples > example "accelerate" example index 0 1`] = `
[ [
"0/1 -> 2/1: {\\"s\\":\\"sax\\",\\"accelerate\\":0}", "0/1 -> 2/1: {\\"s\\":\\"sax\\",\\"accelerate\\":0}",
@ -1593,6 +1610,27 @@ exports[`runs examples > example "fastcat" example index 0 1`] = `
] ]
`; `;
exports[`runs examples > example "firstOf" example index 0 1`] = `
[
"3/4 -> 1/1: {\\"note\\":\\"c3\\"}",
"1/2 -> 3/4: {\\"note\\":\\"d3\\"}",
"1/4 -> 1/2: {\\"note\\":\\"e3\\"}",
"0/1 -> 1/4: {\\"note\\":\\"g3\\"}",
"1/1 -> 5/4: {\\"note\\":\\"c3\\"}",
"5/4 -> 3/2: {\\"note\\":\\"d3\\"}",
"3/2 -> 7/4: {\\"note\\":\\"e3\\"}",
"7/4 -> 2/1: {\\"note\\":\\"g3\\"}",
"2/1 -> 9/4: {\\"note\\":\\"c3\\"}",
"9/4 -> 5/2: {\\"note\\":\\"d3\\"}",
"5/2 -> 11/4: {\\"note\\":\\"e3\\"}",
"11/4 -> 3/1: {\\"note\\":\\"g3\\"}",
"3/1 -> 13/4: {\\"note\\":\\"c3\\"}",
"13/4 -> 7/2: {\\"note\\":\\"d3\\"}",
"7/2 -> 15/4: {\\"note\\":\\"e3\\"}",
"15/4 -> 4/1: {\\"note\\":\\"g3\\"}",
]
`;
exports[`runs examples > example "freq" example index 0 1`] = ` exports[`runs examples > example "freq" example index 0 1`] = `
[ [
"0/1 -> 1/4: {\\"freq\\":220,\\"s\\":\\"superzow\\"}", "0/1 -> 1/4: {\\"freq\\":220,\\"s\\":\\"superzow\\"}",
@ -1793,6 +1831,27 @@ exports[`runs examples > example "iterBack" example index 0 1`] = `
] ]
`; `;
exports[`runs examples > example "lastOf" example index 0 1`] = `
[
"0/1 -> 1/4: {\\"note\\":\\"c3\\"}",
"1/4 -> 1/2: {\\"note\\":\\"d3\\"}",
"1/2 -> 3/4: {\\"note\\":\\"e3\\"}",
"3/4 -> 1/1: {\\"note\\":\\"g3\\"}",
"1/1 -> 5/4: {\\"note\\":\\"c3\\"}",
"5/4 -> 3/2: {\\"note\\":\\"d3\\"}",
"3/2 -> 7/4: {\\"note\\":\\"e3\\"}",
"7/4 -> 2/1: {\\"note\\":\\"g3\\"}",
"2/1 -> 9/4: {\\"note\\":\\"c3\\"}",
"9/4 -> 5/2: {\\"note\\":\\"d3\\"}",
"5/2 -> 11/4: {\\"note\\":\\"e3\\"}",
"11/4 -> 3/1: {\\"note\\":\\"g3\\"}",
"15/4 -> 4/1: {\\"note\\":\\"c3\\"}",
"7/2 -> 15/4: {\\"note\\":\\"d3\\"}",
"13/4 -> 7/2: {\\"note\\":\\"e3\\"}",
"3/1 -> 13/4: {\\"note\\":\\"g3\\"}",
]
`;
exports[`runs examples > example "late" example index 0 1`] = ` exports[`runs examples > example "late" example index 0 1`] = `
[ [
"0/1 -> 1/2: {\\"s\\":\\"bd\\"}", "0/1 -> 1/2: {\\"s\\":\\"bd\\"}",