mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-13 06:19:33 -04:00
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:
@@ -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/>.
|
||||
*/
|
||||
|
||||
import { Pattern, register, sequence } from './pattern.mjs';
|
||||
import { Pattern, register, reify } from './pattern.mjs';
|
||||
|
||||
export function createParam(names) {
|
||||
let isMulti = Array.isArray(names);
|
||||
names = !isMulti ? [names] : names;
|
||||
const name = names[0];
|
||||
|
||||
// todo: make this less confusing
|
||||
const withVal = (xs) => {
|
||||
let bag;
|
||||
// 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);
|
||||
|
||||
const setter = function (...pats) {
|
||||
if (!pats.length) {
|
||||
return this.fmap(withVal);
|
||||
// todo: make this less confusing
|
||||
const func = function (value, pat) {
|
||||
if (!pat) {
|
||||
return reify(value).withValue(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;
|
||||
}
|
||||
|
||||
// maps control alias names to the "main" control name
|
||||
const controlAlias = new Map();
|
||||
|
||||
export function isControlName(name) {
|
||||
return controlAlias.has(name);
|
||||
}
|
||||
|
||||
export function registerControl(names, ...aliases) {
|
||||
const name = Array.isArray(names) ? names[0] : names;
|
||||
let bag = {};
|
||||
bag[name] = createParam(names);
|
||||
controlAlias.set(name, name);
|
||||
aliases.forEach((alias) => {
|
||||
bag[alias] = bag[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.
|
||||
* Each part will be played exactly once per cycle.
|
||||
* @name shuffle
|
||||
* @example
|
||||
* note("c d e f").sound("piano").shuffle(4)
|
||||
* @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) => {
|
||||
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`,
|
||||
* but parts might be played more than once, or not at all, per cycle.
|
||||
* @name scramble
|
||||
* @example
|
||||
* note("c d e f").sound("piano").scramble(4)
|
||||
* @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) => {
|
||||
return _rearrangeWith(_irand(n)._segment(n), n, pat);
|
||||
|
||||
@@ -1001,7 +1001,7 @@ describe('Pattern', () => {
|
||||
});
|
||||
describe('hurry', () => {
|
||||
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', () => {
|
||||
|
||||
@@ -25,28 +25,28 @@ describe('tonal', () => {
|
||||
});
|
||||
it('scale with n values', () => {
|
||||
expect(
|
||||
n(0, 1, 2)
|
||||
n(seq(0, 1, 2))
|
||||
.scale('C major')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('scale with colon', () => {
|
||||
expect(
|
||||
n(0, 1, 2)
|
||||
n(seq(0, 1, 2))
|
||||
.scale('C:major')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('scale with mininotation colon', () => {
|
||||
expect(
|
||||
n(0, 1, 2)
|
||||
n(seq(0, 1, 2))
|
||||
.scale(mini('C:major'))
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('transposes note numbers with interval numbers', () => {
|
||||
expect(
|
||||
note(40, 40, 40)
|
||||
note(seq(40, 40, 40))
|
||||
.transpose(0, 1, 2)
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual([40, 41, 42]);
|
||||
@@ -54,7 +54,7 @@ describe('tonal', () => {
|
||||
});
|
||||
it('transposes note numbers with interval strings', () => {
|
||||
expect(
|
||||
note(40, 40, 40)
|
||||
note(seq(40, 40, 40))
|
||||
.transpose('1P', '2M', '3m')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual([40, 42, 43]);
|
||||
@@ -62,7 +62,7 @@ describe('tonal', () => {
|
||||
});
|
||||
it('transposes note strings with interval numbers', () => {
|
||||
expect(
|
||||
note('c', 'c', 'c')
|
||||
note(seq('c', 'c', 'c'))
|
||||
.transpose(0, 1, 2)
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C', 'Db', 'D']);
|
||||
@@ -70,7 +70,7 @@ describe('tonal', () => {
|
||||
});
|
||||
it('transposes note strings with interval strings', () => {
|
||||
expect(
|
||||
note('c', 'c', 'c')
|
||||
note(seq('c', 'c', 'c'))
|
||||
.transpose('1P', '2M', '3m')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C', 'D', 'Eb']);
|
||||
|
||||
@@ -7401,9 +7401,7 @@ exports[`runs examples > example "scope" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "scramble
|
||||
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`] = `
|
||||
exports[`runs examples > example "scramble" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | note:c 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
|
||||
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`] = `
|
||||
exports[`runs examples > example "scramble" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | note:c 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
|
||||
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`] = `
|
||||
exports[`runs examples > example "shuffle" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | note:c 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
|
||||
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`] = `
|
||||
exports[`runs examples > example "shuffle" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | note:c s:piano ]",
|
||||
"[ 1/8 → 1/4 | note:d s:piano ]",
|
||||
|
||||
@@ -69,32 +69,32 @@ stack(
|
||||
|
||||
export const giantSteps = `// John Coltrane - Giant Steps
|
||||
|
||||
let melody = note(
|
||||
let melody = seq(
|
||||
"[F#5 D5] [B4 G4] Bb4 [B4 A4]",
|
||||
"[D5 Bb4] [G4 Eb4] F#4 [G4 F4]",
|
||||
"Bb4 [B4 A4] D5 [D#5 C#5]",
|
||||
"F#5 [G5 F5] Bb5 [F#5 F#5]",
|
||||
)
|
||||
).note()
|
||||
|
||||
stack(
|
||||
// melody
|
||||
melody.color('#F8E71C'),
|
||||
// chords
|
||||
chord(
|
||||
seq(
|
||||
"[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]",
|
||||
"[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]",
|
||||
"Eb^7 [Am7 D7] G^7 [C#m7 F#7]",
|
||||
"B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]"
|
||||
).dict('lefthand')
|
||||
).chord().dict('lefthand')
|
||||
.anchor(melody).mode('duck')
|
||||
.voicing().color('#7ED321'),
|
||||
// bass
|
||||
note(
|
||||
seq(
|
||||
"[B2 D2] [G2 Bb2] [Eb2 Bb3] [A2 D2]",
|
||||
"[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]",
|
||||
"[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]",
|
||||
"[B2 F#2] [F2 Bb2] [Eb2 Bb3] [C#2 F#2]"
|
||||
).color('#00B8D4')
|
||||
).note().color('#00B8D4')
|
||||
).slow(20)
|
||||
.pianoroll({fold:1})`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user