@msb235/blits 0.7.1 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +193 -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 +8 -0
- package/dist/clone.js +22 -0
- 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 +216 -31
- package/dist/frames.d.ts +21 -0
- package/dist/frames.js +59 -0
- 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/locus.js +2 -2
- 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 +39 -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 +45 -6
- package/dist/transport.js +64 -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/fold.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { frozenTime, passAt, phaseAt, silent } from './clock.js';
|
|
2
|
-
import { copy as copyValue } from './clone.js';
|
|
2
|
+
import { copy as copyValue, first } from './clone.js';
|
|
3
|
+
import { open as openRecord, shut as shutRecord, weighAt } from './everyone.js';
|
|
3
4
|
import { recordHost, remember } from './history.js';
|
|
4
5
|
import { stateful } from './hosts.js';
|
|
5
6
|
import { locusScratch } from './locus.js';
|
|
7
|
+
import { moved } from './moved.js';
|
|
6
8
|
import { readKeys } from './patch.js';
|
|
7
9
|
import { reading } from './reading.js';
|
|
8
10
|
// V8 reads a local const as a constant, where it reads an import from its module on every call.
|
|
9
11
|
const copy = copyValue;
|
|
12
|
+
const open = openRecord;
|
|
13
|
+
const shut = shutRecord;
|
|
10
14
|
/** A record's band state, made when a rest-less channel first asks. */
|
|
11
15
|
function bandsFor(held, n) {
|
|
12
16
|
const bands = new Uint8Array(n);
|
|
@@ -25,7 +29,7 @@ export function near(a, b) {
|
|
|
25
29
|
* new array: `b` belongs to a patch or the mix's scratch, and a host may edit the pose. `acc` is a
|
|
26
30
|
* value this fold made.
|
|
27
31
|
*/
|
|
28
|
-
function merged(channel, acc, b) {
|
|
32
|
+
export function merged(channel, acc, b) {
|
|
29
33
|
const r = channel.merge(acc, b);
|
|
30
34
|
if (r !== b || !Array.isArray(r))
|
|
31
35
|
return r;
|
|
@@ -35,6 +39,24 @@ function merged(channel, acc, b) {
|
|
|
35
39
|
acc[i] = r[i];
|
|
36
40
|
return acc;
|
|
37
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* A record after a seek that rebuilds: fresh state, stepped again from where the voice's clock now
|
|
44
|
+
* puts this subject's start, which under `stepMs` lands where a voice cued fresh would.
|
|
45
|
+
*/
|
|
46
|
+
function rebuild(mix, voice, subject, now, held) {
|
|
47
|
+
held.rebuilt = voice.rebuilds;
|
|
48
|
+
const patch = voice.patch;
|
|
49
|
+
if (patch.state === undefined && patch.step === undefined && held.kept === null)
|
|
50
|
+
return;
|
|
51
|
+
held.state = patch.state ? patch.state(subject) : undefined;
|
|
52
|
+
held.kept = null;
|
|
53
|
+
held.unkept = undefined;
|
|
54
|
+
held.since = mix.sinceOf(voice, held.delay);
|
|
55
|
+
held.shown = mix.shownOf(voice, held.since);
|
|
56
|
+
held.ticks = 0;
|
|
57
|
+
held.stepped = held.since < now ? held.since : now;
|
|
58
|
+
held.probed = Number.NaN;
|
|
59
|
+
}
|
|
38
60
|
/**
|
|
39
61
|
* One live voice's delta for one subject this frame, through its record for the subject, or null
|
|
40
62
|
* when it does not reach. Its weight is left in `w`.
|
|
@@ -45,12 +67,16 @@ export function contribution(voice, subject, now, held) {
|
|
|
45
67
|
return null;
|
|
46
68
|
if (voice.out?.rest && held.rested)
|
|
47
69
|
return null;
|
|
70
|
+
if (held.rebuilt !== voice.rebuilds)
|
|
71
|
+
rebuild(this, voice, subject, now, held);
|
|
48
72
|
const raw = voice.elapsedAt(now) - held.delay;
|
|
49
|
-
|
|
73
|
+
// Not `raw < 0`: see `frozenTime` for the clock that reads NaN before its start.
|
|
74
|
+
const early = !(raw >= 0);
|
|
75
|
+
if (early && !voice.freezesBefore)
|
|
50
76
|
return null;
|
|
51
77
|
const elapsed = frozenTime(raw, voice.freezesBefore, voice.freezesAfter, voice.span);
|
|
52
78
|
// A frozen subject's clock stands still at the edge it freezes at: -1 before, 1 after, 0 playing.
|
|
53
|
-
const still =
|
|
79
|
+
const still = early ? -1 : voice.freezesAfter && raw > voice.span ? 1 : 0;
|
|
54
80
|
const duration = voice.duration;
|
|
55
81
|
const phase = phaseAt(elapsed, duration, voice.passes);
|
|
56
82
|
const pass = passAt(elapsed, duration, voice.passes);
|
|
@@ -79,6 +105,10 @@ export function contribution(voice, subject, now, held) {
|
|
|
79
105
|
if (voice.patch.step && held.probed !== now && still >= 0) {
|
|
80
106
|
// Frozen after, it steps once more to where its last pass ended, and no further.
|
|
81
107
|
const to = still === 1 ? voice.timeAt(held.delay + voice.span) : now;
|
|
108
|
+
if (held.stepped < held.since) {
|
|
109
|
+
held.stepped = held.since;
|
|
110
|
+
setting.dt = this.capped(now - held.since);
|
|
111
|
+
}
|
|
82
112
|
if (tick !== undefined && tick > 0 && !this.reducedNow)
|
|
83
113
|
this.tick(voice, subject, held, tick, to);
|
|
84
114
|
else if (still === 0 ? now !== held.stepped : to > held.stepped) {
|
|
@@ -102,6 +132,9 @@ export function contribution(voice, subject, now, held) {
|
|
|
102
132
|
held.phase = phase;
|
|
103
133
|
delta = this.keyed(voice, subject, held);
|
|
104
134
|
}
|
|
135
|
+
else if (voice.motion !== undefined) {
|
|
136
|
+
delta = moved(voice.motion, voice.patch.writes[0], subject, setting, held);
|
|
137
|
+
}
|
|
105
138
|
else {
|
|
106
139
|
delta = voice.patch.at(phase, subject, setting);
|
|
107
140
|
}
|
|
@@ -230,6 +263,14 @@ export function isRest(delta) {
|
|
|
230
263
|
}
|
|
231
264
|
return true;
|
|
232
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* A record's band state once its voice is skipped at weight 0: off, as `passes` would have said, so
|
|
268
|
+
* a weight that comes back inside the band does not find it still on.
|
|
269
|
+
*/
|
|
270
|
+
export function unband(held) {
|
|
271
|
+
if (held?.bands)
|
|
272
|
+
held.bands.fill(2);
|
|
273
|
+
}
|
|
233
274
|
/** Whether a rest-less channel's contribution is switched on, across a band rather than an edge. */
|
|
234
275
|
export function passes(was, w) {
|
|
235
276
|
const { on, off } = this.band;
|
|
@@ -257,25 +298,62 @@ export function apply(pose, voice, held, delta, weight) {
|
|
|
257
298
|
const on = this.passes(band === 0 ? undefined : band === 1, weight);
|
|
258
299
|
bands[i] = on ? 1 : 2;
|
|
259
300
|
if (on)
|
|
260
|
-
pose[key] = pose[key] === undefined ?
|
|
301
|
+
pose[key] = pose[key] === undefined ? first(value) : merged(channel, pose[key], value);
|
|
261
302
|
}
|
|
262
303
|
}
|
|
263
304
|
/** Clamps every bounded channel of a folded pose into its range, in place. */
|
|
264
305
|
export function clamp(pose) {
|
|
265
306
|
for (const slot of this.bounded) {
|
|
266
|
-
const [lo, hi] = this.channels[slot].bounds;
|
|
267
307
|
const key = this.names[slot];
|
|
268
|
-
|
|
269
|
-
if (typeof v === 'number')
|
|
270
|
-
pose[key] = v < lo ? lo : v > hi ? hi : v;
|
|
271
|
-
else if (Array.isArray(v))
|
|
272
|
-
for (let i = 0; i < v.length; i++) {
|
|
273
|
-
const x = v[i];
|
|
274
|
-
v[i] = x < lo ? lo : x > hi ? hi : x;
|
|
275
|
-
}
|
|
308
|
+
pose[key] = bound(this.channels[slot], pose[key]);
|
|
276
309
|
}
|
|
277
310
|
return pose;
|
|
278
311
|
}
|
|
312
|
+
/** A folded value held to its channel's bounds: a number clamped, an array clamped in place. */
|
|
313
|
+
export function bound(channel, v) {
|
|
314
|
+
const [lo, hi] = channel.bounds;
|
|
315
|
+
if (typeof v === 'number')
|
|
316
|
+
return v < lo ? lo : v > hi ? hi : v;
|
|
317
|
+
if (Array.isArray(v))
|
|
318
|
+
for (let i = 0; i < v.length; i++) {
|
|
319
|
+
const x = v[i];
|
|
320
|
+
v[i] = x < lo ? lo : x > hi ? hi : x;
|
|
321
|
+
}
|
|
322
|
+
return v;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Whether a fold's next voice is its chain's next record, `held`, and not the next voice sharing
|
|
326
|
+
* one record, `sharers[q]`: the two lists are each in voice order, and a fold takes them as one.
|
|
327
|
+
*/
|
|
328
|
+
export function chainFirst(held, sharers, q) {
|
|
329
|
+
if (held === null)
|
|
330
|
+
return false;
|
|
331
|
+
if (q >= sharers.length || held.voice === null)
|
|
332
|
+
return true;
|
|
333
|
+
const next = sharers[q];
|
|
334
|
+
return next.id > held.voice.id;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* After a fold read a sharing voice for the subject numbered `slot`: keeps the weight it gave and
|
|
338
|
+
* what the read left on its record, and gives the fold's place in `sharers`, which moves back where
|
|
339
|
+
* the read made the voice share no more.
|
|
340
|
+
*/
|
|
341
|
+
export function shared(voice, slot, rec, q) {
|
|
342
|
+
if (voice.sharing) {
|
|
343
|
+
// A projection numbers no subject, and keeps nothing by subject.
|
|
344
|
+
if (slot < 0)
|
|
345
|
+
return q;
|
|
346
|
+
const cap = this.lanes.cap;
|
|
347
|
+
weighAt(voice, slot, rec.weight, cap);
|
|
348
|
+
shut(voice, rec, slot, cap);
|
|
349
|
+
return q;
|
|
350
|
+
}
|
|
351
|
+
const sharers = this.sharers;
|
|
352
|
+
let i = 0;
|
|
353
|
+
while (i < sharers.length && sharers[i].id < voice.id)
|
|
354
|
+
i++;
|
|
355
|
+
return i;
|
|
356
|
+
}
|
|
279
357
|
export function fold(subject, out, dry = false, except) {
|
|
280
358
|
const pose = this.folded(subject, out, dry, except);
|
|
281
359
|
return (this.bounded.length === 0 ? pose : this.clamp(pose));
|
|
@@ -296,20 +374,29 @@ export function linked(subject) {
|
|
|
296
374
|
}
|
|
297
375
|
/** The fold after a subject's chain is linked and its lanes asked whether it reads from them. */
|
|
298
376
|
export function foldWith(subject, out, head, laned, dry, except) {
|
|
299
|
-
const now = this.now;
|
|
300
377
|
const pose = out;
|
|
301
378
|
const lanes = this.lanes;
|
|
302
379
|
const skip = laned ? lanes.copies : null;
|
|
303
380
|
for (let i = 0; i < this.names.length; i++) {
|
|
304
381
|
if (skip?.[i])
|
|
305
382
|
continue;
|
|
306
|
-
const
|
|
383
|
+
const channel = this.channels[i];
|
|
384
|
+
const rest = channel.rest;
|
|
307
385
|
const key = this.names[i];
|
|
308
386
|
// Never `delete`: it drops a reused out object into dictionary mode for good.
|
|
309
|
-
if (rest
|
|
310
|
-
pose[key]
|
|
311
|
-
|
|
312
|
-
|
|
387
|
+
if (rest === undefined) {
|
|
388
|
+
if (pose[key] !== undefined)
|
|
389
|
+
pose[key] = undefined;
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
// A stock array channel never keeps the pose's array, so one a last fold left in `out` is ours.
|
|
393
|
+
const held = this.lerpsInto[i] === undefined ? undefined : pose[key];
|
|
394
|
+
if (Array.isArray(held) && held !== rest && held.length === rest.length) {
|
|
395
|
+
for (let a = 0; a < held.length; a++)
|
|
396
|
+
held[a] = rest[a];
|
|
397
|
+
}
|
|
398
|
+
else
|
|
399
|
+
pose[key] = channel.copy ? channel.copy(rest) : copy(rest);
|
|
313
400
|
}
|
|
314
401
|
if (head === null)
|
|
315
402
|
return pose;
|
|
@@ -323,7 +410,8 @@ export function foldWith(subject, out, head, laned, dry, except) {
|
|
|
323
410
|
else if (l.whole)
|
|
324
411
|
return pose;
|
|
325
412
|
}
|
|
326
|
-
if (this.
|
|
413
|
+
if (this.detours === 0) {
|
|
414
|
+
const now = this.now;
|
|
327
415
|
for (let held = head; held !== null; held = held.next) {
|
|
328
416
|
const voice = held.voice;
|
|
329
417
|
if (voice === null || (laned && voice.laned && !this.owedBy(owed, voice)))
|
|
@@ -333,13 +421,82 @@ export function foldWith(subject, out, head, laned, dry, except) {
|
|
|
333
421
|
const delta = this.read(voice, subject, now, dry, held);
|
|
334
422
|
if (owed >= 0 && voice.laned)
|
|
335
423
|
lanes.paid(voice.id, owed, held.weight);
|
|
424
|
+
if (!(held.weight > 0))
|
|
425
|
+
unband(held);
|
|
336
426
|
if (delta === null || this.w <= 0)
|
|
337
427
|
continue;
|
|
338
428
|
this.apply(pose, voice, held, delta, this.w);
|
|
339
429
|
}
|
|
340
430
|
return pose;
|
|
341
431
|
}
|
|
342
|
-
return this.
|
|
432
|
+
return this.foldDetour(subject, pose, head, laned, dry, except, owed);
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* `foldWith`'s voices in a mix with a locus or a voice sharing one record in it. The chain is taken
|
|
436
|
+
* again where a voice stopped sharing since it was linked, as a patch call in this probe's fill can
|
|
437
|
+
* make one do. With no locus, the chain's records fold in voice order, and each voice sharing one
|
|
438
|
+
* record, which is in no chain, before the first chain record that comes after it.
|
|
439
|
+
*/
|
|
440
|
+
export function foldDetour(subject, pose, linkedHead, laned, dry, except, owed) {
|
|
441
|
+
const head = linkedHead.version === this.version ? linkedHead : this.chain(subject);
|
|
442
|
+
if (this.loci !== 0)
|
|
443
|
+
return this.foldLoci(subject, pose, head, laned, dry, except, owed);
|
|
444
|
+
const now = this.now;
|
|
445
|
+
const lanes = this.lanes;
|
|
446
|
+
const sharers = this.sharers;
|
|
447
|
+
let q = 0;
|
|
448
|
+
for (let held = head; held !== null; held = held.next) {
|
|
449
|
+
const voice = held.voice;
|
|
450
|
+
if (voice === null)
|
|
451
|
+
continue;
|
|
452
|
+
if (q < sharers.length && sharers[q].id < voice.id)
|
|
453
|
+
q = this.foldSharers(pose, subject, head, q, voice.id, laned, dry, except, owed);
|
|
454
|
+
if (laned && voice.laned && !this.owedBy(owed, voice))
|
|
455
|
+
continue;
|
|
456
|
+
if (voice.id === except || voice.state === 'done')
|
|
457
|
+
continue;
|
|
458
|
+
const delta = this.read(voice, subject, now, dry, held);
|
|
459
|
+
if (owed >= 0 && voice.laned)
|
|
460
|
+
lanes.paid(voice.id, owed, held.weight);
|
|
461
|
+
if (!(held.weight > 0))
|
|
462
|
+
unband(held);
|
|
463
|
+
if (delta === null || this.w <= 0)
|
|
464
|
+
continue;
|
|
465
|
+
this.apply(pose, voice, held, delta, this.w);
|
|
466
|
+
}
|
|
467
|
+
if (q < sharers.length)
|
|
468
|
+
this.foldSharers(pose, subject, head, q, Number.MAX_SAFE_INTEGER, laned, dry, except, owed);
|
|
469
|
+
return pose;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Folds the voices sharing one record from `sharers[q]` up to the first whose id is `before` or
|
|
473
|
+
* more, as `foldWith` folds a chain's records, and gives the place it stopped at.
|
|
474
|
+
*/
|
|
475
|
+
export function foldSharers(pose, subject, head, q, before, laned, dry, except, owed) {
|
|
476
|
+
const sharers = this.sharers;
|
|
477
|
+
const now = this.now;
|
|
478
|
+
while (q < sharers.length) {
|
|
479
|
+
const voice = sharers[q];
|
|
480
|
+
if (voice.id >= before)
|
|
481
|
+
break;
|
|
482
|
+
q++;
|
|
483
|
+
const rec = voice.state === 'pending' ? null : voice.everyone;
|
|
484
|
+
if (rec === null)
|
|
485
|
+
continue;
|
|
486
|
+
if (laned && voice.laned && !this.owedBy(owed, voice))
|
|
487
|
+
continue;
|
|
488
|
+
if (voice.id === except || voice.state === 'done')
|
|
489
|
+
continue;
|
|
490
|
+
open(voice, rec, head.slot);
|
|
491
|
+
const delta = this.read(voice, subject, now, dry, rec);
|
|
492
|
+
if (owed >= 0 && voice.laned)
|
|
493
|
+
this.lanes.paid(voice.id, owed, rec.weight);
|
|
494
|
+
q = this.shared(voice, head.slot, rec, q);
|
|
495
|
+
if (delta === null || this.w <= 0)
|
|
496
|
+
continue;
|
|
497
|
+
this.apply(pose, voice, rec, delta, this.w);
|
|
498
|
+
}
|
|
499
|
+
return q;
|
|
343
500
|
}
|
|
344
501
|
/** Whether the subject numbered `owed` reads laned `voice` from the general path this fill. */
|
|
345
502
|
export function owedBy(owed, voice) {
|
|
@@ -359,15 +516,36 @@ export function foldLoci(subject, pose, head, laned, dry, except, owed) {
|
|
|
359
516
|
k.n = 0;
|
|
360
517
|
k.m = 0;
|
|
361
518
|
k.names.length = 0;
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
519
|
+
const sharers = this.sharers;
|
|
520
|
+
let q = 0;
|
|
521
|
+
let at = head;
|
|
522
|
+
while (at !== null || q < sharers.length) {
|
|
523
|
+
const mine = chainFirst(at, sharers, q);
|
|
524
|
+
let voice;
|
|
525
|
+
let rec;
|
|
526
|
+
if (mine) {
|
|
527
|
+
rec = at;
|
|
528
|
+
voice = rec.voice;
|
|
529
|
+
at = rec.next;
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
voice = sharers[q++];
|
|
533
|
+
rec = voice.state === 'pending' ? null : voice.everyone;
|
|
534
|
+
}
|
|
535
|
+
if (voice === null || rec === null)
|
|
536
|
+
continue;
|
|
537
|
+
if (laned && voice.laned && !this.owedBy(owed, voice))
|
|
365
538
|
continue;
|
|
366
539
|
if (voice.id === except || voice.state === 'done')
|
|
367
540
|
continue;
|
|
541
|
+
const held = rec;
|
|
542
|
+
if (!mine)
|
|
543
|
+
open(voice, held, head.slot);
|
|
368
544
|
const delta = this.read(voice, subject, now, dry, held);
|
|
369
545
|
if (owed >= 0 && voice.laned)
|
|
370
546
|
this.lanes.paid(voice.id, owed, held.weight);
|
|
547
|
+
if (!mine)
|
|
548
|
+
q = this.shared(voice, head.slot, held, q);
|
|
371
549
|
if (delta === null)
|
|
372
550
|
continue;
|
|
373
551
|
const locus = voice.spec.locus;
|
|
@@ -404,12 +582,16 @@ export function foldLoci(subject, pose, head, laned, dry, except, owed) {
|
|
|
404
582
|
const weight = k.weights[n];
|
|
405
583
|
if (weight > 0)
|
|
406
584
|
this.apply(pose, k.voices[n], k.helds[n], k.deltas[n], weight);
|
|
585
|
+
else
|
|
586
|
+
unband(k.helds[n]);
|
|
407
587
|
continue;
|
|
408
588
|
}
|
|
409
589
|
const weight = this.foldLocus(k, group);
|
|
410
|
-
if (weight <= 0)
|
|
411
|
-
continue;
|
|
412
590
|
const name = k.names[group];
|
|
591
|
+
if (!(weight > 0)) {
|
|
592
|
+
bands.get(name)?.fill(2);
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
413
595
|
let on = bands.get(name);
|
|
414
596
|
if (on === undefined) {
|
|
415
597
|
on = new Uint8Array(this.channels.length);
|
|
@@ -421,18 +603,21 @@ export function foldLoci(subject, pose, head, laned, dry, except, owed) {
|
|
|
421
603
|
k.values[slot] = undefined;
|
|
422
604
|
const key = this.names[slot];
|
|
423
605
|
const channel = this.channels[slot];
|
|
606
|
+
// A channel folds at the weight of the members that wrote it, not the whole locus.
|
|
607
|
+
const taken = k.taken[slot];
|
|
608
|
+
const w = taken > 1 ? 1 : taken;
|
|
424
609
|
if (channel.rest !== undefined && channel.scale) {
|
|
425
610
|
// pose[key] is the copy of rest this fold made, so it is ours to write into.
|
|
426
611
|
pose[key] = channel.fold
|
|
427
|
-
? channel.fold(pose[key], value,
|
|
428
|
-
: merged(channel, pose[key], channel.scale(value,
|
|
612
|
+
? channel.fold(pose[key], value, w)
|
|
613
|
+
: merged(channel, pose[key], channel.scale(value, w));
|
|
429
614
|
continue;
|
|
430
615
|
}
|
|
431
616
|
const band = on[slot];
|
|
432
|
-
const passes = this.passes(band === 0 ? undefined : band === 1,
|
|
617
|
+
const passes = this.passes(band === 0 ? undefined : band === 1, w);
|
|
433
618
|
on[slot] = passes ? 1 : 2;
|
|
434
619
|
if (passes)
|
|
435
|
-
pose[key] = pose[key] === undefined ?
|
|
620
|
+
pose[key] = pose[key] === undefined ? first(value) : merged(channel, pose[key], value);
|
|
436
621
|
}
|
|
437
622
|
}
|
|
438
623
|
return pose;
|
package/dist/frames.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Frame } from './transport.js';
|
|
2
|
+
/**
|
|
3
|
+
* The frames a transport's history can still reach, oldest first. A frame leaves the front on
|
|
4
|
+
* most syncs, so the front is an index that moves and the array closes up only now and then.
|
|
5
|
+
*/
|
|
6
|
+
export declare class Frames {
|
|
7
|
+
private list;
|
|
8
|
+
private head;
|
|
9
|
+
get length(): number;
|
|
10
|
+
push(frame: Frame): void;
|
|
11
|
+
/** Lets go of every frame before the last one at or before `reach`, giving each to `each`. */
|
|
12
|
+
shed(reach: number, each?: (frame: Frame) => void): void;
|
|
13
|
+
/**
|
|
14
|
+
* The frame a seek or read to mix time `t` lands on: the last one standing at or before `t`,
|
|
15
|
+
* since rate 0 can hold the clock at `t` for many.
|
|
16
|
+
*/
|
|
17
|
+
at(t: number): Frame | undefined;
|
|
18
|
+
/** Every frame held, oldest first. */
|
|
19
|
+
all(): Frame[];
|
|
20
|
+
set(frames: Frame[]): void;
|
|
21
|
+
}
|
package/dist/frames.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** Past this many frames let go of at the front, the list is closed up, once they are half of it. */
|
|
2
|
+
const SLACK = 512;
|
|
3
|
+
/**
|
|
4
|
+
* The frames a transport's history can still reach, oldest first. A frame leaves the front on
|
|
5
|
+
* most syncs, so the front is an index that moves and the array closes up only now and then.
|
|
6
|
+
*/
|
|
7
|
+
export class Frames {
|
|
8
|
+
list = [];
|
|
9
|
+
head = 0;
|
|
10
|
+
get length() {
|
|
11
|
+
return this.list.length - this.head;
|
|
12
|
+
}
|
|
13
|
+
push(frame) {
|
|
14
|
+
this.list.push(frame);
|
|
15
|
+
}
|
|
16
|
+
/** Lets go of every frame before the last one at or before `reach`, giving each to `each`. */
|
|
17
|
+
shed(reach, each) {
|
|
18
|
+
const list = this.list;
|
|
19
|
+
let head = this.head;
|
|
20
|
+
while (head + 1 < list.length && list[head + 1].at <= reach) {
|
|
21
|
+
each?.(list[head]);
|
|
22
|
+
head++;
|
|
23
|
+
}
|
|
24
|
+
if (head >= SLACK && head * 2 >= list.length) {
|
|
25
|
+
list.splice(0, head);
|
|
26
|
+
head = 0;
|
|
27
|
+
}
|
|
28
|
+
this.head = head;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The frame a seek or read to mix time `t` lands on: the last one standing at or before `t`,
|
|
32
|
+
* since rate 0 can hold the clock at `t` for many.
|
|
33
|
+
*/
|
|
34
|
+
at(t) {
|
|
35
|
+
const list = this.list;
|
|
36
|
+
let lo = this.head;
|
|
37
|
+
let hi = list.length - 1;
|
|
38
|
+
let found;
|
|
39
|
+
while (lo <= hi) {
|
|
40
|
+
const mid = (lo + hi) >> 1;
|
|
41
|
+
const f = list[mid];
|
|
42
|
+
if (f.at <= t) {
|
|
43
|
+
found = f;
|
|
44
|
+
lo = mid + 1;
|
|
45
|
+
}
|
|
46
|
+
else
|
|
47
|
+
hi = mid - 1;
|
|
48
|
+
}
|
|
49
|
+
return found;
|
|
50
|
+
}
|
|
51
|
+
/** Every frame held, oldest first. */
|
|
52
|
+
all() {
|
|
53
|
+
return this.list.slice(this.head);
|
|
54
|
+
}
|
|
55
|
+
set(frames) {
|
|
56
|
+
this.list = frames;
|
|
57
|
+
this.head = 0;
|
|
58
|
+
}
|
|
59
|
+
}
|
package/dist/gather.d.ts
CHANGED
|
@@ -11,20 +11,26 @@ export declare function gatherLocus<I, O>(lanes: Lanes<I, O>, g: Locus<I, O>): v
|
|
|
11
11
|
export declare function foldLocus<I, O>(lanes: Lanes<I, O>, lane: Lane<I, O>, g: Locus<I, O>): void;
|
|
12
12
|
/** For a keys voice: the delta's value for each channel it writes, read once per phase. */
|
|
13
13
|
export declare function gather<I, O>(this: Lanes<I, O>, lane: Lane<I, O>, delta: Record<string, unknown>): void;
|
|
14
|
-
/** Folds what `gather` read into a subject's laned values. */
|
|
15
|
-
export declare function fold<I, O>(this: Lanes<I, O>, lane: Lane<I, O>, slot: number
|
|
14
|
+
/** Folds what `gather` read into a subject's laned values, at `Arg.WEIGHT`. */
|
|
15
|
+
export declare function fold<I, O>(this: Lanes<I, O>, lane: Lane<I, O>, slot: number): void;
|
|
16
16
|
/**
|
|
17
|
-
* Folds a delta a patch returned into a subject's laned values. The first few
|
|
18
|
-
* at a site of their own, which in most mixes sees one channel name and stays
|
|
19
|
-
* site reading every name in turn slows every read.
|
|
17
|
+
* Folds a delta a patch returned into a subject's laned values, at `Arg.WEIGHT`. The first few
|
|
18
|
+
* channels are read at a site of their own, which in most mixes sees one channel name and stays
|
|
19
|
+
* fast, where one site reading every name in turn slows every read.
|
|
20
20
|
*/
|
|
21
|
-
export declare function foldDelta<I, O>(this: Lanes<I, O>, chans: readonly Laned[], slot: number, delta: Record<string, unknown
|
|
21
|
+
export declare function foldDelta<I, O>(this: Lanes<I, O>, chans: readonly Laned[], slot: number, delta: Record<string, unknown>): void;
|
|
22
22
|
/** Folds a motion sample's axes, kept apart from `foldInto` so neither reads two array kinds. */
|
|
23
|
-
export declare function foldRun<I, O>(this: Lanes<I, O>, ch: Laned, slot: number, xs: Float64Array
|
|
24
|
-
|
|
23
|
+
export declare function foldRun<I, O>(this: Lanes<I, O>, ch: Laned, slot: number, xs: Float64Array): void;
|
|
24
|
+
/** Folds one channel's value into a subject's laned values, or its locus, at `Arg.WEIGHT`. */
|
|
25
|
+
export declare function foldInto<I, O>(this: Lanes<I, O>, ch: Laned, slot: number, value: unknown): void;
|
|
25
26
|
/**
|
|
26
27
|
* Whether a rest-less channel's contribution passes the band, for the record `one` is folding: the
|
|
27
28
|
* general path's `apply` decides it from the same band state on the same record, so a subject
|
|
28
29
|
* moving between the paths carries it.
|
|
29
30
|
*/
|
|
30
|
-
export declare function gate<I, O>(this: Lanes<I, O>, ch: Laned
|
|
31
|
+
export declare function gate<I, O>(this: Lanes<I, O>, ch: Laned): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* `foldInto` for a channel that is not plain, at `Arg.WEIGHT`. A rest-less one asks its band, and
|
|
34
|
+
* true is a pass, which `foldInto` then folds; a rotation is folded here, so false.
|
|
35
|
+
*/
|
|
36
|
+
export declare function unplain<I, O>(this: Lanes<I, O>, ch: Laned, slot: number, value: unknown): boolean;
|