@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/patch.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { lerpInto } from './channels.js';
|
|
2
|
-
import { curve } from './easing.js';
|
|
1
|
+
import { lerpInto, lerpNumber } from './channels.js';
|
|
2
|
+
import { curve, startSlope } from './easing.js';
|
|
3
3
|
/**
|
|
4
4
|
* The procedural form. Flicker, roving, a hash walk, a starter strike.
|
|
5
5
|
*
|
|
@@ -12,7 +12,6 @@ export function patch(duration, at, opts) {
|
|
|
12
12
|
return {
|
|
13
13
|
form: 'fn',
|
|
14
14
|
duration,
|
|
15
|
-
period: duration,
|
|
16
15
|
writes,
|
|
17
16
|
kit: opts.kit,
|
|
18
17
|
reads: opts.reads,
|
|
@@ -23,11 +22,6 @@ export function patch(duration, at, opts) {
|
|
|
23
22
|
unpack: opts.unpack,
|
|
24
23
|
};
|
|
25
24
|
}
|
|
26
|
-
const options = new WeakMap();
|
|
27
|
-
/** The options a `keys` patch was built with, for an engine reading its stops rather than calling it. */
|
|
28
|
-
export function keysOptionsOf(p) {
|
|
29
|
-
return options.get(p);
|
|
30
|
-
}
|
|
31
25
|
const linear = (a, b, u) => a + (b - a) * u;
|
|
32
26
|
function interpolate(a, b, u) {
|
|
33
27
|
if (typeof a === 'number' && typeof b === 'number')
|
|
@@ -64,6 +58,10 @@ function build(stops, writes, duration, opts) {
|
|
|
64
58
|
}
|
|
65
59
|
all.sort((x, y) => x.at - y.at);
|
|
66
60
|
const tail = all.filter((pt) => pt.at !== 0);
|
|
61
|
+
const lerp = opts.lerpBy?.(channel) ?? opts.kit?.[channel]?.lerp;
|
|
62
|
+
const plain = all.length > 0 &&
|
|
63
|
+
(lerp === undefined || lerp === lerpNumber) &&
|
|
64
|
+
all.every((pt) => typeof pt.value === 'number');
|
|
67
65
|
tracks.push({
|
|
68
66
|
channel: channel,
|
|
69
67
|
all,
|
|
@@ -71,7 +69,14 @@ function build(stops, writes, duration, opts) {
|
|
|
71
69
|
ats: all.map((pt) => pt.at),
|
|
72
70
|
tailAts: null,
|
|
73
71
|
delay: opts.delayBy?.(channel) ?? 0,
|
|
74
|
-
lerp
|
|
72
|
+
lerp,
|
|
73
|
+
numbers: plain
|
|
74
|
+
? {
|
|
75
|
+
ats: Float64Array.from(all, (pt) => pt.at),
|
|
76
|
+
values: Float64Array.from(all, (pt) => pt.value),
|
|
77
|
+
eases: all.map((pt) => pt.ease),
|
|
78
|
+
}
|
|
79
|
+
: null,
|
|
75
80
|
});
|
|
76
81
|
}
|
|
77
82
|
return { tracks, duration };
|
|
@@ -100,12 +105,17 @@ export function shifted(track, phase, duration) {
|
|
|
100
105
|
}
|
|
101
106
|
/** A phase once a channel's delay of `delay` ms in a duration of `duration` applies. */
|
|
102
107
|
export function shift(delay, phase, duration) {
|
|
103
|
-
|
|
108
|
+
if (delay === 0 || duration === 0)
|
|
109
|
+
return phase;
|
|
110
|
+
// The channel travels in what is left after its wait, so it lands with the rest at phase 1.
|
|
111
|
+
if (delay >= duration)
|
|
112
|
+
return phase >= 1 ? 1 : 0;
|
|
113
|
+
return Math.max(0, (phase * duration - delay) / (duration - delay));
|
|
104
114
|
}
|
|
105
115
|
/**
|
|
106
116
|
* Where `phase` falls among `n` stop phases `ats[off + k * stride]`, in order: -2 for no stops, -1
|
|
107
117
|
* at or before the first, `n` at or past the last, and otherwise the first stop past it, where the
|
|
108
|
-
* segment from the one before ends.
|
|
118
|
+
* segment from the one before ends. `segment` and `readNumber` make the same search.
|
|
109
119
|
*/
|
|
110
120
|
export function locate(ats, off, stride, n, phase) {
|
|
111
121
|
if (n === 0)
|
|
@@ -132,8 +142,8 @@ export function fraction(phase, a, b) {
|
|
|
132
142
|
return (phase - a) / (b - a);
|
|
133
143
|
}
|
|
134
144
|
/**
|
|
135
|
-
* Finds where one track is at a phase, into `seg`: the
|
|
136
|
-
*
|
|
145
|
+
* Finds where one track is at a phase, into `seg`: the search `read` and a lane folding stops
|
|
146
|
+
* straight into a subject's values both make. `base` stands in for the value at
|
|
137
147
|
* phase 0, which is how `from: 'current'` starts a voice wherever the subject already is.
|
|
138
148
|
*/
|
|
139
149
|
export function segment(track, phase, base) {
|
|
@@ -179,10 +189,44 @@ export function segment(track, phase, base) {
|
|
|
179
189
|
seg.ease = b.ease;
|
|
180
190
|
return BETWEEN;
|
|
181
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* A plain-number track at a phase, in exactly `segment`'s and `read`'s arithmetic: the stop at or
|
|
194
|
+
* past the phase by binary search, the fraction before easing, then a straight lerp.
|
|
195
|
+
*/
|
|
196
|
+
function readNumber(t, phase) {
|
|
197
|
+
const ats = t.ats;
|
|
198
|
+
const values = t.values;
|
|
199
|
+
const n = ats.length;
|
|
200
|
+
if (phase <= ats[0])
|
|
201
|
+
return values[0];
|
|
202
|
+
if (phase >= ats[n - 1])
|
|
203
|
+
return values[n - 1];
|
|
204
|
+
let lo = 1;
|
|
205
|
+
let hi = n - 1;
|
|
206
|
+
while (lo < hi) {
|
|
207
|
+
const mid = (lo + hi) >> 1;
|
|
208
|
+
if (ats[mid] >= phase)
|
|
209
|
+
hi = mid;
|
|
210
|
+
else
|
|
211
|
+
lo = mid + 1;
|
|
212
|
+
}
|
|
213
|
+
const a = ats[lo - 1];
|
|
214
|
+
const u = (phase - a) / (ats[lo] - a);
|
|
215
|
+
const ease = t.eases[lo];
|
|
216
|
+
const eased = ease ? ease(u) : u;
|
|
217
|
+
const from = values[lo - 1];
|
|
218
|
+
return from + (values[lo] - from) * eased;
|
|
219
|
+
}
|
|
182
220
|
/** Reads one track at a phase; `base` as `segment` takes it. */
|
|
183
221
|
function read(track, phase, base, lerp,
|
|
184
222
|
/** Units per ms, for a retarget's first segment. */
|
|
185
223
|
slope, duration, into, reuse) {
|
|
224
|
+
const numbers = track.numbers;
|
|
225
|
+
if (numbers !== null &&
|
|
226
|
+
base === undefined &&
|
|
227
|
+
slope === undefined &&
|
|
228
|
+
(lerp === undefined || lerp === lerpNumber || track.lerp !== undefined))
|
|
229
|
+
return readNumber(numbers, phase);
|
|
186
230
|
const found = segment(track, phase, base);
|
|
187
231
|
if (found === NOTHING)
|
|
188
232
|
return undefined;
|
|
@@ -208,7 +252,9 @@ slope, duration, into, reuse) {
|
|
|
208
252
|
// add c·u(1−u)², which is 0 at both ends, flat at the end, and fixes the slope at the start.
|
|
209
253
|
if (slope === undefined || !first)
|
|
210
254
|
return value;
|
|
211
|
-
const e0 = ease ? ease
|
|
255
|
+
const e0 = ease ? startSlope(ease) : 1;
|
|
256
|
+
if (e0 === Number.POSITIVE_INFINITY)
|
|
257
|
+
return value;
|
|
212
258
|
const bend = (s, a, z) => (s * duration * len - (z - a) * e0) * u * (1 - u) * (1 - u);
|
|
213
259
|
if (typeof value === 'number' && typeof slope === 'number')
|
|
214
260
|
return value + bend(slope, aValue, bValue);
|
|
@@ -262,14 +308,15 @@ slopes, intos, scratch) {
|
|
|
262
308
|
}
|
|
263
309
|
return out;
|
|
264
310
|
}
|
|
265
|
-
/** A patch's `duration`, or the deprecated `period` where only that is set. */
|
|
266
|
-
export const durationOf = (p) => p.duration ?? p.period ?? 0;
|
|
267
311
|
const cache = new WeakMap();
|
|
268
|
-
/**
|
|
312
|
+
/**
|
|
313
|
+
* The built form of a patch's stops, made on first ask for a patch `keys()` did not build: a copy
|
|
314
|
+
* of one, or one another copy of blits made, each read by the fields it carries.
|
|
315
|
+
*/
|
|
269
316
|
export function builtOf(p) {
|
|
270
317
|
let held = cache.get(p);
|
|
271
318
|
if (held === undefined) {
|
|
272
|
-
held = build(p.keys, p.writes,
|
|
319
|
+
held = build(p.keys, p.writes, p.duration, p);
|
|
273
320
|
cache.set(p, held);
|
|
274
321
|
}
|
|
275
322
|
return held;
|
|
@@ -289,13 +336,28 @@ export function keys(duration, stops, opts = {}) {
|
|
|
289
336
|
const p = {
|
|
290
337
|
form: 'keys',
|
|
291
338
|
duration,
|
|
292
|
-
period: duration,
|
|
293
339
|
writes,
|
|
294
340
|
kit: opts.kit,
|
|
295
341
|
keys: stops,
|
|
296
|
-
|
|
342
|
+
...given(opts),
|
|
343
|
+
// A copy of the patch carries this `at` with fields of its own, which it is read by.
|
|
344
|
+
at(phase) {
|
|
345
|
+
return readKeys(this === p ? made : builtOf(this), phase, {});
|
|
346
|
+
},
|
|
297
347
|
};
|
|
298
|
-
options.set(p, opts);
|
|
299
348
|
cache.set(p, made);
|
|
300
349
|
return p;
|
|
301
350
|
}
|
|
351
|
+
/** The `keys` options a patch carries as fields, those given only: an absent one adds no key. */
|
|
352
|
+
function given(opts) {
|
|
353
|
+
const out = {};
|
|
354
|
+
if (opts.ease !== undefined)
|
|
355
|
+
out.ease = opts.ease;
|
|
356
|
+
if (opts.easeBy !== undefined)
|
|
357
|
+
out.easeBy = opts.easeBy;
|
|
358
|
+
if (opts.delayBy !== undefined)
|
|
359
|
+
out.delayBy = opts.delayBy;
|
|
360
|
+
if (opts.lerpBy !== undefined)
|
|
361
|
+
out.lerpBy = opts.lerpBy;
|
|
362
|
+
return out;
|
|
363
|
+
}
|
package/dist/place.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { departedMatches } from './departed.js';
|
|
2
|
+
import { reorigin } from './held.js';
|
|
1
3
|
import { noted } from './history.js';
|
|
2
4
|
import { markOf } from './marks.js';
|
|
3
5
|
import { mixTime, ownerReading } from './owner.js';
|
|
6
|
+
import { holders, scored, scoredGone, scoreVersion, unhold } from './scored.js';
|
|
4
7
|
import { none } from './voice.js';
|
|
5
8
|
/** What a voice's own owner's clock reads now: the mix clock for a voice no owner holds. */
|
|
6
9
|
export function localNow(mix, voice) {
|
|
@@ -36,6 +39,10 @@ function startAt(mix, voice, start) {
|
|
|
36
39
|
voice.start = start;
|
|
37
40
|
voice.anchorNow = start;
|
|
38
41
|
voice.anchorElapsed = 0;
|
|
42
|
+
voice.clocks = null;
|
|
43
|
+
// A clock moved outright, as a seek moves it: what a record computed at this mix time is stale.
|
|
44
|
+
voice.seeks++;
|
|
45
|
+
reorigin(mix, voice, mix.now);
|
|
39
46
|
noted(mix, voice);
|
|
40
47
|
return true;
|
|
41
48
|
}
|
|
@@ -178,7 +185,7 @@ function resolve(mix, a, self) {
|
|
|
178
185
|
mark = 'start';
|
|
179
186
|
by = -by;
|
|
180
187
|
}
|
|
181
|
-
const t = timeOf(mix,
|
|
188
|
+
const t = timeOf(mix, query, mark, self);
|
|
182
189
|
if (t === undefined)
|
|
183
190
|
return undefined;
|
|
184
191
|
if (o === null)
|
|
@@ -200,46 +207,197 @@ function member(mix, a, self) {
|
|
|
200
207
|
}
|
|
201
208
|
return t;
|
|
202
209
|
}
|
|
210
|
+
/** Picks among the times a query meets, as its resolver says, in the order they are met. */
|
|
211
|
+
class Pick {
|
|
212
|
+
resolver;
|
|
213
|
+
now;
|
|
214
|
+
any = false;
|
|
215
|
+
firstOrder = Number.POSITIVE_INFINITY;
|
|
216
|
+
lastOrder = Number.NEGATIVE_INFINITY;
|
|
217
|
+
first;
|
|
218
|
+
last;
|
|
219
|
+
pick;
|
|
220
|
+
constructor(resolver, now) {
|
|
221
|
+
this.resolver = resolver;
|
|
222
|
+
this.now = now;
|
|
223
|
+
}
|
|
224
|
+
take(order, t) {
|
|
225
|
+
this.any = true;
|
|
226
|
+
if (order < this.firstOrder) {
|
|
227
|
+
this.firstOrder = order;
|
|
228
|
+
this.first = t;
|
|
229
|
+
}
|
|
230
|
+
if (order > this.lastOrder) {
|
|
231
|
+
this.lastOrder = order;
|
|
232
|
+
this.last = t;
|
|
233
|
+
}
|
|
234
|
+
if (t !== undefined)
|
|
235
|
+
this.consider(t);
|
|
236
|
+
}
|
|
237
|
+
/** Takes what `o` picked as though its times had been met here. */
|
|
238
|
+
merge(o) {
|
|
239
|
+
if (!o.any)
|
|
240
|
+
return;
|
|
241
|
+
this.any = true;
|
|
242
|
+
if (o.firstOrder < this.firstOrder) {
|
|
243
|
+
this.firstOrder = o.firstOrder;
|
|
244
|
+
this.first = o.first;
|
|
245
|
+
}
|
|
246
|
+
if (o.lastOrder > this.lastOrder) {
|
|
247
|
+
this.lastOrder = o.lastOrder;
|
|
248
|
+
this.last = o.last;
|
|
249
|
+
}
|
|
250
|
+
if (o.pick !== undefined)
|
|
251
|
+
this.consider(o.pick);
|
|
252
|
+
}
|
|
253
|
+
consider(t) {
|
|
254
|
+
const p = this.pick;
|
|
255
|
+
if (this.resolver === 'earliest'
|
|
256
|
+
? p === undefined || t < p
|
|
257
|
+
: this.resolver === 'latest'
|
|
258
|
+
? p === undefined || t > p
|
|
259
|
+
: this.resolver === 'next' && t >= this.now && (p === undefined || t < p))
|
|
260
|
+
this.pick = t;
|
|
261
|
+
}
|
|
262
|
+
answer() {
|
|
263
|
+
if (!this.any)
|
|
264
|
+
return undefined;
|
|
265
|
+
if (this.resolver === 'first')
|
|
266
|
+
return this.first;
|
|
267
|
+
if (this.resolver === 'last')
|
|
268
|
+
return this.last;
|
|
269
|
+
return this.pick;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
203
272
|
/**
|
|
204
273
|
* The mix time a query answers: `mark` of the voice it picks, among those cued and those that have
|
|
205
274
|
* left, never the asker; or a mark the host announced on the score, which is the same time for
|
|
206
|
-
* any
|
|
275
|
+
* any mark. Undefined while what it picks has no such time. A query naming no score looks
|
|
207
276
|
* among the asker's siblings, the voices its owner holds.
|
|
208
277
|
*/
|
|
209
|
-
function timeOf(mix,
|
|
278
|
+
function timeOf(mix, key, mark, self) {
|
|
279
|
+
const q = typeof key === 'string' ? { name: key } : key;
|
|
210
280
|
const score = q.score ?? self.spec.score;
|
|
211
281
|
const anywhere = q.score !== undefined;
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
282
|
+
const resolver = q.resolver ?? 'last';
|
|
283
|
+
const now = Number.isNaN(mix.now) ? Number.NEGATIVE_INFINITY : mix.now;
|
|
284
|
+
const picked = new Pick(resolver, now);
|
|
285
|
+
const matches = (v) => v !== self &&
|
|
286
|
+
v.spec.score === score &&
|
|
287
|
+
(anywhere || v.owner === self.owner) &&
|
|
288
|
+
(q.name === undefined || v.spec.name === q.name) &&
|
|
289
|
+
(q.tag === undefined || (v.spec.tags ?? none).includes(q.tag)) &&
|
|
290
|
+
(q.writes === undefined || v.patch.writes.includes(q.writes));
|
|
291
|
+
// A query naming a score looks in every mix on the transport that holds a voice on it; one
|
|
292
|
+
// naming none, in its own.
|
|
293
|
+
const listed = anywhere && score !== undefined && !mix.projecting;
|
|
294
|
+
const mixes = !anywhere
|
|
295
|
+
? [mix]
|
|
296
|
+
: listed
|
|
297
|
+
? holders(mix, score)
|
|
298
|
+
: mix.transport.members;
|
|
299
|
+
// Voices forgotten on leaving answer only where no voice the mix still knows does.
|
|
300
|
+
const owner = self.owner?.id ?? -1;
|
|
301
|
+
const forgotten = new Pick(resolver, now);
|
|
302
|
+
for (let i = 0; i < mixes.length; i++) {
|
|
303
|
+
const m = mixes[i];
|
|
304
|
+
let departed = false;
|
|
305
|
+
for (const d of m.departed.all)
|
|
306
|
+
if (departedMatches(d, q, score, anywhere, owner)) {
|
|
307
|
+
forgotten.take(d.id, d.marks[mark]);
|
|
308
|
+
departed = true;
|
|
309
|
+
}
|
|
310
|
+
if (score === undefined || m.projecting || mix.projecting) {
|
|
311
|
+
for (const v of [...m.gone, ...m.cued])
|
|
312
|
+
if (matches(v))
|
|
313
|
+
picked.take(v.id, markOf(m, v, mark));
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
const cued = scored(m, score);
|
|
317
|
+
const list = scoredGone(m, score);
|
|
318
|
+
if (cued.size === 0 && list.length === 0 && !departed) {
|
|
319
|
+
if (listed) {
|
|
320
|
+
unhold(m, score);
|
|
321
|
+
i--;
|
|
322
|
+
}
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
// `next` asks what is still ahead of now, which a running pick cannot keep.
|
|
326
|
+
if (resolver === 'next') {
|
|
327
|
+
for (const v of cued)
|
|
328
|
+
if (matches(v))
|
|
329
|
+
picked.take(v.id, markOf(m, v, mark));
|
|
330
|
+
for (const v of list)
|
|
331
|
+
if (matches(v))
|
|
332
|
+
picked.take(v.id, markOf(m, v, mark));
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
const fold = foldOf(m, list, key, mark, self, resolver);
|
|
336
|
+
const version = scoreVersion(m, score);
|
|
337
|
+
const reduced = m.reduced;
|
|
338
|
+
if (fold.live === null ||
|
|
339
|
+
fold.liveOf !== m.cued ||
|
|
340
|
+
fold.liveVersion !== version ||
|
|
341
|
+
fold.liveReduced !== reduced) {
|
|
342
|
+
const live = new Pick(resolver, Number.NEGATIVE_INFINITY);
|
|
343
|
+
let keeps = true;
|
|
344
|
+
for (const v of cued) {
|
|
345
|
+
if (!matches(v))
|
|
346
|
+
continue;
|
|
347
|
+
if (v.owner !== null || v.holding !== null || v.fitting !== null)
|
|
348
|
+
keeps = false;
|
|
349
|
+
live.take(v.id, markOf(m, v, mark));
|
|
350
|
+
}
|
|
351
|
+
fold.live = live;
|
|
352
|
+
fold.liveOf = keeps ? m.cued : null;
|
|
353
|
+
fold.liveVersion = version;
|
|
354
|
+
fold.liveReduced = reduced;
|
|
355
|
+
}
|
|
356
|
+
picked.merge(fold.live);
|
|
357
|
+
for (; fold.seen < list.length; fold.seen++) {
|
|
358
|
+
const v = list[fold.seen];
|
|
359
|
+
if (!matches(v))
|
|
360
|
+
continue;
|
|
361
|
+
if (v.owner === null)
|
|
362
|
+
fold.pick.take(v.id, markOf(m, v, mark));
|
|
363
|
+
else
|
|
364
|
+
fold.owned.push(v);
|
|
365
|
+
}
|
|
366
|
+
picked.merge(fold.pick);
|
|
367
|
+
for (const v of fold.owned)
|
|
368
|
+
picked.take(v.id, markOf(m, v, mark));
|
|
369
|
+
}
|
|
224
370
|
if (q.writes === undefined && (anywhere || self.owner === null))
|
|
225
371
|
for (const a of mix.announced)
|
|
226
372
|
if (a.score === score &&
|
|
227
373
|
(score !== undefined || a.slot === mix.slot) &&
|
|
228
374
|
(q.name === undefined || a.name === q.name) &&
|
|
229
375
|
(q.tag === undefined || a.tags.includes(q.tag)))
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
if (
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
376
|
+
picked.take(a.order, Number.isNaN(a.at) ? undefined : mixAt(mix, a.at));
|
|
377
|
+
return (picked.any ? picked : forgotten).answer();
|
|
378
|
+
}
|
|
379
|
+
/** `self`'s read of `m`'s gone voices for one query, started over where the list has changed under it. */
|
|
380
|
+
function foldOf(m, list, key, mark, self, resolver) {
|
|
381
|
+
self.folds ??= new Map();
|
|
382
|
+
let byMix = self.folds.get(key);
|
|
383
|
+
if (byMix === undefined) {
|
|
384
|
+
byMix = new Map();
|
|
385
|
+
self.folds.set(key, byMix);
|
|
386
|
+
}
|
|
387
|
+
let fold = byMix.get(m);
|
|
388
|
+
if (fold === undefined || fold.list !== list || fold.mark !== mark) {
|
|
389
|
+
fold = {
|
|
390
|
+
list,
|
|
391
|
+
mark,
|
|
392
|
+
seen: 0,
|
|
393
|
+
pick: new Pick(resolver, Number.NEGATIVE_INFINITY),
|
|
394
|
+
owned: [],
|
|
395
|
+
live: null,
|
|
396
|
+
liveOf: null,
|
|
397
|
+
liveVersion: -1,
|
|
398
|
+
liveReduced: false,
|
|
399
|
+
};
|
|
400
|
+
byMix.set(m, fold);
|
|
401
|
+
}
|
|
402
|
+
return fold;
|
|
245
403
|
}
|
package/dist/project.d.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Mixer } from './mixer.js';
|
|
2
|
+
import { type Cut, Transport } from './transport.js';
|
|
2
3
|
import type { TransportProjection } from './types.js';
|
|
4
|
+
import type { Subject, Voice } from './voice.js';
|
|
3
5
|
/**
|
|
4
6
|
* Every mix on a transport read at mix time `t`, through copies of them on a transport of their
|
|
5
7
|
* own, moved together so an anchor across mixes answers as it would live.
|
|
6
8
|
*/
|
|
7
9
|
export declare function projectAll(transport: Transport, t: number): TransportProjection;
|
|
10
|
+
/** Sets each copied voice's state to what its clock and fade make it at `t`. */
|
|
11
|
+
export declare function stateAt<I, O>(c: Mixer<I, O>, t: number): void;
|
|
12
|
+
/** Gives a projection's copy what it reads of the live mix as it is, before its voices are copied. */
|
|
13
|
+
export declare function mirror<I, O>(mix: Mixer<I, O>, c: Mixer<I, O>): void;
|
|
14
|
+
/**
|
|
15
|
+
* A projection back starts each subject from the latest copy kept at or before `t`, else from its
|
|
16
|
+
* voice's start with fresh state, which is exact for a voice stepped at a fixed interval.
|
|
17
|
+
*/
|
|
18
|
+
export declare function recall<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I, t: number, cut: Cut): Subject<unknown> | undefined;
|