@msb235/blits 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +163 -0
- package/README.md +32 -8
- package/dist/bare.js +7 -1
- package/dist/book.d.ts +8 -1
- package/dist/book.js +35 -4
- package/dist/chain.d.ts +4 -3
- package/dist/chain.js +10 -6
- package/dist/channels.d.ts +12 -19
- package/dist/channels.js +18 -118
- package/dist/clock.d.ts +12 -5
- package/dist/clock.js +13 -5
- package/dist/closed.d.ts +15 -0
- package/dist/closed.js +144 -0
- package/dist/color.d.ts +61 -0
- package/dist/color.js +229 -0
- package/dist/columns.js +2 -1
- package/dist/crowd.d.ts +4 -4
- package/dist/crowd.js +11 -8
- package/dist/cue.d.ts +2 -0
- package/dist/cue.js +78 -27
- package/dist/due.js +5 -2
- package/dist/fade.d.ts +13 -0
- package/dist/fade.js +55 -4
- package/dist/fill.d.ts +1 -1
- package/dist/fill.js +17 -10
- package/dist/fit.d.ts +144 -0
- package/dist/fit.js +181 -0
- package/dist/fold.d.ts +3 -3
- package/dist/fold.js +25 -18
- package/dist/gather.d.ts +6 -0
- package/dist/gather.js +18 -0
- package/dist/handle.d.ts +27 -2
- package/dist/handle.js +60 -11
- package/dist/held.js +7 -3
- package/dist/history.d.ts +15 -2
- package/dist/history.js +54 -9
- package/dist/hosts.d.ts +2 -2
- package/dist/hosts.js +19 -6
- package/dist/index.d.ts +10 -2
- package/dist/index.js +6 -1
- package/dist/lane.d.ts +4 -4
- package/dist/lane.js +5 -5
- package/dist/lanes.d.ts +32 -9
- package/dist/lanes.js +53 -24
- package/dist/locus.d.ts +3 -3
- package/dist/locus.js +1 -1
- package/dist/marks.d.ts +3 -0
- package/dist/marks.js +81 -13
- package/dist/meet.d.ts +3 -2
- package/dist/meet.js +15 -3
- package/dist/mixer.d.ts +55 -35
- package/dist/mixer.js +173 -83
- package/dist/motion.d.ts +2 -199
- package/dist/motion.js +2 -652
- package/dist/motions.d.ts +266 -0
- package/dist/motions.js +724 -0
- package/dist/move.d.ts +1 -1
- package/dist/move.js +27 -11
- package/dist/owner.d.ts +2 -2
- package/dist/owner.js +5 -5
- package/dist/pace.d.ts +2 -0
- package/dist/pace.js +8 -0
- package/dist/pack.d.ts +31 -0
- package/dist/pack.js +65 -0
- package/dist/paging.d.ts +63 -0
- package/dist/paging.js +110 -0
- package/dist/patch.d.ts +3 -0
- package/dist/patch.js +2 -0
- package/dist/place.js +74 -25
- package/dist/project.d.ts +7 -4
- package/dist/project.js +117 -78
- package/dist/pull.js +4 -3
- package/dist/qualify.js +4 -2
- package/dist/revive.d.ts +58 -0
- package/dist/revive.js +158 -0
- package/dist/sample.d.ts +1 -1
- package/dist/sample.js +6 -1
- package/dist/seek.d.ts +3 -0
- package/dist/seek.js +311 -0
- package/dist/signals.d.ts +3 -2
- package/dist/signals.js +12 -6
- package/dist/spans.d.ts +29 -0
- package/dist/spans.js +165 -0
- package/dist/strays.d.ts +1 -1
- package/dist/strays.js +21 -0
- package/dist/tape.d.ts +21 -0
- package/dist/tape.js +65 -0
- package/dist/ticker.d.ts +73 -0
- package/dist/ticker.js +115 -0
- package/dist/transport.d.ts +121 -0
- package/dist/transport.js +271 -0
- package/dist/types.d.ts +427 -50
- package/dist/unpage.d.ts +8 -0
- package/dist/unpage.js +163 -0
- package/dist/voice.d.ts +78 -12
- package/dist/voice.js +126 -12
- package/dist/weigh.js +2 -2
- package/docs/schema.html +140 -48
- package/package.json +2 -1
package/dist/fit.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/** How far through the one before a child starts under each order, before any fit. */
|
|
2
|
+
export function shareOf(order, stagger) {
|
|
3
|
+
return order === 'queue' ? 1 : order === 'together' ? 0 : stagger;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* The plan before any fit: every child at its own rate, played, in the span's order.
|
|
7
|
+
*
|
|
8
|
+
* @category score
|
|
9
|
+
*/
|
|
10
|
+
export function plain(span, kids) {
|
|
11
|
+
return {
|
|
12
|
+
rate: kids.map(() => 1),
|
|
13
|
+
skip: kids.map(() => false),
|
|
14
|
+
share: shareOf(span.order, span.share),
|
|
15
|
+
allow: 0,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Where a plan puts each child, span ms from now, and how long they take together. A playing child
|
|
20
|
+
* is where it is, at 0; each pending one starts its share through the one before it.
|
|
21
|
+
*
|
|
22
|
+
* @category score
|
|
23
|
+
*/
|
|
24
|
+
export function layout(span, kids, plan) {
|
|
25
|
+
const base = shareOf(span.order, span.share);
|
|
26
|
+
const at = [];
|
|
27
|
+
let length = 0;
|
|
28
|
+
let prevAt = 0;
|
|
29
|
+
let prevLen = 0;
|
|
30
|
+
kids.forEach((k, i) => {
|
|
31
|
+
const len = plan.skip[i] ? 0 : k.natural / plan.rate[i];
|
|
32
|
+
const t = k.state === 'playing' || i === 0
|
|
33
|
+
? 0
|
|
34
|
+
: prevAt + (k.overlap ? Math.min(plan.share, base) : base) * prevLen;
|
|
35
|
+
at.push(t);
|
|
36
|
+
length = Math.max(length, t + len);
|
|
37
|
+
prevAt = t;
|
|
38
|
+
prevLen = len;
|
|
39
|
+
});
|
|
40
|
+
return { at, length };
|
|
41
|
+
}
|
|
42
|
+
const fits = (span, kids, plan) => layout(span, kids, plan).length <= span.left + plan.allow + 1e-9;
|
|
43
|
+
/** The smallest value in `lo..hi` at which `ok` holds, where it holds from some point up. */
|
|
44
|
+
function least(lo, hi, ok) {
|
|
45
|
+
if (ok(lo))
|
|
46
|
+
return lo;
|
|
47
|
+
if (!ok(hi))
|
|
48
|
+
return hi;
|
|
49
|
+
for (let i = 0; i < 60; i++) {
|
|
50
|
+
const mid = (lo + hi) / 2;
|
|
51
|
+
if (ok(mid))
|
|
52
|
+
hi = mid;
|
|
53
|
+
else
|
|
54
|
+
lo = mid;
|
|
55
|
+
}
|
|
56
|
+
return hi;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Moves every child's rate by one factor toward the budget, before the span's fit: faster where
|
|
60
|
+
* they run past it, each no faster than its `faster` allows, and slower where they leave room in
|
|
61
|
+
* it, each no slower than its `slower` allows, so they fill it.
|
|
62
|
+
*/
|
|
63
|
+
export function rescale(span, kids, plan) {
|
|
64
|
+
if (!fits(span, kids, plan)) {
|
|
65
|
+
const top = Math.max(1, ...kids.map((k) => k.faster));
|
|
66
|
+
const at = (f) => ({
|
|
67
|
+
...plan,
|
|
68
|
+
rate: kids.map((k, i) => Math.max(plan.rate[i], Math.min(f, k.faster))),
|
|
69
|
+
});
|
|
70
|
+
return at(least(1, top, (f) => fits(span, kids, at(f))));
|
|
71
|
+
}
|
|
72
|
+
if (!Number.isFinite(span.left))
|
|
73
|
+
return plan;
|
|
74
|
+
const top = Math.max(1, ...kids.map((k) => k.slower));
|
|
75
|
+
if (top === 1)
|
|
76
|
+
return plan;
|
|
77
|
+
const at = (f) => ({
|
|
78
|
+
...plan,
|
|
79
|
+
rate: kids.map((k, i) => plan.rate[i] / Math.min(f, k.slower)),
|
|
80
|
+
});
|
|
81
|
+
// The largest factor that still fits: `least` over the factors that do not.
|
|
82
|
+
const f = least(1, top, (x) => !fits(span, kids, at(x)));
|
|
83
|
+
return fits(span, kids, at(f)) ? at(f) : at(Math.max(1, f - 1e-9));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Starts children that may overlap sooner after the one before, toward all at once, until they
|
|
87
|
+
* fit.
|
|
88
|
+
*
|
|
89
|
+
* @category score
|
|
90
|
+
*/
|
|
91
|
+
export function condense() {
|
|
92
|
+
return (span, kids, plan) => {
|
|
93
|
+
if (fits(span, kids, plan) || !kids.some((k) => k.overlap))
|
|
94
|
+
return plan;
|
|
95
|
+
const at = (s) => ({ ...plan, share: s });
|
|
96
|
+
// `least` finds the smallest x that fits; the share wanted is the largest, so search 1 - share.
|
|
97
|
+
const x = least(0, plan.share, (d) => fits(span, kids, at(plan.share - d)));
|
|
98
|
+
return at(plan.share - x);
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Jumps children that may be skipped to their end, latest first, until they fit.
|
|
103
|
+
*
|
|
104
|
+
* @category score
|
|
105
|
+
*/
|
|
106
|
+
export function shed() {
|
|
107
|
+
return (span, kids, plan) => {
|
|
108
|
+
let p = plan;
|
|
109
|
+
for (let i = kids.length - 1; i >= 0 && !fits(span, kids, p); i--) {
|
|
110
|
+
if (!kids[i].ballast || p.skip[i])
|
|
111
|
+
continue;
|
|
112
|
+
const s = p.skip.slice();
|
|
113
|
+
s[i] = true;
|
|
114
|
+
p = { ...p, skip: s };
|
|
115
|
+
}
|
|
116
|
+
return p;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Jumps every child left to its end at once, whether or not it fits otherwise.
|
|
121
|
+
*
|
|
122
|
+
* @category score
|
|
123
|
+
*/
|
|
124
|
+
export function conclude() {
|
|
125
|
+
return (_span, kids, plan) => ({ ...plan, skip: kids.map(() => true) });
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Lets the span run past its budget: up to `cap` times it, or without limit.
|
|
129
|
+
*
|
|
130
|
+
* @category score
|
|
131
|
+
*/
|
|
132
|
+
export function overrun(opts = {}) {
|
|
133
|
+
return (span, _kids, plan) => {
|
|
134
|
+
const cap = opts.cap;
|
|
135
|
+
const allow = cap === undefined ? Number.POSITIVE_INFINITY : Math.max(0, (cap - 1) * span.budget);
|
|
136
|
+
return { ...plan, allow: Math.max(plan.allow, allow) };
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Tries each fit in turn, handing the plan on, and stops once the children fit.
|
|
141
|
+
*
|
|
142
|
+
* @category score
|
|
143
|
+
*/
|
|
144
|
+
export function pipe(...fits_) {
|
|
145
|
+
return (span, kids, plan) => {
|
|
146
|
+
let p = plan;
|
|
147
|
+
for (const f of fits_) {
|
|
148
|
+
if (fits(span, kids, p))
|
|
149
|
+
break;
|
|
150
|
+
p = f(span, kids, p);
|
|
151
|
+
}
|
|
152
|
+
return p;
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Every way of giving way after the span's retiming, overrunning only once the rest are spent.
|
|
157
|
+
*
|
|
158
|
+
* @category score
|
|
159
|
+
*/
|
|
160
|
+
export function lax(opts = {}) {
|
|
161
|
+
return pipe(condense(), shed(), overrun(opts));
|
|
162
|
+
}
|
|
163
|
+
/** The fit a span takes without one of its own. */
|
|
164
|
+
export const defaultFit = pipe(condense(), shed());
|
|
165
|
+
const rank = { weak: 0, strong: 1, required: 2 };
|
|
166
|
+
/**
|
|
167
|
+
* A span's fit settled: the plan, and what it had to do past the strategy, which starts from the
|
|
168
|
+
* children retimed toward the budget. Where the children still
|
|
169
|
+
* do not fit, a span weaker than `strong` lets them run long; otherwise `spill` either skips
|
|
170
|
+
* every child weaker than the span (`instant`), or lets them run long (`overrun`).
|
|
171
|
+
*/
|
|
172
|
+
export function settle(span, kids, fit, spill) {
|
|
173
|
+
let plan = fit(span, kids, rescale(span, kids, plain(span, kids)));
|
|
174
|
+
if (fits(span, kids, plan) || rank[span.priority] === 0 || spill === 'overrun')
|
|
175
|
+
return { plan, fell: !fits(span, kids, plan) && rank[span.priority] > 0 };
|
|
176
|
+
plan = {
|
|
177
|
+
...plan,
|
|
178
|
+
skip: kids.map((k, i) => plan.skip[i] || rank[k.priority] < rank[span.priority]),
|
|
179
|
+
};
|
|
180
|
+
return { plan, fell: true };
|
|
181
|
+
}
|
package/dist/fold.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type Values = Record<string, unknown>;
|
|
|
7
7
|
* One live voice's delta for one subject this frame, through its record for the subject, or null
|
|
8
8
|
* when it does not reach. Its weight is left in `w`.
|
|
9
9
|
*/
|
|
10
|
-
export declare function
|
|
10
|
+
export declare function contribution<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I, now: number, held: Subject<unknown>): Record<string, unknown> | null;
|
|
11
11
|
/** Reads a keys voice's stops at `held.phase` into the subject's delta, through the voice's scratch. */
|
|
12
12
|
export declare function keyed<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I, held: Subject<unknown>): Record<string, unknown>;
|
|
13
13
|
/**
|
|
@@ -28,7 +28,7 @@ export declare function baseFor<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, sub
|
|
|
28
28
|
*/
|
|
29
29
|
export declare function slopeFor<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I): Record<string, unknown> | undefined;
|
|
30
30
|
export declare function isRest<I, O>(this: Mixer<I, O>, delta: Record<string, unknown>): boolean;
|
|
31
|
-
/** Whether a rest-less channel's
|
|
31
|
+
/** Whether a rest-less channel's contribution is switched on, across a band rather than an edge. */
|
|
32
32
|
export declare function passes<I, O>(this: Mixer<I, O>, was: boolean | undefined, w: number): boolean;
|
|
33
33
|
/** Folds one voice's delta into the pose, its band state kept on the subject's record. */
|
|
34
34
|
export declare function apply<I, O>(this: Mixer<I, O>, pose: Record<string, unknown>, voice: Voice<I, O>, held: Subject<unknown>, delta: Record<string, unknown>, weight: number): void;
|
|
@@ -47,5 +47,5 @@ export declare function foldWith<I, O>(this: Mixer<I, O>, subject: I, out: O, he
|
|
|
47
47
|
export declare function owedBy<I, O>(this: Mixer<I, O>, owed: number, voice: Voice<I, O>): boolean;
|
|
48
48
|
/** `foldWith`'s voices with a locus in play: each locus folds at its first member's place. */
|
|
49
49
|
export declare function foldLoci<I, O>(this: Mixer<I, O>, subject: I, pose: Record<string, unknown>, head: Subject<unknown>, laned: boolean, dry: boolean, except: number | undefined, owed: number): O;
|
|
50
|
-
/** `
|
|
50
|
+
/** `contribution`, or for a dry fold a subject already probed this frame, without advancing it. */
|
|
51
51
|
export declare function read<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I, now: number, dry: boolean, held: Subject<unknown>): Record<string, unknown> | null;
|
package/dist/fold.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { frozenTime, passAt, phaseAt, silent } from './clock.js';
|
|
2
2
|
import { copy as copyValue } from './clone.js';
|
|
3
3
|
import { recordHost, remember } from './history.js';
|
|
4
4
|
import { stateful } from './hosts.js';
|
|
@@ -39,18 +39,18 @@ function merged(channel, acc, b) {
|
|
|
39
39
|
* One live voice's delta for one subject this frame, through its record for the subject, or null
|
|
40
40
|
* when it does not reach. Its weight is left in `w`.
|
|
41
41
|
*/
|
|
42
|
-
export function
|
|
42
|
+
export function contribution(voice, subject, now, held) {
|
|
43
43
|
held.weight = 0;
|
|
44
44
|
if (!held.reaches)
|
|
45
45
|
return null;
|
|
46
46
|
if (voice.out?.rest && held.rested)
|
|
47
47
|
return null;
|
|
48
48
|
const raw = voice.elapsedAt(now) - held.delay;
|
|
49
|
-
if (raw < 0 && !voice.
|
|
49
|
+
if (raw < 0 && !voice.freezesBefore)
|
|
50
50
|
return null;
|
|
51
|
-
const elapsed =
|
|
52
|
-
// A
|
|
53
|
-
const still = raw < 0 ? -1 : voice.
|
|
51
|
+
const elapsed = frozenTime(raw, voice.freezesBefore, voice.freezesAfter, voice.span);
|
|
52
|
+
// A frozen subject's clock stands still at the edge it freezes at: -1 before, 1 after, 0 playing.
|
|
53
|
+
const still = raw < 0 ? -1 : voice.freezesAfter && raw > voice.span ? 1 : 0;
|
|
54
54
|
const duration = voice.duration;
|
|
55
55
|
const phase = phaseAt(elapsed, duration, voice.passes);
|
|
56
56
|
const pass = passAt(elapsed, duration, voice.passes);
|
|
@@ -77,7 +77,7 @@ export function influence(voice, subject, now, held) {
|
|
|
77
77
|
reading.horizon = this.horizonFor(voice, held.delay, now);
|
|
78
78
|
const tick = this.opts.stepMs;
|
|
79
79
|
if (voice.patch.step && held.probed !== now && still >= 0) {
|
|
80
|
-
//
|
|
80
|
+
// Frozen after, it steps once more to where its last pass ended, and no further.
|
|
81
81
|
const to = still === 1 ? voice.timeAt(held.delay + voice.span) : now;
|
|
82
82
|
if (tick !== undefined && tick > 0 && !this.reducedNow)
|
|
83
83
|
this.tick(voice, subject, held, tick, to);
|
|
@@ -89,19 +89,27 @@ export function influence(voice, subject, now, held) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
let delta = null;
|
|
92
|
-
//
|
|
93
|
-
|
|
92
|
+
// A keys voice in a locus reads at weight 0 too: having a delta makes it a member, and the
|
|
93
|
+
// locus folds at its first member's place.
|
|
94
|
+
if (silent(voice, weight) && (voice.built === null || voice.spec.locus === undefined)) {
|
|
95
|
+
// A `from: 'current'` voice takes its base on its first read, silent or not.
|
|
96
|
+
if (voice.built && voice.spec.from === 'current' && held.base === undefined) {
|
|
97
|
+
held.base = this.baseFor(voice, subject);
|
|
98
|
+
held.slope = this.slopeFor(voice, subject);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else if (voice.built) {
|
|
94
102
|
held.phase = phase;
|
|
95
103
|
delta = this.keyed(voice, subject, held);
|
|
96
104
|
}
|
|
97
|
-
else
|
|
105
|
+
else {
|
|
98
106
|
delta = voice.patch.at(phase, subject, setting);
|
|
99
107
|
}
|
|
100
108
|
held.delta = delta;
|
|
101
109
|
held.probed = now;
|
|
102
110
|
held.seeks = voice.seeks;
|
|
103
111
|
if (history !== undefined)
|
|
104
|
-
remember(this, voice, held);
|
|
112
|
+
remember(this, voice, subject, held);
|
|
105
113
|
if (reading.kept !== keptBefore && !voice.keeping)
|
|
106
114
|
stateful(this, voice);
|
|
107
115
|
if (delta === null)
|
|
@@ -222,7 +230,7 @@ export function isRest(delta) {
|
|
|
222
230
|
}
|
|
223
231
|
return true;
|
|
224
232
|
}
|
|
225
|
-
/** Whether a rest-less channel's
|
|
233
|
+
/** Whether a rest-less channel's contribution is switched on, across a band rather than an edge. */
|
|
226
234
|
export function passes(was, w) {
|
|
227
235
|
const { on, off } = this.band;
|
|
228
236
|
return w >= on ? true : w <= off ? false : (was ?? w >= on);
|
|
@@ -281,10 +289,9 @@ export function folded(subject, out, dry = false, except) {
|
|
|
281
289
|
* voices on them; a subject numbered since the frame's fill folds every voice, laned ones included.
|
|
282
290
|
*/
|
|
283
291
|
export function linked(subject) {
|
|
284
|
-
const
|
|
285
|
-
const head = Number.isNaN(now) ? null : this.chain(subject, now);
|
|
292
|
+
const head = Number.isNaN(this.now) ? null : this.chain(subject);
|
|
286
293
|
this.linkedLaned =
|
|
287
|
-
head !== null && this.lanes?.prepare(head.slot, subject,
|
|
294
|
+
head !== null && this.lanes?.prepare(head.slot, subject, this.version, head) === true;
|
|
288
295
|
return head;
|
|
289
296
|
}
|
|
290
297
|
/** The fold after a subject's chain is linked and its lanes asked whether it reads from them. */
|
|
@@ -450,13 +457,13 @@ export function foldLoci(subject, pose, head, laned, dry, except, owed) {
|
|
|
450
457
|
this.locusDepth--;
|
|
451
458
|
}
|
|
452
459
|
}
|
|
453
|
-
/** `
|
|
460
|
+
/** `contribution`, or for a dry fold a subject already probed this frame, without advancing it. */
|
|
454
461
|
export function read(voice, subject, now, dry, held) {
|
|
455
462
|
if (dry) {
|
|
456
463
|
if (held.reaches && held.probed === now && held.delta && held.seeks === voice.seeks) {
|
|
457
464
|
const setting = voice.setting;
|
|
458
465
|
// The setting the probe that read the delta had, but for `dt`: a dry read advances nothing.
|
|
459
|
-
const elapsed =
|
|
466
|
+
const elapsed = frozenTime(voice.elapsedAt(now) - held.delay, voice.freezesBefore, voice.freezesAfter, voice.span);
|
|
460
467
|
setting.timestamp = now;
|
|
461
468
|
setting.dt = 0;
|
|
462
469
|
setting.elapsed = elapsed;
|
|
@@ -471,5 +478,5 @@ export function read(voice, subject, now, dry, held) {
|
|
|
471
478
|
return held.delta;
|
|
472
479
|
}
|
|
473
480
|
}
|
|
474
|
-
return this.
|
|
481
|
+
return this.contribution(voice, subject, now, held);
|
|
475
482
|
}
|
package/dist/gather.d.ts
CHANGED
|
@@ -22,3 +22,9 @@ export declare function foldDelta<I, O>(this: Lanes<I, O>, chans: readonly Laned
|
|
|
22
22
|
/** Folds a motion sample's axes, kept apart from `foldInto` so neither reads two array kinds. */
|
|
23
23
|
export declare function foldRun<I, O>(this: Lanes<I, O>, ch: Laned, slot: number, xs: Float64Array, w: number): void;
|
|
24
24
|
export declare function foldInto<I, O>(this: Lanes<I, O>, ch: Laned, slot: number, value: unknown, w: number): void;
|
|
25
|
+
/**
|
|
26
|
+
* Whether a rest-less channel's contribution passes the band, for the record `one` is folding: the
|
|
27
|
+
* general path's `apply` decides it from the same band state on the same record, so a subject
|
|
28
|
+
* moving between the paths carries it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function gate<I, O>(this: Lanes<I, O>, ch: Laned, w: number): boolean;
|
package/dist/gather.js
CHANGED
|
@@ -171,6 +171,8 @@ export function foldInto(ch, slot, value, w) {
|
|
|
171
171
|
gatherInto(this, this.into, ch, slot, value, w);
|
|
172
172
|
return;
|
|
173
173
|
}
|
|
174
|
+
if (ch.op === 'last' && !this.gate(ch, w))
|
|
175
|
+
return;
|
|
174
176
|
const values = ch.values;
|
|
175
177
|
if (ch.scalar) {
|
|
176
178
|
values[slot] = foldNumber(ch.op, values[slot], value, w);
|
|
@@ -184,3 +186,19 @@ export function foldInto(ch, slot, value, w) {
|
|
|
184
186
|
values[base + a] = foldNumber(ch.op, values[base + a], v, w);
|
|
185
187
|
}
|
|
186
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Whether a rest-less channel's contribution passes the band, for the record `one` is folding: the
|
|
191
|
+
* general path's `apply` decides it from the same band state on the same record, so a subject
|
|
192
|
+
* moving between the paths carries it.
|
|
193
|
+
*/
|
|
194
|
+
export function gate(ch, w) {
|
|
195
|
+
const lane = this.folding;
|
|
196
|
+
const rec = this.rec;
|
|
197
|
+
const i = lane.chans.indexOf(ch);
|
|
198
|
+
const bands = rec.bands ?? new Uint8Array(lane.chans.length);
|
|
199
|
+
rec.bands = bands;
|
|
200
|
+
const band = bands[i];
|
|
201
|
+
const on = this.host.passes(band === 0 ? undefined : band === 1, w);
|
|
202
|
+
bands[i] = on ? 1 : 2;
|
|
203
|
+
return on;
|
|
204
|
+
}
|
package/dist/handle.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FadeOptions, Handle } from './types.js';
|
|
1
|
+
import type { FadeOptions, FitResult, Handle } from './types.js';
|
|
2
2
|
import type { Voice } from './voice.js';
|
|
3
3
|
/** What a voice's handle asks of the mix that cued it: one per mix, shared by every handle. */
|
|
4
4
|
export interface HandleHost<I, O> {
|
|
@@ -9,17 +9,39 @@ export interface HandleHost<I, O> {
|
|
|
9
9
|
/** The voice's clock jumped: for an owner, so did every clock it holds. */
|
|
10
10
|
sought(voice: Voice<I, O>): void;
|
|
11
11
|
fade(voice: Voice<I, O>, opts: FadeOptions<I> | undefined): void;
|
|
12
|
+
rise(voice: Voice<I, O>, opts: {
|
|
13
|
+
over?: number;
|
|
14
|
+
} | undefined): void;
|
|
12
15
|
weightOf(voice: Voice<I, O>, subject: I): number;
|
|
16
|
+
/** Records a write the host made, which `again` makes once more where a seek replays it. */
|
|
17
|
+
record(label: string, again: () => void): void;
|
|
13
18
|
}
|
|
14
|
-
/**
|
|
19
|
+
/** What a handle answers for a voice a history store paged out, which left long ago. */
|
|
20
|
+
export interface Gone {
|
|
21
|
+
weight: number;
|
|
22
|
+
played: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A voice's handle: the host's only way to control a voice once cued. A voice a history store
|
|
26
|
+
* pages out is let go of here too, so the handle answers as a gone voice's does, and a seek that
|
|
27
|
+
* revives it puts the revived voice behind the same handle.
|
|
28
|
+
*/
|
|
15
29
|
export declare class VoiceHandle<I, O> implements Handle<I> {
|
|
16
30
|
#private;
|
|
17
31
|
readonly id: number;
|
|
18
32
|
constructor(voice: Voice<I, O>, host: HandleHost<I, O>);
|
|
33
|
+
/** The voice behind a handle; null while a history store holds it. */
|
|
34
|
+
static voiceOf<I, O>(h: VoiceHandle<I, O>): Voice<I, O> | null;
|
|
35
|
+
/** Lets go of a voice a history store paged out. */
|
|
36
|
+
static page<I, O>(h: VoiceHandle<I, O>, gone: Gone): void;
|
|
37
|
+
/** Puts a voice revived from a history store behind the handle it had. */
|
|
38
|
+
static rehome<I, O>(h: VoiceHandle<I, O>, voice: Voice<I, O>): void;
|
|
19
39
|
get state(): Handle<I>['state'];
|
|
20
40
|
get owner(): Handle<I> | undefined;
|
|
21
41
|
get played(): Promise<boolean>;
|
|
22
42
|
get done(): Promise<void>;
|
|
43
|
+
/** How a span's children were last fitted; undefined for any voice but a span. */
|
|
44
|
+
get result(): FitResult | undefined;
|
|
23
45
|
get weight(): number;
|
|
24
46
|
set weight(w: number);
|
|
25
47
|
get rate(): number;
|
|
@@ -27,5 +49,8 @@ export declare class VoiceHandle<I, O> implements Handle<I> {
|
|
|
27
49
|
ramp(r: number, over: number): void;
|
|
28
50
|
seek(elapsed: number): void;
|
|
29
51
|
fade(opts?: FadeOptions<I>): void;
|
|
52
|
+
rise(opts?: {
|
|
53
|
+
over?: number;
|
|
54
|
+
}): void;
|
|
30
55
|
weightOf(subject: I): number;
|
|
31
56
|
}
|
package/dist/handle.js
CHANGED
|
@@ -1,56 +1,105 @@
|
|
|
1
1
|
import { retime } from './clock.js';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* A voice's handle: the host's only way to control a voice once cued. A voice a history store
|
|
4
|
+
* pages out is let go of here too, so the handle answers as a gone voice's does, and a seek that
|
|
5
|
+
* revives it puts the revived voice behind the same handle.
|
|
6
|
+
*/
|
|
3
7
|
export class VoiceHandle {
|
|
4
8
|
id;
|
|
5
9
|
#voice;
|
|
10
|
+
#gone = null;
|
|
6
11
|
#host;
|
|
7
12
|
constructor(voice, host) {
|
|
8
13
|
this.id = voice.id;
|
|
9
14
|
this.#voice = voice;
|
|
10
15
|
this.#host = host;
|
|
11
16
|
}
|
|
17
|
+
/** The voice behind a handle; null while a history store holds it. */
|
|
18
|
+
static voiceOf(h) {
|
|
19
|
+
return h.#voice;
|
|
20
|
+
}
|
|
21
|
+
/** Lets go of a voice a history store paged out. */
|
|
22
|
+
static page(h, gone) {
|
|
23
|
+
h.#voice = null;
|
|
24
|
+
h.#gone = gone;
|
|
25
|
+
}
|
|
26
|
+
/** Puts a voice revived from a history store behind the handle it had. */
|
|
27
|
+
static rehome(h, voice) {
|
|
28
|
+
h.#voice = voice;
|
|
29
|
+
h.#gone = null;
|
|
30
|
+
}
|
|
12
31
|
get state() {
|
|
13
|
-
return this.#voice
|
|
32
|
+
return this.#voice?.state ?? 'done';
|
|
14
33
|
}
|
|
15
34
|
get owner() {
|
|
16
|
-
return this.#voice
|
|
35
|
+
return this.#voice?.owner?.handle ?? undefined;
|
|
17
36
|
}
|
|
18
37
|
get played() {
|
|
19
|
-
return this.#voice.played;
|
|
38
|
+
return this.#voice?.played ?? Promise.resolve(this.#gone?.played ?? false);
|
|
20
39
|
}
|
|
21
40
|
get done() {
|
|
22
|
-
return this.#voice.
|
|
41
|
+
return this.#voice?.done ?? Promise.resolve();
|
|
42
|
+
}
|
|
43
|
+
/** How a span's children were last fitted; undefined for any voice but a span. */
|
|
44
|
+
get result() {
|
|
45
|
+
return this.#voice?.fitting?.report;
|
|
23
46
|
}
|
|
24
47
|
get weight() {
|
|
25
|
-
return this.#voice
|
|
48
|
+
return this.#voice?.weight ?? this.#gone?.weight ?? 0;
|
|
26
49
|
}
|
|
50
|
+
// A write to a paged voice changes nothing: it had left when the host made it, as it has on replay.
|
|
27
51
|
set weight(w) {
|
|
28
|
-
|
|
29
|
-
|
|
52
|
+
const voice = this.#voice;
|
|
53
|
+
if (voice === null)
|
|
54
|
+
return;
|
|
55
|
+
voice.weight = w;
|
|
56
|
+
this.#host.changed(voice);
|
|
57
|
+
this.#host.record('weight', () => {
|
|
58
|
+
this.weight = w;
|
|
59
|
+
});
|
|
30
60
|
}
|
|
31
61
|
get rate() {
|
|
32
|
-
|
|
62
|
+
const voice = this.#voice;
|
|
63
|
+
return voice === null ? 0 : voice.rateAt(this.#host.nowFor(voice));
|
|
33
64
|
}
|
|
34
65
|
set rate(r) {
|
|
35
66
|
this.ramp(r, 0);
|
|
36
67
|
}
|
|
37
68
|
ramp(r, over) {
|
|
38
69
|
const voice = this.#voice;
|
|
70
|
+
if (voice === null)
|
|
71
|
+
return;
|
|
39
72
|
retime(voice, this.#host.nowFor(voice), r, over);
|
|
40
73
|
this.#host.changed(voice);
|
|
74
|
+
this.#host.record('rate', () => this.ramp(r, over));
|
|
41
75
|
}
|
|
42
76
|
seek(elapsed) {
|
|
43
77
|
const voice = this.#voice;
|
|
78
|
+
if (voice === null)
|
|
79
|
+
return;
|
|
44
80
|
voice.rebase(this.#host.nowFor(voice));
|
|
45
81
|
voice.anchorElapsed = elapsed;
|
|
46
82
|
voice.seeks++;
|
|
47
83
|
this.#host.sought(voice);
|
|
48
84
|
this.#host.changed(voice);
|
|
85
|
+
this.#host.record('seek', () => this.seek(elapsed));
|
|
49
86
|
}
|
|
50
87
|
fade(opts) {
|
|
51
|
-
this.#
|
|
88
|
+
const voice = this.#voice;
|
|
89
|
+
if (voice === null)
|
|
90
|
+
return;
|
|
91
|
+
this.#host.fade(voice, opts);
|
|
92
|
+
this.#host.record('fade', () => this.fade(opts));
|
|
93
|
+
}
|
|
94
|
+
rise(opts) {
|
|
95
|
+
const voice = this.#voice;
|
|
96
|
+
if (voice === null)
|
|
97
|
+
return;
|
|
98
|
+
this.#host.rise(voice, opts);
|
|
99
|
+
this.#host.record('rise', () => this.rise(opts));
|
|
52
100
|
}
|
|
53
101
|
weightOf(subject) {
|
|
54
|
-
|
|
102
|
+
const voice = this.#voice;
|
|
103
|
+
return voice === null ? 0 : this.#host.weightOf(voice, subject);
|
|
55
104
|
}
|
|
56
105
|
}
|
package/dist/held.js
CHANGED
|
@@ -49,6 +49,10 @@ export function held(voice, subject, now, slot) {
|
|
|
49
49
|
voice.subjects.set(subject, none);
|
|
50
50
|
return none;
|
|
51
51
|
}
|
|
52
|
+
if (this.keys !== null) {
|
|
53
|
+
voice.keyed ??= new Set();
|
|
54
|
+
voice.keyed.add(this.keys.key(subject));
|
|
55
|
+
}
|
|
52
56
|
const delay = voice.spec.stagger ? voice.spec.stagger(subject) : 0;
|
|
53
57
|
const since = this.sinceOf(voice, delay);
|
|
54
58
|
held = {
|
|
@@ -85,8 +89,8 @@ export function held(voice, subject, now, slot) {
|
|
|
85
89
|
voice.seen++;
|
|
86
90
|
if (delay > voice.latest) {
|
|
87
91
|
voice.latest = delay;
|
|
88
|
-
// A later end can take a
|
|
89
|
-
if (voice.state === '
|
|
92
|
+
// A later end can take a frozen voice live again.
|
|
93
|
+
if (voice.state === 'frozen')
|
|
90
94
|
schedule(this, voice);
|
|
91
95
|
}
|
|
92
96
|
return held;
|
|
@@ -102,7 +106,7 @@ export function sinceOf(voice, delay) {
|
|
|
102
106
|
return voice.owner === null ? t : mixTime(voice.owner, t);
|
|
103
107
|
}
|
|
104
108
|
export function shownOf(voice, since) {
|
|
105
|
-
return voice.
|
|
109
|
+
return voice.freezesBefore && voice.opened < since ? voice.opened : since;
|
|
106
110
|
}
|
|
107
111
|
/** A voice has gone live: the records it made while pending count from where it started. */
|
|
108
112
|
export function started(mix, voice) {
|
package/dist/history.d.ts
CHANGED
|
@@ -17,6 +17,19 @@ export declare function copyHeld<I, O>(voice: Voice<I, O>, h: Subject<unknown>):
|
|
|
17
17
|
/** Under `history` with `inputs`, copies the host fields any voice reads, once a frame, when they changed. */
|
|
18
18
|
export declare function recordHost<I, O>(mix: Mixer<I, O>, now: number): void;
|
|
19
19
|
/** Under `history` with `inputs`, keeps what an input signal read, each time it changes. */
|
|
20
|
-
export declare function record<I, O>(mix: Mixer<I, O>, held: Subject<unknown>, value: number): void;
|
|
20
|
+
export declare function record<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I, held: Subject<unknown>, value: number): void;
|
|
21
21
|
/** Under `history`, keeps a copy of a stateful voice's record for this subject every so often. */
|
|
22
|
-
export declare function remember<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, held: Subject<unknown>): void;
|
|
22
|
+
export declare function remember<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I, held: Subject<unknown>): void;
|
|
23
|
+
/**
|
|
24
|
+
* Under history, keeps the record a subject had when it left a voice, faded out of it or dropped,
|
|
25
|
+
* for a seek or a read back to before then; without history, keeps nothing.
|
|
26
|
+
*/
|
|
27
|
+
export declare function leave<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I, held: Subject<unknown> | undefined): void;
|
|
28
|
+
/** The record a subject had at mix time `t` and left a voice with after it, if it left after `t`. */
|
|
29
|
+
export declare function leftAt<I>(left: readonly {
|
|
30
|
+
subject: I;
|
|
31
|
+
at: number;
|
|
32
|
+
held: Subject<unknown>;
|
|
33
|
+
}[] | null, subject: I, t: number): Subject<unknown> | undefined;
|
|
34
|
+
/** The mix time a motion patch keeps a released subject from, and how far back, under history. */
|
|
35
|
+
export declare function releasing<I, O>(mix: Mixer<I, O>): [at?: number, reach?: number];
|