@msb235/blits 0.10.0 → 0.12.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 +248 -0
- package/README.md +15 -4
- package/dist/arcs.d.ts +40 -0
- package/dist/arcs.js +239 -0
- package/dist/blend.js +1 -5
- package/dist/book.d.ts +2 -0
- package/dist/book.js +27 -3
- package/dist/chain.d.ts +17 -1
- package/dist/chain.js +57 -15
- package/dist/channels.d.ts +2 -0
- package/dist/channels.js +12 -1
- package/dist/clock.d.ts +14 -0
- package/dist/clock.js +9 -1
- package/dist/clone.js +16 -4
- package/dist/columns.d.ts +4 -5
- package/dist/columns.js +11 -5
- package/dist/crowd.d.ts +1 -1
- package/dist/crowd.js +4 -1
- package/dist/cue.js +63 -7
- package/dist/departed.d.ts +8 -3
- package/dist/departed.js +76 -29
- package/dist/due.d.ts +4 -0
- package/dist/due.js +16 -3
- package/dist/easing.d.ts +12 -1
- package/dist/easing.js +7 -1
- package/dist/everyone.d.ts +14 -1
- package/dist/everyone.js +27 -2
- package/dist/fade.d.ts +12 -5
- package/dist/fade.js +57 -19
- package/dist/fill.js +16 -9
- package/dist/fit.d.ts +8 -1
- package/dist/fit.js +7 -3
- package/dist/fold.d.ts +1 -1
- package/dist/fold.js +112 -9
- package/dist/frames.d.ts +2 -0
- package/dist/frames.js +17 -0
- package/dist/gather.js +38 -7
- package/dist/handle.d.ts +7 -2
- package/dist/handle.js +28 -12
- package/dist/held.d.ts +13 -2
- package/dist/held.js +43 -9
- package/dist/history.d.ts +25 -2
- package/dist/history.js +163 -35
- package/dist/hosts.js +45 -17
- package/dist/index.d.ts +5 -2
- package/dist/index.js +3 -1
- package/dist/keyfill.js +3 -1
- package/dist/lane.d.ts +1 -1
- package/dist/lane.js +3 -1
- package/dist/lanes.d.ts +31 -7
- package/dist/lanes.js +57 -6
- package/dist/lean.d.ts +15 -0
- package/dist/lean.js +23 -0
- package/dist/leavings.d.ts +6 -1
- package/dist/leavings.js +16 -2
- package/dist/marks.d.ts +7 -3
- package/dist/marks.js +105 -27
- package/dist/meet.js +1 -0
- package/dist/mixer.d.ts +35 -6
- package/dist/mixer.js +113 -45
- package/dist/motion.d.ts +4 -1
- package/dist/motion.js +16 -2
- package/dist/motions.d.ts +4 -0
- package/dist/motions.js +45 -11
- package/dist/move.js +35 -12
- package/dist/numbers.d.ts +5 -1
- package/dist/numbers.js +4 -0
- package/dist/owner.d.ts +18 -2
- package/dist/owner.js +44 -3
- package/dist/pack.d.ts +1 -0
- package/dist/pack.js +3 -0
- package/dist/place.d.ts +8 -0
- package/dist/place.js +16 -8
- package/dist/placed.d.ts +19 -0
- package/dist/placed.js +54 -0
- package/dist/project.d.ts +5 -2
- package/dist/project.js +238 -34
- package/dist/pull.js +37 -8
- package/dist/qualify.d.ts +10 -0
- package/dist/qualify.js +60 -3
- package/dist/reading.d.ts +8 -1
- package/dist/reading.js +8 -1
- package/dist/record.d.ts +6 -0
- package/dist/record.js +2 -0
- package/dist/restored.d.ts +69 -17
- package/dist/restored.js +163 -50
- package/dist/revive.d.ts +6 -3
- package/dist/revive.js +11 -3
- package/dist/roster.js +2 -1
- package/dist/rows.js +15 -5
- package/dist/seek.d.ts +7 -2
- package/dist/seek.js +152 -43
- package/dist/shared.d.ts +5 -0
- package/dist/shared.js +5 -0
- package/dist/signals.d.ts +10 -0
- package/dist/signals.js +17 -0
- package/dist/spans.d.ts +5 -0
- package/dist/spans.js +75 -5
- package/dist/store.d.ts +13 -0
- package/dist/store.js +23 -0
- package/dist/strays.js +50 -2
- package/dist/tape.d.ts +29 -4
- package/dist/tape.js +49 -11
- package/dist/touch.js +3 -2
- package/dist/transport.d.ts +11 -2
- package/dist/transport.js +26 -10
- package/dist/types/arc.d.ts +55 -0
- package/dist/types/arc.js +1 -0
- package/dist/types/mix.d.ts +57 -14
- package/dist/types/patch.d.ts +5 -1
- package/dist/types/place.d.ts +11 -4
- package/dist/types/transport.d.ts +12 -3
- package/dist/types/voice.d.ts +72 -5
- package/dist/types/watch.d.ts +126 -0
- package/dist/types/watch.js +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/unpage.d.ts +6 -1
- package/dist/unpage.js +34 -2
- package/dist/voice.d.ts +93 -16
- package/dist/voice.js +60 -3
- package/dist/watch.d.ts +26 -0
- package/dist/watch.js +441 -0
- package/dist/weigh.d.ts +13 -2
- package/dist/weigh.js +10 -3
- package/docs/schema.html +106 -38
- package/package.json +10 -4
package/dist/departed.js
CHANGED
|
@@ -1,20 +1,34 @@
|
|
|
1
|
-
import { markOf } from './marks.js';
|
|
1
|
+
import { markOf, voiceMarks } from './marks.js';
|
|
2
2
|
import { none } from './voice.js';
|
|
3
|
-
|
|
3
|
+
/** A departed voice's marks: its five as fields, so it stays in fast mode, on no prototype. */
|
|
4
|
+
class Marks {
|
|
5
|
+
start = undefined;
|
|
6
|
+
in = undefined;
|
|
7
|
+
coast = undefined;
|
|
8
|
+
out = undefined;
|
|
9
|
+
end = undefined;
|
|
10
|
+
}
|
|
11
|
+
Object.setPrototypeOf(Marks.prototype, null);
|
|
4
12
|
/**
|
|
5
13
|
* The departed voices a query could still pick: for each name, tag and channel within a score and
|
|
6
14
|
* owner, the first and the last to leave. It grows with those, never with how many voices left.
|
|
7
15
|
*/
|
|
8
16
|
export class DepartedIndex {
|
|
9
|
-
|
|
17
|
+
scopes = new Map();
|
|
10
18
|
all = new Set();
|
|
11
19
|
add(mix, voice) {
|
|
12
20
|
// Nothing can be cued beside a voice whose owner has gone, so nothing will ask for it.
|
|
13
21
|
if (voice.owner?.state === 'done')
|
|
14
22
|
return;
|
|
15
|
-
const marks =
|
|
16
|
-
for (
|
|
23
|
+
const marks = new Marks();
|
|
24
|
+
for (let i = 0; i < voiceMarks.length; i++) {
|
|
25
|
+
const mark = voiceMarks[i];
|
|
17
26
|
marks[mark] = markOf(mix, voice, mark);
|
|
27
|
+
}
|
|
28
|
+
const inner = voice.spec.marks;
|
|
29
|
+
if (inner !== undefined)
|
|
30
|
+
for (const mark of Object.keys(inner))
|
|
31
|
+
marks[mark] = markOf(mix, voice, mark);
|
|
18
32
|
const d = {
|
|
19
33
|
id: voice.id,
|
|
20
34
|
score: voice.spec.score,
|
|
@@ -25,25 +39,19 @@ export class DepartedIndex {
|
|
|
25
39
|
marks,
|
|
26
40
|
held: 0,
|
|
27
41
|
};
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
for (const tag of d.tags)
|
|
33
|
-
keys.push(`${base}t\u0000${tag}`);
|
|
34
|
-
for (const ch of d.writes)
|
|
35
|
-
keys.push(`${base}c\u0000${String(ch)}`);
|
|
36
|
-
for (const key of keys) {
|
|
37
|
-
const end = this.ends.get(key);
|
|
38
|
-
if (end === undefined) {
|
|
39
|
-
this.ends.set(key, { first: d, last: d });
|
|
40
|
-
d.held += 2;
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
this.release(end.last);
|
|
44
|
-
end.last = d;
|
|
45
|
-
d.held++;
|
|
42
|
+
const scope = this.scopeOf(d.score, d.owner);
|
|
43
|
+
if (scope.whole === undefined) {
|
|
44
|
+
scope.whole = { first: d, last: d };
|
|
45
|
+
d.held += 2;
|
|
46
46
|
}
|
|
47
|
+
else
|
|
48
|
+
this.hold(scope.whole, d);
|
|
49
|
+
if (d.name !== undefined)
|
|
50
|
+
this.keep(scope.names, d.name, d);
|
|
51
|
+
for (let i = 0; i < d.tags.length; i++)
|
|
52
|
+
this.keep(scope.tags, d.tags[i], d);
|
|
53
|
+
for (let i = 0; i < d.writes.length; i++)
|
|
54
|
+
this.keep(scope.writes, d.writes[i], d);
|
|
47
55
|
if (d.held > 0)
|
|
48
56
|
this.all.add(d);
|
|
49
57
|
if (voice.holding !== null)
|
|
@@ -51,12 +59,51 @@ export class DepartedIndex {
|
|
|
51
59
|
}
|
|
52
60
|
/** Lets go of the voices an owner that has left held: nothing can be cued beside them again. */
|
|
53
61
|
dropOwner(owner) {
|
|
54
|
-
for (const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
for (const owners of this.scopes.values()) {
|
|
63
|
+
const scope = owners.get(owner);
|
|
64
|
+
if (scope === undefined)
|
|
65
|
+
continue;
|
|
66
|
+
owners.delete(owner);
|
|
67
|
+
if (scope.whole !== undefined)
|
|
68
|
+
this.forget(scope.whole);
|
|
69
|
+
for (const end of scope.names.values())
|
|
70
|
+
this.forget(end);
|
|
71
|
+
for (const end of scope.tags.values())
|
|
72
|
+
this.forget(end);
|
|
73
|
+
for (const end of scope.writes.values())
|
|
74
|
+
this.forget(end);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
scopeOf(score, owner) {
|
|
78
|
+
let owners = this.scopes.get(score);
|
|
79
|
+
if (owners === undefined) {
|
|
80
|
+
owners = new Map();
|
|
81
|
+
this.scopes.set(score, owners);
|
|
82
|
+
}
|
|
83
|
+
let scope = owners.get(owner);
|
|
84
|
+
if (scope === undefined) {
|
|
85
|
+
scope = { whole: undefined, names: new Map(), tags: new Map(), writes: new Map() };
|
|
86
|
+
owners.set(owner, scope);
|
|
87
|
+
}
|
|
88
|
+
return scope;
|
|
89
|
+
}
|
|
90
|
+
keep(ends, key, d) {
|
|
91
|
+
const end = ends.get(key);
|
|
92
|
+
if (end === undefined) {
|
|
93
|
+
ends.set(key, { first: d, last: d });
|
|
94
|
+
d.held += 2;
|
|
95
|
+
}
|
|
96
|
+
else
|
|
97
|
+
this.hold(end, d);
|
|
98
|
+
}
|
|
99
|
+
hold(end, d) {
|
|
100
|
+
this.release(end.last);
|
|
101
|
+
end.last = d;
|
|
102
|
+
d.held++;
|
|
103
|
+
}
|
|
104
|
+
forget(end) {
|
|
105
|
+
this.all.delete(end.first);
|
|
106
|
+
this.all.delete(end.last);
|
|
60
107
|
}
|
|
61
108
|
release(d) {
|
|
62
109
|
if (--d.held === 0)
|
package/dist/due.d.ts
CHANGED
|
@@ -10,3 +10,7 @@ export interface Due<I, O> {
|
|
|
10
10
|
export declare function schedule<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>): void;
|
|
11
11
|
/** Takes every current entry due by `now` off the queue, in cue order. */
|
|
12
12
|
export declare function popDue<I, O>(mix: Mixer<I, O>, now: number): Voice<I, O>[];
|
|
13
|
+
/** Puts voices in id order in place, those of one id left in the order they had; returns `list`. */
|
|
14
|
+
export declare function byId<V extends {
|
|
15
|
+
readonly id: number;
|
|
16
|
+
}>(list: V[]): V[];
|
package/dist/due.js
CHANGED
|
@@ -97,7 +97,20 @@ export function popDue(mix, now) {
|
|
|
97
97
|
out.push(top.voice);
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
return byId(out);
|
|
101
|
+
}
|
|
102
|
+
/** Past this many voices the built-in sort, which makes working arrays however few it is given. */
|
|
103
|
+
const SHORT = 8;
|
|
104
|
+
/** Puts voices in id order in place, those of one id left in the order they had; returns `list`. */
|
|
105
|
+
export function byId(list) {
|
|
106
|
+
if (list.length > SHORT)
|
|
107
|
+
return list.sort((a, b) => a.id - b.id);
|
|
108
|
+
for (let i = 1; i < list.length; i++) {
|
|
109
|
+
const v = list[i];
|
|
110
|
+
let k = i;
|
|
111
|
+
for (; k > 0 && list[k - 1].id > v.id; k--)
|
|
112
|
+
list[k] = list[k - 1];
|
|
113
|
+
list[k] = v;
|
|
114
|
+
}
|
|
115
|
+
return list;
|
|
103
116
|
}
|
package/dist/easing.d.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import type { Easing } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* A curve on 0..1: the function an easing stands for.
|
|
4
|
+
*
|
|
5
|
+
* @category patch
|
|
6
|
+
*/
|
|
2
7
|
export type Curve = (u: number) => number;
|
|
3
8
|
/**
|
|
4
9
|
* How fast a curve leaves 0, for bending a retarget to the speed the subject had: Infinity where
|
|
5
10
|
* the curve jumps or leaves vertically, so no finite bend can match it.
|
|
6
11
|
*/
|
|
7
12
|
export declare function startSlope(c: Curve): number;
|
|
8
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* The function an easing names: a CSS name, a `bezier` or `steps`, or a function, which comes back
|
|
15
|
+
* as it is. Call it once where a curve is built, not per frame; the same name or easing object
|
|
16
|
+
* gives the same function each time.
|
|
17
|
+
*
|
|
18
|
+
* @category patch
|
|
19
|
+
*/
|
|
9
20
|
export declare function curve(easing: Easing): Curve;
|
package/dist/easing.js
CHANGED
|
@@ -71,7 +71,13 @@ export function startSlope(c) {
|
|
|
71
71
|
// Past a thousandth of the way within a millionth of the time: a jump, not a slope.
|
|
72
72
|
return v > 1e-3 ? Number.POSITIVE_INFINITY : v / h;
|
|
73
73
|
}
|
|
74
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* The function an easing names: a CSS name, a `bezier` or `steps`, or a function, which comes back
|
|
76
|
+
* as it is. Call it once where a curve is built, not per frame; the same name or easing object
|
|
77
|
+
* gives the same function each time.
|
|
78
|
+
*
|
|
79
|
+
* @category patch
|
|
80
|
+
*/
|
|
75
81
|
export function curve(easing) {
|
|
76
82
|
if (typeof easing === 'function')
|
|
77
83
|
return easing;
|
package/dist/everyone.d.ts
CHANGED
|
@@ -10,9 +10,22 @@ import type { Subject, Voice } from './voice.js';
|
|
|
10
10
|
* and `shut` takes back after. About 32 B a subject where a record and its place in the voice's
|
|
11
11
|
* store took about 370 (bench/allocs.mjs on the `swap` row, 2026-10-09). It needs lanes for the
|
|
12
12
|
* numbers. What only shows later (a subject faded out of it alone, a fade at rest, state kept
|
|
13
|
-
* through `setting.keep`) turns it back with `unshare
|
|
13
|
+
* through `setting.keep`) turns it back with `unshare`, and so does playing `shareFrames` frames.
|
|
14
14
|
*/
|
|
15
15
|
export declare function shares<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* How many frames a voice plays sharing one record before it takes one a subject. Sharing saves a
|
|
18
|
+
* voice cued and gone within a few frames a record per subject, but costs a read more than a record
|
|
19
|
+
* of the subject's own: 0.13 ms a frame for one voice over 10,000 subjects (`glowbase`, teitou,
|
|
20
|
+
* 2026-10-21), against about 2 MB allocated once to stop. Settable for bench/same.mjs.
|
|
21
|
+
*/
|
|
22
|
+
export declare let shareFrames: number;
|
|
23
|
+
export declare function setShareFrames(n: number): void;
|
|
24
|
+
/**
|
|
25
|
+
* Turns back each voice that has played `shareFrames` frames sharing one record. Not a keys voice,
|
|
26
|
+
* whose record `open` never points at a subject, so it costs no read more.
|
|
27
|
+
*/
|
|
28
|
+
export declare function outgrow<I, O>(mix: Mixer<I, O>): void;
|
|
16
29
|
/**
|
|
17
30
|
* A sharing voice's one record, made when a chain first asks; null where its origin is still
|
|
18
31
|
* ahead, which a record of its own per subject follows as the clock moves, so it shares no more.
|
package/dist/everyone.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { headOf } from './chain.js';
|
|
1
2
|
import { numericOf } from './channels.js';
|
|
2
3
|
import { record } from './record.js';
|
|
3
4
|
/**
|
|
@@ -10,7 +11,7 @@ import { record } from './record.js';
|
|
|
10
11
|
* and `shut` takes back after. About 32 B a subject where a record and its place in the voice's
|
|
11
12
|
* store took about 370 (bench/allocs.mjs on the `swap` row, 2026-10-09). It needs lanes for the
|
|
12
13
|
* numbers. What only shows later (a subject faded out of it alone, a fade at rest, state kept
|
|
13
|
-
* through `setting.keep`) turns it back with `unshare
|
|
14
|
+
* through `setting.keep`) turns it back with `unshare`, and so does playing `shareFrames` frames.
|
|
14
15
|
*/
|
|
15
16
|
export function shares(mix, voice) {
|
|
16
17
|
const spec = voice.spec;
|
|
@@ -38,6 +39,30 @@ export function shares(mix, voice) {
|
|
|
38
39
|
return channel.rest !== undefined && numericOf(channel)?.op !== 'last';
|
|
39
40
|
}));
|
|
40
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* How many frames a voice plays sharing one record before it takes one a subject. Sharing saves a
|
|
44
|
+
* voice cued and gone within a few frames a record per subject, but costs a read more than a record
|
|
45
|
+
* of the subject's own: 0.13 ms a frame for one voice over 10,000 subjects (`glowbase`, teitou,
|
|
46
|
+
* 2026-10-21), against about 2 MB allocated once to stop. Settable for bench/same.mjs.
|
|
47
|
+
*/
|
|
48
|
+
export let shareFrames = 60;
|
|
49
|
+
export function setShareFrames(n) {
|
|
50
|
+
shareFrames = n;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Turns back each voice that has played `shareFrames` frames sharing one record. Not a keys voice,
|
|
54
|
+
* whose record `open` never points at a subject, so it costs no read more.
|
|
55
|
+
*/
|
|
56
|
+
export function outgrow(mix) {
|
|
57
|
+
const sharers = mix.sharers;
|
|
58
|
+
for (let i = sharers.length - 1; i >= 0; i--) {
|
|
59
|
+
const voice = sharers[i];
|
|
60
|
+
if (voice.built !== null || voice.state === 'pending' || voice.state === 'done')
|
|
61
|
+
continue;
|
|
62
|
+
if (++voice.shared >= shareFrames)
|
|
63
|
+
unshare(mix, voice);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
41
66
|
/**
|
|
42
67
|
* A sharing voice's one record, made when a chain first asks; null where its origin is still
|
|
43
68
|
* ahead, which a record of its own per subject follows as the clock moves, so it shares no more.
|
|
@@ -191,7 +216,7 @@ export function unshare(mix, voice, kept = false, keeper) {
|
|
|
191
216
|
const at = mix.sharers.indexOf(voice);
|
|
192
217
|
if (at >= 0)
|
|
193
218
|
mix.sharers.splice(at, 1);
|
|
194
|
-
const slot = kept ? (mix
|
|
219
|
+
const slot = kept ? (headOf(mix, keeper)?.slot ?? -1) : -1;
|
|
195
220
|
// A projection numbers no subject, so its copy's record is the keeper's without being asked.
|
|
196
221
|
if (all !== null && kept && (mix.projecting || sighted(voice, slot))) {
|
|
197
222
|
if (slot >= 0)
|
package/dist/fade.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Mixer } from './mixer.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Left, Parted, Parting, Voice } from './voice.js';
|
|
3
3
|
/** Starts one subject's ramp out of a voice, or takes it out at once for a ramp of 0. */
|
|
4
4
|
export declare function fadeSubject<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I, over?: number): void;
|
|
5
5
|
/** What a subject's own ramp out of a voice leaves of its weight this frame, 0..1. */
|
|
@@ -12,11 +12,18 @@ export declare function part<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject
|
|
|
12
12
|
/** Brings a subject faded out of a voice back, to be met afresh on its next probe. */
|
|
13
13
|
export declare function unpart<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I): void;
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* record of that or none.
|
|
15
|
+
* Keeps, under history, the record a subject leaves a voice with as it is let go of: for one faded
|
|
16
|
+
* out before the voice met it, a record saying it is not reached.
|
|
18
17
|
*/
|
|
19
|
-
export declare function
|
|
18
|
+
export declare function leaveVoice<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I): void;
|
|
19
|
+
/**
|
|
20
|
+
* Puts back into a voice's fades, or a copy's, those of the subjects that left it after frame `seq`
|
|
21
|
+
* as they stood then: `leave` keeps them, since a drop or a subject brought back forgets them.
|
|
22
|
+
*/
|
|
23
|
+
export declare function fadesAt<I>(fades: {
|
|
24
|
+
parts: Map<I, Parting> | null;
|
|
25
|
+
parted: Map<I, Parted> | null;
|
|
26
|
+
}, left: readonly Left<I>[], seq: number): void;
|
|
20
27
|
export declare function beginFade<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, opts: {
|
|
21
28
|
over?: number;
|
|
22
29
|
at?: 'rest';
|
package/dist/fade.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { changed } from './chain.js';
|
|
1
|
+
import { changed, headOf, unlink } from './chain.js';
|
|
2
2
|
import { envelope } from './clock.js';
|
|
3
3
|
import { schedule } from './due.js';
|
|
4
|
-
import { unshare } from './everyone.js';
|
|
4
|
+
import { unshare, unsight } from './everyone.js';
|
|
5
5
|
import { leave, noted, releasing } from './history.js';
|
|
6
|
-
import {
|
|
6
|
+
import { markOf } from './marks.js';
|
|
7
|
+
import { orphan, ownerReading } from './owner.js';
|
|
7
8
|
import { startOf } from './place.js';
|
|
8
9
|
import { scoreTouched } from './scored.js';
|
|
9
10
|
import { lingers, refit } from './spans.js';
|
|
@@ -38,11 +39,12 @@ export function parting(voice, subject, now) {
|
|
|
38
39
|
* its lane position and a motion patch's state for it, and reaches it no more.
|
|
39
40
|
*/
|
|
40
41
|
export function part(mix, voice, subject, at) {
|
|
42
|
+
const ramp = voice.parts?.get(subject);
|
|
41
43
|
voice.parts?.delete(subject);
|
|
42
44
|
if (voice.parts?.size === 0)
|
|
43
45
|
voice.parts = null;
|
|
44
46
|
voice.parted ??= new Map();
|
|
45
|
-
voice.parted.set(subject, { at, seq: mix.transport.seq });
|
|
47
|
+
voice.parted.set(subject, { at, seq: mix.transport.seq, ramp });
|
|
46
48
|
mix.parters.add(voice);
|
|
47
49
|
forgetIn(mix, voice, subject);
|
|
48
50
|
const motion = voice.motion;
|
|
@@ -55,31 +57,54 @@ export function unpart(mix, voice, subject) {
|
|
|
55
57
|
return;
|
|
56
58
|
// `forgetIn` keeps the subject's own record for a read back; without one, keep the voice's.
|
|
57
59
|
if (voice.subjects.get(subject) === undefined)
|
|
58
|
-
|
|
60
|
+
leaveVoice(mix, voice, subject);
|
|
61
|
+
// Left with its parting still on, which a seek back to before this puts back.
|
|
62
|
+
forgetIn(mix, voice, subject);
|
|
59
63
|
voice.parted.delete(subject);
|
|
60
64
|
if (voice.parted.size === 0)
|
|
61
65
|
voice.parted = null;
|
|
62
|
-
|
|
63
|
-
const slot = mix.chains.get(subject)?.slot ?? -1;
|
|
66
|
+
const slot = headOf(mix, subject)?.slot ?? -1;
|
|
64
67
|
if (slot >= 0)
|
|
65
68
|
mix.lanes?.rejoin(slot);
|
|
66
69
|
}
|
|
67
70
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* record of that or none.
|
|
71
|
+
* Keeps, under history, the record a subject leaves a voice with as it is let go of: for one faded
|
|
72
|
+
* out before the voice met it, a record saying it is not reached.
|
|
71
73
|
*/
|
|
72
|
-
export function
|
|
74
|
+
export function leaveVoice(mix, voice, subject) {
|
|
73
75
|
const held = voice.subjects.get(subject);
|
|
74
76
|
if (held !== undefined || voice.parted?.has(subject) !== true)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
leave(mix, voice, subject, held);
|
|
78
|
+
else
|
|
79
|
+
leave(mix, voice, subject, voice.unreached ?? mix.unreachedOf(voice, mix.now));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Puts back into a voice's fades, or a copy's, those of the subjects that left it after frame `seq`
|
|
83
|
+
* as they stood then: `leave` keeps them, since a drop or a subject brought back forgets them.
|
|
84
|
+
*/
|
|
85
|
+
export function fadesAt(fades, left, seq) {
|
|
86
|
+
for (const e of left) {
|
|
87
|
+
const parted = e.parted;
|
|
88
|
+
if (parted !== null && parted.seq <= seq) {
|
|
89
|
+
fades.parted ??= new Map();
|
|
90
|
+
fades.parted.set(e.subject, parted);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const ramp = e.ramp ?? parted?.ramp;
|
|
94
|
+
if (ramp === undefined || ramp === null || ramp.seq > seq)
|
|
95
|
+
continue;
|
|
96
|
+
fades.parts ??= new Map();
|
|
97
|
+
fades.parts.set(e.subject, ramp);
|
|
98
|
+
}
|
|
77
99
|
}
|
|
78
100
|
/** Drops a voice's record of a subject and relinks the subject's chain without it. */
|
|
79
101
|
function forgetIn(mix, voice, subject) {
|
|
80
|
-
const head = mix
|
|
81
|
-
if (head !== undefined && head.slot >= 0)
|
|
102
|
+
const head = headOf(mix, subject);
|
|
103
|
+
if (head !== undefined && head.slot >= 0) {
|
|
82
104
|
unreach(voice, head.slot, false);
|
|
105
|
+
// What the voice kept for the subject while it shared one record, which `weightOf` reads.
|
|
106
|
+
unsight(voice, head.slot);
|
|
107
|
+
}
|
|
83
108
|
const held = voice.subjects.get(subject);
|
|
84
109
|
if (held !== undefined) {
|
|
85
110
|
leave(mix, voice, subject, held);
|
|
@@ -93,8 +118,7 @@ function forgetIn(mix, voice, subject) {
|
|
|
93
118
|
}
|
|
94
119
|
if (head === undefined)
|
|
95
120
|
return;
|
|
96
|
-
head
|
|
97
|
-
mix.relinks++;
|
|
121
|
+
unlink(mix, head);
|
|
98
122
|
if (voice.laned && head.slot >= 0)
|
|
99
123
|
mix.lanes?.part(voice.id, head.slot);
|
|
100
124
|
mix.lanes?.refill();
|
|
@@ -153,12 +177,20 @@ export function beginRise(mix, voice, opts) {
|
|
|
153
177
|
const over = mix.reduced ? 0 : (opts.over ?? voice.fade.in ?? voice.fade.out ?? 0);
|
|
154
178
|
const left = out.rest || out.over === 0 ? 1 : 1 - (at - out.at) / out.over;
|
|
155
179
|
const from = left < 0 ? 0 : left > 1 ? 1 : left;
|
|
180
|
+
// A voice faded before its start waits for it again, as a seek back finds it.
|
|
181
|
+
const waiting = Number.isNaN(mix.now) ||
|
|
182
|
+
(voice.owner === null ? at : ownerReading(voice.owner, at)) < voice.start;
|
|
156
183
|
voice.out = null;
|
|
157
184
|
voice.outAt = Number.POSITIVE_INFINITY;
|
|
158
|
-
voice.state = 'live';
|
|
185
|
+
voice.state = waiting ? 'pending' : 'live';
|
|
159
186
|
voice.back = over > 0 && from < 1 ? { from, at, over } : null;
|
|
187
|
+
if (waiting)
|
|
188
|
+
changed(mix, voice);
|
|
160
189
|
noted(mix, voice);
|
|
161
|
-
|
|
190
|
+
if (waiting)
|
|
191
|
+
mix.lanes?.invalidate();
|
|
192
|
+
else
|
|
193
|
+
mix.lanes?.refill();
|
|
162
194
|
}
|
|
163
195
|
/**
|
|
164
196
|
* Fades a voice out from mix time `at`: ahead, it plays untouched until then and begins its fade
|
|
@@ -182,6 +214,11 @@ export function fadeAt(mix, voice, at, over) {
|
|
|
182
214
|
function asks(mix, motion, voice) {
|
|
183
215
|
return motion.owner === mix.owner && motion.ownerId === voice.id;
|
|
184
216
|
}
|
|
217
|
+
/** What a child leaving tells its owner of when the owner coasts. */
|
|
218
|
+
function leaving(mix, child) {
|
|
219
|
+
const h = child.owner.holding;
|
|
220
|
+
h.left(child.passed ? markOf(mix, child, 'coast') : undefined, child.doneSeq);
|
|
221
|
+
}
|
|
185
222
|
/** Removes a voice, recording that it left at `at`, default now. */
|
|
186
223
|
export function retire(mix, voice, at) {
|
|
187
224
|
if (voice.holding === null)
|
|
@@ -209,6 +246,7 @@ export function retire(mix, voice, at) {
|
|
|
209
246
|
retire(mix, child, voice.doneAt);
|
|
210
247
|
if (voice.owner !== null) {
|
|
211
248
|
const span = voice.owner;
|
|
249
|
+
leaving(mix, voice);
|
|
212
250
|
const owner = orphan(voice);
|
|
213
251
|
if (owner !== null && !lingers(owner, voice.doneAt))
|
|
214
252
|
retire(mix, owner, voice.doneAt);
|
package/dist/fill.js
CHANGED
|
@@ -6,7 +6,6 @@ import { open as openRecord, shut as shutRecord } from './everyone.js';
|
|
|
6
6
|
import { unband } from './fold.js';
|
|
7
7
|
import { foldLocus, gatherLocus } from './gather.js';
|
|
8
8
|
import { runKeys } from './keyfill.js';
|
|
9
|
-
import { frozenAt } from './lane.js';
|
|
10
9
|
import { reach } from './meet.js';
|
|
11
10
|
import { absent } from './numbers.js';
|
|
12
11
|
import { AT, NOTHING, readKeys, seg, segment, shifted } from './patch.js';
|
|
@@ -184,8 +183,6 @@ export function wake(lanes, lane) {
|
|
|
184
183
|
*/
|
|
185
184
|
export function signalled(voice, slot, rec) {
|
|
186
185
|
const arg = this.arg;
|
|
187
|
-
const elapsed = arg[1 /* Arg.ELAPSED */];
|
|
188
|
-
const pass = arg[3 /* Arg.PASS */];
|
|
189
186
|
const fade = arg[5 /* Arg.FADE */];
|
|
190
187
|
if (Number.isNaN(rec.probed)) {
|
|
191
188
|
this.late.push(slot);
|
|
@@ -196,8 +193,8 @@ export function signalled(voice, slot, rec) {
|
|
|
196
193
|
arg[0 /* Arg.WEIGHT */] = 0;
|
|
197
194
|
return true;
|
|
198
195
|
}
|
|
199
|
-
|
|
200
|
-
arg[0 /* Arg.WEIGHT */] = weighed(
|
|
196
|
+
this.host.signal(voice, subject, rec, slot);
|
|
197
|
+
arg[0 /* Arg.WEIGHT */] = weighed(arg[0 /* Arg.WEIGHT */], fade, voice.parts === null ? 1 : this.host.parting(voice, subject));
|
|
201
198
|
return true;
|
|
202
199
|
}
|
|
203
200
|
/** What a subject's own ramp out of a voice leaves of its weight: 1 with none. */
|
|
@@ -299,7 +296,19 @@ export function one(lane, p, slot) {
|
|
|
299
296
|
if (this.lately >= 0 && this.unread(slot))
|
|
300
297
|
return false;
|
|
301
298
|
const delay = data[o + 0 /* Row.DELAY */];
|
|
302
|
-
const
|
|
299
|
+
const raw = elapsedNow - delay;
|
|
300
|
+
// `frozenAt` written out: V8 leaves it out of line here, a call a subject.
|
|
301
|
+
const elapsed = !(raw >= 0)
|
|
302
|
+
? voice.freezesBefore
|
|
303
|
+
? 0
|
|
304
|
+
: Number.NaN
|
|
305
|
+
: raw > voice.span
|
|
306
|
+
? voice.hidesPast
|
|
307
|
+
? Number.NaN
|
|
308
|
+
: voice.freezesAfter
|
|
309
|
+
? voice.span
|
|
310
|
+
: raw
|
|
311
|
+
: raw;
|
|
303
312
|
if (!(elapsed >= 0)) {
|
|
304
313
|
data[o + 2 /* Row.WEIGHT */] = 0;
|
|
305
314
|
unband(lane.records[p]);
|
|
@@ -418,9 +427,7 @@ export function readKeyed(voice, phase, delta, scratch) {
|
|
|
418
427
|
*/
|
|
419
428
|
export function call(voice, chans, rec, slot) {
|
|
420
429
|
const arg = this.arg;
|
|
421
|
-
const elapsed = arg[1 /* Arg.ELAPSED */];
|
|
422
430
|
const phase = arg[2 /* Arg.PHASE */];
|
|
423
|
-
const pass = arg[3 /* Arg.PASS */];
|
|
424
431
|
const delay = arg[4 /* Arg.DELAY */];
|
|
425
432
|
const w = arg[0 /* Arg.WEIGHT */];
|
|
426
433
|
const host = this.host;
|
|
@@ -436,7 +443,7 @@ export function call(voice, chans, rec, slot) {
|
|
|
436
443
|
if (silent(voice, w))
|
|
437
444
|
return false;
|
|
438
445
|
const kept = reading.kept;
|
|
439
|
-
host.ready(voice, subject, rec
|
|
446
|
+
host.ready(voice, subject, rec);
|
|
440
447
|
if (this.keeps)
|
|
441
448
|
host.horizon(voice, delay);
|
|
442
449
|
else
|
package/dist/fit.d.ts
CHANGED
|
@@ -30,6 +30,11 @@ export interface Claim {
|
|
|
30
30
|
/** Whether it may jump to its end instead of playing. */
|
|
31
31
|
ballast: boolean;
|
|
32
32
|
priority: Strength;
|
|
33
|
+
/**
|
|
34
|
+
* Span ms from now before which it may not start, from its `earliest`; absent for none. Below 0
|
|
35
|
+
* where it has passed, which places the child there.
|
|
36
|
+
*/
|
|
37
|
+
floor?: number;
|
|
33
38
|
}
|
|
34
39
|
/**
|
|
35
40
|
* The span as a fit sees it.
|
|
@@ -75,7 +80,9 @@ export declare function shareOf(order: Order, stagger: number): number;
|
|
|
75
80
|
export declare function plain(span: SpanClaim, kids: readonly Claim[]): FitPlan;
|
|
76
81
|
/**
|
|
77
82
|
* Where a plan puts each child, span ms from now, and how long they take together. A playing child
|
|
78
|
-
* is where it is, at 0; each pending one starts its share through the one before it
|
|
83
|
+
* is where it is, at 0; each pending one starts its share through the one before it, or at its
|
|
84
|
+
* floor where that is later. A floor already past places a child before now, so it starts partway
|
|
85
|
+
* through; one without a floor starts no sooner than now.
|
|
79
86
|
*
|
|
80
87
|
* @category score
|
|
81
88
|
*/
|
package/dist/fit.js
CHANGED
|
@@ -17,7 +17,9 @@ export function plain(span, kids) {
|
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* Where a plan puts each child, span ms from now, and how long they take together. A playing child
|
|
20
|
-
* is where it is, at 0; each pending one starts its share through the one before it
|
|
20
|
+
* is where it is, at 0; each pending one starts its share through the one before it, or at its
|
|
21
|
+
* floor where that is later. A floor already past places a child before now, so it starts partway
|
|
22
|
+
* through; one without a floor starts no sooner than now.
|
|
21
23
|
*
|
|
22
24
|
* @category score
|
|
23
25
|
*/
|
|
@@ -29,9 +31,11 @@ export function layout(span, kids, plan) {
|
|
|
29
31
|
let prevLen = 0;
|
|
30
32
|
kids.forEach((k, i) => {
|
|
31
33
|
const len = plan.skip[i] ? 0 : k.natural / plan.rate[i];
|
|
32
|
-
const t = k.state === 'playing'
|
|
34
|
+
const t = k.state === 'playing'
|
|
33
35
|
? 0
|
|
34
|
-
:
|
|
36
|
+
: Math.max(i === 0
|
|
37
|
+
? Number.NEGATIVE_INFINITY
|
|
38
|
+
: prevAt + (k.overlap ? Math.min(plan.share, base) : base) * prevLen, k.floor ?? 0);
|
|
35
39
|
at.push(t);
|
|
36
40
|
length = Math.max(length, t + len);
|
|
37
41
|
prevAt = t;
|
package/dist/fold.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Mixer } from './mixer.js';
|
|
2
2
|
import type { Channel } from './types.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type Subject, type Voice } from './voice.js';
|
|
4
4
|
export type Key<O> = keyof O & string;
|
|
5
5
|
export declare function near(a: unknown, b: unknown): boolean;
|
|
6
6
|
/**
|