@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/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Fit, Order, Strength } from './fit.js';
|
|
1
2
|
/**
|
|
2
3
|
* One field of a delta, with its own arithmetic.
|
|
3
4
|
*
|
|
@@ -18,7 +19,7 @@ export interface Channel<V> {
|
|
|
18
19
|
bounds?: readonly [min: number, max: number];
|
|
19
20
|
/** Identity. Absent means the channel has none: it replaces rather than contributes. */
|
|
20
21
|
rest?: V;
|
|
21
|
-
/** Fold two
|
|
22
|
+
/** Fold two contributions into one. */
|
|
22
23
|
merge(a: V, b: V): V;
|
|
23
24
|
/** Fade toward `rest` by weight 0..1. Required when `rest` is set; absent otherwise. */
|
|
24
25
|
scale?(v: V, w: number): V;
|
|
@@ -26,7 +27,7 @@ export interface Channel<V> {
|
|
|
26
27
|
lerp(a: V, b: V, u: number): V;
|
|
27
28
|
/**
|
|
28
29
|
* Optional: `merge(into, scale(v, w))`, written into `into` and returned, so a channel whose
|
|
29
|
-
* values are objects need not allocate per
|
|
30
|
+
* values are objects need not allocate per contribution. The mix only hands it a value it made.
|
|
30
31
|
*/
|
|
31
32
|
fold?(into: V, v: V, w: number): V;
|
|
32
33
|
}
|
|
@@ -94,17 +95,18 @@ export interface Setting<S = void, H = unknown> {
|
|
|
94
95
|
* The state `owner` keeps for this voice and this subject, made by `init` on first ask. The mix
|
|
95
96
|
* holds it, so a read at another time can copy it instead of moving it. A stateful signal keeps
|
|
96
97
|
* its state here and nowhere else. With lanes on, a patch or weight signal that first calls it
|
|
97
|
-
* partway through playing can have that state advanced once for one subject
|
|
98
|
-
*
|
|
99
|
-
* never does.
|
|
98
|
+
* partway through playing can have that state advanced once for one subject probed the frame
|
|
99
|
+
* before it starts and not on that frame; one that keeps state from its first call, or a patch
|
|
100
|
+
* that declares `state`, never does.
|
|
100
101
|
*/
|
|
101
102
|
keep<K>(owner: object, init: () => K): K;
|
|
102
103
|
/**
|
|
103
104
|
* Reports an event at `timestamp`, for this voice and this subject. The mix queues it until the
|
|
104
105
|
* host drains it, and never calls back. Meant for `step`, where under `stepMs` the timestamp is
|
|
105
106
|
* the interval the event happened in rather than the frame that sampled it. Sent from a stateless
|
|
106
|
-
* patch's `at` while it runs on a lane, it goes out for every subject the
|
|
107
|
-
* frame or not; `at` is not asked for a subject at weight 0,
|
|
107
|
+
* patch's `at` while it runs on a lane, it goes out for every subject the lane fills, probed that
|
|
108
|
+
* frame or not: each probed that frame or the last; `at` is not asked for a subject at weight 0,
|
|
109
|
+
* so nothing is sent from it there.
|
|
108
110
|
*/
|
|
109
111
|
send(event: unknown): void;
|
|
110
112
|
}
|
|
@@ -175,7 +177,7 @@ export interface Patch<I, O, S = void, H = unknown> {
|
|
|
175
177
|
readonly reads?: readonly string[];
|
|
176
178
|
/**
|
|
177
179
|
* `phase` is 0..1 across one pass, wrapping. Not asked for a subject its voice gives no weight, unless the voice is fading to rest;
|
|
178
|
-
* `step` still runs.
|
|
180
|
+
* `step` still runs. A `keys` patch's stops are not read there either, unless its voice is in a locus.
|
|
179
181
|
*/
|
|
180
182
|
at(phase: number, subject: I, setting: Setting<S, H>): Partial<O>;
|
|
181
183
|
/** Per-subject state, created on the first frame this patch sees a subject. */
|
|
@@ -195,6 +197,13 @@ export interface Patch<I, O, S = void, H = unknown> {
|
|
|
195
197
|
* function needs its own.
|
|
196
198
|
*/
|
|
197
199
|
clone?(state: S): S;
|
|
200
|
+
/**
|
|
201
|
+
* `state` as data that survives `structuredClone`, for a history `store` to keep; `unpack` makes
|
|
202
|
+
* a state from it again. With a store, a patch with `state` or `step` needs both, or `cue`
|
|
203
|
+
* refuses it.
|
|
204
|
+
*/
|
|
205
|
+
pack?(state: S): unknown;
|
|
206
|
+
unpack?(data: unknown): S;
|
|
198
207
|
}
|
|
199
208
|
/**
|
|
200
209
|
* A 0..1 scalar resolved per subject per frame from something outside the clock. `input` is set
|
|
@@ -217,12 +226,15 @@ export interface FadeSpec {
|
|
|
217
226
|
ease?: Easing;
|
|
218
227
|
}
|
|
219
228
|
/**
|
|
220
|
-
* The
|
|
221
|
-
* fully in (`in`), when its fade out begins (`out`), and when it
|
|
229
|
+
* The times a voice is known by on the mix clock: when its fade in begins (`start`), when it is
|
|
230
|
+
* fully in (`in`), when its last pass ends (`coast`), when its fade out begins (`out`), and when it
|
|
231
|
+
* is gone (`end`). A voice coasts when `played` resolves true, for its latest-staggered subject: at
|
|
232
|
+
* its `out` where it does not freeze, and where it starts showing its last frame where it does. A
|
|
233
|
+
* voice that loops for good, or fades before its last pass ends, never coasts.
|
|
222
234
|
*
|
|
223
235
|
* @category score
|
|
224
236
|
*/
|
|
225
|
-
export type Mark = 'start' | 'in' | 'out' | 'end';
|
|
237
|
+
export type Mark = 'start' | 'in' | 'coast' | 'out' | 'end';
|
|
226
238
|
/**
|
|
227
239
|
* Selects voices by what the plan knows of them, never by a channel's value, within the asking
|
|
228
240
|
* voice's own score unless it names another. Where several match,
|
|
@@ -243,7 +255,8 @@ export interface Query {
|
|
|
243
255
|
/**
|
|
244
256
|
* A time given by another voice: `mark` of the voice `of` selects, moved by `by` ms. `after` is
|
|
245
257
|
* sugar for that voice's `end`, `with` for its `start`, and `before` for its start less `by`. A
|
|
246
|
-
* string selects by name.
|
|
258
|
+
* string selects by name. `all` is the latest of its anchors, known once every one is; `any` the
|
|
259
|
+
* earliest, known once one has passed or every one is known.
|
|
247
260
|
*
|
|
248
261
|
* @category score
|
|
249
262
|
*/
|
|
@@ -260,6 +273,10 @@ export type Anchor = {
|
|
|
260
273
|
} | {
|
|
261
274
|
before: string | Query;
|
|
262
275
|
by?: number;
|
|
276
|
+
} | {
|
|
277
|
+
all: readonly Anchor[];
|
|
278
|
+
} | {
|
|
279
|
+
any: readonly Anchor[];
|
|
263
280
|
};
|
|
264
281
|
/**
|
|
265
282
|
* Where a voice sits on the mix clock: at most one of `start` and `in`, and at most one of `out`
|
|
@@ -276,21 +293,23 @@ export interface Placement {
|
|
|
276
293
|
end?: number | Anchor;
|
|
277
294
|
}
|
|
278
295
|
/**
|
|
279
|
-
* One mark as `marks` lists it: one of a voice's
|
|
280
|
-
*
|
|
296
|
+
* One mark as `marks` lists it: one of a voice's, or one the host announced on a score, which has a
|
|
297
|
+
* name and no voice.
|
|
281
298
|
*
|
|
282
299
|
* @category score
|
|
283
300
|
*/
|
|
284
301
|
export interface Marked {
|
|
285
302
|
/** On the host's clock. */
|
|
286
303
|
timestamp: number;
|
|
287
|
-
/** Which of a voice's
|
|
304
|
+
/** Which of a voice's this is; undefined for an announced mark. */
|
|
288
305
|
mark: Mark | undefined;
|
|
289
306
|
/** The voice it belongs to; undefined for an announced mark. */
|
|
290
307
|
voice: number | undefined;
|
|
291
308
|
score: string | undefined;
|
|
292
309
|
name: string | undefined;
|
|
293
310
|
tags: readonly string[];
|
|
311
|
+
/** The `name` of the mix the voice is in, or that announced the mark; undefined for none. */
|
|
312
|
+
mix: string | undefined;
|
|
294
313
|
}
|
|
295
314
|
/**
|
|
296
315
|
* An event at a time on a voice's own clock, which `book` hands a host ahead of time. It plays once
|
|
@@ -368,8 +387,31 @@ export interface Booker {
|
|
|
368
387
|
*
|
|
369
388
|
* @category voice
|
|
370
389
|
*/
|
|
371
|
-
|
|
390
|
+
/**
|
|
391
|
+
* How a voice or owner held by a span may give way so the span keeps its budget, which the span's
|
|
392
|
+
* fit reads. Anything it does not allow, it will not do. Read only under a span.
|
|
393
|
+
*
|
|
394
|
+
* @category score
|
|
395
|
+
*/
|
|
396
|
+
export interface SpanHints {
|
|
397
|
+
/** How many times faster than its own rate it may play. Default 1: no faster. */
|
|
398
|
+
faster?: number;
|
|
399
|
+
/** How many times slower than its own rate it may play. Default 1: no slower. */
|
|
400
|
+
slower?: number;
|
|
401
|
+
/** Whether it may start before the one before it ends, where the span's order would not. */
|
|
402
|
+
overlap?: boolean;
|
|
403
|
+
/** Whether it may jump to its end instead of playing. A span held by a span is never skipped. */
|
|
404
|
+
ballast?: boolean;
|
|
405
|
+
/** How hard its own length holds against the span's budget. Default `weak`. */
|
|
406
|
+
priority?: Strength;
|
|
407
|
+
}
|
|
408
|
+
export interface VoiceSpec<I, O, H = unknown> extends SpanHints {
|
|
372
409
|
patch: Patch<I, O, unknown, H>;
|
|
410
|
+
/**
|
|
411
|
+
* What `history.revive` rebuilds this spec from, so a history `store` can page the voice out once
|
|
412
|
+
* it has left. A voice cued without it stays in memory for as long as history reaches it.
|
|
413
|
+
*/
|
|
414
|
+
as?: Described;
|
|
373
415
|
/**
|
|
374
416
|
* Which subjects this voice reaches. Default: all of them. The predicate is fixed at `cue`; it
|
|
375
417
|
* runs per subject the first time the mix sees that subject, and the answer is kept. Every
|
|
@@ -395,7 +437,7 @@ export interface VoiceSpec<I, O, H = unknown> {
|
|
|
395
437
|
/**
|
|
396
438
|
* true loops for good, false plays one pass, n plays n passes. A finite loop leaves when its
|
|
397
439
|
* passes are done for the latest-staggered subject it has seen — over `fade.out` where one is
|
|
398
|
-
* set, at once where none is — unless it
|
|
440
|
+
* set, at once where none is — unless it freezes after.
|
|
399
441
|
*/
|
|
400
442
|
loop?: boolean | number;
|
|
401
443
|
/**
|
|
@@ -406,12 +448,14 @@ export interface VoiceSpec<I, O, H = unknown> {
|
|
|
406
448
|
/**
|
|
407
449
|
* Show a subject the first frame before it starts — while the voice is pending and while the
|
|
408
450
|
* subject waits out its `stagger` — the last frame of the last pass after a finite loop plays
|
|
409
|
-
* out, or both. A
|
|
451
|
+
* out, or both. A frozen subject's clock stands still: `at` is asked for the edge frame and `step`
|
|
410
452
|
* does not run, past one last step to the end of the last pass. `fade.in` counts from the first
|
|
411
|
-
* frame the voice shows. A voice
|
|
412
|
-
* takes it out, so every one cued must be faded or it stays in the mix for good. Does
|
|
413
|
-
* a motion patch, which
|
|
453
|
+
* frame the voice shows. A voice freezing after stays, `frozen`, until `fade()` or an anchored
|
|
454
|
+
* `out` takes it out, so every one cued must be faded or it stays in the mix for good. Does
|
|
455
|
+
* nothing to a motion patch, which keeps its target already. Default: neither.
|
|
414
456
|
*/
|
|
457
|
+
freeze?: 'before' | 'after' | 'both';
|
|
458
|
+
/** @deprecated Use `freeze`. A voice that sets only `hold` still freezes; `freeze` wins over it. */
|
|
415
459
|
hold?: 'before' | 'after' | 'both';
|
|
416
460
|
/** Steady weight, or a signal read per subject per frame. Default 1. */
|
|
417
461
|
weight?: number | Signal<I, H>;
|
|
@@ -443,7 +487,7 @@ export interface VoiceSpec<I, O, H = unknown> {
|
|
|
443
487
|
* the owner's clock, in ms from when the owner starts, and default to where that clock is now; a
|
|
444
488
|
* bare name in an anchor means a sibling, another voice the owner holds. Its clock runs on the
|
|
445
489
|
* owner's, so the owner's rate, ramp and seeks move it, and its weight is multiplied by the
|
|
446
|
-
* owner's weight and fade. Without a `
|
|
490
|
+
* owner's weight and fade. Without a `freeze` of its own it takes the owner's.
|
|
447
491
|
*/
|
|
448
492
|
owner?: Handle<I>;
|
|
449
493
|
}
|
|
@@ -452,7 +496,7 @@ export interface VoiceSpec<I, O, H = unknown> {
|
|
|
452
496
|
*
|
|
453
497
|
* @category voice
|
|
454
498
|
*/
|
|
455
|
-
export interface OwnerSpec<I, H = unknown> {
|
|
499
|
+
export interface OwnerSpec<I, H = unknown> extends SpanHints {
|
|
456
500
|
/**
|
|
457
501
|
* When the owner starts, on its own owner's clock: the host's for an owner on the mix, as a
|
|
458
502
|
* voice's `start` is. Default: now.
|
|
@@ -467,7 +511,9 @@ export interface OwnerSpec<I, H = unknown> {
|
|
|
467
511
|
weight?: number | Signal<I, H>;
|
|
468
512
|
/** One ramp in and out over every child, multiplied into their weights as `weight` is. */
|
|
469
513
|
fade?: FadeSpec;
|
|
470
|
-
/** The
|
|
514
|
+
/** The freeze every child without one of its own takes. */
|
|
515
|
+
freeze?: 'before' | 'after' | 'both';
|
|
516
|
+
/** @deprecated Use `freeze`. An owner that sets only `hold` still freezes; `freeze` wins over it. */
|
|
471
517
|
hold?: 'before' | 'after' | 'both';
|
|
472
518
|
/** Words a source attaches to it, as a voice's. */
|
|
473
519
|
tags?: readonly string[];
|
|
@@ -480,6 +526,58 @@ export interface OwnerSpec<I, H = unknown> {
|
|
|
480
526
|
/** The owner it plays under, so owners nest. */
|
|
481
527
|
owner?: Handle<I>;
|
|
482
528
|
}
|
|
529
|
+
/**
|
|
530
|
+
* What `span` takes: an owner with a budget, which lays out the voices it holds in an order and
|
|
531
|
+
* fits them into the budget.
|
|
532
|
+
*
|
|
533
|
+
* @category score
|
|
534
|
+
*/
|
|
535
|
+
export interface SpanSpec<I, H = unknown> extends OwnerSpec<I, H> {
|
|
536
|
+
/** Its budget, ms on its own clock. Default: none, so its children only keep their order. */
|
|
537
|
+
duration?: number;
|
|
538
|
+
/** How hard the budget holds against its children's own lengths. Default `strong`. */
|
|
539
|
+
priority?: Strength;
|
|
540
|
+
/** How its children are laid out, in the order they were cued. Default `queue`. */
|
|
541
|
+
order?: Order;
|
|
542
|
+
/** How far through the one before a child starts under `stagger`, 0..1. Default 0.5. */
|
|
543
|
+
share?: number;
|
|
544
|
+
/**
|
|
545
|
+
* How the children are fitted once they are retimed toward the budget, faster or slower as
|
|
546
|
+
* their hints allow. Default `pipe(condense(), shed())`.
|
|
547
|
+
*/
|
|
548
|
+
fit?: Fit;
|
|
549
|
+
/**
|
|
550
|
+
* What happens where the fit leaves them too long for a span at least `strong`: `instant` jumps
|
|
551
|
+
* every child weaker than the span to its end at once, and `overrun` lets them run long.
|
|
552
|
+
* Default `instant`.
|
|
553
|
+
*/
|
|
554
|
+
spill?: 'instant' | 'overrun';
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* How a span's last fit came out.
|
|
558
|
+
*
|
|
559
|
+
* @category score
|
|
560
|
+
*/
|
|
561
|
+
export interface FitResult {
|
|
562
|
+
/** Its budget, ms on its own clock; Infinity for none. */
|
|
563
|
+
budget: number;
|
|
564
|
+
/** Where its children end, ms on its own clock from its start. */
|
|
565
|
+
length: number;
|
|
566
|
+
/** How far `length` runs past the budget, 0 where it does not. */
|
|
567
|
+
over: number;
|
|
568
|
+
/** How many children it jumps to their end. */
|
|
569
|
+
skipped: number;
|
|
570
|
+
/** Whether the fit failed and `spill` decided. */
|
|
571
|
+
fell: boolean;
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* A span's handle: an owner's, and how its children were last fitted.
|
|
575
|
+
*
|
|
576
|
+
* @category score
|
|
577
|
+
*/
|
|
578
|
+
export interface SpanHandle<I = unknown> extends Handle<I> {
|
|
579
|
+
readonly result: FitResult;
|
|
580
|
+
}
|
|
483
581
|
/**
|
|
484
582
|
* How a voice leaves when it is faded: the whole voice, or one subject.
|
|
485
583
|
*
|
|
@@ -488,8 +586,13 @@ export interface OwnerSpec<I, H = unknown> {
|
|
|
488
586
|
export type FadeOptions<I = unknown> = {
|
|
489
587
|
/** The ramp, ms. Defaults to the voice's own `fade.out`. */
|
|
490
588
|
over?: number;
|
|
491
|
-
/**
|
|
492
|
-
|
|
589
|
+
/**
|
|
590
|
+
* When the fade begins. A mix time: ahead, the voice plays untouched until then and begins
|
|
591
|
+
* its fade exactly there, its `out` mark fixed from now, and `rise` before then takes it
|
|
592
|
+
* back with nothing changed; at or behind now, the fade began then and is partway. `'rest'`:
|
|
593
|
+
* leave per subject at the first frame that subject's contribution is at rest. Default now.
|
|
594
|
+
*/
|
|
595
|
+
at?: number | 'rest';
|
|
493
596
|
/** Ms after which the voice leaves whether or not it rested. */
|
|
494
597
|
deadline?: number;
|
|
495
598
|
subject?: never;
|
|
@@ -510,8 +613,8 @@ export type FadeOptions<I = unknown> = {
|
|
|
510
613
|
*/
|
|
511
614
|
export interface Handle<I = unknown> {
|
|
512
615
|
readonly id: number;
|
|
513
|
-
/** `
|
|
514
|
-
readonly state: 'pending' | 'live' | '
|
|
616
|
+
/** `frozen`: a voice freezing after has played every pass and shows its last frame until faded. */
|
|
617
|
+
readonly state: 'pending' | 'live' | 'frozen' | 'fading' | 'done';
|
|
515
618
|
/** The handle of the owner this voice plays under; undefined for one on the mix clock. */
|
|
516
619
|
readonly owner: Handle<I> | undefined;
|
|
517
620
|
/** Live. Writes land on the next sync. */
|
|
@@ -530,10 +633,19 @@ export interface Handle<I = unknown> {
|
|
|
530
633
|
*/
|
|
531
634
|
seek(elapsed: number): void;
|
|
532
635
|
fade(opts?: FadeOptions<I>): void;
|
|
636
|
+
/**
|
|
637
|
+
* Turns a fade out around: the voice climbs back from where its fade had got to, up the same
|
|
638
|
+
* curve, over `over` ms for the whole curve (default its `fade.in`, else its `fade.out`), and
|
|
639
|
+
* plays on as if never faded. A fade waiting for rest is taken back at once; a voice not fading
|
|
640
|
+
* is left as it is. A voice whose own end began the fade starts it again at its next frame.
|
|
641
|
+
*/
|
|
642
|
+
rise(opts?: {
|
|
643
|
+
over?: number;
|
|
644
|
+
}): void;
|
|
533
645
|
/**
|
|
534
646
|
* The weight this voice gave `subject` the last frame that subject was probed: after its fades
|
|
535
647
|
* and its weight signal, before a locus folds it with its alternatives. 0 for a subject it does
|
|
536
|
-
* not reach, has not started on and does not
|
|
648
|
+
* not reach, has not started on and does not freeze before, has left at rest, or has never been
|
|
537
649
|
* probed for; 0 once done.
|
|
538
650
|
* Costs nothing until it is asked.
|
|
539
651
|
*/
|
|
@@ -558,7 +670,7 @@ export interface MixOptions<H = unknown> {
|
|
|
558
670
|
/** Merged into every `setting.host`. Its type is the mix's `H`, which every patch and signal it cues reads. */
|
|
559
671
|
host?: H;
|
|
560
672
|
/**
|
|
561
|
-
* The band a rest-less channel's
|
|
673
|
+
* The band a rest-less channel's contribution switches on and off across. It has to clear the frame
|
|
562
674
|
* noise in a real signal; these are placeholders until the klieg port measures one.
|
|
563
675
|
*/
|
|
564
676
|
band?: {
|
|
@@ -584,23 +696,232 @@ export interface MixOptions<H = unknown> {
|
|
|
584
696
|
* read back restores the nearest copy and steps forward from it. Off by default, and a mix without
|
|
585
697
|
* it keeps nothing. With `inputs`, it also keeps what each input signal on a voice's weight read
|
|
586
698
|
* per subject, and the host fields patches `reads`, each time they changed, so a read back over a
|
|
587
|
-
* `level` or a pointer is known rather than held.
|
|
699
|
+
* `level` or a pointer is known rather than held. With `tape`, the mix also records every call
|
|
700
|
+
* the host makes on it, its handles and its motion patches, so `seek` can move it and play those
|
|
701
|
+
* calls again.
|
|
588
702
|
*/
|
|
589
|
-
history?:
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
703
|
+
history?: HistoryOptions;
|
|
704
|
+
/**
|
|
705
|
+
* A subject's key, for a history `store`: unique among this mix's subjects for the session. A
|
|
706
|
+
* string or number subject is its own key; with a store, an object subject needs this.
|
|
707
|
+
*/
|
|
708
|
+
keyOf?(subject: never): string | number;
|
|
594
709
|
/**
|
|
595
710
|
* Whether a channel may run as a lane: computed for every subject at once in flat arrays, when
|
|
596
711
|
* every voice writing it can run that way. On by default; the pose is the same either way, so
|
|
597
712
|
* turning it off is for ruling a lane out, or for comparing against. Two things differ: a
|
|
598
|
-
* stateless patch's `setting.send` from `at` sends for every subject a lane fills, probed
|
|
599
|
-
* and a patch that first calls `setting.keep` partway through playing can
|
|
600
|
-
* more for one
|
|
713
|
+
* stateless patch's `setting.send` from `at` sends for every subject a lane fills, those probed
|
|
714
|
+
* this frame or the last, and a patch that first calls `setting.keep` partway through playing can
|
|
715
|
+
* advance that state once more for one subject probed the frame before (see `Setting.keep`).
|
|
601
716
|
*/
|
|
602
717
|
lanes?: boolean;
|
|
718
|
+
/**
|
|
719
|
+
* The transport this mix plays on, shared with other mixes, so they keep one clock and a query
|
|
720
|
+
* naming a score looks in all of them. The transport's `sync`, `rebase`, `rate`, `ramp` and
|
|
721
|
+
* `seek` move it, and the mix's own throw. Its history is the transport's, so the mix takes no
|
|
722
|
+
* `history` of its own.
|
|
723
|
+
*/
|
|
724
|
+
transport?: Transport;
|
|
725
|
+
/** What `Marked.mix` and errors call this mix. */
|
|
726
|
+
name?: string;
|
|
603
727
|
}
|
|
728
|
+
/**
|
|
729
|
+
* What a mix or transport remembers, for `seek` and `project` to read back: see
|
|
730
|
+
* `MixOptions.history`.
|
|
731
|
+
*
|
|
732
|
+
* @category mix
|
|
733
|
+
*/
|
|
734
|
+
export interface HistoryOptions {
|
|
735
|
+
/** How much stays in memory, ms of mix time. With a `store`, the rest is paged out, not dropped. */
|
|
736
|
+
ms: number;
|
|
737
|
+
every?: number;
|
|
738
|
+
inputs?: boolean;
|
|
739
|
+
tape?: TapeMaker;
|
|
740
|
+
/**
|
|
741
|
+
* Where history older than `ms` goes instead of being dropped. A `seek` or `project` reaching
|
|
742
|
+
* past memory needs `prepare` first, or throws `HistoryMiss`. With a store, blits never prunes
|
|
743
|
+
* the tape: the host bounds it.
|
|
744
|
+
*/
|
|
745
|
+
store?: HistoryStore;
|
|
746
|
+
/** Rebuilds a cued voice's spec from the descriptor it was cued with, so the voice can be paged out. */
|
|
747
|
+
revive?(d: Described): VoiceSpec<any, any>;
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* A voice as plain data: `kind` names what built it, and `data` survives `structuredClone`.
|
|
751
|
+
*
|
|
752
|
+
* @category mix
|
|
753
|
+
*/
|
|
754
|
+
export interface Described {
|
|
755
|
+
kind: string;
|
|
756
|
+
data: unknown;
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Where a history `store` keeps what memory let go of. blits decides what is kept and when it is
|
|
760
|
+
* read back; the store decides where it lives.
|
|
761
|
+
*
|
|
762
|
+
* @category mix
|
|
763
|
+
*/
|
|
764
|
+
export interface HistoryStore {
|
|
765
|
+
/** Records leaving memory. One brought back by a seek may be paged again: keep one per key and `at`. */
|
|
766
|
+
page(out: readonly Paged[]): void;
|
|
767
|
+
/**
|
|
768
|
+
* What a restore to mix time `t` needs, for every mix: from each stream, per voice and subject,
|
|
769
|
+
* the latest record at or before `t` and every record after it. Returning everything is fine.
|
|
770
|
+
*/
|
|
771
|
+
load(t: number): Promise<readonly Paged[]>;
|
|
772
|
+
/** A seek went back to `t`: every record after it is from a future the tape makes again. Forget them. */
|
|
773
|
+
cut(t: number): void;
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* What a history record holds: a voice's `controls`, a stateful record's `snap`, an `input`
|
|
777
|
+
* signal's reading, a subject's record when it `left` a voice, a motion subject's `released` run
|
|
778
|
+
* or older `stretch`, the `host` fields patches read, or a whole `voice` that has left.
|
|
779
|
+
*
|
|
780
|
+
* @category mix
|
|
781
|
+
*/
|
|
782
|
+
export type PagedStream = 'voice' | 'snap' | 'input' | 'left' | 'released' | 'stretch' | 'host' | 'controls';
|
|
783
|
+
/**
|
|
784
|
+
* One history record leaving memory, as plain data.
|
|
785
|
+
*
|
|
786
|
+
* @category mix
|
|
787
|
+
*/
|
|
788
|
+
export interface Paged {
|
|
789
|
+
/** The mix's `name`; '' for a mix made alone without one. */
|
|
790
|
+
mix: string;
|
|
791
|
+
stream: PagedStream;
|
|
792
|
+
/** The voice's id, for every stream but `host`. */
|
|
793
|
+
voice?: number;
|
|
794
|
+
/** The subject's key, for a record of one subject. */
|
|
795
|
+
subject?: string | number;
|
|
796
|
+
/** Mix time. */
|
|
797
|
+
at: number;
|
|
798
|
+
data: unknown;
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* How a transport is made: what history it keeps for every mix on it.
|
|
802
|
+
*
|
|
803
|
+
* @category mix
|
|
804
|
+
*/
|
|
805
|
+
export interface TransportOptions {
|
|
806
|
+
history?: MixOptions['history'];
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Every mix on a transport read at another mix time, with nothing live moved: `of` gives one mix's
|
|
810
|
+
* reading. Valid until the transport is next synced or a mix on it cued.
|
|
811
|
+
*
|
|
812
|
+
* @category mix
|
|
813
|
+
*/
|
|
814
|
+
export interface TransportProjection {
|
|
815
|
+
readonly timestamp: number;
|
|
816
|
+
of<I, O, H>(mix: Mix<I, O, H>): Projection<I, O>;
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* One clock several mixes play on, each over its own kit: one sync, one rate, one seek and one
|
|
820
|
+
* tape for all of them, and a score that spans them, so an anchor in one mix waits on a voice in
|
|
821
|
+
* another by naming the score they share. A query naming no score stays in its own mix.
|
|
822
|
+
*
|
|
823
|
+
* @category mix
|
|
824
|
+
*/
|
|
825
|
+
export interface Transport {
|
|
826
|
+
/** The host reports the clock once a frame, after the frame's cues, as `Mix.sync` does. */
|
|
827
|
+
sync(timestamp: number): void;
|
|
828
|
+
/** As `Mix.rebase`, for every mix on it. */
|
|
829
|
+
rebase(): void;
|
|
830
|
+
/** As `Mix.rate`, multiplied into every voice of every mix on it. */
|
|
831
|
+
rate: number;
|
|
832
|
+
ramp(rate: number, over: number): void;
|
|
833
|
+
/**
|
|
834
|
+
* As `Mix.seek`, for every mix on it at once: back, each mix restores itself; forward, the one
|
|
835
|
+
* tape plays every mix's calls again in the order they were made, so an anchor across mixes
|
|
836
|
+
* answers as it did. A mix dropped after the moment sought is back on the transport.
|
|
837
|
+
*/
|
|
838
|
+
seek(time: number): void;
|
|
839
|
+
/**
|
|
840
|
+
* Loads from the history `store` what `seek(time)` and `project(time)` will need, where that
|
|
841
|
+
* reaches past memory. Without a store, or within memory, it loads nothing.
|
|
842
|
+
*/
|
|
843
|
+
prepare(time: number): Promise<void>;
|
|
844
|
+
readonly now: number;
|
|
845
|
+
readonly tape: Tape | undefined;
|
|
846
|
+
/** Puts a named mark on a score every mix on it sees. Throws without a `score`. */
|
|
847
|
+
announce(name: string, opts: {
|
|
848
|
+
at?: number;
|
|
849
|
+
score: string;
|
|
850
|
+
tags?: readonly string[];
|
|
851
|
+
}): void;
|
|
852
|
+
/** Every mark of every mix on it, and every mark announced on it, earliest first. */
|
|
853
|
+
marks(from: number, to: number): Marked[];
|
|
854
|
+
/** Every mix on it read at mix time `time`, together, so anchors across them answer. */
|
|
855
|
+
project(time: number): TransportProjection;
|
|
856
|
+
/** Any mix on it live. */
|
|
857
|
+
readonly live: boolean;
|
|
858
|
+
/** Every mix on it inert. */
|
|
859
|
+
readonly inert: boolean;
|
|
860
|
+
/** Calls `fn` when any mix on it needs frames again after a sync, once until the next sync. */
|
|
861
|
+
onWake(fn: () => void): () => void;
|
|
862
|
+
/**
|
|
863
|
+
* Takes a mix off: it is no longer synced and takes no more calls. With history, a seek back
|
|
864
|
+
* before the drop puts it back as it was then; without, it is let go.
|
|
865
|
+
*/
|
|
866
|
+
drop<I, O, H>(mix: Mix<I, O, H>): void;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* One host call a mix recorded on its tape: `apply` makes it again, and `invert` does nothing,
|
|
870
|
+
* since a mix goes back by restoring what it kept rather than by undoing calls.
|
|
871
|
+
*
|
|
872
|
+
* @category mix
|
|
873
|
+
*/
|
|
874
|
+
export interface TapeOp {
|
|
875
|
+
apply(adapter: unknown): void;
|
|
876
|
+
invert(): TapeOp;
|
|
877
|
+
label?: string;
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* One future a tape keeps at its current entry: the calls the host made after the mix last went
|
|
881
|
+
* back here, before it made new ones.
|
|
882
|
+
*
|
|
883
|
+
* @category mix
|
|
884
|
+
*/
|
|
885
|
+
export interface TapeBranch {
|
|
886
|
+
/** Its first entry's id, which `switchBranch` takes. */
|
|
887
|
+
id: number;
|
|
888
|
+
label: string;
|
|
889
|
+
timestamp: number;
|
|
890
|
+
/** How many entries run along it from here. */
|
|
891
|
+
length: number;
|
|
892
|
+
/** Whether the mix plays it on from here. */
|
|
893
|
+
current: boolean;
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Where a mix keeps the host's calls, by mix time, for `seek`. weasel-history's `History`
|
|
897
|
+
* (`@weasel-js/history`) has this shape. A host reads `branches` and calls `switchBranch` to pick
|
|
898
|
+
* which recorded future plays on; moving it any other way is the mix's job.
|
|
899
|
+
*
|
|
900
|
+
* @category mix
|
|
901
|
+
*/
|
|
902
|
+
export interface Tape {
|
|
903
|
+
recordEntry(ops: TapeOp[], label: string): void;
|
|
904
|
+
undoDepth(): number;
|
|
905
|
+
timestampAt(i: number): number | undefined;
|
|
906
|
+
depthAt(t: number): number;
|
|
907
|
+
goto(n: number): void;
|
|
908
|
+
redo(): void;
|
|
909
|
+
prune(t: number): void;
|
|
910
|
+
branches(): readonly TapeBranch[];
|
|
911
|
+
switchBranch(id: number): void;
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Makes a mix's tape: weasel-history's `createHistory` is one. The mix passes the clock it stamps
|
|
915
|
+
* calls with, branching on, so a call made after going back keeps the old future as a branch, and
|
|
916
|
+
* coalescing off.
|
|
917
|
+
*
|
|
918
|
+
* @category mix
|
|
919
|
+
*/
|
|
920
|
+
export type TapeMaker = (adapter: unknown, opts: {
|
|
921
|
+
now: () => number;
|
|
922
|
+
branching: boolean;
|
|
923
|
+
coalesceWindowMs: number;
|
|
924
|
+
}) => Tape;
|
|
604
925
|
/**
|
|
605
926
|
* How sure a projection is of one channel: `exact` where only the clock and known changes drove
|
|
606
927
|
* it; `stepped` where state was advanced across a gap in one go, which is as good as the patch's
|
|
@@ -617,7 +938,7 @@ export type Doubt = 'exact' | 'stepped' | 'held';
|
|
|
617
938
|
* @category mix
|
|
618
939
|
*/
|
|
619
940
|
export interface Projection<I, O> {
|
|
620
|
-
/** The
|
|
941
|
+
/** The mix time it reads at. */
|
|
621
942
|
readonly timestamp: number;
|
|
622
943
|
/** The merged pose for one subject at this projection's timestamp. */
|
|
623
944
|
probe(subject: I, out?: O): O;
|
|
@@ -660,7 +981,20 @@ export interface Mix<I, O, H = unknown> {
|
|
|
660
981
|
* nest. Throws for a `loop`: a pass would have to restart its children.
|
|
661
982
|
*/
|
|
662
983
|
owns(spec: OwnerSpec<I, H>): Handle<I>;
|
|
663
|
-
/**
|
|
984
|
+
/**
|
|
985
|
+
* Cues a span: an owner that lays out the voices it holds in `order` and fits them into its
|
|
986
|
+
* `duration`, again each time one joins or leaves early. A child's start and rate are the
|
|
987
|
+
* span's to set, so it takes neither `start` nor an anchored start; its `SpanHints` say how it
|
|
988
|
+
* may give way. A span with a budget stays until the budget has passed, though its children
|
|
989
|
+
* finish sooner, so late ones can still join, and its `end` mark is where its children end or
|
|
990
|
+
* its budget does, whichever is later. A voice that loops for good cannot join one.
|
|
991
|
+
*/
|
|
992
|
+
span(spec: SpanSpec<I, H>): SpanHandle<I>;
|
|
993
|
+
/**
|
|
994
|
+
* The host reports the clock, once a frame. Nothing advances at the call. Throws for a timestamp
|
|
995
|
+
* earlier than the last sync's, short of a `rebase`: the host's clock only goes forward, and
|
|
996
|
+
* `seek` moves the mix.
|
|
997
|
+
*/
|
|
664
998
|
sync(timestamp: number): void;
|
|
665
999
|
/**
|
|
666
1000
|
* The time between the last sync and the next one is not to count: a host calls it when a
|
|
@@ -669,7 +1003,7 @@ export interface Mix<I, O, H = unknown> {
|
|
|
669
1003
|
rebase(): void;
|
|
670
1004
|
/**
|
|
671
1005
|
* The mix's own playback rate, multiplied into every voice's: 1 is real time and 0 pauses the
|
|
672
|
-
* whole mix. A negative rate throws
|
|
1006
|
+
* whole mix. A negative rate throws: the mix plays only forward, and `seek` moves it back. Setting it changes speed at
|
|
673
1007
|
* once; `ramp` eases into a new one. It scales mix time, which everything a voice owns runs on:
|
|
674
1008
|
* its clock, its fades and a handle's ramp, `stagger`, an anchor's `by`, `stepMs`, `maxDt`,
|
|
675
1009
|
* `history.ms`, every `dt`, and `setting.timestamp`. Timestamps on the host's clock name the same
|
|
@@ -703,18 +1037,54 @@ export interface Mix<I, O, H = unknown> {
|
|
|
703
1037
|
*/
|
|
704
1038
|
pull(subjects: Iterable<I>, into: Columns<O>): void;
|
|
705
1039
|
/**
|
|
706
|
-
* Reads the mix at another
|
|
707
|
-
*
|
|
708
|
-
* older than the
|
|
1040
|
+
* Reads the mix at another mix time, without moving it. Ahead of the mix it plays what is cued
|
|
1041
|
+
* forward, and throws for a time past a call the tape will play again after a `seek` back, which
|
|
1042
|
+
* only a seek there makes; behind it, it needs `history`, and throws for a time older than the
|
|
1043
|
+
* history reaches. On a transport it reads every mix on it together, so anchors across them
|
|
1044
|
+
* answer.
|
|
1045
|
+
*/
|
|
1046
|
+
project(time: number): Projection<I, O>;
|
|
1047
|
+
/**
|
|
1048
|
+
* Moves the mix to mix time `time`, as it stood at the end of that frame, and plays on from
|
|
1049
|
+
* there. Needs `history` with a `tape`, which records the host's calls by mix time.
|
|
1050
|
+
*
|
|
1051
|
+
* Back, the mix restores itself: controls, records, voices and marks as they stood then. Voices
|
|
1052
|
+
* that left after it are back on the handles the host holds, their `done` and `played` starting
|
|
1053
|
+
* over where they had settled since. Voices cued after it wait as `pending` until the mix plays
|
|
1054
|
+
* their cue again. Stateful voices restart from the copy `history` kept nearest before it and
|
|
1055
|
+
* step once to it, exact under `stepMs`. A subject faded out of a voice or dropped after it comes
|
|
1056
|
+
* back with the record it had, a motion patch's state included. Recorded input and host fields
|
|
1057
|
+
* after it are let go: after a seek back, signals and host fields read live again, and `assess`
|
|
1058
|
+
* reports what they fed as `held`.
|
|
1059
|
+
*
|
|
1060
|
+
* Forward, through a sync or a later seek, the mix plays the recorded calls again at the mix
|
|
1061
|
+
* times they were made: cues, handle writes, fades, retargets and pushes, its rate, marks,
|
|
1062
|
+
* `mute` and `drop`. A call the host makes while there are recorded calls ahead starts a new
|
|
1063
|
+
* branch, and the tape keeps the old future beside it; `tape.switchBranch` picks one back.
|
|
1064
|
+
* Playing past a time again sends its events and books its marks and hits again.
|
|
1065
|
+
*
|
|
1066
|
+
* The host goes on passing its own clock: the next `sync` reads the moment sought plus the
|
|
1067
|
+
* host's time since its last sync. Throws for a time older than history reaches, and before
|
|
1068
|
+
* the first sync. With a history `store`, a time older than memory needs `prepare` first, or
|
|
1069
|
+
* throws `HistoryMiss` before anything moves.
|
|
709
1070
|
*/
|
|
710
|
-
|
|
1071
|
+
seek(time: number): void;
|
|
1072
|
+
/** As `Transport.prepare`, for a mix made alone. */
|
|
1073
|
+
prepare(time: number): Promise<void>;
|
|
1074
|
+
/**
|
|
1075
|
+
* The mix clock at the last sync or seek, which `seek` and `project` take; NaN before the first
|
|
1076
|
+
* sync. Host time at the mix's rate, less what `rebase` took out and `seek` moved.
|
|
1077
|
+
*/
|
|
1078
|
+
readonly now: number;
|
|
1079
|
+
/** The tape `history.tape` made, undefined without one. */
|
|
1080
|
+
readonly tape: Tape | undefined;
|
|
711
1081
|
/**
|
|
712
1082
|
* Every channel at rest for this subject this frame, so a host can skip the write. After a probe
|
|
713
1083
|
* of the subject this frame, it answers for the pose that probe gave, without folding again.
|
|
714
1084
|
*/
|
|
715
1085
|
atRest(subject: I): boolean;
|
|
716
1086
|
/**
|
|
717
|
-
* A handle on every voice still in the mix, pending, live,
|
|
1087
|
+
* A handle on every voice still in the mix, pending, live, frozen or fading, in the order they were
|
|
718
1088
|
* cued; given a tag, only the voices whose `tags` carry it. Each is the handle `cue` returned for
|
|
719
1089
|
* that voice, so a listed handle is `===` the cued one.
|
|
720
1090
|
*/
|
|
@@ -722,12 +1092,19 @@ export interface Mix<I, O, H = unknown> {
|
|
|
722
1092
|
/** Anything still contributing, fading, or pending. */
|
|
723
1093
|
readonly live: boolean;
|
|
724
1094
|
/**
|
|
725
|
-
* Another frame would change no pose, so a host's frame loop may sleep: every voice is done,
|
|
1095
|
+
* Another frame would change no pose, so a host's frame loop may sleep: every voice is done, frozen
|
|
726
1096
|
* at a plain weight, or a motion whose every subject has landed on its target. At rate 0 it is
|
|
727
1097
|
* enough that every voice not done has started, at a plain weight, with no anchor. A change made
|
|
728
1098
|
* since the last sync, a retarget or a rate included, makes it false until the next.
|
|
729
1099
|
*/
|
|
730
1100
|
readonly inert: boolean;
|
|
1101
|
+
/**
|
|
1102
|
+
* Calls `fn` when a host call makes the mix need frames again after a sync: a cue, a fade, a
|
|
1103
|
+
* handle's or the mix's rate, a seek, a `drop`, a motion retargeted. It fires once until the next
|
|
1104
|
+
* sync clears the change, and never during `sync`. Returns a function that unsubscribes `fn`.
|
|
1105
|
+
* A frame loop sleeping on `inert` subscribes here to wake.
|
|
1106
|
+
*/
|
|
1107
|
+
onWake(fn: () => void): () => void;
|
|
731
1108
|
/** Fades every voice out: over `over` when given, over each voice's own `fade.out` otherwise. */
|
|
732
1109
|
mute(opts?: {
|
|
733
1110
|
over?: number;
|
|
@@ -755,8 +1132,8 @@ export interface Mix<I, O, H = unknown> {
|
|
|
755
1132
|
* Every event patches have sent since the last drain, earliest first, in the order they were sent
|
|
756
1133
|
* where two share a timestamp. A subject's events are made while it catches up, which is when it is
|
|
757
1134
|
* probed, so one nobody probes has sent nothing yet: promptness is the host's, by probing. A
|
|
758
|
-
* stateless patch's `at` on a lane is the exception: it runs
|
|
759
|
-
*
|
|
1135
|
+
* stateless patch's `at` on a lane is the exception: it runs at the frame's first probe for every
|
|
1136
|
+
* subject probed that frame or the last. Under `stepMs` each carries the end of the interval it happened in, so
|
|
760
1137
|
* what is sent and when does not depend on how the host spaces its probes. Time `rebase` took out
|
|
761
1138
|
* sends nothing. Given a tag, it takes only the events of voices carrying it and leaves the rest for
|
|
762
1139
|
* whoever drains them.
|