mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-13 22:35:15 -04:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7bcd5897da | |||
| a7adebe036 | |||
| 62379269f0 | |||
| 1162a42218 | |||
| 32d11d0332 | |||
| e5eb3b8d7b |
@@ -8,6 +8,7 @@ import TimeSpan from './timespan.mjs';
|
||||
import Fraction, { isFraction, lcm } from './fraction.mjs';
|
||||
import Hap from './hap.mjs';
|
||||
import State from './state.mjs';
|
||||
import { getTime } from './time.mjs';
|
||||
import { unionWithObj } from './value.mjs';
|
||||
|
||||
import {
|
||||
@@ -3533,3 +3534,58 @@ export const morph = (frompat, topat, bypat) => {
|
||||
bypat = reify(bypat);
|
||||
return frompat.innerBind((from) => topat.innerBind((to) => bypat.innerBind((by) => _morph(from, to, by))));
|
||||
};
|
||||
|
||||
export const TIMELINES = {
|
||||
state: {},
|
||||
polarities: {},
|
||||
};
|
||||
|
||||
/**
|
||||
* Aligns the pattern with the specified `timeline` by ID.
|
||||
*
|
||||
* More specifically, if a timeline ID is encountered for the first time, that moment
|
||||
* in time is marked as the "start" of that pattern. Thereafter, any other patterns aligned
|
||||
* with that same id will begin at that same moment in time.
|
||||
*
|
||||
* If the sign of the id changes (positive to negative or vice versa), we reset the timeline.
|
||||
*
|
||||
* @param {number | Pattern} id Timeline id. Must be a non-zero number. Switch signs to reset.
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("tri").seg(8).n(irand(12)).scale("G#:minor").lpf(400).room(2)
|
||||
* .timeline("<1 -1 1 2>")
|
||||
* @example
|
||||
* $: n("[0 .. 6]/4").scale("F:minor")
|
||||
* // Execute the following line at any point in the session
|
||||
* // and it will always start on note 0
|
||||
* // $: n("[0 .. 6]/4").scale("F:minor").timeline(2)
|
||||
*/
|
||||
export const timeline = register('timeline', (id, pat) => {
|
||||
if (typeof id !== 'number' || id === 0) {
|
||||
logger(
|
||||
`[query] ${id} is not a valid timeline id. Please ensure it is a non-zero number. Defaulting to timeline 1.`,
|
||||
);
|
||||
id = 1;
|
||||
}
|
||||
const { state, polarities } = TIMELINES;
|
||||
const key = Math.abs(id);
|
||||
// We let the pattern run until the sign switches
|
||||
const p = id > 0 ? 1 : -1;
|
||||
let t = polarities[key] === p ? state[key] : Math.ceil(getTime());
|
||||
return (
|
||||
pat
|
||||
.late(t)
|
||||
.onTrigger((hap) => {
|
||||
const T = Number(hap.part.begin);
|
||||
// Set state on the first trigger
|
||||
state[key] ??= T;
|
||||
polarities[key] ??= p;
|
||||
if (polarities[key] !== p) {
|
||||
state[key] = T;
|
||||
}
|
||||
polarities[key] = p;
|
||||
}, false)
|
||||
// Add labels
|
||||
.withValue((v) => ({ ...v, timeline: id, offset: t }))
|
||||
);
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import { evaluate as _evaluate } from './evaluate.mjs';
|
||||
import { errorLogger, logger } from './logger.mjs';
|
||||
import { setTime } from './time.mjs';
|
||||
import { evalScope } from './evaluate.mjs';
|
||||
import { register, Pattern, isPattern, silence, stack } from './pattern.mjs';
|
||||
import { register, Pattern, isPattern, silence, stack, TIMELINES } from './pattern.mjs';
|
||||
|
||||
export function repl({
|
||||
defaultOutput,
|
||||
@@ -52,6 +52,11 @@ export function repl({
|
||||
onToggle: (started) => {
|
||||
updateState({ started });
|
||||
onToggle?.(started);
|
||||
if (!started) {
|
||||
// Reset timeline state
|
||||
TIMELINES.state = {};
|
||||
TIMELINES.polarities = {};
|
||||
}
|
||||
},
|
||||
setInterval,
|
||||
clearInterval,
|
||||
|
||||
@@ -10844,6 +10844,23 @@ exports[`runs examples > example "take" example index 2 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "timeline" example index 0 1`] = `[]`;
|
||||
|
||||
exports[`runs examples > example "timeline" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 4/7 | note:F3 ]",
|
||||
"[ (4/7 → 1/1) ⇝ 8/7 | note:G3 ]",
|
||||
"[ 4/7 ⇜ (1/1 → 8/7) | note:G3 ]",
|
||||
"[ 8/7 → 12/7 | note:Ab3 ]",
|
||||
"[ (12/7 → 2/1) ⇝ 16/7 | note:Bb3 ]",
|
||||
"[ 12/7 ⇜ (2/1 → 16/7) | note:Bb3 ]",
|
||||
"[ 16/7 → 20/7 | note:C4 ]",
|
||||
"[ (20/7 → 3/1) ⇝ 24/7 | note:Db4 ]",
|
||||
"[ 20/7 ⇜ (3/1 → 24/7) | note:Db4 ]",
|
||||
"[ 24/7 → 4/1 | note:Eb4 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "tour" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | note:e s:folkharp ]",
|
||||
|
||||
@@ -381,7 +381,7 @@ Sampler effects are functions that can be used to change the behaviour of sample
|
||||
|
||||
### scrub
|
||||
|
||||
<JsDoc client:idle name="Pattern.scrub" h={0} />{' '}
|
||||
<JsDoc client:idle name="Pattern.scrub" h={0} />
|
||||
|
||||
### speed
|
||||
|
||||
|
||||
Reference in New Issue
Block a user