breaking: refactor controls
- multiple args wont be interpreted as sequence anymore - you can now pass value, pat to set value to pat
This commit is contained in:
parent
e782dc09dd
commit
11196fb1e6
6 changed files with 40 additions and 38 deletions
|
|
@ -4,13 +4,14 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Pattern, register, sequence } from './pattern.mjs';
|
import { Pattern, register, reify } from './pattern.mjs';
|
||||||
|
|
||||||
export function createParam(names) {
|
export function createParam(names) {
|
||||||
let isMulti = Array.isArray(names);
|
let isMulti = Array.isArray(names);
|
||||||
names = !isMulti ? [names] : names;
|
names = !isMulti ? [names] : names;
|
||||||
const name = names[0];
|
const name = names[0];
|
||||||
|
|
||||||
|
// todo: make this less confusing
|
||||||
const withVal = (xs) => {
|
const withVal = (xs) => {
|
||||||
let bag;
|
let bag;
|
||||||
// check if we have an object with an unnamed control (.value)
|
// check if we have an object with an unnamed control (.value)
|
||||||
|
|
@ -35,25 +36,34 @@ export function createParam(names) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const func = (...pats) => sequence(...pats).withValue(withVal);
|
// todo: make this less confusing
|
||||||
|
const func = function (value, pat) {
|
||||||
const setter = function (...pats) {
|
if (!pat) {
|
||||||
if (!pats.length) {
|
return reify(value).withValue(withVal);
|
||||||
return this.fmap(withVal);
|
|
||||||
}
|
}
|
||||||
return this.set(func(...pats));
|
if (typeof value === 'undefined') {
|
||||||
|
return pat.fmap(withVal);
|
||||||
|
}
|
||||||
|
return pat.set(reify(value).withValue(withVal));
|
||||||
|
};
|
||||||
|
Pattern.prototype[name] = function (value) {
|
||||||
|
return func(value, this);
|
||||||
};
|
};
|
||||||
Pattern.prototype[name] = setter;
|
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
// maps control alias names to the "main" control name
|
// maps control alias names to the "main" control name
|
||||||
const controlAlias = new Map();
|
const controlAlias = new Map();
|
||||||
|
|
||||||
|
export function isControlName(name) {
|
||||||
|
return controlAlias.has(name);
|
||||||
|
}
|
||||||
|
|
||||||
export function registerControl(names, ...aliases) {
|
export function registerControl(names, ...aliases) {
|
||||||
const name = Array.isArray(names) ? names[0] : names;
|
const name = Array.isArray(names) ? names[0] : names;
|
||||||
let bag = {};
|
let bag = {};
|
||||||
bag[name] = createParam(names);
|
bag[name] = createParam(names);
|
||||||
|
controlAlias.set(name, name);
|
||||||
aliases.forEach((alias) => {
|
aliases.forEach((alias) => {
|
||||||
bag[alias] = bag[name];
|
bag[alias] = bag[name];
|
||||||
controlAlias.set(alias, name);
|
controlAlias.set(alias, name);
|
||||||
|
|
|
||||||
|
|
@ -279,26 +279,26 @@ const _rearrangeWith = (ipat, n, pat) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name shuffle
|
|
||||||
* Slices a pattern into the given number of parts, then plays those parts in random order.
|
* Slices a pattern into the given number of parts, then plays those parts in random order.
|
||||||
* Each part will be played exactly once per cycle.
|
* Each part will be played exactly once per cycle.
|
||||||
|
* @name shuffle
|
||||||
* @example
|
* @example
|
||||||
* note("c d e f").sound("piano").shuffle(4)
|
* note("c d e f").sound("piano").shuffle(4)
|
||||||
* @example
|
* @example
|
||||||
* note("c d e f".shuffle(4), "g").sound("piano")
|
* seq("c d e f".shuffle(4), "g").note().sound("piano")
|
||||||
*/
|
*/
|
||||||
export const shuffle = register('shuffle', (n, pat) => {
|
export const shuffle = register('shuffle', (n, pat) => {
|
||||||
return _rearrangeWith(randrun(n), n, pat);
|
return _rearrangeWith(randrun(n), n, pat);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name scramble
|
|
||||||
* Slices a pattern into the given number of parts, then plays those parts at random. Similar to `shuffle`,
|
* Slices a pattern into the given number of parts, then plays those parts at random. Similar to `shuffle`,
|
||||||
* but parts might be played more than once, or not at all, per cycle.
|
* but parts might be played more than once, or not at all, per cycle.
|
||||||
|
* @name scramble
|
||||||
* @example
|
* @example
|
||||||
* note("c d e f").sound("piano").scramble(4)
|
* note("c d e f").sound("piano").scramble(4)
|
||||||
* @example
|
* @example
|
||||||
* note("c d e f".scramble(4), "g").sound("piano")
|
* seq("c d e f".scramble(4), "g").note().sound("piano")
|
||||||
*/
|
*/
|
||||||
export const scramble = register('scramble', (n, pat) => {
|
export const scramble = register('scramble', (n, pat) => {
|
||||||
return _rearrangeWith(_irand(n)._segment(n), n, pat);
|
return _rearrangeWith(_irand(n)._segment(n), n, pat);
|
||||||
|
|
|
||||||
|
|
@ -1001,7 +1001,7 @@ describe('Pattern', () => {
|
||||||
});
|
});
|
||||||
describe('hurry', () => {
|
describe('hurry', () => {
|
||||||
it('Can speed up patterns and sounds', () => {
|
it('Can speed up patterns and sounds', () => {
|
||||||
sameFirst(s('a', 'b').hurry(2), s('a', 'b').fast(2).speed(2));
|
sameFirst(s(sequence('a', 'b')).hurry(2), s(sequence('a', 'b')).fast(2).speed(2));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
/*describe('composable functions', () => {
|
/*describe('composable functions', () => {
|
||||||
|
|
|
||||||
|
|
@ -25,28 +25,28 @@ describe('tonal', () => {
|
||||||
});
|
});
|
||||||
it('scale with n values', () => {
|
it('scale with n values', () => {
|
||||||
expect(
|
expect(
|
||||||
n(0, 1, 2)
|
n(seq(0, 1, 2))
|
||||||
.scale('C major')
|
.scale('C major')
|
||||||
.firstCycleValues.map((h) => h.note),
|
.firstCycleValues.map((h) => h.note),
|
||||||
).toEqual(['C3', 'D3', 'E3']);
|
).toEqual(['C3', 'D3', 'E3']);
|
||||||
});
|
});
|
||||||
it('scale with colon', () => {
|
it('scale with colon', () => {
|
||||||
expect(
|
expect(
|
||||||
n(0, 1, 2)
|
n(seq(0, 1, 2))
|
||||||
.scale('C:major')
|
.scale('C:major')
|
||||||
.firstCycleValues.map((h) => h.note),
|
.firstCycleValues.map((h) => h.note),
|
||||||
).toEqual(['C3', 'D3', 'E3']);
|
).toEqual(['C3', 'D3', 'E3']);
|
||||||
});
|
});
|
||||||
it('scale with mininotation colon', () => {
|
it('scale with mininotation colon', () => {
|
||||||
expect(
|
expect(
|
||||||
n(0, 1, 2)
|
n(seq(0, 1, 2))
|
||||||
.scale(mini('C:major'))
|
.scale(mini('C:major'))
|
||||||
.firstCycleValues.map((h) => h.note),
|
.firstCycleValues.map((h) => h.note),
|
||||||
).toEqual(['C3', 'D3', 'E3']);
|
).toEqual(['C3', 'D3', 'E3']);
|
||||||
});
|
});
|
||||||
it('transposes note numbers with interval numbers', () => {
|
it('transposes note numbers with interval numbers', () => {
|
||||||
expect(
|
expect(
|
||||||
note(40, 40, 40)
|
note(seq(40, 40, 40))
|
||||||
.transpose(0, 1, 2)
|
.transpose(0, 1, 2)
|
||||||
.firstCycleValues.map((h) => h.note),
|
.firstCycleValues.map((h) => h.note),
|
||||||
).toEqual([40, 41, 42]);
|
).toEqual([40, 41, 42]);
|
||||||
|
|
@ -54,7 +54,7 @@ describe('tonal', () => {
|
||||||
});
|
});
|
||||||
it('transposes note numbers with interval strings', () => {
|
it('transposes note numbers with interval strings', () => {
|
||||||
expect(
|
expect(
|
||||||
note(40, 40, 40)
|
note(seq(40, 40, 40))
|
||||||
.transpose('1P', '2M', '3m')
|
.transpose('1P', '2M', '3m')
|
||||||
.firstCycleValues.map((h) => h.note),
|
.firstCycleValues.map((h) => h.note),
|
||||||
).toEqual([40, 42, 43]);
|
).toEqual([40, 42, 43]);
|
||||||
|
|
@ -62,7 +62,7 @@ describe('tonal', () => {
|
||||||
});
|
});
|
||||||
it('transposes note strings with interval numbers', () => {
|
it('transposes note strings with interval numbers', () => {
|
||||||
expect(
|
expect(
|
||||||
note('c', 'c', 'c')
|
note(seq('c', 'c', 'c'))
|
||||||
.transpose(0, 1, 2)
|
.transpose(0, 1, 2)
|
||||||
.firstCycleValues.map((h) => h.note),
|
.firstCycleValues.map((h) => h.note),
|
||||||
).toEqual(['C', 'Db', 'D']);
|
).toEqual(['C', 'Db', 'D']);
|
||||||
|
|
@ -70,7 +70,7 @@ describe('tonal', () => {
|
||||||
});
|
});
|
||||||
it('transposes note strings with interval strings', () => {
|
it('transposes note strings with interval strings', () => {
|
||||||
expect(
|
expect(
|
||||||
note('c', 'c', 'c')
|
note(seq('c', 'c', 'c'))
|
||||||
.transpose('1P', '2M', '3m')
|
.transpose('1P', '2M', '3m')
|
||||||
.firstCycleValues.map((h) => h.note),
|
.firstCycleValues.map((h) => h.note),
|
||||||
).toEqual(['C', 'D', 'Eb']);
|
).toEqual(['C', 'D', 'Eb']);
|
||||||
|
|
|
||||||
|
|
@ -7401,9 +7401,7 @@ exports[`runs examples > example "scope" example index 0 1`] = `
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "scramble
|
exports[`runs examples > example "scramble" example index 0 1`] = `
|
||||||
Slices a pattern into the given number of parts, then plays those parts at random. Similar to \`shuffle\`,
|
|
||||||
but parts might be played more than once, or not at all, per cycle." example index 0 1`] = `
|
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/4 | note:c s:piano ]",
|
"[ 0/1 → 1/4 | note:c s:piano ]",
|
||||||
"[ 1/4 → 1/2 | note:d s:piano ]",
|
"[ 1/4 → 1/2 | note:d s:piano ]",
|
||||||
|
|
@ -7424,9 +7422,7 @@ but parts might be played more than once, or not at all, per cycle." example ind
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "scramble
|
exports[`runs examples > example "scramble" example index 1 1`] = `
|
||||||
Slices a pattern into the given number of parts, then plays those parts at random. Similar to \`shuffle\`,
|
|
||||||
but parts might be played more than once, or not at all, per cycle." example index 1 1`] = `
|
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/8 | note:c s:piano ]",
|
"[ 0/1 → 1/8 | note:c s:piano ]",
|
||||||
"[ 1/8 → 1/4 | note:d s:piano ]",
|
"[ 1/8 → 1/4 | note:d s:piano ]",
|
||||||
|
|
@ -7835,9 +7831,7 @@ exports[`runs examples > example "shrink" example index 3 1`] = `
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "shuffle
|
exports[`runs examples > example "shuffle" example index 0 1`] = `
|
||||||
Slices a pattern into the given number of parts, then plays those parts in random order.
|
|
||||||
Each part will be played exactly once per cycle." example index 0 1`] = `
|
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/4 | note:c s:piano ]",
|
"[ 0/1 → 1/4 | note:c s:piano ]",
|
||||||
"[ 1/4 → 1/2 | note:d s:piano ]",
|
"[ 1/4 → 1/2 | note:d s:piano ]",
|
||||||
|
|
@ -7858,9 +7852,7 @@ Each part will be played exactly once per cycle." example index 0 1`] = `
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "shuffle
|
exports[`runs examples > example "shuffle" example index 1 1`] = `
|
||||||
Slices a pattern into the given number of parts, then plays those parts in random order.
|
|
||||||
Each part will be played exactly once per cycle." example index 1 1`] = `
|
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/8 | note:c s:piano ]",
|
"[ 0/1 → 1/8 | note:c s:piano ]",
|
||||||
"[ 1/8 → 1/4 | note:d s:piano ]",
|
"[ 1/8 → 1/4 | note:d s:piano ]",
|
||||||
|
|
|
||||||
|
|
@ -69,32 +69,32 @@ stack(
|
||||||
|
|
||||||
export const giantSteps = `// John Coltrane - Giant Steps
|
export const giantSteps = `// John Coltrane - Giant Steps
|
||||||
|
|
||||||
let melody = note(
|
let melody = seq(
|
||||||
"[F#5 D5] [B4 G4] Bb4 [B4 A4]",
|
"[F#5 D5] [B4 G4] Bb4 [B4 A4]",
|
||||||
"[D5 Bb4] [G4 Eb4] F#4 [G4 F4]",
|
"[D5 Bb4] [G4 Eb4] F#4 [G4 F4]",
|
||||||
"Bb4 [B4 A4] D5 [D#5 C#5]",
|
"Bb4 [B4 A4] D5 [D#5 C#5]",
|
||||||
"F#5 [G5 F5] Bb5 [F#5 F#5]",
|
"F#5 [G5 F5] Bb5 [F#5 F#5]",
|
||||||
)
|
).note()
|
||||||
|
|
||||||
stack(
|
stack(
|
||||||
// melody
|
// melody
|
||||||
melody.color('#F8E71C'),
|
melody.color('#F8E71C'),
|
||||||
// chords
|
// chords
|
||||||
chord(
|
seq(
|
||||||
"[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]",
|
"[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]",
|
||||||
"[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]",
|
"[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]",
|
||||||
"Eb^7 [Am7 D7] G^7 [C#m7 F#7]",
|
"Eb^7 [Am7 D7] G^7 [C#m7 F#7]",
|
||||||
"B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]"
|
"B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]"
|
||||||
).dict('lefthand')
|
).chord().dict('lefthand')
|
||||||
.anchor(melody).mode('duck')
|
.anchor(melody).mode('duck')
|
||||||
.voicing().color('#7ED321'),
|
.voicing().color('#7ED321'),
|
||||||
// bass
|
// bass
|
||||||
note(
|
seq(
|
||||||
"[B2 D2] [G2 Bb2] [Eb2 Bb3] [A2 D2]",
|
"[B2 D2] [G2 Bb2] [Eb2 Bb3] [A2 D2]",
|
||||||
"[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]",
|
"[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]",
|
||||||
"[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]",
|
"[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]",
|
||||||
"[B2 F#2] [F2 Bb2] [Eb2 Bb3] [C#2 F#2]"
|
"[B2 F#2] [F2 Bb2] [Eb2 Bb3] [C#2 F#2]"
|
||||||
).color('#00B8D4')
|
).note().color('#00B8D4')
|
||||||
).slow(20)
|
).slow(20)
|
||||||
.pianoroll({fold:1})`;
|
.pianoroll({fold:1})`;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue