@msb235/blits 0.7.1 → 0.8.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 +180 -0
- package/README.md +22 -4
- package/dist/book.js +4 -4
- package/dist/chain.d.ts +1 -1
- package/dist/chain.js +57 -54
- package/dist/channels.d.ts +26 -5
- package/dist/channels.js +12 -8
- package/dist/clock.d.ts +8 -1
- package/dist/clock.js +14 -3
- package/dist/clone.d.ts +3 -0
- package/dist/clone.js +16 -1
- package/dist/closed.d.ts +1 -1
- package/dist/closed.js +3 -2
- package/dist/color.d.ts +0 -11
- package/dist/color.js +4 -24
- package/dist/columns.js +0 -1
- package/dist/copyout.d.ts +6 -0
- package/dist/copyout.js +38 -0
- package/dist/crowd.d.ts +7 -5
- package/dist/crowd.js +32 -18
- package/dist/cue.d.ts +3 -1
- package/dist/cue.js +31 -20
- package/dist/departed.d.ts +34 -0
- package/dist/departed.js +73 -0
- package/dist/easing.d.ts +5 -0
- package/dist/easing.js +31 -2
- package/dist/everyone.d.ts +56 -0
- package/dist/everyone.js +221 -0
- package/dist/fade.d.ts +8 -1
- package/dist/fade.js +34 -6
- package/dist/fill.d.ts +22 -13
- package/dist/fill.js +104 -41
- package/dist/fit.js +2 -1
- package/dist/fold.d.ts +37 -0
- package/dist/fold.js +213 -28
- package/dist/gather.d.ts +15 -9
- package/dist/gather.js +60 -25
- package/dist/gone.js +10 -0
- package/dist/handle.d.ts +16 -5
- package/dist/handle.js +56 -6
- package/dist/held.d.ts +8 -5
- package/dist/held.js +56 -58
- package/dist/history.d.ts +17 -13
- package/dist/history.js +47 -28
- package/dist/hosts.js +55 -8
- package/dist/index.d.ts +9 -4
- package/dist/index.js +5 -2
- package/dist/keyfill.d.ts +7 -0
- package/dist/keyfill.js +124 -0
- package/dist/keyrows.d.ts +1 -0
- package/dist/keyrows.js +1 -1
- package/dist/lane.d.ts +32 -2
- package/dist/lane.js +5 -0
- package/dist/lanes.d.ts +32 -8
- package/dist/lanes.js +69 -37
- package/dist/leavings.d.ts +26 -0
- package/dist/leavings.js +70 -0
- package/dist/meet.js +14 -8
- package/dist/mixer.d.ts +39 -7
- package/dist/mixer.js +50 -10
- package/dist/motion.d.ts +2 -0
- package/dist/motion.js +4 -1
- package/dist/motions.d.ts +22 -15
- package/dist/motions.js +40 -34
- package/dist/move.js +23 -16
- package/dist/moved.d.ts +11 -0
- package/dist/moved.js +29 -0
- package/dist/origin.d.ts +19 -0
- package/dist/origin.js +68 -0
- package/dist/owed.d.ts +4 -4
- package/dist/owed.js +8 -8
- package/dist/owner.d.ts +1 -1
- package/dist/owner.js +2 -2
- package/dist/pace.d.ts +13 -5
- package/dist/pace.js +21 -8
- package/dist/pack.d.ts +3 -1
- package/dist/pack.js +15 -24
- package/dist/paging.d.ts +8 -2
- package/dist/paging.js +44 -4
- package/dist/patch.d.ts +14 -9
- package/dist/patch.js +83 -21
- package/dist/place.js +18 -3
- package/dist/project.d.ts +12 -1
- package/dist/project.js +101 -83
- package/dist/pull.d.ts +2 -0
- package/dist/pull.js +8 -0
- package/dist/qualify.js +10 -2
- package/dist/reading.d.ts +5 -0
- package/dist/reading.js +5 -0
- package/dist/record.d.ts +9 -0
- package/dist/record.js +42 -0
- package/dist/relink.d.ts +11 -5
- package/dist/relink.js +7 -5
- package/dist/restored.d.ts +41 -0
- package/dist/restored.js +91 -0
- package/dist/revive.d.ts +10 -3
- package/dist/revive.js +31 -23
- package/dist/roster.d.ts +11 -0
- package/dist/roster.js +46 -0
- package/dist/rotation.d.ts +29 -0
- package/dist/rotation.js +91 -0
- package/dist/rows.d.ts +5 -5
- package/dist/rows.js +5 -5
- package/dist/sample.d.ts +3 -2
- package/dist/sample.js +32 -15
- package/dist/seek.js +97 -114
- package/dist/shared.d.ts +6 -0
- package/dist/shared.js +15 -0
- package/dist/signals.d.ts +24 -3
- package/dist/signals.js +50 -11
- package/dist/spans.js +7 -0
- package/dist/stack.d.ts +11 -0
- package/dist/stack.js +47 -0
- package/dist/standing.d.ts +10 -0
- package/dist/standing.js +79 -0
- package/dist/store.d.ts +3 -3
- package/dist/store.js +3 -3
- package/dist/strays.js +3 -0
- package/dist/tape.d.ts +8 -5
- package/dist/tape.js +21 -13
- package/dist/testing.d.ts +35 -0
- package/dist/testing.js +116 -0
- package/dist/touch.d.ts +6 -0
- package/dist/touch.js +28 -0
- package/dist/transport.d.ts +49 -6
- package/dist/transport.js +89 -19
- package/dist/turns.d.ts +18 -0
- package/dist/turns.js +72 -0
- package/dist/tweened.js +1 -1
- package/dist/types/channel.d.ts +46 -0
- package/dist/types/channel.js +1 -0
- package/dist/types/history.d.ts +139 -0
- package/dist/types/history.js +1 -0
- package/dist/types/mix.d.ts +336 -0
- package/dist/types/mix.js +1 -0
- package/dist/types/patch.d.ts +187 -0
- package/dist/types/patch.js +1 -0
- package/dist/types/place.d.ts +157 -0
- package/dist/types/place.js +1 -0
- package/dist/types/transport.d.ts +71 -0
- package/dist/types/transport.js +1 -0
- package/dist/types/voice.d.ts +323 -0
- package/dist/types/voice.js +1 -0
- package/dist/types.d.ts +7 -1162
- package/dist/unpage.d.ts +2 -2
- package/dist/unpage.js +53 -25
- package/dist/voice.d.ts +90 -22
- package/dist/voice.js +105 -43
- package/dist/wave.d.ts +42 -0
- package/dist/wave.js +62 -0
- package/dist/weigh.js +1 -1
- package/docs/schema.html +42 -36
- package/package.json +8 -3
package/dist/motions.js
CHANGED
|
@@ -43,7 +43,7 @@ const wrongKind = (channel, holdsNumber) => new Error(`blits: channel ${channel}
|
|
|
43
43
|
* The one copy, read by `at` and by a mix's lane alike.
|
|
44
44
|
*
|
|
45
45
|
* After the law, each subject's run of `stride` numbers is its release time, its flags, a tween's
|
|
46
|
-
*
|
|
46
|
+
* ms for the stretch, then `x0`, `v0` and `to` per axis. A live sample reads this one buffer and nothing else the patch owns: at
|
|
47
47
|
* 10k springs of one subject each, a buffer per field, a map lookup per sample and the law in an
|
|
48
48
|
* object of its own took a call from 130 ns to 490.
|
|
49
49
|
*/
|
|
@@ -52,6 +52,8 @@ export class Motions {
|
|
|
52
52
|
numbers = new Numbers(this);
|
|
53
53
|
/** Numbers by subject once a second is numbered: till then the one subject is found by `numbers`. */
|
|
54
54
|
slots = new Store();
|
|
55
|
+
/** How many numbers have been given up, so a number kept elsewhere can tell it is stale. */
|
|
56
|
+
freed = 0;
|
|
55
57
|
/** Axes per subject; -1 until the first subject sets it. */
|
|
56
58
|
n = -1;
|
|
57
59
|
cap = 0;
|
|
@@ -163,10 +165,10 @@ export class Motions {
|
|
|
163
165
|
*/
|
|
164
166
|
released = [];
|
|
165
167
|
/**
|
|
166
|
-
* Forgets a subject, so a later ask starts it afresh from `from`. Given the mix time `at
|
|
167
|
-
* history, its run is kept for a rewind before then, until `reach` passes it.
|
|
168
|
+
* Forgets a subject, so a later ask starts it afresh from `from`. Given the mix time `at` and frame
|
|
169
|
+
* `seq`, under history, its run is kept for a rewind before then, until `reach` passes it.
|
|
168
170
|
*/
|
|
169
|
-
release(subject, at, reach = Number.NEGATIVE_INFINITY) {
|
|
171
|
+
release(subject, at, reach = Number.NEGATIVE_INFINITY, seq = Number.NaN) {
|
|
170
172
|
const s = this.known(subject);
|
|
171
173
|
if (s === undefined)
|
|
172
174
|
return;
|
|
@@ -176,19 +178,21 @@ export class Motions {
|
|
|
176
178
|
if (e.at >= reach)
|
|
177
179
|
kept.push(e);
|
|
178
180
|
else
|
|
179
|
-
this.owner?.page(this.ownerId, 'released', e.subject, [
|
|
181
|
+
this.owner?.page(this.ownerId, 'released', e.subject, [
|
|
182
|
+
{ at: e.at, seq: e.seq, data: runOf(e) },
|
|
183
|
+
]);
|
|
180
184
|
this.released = kept;
|
|
181
|
-
this.released.push({ subject, at, ...this.runAt(s) });
|
|
185
|
+
this.released.push({ subject, at, seq, ...this.runAt(s) });
|
|
182
186
|
}
|
|
183
187
|
this.slots.delete(subject);
|
|
184
188
|
this.numbers.release(s);
|
|
185
189
|
}
|
|
186
|
-
/** Puts back each subject released after
|
|
187
|
-
unrelease(
|
|
188
|
-
const back = this.released.filter((e) => e.
|
|
190
|
+
/** Puts back each subject released after frame `seq`, as it stood when released. */
|
|
191
|
+
unrelease(seq) {
|
|
192
|
+
const back = this.released.filter((e) => e.seq > seq).sort((a, b) => a.seq - b.seq);
|
|
189
193
|
if (back.length === 0)
|
|
190
194
|
return;
|
|
191
|
-
this.released = this.released.filter((e) => e.
|
|
195
|
+
this.released = this.released.filter((e) => e.seq <= seq);
|
|
192
196
|
const done = new Set();
|
|
193
197
|
for (const e of back) {
|
|
194
198
|
if (done.has(e.subject))
|
|
@@ -239,24 +243,24 @@ export class Motions {
|
|
|
239
243
|
}
|
|
240
244
|
}
|
|
241
245
|
/** A released run a history store gave back, for a rewind to before its release. */
|
|
242
|
-
reclaim(subject, at, run) {
|
|
243
|
-
if (this.released.some((e) => e.
|
|
246
|
+
reclaim(subject, at, seq, run) {
|
|
247
|
+
if (this.released.some((e) => e.seq === seq && Object.is(e.subject, subject)))
|
|
244
248
|
return;
|
|
245
|
-
this.released.push({ subject, at, ...run });
|
|
249
|
+
this.released.push({ subject, at, seq, ...run });
|
|
246
250
|
}
|
|
247
251
|
/**
|
|
248
|
-
* Stretches a history store gave back, each by the
|
|
252
|
+
* Stretches a history store gave back, each by the frame it was in force by: each life of the
|
|
249
253
|
* subject, ended by a release, takes those from its own, ahead of the earliest it still keeps.
|
|
250
254
|
*/
|
|
251
255
|
restretch(subject, back) {
|
|
252
256
|
const lives = this.released
|
|
253
257
|
.filter((e) => Object.is(e.subject, subject))
|
|
254
|
-
.sort((x, y) => x.
|
|
258
|
+
.sort((x, y) => x.seq - y.seq);
|
|
255
259
|
let from = Number.NEGATIVE_INFINITY;
|
|
256
|
-
const within = (to) => back.filter((r) => r.
|
|
260
|
+
const within = (to) => back.filter((r) => r.seq > from && r.seq <= to).map((r) => r.data);
|
|
257
261
|
for (const e of lives) {
|
|
258
|
-
e.stretches = ahead(within(e.
|
|
259
|
-
from = e.
|
|
262
|
+
e.stretches = ahead(within(e.seq), e.stretches);
|
|
263
|
+
from = e.seq;
|
|
260
264
|
}
|
|
261
265
|
const s = this.known(subject);
|
|
262
266
|
if (s === undefined)
|
|
@@ -298,7 +302,7 @@ export class Motions {
|
|
|
298
302
|
return true;
|
|
299
303
|
}
|
|
300
304
|
/**
|
|
301
|
-
* Copies subject `s`'s current stretch into `out` from `o` as its release time, its
|
|
305
|
+
* Copies subject `s`'s current stretch into `out` from `o` as its release time, its ms, then
|
|
302
306
|
* `x0`, `v0` and `to` per axis; false, copying nothing, while it has a change pending or an
|
|
303
307
|
* earlier stretch kept, which only `sample` handles.
|
|
304
308
|
*/
|
|
@@ -354,7 +358,7 @@ export class Motions {
|
|
|
354
358
|
this.peek(s, t, xo, vo);
|
|
355
359
|
}
|
|
356
360
|
/**
|
|
357
|
-
* Where subject `s`'s run starts in `runs` (its release time, then flags,
|
|
361
|
+
* Where subject `s`'s run starts in `runs` (its release time, then flags, ms, `x0`, `v0`
|
|
358
362
|
* and `to`) while it has nothing pending and no earlier stretch kept; -1 for any other.
|
|
359
363
|
*/
|
|
360
364
|
bareAt(s) {
|
|
@@ -455,6 +459,7 @@ export class Motions {
|
|
|
455
459
|
this.check(c.to, this.n);
|
|
456
460
|
this.check(c.v, this.n);
|
|
457
461
|
c.made = this.owner === null ? Number.NaN : this.owner.now();
|
|
462
|
+
c.seq = this.owner === null ? Number.NaN : this.owner.seq();
|
|
458
463
|
if (c.at === undefined) {
|
|
459
464
|
const at = this.frame(subject);
|
|
460
465
|
if (!Number.isNaN(at))
|
|
@@ -511,6 +516,7 @@ export class Motions {
|
|
|
511
516
|
this.runs[i] = on ? flags | bit : flags & ~bit;
|
|
512
517
|
}
|
|
513
518
|
forget(s) {
|
|
519
|
+
this.freed++;
|
|
514
520
|
if (this.pending !== null)
|
|
515
521
|
this.pending[s] = undefined;
|
|
516
522
|
if (this.older !== null)
|
|
@@ -519,16 +525,16 @@ export class Motions {
|
|
|
519
525
|
this.causes[s] = undefined;
|
|
520
526
|
}
|
|
521
527
|
/**
|
|
522
|
-
* Takes back every change made after
|
|
528
|
+
* Takes back every change made after frame `seq`: each subject plays from its earliest stretch
|
|
523
529
|
* kept, with the changes made by then queued to be applied again.
|
|
524
530
|
*/
|
|
525
|
-
rewind(
|
|
526
|
-
this.unrelease(
|
|
531
|
+
rewind(seq) {
|
|
532
|
+
this.unrelease(seq);
|
|
527
533
|
for (let s = 0; s < this.numbers.size; s++)
|
|
528
534
|
if (this.numbers.subject(s) !== absent)
|
|
529
|
-
this.cut(s,
|
|
535
|
+
this.cut(s, seq);
|
|
530
536
|
}
|
|
531
|
-
cut(s,
|
|
537
|
+
cut(s, seq) {
|
|
532
538
|
const older = this.older?.[s];
|
|
533
539
|
const pending = this.pending?.[s];
|
|
534
540
|
const current = this.latest(s);
|
|
@@ -536,7 +542,7 @@ export class Motions {
|
|
|
536
542
|
const kept = [];
|
|
537
543
|
let cut = false;
|
|
538
544
|
for (const c of [...stretches.slice(1).map((seg) => seg.change), ...(pending ?? [])])
|
|
539
|
-
if (c === undefined || c.
|
|
545
|
+
if (c === undefined || c.seq === undefined || !(c.seq > seq)) {
|
|
540
546
|
if (c !== undefined)
|
|
541
547
|
kept.push(c);
|
|
542
548
|
}
|
|
@@ -684,12 +690,15 @@ export class Motions {
|
|
|
684
690
|
list.splice(i, 0, seg);
|
|
685
691
|
}
|
|
686
692
|
/**
|
|
687
|
-
* The mix time a stretch was in force by, which a store keys it under: when the change
|
|
688
|
-
* ended it was made, which falls in the same life of the subject.
|
|
693
|
+
* The mix time and frame a stretch was in force by, which a store keys it under: when the change
|
|
694
|
+
* that ended it was made, which falls in the same life of the subject.
|
|
689
695
|
*/
|
|
690
696
|
made(next) {
|
|
691
|
-
const
|
|
692
|
-
|
|
697
|
+
const c = next.change;
|
|
698
|
+
const made = c?.made;
|
|
699
|
+
if (made === undefined || Number.isNaN(made))
|
|
700
|
+
return { at: this.owner?.now() ?? Number.NaN, seq: this.owner?.seq() ?? Number.NaN };
|
|
701
|
+
return { at: made, seq: c?.seq ?? Number.NaN };
|
|
693
702
|
}
|
|
694
703
|
/** Lets go of stretches older than the one in force at `reading.horizon`. */
|
|
695
704
|
prune(s) {
|
|
@@ -706,10 +715,7 @@ export class Motions {
|
|
|
706
715
|
const out = list.splice(0, n);
|
|
707
716
|
const subject = this.numbers.subject(s);
|
|
708
717
|
if (this.owner !== null && subject !== absent)
|
|
709
|
-
this.owner.page(this.ownerId, 'stretch', subject, out.map((seg, i) => ({
|
|
710
|
-
at: this.made(out[i + 1] ?? list[0] ?? this.latest(s)),
|
|
711
|
-
data: seg,
|
|
712
|
-
})));
|
|
718
|
+
this.owner.page(this.ownerId, 'stretch', subject, out.map((seg, i) => ({ ...this.made(out[i + 1] ?? list[0] ?? this.latest(s)), data: seg })));
|
|
713
719
|
if (list.length === 0) {
|
|
714
720
|
this.older[s] = undefined;
|
|
715
721
|
this.flag(s, OLDER, false);
|
package/dist/move.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { changed
|
|
1
|
+
import { changed } from './chain.js';
|
|
2
2
|
import { popDue, schedule } from './due.js';
|
|
3
|
+
import { detour } from './everyone.js';
|
|
3
4
|
import { beginFade, part, retire } from './fade.js';
|
|
4
5
|
import { expireGone, keepGone } from './gone.js';
|
|
5
|
-
import {
|
|
6
|
+
import { reorigin } from './held.js';
|
|
6
7
|
import { ownerReading } from './owner.js';
|
|
7
8
|
import { mixAt, place, repin, startOf } from './place.js';
|
|
8
9
|
import { pageVoice } from './revive.js';
|
|
10
|
+
import { delist } from './roster.js';
|
|
9
11
|
import { scoredLeft, scoredRemove } from './scored.js';
|
|
10
12
|
import { lapse } from './spans.js';
|
|
11
13
|
import { unplay } from './strays.js';
|
|
@@ -31,6 +33,11 @@ export function move(mix, now) {
|
|
|
31
33
|
}
|
|
32
34
|
function moveTo(mix, now) {
|
|
33
35
|
mix.now = now;
|
|
36
|
+
mix.looked = false;
|
|
37
|
+
if (mix.unshared) {
|
|
38
|
+
mix.unshared = false;
|
|
39
|
+
detour(mix);
|
|
40
|
+
}
|
|
34
41
|
mix.frame = nextFrame();
|
|
35
42
|
mix.reducedNow = mix.reduced;
|
|
36
43
|
for (const a of mix.announced)
|
|
@@ -52,18 +59,19 @@ function moveTo(mix, now) {
|
|
|
52
59
|
continue;
|
|
53
60
|
if (Number.isNaN(voice.opened))
|
|
54
61
|
voice.opened = now;
|
|
55
|
-
if ((voice.state === 'live' || voice.state === 'frozen') && voice.outAt <= now)
|
|
56
|
-
beginFade(mix, voice, { over: Number.isNaN(voice.outOver) ? undefined : voice.outOver }, Math.max(startOf(voice), voice.outAt));
|
|
57
62
|
if (voice.state === 'pending' &&
|
|
58
63
|
(voice.owner === null ? now : ownerReading(voice.owner, now)) >= voice.start) {
|
|
59
64
|
voice.state = 'live';
|
|
60
|
-
|
|
65
|
+
reorigin(mix, voice, now);
|
|
61
66
|
changed(mix, voice);
|
|
62
67
|
}
|
|
68
|
+
// After it starts, so a fade due by now on a voice that started since the last frame begins.
|
|
69
|
+
if ((voice.state === 'live' || voice.state === 'frozen') && voice.outAt <= now)
|
|
70
|
+
beginFade(mix, voice, { over: Number.isNaN(voice.outOver) ? undefined : voice.outOver }, Math.max(startOf(voice), voice.outAt));
|
|
63
71
|
if (voice.state !== 'pending' && Number.isFinite(voice.span)) {
|
|
64
72
|
const end = voice.span + voice.latest;
|
|
65
73
|
if (voice.elapsedAt(now) >= end) {
|
|
66
|
-
voice.play(true, now);
|
|
74
|
+
voice.play(true, now, mix.transport.seq);
|
|
67
75
|
// The fade starts when the last pass ended, not at the frame that noticed, so it plays
|
|
68
76
|
// the same at any frame rate and a read at another time can find it.
|
|
69
77
|
if (voice.state === 'live') {
|
|
@@ -154,8 +162,12 @@ function prune(mix) {
|
|
|
154
162
|
}
|
|
155
163
|
voices.length = kept;
|
|
156
164
|
}
|
|
157
|
-
if (pruned)
|
|
165
|
+
if (pruned) {
|
|
158
166
|
mix.general = mix.general.filter((v) => v.state !== 'done');
|
|
167
|
+
if (mix.sharers.length > 0)
|
|
168
|
+
mix.sharers = mix.sharers.filter((v) => v.state !== 'done');
|
|
169
|
+
detour(mix);
|
|
170
|
+
}
|
|
159
171
|
}
|
|
160
172
|
/** The voice's place in a list in id order, or -1 where it is not there. */
|
|
161
173
|
function indexOf(voices, voice) {
|
|
@@ -180,18 +192,13 @@ function leave(mix, voice) {
|
|
|
180
192
|
mix.gone.push(voice);
|
|
181
193
|
scoredLeft(mix, voice);
|
|
182
194
|
}
|
|
183
|
-
else
|
|
195
|
+
else {
|
|
184
196
|
mix.parters.delete(voice);
|
|
197
|
+
mix.departed.add(mix, voice);
|
|
198
|
+
}
|
|
185
199
|
unplay(mix, voice);
|
|
186
200
|
changed(mix, voice);
|
|
187
|
-
|
|
188
|
-
unindex(mix, voice);
|
|
189
|
-
if (voice.holding !== null && mix.owners !== null)
|
|
190
|
-
mix.owners = mix.owners.filter((v) => v !== voice);
|
|
191
|
-
if (voice.spec.locus !== undefined)
|
|
192
|
-
mix.loci--;
|
|
193
|
-
if (voice.spec.anchor !== undefined)
|
|
194
|
-
mix.anchored--;
|
|
201
|
+
delist(mix, voice);
|
|
195
202
|
return voice.named === null;
|
|
196
203
|
}
|
|
197
204
|
/** Lets go of the voices that have left and that history no longer reaches. */
|
package/dist/moved.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Motions } from './motions.js';
|
|
2
|
+
import type { Subject } from './voice.js';
|
|
3
|
+
/**
|
|
4
|
+
* A motion voice's delta for a subject on the general path: what its patch's `at` gives, written
|
|
5
|
+
* into the record's last delta and the array that holds, where `at` makes an object and an array a
|
|
6
|
+
* call, and the patch's number for the subject kept on the record, where `at` looks it up a call.
|
|
7
|
+
* `setting` has the voice time to sample at.
|
|
8
|
+
*/
|
|
9
|
+
export declare function moved<I>(run: Motions<I>, key: string, subject: I, setting: {
|
|
10
|
+
readonly elapsed: number;
|
|
11
|
+
}, rec: Subject<unknown>): Record<string, unknown>;
|
package/dist/moved.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { doubles } from './channels.js';
|
|
2
|
+
/**
|
|
3
|
+
* A motion voice's delta for a subject on the general path: what its patch's `at` gives, written
|
|
4
|
+
* into the record's last delta and the array that holds, where `at` makes an object and an array a
|
|
5
|
+
* call, and the patch's number for the subject kept on the record, where `at` looks it up a call.
|
|
6
|
+
* `setting` has the voice time to sample at.
|
|
7
|
+
*/
|
|
8
|
+
export function moved(run, key, subject, setting, rec) {
|
|
9
|
+
let s = rec.run;
|
|
10
|
+
if (rec.freed !== run.freed) {
|
|
11
|
+
s = run.slot(subject);
|
|
12
|
+
rec.run = s;
|
|
13
|
+
rec.freed = run.freed;
|
|
14
|
+
}
|
|
15
|
+
const xs = run.xs;
|
|
16
|
+
run.sample(s, setting.elapsed, xs, run.vs);
|
|
17
|
+
const delta = rec.delta ?? {};
|
|
18
|
+
if (run.scalar(s)) {
|
|
19
|
+
delta[key] = xs[0];
|
|
20
|
+
return delta;
|
|
21
|
+
}
|
|
22
|
+
const n = run.n;
|
|
23
|
+
const held = delta[key];
|
|
24
|
+
const out = Array.isArray(held) && held.length === n ? held : doubles(n, 0);
|
|
25
|
+
for (let i = 0; i < n; i++)
|
|
26
|
+
out[i] = xs[i];
|
|
27
|
+
delta[key] = out;
|
|
28
|
+
return delta;
|
|
29
|
+
}
|
package/dist/origin.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Clock } from './clock.js';
|
|
2
|
+
import type { Voice } from './voice.js';
|
|
3
|
+
/** A clock a voice ran on before its current one, until its local time `until`. */
|
|
4
|
+
export interface Past extends Clock {
|
|
5
|
+
until: number;
|
|
6
|
+
/** The voice's `rebuilds` while it ran on this clock. */
|
|
7
|
+
rebuilds: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Keeps the clock a voice is leaving at its local time `u`, ahead of a rate change, ramp or seek,
|
|
11
|
+
* where a subject's origin could still be found on it.
|
|
12
|
+
*/
|
|
13
|
+
export declare function leave<I, O>(voice: Voice<I, O>, u: number): void;
|
|
14
|
+
/**
|
|
15
|
+
* The mix time a voice's clock first read `elapsed`, or will by its controls now, through its owners'
|
|
16
|
+
* clocks. A clock entered already past it, by a seek, answers where that clock would have read it.
|
|
17
|
+
* `rebuilt` starts from the last seek that rebuilt state, as a stateful record does.
|
|
18
|
+
*/
|
|
19
|
+
export declare function originOf<I, O>(voice: Voice<I, O>, elapsed: number, rebuilt: boolean): number;
|
package/dist/origin.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { elapsedWith, timeWith } from './clock.js';
|
|
2
|
+
/** The most past clocks a voice keeps. */
|
|
3
|
+
const MOST = 256;
|
|
4
|
+
/**
|
|
5
|
+
* Keeps the clock a voice is leaving at its local time `u`, ahead of a rate change, ramp or seek,
|
|
6
|
+
* where a subject's origin could still be found on it.
|
|
7
|
+
*/
|
|
8
|
+
export function leave(voice, u) {
|
|
9
|
+
const past = voice.clocks;
|
|
10
|
+
if (past !== null && past.length >= MOST)
|
|
11
|
+
return;
|
|
12
|
+
// A voice with neither stagger nor children is only ever asked when its clock read 0.
|
|
13
|
+
const reach = voice.holding !== null || voice.spec.stagger !== undefined ? Number.POSITIVE_INFINITY : 0;
|
|
14
|
+
if (past !== null && reached(past, 0) >= reach && reached(past, fresh(voice)) >= reach)
|
|
15
|
+
return;
|
|
16
|
+
const { anchorNow, anchorElapsed, rate, ramp, rebuilds } = voice;
|
|
17
|
+
const left = { anchorNow, anchorElapsed, rate, ramp, until: u, rebuilds };
|
|
18
|
+
if (past === null)
|
|
19
|
+
voice.clocks = [left];
|
|
20
|
+
else
|
|
21
|
+
past.push(left);
|
|
22
|
+
}
|
|
23
|
+
/** The most a voice's past clocks from index `from` read before they were left. */
|
|
24
|
+
function reached(past, from) {
|
|
25
|
+
let most = Number.NEGATIVE_INFINITY;
|
|
26
|
+
for (let i = from; i < past.length; i++) {
|
|
27
|
+
const p = past[i];
|
|
28
|
+
const e = elapsedWith(p, p.until);
|
|
29
|
+
if (e > most)
|
|
30
|
+
most = e;
|
|
31
|
+
}
|
|
32
|
+
return most;
|
|
33
|
+
}
|
|
34
|
+
/** Where a walk for a record rebuilt by the voice's last seek starts: its first clock since. */
|
|
35
|
+
function fresh(voice) {
|
|
36
|
+
const past = voice.clocks;
|
|
37
|
+
if (past === null)
|
|
38
|
+
return 0;
|
|
39
|
+
let i = 0;
|
|
40
|
+
while (i < past.length && past[i].rebuilds !== voice.rebuilds)
|
|
41
|
+
i++;
|
|
42
|
+
return i;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The mix time a voice's clock first read `elapsed`, or will by its controls now, through its owners'
|
|
46
|
+
* clocks. A clock entered already past it, by a seek, answers where that clock would have read it.
|
|
47
|
+
* `rebuilt` starts from the last seek that rebuilt state, as a stateful record does.
|
|
48
|
+
*/
|
|
49
|
+
export function originOf(voice, elapsed, rebuilt) {
|
|
50
|
+
const past = voice.clocks;
|
|
51
|
+
let t = Number.NaN;
|
|
52
|
+
if (past !== null) {
|
|
53
|
+
for (let i = rebuilt ? fresh(voice) : 0; i < past.length; i++) {
|
|
54
|
+
const p = past[i];
|
|
55
|
+
const at = timeWith(p, elapsed);
|
|
56
|
+
if (at <= p.until) {
|
|
57
|
+
t = at;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (Number.isNaN(t)) {
|
|
63
|
+
t = timeWith(voice, elapsed);
|
|
64
|
+
if (t === Number.POSITIVE_INFINITY)
|
|
65
|
+
t = voice.start + elapsed;
|
|
66
|
+
}
|
|
67
|
+
return voice.owner === null ? t : originOf(voice.owner, t, rebuilt);
|
|
68
|
+
}
|
package/dist/owed.d.ts
CHANGED
|
@@ -17,8 +17,8 @@ export declare class Owed {
|
|
|
17
17
|
owes(slot: number): boolean;
|
|
18
18
|
/** Whether the subject at `slot` owes voice `id` this fill. */
|
|
19
19
|
owesVoice(slot: number, id: number): boolean;
|
|
20
|
-
/** The subject at `slot` owes the voices `met`, which the caller may reuse after. */
|
|
21
|
-
owe(slot: number, met: readonly number[]): void;
|
|
22
|
-
/** Whether `met`
|
|
23
|
-
last(met: readonly number[]): boolean;
|
|
20
|
+
/** The subject at `slot` owes the first `n` voices of `met`, which the caller may reuse after. */
|
|
21
|
+
owe(slot: number, met: readonly number[], n: number): void;
|
|
22
|
+
/** Whether the first `n` of `met` are the last list a subject came to owe this fill. */
|
|
23
|
+
last(met: readonly number[], n: number): boolean;
|
|
24
24
|
}
|
package/dist/owed.js
CHANGED
|
@@ -31,19 +31,19 @@ export class Owed {
|
|
|
31
31
|
owesVoice(slot, id) {
|
|
32
32
|
return this.lists[this.at[slot]].includes(id);
|
|
33
33
|
}
|
|
34
|
-
/** The subject at `slot` owes the voices `met`, which the caller may reuse after. */
|
|
35
|
-
owe(slot, met) {
|
|
36
|
-
if (!this.last(met))
|
|
37
|
-
this.lists.push(met.slice());
|
|
34
|
+
/** The subject at `slot` owes the first `n` voices of `met`, which the caller may reuse after. */
|
|
35
|
+
owe(slot, met, n) {
|
|
36
|
+
if (!this.last(met, n))
|
|
37
|
+
this.lists.push(met.slice(0, n));
|
|
38
38
|
this.fill[slot] = this.now;
|
|
39
39
|
this.at[slot] = this.lists.length - 1;
|
|
40
40
|
}
|
|
41
|
-
/** Whether `met`
|
|
42
|
-
last(met) {
|
|
41
|
+
/** Whether the first `n` of `met` are the last list a subject came to owe this fill. */
|
|
42
|
+
last(met, n) {
|
|
43
43
|
const last = this.lists[this.lists.length - 1];
|
|
44
|
-
if (last === undefined || last.length !==
|
|
44
|
+
if (last === undefined || last.length !== n)
|
|
45
45
|
return false;
|
|
46
|
-
for (let i = 0; i <
|
|
46
|
+
for (let i = 0; i < n; i++)
|
|
47
47
|
if (last[i] !== met[i])
|
|
48
48
|
return false;
|
|
49
49
|
return true;
|
package/dist/owner.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export declare function adopt<I, O>(owner: Voice<I, O>, child: Voice<I, O>): voi
|
|
|
24
24
|
/** A child left: its owner, where that was its last child and it should leave too. */
|
|
25
25
|
export declare function orphan<I, O>(child: Voice<I, O>): Voice<I, O> | null;
|
|
26
26
|
/** A child finished its passes: its owner has played once every child it held has. */
|
|
27
|
-
export declare function childPlayed<I, O>(owner: Voice<I, O>, at: number): void;
|
|
27
|
+
export declare function childPlayed<I, O>(owner: Voice<I, O>, at: number, seq: number): void;
|
|
28
28
|
/** Every voice an owner holds, its children's children included. */
|
|
29
29
|
export declare function descendants<I, O>(owner: Voice<I, O>, visit: (v: Voice<I, O>) => void): void;
|
|
30
30
|
/** Whether any owner above a voice reads a signal for its weight, which lanes cannot fill. */
|
package/dist/owner.js
CHANGED
|
@@ -48,10 +48,10 @@ export function orphan(child) {
|
|
|
48
48
|
return h.children.length === 0 && owner.state !== 'done' ? owner : null;
|
|
49
49
|
}
|
|
50
50
|
/** A child finished its passes: its owner has played once every child it held has. */
|
|
51
|
-
export function childPlayed(owner, at) {
|
|
51
|
+
export function childPlayed(owner, at, seq) {
|
|
52
52
|
const h = owner.holding;
|
|
53
53
|
if (++h.played === h.kids)
|
|
54
|
-
owner.play(true, at);
|
|
54
|
+
owner.play(true, at, seq);
|
|
55
55
|
}
|
|
56
56
|
/** Every voice an owner holds, its children's children included. */
|
|
57
57
|
export function descendants(owner, visit) {
|
package/dist/pace.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { type Clock } from './clock.js';
|
|
2
|
+
/** A clock in a pace's log, with the frame that set it, which a history store's cut goes by. */
|
|
3
|
+
export interface Paced extends Clock {
|
|
4
|
+
seq: number;
|
|
5
|
+
}
|
|
2
6
|
/**
|
|
3
7
|
* A mix's own rate, as a clock: mix time as a function of host time, which here means the host's
|
|
4
8
|
* timestamp less what `rebase` took out. Each entry is the clock from the host time it took effect,
|
|
@@ -9,7 +13,7 @@ import { type Clock } from './clock.js';
|
|
|
9
13
|
export declare class Pace {
|
|
10
14
|
private readonly keeps;
|
|
11
15
|
private log;
|
|
12
|
-
constructor(keeps: boolean, log?:
|
|
16
|
+
constructor(keeps: boolean, log?: Paced[]);
|
|
13
17
|
private get clock();
|
|
14
18
|
/** Set before the first sync, the clock waits to be anchored there, reading host time until then. */
|
|
15
19
|
private get waiting();
|
|
@@ -30,11 +34,15 @@ export declare class Pace {
|
|
|
30
34
|
/** Whether the mix clock stands still from host time `u` on, short of another change. */
|
|
31
35
|
stopped(u: number): boolean;
|
|
32
36
|
/**
|
|
33
|
-
* Sets the rate from host time `u`, at once or over `over` host ms, the way a
|
|
34
|
-
* voice's. Under history, lets go of the entries a read no longer reaches: those
|
|
35
|
-
* time `reach`.
|
|
37
|
+
* Sets the rate from host time `u`, in frame `seq`, at once or over `over` host ms, the way a
|
|
38
|
+
* handle sets a voice's. Under history, lets go of the entries a read no longer reaches: those
|
|
39
|
+
* over before mix time `reach`.
|
|
36
40
|
*/
|
|
37
|
-
change(u: number, rate: number, over: number, reach: number): void;
|
|
41
|
+
change(u: number, rate: number, over: number, reach: number, seq: number): void;
|
|
42
|
+
/** Lets go of the clocks over before mix time `reach`, and answers them. */
|
|
43
|
+
shed(reach: number): Paced[];
|
|
44
|
+
/** Puts back clocks a history store kept, ahead of the oldest still held. */
|
|
45
|
+
unshed(back: readonly Paced[]): void;
|
|
38
46
|
/** Lets go of the clocks set after host time `u`, for a seek back to then. */
|
|
39
47
|
cut(u: number): void;
|
|
40
48
|
/** A copy holding the clocks that took effect before host time `u`, for a read back to then. */
|
package/dist/pace.js
CHANGED
|
@@ -9,7 +9,9 @@ import { elapsedWith, rateWith, retime, timeWith } from './clock.js';
|
|
|
9
9
|
export class Pace {
|
|
10
10
|
keeps;
|
|
11
11
|
log;
|
|
12
|
-
constructor(keeps, log = [
|
|
12
|
+
constructor(keeps, log = [
|
|
13
|
+
{ anchorNow: 0, anchorElapsed: 0, rate: 1, ramp: null, seq: Number.NEGATIVE_INFINITY },
|
|
14
|
+
]) {
|
|
13
15
|
this.keeps = keeps;
|
|
14
16
|
this.log = log;
|
|
15
17
|
}
|
|
@@ -75,13 +77,13 @@ export class Pace {
|
|
|
75
77
|
return r.to === 0 && !this.waiting && u >= c.anchorNow + r.over;
|
|
76
78
|
}
|
|
77
79
|
/**
|
|
78
|
-
* Sets the rate from host time `u`, at once or over `over` host ms, the way a
|
|
79
|
-
* voice's. Under history, lets go of the entries a read no longer reaches: those
|
|
80
|
-
* time `reach`.
|
|
80
|
+
* Sets the rate from host time `u`, in frame `seq`, at once or over `over` host ms, the way a
|
|
81
|
+
* handle sets a voice's. Under history, lets go of the entries a read no longer reaches: those
|
|
82
|
+
* over before mix time `reach`.
|
|
81
83
|
*/
|
|
82
|
-
change(u, rate, over, reach) {
|
|
84
|
+
change(u, rate, over, reach, seq) {
|
|
83
85
|
const was = this.clock;
|
|
84
|
-
const next = { ...was };
|
|
86
|
+
const next = { ...was, seq };
|
|
85
87
|
if (Number.isNaN(u) || this.waiting) {
|
|
86
88
|
const from = this.rateAt(Number.NaN);
|
|
87
89
|
next.ramp = over > 0 && rate !== from ? { from, to: rate, over } : null;
|
|
@@ -96,11 +98,22 @@ export class Pace {
|
|
|
96
98
|
log.push(next);
|
|
97
99
|
else
|
|
98
100
|
log[log.length - 1] = next;
|
|
101
|
+
this.shed(reach);
|
|
102
|
+
}
|
|
103
|
+
/** Lets go of the clocks over before mix time `reach`, and answers them. */
|
|
104
|
+
shed(reach) {
|
|
105
|
+
const log = this.log;
|
|
99
106
|
let drop = 0;
|
|
100
107
|
while (drop + 1 < log.length && log[drop + 1].anchorElapsed <= reach)
|
|
101
108
|
drop++;
|
|
102
|
-
|
|
103
|
-
|
|
109
|
+
return drop > 0 ? log.splice(0, drop) : [];
|
|
110
|
+
}
|
|
111
|
+
/** Puts back clocks a history store kept, ahead of the oldest still held. */
|
|
112
|
+
unshed(back) {
|
|
113
|
+
const first = this.log[0].anchorNow;
|
|
114
|
+
const older = back.filter((c) => c.anchorNow < first).sort((a, b) => a.anchorNow - b.anchorNow);
|
|
115
|
+
if (older.length > 0)
|
|
116
|
+
this.log = [...older, ...this.log];
|
|
104
117
|
}
|
|
105
118
|
/** Lets go of the clocks set after host time `u`, for a seek back to then. */
|
|
106
119
|
cut(u) {
|
package/dist/pack.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface PackedRecord {
|
|
|
11
11
|
state: unknown;
|
|
12
12
|
stepped: number;
|
|
13
13
|
ticks: number;
|
|
14
|
+
rebuilt: number;
|
|
14
15
|
/**
|
|
15
16
|
* What `setting.keep` held, in the order its owners first kept it. An owner is code, which no
|
|
16
17
|
* store keeps, and a voice revived from a descriptor has new ones; the same code keeps in the same
|
|
@@ -19,9 +20,10 @@ export interface PackedRecord {
|
|
|
19
20
|
kept: unknown[] | null;
|
|
20
21
|
base?: Record<string, unknown>;
|
|
21
22
|
slope?: Record<string, unknown>;
|
|
22
|
-
snaps?: [at: number, held: PackedRecord][];
|
|
23
|
+
snaps?: [at: number, seq: number, held: PackedRecord][];
|
|
23
24
|
inputs?: {
|
|
24
25
|
at: number;
|
|
26
|
+
seq: number;
|
|
25
27
|
value: number;
|
|
26
28
|
}[];
|
|
27
29
|
}
|
package/dist/pack.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { clone } from './clone.js';
|
|
2
|
+
import { record } from './record.js';
|
|
2
3
|
/** A voice's record of one subject as plain data, its patch's `pack` taking its state. */
|
|
3
4
|
export function packHeld(voice, h) {
|
|
4
5
|
const patch = voice.patch;
|
|
@@ -14,6 +15,7 @@ export function packHeld(voice, h) {
|
|
|
14
15
|
state: h.state === undefined ? undefined : patch.pack ? patch.pack(h.state) : clone(h.state),
|
|
15
16
|
stepped: h.stepped,
|
|
16
17
|
ticks: h.ticks,
|
|
18
|
+
rebuilt: h.rebuilt,
|
|
17
19
|
kept: kept.length === 0 ? null : kept.map(clone),
|
|
18
20
|
};
|
|
19
21
|
if (h.base !== undefined)
|
|
@@ -21,7 +23,7 @@ export function packHeld(voice, h) {
|
|
|
21
23
|
if (h.slope !== undefined)
|
|
22
24
|
p.slope = clone(h.slope);
|
|
23
25
|
if (h.snaps !== undefined)
|
|
24
|
-
p.snaps = h.snaps.map((s) => [s.at, packHeld(voice, s.held)]);
|
|
26
|
+
p.snaps = h.snaps.map((s) => [s.at, s.seq, packHeld(voice, s.held)]);
|
|
25
27
|
if (h.inputs !== undefined)
|
|
26
28
|
p.inputs = h.inputs.map((e) => ({ ...e }));
|
|
27
29
|
return p;
|
|
@@ -29,28 +31,17 @@ export function packHeld(voice, h) {
|
|
|
29
31
|
/** A record made again from `packHeld`'s data, for `voice`. */
|
|
30
32
|
export function unpackHeld(voice, p) {
|
|
31
33
|
const patch = voice.patch;
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
probed: Number.NaN,
|
|
44
|
-
delta: null,
|
|
45
|
-
phase: 0,
|
|
46
|
-
seeks: 0,
|
|
47
|
-
kept: null,
|
|
48
|
-
voice,
|
|
49
|
-
next: null,
|
|
50
|
-
version: Number.NaN,
|
|
51
|
-
loci: null,
|
|
52
|
-
slot: -1,
|
|
53
|
-
};
|
|
34
|
+
const bands = p.bands === null ? null : Uint8Array.from(p.bands);
|
|
35
|
+
const h = record(voice, p.reaches, p.state === undefined ? undefined : patch.unpack ? patch.unpack(p.state) : p.state);
|
|
36
|
+
h.delay = p.delay;
|
|
37
|
+
h.since = p.since;
|
|
38
|
+
h.shown = p.shown;
|
|
39
|
+
h.weight = p.weight;
|
|
40
|
+
h.rested = p.rested;
|
|
41
|
+
h.bands = bands;
|
|
42
|
+
h.stepped = p.stepped;
|
|
43
|
+
h.ticks = p.ticks;
|
|
44
|
+
h.rebuilt = p.rebuilt;
|
|
54
45
|
if (p.kept !== null)
|
|
55
46
|
h.unkept = p.kept;
|
|
56
47
|
if (p.base !== undefined)
|
|
@@ -58,7 +49,7 @@ export function unpackHeld(voice, p) {
|
|
|
58
49
|
if (p.slope !== undefined)
|
|
59
50
|
h.slope = p.slope;
|
|
60
51
|
if (p.snaps !== undefined)
|
|
61
|
-
h.snaps = p.snaps.map(([at, held]) => ({ at, held: unpackHeld(voice, held) }));
|
|
52
|
+
h.snaps = p.snaps.map(([at, seq, held]) => ({ at, seq, held: unpackHeld(voice, held) }));
|
|
62
53
|
if (p.inputs !== undefined)
|
|
63
54
|
h.inputs = p.inputs;
|
|
64
55
|
return h;
|