Polarity approach

This commit is contained in:
Aria
2025-10-11 15:09:14 -05:00
parent 1162a42218
commit 62379269f0
4 changed files with 22 additions and 40 deletions
+17 -35
View File
@@ -3537,7 +3537,7 @@ export const morph = (frompat, topat, bypat) => {
export const TIMELINES = {
currentOffsets: {},
previousOffsets: {},
polarity: {},
};
/**
@@ -3547,9 +3547,10 @@ export const TIMELINES = {
* 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 a negative ID is supplied, the timeline will reset at the start of the next cycle.
* If the sign of the id changes (positive to negative or vice versa), we reset the timeline at the
* beginning of the next cycle.
*
* @param {number | Pattern} id Timeline id. Must be a number. Set to negative to 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)
@@ -3560,18 +3561,16 @@ export const TIMELINES = {
* // and it will always start on note 0
* // $: n("[0 .. 6]/4").scale("F:minor").timeline(2)
*/
const timeline = register('timeline', (id, pat) => {
// TODO: This hard-coding of `behavior` and the switch below are only included to demonstrate
// the possible reset options. We should pick one before merging
const behavior = 0;
if (typeof id !== 'number') {
logger(`[query] ${id} is not a valid timeline id. Please ensure it is a number. Defaulting to timeline 1.`);
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 { currentOffsets: state, previousOffsets: prev } = TIMELINES;
const { currentOffsets: state, polarity } = TIMELINES;
const key = Math.abs(id);
// For negative id we just let the pattern run without resetting, hence tracking `prev`
let t = id < 0 ? prev[key] : state[key];
let t = state[key];
// We default here instead of updating `state` to prevent the `draw` functions from
// interfering with `state` when querying
t ??= Math.ceil(getTime());
@@ -3582,31 +3581,14 @@ const timeline = register('timeline', (id, pat) => {
const T = Number(hap.part.begin);
// Set state on the first trigger
state[key] ??= T;
prev[key] ??= T;
if (id < 0) {
const p = id > 0 ? 1 : -1;
polarity[key] ??= p;
// TODO: decide if we want to put `p === -1 &&` in this conditional
if (polarity[key] !== p) {
// Restart on next cycle
const nextCycle = Math.floor(T) + 1;
switch (behavior) {
case 0: {
// Restart at start of next cycle
state[key] = nextCycle;
break;
}
case 1: {
// Restart immediately
state[key] = T;
break;
}
case 2: {
// Restart at next hap or next cycle, whichever comes first
const haps = pat.queryArc(T, nextCycle).filter((h) => Number(h.part.begin) !== T);
state[key] = haps.length ? Number(haps[0].part.begin) : nextCycle;
break;
}
}
} else {
prev[key] = state[key];
state[key] = Math.floor(T) + 1;
}
polarity[key] = p;
}, false)
// Add labels
.withValue((v) => ({ ...v, timeline: id, offset: t }))
+1 -1
View File
@@ -55,7 +55,7 @@ export function repl({
if (!started) {
// Reset timeline state
TIMELINES.currentOffsets = {};
TIMELINES.previousOffsets = {};
TIMELINES.polarity = {};
}
},
setInterval,
+2 -2
View File
@@ -796,7 +796,7 @@ describe('Pattern', () => {
});
});
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());
}),
it('Can apply a pattern of functions', () => {
@@ -804,7 +804,7 @@ describe('Pattern', () => {
expect(sequence('a', 'b').apply(fast(2), fast(3)).firstCycle()).toStrictEqual(
sequence('a', 'b').fast(2, 3).firstCycle(),
);
});
}));
});
describe('layer', () => {
it('Can layer up multiple functions', () => {
+2 -2
View File
@@ -18,8 +18,8 @@
--docsearch-modal-background: var(--background);
--docsearch-muted-color: color-mix(in srgb, var(--foreground), #fff 30%);
--docsearch-key-gradient: var(--foreground);
--docsearch-key-shadow: inset 0 -2px 0 0 var(--gutterForeground), inset 0 0 1px 1px var(--foreground),
0 1px 2px 1px var(--gutterBackground);
--docsearch-key-shadow:
inset 0 -2px 0 0 var(--gutterForeground), inset 0 0 1px 1px var(--foreground), 0 1px 2px 1px var(--gutterBackground);
}
.dark {
--docsearch-muted-color: color-mix(in srgb, var(--foreground), #000 30%);