@msb235/blits 0.6.0 → 0.7.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 +136 -0
- package/README.md +31 -7
- package/dist/bare.js +7 -1
- package/dist/book.d.ts +7 -0
- package/dist/book.js +35 -4
- package/dist/chain.d.ts +2 -1
- package/dist/chain.js +7 -3
- package/dist/channels.d.ts +12 -19
- package/dist/channels.js +18 -118
- package/dist/clock.d.ts +9 -2
- package/dist/clock.js +10 -2
- package/dist/closed.d.ts +15 -0
- package/dist/closed.js +144 -0
- package/dist/color.d.ts +61 -0
- package/dist/color.js +229 -0
- package/dist/columns.js +2 -1
- package/dist/crowd.d.ts +1 -1
- package/dist/crowd.js +4 -1
- package/dist/cue.d.ts +2 -0
- package/dist/cue.js +78 -27
- package/dist/due.js +3 -0
- package/dist/fade.d.ts +13 -0
- package/dist/fade.js +55 -4
- package/dist/fill.d.ts +1 -1
- package/dist/fill.js +15 -8
- package/dist/fit.d.ts +144 -0
- package/dist/fit.js +181 -0
- package/dist/fold.d.ts +3 -3
- package/dist/fold.js +18 -11
- package/dist/gather.d.ts +6 -0
- package/dist/gather.js +18 -0
- package/dist/handle.d.ts +27 -2
- package/dist/handle.js +60 -11
- package/dist/held.js +4 -0
- package/dist/history.d.ts +15 -2
- package/dist/history.js +54 -9
- package/dist/hosts.d.ts +2 -2
- package/dist/hosts.js +18 -5
- package/dist/index.d.ts +8 -2
- package/dist/index.js +5 -1
- package/dist/lane.d.ts +1 -1
- package/dist/lanes.d.ts +32 -9
- package/dist/lanes.js +53 -24
- package/dist/locus.d.ts +3 -3
- package/dist/locus.js +1 -1
- package/dist/marks.d.ts +3 -0
- package/dist/marks.js +80 -12
- package/dist/meet.d.ts +3 -2
- package/dist/meet.js +15 -3
- package/dist/mixer.d.ts +52 -36
- package/dist/mixer.js +148 -86
- package/dist/motion.d.ts +2 -199
- package/dist/motion.js +2 -652
- package/dist/motions.d.ts +266 -0
- package/dist/motions.js +724 -0
- package/dist/move.d.ts +1 -1
- package/dist/move.js +23 -7
- package/dist/owner.d.ts +1 -1
- package/dist/owner.js +3 -3
- package/dist/pace.d.ts +2 -0
- package/dist/pace.js +8 -0
- package/dist/pack.d.ts +31 -0
- package/dist/pack.js +65 -0
- package/dist/paging.d.ts +63 -0
- package/dist/paging.js +110 -0
- package/dist/patch.d.ts +3 -0
- package/dist/patch.js +2 -0
- package/dist/place.js +74 -25
- package/dist/project.d.ts +7 -4
- package/dist/project.js +117 -78
- package/dist/pull.js +4 -3
- package/dist/qualify.js +4 -2
- package/dist/revive.d.ts +58 -0
- package/dist/revive.js +158 -0
- package/dist/sample.d.ts +1 -1
- package/dist/sample.js +6 -1
- package/dist/seek.d.ts +3 -0
- package/dist/seek.js +311 -0
- package/dist/spans.d.ts +29 -0
- package/dist/spans.js +165 -0
- package/dist/strays.d.ts +1 -1
- package/dist/strays.js +21 -0
- package/dist/tape.d.ts +21 -0
- package/dist/tape.js +65 -0
- package/dist/ticker.d.ts +28 -3
- package/dist/ticker.js +3 -0
- package/dist/transport.d.ts +121 -0
- package/dist/transport.js +271 -0
- package/dist/types.d.ts +404 -38
- package/dist/unpage.d.ts +8 -0
- package/dist/unpage.js +163 -0
- package/dist/voice.d.ts +73 -7
- package/dist/voice.js +116 -4
- package/dist/weigh.js +2 -2
- package/docs/schema.html +120 -31
- package/package.json +2 -1
package/dist/unpage.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { unpackHeld } from './pack.js';
|
|
2
|
+
import { HistoryMiss } from './paging.js';
|
|
3
|
+
import { reviveVoice } from './revive.js';
|
|
4
|
+
/**
|
|
5
|
+
* Makes memory reach mix time `t` for a seek or read there, from what `prepare` loaded; true where
|
|
6
|
+
* it took records from the store. Throws before anything moves where it cannot.
|
|
7
|
+
*/
|
|
8
|
+
export function cover(transport, t) {
|
|
9
|
+
if (transport.reaches(t))
|
|
10
|
+
return false;
|
|
11
|
+
const pager = transport.pager;
|
|
12
|
+
if (pager === null)
|
|
13
|
+
throw new Error(`blits: ${t} is older than this mix's history reaches`);
|
|
14
|
+
const loaded = pager.loaded;
|
|
15
|
+
if (loaded === null || loaded.t > t) {
|
|
16
|
+
const last = pager.last;
|
|
17
|
+
throw new HistoryMiss(last?.mix ?? '', last?.stream ?? 'history', t);
|
|
18
|
+
}
|
|
19
|
+
for (const m of [...transport.members, ...transport.dropped.map((d) => d.mix)]) {
|
|
20
|
+
const name = m.name ?? '';
|
|
21
|
+
const id = m.outs?.missing(loaded.t, (v) => loaded.records.some((r) => r.stream === 'voice' && r.voice === v && r.mix === name));
|
|
22
|
+
if (id !== undefined)
|
|
23
|
+
throw new HistoryMiss(name, 'voice', t);
|
|
24
|
+
}
|
|
25
|
+
pager.loaded = null;
|
|
26
|
+
unpage(transport, loaded.records);
|
|
27
|
+
transport.floor = loaded.t;
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
/** A seek back to `t`: what the store and `prepare` hold after it is from a future the tape makes again. */
|
|
31
|
+
export function cut(transport, t, unpaged) {
|
|
32
|
+
const pager = transport.pager;
|
|
33
|
+
if (pager === null)
|
|
34
|
+
return;
|
|
35
|
+
if (unpaged)
|
|
36
|
+
pager.store.cut(t);
|
|
37
|
+
const loaded = pager.loaded;
|
|
38
|
+
if (loaded !== null)
|
|
39
|
+
loaded.records = loaded.records.filter((r) => r.at <= t);
|
|
40
|
+
}
|
|
41
|
+
/** Puts records a store gave back into memory ahead of what it still holds, as if never paged. */
|
|
42
|
+
function unpage(transport, records) {
|
|
43
|
+
const mixes = new Map();
|
|
44
|
+
for (const m of [...transport.members, ...transport.dropped.map((d) => d.mix)])
|
|
45
|
+
mixes.set(m.name ?? '', m);
|
|
46
|
+
const by = new Map();
|
|
47
|
+
for (const r of [...records].sort((a, b) => a.at - b.at)) {
|
|
48
|
+
const m = mixes.get(r.mix);
|
|
49
|
+
if (m === undefined)
|
|
50
|
+
continue;
|
|
51
|
+
const list = by.get(m);
|
|
52
|
+
if (list === undefined)
|
|
53
|
+
by.set(m, [r]);
|
|
54
|
+
else
|
|
55
|
+
list.push(r);
|
|
56
|
+
}
|
|
57
|
+
for (const [m, list] of by)
|
|
58
|
+
unpageMix(m, list);
|
|
59
|
+
}
|
|
60
|
+
function unpageMix(mix, records) {
|
|
61
|
+
const voices = new Map();
|
|
62
|
+
for (const v of [...mix.cued, ...mix.gone])
|
|
63
|
+
voices.set(v.id, v);
|
|
64
|
+
for (const r of records)
|
|
65
|
+
if (r.stream === 'voice' && r.voice !== undefined && !voices.has(r.voice)) {
|
|
66
|
+
const v = reviveVoice(mix, r.voice, r.data);
|
|
67
|
+
voices.set(v.id, v);
|
|
68
|
+
mix.gone.push(v);
|
|
69
|
+
}
|
|
70
|
+
const host = records.filter((r) => r.stream === 'host');
|
|
71
|
+
mix.hostLog = before(host.map((r) => ({ at: r.at, fields: r.data })), mix.hostLog);
|
|
72
|
+
const byVoice = new Map();
|
|
73
|
+
for (const r of records) {
|
|
74
|
+
if (r.voice === undefined)
|
|
75
|
+
continue;
|
|
76
|
+
const list = byVoice.get(r.voice);
|
|
77
|
+
if (list === undefined)
|
|
78
|
+
byVoice.set(r.voice, [r]);
|
|
79
|
+
else
|
|
80
|
+
list.push(r);
|
|
81
|
+
}
|
|
82
|
+
const keys = mix.keys;
|
|
83
|
+
for (const [id, list] of byVoice) {
|
|
84
|
+
const voice = voices.get(id);
|
|
85
|
+
if (voice !== undefined)
|
|
86
|
+
unpageVoice(voice, list, keys);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function unpageVoice(voice, records, keys) {
|
|
90
|
+
const of = (stream) => records.filter((r) => r.stream === stream);
|
|
91
|
+
if (voice.log !== null)
|
|
92
|
+
voice.log = before(of('controls').map((r) => r.data), voice.log);
|
|
93
|
+
const left = voice.left ?? [];
|
|
94
|
+
for (const r of of('left')) {
|
|
95
|
+
const subject = keys.subject(r.subject);
|
|
96
|
+
if (!left.some((e) => e.at === r.at && Object.is(e.subject, subject)))
|
|
97
|
+
left.push({ subject, at: r.at, held: unpackHeld(voice, r.data) });
|
|
98
|
+
}
|
|
99
|
+
left.sort((a, b) => a.at - b.at);
|
|
100
|
+
voice.left = left.length === 0 ? null : left;
|
|
101
|
+
for (const [key, list] of bySubject(of('snap'))) {
|
|
102
|
+
const subject = keys.subject(key);
|
|
103
|
+
for (const [held, snaps] of lives(voice, subject, list))
|
|
104
|
+
held.snaps = before(snaps.map((r) => ({ at: r.at, held: unpackHeld(voice, r.data) })), held.snaps ?? []);
|
|
105
|
+
}
|
|
106
|
+
for (const [key, list] of bySubject(of('input'))) {
|
|
107
|
+
const subject = keys.subject(key);
|
|
108
|
+
for (const [held, inputs] of lives(voice, subject, list))
|
|
109
|
+
held.inputs = before(inputs.map((r) => ({ at: r.at, value: r.data })), held.inputs ?? []);
|
|
110
|
+
}
|
|
111
|
+
const motion = voice.motion;
|
|
112
|
+
if (motion === undefined)
|
|
113
|
+
return;
|
|
114
|
+
for (const r of of('released'))
|
|
115
|
+
motion.reclaim(keys.subject(r.subject), r.at, r.data);
|
|
116
|
+
for (const [key, list] of bySubject(of('stretch')))
|
|
117
|
+
motion.restretch(keys.subject(key), list);
|
|
118
|
+
}
|
|
119
|
+
function bySubject(records) {
|
|
120
|
+
const out = new Map();
|
|
121
|
+
for (const r of records) {
|
|
122
|
+
const list = out.get(r.subject);
|
|
123
|
+
if (list === undefined)
|
|
124
|
+
out.set(r.subject, [r]);
|
|
125
|
+
else
|
|
126
|
+
list.push(r);
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* A subject's records in a voice, each with the paged entries from its life: one ends when the
|
|
132
|
+
* subject leaves the voice, and the record it has now began after its last leaving.
|
|
133
|
+
*/
|
|
134
|
+
function lives(voice, subject, records) {
|
|
135
|
+
const left = (voice.left ?? []).filter((e) => Object.is(e.subject, subject));
|
|
136
|
+
const out = [];
|
|
137
|
+
let from = Number.NEGATIVE_INFINITY;
|
|
138
|
+
for (const e of left) {
|
|
139
|
+
out.push([e.held, records.filter((r) => r.at > from && r.at <= e.at)]);
|
|
140
|
+
from = e.at;
|
|
141
|
+
}
|
|
142
|
+
const live = voice.subjects.get(subject);
|
|
143
|
+
if (live?.reaches)
|
|
144
|
+
out.push([live, records.filter((r) => r.at > from)]);
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* `loaded`, in time order, ahead of `list`: those older than its first entry, the last of any that
|
|
149
|
+
* share a time, which is the one a read finds.
|
|
150
|
+
*/
|
|
151
|
+
function before(loaded, list) {
|
|
152
|
+
const first = list[0]?.at ?? Number.POSITIVE_INFINITY;
|
|
153
|
+
const out = [];
|
|
154
|
+
for (const e of loaded) {
|
|
155
|
+
if (!(e.at < first))
|
|
156
|
+
continue;
|
|
157
|
+
if (out.length > 0 && out[out.length - 1].at === e.at)
|
|
158
|
+
out[out.length - 1] = e;
|
|
159
|
+
else
|
|
160
|
+
out.push(e);
|
|
161
|
+
}
|
|
162
|
+
return out.length === 0 ? list : [...out, ...list];
|
|
163
|
+
}
|
package/dist/voice.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { Blend } from './blend.js';
|
|
2
2
|
import { type Clock } from './clock.js';
|
|
3
3
|
import { type Curve } from './easing.js';
|
|
4
|
-
import {
|
|
4
|
+
import type { Motions } from './motions.js';
|
|
5
5
|
import { Named } from './named.js';
|
|
6
6
|
import { Holding } from './owner.js';
|
|
7
7
|
import { type Built, intosOf, type Scratch } from './patch.js';
|
|
8
|
+
import type { Fitting } from './spans.js';
|
|
8
9
|
import { Store } from './store.js';
|
|
9
|
-
import type { Channel, FadeSpec, Handle, Patch, Setting, VoiceSpec } from './types.js';
|
|
10
|
+
import type { Anchor, Channel, FadeSpec, Handle, Patch, Setting, VoiceSpec } from './types.js';
|
|
10
11
|
export declare const none: readonly string[];
|
|
11
12
|
/** Everything one voice holds for one subject, chained through the next voice that reaches it. */
|
|
12
13
|
export interface Subject<S> {
|
|
@@ -23,7 +24,7 @@ export interface Subject<S> {
|
|
|
23
24
|
/** Left at rest during a handover, so it stops contributing. */
|
|
24
25
|
rested: boolean;
|
|
25
26
|
/**
|
|
26
|
-
* Per written channel, whether a rest-less
|
|
27
|
+
* Per written channel, whether a rest-less contribution is on: 0 unknown, 1 on, 2 off; null until
|
|
27
28
|
* a rest-less channel asks.
|
|
28
29
|
*/
|
|
29
30
|
bands: Uint8Array | null;
|
|
@@ -46,6 +47,8 @@ export interface Subject<S> {
|
|
|
46
47
|
slope?: Record<string, unknown>;
|
|
47
48
|
/** What `setting.keep` holds for this voice and subject, by owner; null until it holds any. */
|
|
48
49
|
kept: Map<object, unknown> | null;
|
|
50
|
+
/** Kept state a history store gave back, which each owner takes in turn on its first keep. */
|
|
51
|
+
unkept?: unknown[];
|
|
49
52
|
/** Under `history`, copies of this record by the mix time they were taken, oldest first. */
|
|
50
53
|
snaps?: {
|
|
51
54
|
at: number;
|
|
@@ -70,7 +73,7 @@ export interface Subject<S> {
|
|
|
70
73
|
next: Subject<unknown> | null;
|
|
71
74
|
/**
|
|
72
75
|
* On the first record, which the mix keeps per subject so a probe makes one lookup: the `version`
|
|
73
|
-
* the chain was linked at, and per locus, by channel slot, whether a rest-less
|
|
76
|
+
* the chain was linked at, and per locus, by channel slot, whether a rest-less contribution is on:
|
|
74
77
|
* 0 never decided, 1 on, 2 off.
|
|
75
78
|
*/
|
|
76
79
|
version: number;
|
|
@@ -83,9 +86,13 @@ export interface Controls extends Clock {
|
|
|
83
86
|
at: number;
|
|
84
87
|
weight: number;
|
|
85
88
|
out: Ramp | null;
|
|
89
|
+
back: Rise | null;
|
|
86
90
|
/** Where its anchors had placed it then: start, and the start of an anchored fade out. */
|
|
87
91
|
start: number;
|
|
88
92
|
outAt: number;
|
|
93
|
+
/** A fade the host set for `outAt`: its ramp, NaN for the voice's own `fade.out`. */
|
|
94
|
+
outOver: number;
|
|
95
|
+
outSet: boolean;
|
|
89
96
|
/** Made by a sync, so it shows in that frame; a host's change between frames shows from the next. */
|
|
90
97
|
sync: boolean;
|
|
91
98
|
}
|
|
@@ -96,6 +103,15 @@ export interface Ramp {
|
|
|
96
103
|
rest: boolean;
|
|
97
104
|
deadline?: number;
|
|
98
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* A climb back from a fade out that was turned around: from `from`, the place along the fade's
|
|
108
|
+
* curve it had reached, at mix time `at`, rising at the whole curve per `over` ms.
|
|
109
|
+
*/
|
|
110
|
+
export interface Rise {
|
|
111
|
+
from: number;
|
|
112
|
+
at: number;
|
|
113
|
+
over: number;
|
|
114
|
+
}
|
|
99
115
|
export declare class Voice<I, O> {
|
|
100
116
|
readonly id: number;
|
|
101
117
|
readonly spec: VoiceSpec<I, O>;
|
|
@@ -120,7 +136,9 @@ export declare class Voice<I, O> {
|
|
|
120
136
|
over: number;
|
|
121
137
|
} | null;
|
|
122
138
|
out: Ramp | null;
|
|
123
|
-
|
|
139
|
+
/** A fade out turned around, still climbing back; null once back in, or never turned. */
|
|
140
|
+
back: Rise | null;
|
|
141
|
+
subjects: Store<I, Subject<unknown>>;
|
|
124
142
|
/** The subjects its spec names, or null where it names none. */
|
|
125
143
|
readonly named: Named<I> | null;
|
|
126
144
|
/** Kit slot of each channel the patch writes, in `writes` order. */
|
|
@@ -151,6 +169,15 @@ export declare class Voice<I, O> {
|
|
|
151
169
|
}> | null;
|
|
152
170
|
/** Subjects faded out of this voice, by the mix time each left at; null while none have. */
|
|
153
171
|
parted: Map<I, number> | null;
|
|
172
|
+
/**
|
|
173
|
+
* Under history, the records of subjects that left this voice, by the mix time each left at,
|
|
174
|
+
* while a seek or a read back may reach them; null while none have.
|
|
175
|
+
*/
|
|
176
|
+
left: {
|
|
177
|
+
subject: I;
|
|
178
|
+
at: number;
|
|
179
|
+
held: Subject<unknown>;
|
|
180
|
+
}[] | null;
|
|
154
181
|
readonly ease: Curve | undefined;
|
|
155
182
|
/** How many passes its `loop` plays, worked out once. */
|
|
156
183
|
readonly passes: number;
|
|
@@ -181,10 +208,20 @@ export declare class Voice<I, O> {
|
|
|
181
208
|
doneAt: number;
|
|
182
209
|
/** Under `history`, its controls after each change, oldest first. */
|
|
183
210
|
log: Controls[] | null;
|
|
211
|
+
/** Under `history`, its controls as `cue` left them, which a voice parked by a seek takes back. */
|
|
212
|
+
first: Controls | null;
|
|
184
213
|
/** Where an anchored `out` or `end` puts its fade's start, mix time; Infinity until known. */
|
|
185
214
|
outAt: number;
|
|
215
|
+
/** The ramp of a fade the host set for `outAt` with `fade({ at })`; NaN for the voice's own. */
|
|
216
|
+
outOver: number;
|
|
217
|
+
/** Whether the host set `outAt` with `fade({ at })`, which no anchor moves. */
|
|
218
|
+
outSet: boolean;
|
|
219
|
+
/** The host time a pinned start was given at, NaN for none: a seek back pins it again. */
|
|
220
|
+
pinned: number;
|
|
186
221
|
/** Whether its start is still to be fixed by an anchor, so it waits pending. */
|
|
187
222
|
placing: boolean;
|
|
223
|
+
/** What each member of a join last answered, kept for once its target has left. */
|
|
224
|
+
answers: Map<Anchor, number> | null;
|
|
188
225
|
/** The record `setting.keep` writes to: the one its patch or signal is being called for. */
|
|
189
226
|
keepOn: Subject<unknown> | null;
|
|
190
227
|
/** What `setting.keep` holds when called before any record is. */
|
|
@@ -200,14 +237,20 @@ export declare class Voice<I, O> {
|
|
|
200
237
|
unreachedBits: Uint32Array | null;
|
|
201
238
|
/** The handle `cue` returned, which `voices` hands back too. */
|
|
202
239
|
handle: Handle<I> | null;
|
|
240
|
+
/** With a history store, the key of every subject it has a record of; null without one. */
|
|
241
|
+
keyed: Set<string | number | undefined> | null;
|
|
203
242
|
/** For an owner, the voices it holds; null for any other voice. */
|
|
204
243
|
holding: Holding<Voice<I, O>> | null;
|
|
244
|
+
/** For a span, its budget and how its children were last fitted; null for any other voice. */
|
|
245
|
+
fitting: Fitting | null;
|
|
205
246
|
/**
|
|
206
247
|
* `done` and `played`, made when first asked for, already settled if the voice is: most hosts
|
|
207
248
|
* never await either, and a mix may hold tens of thousands of voices.
|
|
208
249
|
*/
|
|
209
250
|
private finished;
|
|
210
251
|
private playedAs;
|
|
252
|
+
/** The mix time `played` was settled at, so a seek back to before it can open it again. */
|
|
253
|
+
private playedAt;
|
|
211
254
|
private donePromise;
|
|
212
255
|
private doneSettle;
|
|
213
256
|
private playedPromise;
|
|
@@ -218,8 +261,31 @@ export declare class Voice<I, O> {
|
|
|
218
261
|
get played(): Promise<boolean>;
|
|
219
262
|
/** The voice has left: `done` resolves. */
|
|
220
263
|
resolve(): void;
|
|
221
|
-
/**
|
|
222
|
-
|
|
264
|
+
/**
|
|
265
|
+
* Its finite loop ended (true) or it left first (false), whichever comes first, noticed at mix
|
|
266
|
+
* time `at`: `played` resolves.
|
|
267
|
+
*/
|
|
268
|
+
play(played: boolean, at: number): void;
|
|
269
|
+
/** How `played` settled, and the mix time it did; undefined while it has not. */
|
|
270
|
+
get settled(): {
|
|
271
|
+
played: boolean | undefined;
|
|
272
|
+
at: number;
|
|
273
|
+
};
|
|
274
|
+
/** Whether `played` settled true, which its owner counted. */
|
|
275
|
+
get passed(): boolean;
|
|
276
|
+
/** Whether `played` settled false: it left before its last pass ended. */
|
|
277
|
+
get unplayed(): boolean;
|
|
278
|
+
/**
|
|
279
|
+
* A seek back to mix time `t`: `done` and `played`, where they settled after it, start over with
|
|
280
|
+
* fresh promises. True where `played` had settled true, which its owner counted.
|
|
281
|
+
*/
|
|
282
|
+
reopen(t: number): boolean;
|
|
283
|
+
/**
|
|
284
|
+
* A seek back to before its cue: out of the mix, as `cue` left it, until the mix plays the cue
|
|
285
|
+
* again. What it played since is forgotten, and `done` and `played` start over where they had
|
|
286
|
+
* settled.
|
|
287
|
+
*/
|
|
288
|
+
park(): void;
|
|
223
289
|
constructor(id: number, spec: VoiceSpec<I, O>, patch: Patch<I, O, unknown>, fade: FadeSpec, now: number, start: number, slotOf: Map<string, number>, channels: readonly Channel<unknown>[], host: unknown, send: (event: unknown) => void,
|
|
224
290
|
/** The owner whose clock it runs on, its start included; null for one on the mix clock. */
|
|
225
291
|
owner: Voice<I, O> | null);
|
package/dist/voice.js
CHANGED
|
@@ -24,7 +24,10 @@ function keeping(voice) {
|
|
|
24
24
|
}
|
|
25
25
|
if (kept.has(owner))
|
|
26
26
|
return kept.get(owner);
|
|
27
|
-
const
|
|
27
|
+
const unkept = held?.unkept;
|
|
28
|
+
const made = unkept === undefined ? init() : unkept.shift();
|
|
29
|
+
if (unkept?.length === 0 && held !== null)
|
|
30
|
+
held.unkept = undefined;
|
|
28
31
|
kept.set(owner, made);
|
|
29
32
|
reading.kept++;
|
|
30
33
|
return made;
|
|
@@ -93,6 +96,8 @@ export class Voice {
|
|
|
93
96
|
anchorElapsed = 0;
|
|
94
97
|
ramp = null;
|
|
95
98
|
out = null;
|
|
99
|
+
/** A fade out turned around, still climbing back; null once back in, or never turned. */
|
|
100
|
+
back = null;
|
|
96
101
|
subjects = new Store();
|
|
97
102
|
/** The subjects its spec names, or null where it names none. */
|
|
98
103
|
named;
|
|
@@ -121,6 +126,11 @@ export class Voice {
|
|
|
121
126
|
parts = null;
|
|
122
127
|
/** Subjects faded out of this voice, by the mix time each left at; null while none have. */
|
|
123
128
|
parted = null;
|
|
129
|
+
/**
|
|
130
|
+
* Under history, the records of subjects that left this voice, by the mix time each left at,
|
|
131
|
+
* while a seek or a read back may reach them; null while none have.
|
|
132
|
+
*/
|
|
133
|
+
left = null;
|
|
124
134
|
ease;
|
|
125
135
|
/** How many passes its `loop` plays, worked out once. */
|
|
126
136
|
passes;
|
|
@@ -151,10 +161,20 @@ export class Voice {
|
|
|
151
161
|
doneAt = Number.POSITIVE_INFINITY;
|
|
152
162
|
/** Under `history`, its controls after each change, oldest first. */
|
|
153
163
|
log = null;
|
|
164
|
+
/** Under `history`, its controls as `cue` left them, which a voice parked by a seek takes back. */
|
|
165
|
+
first = null;
|
|
154
166
|
/** Where an anchored `out` or `end` puts its fade's start, mix time; Infinity until known. */
|
|
155
167
|
outAt = Number.POSITIVE_INFINITY;
|
|
168
|
+
/** The ramp of a fade the host set for `outAt` with `fade({ at })`; NaN for the voice's own. */
|
|
169
|
+
outOver = Number.NaN;
|
|
170
|
+
/** Whether the host set `outAt` with `fade({ at })`, which no anchor moves. */
|
|
171
|
+
outSet = false;
|
|
172
|
+
/** The host time a pinned start was given at, NaN for none: a seek back pins it again. */
|
|
173
|
+
pinned = Number.NaN;
|
|
156
174
|
/** Whether its start is still to be fixed by an anchor, so it waits pending. */
|
|
157
175
|
placing = false;
|
|
176
|
+
/** What each member of a join last answered, kept for once its target has left. */
|
|
177
|
+
answers = null;
|
|
158
178
|
/** The record `setting.keep` writes to: the one its patch or signal is being called for. */
|
|
159
179
|
keepOn = null;
|
|
160
180
|
/** What `setting.keep` holds when called before any record is. */
|
|
@@ -167,14 +187,20 @@ export class Voice {
|
|
|
167
187
|
unreachedBits = null;
|
|
168
188
|
/** The handle `cue` returned, which `voices` hands back too. */
|
|
169
189
|
handle = null;
|
|
190
|
+
/** With a history store, the key of every subject it has a record of; null without one. */
|
|
191
|
+
keyed = null;
|
|
170
192
|
/** For an owner, the voices it holds; null for any other voice. */
|
|
171
193
|
holding;
|
|
194
|
+
/** For a span, its budget and how its children were last fitted; null for any other voice. */
|
|
195
|
+
fitting = null;
|
|
172
196
|
/**
|
|
173
197
|
* `done` and `played`, made when first asked for, already settled if the voice is: most hosts
|
|
174
198
|
* never await either, and a mix may hold tens of thousands of voices.
|
|
175
199
|
*/
|
|
176
200
|
finished = false;
|
|
177
201
|
playedAs = undefined;
|
|
202
|
+
/** The mix time `played` was settled at, so a seek back to before it can open it again. */
|
|
203
|
+
playedAt = Number.NaN;
|
|
178
204
|
donePromise = null;
|
|
179
205
|
doneSettle = null;
|
|
180
206
|
playedPromise = null;
|
|
@@ -209,14 +235,94 @@ export class Voice {
|
|
|
209
235
|
this.finished = true;
|
|
210
236
|
this.doneSettle?.();
|
|
211
237
|
}
|
|
212
|
-
/**
|
|
213
|
-
|
|
238
|
+
/**
|
|
239
|
+
* Its finite loop ended (true) or it left first (false), whichever comes first, noticed at mix
|
|
240
|
+
* time `at`: `played` resolves.
|
|
241
|
+
*/
|
|
242
|
+
play(played, at) {
|
|
214
243
|
if (this.quiet || this.playedAs !== undefined)
|
|
215
244
|
return;
|
|
216
245
|
this.playedAs = played;
|
|
246
|
+
this.playedAt = at;
|
|
217
247
|
this.playedSettle?.(played);
|
|
218
248
|
if (played && this.owner !== null)
|
|
219
|
-
childPlayed(this.owner);
|
|
249
|
+
childPlayed(this.owner, at);
|
|
250
|
+
}
|
|
251
|
+
/** How `played` settled, and the mix time it did; undefined while it has not. */
|
|
252
|
+
get settled() {
|
|
253
|
+
return { played: this.playedAs, at: this.playedAt };
|
|
254
|
+
}
|
|
255
|
+
/** Whether `played` settled true, which its owner counted. */
|
|
256
|
+
get passed() {
|
|
257
|
+
return this.playedAs === true;
|
|
258
|
+
}
|
|
259
|
+
/** Whether `played` settled false: it left before its last pass ended. */
|
|
260
|
+
get unplayed() {
|
|
261
|
+
return this.playedAs === false;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* A seek back to mix time `t`: `done` and `played`, where they settled after it, start over with
|
|
265
|
+
* fresh promises. True where `played` had settled true, which its owner counted.
|
|
266
|
+
*/
|
|
267
|
+
reopen(t) {
|
|
268
|
+
let unplayed = false;
|
|
269
|
+
if (this.playedAs !== undefined && !(this.playedAt <= t)) {
|
|
270
|
+
unplayed = this.playedAs;
|
|
271
|
+
this.playedAs = undefined;
|
|
272
|
+
this.playedPromise = null;
|
|
273
|
+
this.playedSettle = null;
|
|
274
|
+
}
|
|
275
|
+
if (this.finished && !(this.doneAt <= t)) {
|
|
276
|
+
this.finished = false;
|
|
277
|
+
this.donePromise = null;
|
|
278
|
+
this.doneSettle = null;
|
|
279
|
+
}
|
|
280
|
+
return unplayed;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* A seek back to before its cue: out of the mix, as `cue` left it, until the mix plays the cue
|
|
284
|
+
* again. What it played since is forgotten, and `done` and `played` start over where they had
|
|
285
|
+
* settled.
|
|
286
|
+
*/
|
|
287
|
+
park() {
|
|
288
|
+
this.reopen(Number.NEGATIVE_INFINITY);
|
|
289
|
+
const c = this.first;
|
|
290
|
+
this.anchorNow = c.anchorNow;
|
|
291
|
+
this.anchorElapsed = c.anchorElapsed;
|
|
292
|
+
this.rate = c.rate;
|
|
293
|
+
this.ramp = c.ramp;
|
|
294
|
+
this.weight = c.weight;
|
|
295
|
+
this.out = c.out;
|
|
296
|
+
this.start = c.start;
|
|
297
|
+
this.outAt = c.outAt;
|
|
298
|
+
this.outOver = c.outOver;
|
|
299
|
+
this.outSet = c.outSet;
|
|
300
|
+
this.log = null;
|
|
301
|
+
this.state = 'pending';
|
|
302
|
+
this.subjects = new Store();
|
|
303
|
+
this.scratch = [];
|
|
304
|
+
this.holder = null;
|
|
305
|
+
this.laned = false;
|
|
306
|
+
this.seeks++;
|
|
307
|
+
this.keeping = false;
|
|
308
|
+
this.parts = null;
|
|
309
|
+
this.parted = null;
|
|
310
|
+
this.opened = Number.NaN;
|
|
311
|
+
this.dueToken++;
|
|
312
|
+
this.early = null;
|
|
313
|
+
this.seen = 0;
|
|
314
|
+
this.latest = 0;
|
|
315
|
+
this.restedCount = 0;
|
|
316
|
+
this.cuedAt = Number.NEGATIVE_INFINITY;
|
|
317
|
+
this.doneAt = Number.POSITIVE_INFINITY;
|
|
318
|
+
this.placing = false;
|
|
319
|
+
this.answers = null;
|
|
320
|
+
this.keepOn = null;
|
|
321
|
+
this.ownKept = null;
|
|
322
|
+
this.unreached = null;
|
|
323
|
+
this.unreachedBits = null;
|
|
324
|
+
if (this.holding !== null)
|
|
325
|
+
this.holding = new Holding();
|
|
220
326
|
}
|
|
221
327
|
constructor(id, spec, patch, fade, now, start, slotOf, channels, host, send,
|
|
222
328
|
/** The owner whose clock it runs on, its start included; null for one on the mix clock. */
|
|
@@ -277,8 +383,11 @@ export class Voice {
|
|
|
277
383
|
ramp: this.ramp,
|
|
278
384
|
weight: this.weight,
|
|
279
385
|
out: this.out,
|
|
386
|
+
back: this.back,
|
|
280
387
|
start: this.start,
|
|
281
388
|
outAt: this.outAt,
|
|
389
|
+
outOver: this.outOver,
|
|
390
|
+
outSet: this.outSet,
|
|
282
391
|
});
|
|
283
392
|
}
|
|
284
393
|
/**
|
|
@@ -306,8 +415,11 @@ export class Voice {
|
|
|
306
415
|
v.ramp = controls.ramp;
|
|
307
416
|
v.weight = controls.weight;
|
|
308
417
|
v.out = controls.out;
|
|
418
|
+
v.back = controls.back;
|
|
309
419
|
v.start = controls.start;
|
|
310
420
|
v.outAt = controls.outAt;
|
|
421
|
+
v.outOver = controls.outOver;
|
|
422
|
+
v.outSet = controls.outSet;
|
|
311
423
|
}
|
|
312
424
|
v.quiet = true;
|
|
313
425
|
v.unreached = null;
|
package/dist/weigh.js
CHANGED
|
@@ -27,7 +27,7 @@ export function horizonFor(voice, delay, now) {
|
|
|
27
27
|
}
|
|
28
28
|
/** The ramp a voice's own fade envelope applies this frame, 0..1. */
|
|
29
29
|
export function fadeOf(voice, now, since) {
|
|
30
|
-
return envelope(voice.fade.in ?? 0, voice.out, voice.ease, this.reducedNow, now, since);
|
|
30
|
+
return envelope(voice.fade.in ?? 0, voice.out, voice.ease, this.reducedNow, now, since, voice.back);
|
|
31
31
|
}
|
|
32
32
|
/** What every owner above a voice multiplies into its weight for a subject this frame. */
|
|
33
33
|
export function ownedBy(voice, subject) {
|
|
@@ -84,6 +84,6 @@ export function base(voice, subject, now, held, slot = -1) {
|
|
|
84
84
|
? signal(subject, voice.setting)
|
|
85
85
|
: this.blended(voice, subject, held, slot);
|
|
86
86
|
if (signal.input && !was)
|
|
87
|
-
record(this, held, base);
|
|
87
|
+
record(this, voice, subject, held, base);
|
|
88
88
|
return base;
|
|
89
89
|
}
|