@msb235/blits 0.7.0 → 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 +196 -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 +33 -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 +36 -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.d.ts +32 -0
- package/dist/gone.js +144 -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 +49 -28
- package/dist/hosts.js +57 -9
- 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 +43 -7
- package/dist/mixer.js +60 -11
- 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 +31 -20
- 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 +188 -30
- 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/scored.d.ts +57 -0
- package/dist/scored.js +196 -0
- 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 +53 -6
- package/dist/transport.js +102 -20
- 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 +55 -25
- package/dist/voice.d.ts +92 -22
- package/dist/voice.js +108 -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/scored.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A mix's voices by the score they were cued on, for an anchor that asks for a score's marks: the
|
|
3
|
+
* cued ones as a set, the gone ones in the order they left, which an anchor reads once each. Built
|
|
4
|
+
* over the lists it saw: a seek or a copy that replaces either rebuilds that half on the next read.
|
|
5
|
+
* A score whose gone voices history lets go of gets a fresh list, so a read of the old one starts
|
|
6
|
+
* over.
|
|
7
|
+
*/
|
|
8
|
+
export class ScoreIndex {
|
|
9
|
+
cued = null;
|
|
10
|
+
by = new Map();
|
|
11
|
+
gone = null;
|
|
12
|
+
goneBy = new Map();
|
|
13
|
+
/** Bumped for a score each time a voice on it is cued, retimed, retired or leaves. */
|
|
14
|
+
versions = new Map();
|
|
15
|
+
/** The scores whose list on the transport names this mix. */
|
|
16
|
+
held = new Set();
|
|
17
|
+
}
|
|
18
|
+
function hold(mix, score) {
|
|
19
|
+
const held = mix.scored.held;
|
|
20
|
+
// A dropped mix is off the transport's lists until a seek puts it back, and `scoredJoin` then.
|
|
21
|
+
if (held.has(score) || mix.dropped)
|
|
22
|
+
return;
|
|
23
|
+
held.add(score);
|
|
24
|
+
const by = mix.transport.holders;
|
|
25
|
+
const m = mix;
|
|
26
|
+
const list = by.get(score);
|
|
27
|
+
if (list === undefined) {
|
|
28
|
+
by.set(score, [m]);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const i = list.findIndex((o) => o.slot > m.slot);
|
|
32
|
+
if (i < 0)
|
|
33
|
+
list.push(m);
|
|
34
|
+
else
|
|
35
|
+
list.splice(i, 0, m);
|
|
36
|
+
}
|
|
37
|
+
/** The mixes on `mix`'s transport that may hold a voice on `score`; the list is the transport's own. */
|
|
38
|
+
export function holders(mix, score) {
|
|
39
|
+
return (mix.transport.holders.get(score) ?? none);
|
|
40
|
+
}
|
|
41
|
+
/** Takes `mix` off `score`'s list, having found it holds nothing there. */
|
|
42
|
+
export function unhold(mix, score) {
|
|
43
|
+
mix.scored.held.delete(score);
|
|
44
|
+
const by = mix.transport.holders;
|
|
45
|
+
const list = by.get(score);
|
|
46
|
+
if (list === undefined)
|
|
47
|
+
return;
|
|
48
|
+
const i = list.indexOf(mix);
|
|
49
|
+
if (i >= 0)
|
|
50
|
+
list.splice(i, 1);
|
|
51
|
+
if (list.length === 0)
|
|
52
|
+
by.delete(score);
|
|
53
|
+
}
|
|
54
|
+
/** A mix joining its transport, with whatever voices it already has. */
|
|
55
|
+
export function scoredJoin(mix) {
|
|
56
|
+
for (const v of mix.cued)
|
|
57
|
+
if (v.spec.score !== undefined)
|
|
58
|
+
hold(mix, v.spec.score);
|
|
59
|
+
for (const v of mix.gone)
|
|
60
|
+
if (v.spec.score !== undefined)
|
|
61
|
+
hold(mix, v.spec.score);
|
|
62
|
+
}
|
|
63
|
+
/** A mix leaving its transport. */
|
|
64
|
+
export function scoredDrop(mix) {
|
|
65
|
+
for (const score of [...mix.scored.held])
|
|
66
|
+
unhold(mix, score);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A voice on its score was cued, retimed, retired or left: what was read of the score is stale. A
|
|
70
|
+
* host can still retime a gone voice through a handle it kept, so `retimed` on a done voice
|
|
71
|
+
* replaces its score's gone list too, which starts every read of it over.
|
|
72
|
+
*/
|
|
73
|
+
export function scoreTouched(mix, voice, retimed = false) {
|
|
74
|
+
const score = voice.spec.score;
|
|
75
|
+
if (score === undefined)
|
|
76
|
+
return;
|
|
77
|
+
const ix = mix.scored;
|
|
78
|
+
ix.versions.set(score, (ix.versions.get(score) ?? 0) + 1);
|
|
79
|
+
if (!retimed || voice.state !== 'done')
|
|
80
|
+
return;
|
|
81
|
+
const gone = ix.goneBy.get(score);
|
|
82
|
+
if (gone !== undefined)
|
|
83
|
+
ix.goneBy.set(score, [...gone]);
|
|
84
|
+
}
|
|
85
|
+
/** How many times `score` has been touched in `mix`. */
|
|
86
|
+
export function scoreVersion(mix, score) {
|
|
87
|
+
return mix.scored.versions.get(score) ?? 0;
|
|
88
|
+
}
|
|
89
|
+
function put(by, voice) {
|
|
90
|
+
const score = voice.spec.score;
|
|
91
|
+
if (score === undefined)
|
|
92
|
+
return;
|
|
93
|
+
const set = by.get(score);
|
|
94
|
+
if (set === undefined)
|
|
95
|
+
by.set(score, new Set([voice]));
|
|
96
|
+
else
|
|
97
|
+
set.add(voice);
|
|
98
|
+
}
|
|
99
|
+
/** The voices of `mix` cued on `score` and not yet gone, in no order. */
|
|
100
|
+
export function scored(mix, score) {
|
|
101
|
+
const ix = mix.scored;
|
|
102
|
+
if (ix.cued !== mix.cued) {
|
|
103
|
+
ix.by = new Map();
|
|
104
|
+
for (const v of mix.cued)
|
|
105
|
+
put(ix.by, v);
|
|
106
|
+
ix.cued = mix.cued;
|
|
107
|
+
}
|
|
108
|
+
return ix.by.get(score) ?? noVoices;
|
|
109
|
+
}
|
|
110
|
+
const noVoices = new Set();
|
|
111
|
+
/** The voices of `mix` cued on `score` that have gone, in the order they left. */
|
|
112
|
+
export function scoredGone(mix, score) {
|
|
113
|
+
const ix = mix.scored;
|
|
114
|
+
if (ix.gone !== mix.gone) {
|
|
115
|
+
ix.goneBy = new Map();
|
|
116
|
+
for (const v of mix.gone)
|
|
117
|
+
goneAdd(ix.goneBy, v);
|
|
118
|
+
ix.gone = mix.gone;
|
|
119
|
+
}
|
|
120
|
+
return ix.goneBy.get(score) ?? none;
|
|
121
|
+
}
|
|
122
|
+
const none = [];
|
|
123
|
+
function goneAdd(by, voice) {
|
|
124
|
+
const score = voice.spec.score;
|
|
125
|
+
if (score === undefined)
|
|
126
|
+
return;
|
|
127
|
+
const list = by.get(score);
|
|
128
|
+
if (list === undefined)
|
|
129
|
+
by.set(score, [voice]);
|
|
130
|
+
else
|
|
131
|
+
list.push(voice);
|
|
132
|
+
}
|
|
133
|
+
/** A voice just pushed onto `gone`. */
|
|
134
|
+
export function scoredLeft(mix, voice) {
|
|
135
|
+
if (voice.spec.score !== undefined)
|
|
136
|
+
hold(mix, voice.spec.score);
|
|
137
|
+
const ix = mix.scored;
|
|
138
|
+
if (ix.gone === mix.gone)
|
|
139
|
+
goneAdd(ix.goneBy, voice);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Gone voices history let go of, out of `was`; `mix.gone` is already what is left, whether that
|
|
143
|
+
* is `was` cut in place or a new list.
|
|
144
|
+
*/
|
|
145
|
+
export function scoredCut(mix, was, out) {
|
|
146
|
+
const ix = mix.scored;
|
|
147
|
+
if (ix.gone !== was)
|
|
148
|
+
return;
|
|
149
|
+
ix.gone = mix.gone;
|
|
150
|
+
const by = new Map();
|
|
151
|
+
for (const v of out) {
|
|
152
|
+
const score = v.spec.score;
|
|
153
|
+
if (score === undefined)
|
|
154
|
+
continue;
|
|
155
|
+
const set = by.get(score);
|
|
156
|
+
if (set === undefined)
|
|
157
|
+
by.set(score, new Set([v]));
|
|
158
|
+
else
|
|
159
|
+
set.add(v);
|
|
160
|
+
}
|
|
161
|
+
for (const [score, gone] of by) {
|
|
162
|
+
const left = (ix.goneBy.get(score) ?? []).filter((v) => !gone.has(v));
|
|
163
|
+
if (left.length === 0)
|
|
164
|
+
ix.goneBy.delete(score);
|
|
165
|
+
else
|
|
166
|
+
ix.goneBy.set(score, left);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/** A voice just cued. */
|
|
170
|
+
export function scoredAdd(mix, voice) {
|
|
171
|
+
scoreTouched(mix, voice);
|
|
172
|
+
if (voice.spec.score !== undefined)
|
|
173
|
+
hold(mix, voice.spec.score);
|
|
174
|
+
const ix = mix.scored;
|
|
175
|
+
if (ix.cued === mix.cued)
|
|
176
|
+
put(ix.by, voice);
|
|
177
|
+
}
|
|
178
|
+
/** Voices no longer cued. */
|
|
179
|
+
export function scoredRemove(mix, voices) {
|
|
180
|
+
for (const v of voices)
|
|
181
|
+
scoreTouched(mix, v);
|
|
182
|
+
const ix = mix.scored;
|
|
183
|
+
if (ix.cued !== mix.cued)
|
|
184
|
+
return;
|
|
185
|
+
for (const v of voices) {
|
|
186
|
+
const score = v.spec.score;
|
|
187
|
+
if (score === undefined)
|
|
188
|
+
continue;
|
|
189
|
+
const set = ix.by.get(score);
|
|
190
|
+
if (set === undefined)
|
|
191
|
+
continue;
|
|
192
|
+
set.delete(v);
|
|
193
|
+
if (set.size === 0)
|
|
194
|
+
ix.by.delete(score);
|
|
195
|
+
}
|
|
196
|
+
}
|
package/dist/seek.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { index } from './chain.js';
|
|
2
1
|
import { schedule } from './due.js';
|
|
3
|
-
import { copyHeld, last,
|
|
2
|
+
import { copyHeld, last, lastWithin } from './history.js';
|
|
4
3
|
import { laneHost } from './hosts.js';
|
|
5
4
|
import { Lanes } from './lanes.js';
|
|
6
|
-
import {
|
|
5
|
+
import { Leavings } from './leavings.js';
|
|
6
|
+
import { move, nextFrame } from './move.js';
|
|
7
7
|
import { ownerReading } from './owner.js';
|
|
8
|
+
import { place } from './place.js';
|
|
9
|
+
import { unpull } from './pull.js';
|
|
10
|
+
import { Restored } from './restored.js';
|
|
11
|
+
import { recount } from './roster.js';
|
|
8
12
|
import { refitAll } from './spans.js';
|
|
9
13
|
import { Store } from './store.js';
|
|
10
14
|
import { hostAt, replay } from './tape.js';
|
|
@@ -35,44 +39,6 @@ function restore(voice, live, t) {
|
|
|
35
39
|
h.inputs = inputs;
|
|
36
40
|
return h;
|
|
37
41
|
}
|
|
38
|
-
/**
|
|
39
|
-
* A voice's records after a seek back: each subject's is put back from the records it had, the first
|
|
40
|
-
* time it is asked for, since the subjects a store holds cannot be listed.
|
|
41
|
-
*/
|
|
42
|
-
class Restored extends Store {
|
|
43
|
-
was;
|
|
44
|
-
make;
|
|
45
|
-
left;
|
|
46
|
-
constructor(was, make, left) {
|
|
47
|
-
super();
|
|
48
|
-
this.was = was;
|
|
49
|
-
this.make = make;
|
|
50
|
-
this.left = left;
|
|
51
|
-
}
|
|
52
|
-
/** Subjects whose record has been put back or deleted, which `left` no longer answers for. */
|
|
53
|
-
settled = new Store();
|
|
54
|
-
get(key) {
|
|
55
|
-
const v = super.get(key);
|
|
56
|
-
if (v !== undefined)
|
|
57
|
-
return v;
|
|
58
|
-
// A subject that left after the moment sought had the record it left with, whatever it has now.
|
|
59
|
-
const kept = this.settled.get(key) === undefined ? this.left(key) : undefined;
|
|
60
|
-
this.settled.set(key, true);
|
|
61
|
-
const live = kept ?? this.was.get(key);
|
|
62
|
-
if (live === undefined)
|
|
63
|
-
return undefined;
|
|
64
|
-
this.was.delete(key);
|
|
65
|
-
const made = this.make(live);
|
|
66
|
-
if (made !== undefined)
|
|
67
|
-
super.set(key, made);
|
|
68
|
-
return made;
|
|
69
|
-
}
|
|
70
|
-
delete(key) {
|
|
71
|
-
super.delete(key);
|
|
72
|
-
this.was.delete(key);
|
|
73
|
-
this.settled.set(key, true);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
42
|
/** Takes a voice cued after the moment sought out of the mix, until the tape cues it again. */
|
|
77
43
|
function park(mix, voice) {
|
|
78
44
|
const owner = voice.owner;
|
|
@@ -86,34 +52,27 @@ function park(mix, voice) {
|
|
|
86
52
|
motion.owner = null;
|
|
87
53
|
voice.park();
|
|
88
54
|
}
|
|
89
|
-
/** Puts a voice's controls and records back as they stood at mix time `t`. */
|
|
90
|
-
function restoreVoice(voice, t) {
|
|
55
|
+
/** Puts a voice's controls and records back as they stood at mix time `t`, the end of frame `seq`. */
|
|
56
|
+
function restoreVoice(mix, voice, t, seq) {
|
|
57
|
+
const cut = { seq, strict: false };
|
|
91
58
|
const log = voice.log;
|
|
92
|
-
const c =
|
|
59
|
+
const c = lastWithin(log, cut) ?? log[0];
|
|
93
60
|
log.length = log.indexOf(c) + 1;
|
|
94
|
-
voice.
|
|
95
|
-
voice.
|
|
96
|
-
voice.rate = c.rate;
|
|
97
|
-
voice.ramp = c.ramp;
|
|
98
|
-
voice.weight = c.weight;
|
|
99
|
-
voice.out = c.out;
|
|
100
|
-
voice.start = c.start;
|
|
101
|
-
voice.outAt = c.outAt;
|
|
102
|
-
voice.outOver = c.outOver;
|
|
103
|
-
voice.outSet = c.outSet;
|
|
104
|
-
if (voice.reopen(t) && voice.owner?.holding)
|
|
61
|
+
voice.take(c);
|
|
62
|
+
if (voice.reopen(seq) && voice.owner?.holding)
|
|
105
63
|
voice.owner.holding.played--;
|
|
106
64
|
voice.doneAt = Number.POSITIVE_INFINITY;
|
|
65
|
+
voice.doneSeq = Number.POSITIVE_INFINITY;
|
|
107
66
|
if (voice.parts !== null) {
|
|
108
67
|
for (const [subject, r] of voice.parts)
|
|
109
|
-
if (r.
|
|
68
|
+
if (r.seq > seq)
|
|
110
69
|
voice.parts.delete(subject);
|
|
111
70
|
if (voice.parts.size === 0)
|
|
112
71
|
voice.parts = null;
|
|
113
72
|
}
|
|
114
73
|
if (voice.parted !== null) {
|
|
115
|
-
for (const [subject,
|
|
116
|
-
if (
|
|
74
|
+
for (const [subject, p] of voice.parted)
|
|
75
|
+
if (p.seq > seq)
|
|
117
76
|
voice.parted.delete(subject);
|
|
118
77
|
if (voice.parted.size === 0)
|
|
119
78
|
voice.parted = null;
|
|
@@ -127,30 +86,43 @@ function restoreVoice(voice, t) {
|
|
|
127
86
|
voice.unreached = null;
|
|
128
87
|
voice.unreachedBits = null;
|
|
129
88
|
voice.seeks++;
|
|
130
|
-
const left = voice.left;
|
|
131
|
-
voice.left = left
|
|
132
|
-
const future = left
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
89
|
+
const left = voice.left?.all() ?? [];
|
|
90
|
+
voice.left = Leavings.from(left.filter((e) => e.seq <= seq));
|
|
91
|
+
const future = Leavings.from(left.filter((e) => e.seq > seq));
|
|
92
|
+
let records = voice.subjects;
|
|
93
|
+
if (!(records instanceof Restored)) {
|
|
94
|
+
records = new Restored(records, (live, least, top) => {
|
|
95
|
+
const h = restore(voice, live, least);
|
|
96
|
+
if (h === undefined)
|
|
97
|
+
return h;
|
|
98
|
+
if (h.reaches)
|
|
99
|
+
voice.seen++;
|
|
100
|
+
if (h.rested)
|
|
101
|
+
voice.restedCount++;
|
|
102
|
+
if (!(h.since <= least)) {
|
|
103
|
+
// Kept while its origin was still ahead: where the controls put back place it now.
|
|
104
|
+
h.since = mix.sinceOf(voice, h.delay);
|
|
105
|
+
h.shown = mix.shownOf(voice, h.since);
|
|
106
|
+
if (h.ticks === 0 && h.stepped > h.since)
|
|
107
|
+
h.stepped = h.since;
|
|
108
|
+
}
|
|
109
|
+
if (!(h.since <= top)) {
|
|
110
|
+
voice.early ??= [];
|
|
111
|
+
voice.early.push(h);
|
|
112
|
+
}
|
|
136
113
|
return h;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (voice.state === 'pending') {
|
|
142
|
-
voice.early ??= [];
|
|
143
|
-
voice.early.push(h);
|
|
144
|
-
}
|
|
145
|
-
return h;
|
|
146
|
-
}, (key) => leftAt(future, key, t));
|
|
114
|
+
});
|
|
115
|
+
voice.subjects = records;
|
|
116
|
+
}
|
|
117
|
+
records.back(t, cut, future);
|
|
147
118
|
}
|
|
148
119
|
/** Each voice's state at mix time `t`, once every clock above it is back where it was. */
|
|
149
120
|
function stateAt(voice, t) {
|
|
121
|
+
// A fade begins at once, even one whose ramp starts later, at the voice's start.
|
|
122
|
+
if (voice.out !== null)
|
|
123
|
+
return 'fading';
|
|
150
124
|
if ((voice.owner === null ? t : ownerReading(voice.owner, t)) < voice.start)
|
|
151
125
|
return 'pending';
|
|
152
|
-
if (voice.out !== null && voice.out.at <= t)
|
|
153
|
-
return 'fading';
|
|
154
126
|
if (voice.freezesAfter && voice.elapsedAt(t) >= voice.span + voice.latest)
|
|
155
127
|
return 'frozen';
|
|
156
128
|
return 'live';
|
|
@@ -172,34 +144,43 @@ export function seek(transport, t) {
|
|
|
172
144
|
if (!(t >= transport.born))
|
|
173
145
|
throw new Error(`blits: ${t} is older than this mix's history reaches`);
|
|
174
146
|
const unpaged = cover(transport, t);
|
|
175
|
-
if (t >= transport.now)
|
|
176
|
-
replay(transport, () => t);
|
|
177
|
-
// Under the rate the recorded calls up to `t` set, ahead; behind, under the rate it had then.
|
|
178
|
-
const u = hostAt(transport, t);
|
|
179
|
-
if (!Number.isFinite(u))
|
|
180
|
-
throw new RangeError(`blits: the mix's rate never reaches ${t}`);
|
|
181
147
|
if (t >= transport.now) {
|
|
148
|
+
// Ahead, under the rate the recorded calls up to `t` set, past any frames rate 0 held at `t`.
|
|
149
|
+
const last = replay(transport, (at) => (transport.pace === null ? at : transport.pace.reading(at)) <= t);
|
|
150
|
+
const u = Number.isNaN(last) ? hostAt(transport, t) : Math.max(last, hostAt(transport, t));
|
|
151
|
+
if (!Number.isFinite(u))
|
|
152
|
+
throw new RangeError(`blits: the mix's rate never reaches ${t}`);
|
|
182
153
|
transport.u = u;
|
|
183
154
|
for (const m of transport.members)
|
|
184
155
|
move(m, t);
|
|
185
156
|
}
|
|
186
157
|
else {
|
|
187
|
-
const
|
|
188
|
-
if (
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
158
|
+
const frame = transport.frameAt(t);
|
|
159
|
+
if (frame === undefined)
|
|
160
|
+
throw new Error(`blits: ${t} is older than this mix's history reaches`);
|
|
161
|
+
// On the frame itself, its own host time: rate 0 may have held the clock there for several.
|
|
162
|
+
const u = frame.at === t ? frame.u : hostAt(transport, t);
|
|
163
|
+
const seq = frame.seq;
|
|
164
|
+
tape.goto(tape.depthAt(u));
|
|
165
|
+
transport.undrop(seq);
|
|
166
|
+
// What history kept after the frame is let go; the tape holds the calls that made it.
|
|
167
|
+
transport.announced = transport.announced.filter((a) => a.seq <= seq);
|
|
168
|
+
transport.frames = transport.frames.filter((f) => f.seq <= seq);
|
|
169
|
+
if (transport.pacedSeq > seq) {
|
|
170
|
+
transport.pace = null;
|
|
171
|
+
transport.pacedSeq = Number.POSITIVE_INFINITY;
|
|
172
|
+
}
|
|
173
|
+
else
|
|
174
|
+
transport.pace?.cut(u);
|
|
194
175
|
transport.u = u;
|
|
195
176
|
transport.now = t;
|
|
196
177
|
for (const m of transport.members)
|
|
197
|
-
back(m, t);
|
|
198
|
-
cut(transport,
|
|
178
|
+
back(m, t, seq, frame.at < t);
|
|
179
|
+
cut(transport, seq, unpaged);
|
|
199
180
|
}
|
|
181
|
+
transport.tick(t);
|
|
200
182
|
// The host's clock reads on from here: its next sync reads `t` plus its time since its last.
|
|
201
|
-
transport.offset = transport.last - u;
|
|
202
|
-
transport.u = u;
|
|
183
|
+
transport.offset = transport.last - transport.u;
|
|
203
184
|
transport.kept();
|
|
204
185
|
for (const m of transport.members) {
|
|
205
186
|
m.stir();
|
|
@@ -209,8 +190,11 @@ export function seek(transport, t) {
|
|
|
209
190
|
b.sought();
|
|
210
191
|
}
|
|
211
192
|
}
|
|
212
|
-
/**
|
|
213
|
-
|
|
193
|
+
/**
|
|
194
|
+
* Puts one mix back as it stood at mix time `t`, the end of frame `seq`, its transport already
|
|
195
|
+
* there; `on` past that frame's own time, where it moves on to `t`.
|
|
196
|
+
*/
|
|
197
|
+
function back(mix, t, seq, on) {
|
|
214
198
|
const all = [...mix.cued, ...mix.gone].sort((a, b) => a.id - b.id);
|
|
215
199
|
const kept = [];
|
|
216
200
|
const gone = [];
|
|
@@ -220,14 +204,14 @@ function back(mix, t) {
|
|
|
220
204
|
if (voice.motion !== undefined)
|
|
221
205
|
motions.add(voice.motion);
|
|
222
206
|
for (const voice of all)
|
|
223
|
-
if (voice.
|
|
207
|
+
if (voice.cuedSeq > seq)
|
|
224
208
|
park(mix, voice);
|
|
225
|
-
else if (voice.
|
|
209
|
+
else if (voice.doneSeq <= seq)
|
|
226
210
|
gone.push(voice);
|
|
227
211
|
else
|
|
228
212
|
kept.push(voice);
|
|
229
213
|
for (const voice of kept)
|
|
230
|
-
restoreVoice(voice, t);
|
|
214
|
+
restoreVoice(mix, voice, t, seq);
|
|
231
215
|
for (const voice of kept)
|
|
232
216
|
voice.state = stateAt(voice, t);
|
|
233
217
|
mix.cued = kept;
|
|
@@ -242,28 +226,17 @@ function back(mix, t) {
|
|
|
242
226
|
if (oldLanes !== null)
|
|
243
227
|
mix.lanes = new Lanes(laneHost(mix));
|
|
244
228
|
mix.chains = new Store();
|
|
245
|
-
mix.named = new Store();
|
|
246
|
-
mix.naming = 0;
|
|
247
|
-
mix.general = [];
|
|
248
|
-
for (const voice of kept)
|
|
249
|
-
index(mix, voice);
|
|
250
229
|
mix.owners = kept.some((v) => v.holding !== null) ? kept.filter((v) => v.holding !== null) : null;
|
|
251
|
-
mix
|
|
252
|
-
mix.anchored = kept.filter((v) => v.spec.anchor !== undefined).length;
|
|
230
|
+
recount(mix);
|
|
253
231
|
mix.parters.clear();
|
|
254
232
|
for (const voice of [...kept, ...gone])
|
|
255
233
|
if (voice.parts !== null || voice.parted !== null)
|
|
256
234
|
mix.parters.add(voice);
|
|
257
|
-
mix.steps.push(null, ++mix.version);
|
|
258
235
|
mix.relinks++;
|
|
259
236
|
mix.pose = new Store();
|
|
260
237
|
if (mix.restStamps !== null)
|
|
261
238
|
mix.restStamps = new Store();
|
|
262
|
-
mix
|
|
263
|
-
mix.pulledHeads = [];
|
|
264
|
-
mix.pulledSlots = new Int32Array(0);
|
|
265
|
-
mix.pulledVersion = Number.NaN;
|
|
266
|
-
mix.pulledRelinks = -1;
|
|
239
|
+
unpull(mix);
|
|
267
240
|
for (const voice of [...kept, ...gone]) {
|
|
268
241
|
voice.unreached = null;
|
|
269
242
|
voice.unreachedBits = null;
|
|
@@ -290,10 +263,10 @@ function back(mix, t) {
|
|
|
290
263
|
for (const motion of motions) {
|
|
291
264
|
if (oldLanes !== null && motion.watcher === oldLanes)
|
|
292
265
|
motion.watcher = null;
|
|
293
|
-
motion.rewind(
|
|
266
|
+
motion.rewind(seq);
|
|
294
267
|
}
|
|
295
268
|
// What history kept after `t` is let go; the tape holds the calls that made it.
|
|
296
|
-
mix.hostLog = mix.hostLog.filter((e) => e.
|
|
269
|
+
mix.hostLog = mix.hostLog.filter((e) => e.seq <= seq);
|
|
297
270
|
mix.sent = mix.sent.filter((e) => e.timestamp <= t);
|
|
298
271
|
mix.sentTo = t;
|
|
299
272
|
mix.pins = null;
|
|
@@ -307,5 +280,15 @@ function back(mix, t) {
|
|
|
307
280
|
for (const voice of kept)
|
|
308
281
|
schedule(mix, voice);
|
|
309
282
|
mix.stir();
|
|
310
|
-
move
|
|
283
|
+
// On the frame's own time its move is behind it: a second would land what the next frame does.
|
|
284
|
+
if (on)
|
|
285
|
+
move(mix, t);
|
|
286
|
+
else {
|
|
287
|
+
mix.frame = nextFrame();
|
|
288
|
+
mix.reducedNow = mix.reduced;
|
|
289
|
+
// A voice history let go of may have placed an anchor then: placed again from what it holds.
|
|
290
|
+
const ms = mix.opts.history.ms;
|
|
291
|
+
if (mix.anchored > 0 && mix.transport.forgotTo >= t - ms)
|
|
292
|
+
place(mix);
|
|
293
|
+
}
|
|
311
294
|
}
|
package/dist/shared.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A WeakMap every copy of blits loaded in one realm shares, so a fact one copy records about a
|
|
3
|
+
* patch or channel it made is read by another copy's mix. The name carries a version, bumped when
|
|
4
|
+
* what is stored changes shape, so copies that disagree on the shape keep apart.
|
|
5
|
+
*/
|
|
6
|
+
export declare function shared<K extends object, V>(name: string): WeakMap<K, V>;
|
package/dist/shared.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A WeakMap every copy of blits loaded in one realm shares, so a fact one copy records about a
|
|
3
|
+
* patch or channel it made is read by another copy's mix. The name carries a version, bumped when
|
|
4
|
+
* what is stored changes shape, so copies that disagree on the shape keep apart.
|
|
5
|
+
*/
|
|
6
|
+
export function shared(name) {
|
|
7
|
+
const key = Symbol.for(`@msb235/blits/${name}`);
|
|
8
|
+
const g = globalThis;
|
|
9
|
+
let map = g[key];
|
|
10
|
+
if (map === undefined) {
|
|
11
|
+
map = new WeakMap();
|
|
12
|
+
g[key] = map;
|
|
13
|
+
}
|
|
14
|
+
return map;
|
|
15
|
+
}
|
package/dist/signals.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { Signal } from './types.js';
|
|
1
|
+
import type { Setting, Signal } from './types.js';
|
|
2
|
+
/** Whether a change to an input signal reaches `reading.inputs`, so a lane fill can see it. */
|
|
3
|
+
export declare const heard: (signal: object) => boolean;
|
|
2
4
|
/**
|
|
3
5
|
* The loudest of several signals.
|
|
4
6
|
*
|
|
@@ -6,7 +8,26 @@ import type { Signal } from './types.js';
|
|
|
6
8
|
*/
|
|
7
9
|
export declare function peak<I, H = unknown>(...signals: readonly Signal<I, H>[]): Signal<I, H>;
|
|
8
10
|
/**
|
|
9
|
-
*
|
|
11
|
+
* Marks `read` as a signal driven from outside the clock, which a host's own signal has to be for
|
|
12
|
+
* `assess` and a history's `inputs` to treat it as one. Two uses:
|
|
13
|
+
*
|
|
14
|
+
* `input(read)` is an input of the host's own: a pointer, a volume, a value in a store. The host
|
|
15
|
+
* calls `touch()` after each change to what `read` reads, and a voice weighted by the signal, or by
|
|
16
|
+
* `peak`, `slew`, `lag` or `gate` over it, keeps its lane. A change nobody reports shows once the
|
|
17
|
+
* mix's clock next moves.
|
|
18
|
+
*
|
|
19
|
+
* `input(read, of)` is a signal built on others, as a wrapper that clamps one is: `of` lists every
|
|
20
|
+
* signal `read` calls, and the result is an input where any of them is, and reports its changes
|
|
21
|
+
* where they all do.
|
|
22
|
+
*
|
|
23
|
+
* @category signal
|
|
24
|
+
*/
|
|
25
|
+
export declare function input<I, H = unknown>(read: (subject: I, setting: Setting<void, H>) => number, of?: readonly Signal<I, H>[]): Signal<I, H> & {
|
|
26
|
+
touch(): void;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* A signal the host writes: `input` over one number, with `set` reporting each change. klieg's
|
|
30
|
+
* `level`.
|
|
10
31
|
*
|
|
11
32
|
* @category signal
|
|
12
33
|
*/
|
|
@@ -14,7 +35,7 @@ export declare function level<I, H = unknown>(initial?: number): Signal<I, H> &
|
|
|
14
35
|
set(v: number): void;
|
|
15
36
|
};
|
|
16
37
|
/**
|
|
17
|
-
* The package's
|
|
38
|
+
* The package's linear decay primitive: follows its input at a rate limit, no faster than `riseMs` per
|
|
18
39
|
* unit climbing or `fallMs` draining. The mix keeps its state per voice and subject, so two voices
|
|
19
40
|
* handed the same slew each follow on their own. A subject starts on its input, or at `from`
|
|
20
41
|
* unless `dt` is infinite.
|