@msb235/blits 0.6.0 → 0.7.1
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 +152 -0
- package/README.md +31 -7
- package/dist/bare.js +7 -1
- package/dist/book.d.ts +7 -0
- package/dist/book.js +35 -4
- package/dist/chain.d.ts +2 -1
- package/dist/chain.js +7 -3
- package/dist/channels.d.ts +12 -19
- package/dist/channels.js +18 -118
- package/dist/clock.d.ts +9 -2
- package/dist/clock.js +10 -2
- 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 +1 -1
- package/dist/crowd.js +4 -1
- package/dist/cue.d.ts +2 -0
- package/dist/cue.js +80 -27
- package/dist/due.js +3 -0
- package/dist/fade.d.ts +13 -0
- package/dist/fade.js +57 -4
- package/dist/fill.d.ts +1 -1
- package/dist/fill.js +15 -8
- package/dist/fit.d.ts +144 -0
- package/dist/fit.js +181 -0
- package/dist/fold.d.ts +3 -3
- package/dist/fold.js +18 -11
- package/dist/gather.d.ts +6 -0
- package/dist/gather.js +18 -0
- package/dist/gone.d.ts +32 -0
- package/dist/gone.js +134 -0
- package/dist/handle.d.ts +27 -2
- package/dist/handle.js +60 -11
- package/dist/held.js +4 -0
- package/dist/history.d.ts +15 -2
- package/dist/history.js +56 -9
- package/dist/hosts.d.ts +2 -2
- package/dist/hosts.js +20 -6
- package/dist/index.d.ts +8 -2
- package/dist/index.js +5 -1
- package/dist/lane.d.ts +1 -1
- 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 +80 -12
- package/dist/meet.d.ts +3 -2
- package/dist/meet.js +15 -3
- package/dist/mixer.d.ts +56 -36
- package/dist/mixer.js +158 -87
- 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 +28 -8
- package/dist/owner.d.ts +1 -1
- package/dist/owner.js +3 -3
- 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 +235 -43
- 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/scored.d.ts +57 -0
- package/dist/scored.js +196 -0
- package/dist/seek.d.ts +3 -0
- package/dist/seek.js +311 -0
- 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 +28 -3
- package/dist/ticker.js +3 -0
- package/dist/transport.d.ts +125 -0
- package/dist/transport.js +283 -0
- package/dist/types.d.ts +404 -38
- package/dist/unpage.d.ts +8 -0
- package/dist/unpage.js +165 -0
- package/dist/voice.d.ts +75 -7
- package/dist/voice.js +119 -4
- package/dist/weigh.js +2 -2
- package/docs/schema.html +120 -31
- package/package.json +2 -1
package/dist/fade.d.ts
CHANGED
|
@@ -16,4 +16,17 @@ export declare function beginFade<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, op
|
|
|
16
16
|
at?: 'rest';
|
|
17
17
|
deadline?: number;
|
|
18
18
|
}, at?: number): void;
|
|
19
|
+
/**
|
|
20
|
+
* Turns a fading voice around: it climbs back from where its fade out had got to, up the same
|
|
21
|
+
* curve, at the whole curve per `over` ms. A fade waiting for rest is taken back at once. A voice
|
|
22
|
+
* not fading is left as it is.
|
|
23
|
+
*/
|
|
24
|
+
export declare function beginRise<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, opts: {
|
|
25
|
+
over?: number;
|
|
26
|
+
}): void;
|
|
27
|
+
/**
|
|
28
|
+
* Fades a voice out from mix time `at`: ahead, it plays untouched until then and begins its fade
|
|
29
|
+
* exactly there; at or behind now, the fade began then and is partway.
|
|
30
|
+
*/
|
|
31
|
+
export declare function fadeAt<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, at: number, over: number | undefined): void;
|
|
19
32
|
export declare function retire<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, at?: number): void;
|
package/dist/fade.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { changed } from './chain.js';
|
|
2
2
|
import { envelope } from './clock.js';
|
|
3
3
|
import { schedule } from './due.js';
|
|
4
|
-
import { noted } from './history.js';
|
|
4
|
+
import { leave, noted, releasing } from './history.js';
|
|
5
5
|
import { orphan } from './owner.js';
|
|
6
6
|
import { startOf } from './place.js';
|
|
7
|
+
import { scoreTouched } from './scored.js';
|
|
8
|
+
import { lingers, refit } from './spans.js';
|
|
7
9
|
import { unreach } from './unreached.js';
|
|
8
10
|
/** Starts one subject's ramp out of a voice, or takes it out at once for a ramp of 0. */
|
|
9
11
|
export function fadeSubject(mix, voice, subject, over) {
|
|
@@ -43,7 +45,7 @@ export function part(mix, voice, subject, at) {
|
|
|
43
45
|
forgetIn(mix, voice, subject);
|
|
44
46
|
const motion = voice.motion;
|
|
45
47
|
if (motion !== undefined && asks(mix, motion, voice))
|
|
46
|
-
motion.release(subject);
|
|
48
|
+
motion.release(subject, ...releasing(mix));
|
|
47
49
|
}
|
|
48
50
|
/** Brings a subject faded out of a voice back, to be met afresh on its next probe. */
|
|
49
51
|
export function unpart(mix, voice, subject) {
|
|
@@ -63,6 +65,7 @@ function forgetIn(mix, voice, subject) {
|
|
|
63
65
|
unreach(voice, head.slot, false);
|
|
64
66
|
const held = voice.subjects.get(subject);
|
|
65
67
|
if (held !== undefined) {
|
|
68
|
+
leave(mix, voice, subject, held);
|
|
66
69
|
voice.subjects.delete(subject);
|
|
67
70
|
if (held.reaches)
|
|
68
71
|
voice.seen--;
|
|
@@ -102,6 +105,52 @@ export function beginFade(mix, voice, opts, at) {
|
|
|
102
105
|
if (over === 0 && opts.at !== 'rest')
|
|
103
106
|
retire(mix, voice, voice.out.at);
|
|
104
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Turns a fading voice around: it climbs back from where its fade out had got to, up the same
|
|
110
|
+
* curve, at the whole curve per `over` ms. A fade waiting for rest is taken back at once. A voice
|
|
111
|
+
* not fading is left as it is.
|
|
112
|
+
*/
|
|
113
|
+
export function beginRise(mix, voice, opts) {
|
|
114
|
+
const out = voice.out;
|
|
115
|
+
if (voice.state !== 'fading' && voice.outSet) {
|
|
116
|
+
// A fade set ahead and not begun is taken back, and the weight never moved.
|
|
117
|
+
voice.outAt = Number.POSITIVE_INFINITY;
|
|
118
|
+
voice.outOver = Number.NaN;
|
|
119
|
+
voice.outSet = false;
|
|
120
|
+
noted(mix, voice);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (voice.state !== 'fading' || out === null)
|
|
124
|
+
return;
|
|
125
|
+
const at = Number.isNaN(mix.now) ? startOf(voice) : mix.now;
|
|
126
|
+
const over = mix.reduced ? 0 : (opts.over ?? voice.fade.in ?? voice.fade.out ?? 0);
|
|
127
|
+
const left = out.rest || out.over === 0 ? 1 : 1 - (at - out.at) / out.over;
|
|
128
|
+
const from = left < 0 ? 0 : left > 1 ? 1 : left;
|
|
129
|
+
voice.out = null;
|
|
130
|
+
voice.outAt = Number.POSITIVE_INFINITY;
|
|
131
|
+
voice.state = 'live';
|
|
132
|
+
voice.back = over > 0 && from < 1 ? { from, at, over } : null;
|
|
133
|
+
noted(mix, voice);
|
|
134
|
+
mix.lanes?.refill();
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Fades a voice out from mix time `at`: ahead, it plays untouched until then and begins its fade
|
|
138
|
+
* exactly there; at or behind now, the fade began then and is partway.
|
|
139
|
+
*/
|
|
140
|
+
export function fadeAt(mix, voice, at, over) {
|
|
141
|
+
if (voice.state === 'done' || voice.state === 'fading')
|
|
142
|
+
return;
|
|
143
|
+
const now = Number.isNaN(mix.now) ? startOf(voice) : mix.now;
|
|
144
|
+
if (at <= now) {
|
|
145
|
+
beginFade(mix, voice, { over }, Math.max(startOf(voice), at));
|
|
146
|
+
mix.stir();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
voice.outAt = at;
|
|
150
|
+
voice.outOver = over ?? Number.NaN;
|
|
151
|
+
voice.outSet = true;
|
|
152
|
+
noted(mix, voice);
|
|
153
|
+
}
|
|
105
154
|
/** Removes a voice, recording that it left at `at`, default now. */
|
|
106
155
|
/** Whether a motion patch still asks this voice of this mix for its time. */
|
|
107
156
|
function asks(mix, motion, voice) {
|
|
@@ -114,11 +163,12 @@ export function retire(mix, voice, at) {
|
|
|
114
163
|
if (motion !== undefined && asks(mix, motion, voice))
|
|
115
164
|
motion.owner = null;
|
|
116
165
|
voice.state = 'done';
|
|
166
|
+
scoreTouched(mix, voice);
|
|
117
167
|
// The record its setting last wrote to, which a retired voice no longer calls for.
|
|
118
168
|
voice.keepOn = null;
|
|
119
169
|
mix.retired.push(voice);
|
|
120
170
|
voice.doneAt = at ?? (Number.isNaN(mix.now) ? Number.NEGATIVE_INFINITY : mix.now);
|
|
121
|
-
voice.play(false);
|
|
171
|
+
voice.play(false, voice.doneAt);
|
|
122
172
|
voice.resolve();
|
|
123
173
|
// An owner takes what it holds with it, and leaves with its last child.
|
|
124
174
|
if (voice.holding !== null)
|
|
@@ -126,8 +176,11 @@ export function retire(mix, voice, at) {
|
|
|
126
176
|
if (child.state !== 'done')
|
|
127
177
|
retire(mix, child, voice.doneAt);
|
|
128
178
|
if (voice.owner !== null) {
|
|
179
|
+
const span = voice.owner;
|
|
129
180
|
const owner = orphan(voice);
|
|
130
|
-
if (owner !== null)
|
|
181
|
+
if (owner !== null && !lingers(owner, voice.doneAt))
|
|
131
182
|
retire(mix, owner, voice.doneAt);
|
|
183
|
+
else if (span.fitting !== null && span.state !== 'done')
|
|
184
|
+
refit(mix, span);
|
|
132
185
|
}
|
|
133
186
|
}
|
package/dist/fill.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export declare function reset<I, O>(this: Lanes<I, O>, lane: Lane<I, O>): void;
|
|
|
23
23
|
export declare function run<I, O>(this: Lanes<I, O>, lane: Lane<I, O>): void;
|
|
24
24
|
/**
|
|
25
25
|
* One subject's contribution from a lane's voice. True where the voice gave the subject a delta,
|
|
26
|
-
* as the general path's `
|
|
26
|
+
* as the general path's `contribution` does where it returns one, which is what makes a voice in a
|
|
27
27
|
* locus one of its members for the subject.
|
|
28
28
|
*/
|
|
29
29
|
export declare function one<I, O>(this: Lanes<I, O>, lane: Lane<I, O>, p: number, slot: number, elapsedNow: number, duration: number, passes: number): boolean;
|
package/dist/fill.js
CHANGED
|
@@ -39,6 +39,7 @@ export function fillAll(lanes, now, version) {
|
|
|
39
39
|
const decide = lanes.fresh;
|
|
40
40
|
lanes.fresh = false;
|
|
41
41
|
const share = lanes.live > 0 ? lanes.lastDistinct / lanes.live : 1;
|
|
42
|
+
lanes.lately = share < 1 ? lanes.lastFrom : -1;
|
|
42
43
|
// Deciding which lanes and crowds go idle first, then running them, in voice order: a crowd's
|
|
43
44
|
// rows are folded between the lanes on either side of their voices, as the general path
|
|
44
45
|
// folds every voice in order.
|
|
@@ -95,11 +96,9 @@ export function fillAll(lanes, now, version) {
|
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
98
|
// With every lane idle there was nothing to fill, and no subject reads a lane this frame.
|
|
98
|
-
|
|
99
|
-
for (let s = 0; s < size; s++)
|
|
100
|
-
lanes.per[s * 6 /* Per.SLOT */ + 0 /* Per.FILLED */] = lanes.fills;
|
|
99
|
+
lanes.busyFill = busy ? lanes.fills : 0;
|
|
101
100
|
for (const slot of lanes.late)
|
|
102
|
-
lanes.per[slot * 6 /* Per.SLOT */ + 0 /* Per.FILLED */] =
|
|
101
|
+
lanes.per[slot * 6 /* Per.SLOT */ + 0 /* Per.FILLED */] = lanes.fills;
|
|
103
102
|
if (lanes.holding) {
|
|
104
103
|
lanes.subjects.fill(undefined, 0, size);
|
|
105
104
|
lanes.holding = false;
|
|
@@ -208,7 +207,11 @@ export function reset(lane) {
|
|
|
208
207
|
lane.read = false;
|
|
209
208
|
lane.weighed = false;
|
|
210
209
|
// With no fade in or out the envelope is 1 for every subject, which is what it would return.
|
|
211
|
-
lane.flat =
|
|
210
|
+
lane.flat =
|
|
211
|
+
!((voice.fade.in ?? 0) > 0) &&
|
|
212
|
+
voice.out === null &&
|
|
213
|
+
voice.back === null &&
|
|
214
|
+
voice.owner === null;
|
|
212
215
|
lane.fade = 1;
|
|
213
216
|
}
|
|
214
217
|
/** One voice's contribution to every subject it plays on. */
|
|
@@ -238,7 +241,7 @@ export function run(lane) {
|
|
|
238
241
|
}
|
|
239
242
|
/**
|
|
240
243
|
* One subject's contribution from a lane's voice. True where the voice gave the subject a delta,
|
|
241
|
-
* as the general path's `
|
|
244
|
+
* as the general path's `contribution` does where it returns one, which is what makes a voice in a
|
|
242
245
|
* locus one of its members for the subject.
|
|
243
246
|
*/
|
|
244
247
|
export function one(lane, p, slot, elapsedNow, duration, passes) {
|
|
@@ -249,6 +252,8 @@ export function one(lane, p, slot, elapsedNow, duration, passes) {
|
|
|
249
252
|
// A probe read this subject from the last fill: keep the weight it read for `weightOf`.
|
|
250
253
|
if (this.per[slot * 6 /* Per.SLOT */ + 5 /* Per.LANE_FILL */] === this.fills - 1)
|
|
251
254
|
data[o + 3 /* Row.PROBED */] = data[o + 2 /* Row.WEIGHT */];
|
|
255
|
+
if (this.lately >= 0 && this.unread(slot))
|
|
256
|
+
return false;
|
|
252
257
|
const delay = data[o + 0 /* Row.DELAY */];
|
|
253
258
|
const elapsed = frozenAt(voice, elapsedNow - delay);
|
|
254
259
|
if (!(elapsed >= 0)) {
|
|
@@ -278,6 +283,8 @@ export function one(lane, p, slot, elapsedNow, duration, passes) {
|
|
|
278
283
|
else
|
|
279
284
|
w = weighed(voice.weight, lane.fade, this.parting(voice, slot));
|
|
280
285
|
data[o + 2 /* Row.WEIGHT */] = w;
|
|
286
|
+
this.folding = lane;
|
|
287
|
+
this.rec = lane.records[p] ?? null;
|
|
281
288
|
if (voice.built !== null) {
|
|
282
289
|
if (!lane.read || !Object.is(elapsed, lane.readAt)) {
|
|
283
290
|
this.readKeyed(voice, lane.phase, lane.delta, lane.scratch);
|
|
@@ -367,12 +374,12 @@ export function call(voice, chans, rec, slot, elapsed, phase, pass, delay, w) {
|
|
|
367
374
|
else
|
|
368
375
|
reading.horizon = Number.POSITIVE_INFINITY;
|
|
369
376
|
const delta = voice.patch.at(phase, subject, voice.setting);
|
|
370
|
-
// What `
|
|
377
|
+
// What `contribution` leaves on the record, so a probe on the general path this frame reuses it.
|
|
371
378
|
rec.delta = delta;
|
|
372
379
|
rec.probed = this.now;
|
|
373
380
|
rec.seeks = voice.seeks;
|
|
374
381
|
if (this.keeps)
|
|
375
|
-
host.after(voice, rec);
|
|
382
|
+
host.after(voice, subject, rec);
|
|
376
383
|
if (reading.kept !== kept && !voice.keeping)
|
|
377
384
|
host.kept(voice);
|
|
378
385
|
if (w > 0)
|
package/dist/fit.d.ts
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How hard a claim on time holds when it meets another: the weaker gives way.
|
|
3
|
+
*
|
|
4
|
+
* @category score
|
|
5
|
+
*/
|
|
6
|
+
export type Strength = 'weak' | 'strong' | 'required';
|
|
7
|
+
/**
|
|
8
|
+
* How a span lays its children out: each after the one before has ended, partway through it, or
|
|
9
|
+
* all at once.
|
|
10
|
+
*
|
|
11
|
+
* @category score
|
|
12
|
+
*/
|
|
13
|
+
export type Order = 'queue' | 'stagger' | 'together';
|
|
14
|
+
/**
|
|
15
|
+
* One child of a span as a fit sees it: what is left of it, and the ways it may give way.
|
|
16
|
+
*
|
|
17
|
+
* @category score
|
|
18
|
+
*/
|
|
19
|
+
export interface Claim {
|
|
20
|
+
/** Span ms left of it at its own rate. */
|
|
21
|
+
natural: number;
|
|
22
|
+
/** A playing child has started, and stays at the front. */
|
|
23
|
+
state: 'pending' | 'playing';
|
|
24
|
+
/** How many times faster it may play; 1 for no faster. */
|
|
25
|
+
faster: number;
|
|
26
|
+
/** How many times slower it may play; 1 for no slower. */
|
|
27
|
+
slower: number;
|
|
28
|
+
/** Whether it may run alongside the one before it where the span's order would not have it. */
|
|
29
|
+
overlap: boolean;
|
|
30
|
+
/** Whether it may jump to its end instead of playing. */
|
|
31
|
+
ballast: boolean;
|
|
32
|
+
priority: Strength;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The span as a fit sees it.
|
|
36
|
+
*
|
|
37
|
+
* @category score
|
|
38
|
+
*/
|
|
39
|
+
export interface SpanClaim {
|
|
40
|
+
/** Span ms left of its budget; Infinity for a span with none. */
|
|
41
|
+
left: number;
|
|
42
|
+
/** Its whole budget. */
|
|
43
|
+
budget: number;
|
|
44
|
+
priority: Strength;
|
|
45
|
+
order: Order;
|
|
46
|
+
/** How far through the one before a staggered child starts, 0..1. */
|
|
47
|
+
share: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* What a fit decides, per child in the order given: a rate over its own, and whether it is
|
|
51
|
+
* skipped. `share` is how far through the one before a child that may overlap starts, and `allow`
|
|
52
|
+
* the span ms the span may run past its budget.
|
|
53
|
+
*
|
|
54
|
+
* @category score
|
|
55
|
+
*/
|
|
56
|
+
export interface FitPlan {
|
|
57
|
+
rate: number[];
|
|
58
|
+
skip: boolean[];
|
|
59
|
+
share: number;
|
|
60
|
+
allow: number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* A strategy for fitting a span's children into its budget: from the plan so far to a new one.
|
|
64
|
+
*
|
|
65
|
+
* @category score
|
|
66
|
+
*/
|
|
67
|
+
export type Fit = (span: SpanClaim, kids: readonly Claim[], plan: FitPlan) => FitPlan;
|
|
68
|
+
/** How far through the one before a child starts under each order, before any fit. */
|
|
69
|
+
export declare function shareOf(order: Order, stagger: number): number;
|
|
70
|
+
/**
|
|
71
|
+
* The plan before any fit: every child at its own rate, played, in the span's order.
|
|
72
|
+
*
|
|
73
|
+
* @category score
|
|
74
|
+
*/
|
|
75
|
+
export declare function plain(span: SpanClaim, kids: readonly Claim[]): FitPlan;
|
|
76
|
+
/**
|
|
77
|
+
* Where a plan puts each child, span ms from now, and how long they take together. A playing child
|
|
78
|
+
* is where it is, at 0; each pending one starts its share through the one before it.
|
|
79
|
+
*
|
|
80
|
+
* @category score
|
|
81
|
+
*/
|
|
82
|
+
export declare function layout(span: SpanClaim, kids: readonly Claim[], plan: FitPlan): {
|
|
83
|
+
at: number[];
|
|
84
|
+
length: number;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Moves every child's rate by one factor toward the budget, before the span's fit: faster where
|
|
88
|
+
* they run past it, each no faster than its `faster` allows, and slower where they leave room in
|
|
89
|
+
* it, each no slower than its `slower` allows, so they fill it.
|
|
90
|
+
*/
|
|
91
|
+
export declare function rescale(span: SpanClaim, kids: readonly Claim[], plan: FitPlan): FitPlan;
|
|
92
|
+
/**
|
|
93
|
+
* Starts children that may overlap sooner after the one before, toward all at once, until they
|
|
94
|
+
* fit.
|
|
95
|
+
*
|
|
96
|
+
* @category score
|
|
97
|
+
*/
|
|
98
|
+
export declare function condense(): Fit;
|
|
99
|
+
/**
|
|
100
|
+
* Jumps children that may be skipped to their end, latest first, until they fit.
|
|
101
|
+
*
|
|
102
|
+
* @category score
|
|
103
|
+
*/
|
|
104
|
+
export declare function shed(): Fit;
|
|
105
|
+
/**
|
|
106
|
+
* Jumps every child left to its end at once, whether or not it fits otherwise.
|
|
107
|
+
*
|
|
108
|
+
* @category score
|
|
109
|
+
*/
|
|
110
|
+
export declare function conclude(): Fit;
|
|
111
|
+
/**
|
|
112
|
+
* Lets the span run past its budget: up to `cap` times it, or without limit.
|
|
113
|
+
*
|
|
114
|
+
* @category score
|
|
115
|
+
*/
|
|
116
|
+
export declare function overrun(opts?: {
|
|
117
|
+
cap?: number;
|
|
118
|
+
}): Fit;
|
|
119
|
+
/**
|
|
120
|
+
* Tries each fit in turn, handing the plan on, and stops once the children fit.
|
|
121
|
+
*
|
|
122
|
+
* @category score
|
|
123
|
+
*/
|
|
124
|
+
export declare function pipe(...fits_: readonly Fit[]): Fit;
|
|
125
|
+
/**
|
|
126
|
+
* Every way of giving way after the span's retiming, overrunning only once the rest are spent.
|
|
127
|
+
*
|
|
128
|
+
* @category score
|
|
129
|
+
*/
|
|
130
|
+
export declare function lax(opts?: {
|
|
131
|
+
cap?: number;
|
|
132
|
+
}): Fit;
|
|
133
|
+
/** The fit a span takes without one of its own. */
|
|
134
|
+
export declare const defaultFit: Fit;
|
|
135
|
+
/**
|
|
136
|
+
* A span's fit settled: the plan, and what it had to do past the strategy, which starts from the
|
|
137
|
+
* children retimed toward the budget. Where the children still
|
|
138
|
+
* do not fit, a span weaker than `strong` lets them run long; otherwise `spill` either skips
|
|
139
|
+
* every child weaker than the span (`instant`), or lets them run long (`overrun`).
|
|
140
|
+
*/
|
|
141
|
+
export declare function settle(span: SpanClaim, kids: readonly Claim[], fit: Fit, spill: 'instant' | 'overrun'): {
|
|
142
|
+
plan: FitPlan;
|
|
143
|
+
fell: boolean;
|
|
144
|
+
};
|
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
|
@@ -39,7 +39,7 @@ 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;
|
|
@@ -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,7 +457,7 @@ 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) {
|
|
@@ -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;
|