@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/fold.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { frozenTime, passAt, phaseAt, silent } from './clock.js';
|
|
2
2
|
import { copy as copyValue } 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;
|
|
@@ -263,19 +304,56 @@ export function apply(pose, voice, held, delta, weight) {
|
|
|
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,15 +603,18 @@ 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
620
|
pose[key] = pose[key] === undefined ? copy(value) : merged(channel, pose[key], value);
|
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;
|
package/dist/gather.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { foldNumber, lerpNumber } from './channels.js';
|
|
2
|
+
import { shortWay, turnFrom, turnInto, turnToward } from './turns.js';
|
|
2
3
|
/** `xs`, or a copy at least `n` long where it is shorter. */
|
|
3
4
|
function sized(xs, n) {
|
|
4
5
|
if (xs.length >= n)
|
|
@@ -37,13 +38,11 @@ export function gatherLocus(lanes, g) {
|
|
|
37
38
|
continue;
|
|
38
39
|
lanes.intoId = voice.id;
|
|
39
40
|
lanes.reset(lane);
|
|
40
|
-
|
|
41
|
-
const duration = voice.duration;
|
|
42
|
-
const passes = voice.passes;
|
|
41
|
+
lane.elapsedNow = voice.elapsedAt(lanes.now);
|
|
43
42
|
const list = lane.list;
|
|
44
43
|
for (let p = 0; p < list.length; p++) {
|
|
45
44
|
const slot = list[p];
|
|
46
|
-
if (lanes.one(lane, p, slot
|
|
45
|
+
if (lanes.one(lane, p, slot)) {
|
|
47
46
|
meetLocus(lanes, g, slot);
|
|
48
47
|
g.sum[slot] =
|
|
49
48
|
g.sum[slot] + lane.data[p * 8 /* Row.STRIDE */ + 2 /* Row.WEIGHT */];
|
|
@@ -68,8 +67,12 @@ function meetLocus(lanes, g, slot) {
|
|
|
68
67
|
if (taken !== undefined)
|
|
69
68
|
taken[slot] = 0;
|
|
70
69
|
}
|
|
71
|
-
/**
|
|
72
|
-
|
|
70
|
+
/**
|
|
71
|
+
* A member's value for one channel of a subject, lerped into its locus: the step of the general
|
|
72
|
+
* path's `foldLocus`.
|
|
73
|
+
*/
|
|
74
|
+
function gatherInto(lanes, g, ch, slot, value) {
|
|
75
|
+
const w = lanes.arg[0 /* Arg.WEIGHT */];
|
|
73
76
|
meetLocus(lanes, g, slot);
|
|
74
77
|
const k = ch.index;
|
|
75
78
|
let values = g.values[k];
|
|
@@ -83,7 +86,9 @@ function gatherInto(lanes, g, ch, slot, value, w) {
|
|
|
83
86
|
const taken = takenAt[slot];
|
|
84
87
|
const total = taken + w;
|
|
85
88
|
const u = w / total;
|
|
86
|
-
if (ch.
|
|
89
|
+
if (!ch.plain)
|
|
90
|
+
turnToward(ch, values, slot * ch.axes, value, taken, u);
|
|
91
|
+
else if (ch.scalar) {
|
|
87
92
|
values[slot] =
|
|
88
93
|
taken === 0 ? value : lerpNumber(values[slot], value, u);
|
|
89
94
|
}
|
|
@@ -110,15 +115,21 @@ export function foldLocus(lanes, lane, g) {
|
|
|
110
115
|
const sum = g.sum[slot];
|
|
111
116
|
if (sum <= 0)
|
|
112
117
|
continue;
|
|
113
|
-
const w = sum > 1 ? 1 : sum;
|
|
114
118
|
for (let k = 0; k < g.values.length; k++) {
|
|
115
119
|
const takenAt = g.taken[k];
|
|
116
|
-
|
|
120
|
+
const taken = takenAt === undefined ? 0 : takenAt[slot];
|
|
121
|
+
if (!(taken > 0))
|
|
117
122
|
continue;
|
|
123
|
+
// A channel folds at the weight of the members that wrote it, not the whole locus.
|
|
124
|
+
const w = taken > 1 ? 1 : taken;
|
|
118
125
|
const ch = lanes.laned[k];
|
|
119
126
|
const values = g.values[k];
|
|
120
127
|
const into = ch.values;
|
|
121
128
|
const base = slot * ch.axes;
|
|
129
|
+
if (!ch.plain) {
|
|
130
|
+
turnFrom(ch, into, base, values, base, w);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
122
133
|
for (let a = 0; a < ch.axes; a++)
|
|
123
134
|
into[base + a] = foldNumber(ch.op, into[base + a], values[base + a], w);
|
|
124
135
|
}
|
|
@@ -130,49 +141,56 @@ export function gather(lane, delta) {
|
|
|
130
141
|
for (let i = 0; i < chans.length; i++)
|
|
131
142
|
lane.values[i] = delta[chans[i].name];
|
|
132
143
|
}
|
|
133
|
-
/** Folds what `gather` read into a subject's laned values. */
|
|
134
|
-
export function fold(lane, slot
|
|
144
|
+
/** Folds what `gather` read into a subject's laned values, at `Arg.WEIGHT`. */
|
|
145
|
+
export function fold(lane, slot) {
|
|
135
146
|
const chans = lane.chans;
|
|
136
147
|
for (let i = 0; i < chans.length; i++)
|
|
137
|
-
this.foldInto(chans[i], slot, lane.values[i]
|
|
148
|
+
this.foldInto(chans[i], slot, lane.values[i]);
|
|
138
149
|
}
|
|
139
150
|
/**
|
|
140
|
-
* Folds a delta a patch returned into a subject's laned values. The first few
|
|
141
|
-
* at a site of their own, which in most mixes sees one channel name and stays
|
|
142
|
-
* site reading every name in turn slows every read.
|
|
151
|
+
* Folds a delta a patch returned into a subject's laned values, at `Arg.WEIGHT`. The first few
|
|
152
|
+
* channels are read at a site of their own, which in most mixes sees one channel name and stays
|
|
153
|
+
* fast, where one site reading every name in turn slows every read.
|
|
143
154
|
*/
|
|
144
|
-
export function foldDelta(chans, slot, delta
|
|
155
|
+
export function foldDelta(chans, slot, delta) {
|
|
145
156
|
const n = chans.length;
|
|
146
157
|
let ch = chans[0];
|
|
147
158
|
if (n > 0)
|
|
148
|
-
this.foldInto(ch, slot, delta[ch.name]
|
|
159
|
+
this.foldInto(ch, slot, delta[ch.name]);
|
|
149
160
|
ch = chans[1];
|
|
150
161
|
if (n > 1)
|
|
151
|
-
this.foldInto(ch, slot, delta[ch.name]
|
|
162
|
+
this.foldInto(ch, slot, delta[ch.name]);
|
|
152
163
|
ch = chans[2];
|
|
153
164
|
if (n > 2)
|
|
154
|
-
this.foldInto(ch, slot, delta[ch.name]
|
|
165
|
+
this.foldInto(ch, slot, delta[ch.name]);
|
|
155
166
|
for (let i = 3; i < n; i++) {
|
|
156
167
|
ch = chans[i];
|
|
157
|
-
this.foldInto(ch, slot, delta[ch.name]
|
|
168
|
+
this.foldInto(ch, slot, delta[ch.name]);
|
|
158
169
|
}
|
|
159
170
|
}
|
|
160
171
|
/** Folds a motion sample's axes, kept apart from `foldInto` so neither reads two array kinds. */
|
|
161
|
-
export function foldRun(ch, slot, xs
|
|
172
|
+
export function foldRun(ch, slot, xs) {
|
|
173
|
+
if (!ch.plain) {
|
|
174
|
+
this.unplain(ch, slot, ch.scalar ? xs[0] : xs);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const w = this.arg[0 /* Arg.WEIGHT */];
|
|
162
178
|
const values = ch.values;
|
|
163
179
|
const base = slot * ch.axes;
|
|
164
180
|
for (let a = 0; a < ch.axes; a++)
|
|
165
181
|
values[base + a] = foldNumber(ch.op, values[base + a], xs[a], w);
|
|
166
182
|
}
|
|
167
|
-
|
|
183
|
+
/** Folds one channel's value into a subject's laned values, or its locus, at `Arg.WEIGHT`. */
|
|
184
|
+
export function foldInto(ch, slot, value) {
|
|
168
185
|
if (value === undefined)
|
|
169
186
|
return;
|
|
170
187
|
if (this.into !== null) {
|
|
171
|
-
gatherInto(this, this.into, ch, slot, value
|
|
188
|
+
gatherInto(this, this.into, ch, slot, value);
|
|
172
189
|
return;
|
|
173
190
|
}
|
|
174
|
-
if (ch.
|
|
191
|
+
if (!ch.plain && !this.unplain(ch, slot, value))
|
|
175
192
|
return;
|
|
193
|
+
const w = this.arg[0 /* Arg.WEIGHT */];
|
|
176
194
|
const values = ch.values;
|
|
177
195
|
if (ch.scalar) {
|
|
178
196
|
values[slot] = foldNumber(ch.op, values[slot], value, w);
|
|
@@ -191,7 +209,8 @@ export function foldInto(ch, slot, value, w) {
|
|
|
191
209
|
* general path's `apply` decides it from the same band state on the same record, so a subject
|
|
192
210
|
* moving between the paths carries it.
|
|
193
211
|
*/
|
|
194
|
-
export function gate(ch
|
|
212
|
+
export function gate(ch) {
|
|
213
|
+
const w = this.arg[0 /* Arg.WEIGHT */];
|
|
195
214
|
const lane = this.folding;
|
|
196
215
|
const rec = this.rec;
|
|
197
216
|
const i = lane.chans.indexOf(ch);
|
|
@@ -202,3 +221,19 @@ export function gate(ch, w) {
|
|
|
202
221
|
bands[i] = on ? 1 : 2;
|
|
203
222
|
return on;
|
|
204
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* `foldInto` for a channel that is not plain, at `Arg.WEIGHT`. A rest-less one asks its band, and
|
|
226
|
+
* true is a pass, which `foldInto` then folds; a rotation is folded here, so false.
|
|
227
|
+
*/
|
|
228
|
+
export function unplain(ch, slot, value) {
|
|
229
|
+
if (ch.op === 'last')
|
|
230
|
+
return this.gate(ch);
|
|
231
|
+
const w = this.arg[0 /* Arg.WEIGHT */];
|
|
232
|
+
const values = ch.values;
|
|
233
|
+
// An angle here and not through `turnInto`, where its weight would cross a call as a boxed double.
|
|
234
|
+
if (ch.turn > 0)
|
|
235
|
+
values[slot] = values[slot] + shortWay(value, ch.turn) * w;
|
|
236
|
+
else
|
|
237
|
+
turnInto(ch, values, slot * ch.axes, value, w);
|
|
238
|
+
return false;
|
|
239
|
+
}
|
package/dist/gone.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Mixer } from './mixer.js';
|
|
2
|
+
import { Store } from './store.js';
|
|
3
|
+
import type { Voice } from './voice.js';
|
|
4
|
+
/**
|
|
5
|
+
* A mix's gone voices as `drop` and a freed slot look for them: those over every subject, and the
|
|
6
|
+
* rest by each subject they name. Kept in step with `gone`, which only grows by push and is
|
|
7
|
+
* otherwise replaced whole, so it catches up lazily and rebuilds only on a replacement it did not
|
|
8
|
+
* make itself. Under history a churning mix holds thousands, too many to walk per subject dropped.
|
|
9
|
+
*/
|
|
10
|
+
export declare class GoneIndex<I, O> {
|
|
11
|
+
/** The `gone` list it was built over, and how much of it. */
|
|
12
|
+
of: readonly Voice<I, O>[] | null;
|
|
13
|
+
count: number;
|
|
14
|
+
general: Voice<I, O>[];
|
|
15
|
+
named: Store<I, Voice<I, O>[]>;
|
|
16
|
+
/** The latest `doneAt` in `of`, and how far any voice finished before one listed ahead of it. */
|
|
17
|
+
latest: number;
|
|
18
|
+
slack: number;
|
|
19
|
+
}
|
|
20
|
+
/** The index caught up with `mix.gone`. */
|
|
21
|
+
export declare function goneIndex<I, O>(mix: Mixer<I, O>): GoneIndex<I, O>;
|
|
22
|
+
/**
|
|
23
|
+
* Keeps the gone voices `keep` says yes to, asking it once each, and takes the rest out of the
|
|
24
|
+
* index one by one. Leaves `gone` as it was when every one stays.
|
|
25
|
+
*/
|
|
26
|
+
export declare function keepGone<I, O>(mix: Mixer<I, O>, keep: (v: Voice<I, O>) => boolean): void;
|
|
27
|
+
/**
|
|
28
|
+
* Lets go of the gone voices that finished before `reach`. Voices go in about the order they
|
|
29
|
+
* finish, so only the front can hold one: past the point where a voice finished `slack` after
|
|
30
|
+
* `reach`, none behind it finished before `reach`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function expireGone<I, O>(mix: Mixer<I, O>, reach: number): void;
|