@msb235/blits 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +152 -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 +80 -27
- package/dist/due.js +3 -0
- package/dist/fade.d.ts +13 -0
- package/dist/fade.js +57 -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/gone.d.ts +32 -0
- package/dist/gone.js +134 -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 +56 -9
- package/dist/hosts.d.ts +2 -2
- package/dist/hosts.js +20 -6
- 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 +56 -36
- package/dist/mixer.js +158 -87
- 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 +28 -8
- 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 +235 -43
- 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/scored.d.ts +57 -0
- package/dist/scored.js +196 -0
- 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 +125 -0
- package/dist/transport.js +283 -0
- package/dist/types.d.ts +404 -38
- package/dist/unpage.d.ts +8 -0
- package/dist/unpage.js +165 -0
- package/dist/voice.d.ts +75 -7
- package/dist/voice.js +119 -4
- package/dist/weigh.js +2 -2
- package/docs/schema.html +120 -31
- package/package.json +2 -1
package/dist/mixer.d.ts
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import { blended, laneRead, shareKept } from './blend.js';
|
|
2
2
|
import type { Book } from './book.js';
|
|
3
|
-
import { aims, chain, linkable } from './chain.js';
|
|
3
|
+
import { aims, chain, linkable, relink } from './chain.js';
|
|
4
4
|
import { type LerpInto } from './channels.js';
|
|
5
5
|
import type { Due } from './due.js';
|
|
6
6
|
import { parting } from './fade.js';
|
|
7
|
-
import { apply, baseFor, clamp, fold, folded, foldLoci, foldWith,
|
|
7
|
+
import { apply, baseFor, clamp, contribution, fold, folded, foldLoci, foldWith, isRest, type Key, keyed, linked, owedBy, passes, read, slopeFor, tick } from './fold.js';
|
|
8
|
+
import { GoneIndex } from './gone.js';
|
|
8
9
|
import type { HandleHost } from './handle.js';
|
|
9
10
|
import { held, shownOf, sinceOf, unreachedOf } from './held.js';
|
|
10
11
|
import { Lanes } from './lanes.js';
|
|
11
12
|
import { foldLocus, type LocusScratch } from './locus.js';
|
|
12
|
-
import type { MotionOwner, Motions } from './
|
|
13
|
-
import { Pace } from './pace.js';
|
|
13
|
+
import type { MotionOwner, Motions } from './motions.js';
|
|
14
|
+
import type { Pace } from './pace.js';
|
|
15
|
+
import { Keys } from './paging.js';
|
|
14
16
|
import { keep } from './pull.js';
|
|
15
17
|
import { Steps } from './relink.js';
|
|
18
|
+
import { Outs } from './revive.js';
|
|
19
|
+
import { ScoreIndex } from './scored.js';
|
|
20
|
+
import { Fitting } from './spans.js';
|
|
16
21
|
import { Store } from './store.js';
|
|
17
|
-
import
|
|
22
|
+
import { type Announced, Transport } from './transport.js';
|
|
23
|
+
import type { Booker, BookOptions, Channel, Columns, Engine, Handle, Kit, Marked, Mix, MixOptions, OwnerSpec, Patch, Projection, Sent, Signal, SpanHandle, SpanSpec, Tape, VoiceSpec } from './types.js';
|
|
18
24
|
import { type Subject, type Voice } from './voice.js';
|
|
19
25
|
import { base, capped, fadeOf, horizonFor, ownedBy, ownerBase, prime, weigh, weighOwned } from './weigh.js';
|
|
20
26
|
export declare class Mixer<I, O> implements Mix<I, O> {
|
|
@@ -23,6 +29,8 @@ export declare class Mixer<I, O> implements Mix<I, O> {
|
|
|
23
29
|
cued: Voice<I, O>[];
|
|
24
30
|
/** Under `history`, voices that have left but that a read back may still reach. */
|
|
25
31
|
gone: Voice<I, O>[];
|
|
32
|
+
goneIx: GoneIndex<I, O>;
|
|
33
|
+
scored: ScoreIndex<I, O>;
|
|
26
34
|
/** Set on a projection's own mixer: it sends nothing, keeps no history, and reads without committing. */
|
|
27
35
|
projecting: boolean;
|
|
28
36
|
/** Set on a projection reading back: a subject it has nothing on starts from its voice's start. */
|
|
@@ -37,19 +45,6 @@ export declare class Mixer<I, O> implements Mix<I, O> {
|
|
|
37
45
|
at: number;
|
|
38
46
|
fields: Record<string, unknown>;
|
|
39
47
|
}[];
|
|
40
|
-
/**
|
|
41
|
-
* Marks the host announced on a score: `at` in host time, so the mix's rate never moves one, and
|
|
42
|
-
* NaN until the next sync for one announced as now before any sync; `made` is the mix time it was
|
|
43
|
-
* announced, for a read back to know what was known.
|
|
44
|
-
*/
|
|
45
|
-
announced: {
|
|
46
|
-
name: string;
|
|
47
|
-
score: string | undefined;
|
|
48
|
-
tags: readonly string[];
|
|
49
|
-
at: number;
|
|
50
|
-
made: number;
|
|
51
|
-
order: number;
|
|
52
|
-
}[];
|
|
53
48
|
hostThen: {
|
|
54
49
|
fields: Record<string, unknown>;
|
|
55
50
|
} | undefined;
|
|
@@ -60,22 +55,17 @@ export declare class Mixer<I, O> implements Mix<I, O> {
|
|
|
60
55
|
prev: O | undefined;
|
|
61
56
|
prevAt: number;
|
|
62
57
|
}>;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
/** The clock this mix plays on, which also holds its tape and the marks announced on it. */
|
|
59
|
+
readonly transport: Transport;
|
|
60
|
+
/** Its place among the mixes on its transport, in the order they joined. */
|
|
61
|
+
slot: number;
|
|
62
|
+
/** Taken off its transport, so it takes no more calls until a seek puts it back. */
|
|
63
|
+
dropped: boolean;
|
|
64
|
+
readonly name: string | undefined;
|
|
70
65
|
/** This mix's frame among every mix's: moved by a sync, and by a control change or drop within one. */
|
|
71
66
|
frame: number;
|
|
72
|
-
offset: number;
|
|
73
|
-
/** The mix's own rate; null while it has never been set, when mix time is host time. */
|
|
74
|
-
pace: Pace | null;
|
|
75
67
|
/** Pending voices whose start the host gave, by its host time, kept where the rate puts it. */
|
|
76
68
|
pins: Map<Voice<I, O>, number> | null;
|
|
77
|
-
last: number;
|
|
78
|
-
rebasing: boolean;
|
|
79
69
|
wantsPose: boolean;
|
|
80
70
|
/** The pose `pull` folds a subject into where it cannot read straight from the lanes. */
|
|
81
71
|
scratch: O | undefined;
|
|
@@ -110,7 +100,7 @@ export declare class Mixer<I, O> implements Mix<I, O> {
|
|
|
110
100
|
* where none does. Relinked when `version` moves, which is whenever the list or a voice's pending
|
|
111
101
|
* state changes.
|
|
112
102
|
*/
|
|
113
|
-
|
|
103
|
+
chains: Store<I, Subject<unknown>>;
|
|
114
104
|
version: number;
|
|
115
105
|
readonly steps: Steps<Voice<I, O>>;
|
|
116
106
|
/** Per subject, the voices whose `subjects` name it, in voice order. */
|
|
@@ -143,7 +133,9 @@ export declare class Mixer<I, O> implements Mix<I, O> {
|
|
|
143
133
|
subject: I;
|
|
144
134
|
};
|
|
145
135
|
readonly send: (event: unknown) => void;
|
|
146
|
-
/** The
|
|
136
|
+
/** The moment a seek went back to: what is stepped again up to it was sent the first time. */
|
|
137
|
+
sentTo: number;
|
|
138
|
+
/** The weight `contribution` found besides the delta, read by the caller at once. */
|
|
147
139
|
w: number;
|
|
148
140
|
readonly names: Key<O>[];
|
|
149
141
|
readonly channels: Channel<unknown>[];
|
|
@@ -155,13 +147,36 @@ export declare class Mixer<I, O> implements Mix<I, O> {
|
|
|
155
147
|
readonly locusScratch: LocusScratch<I, O>[];
|
|
156
148
|
locusDepth: number;
|
|
157
149
|
readonly slotOf: Map<string, number>;
|
|
158
|
-
|
|
150
|
+
lanes: Lanes<I, O> | null;
|
|
159
151
|
handles: HandleHost<I, O> | null;
|
|
160
152
|
/** What `book` made, still booking; null while there is none. */
|
|
161
153
|
bookers: Book<I, O>[] | null;
|
|
154
|
+
/** The fit a span being cued takes, which `cue` hands its voice. */
|
|
155
|
+
fitting: Fitting | null;
|
|
162
156
|
/** Every owner still in the mix, null until one is cued. */
|
|
163
157
|
owners: Voice<I, O>[] | null;
|
|
158
|
+
/** With a history store, the key each subject is paged under; null without one. */
|
|
159
|
+
keys: Keys | null;
|
|
160
|
+
/** With a history store, the voices it paged out whose handles are still held; null without one. */
|
|
161
|
+
outs: Outs | null;
|
|
164
162
|
constructor(kit: Kit<O>, opts: MixOptions);
|
|
163
|
+
/** Throws where the clock is a shared transport's, which only the transport moves. */
|
|
164
|
+
private own;
|
|
165
|
+
get now(): number;
|
|
166
|
+
set now(v: number);
|
|
167
|
+
get u(): number;
|
|
168
|
+
set u(v: number);
|
|
169
|
+
get offset(): number;
|
|
170
|
+
set offset(v: number);
|
|
171
|
+
get pace(): Pace | null;
|
|
172
|
+
set pace(v: Pace | null);
|
|
173
|
+
get announced(): Announced[];
|
|
174
|
+
set announced(v: Announced[]);
|
|
175
|
+
get nextId(): number;
|
|
176
|
+
set nextId(v: number);
|
|
177
|
+
get last(): number;
|
|
178
|
+
get tape(): Tape | undefined;
|
|
179
|
+
get replaying(): boolean;
|
|
165
180
|
get reduced(): boolean;
|
|
166
181
|
get band(): {
|
|
167
182
|
on: number;
|
|
@@ -170,17 +185,21 @@ export declare class Mixer<I, O> implements Mix<I, O> {
|
|
|
170
185
|
cue(spec: VoiceSpec<I, O>): Handle<I>;
|
|
171
186
|
blend(patches: readonly Patch<I, O, unknown>[], by: Signal<I>, spec?: Omit<VoiceSpec<I, O>, 'patch' | 'weight' | 'locus'>): Handle<I>[];
|
|
172
187
|
owns(spec: OwnerSpec<I>): Handle<I>;
|
|
188
|
+
span(spec: SpanSpec<I>): SpanHandle<I>;
|
|
173
189
|
sync(timestamp: number): void;
|
|
174
|
-
private syncAt;
|
|
175
190
|
book(opts: BookOptions): Booker;
|
|
176
191
|
probe(subject: I, out?: O): O;
|
|
177
192
|
pull(subjects: Iterable<I>, into: Columns<O>): void;
|
|
178
|
-
|
|
193
|
+
seek(time: number): void;
|
|
194
|
+
prepare(time: number): Promise<void>;
|
|
195
|
+
project(time: number): Projection<I, O>;
|
|
179
196
|
announce(name: string, opts?: {
|
|
180
197
|
at?: number;
|
|
181
198
|
score?: string;
|
|
182
199
|
tags?: readonly string[];
|
|
183
200
|
}): void;
|
|
201
|
+
/** Throws for a mix taken off its transport. */
|
|
202
|
+
attached(): void;
|
|
184
203
|
marks(from: number, to: number): Marked[];
|
|
185
204
|
atRest(subject: I): boolean;
|
|
186
205
|
rests(pose: O): boolean;
|
|
@@ -225,7 +244,7 @@ declare const methods: {
|
|
|
225
244
|
owedBy: typeof owedBy;
|
|
226
245
|
foldLoci: typeof foldLoci;
|
|
227
246
|
read: typeof read;
|
|
228
|
-
|
|
247
|
+
contribution: typeof contribution;
|
|
229
248
|
keyed: typeof keyed;
|
|
230
249
|
tick: typeof tick;
|
|
231
250
|
apply: typeof apply;
|
|
@@ -248,6 +267,7 @@ declare const methods: {
|
|
|
248
267
|
shareKept: typeof shareKept;
|
|
249
268
|
parting: typeof parting;
|
|
250
269
|
chain: typeof chain;
|
|
270
|
+
relink: typeof relink;
|
|
251
271
|
linkable: typeof linkable;
|
|
252
272
|
aims: typeof aims;
|
|
253
273
|
held: typeof held;
|
package/dist/mixer.js
CHANGED
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
import { blended, blendOf, laneRead, shareKept } from './blend.js';
|
|
2
|
-
import { aims, chain, linkable } from './chain.js';
|
|
2
|
+
import { aims, chain, linkable, relink } from './chain.js';
|
|
3
3
|
import { lerpInto } from './channels.js';
|
|
4
4
|
import { cue } from './cue.js';
|
|
5
5
|
import { beginFade, parting } from './fade.js';
|
|
6
|
-
import { apply, baseFor, clamp, fold, folded, foldLoci, foldWith,
|
|
6
|
+
import { apply, baseFor, clamp, contribution, fold, folded, foldLoci, foldWith, isRest, keyed, linked, near, owedBy, passes, read, slopeFor, tick, } from './fold.js';
|
|
7
|
+
import { GoneIndex, goneIndex } from './gone.js';
|
|
7
8
|
import { held, shownOf, sinceOf, unreachedOf } from './held.js';
|
|
9
|
+
import { leave, releasing } from './history.js';
|
|
8
10
|
import { book, laneHost } from './hosts.js';
|
|
9
11
|
import { Lanes } from './lanes.js';
|
|
10
12
|
import { foldLocus } from './locus.js';
|
|
11
13
|
import { listed } from './marks.js';
|
|
12
|
-
import {
|
|
14
|
+
import { nextFrame } from './move.js';
|
|
13
15
|
import { ownerPatch } from './owner.js';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { project } from './project.js';
|
|
16
|
+
import { Keys } from './paging.js';
|
|
17
|
+
import { projectAll } from './project.js';
|
|
17
18
|
import { keep, pull } from './pull.js';
|
|
18
19
|
import { Steps } from './relink.js';
|
|
20
|
+
import { Outs } from './revive.js';
|
|
21
|
+
import { ScoreIndex } from './scored.js';
|
|
22
|
+
import { seek } from './seek.js';
|
|
23
|
+
import { Fitting, refit } from './spans.js';
|
|
19
24
|
import { Store } from './store.js';
|
|
25
|
+
import { record } from './tape.js';
|
|
26
|
+
import { announce, Transport } from './transport.js';
|
|
20
27
|
import { none } from './voice.js';
|
|
21
28
|
import { base, capped, fadeOf, horizonFor, ownedBy, ownerBase, prime, weigh, weighOwned, } from './weigh.js';
|
|
22
29
|
// biome-ignore lint/suspicious/noUnsafeDeclarationMerging: every member of `Methods` is installed below
|
|
@@ -26,6 +33,8 @@ export class Mixer {
|
|
|
26
33
|
cued = [];
|
|
27
34
|
/** Under `history`, voices that have left but that a read back may still reach. */
|
|
28
35
|
gone = [];
|
|
36
|
+
goneIx = new GoneIndex();
|
|
37
|
+
scored = new ScoreIndex();
|
|
29
38
|
/** Set on a projection's own mixer: it sends nothing, keeps no history, and reads without committing. */
|
|
30
39
|
projecting = false;
|
|
31
40
|
/** Set on a projection reading back: a subject it has nothing on starts from its voice's start. */
|
|
@@ -37,31 +46,20 @@ export class Mixer {
|
|
|
37
46
|
* a projection reading back, the copy in force then.
|
|
38
47
|
*/
|
|
39
48
|
hostLog = [];
|
|
40
|
-
/**
|
|
41
|
-
* Marks the host announced on a score: `at` in host time, so the mix's rate never moves one, and
|
|
42
|
-
* NaN until the next sync for one announced as now before any sync; `made` is the mix time it was
|
|
43
|
-
* announced, for a read back to know what was known.
|
|
44
|
-
*/
|
|
45
|
-
announced = [];
|
|
46
49
|
hostThen;
|
|
47
50
|
/** Each subject's last two poses and when they were probed, for `from: 'current'`. */
|
|
48
51
|
pose = new Store();
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
/** The clock this mix plays on, which also holds its tape and the marks announced on it. */
|
|
53
|
+
transport;
|
|
54
|
+
/** Its place among the mixes on its transport, in the order they joined. */
|
|
55
|
+
slot = -1;
|
|
56
|
+
/** Taken off its transport, so it takes no more calls until a seek puts it back. */
|
|
57
|
+
dropped = false;
|
|
58
|
+
name;
|
|
56
59
|
/** This mix's frame among every mix's: moved by a sync, and by a control change or drop within one. */
|
|
57
60
|
frame = nextFrame();
|
|
58
|
-
offset = 0;
|
|
59
|
-
/** The mix's own rate; null while it has never been set, when mix time is host time. */
|
|
60
|
-
pace = null;
|
|
61
61
|
/** Pending voices whose start the host gave, by its host time, kept where the rate puts it. */
|
|
62
62
|
pins = null;
|
|
63
|
-
last = Number.NaN;
|
|
64
|
-
rebasing = false;
|
|
65
63
|
wantsPose = false;
|
|
66
64
|
/** The pose `pull` folds a subject into where it cannot read straight from the lanes. */
|
|
67
65
|
scratch;
|
|
@@ -94,10 +92,13 @@ export class Mixer {
|
|
|
94
92
|
if (this.stirred)
|
|
95
93
|
return;
|
|
96
94
|
this.stirred = true;
|
|
95
|
+
if (this.syncing)
|
|
96
|
+
return;
|
|
97
97
|
const fns = this.stirFns;
|
|
98
|
-
if (fns !== null
|
|
98
|
+
if (fns !== null)
|
|
99
99
|
for (const fn of [...fns])
|
|
100
100
|
fn();
|
|
101
|
+
this.transport.woke();
|
|
101
102
|
}
|
|
102
103
|
onWake(fn) {
|
|
103
104
|
if (this.stirFns === null)
|
|
@@ -149,7 +150,7 @@ export class Mixer {
|
|
|
149
150
|
};
|
|
150
151
|
send = (event) => {
|
|
151
152
|
const { voice, subject } = this.sending;
|
|
152
|
-
if (voice === null || this.projecting)
|
|
153
|
+
if (voice === null || this.projecting || voice.setting.timestamp <= this.sentTo)
|
|
153
154
|
return;
|
|
154
155
|
this.sent.push({
|
|
155
156
|
timestamp: voice.setting.timestamp,
|
|
@@ -159,7 +160,9 @@ export class Mixer {
|
|
|
159
160
|
event,
|
|
160
161
|
});
|
|
161
162
|
};
|
|
162
|
-
/** The
|
|
163
|
+
/** The moment a seek went back to: what is stepped again up to it was sent the first time. */
|
|
164
|
+
sentTo = Number.NEGATIVE_INFINITY;
|
|
165
|
+
/** The weight `contribution` found besides the delta, read by the caller at once. */
|
|
163
166
|
w = 0;
|
|
164
167
|
names;
|
|
165
168
|
channels;
|
|
@@ -175,8 +178,14 @@ export class Mixer {
|
|
|
175
178
|
handles = null;
|
|
176
179
|
/** What `book` made, still booking; null while there is none. */
|
|
177
180
|
bookers = null;
|
|
181
|
+
/** The fit a span being cued takes, which `cue` hands its voice. */
|
|
182
|
+
fitting = null;
|
|
178
183
|
/** Every owner still in the mix, null until one is cued. */
|
|
179
184
|
owners = null;
|
|
185
|
+
/** With a history store, the key each subject is paged under; null without one. */
|
|
186
|
+
keys = null;
|
|
187
|
+
/** With a history store, the voices it paged out whose handles are still held; null without one. */
|
|
188
|
+
outs = null;
|
|
180
189
|
constructor(kit, opts) {
|
|
181
190
|
this.kit = kit;
|
|
182
191
|
this.opts = opts;
|
|
@@ -188,6 +197,75 @@ export class Mixer {
|
|
|
188
197
|
this.slotOf.set(k, i);
|
|
189
198
|
});
|
|
190
199
|
this.lanes = opts.lanes === false ? null : new Lanes(laneHost(this));
|
|
200
|
+
this.name = opts.name;
|
|
201
|
+
const shared = opts.transport;
|
|
202
|
+
if (shared !== undefined) {
|
|
203
|
+
if (opts.history !== undefined)
|
|
204
|
+
throw new Error('blits: a mix on a transport keeps the history the transport keeps, not its own');
|
|
205
|
+
this.opts = { ...opts, history: shared.history };
|
|
206
|
+
this.transport = shared;
|
|
207
|
+
}
|
|
208
|
+
else
|
|
209
|
+
this.transport = new Transport(opts.history, false);
|
|
210
|
+
if (this.transport.pager !== null) {
|
|
211
|
+
this.keys = new Keys(opts.keyOf);
|
|
212
|
+
this.outs = new Outs();
|
|
213
|
+
if (shared !== undefined)
|
|
214
|
+
named(shared, opts.name);
|
|
215
|
+
}
|
|
216
|
+
this.transport.join(this);
|
|
217
|
+
}
|
|
218
|
+
/** Throws where the clock is a shared transport's, which only the transport moves. */
|
|
219
|
+
own(what) {
|
|
220
|
+
if (this.transport.shared)
|
|
221
|
+
throw new Error(`blits: ${this.name ?? 'this mix'} plays on a transport, so ${what} goes to the transport`);
|
|
222
|
+
return this.transport;
|
|
223
|
+
}
|
|
224
|
+
// The transport's clock, read and written as the mix's own by every module that moves it.
|
|
225
|
+
get now() {
|
|
226
|
+
return this.transport.now;
|
|
227
|
+
}
|
|
228
|
+
set now(v) {
|
|
229
|
+
this.transport.now = v;
|
|
230
|
+
}
|
|
231
|
+
get u() {
|
|
232
|
+
return this.transport.u;
|
|
233
|
+
}
|
|
234
|
+
set u(v) {
|
|
235
|
+
this.transport.u = v;
|
|
236
|
+
}
|
|
237
|
+
get offset() {
|
|
238
|
+
return this.transport.offset;
|
|
239
|
+
}
|
|
240
|
+
set offset(v) {
|
|
241
|
+
this.transport.offset = v;
|
|
242
|
+
}
|
|
243
|
+
get pace() {
|
|
244
|
+
return this.transport.pace;
|
|
245
|
+
}
|
|
246
|
+
set pace(v) {
|
|
247
|
+
this.transport.pace = v;
|
|
248
|
+
}
|
|
249
|
+
get announced() {
|
|
250
|
+
return this.transport.announced;
|
|
251
|
+
}
|
|
252
|
+
set announced(v) {
|
|
253
|
+
this.transport.announced = v;
|
|
254
|
+
}
|
|
255
|
+
get nextId() {
|
|
256
|
+
return this.transport.nextId;
|
|
257
|
+
}
|
|
258
|
+
set nextId(v) {
|
|
259
|
+
this.transport.nextId = v;
|
|
260
|
+
}
|
|
261
|
+
get last() {
|
|
262
|
+
return this.transport.last;
|
|
263
|
+
}
|
|
264
|
+
get tape() {
|
|
265
|
+
return this.transport.tape;
|
|
266
|
+
}
|
|
267
|
+
get replaying() {
|
|
268
|
+
return this.transport.replaying;
|
|
191
269
|
}
|
|
192
270
|
get reduced() {
|
|
193
271
|
const r = this.opts.reduce;
|
|
@@ -197,6 +275,7 @@ export class Mixer {
|
|
|
197
275
|
return this.opts.band ?? { on: 0.6, off: 0.4 };
|
|
198
276
|
}
|
|
199
277
|
cue(spec) {
|
|
278
|
+
this.attached();
|
|
200
279
|
const engine = this.opts.engine ?? mixer;
|
|
201
280
|
if (!engine.runs.has(spec.patch.form))
|
|
202
281
|
throw new Error(`blits: engine ${engine.name} does not run ${spec.patch.form} patches`);
|
|
@@ -218,38 +297,19 @@ export class Mixer {
|
|
|
218
297
|
throw new Error('blits: an owner does not loop: a pass would have to restart its children');
|
|
219
298
|
return this.cue({ ...spec, patch: ownerPatch });
|
|
220
299
|
}
|
|
221
|
-
|
|
222
|
-
this.
|
|
300
|
+
span(spec) {
|
|
301
|
+
this.fitting = new Fitting(spec);
|
|
223
302
|
try {
|
|
224
|
-
this.
|
|
303
|
+
const handle = this.owns(spec);
|
|
304
|
+
refit(this, this.owners?.find((v) => v.handle === handle));
|
|
305
|
+
return handle;
|
|
225
306
|
}
|
|
226
307
|
finally {
|
|
227
|
-
this.
|
|
308
|
+
this.fitting = null;
|
|
228
309
|
}
|
|
229
310
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
this.offset += timestamp - this.last;
|
|
233
|
-
this.rebasing = false;
|
|
234
|
-
this.last = timestamp;
|
|
235
|
-
const u = timestamp - this.offset;
|
|
236
|
-
const pace = this.pace;
|
|
237
|
-
const now = pace === null ? u : pace.sync(u);
|
|
238
|
-
const later = u !== this.u;
|
|
239
|
-
const still = later && now === this.now;
|
|
240
|
-
this.u = u;
|
|
241
|
-
// Host time moving while the mix clock stands still lands what waits on host time or the host.
|
|
242
|
-
if (now !== this.now || (later && pace !== null && waits(this)))
|
|
243
|
-
move(this, now);
|
|
244
|
-
// It is a frame too, which asks every weight signal again, so one reading input follows it.
|
|
245
|
-
if (still) {
|
|
246
|
-
this.frame = nextFrame();
|
|
247
|
-
this.lanes?.refill();
|
|
248
|
-
}
|
|
249
|
-
const bookers = this.bookers;
|
|
250
|
-
if (bookers !== null)
|
|
251
|
-
for (const b of bookers)
|
|
252
|
-
b.sync();
|
|
311
|
+
sync(timestamp) {
|
|
312
|
+
this.own('sync').sync(timestamp);
|
|
253
313
|
}
|
|
254
314
|
book(opts) {
|
|
255
315
|
return book(this, opts);
|
|
@@ -265,20 +325,25 @@ export class Mixer {
|
|
|
265
325
|
pull(subjects, into) {
|
|
266
326
|
pull(this, subjects, into);
|
|
267
327
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
328
|
+
seek(time) {
|
|
329
|
+
if (this.projecting)
|
|
330
|
+
throw new Error('blits: a projection does not seek');
|
|
331
|
+
seek(this.own('seek'), time);
|
|
332
|
+
}
|
|
333
|
+
prepare(time) {
|
|
334
|
+
return this.own('prepare').prepare(time);
|
|
335
|
+
}
|
|
336
|
+
project(time) {
|
|
337
|
+
return projectAll(this.transport, time).of(this);
|
|
271
338
|
}
|
|
272
339
|
announce(name, opts = {}) {
|
|
273
|
-
|
|
274
|
-
this.
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
order: this.nextId++,
|
|
281
|
-
});
|
|
340
|
+
this.attached();
|
|
341
|
+
announce(this.transport, name, opts, this.slot, this.name);
|
|
342
|
+
}
|
|
343
|
+
/** Throws for a mix taken off its transport. */
|
|
344
|
+
attached() {
|
|
345
|
+
if (this.dropped)
|
|
346
|
+
throw new Error(`blits: ${this.name ?? 'this mix'} was dropped from its transport`);
|
|
282
347
|
}
|
|
283
348
|
marks(from, to) {
|
|
284
349
|
return listed(this, from, to).map(({ order: _, ...m }) => m);
|
|
@@ -316,7 +381,7 @@ export class Mixer {
|
|
|
316
381
|
return true;
|
|
317
382
|
}
|
|
318
383
|
rebase() {
|
|
319
|
-
this.
|
|
384
|
+
this.own('rebase').rebase();
|
|
320
385
|
}
|
|
321
386
|
voices(tag) {
|
|
322
387
|
const out = [];
|
|
@@ -341,25 +406,13 @@ export class Mixer {
|
|
|
341
406
|
return this.cued.every((v) => this.still(v, this.now));
|
|
342
407
|
}
|
|
343
408
|
get rate() {
|
|
344
|
-
return this.
|
|
409
|
+
return this.transport.rate;
|
|
345
410
|
}
|
|
346
411
|
set rate(r) {
|
|
347
|
-
this.ramp(r, 0);
|
|
412
|
+
this.own('rate').ramp(r, 0);
|
|
348
413
|
}
|
|
349
414
|
ramp(rate, over) {
|
|
350
|
-
|
|
351
|
-
throw new RangeError(`blits: a mix's rate is a finite number, 0 or more, not ${rate}`);
|
|
352
|
-
if (this.pace === null) {
|
|
353
|
-
this.pace = new Pace(this.opts.history !== undefined);
|
|
354
|
-
// Until now mix time was host time, so a pending voice's start is the host time it was given.
|
|
355
|
-
for (const v of this.cued)
|
|
356
|
-
if (v.state === 'pending' && !v.placing && v.owner === null && v.spec.start !== undefined)
|
|
357
|
-
pin(this, v, v.start);
|
|
358
|
-
}
|
|
359
|
-
const history = this.opts.history;
|
|
360
|
-
const reach = history === undefined ? Number.NEGATIVE_INFINITY : this.now - history.ms;
|
|
361
|
-
this.pace.change(this.u, rate, over, reach);
|
|
362
|
-
this.stir();
|
|
415
|
+
this.own('ramp').ramp(rate, over);
|
|
363
416
|
}
|
|
364
417
|
/** Whether a voice will change no pose from `now` on, short of a change made to it. */
|
|
365
418
|
still(voice, now) {
|
|
@@ -380,6 +433,7 @@ export class Mixer {
|
|
|
380
433
|
mute(opts) {
|
|
381
434
|
for (const voice of this.cued)
|
|
382
435
|
beginFade(this, voice, { over: opts?.over });
|
|
436
|
+
record(this, 'mute', () => this.mute(opts));
|
|
383
437
|
}
|
|
384
438
|
drop(subject) {
|
|
385
439
|
this.frame = nextFrame();
|
|
@@ -397,9 +451,11 @@ export class Mixer {
|
|
|
397
451
|
this.stir();
|
|
398
452
|
// A record of it is only in a voice over every subject, one naming it, or one gone; a ramp out
|
|
399
453
|
// of a voice that does not name it is in `parters`, and a patch's own state in `motions`.
|
|
454
|
+
const kept = releasing(this);
|
|
400
455
|
const forget = (voice) => {
|
|
456
|
+
leave(this, voice, subject, voice.subjects.get(subject));
|
|
401
457
|
voice.subjects.delete(subject);
|
|
402
|
-
voice.motion?.release(subject);
|
|
458
|
+
voice.motion?.release(subject, ...kept);
|
|
403
459
|
voice.parts?.delete(subject);
|
|
404
460
|
voice.parted?.delete(subject);
|
|
405
461
|
voice.blend?.of.reads.delete(subject);
|
|
@@ -410,8 +466,13 @@ export class Mixer {
|
|
|
410
466
|
if (named !== undefined)
|
|
411
467
|
for (const voice of [...named])
|
|
412
468
|
forget(voice);
|
|
413
|
-
|
|
469
|
+
const gone = goneIndex(this);
|
|
470
|
+
for (const voice of gone.general)
|
|
414
471
|
forget(voice);
|
|
472
|
+
const goneNaming = gone.named.get(subject);
|
|
473
|
+
if (goneNaming !== undefined)
|
|
474
|
+
for (const voice of goneNaming)
|
|
475
|
+
forget(voice);
|
|
415
476
|
if (this.owners !== null)
|
|
416
477
|
for (const voice of this.owners)
|
|
417
478
|
forget(voice);
|
|
@@ -427,10 +488,11 @@ export class Mixer {
|
|
|
427
488
|
const strays = this.strays.get(subject);
|
|
428
489
|
if (strays !== undefined)
|
|
429
490
|
for (const motion of strays)
|
|
430
|
-
motion.release(subject);
|
|
491
|
+
motion.release(subject, ...kept);
|
|
431
492
|
this.strays.delete(subject);
|
|
432
493
|
for (const motion of this.strayAll)
|
|
433
|
-
motion.release(subject);
|
|
494
|
+
motion.release(subject, ...kept);
|
|
495
|
+
record(this, 'drop', () => this.drop(subject));
|
|
434
496
|
}
|
|
435
497
|
drain(tag) {
|
|
436
498
|
const all = this.sent;
|
|
@@ -491,7 +553,7 @@ const methods = {
|
|
|
491
553
|
owedBy,
|
|
492
554
|
foldLoci,
|
|
493
555
|
read,
|
|
494
|
-
|
|
556
|
+
contribution,
|
|
495
557
|
keyed,
|
|
496
558
|
tick,
|
|
497
559
|
apply,
|
|
@@ -514,6 +576,7 @@ const methods = {
|
|
|
514
576
|
shareKept,
|
|
515
577
|
parting,
|
|
516
578
|
chain,
|
|
579
|
+
relink,
|
|
517
580
|
linkable,
|
|
518
581
|
aims,
|
|
519
582
|
held,
|
|
@@ -525,6 +588,14 @@ const methods = {
|
|
|
525
588
|
};
|
|
526
589
|
for (const [name, value] of Object.entries(methods))
|
|
527
590
|
Object.defineProperty(Mixer.prototype, name, { value, writable: true, configurable: true });
|
|
591
|
+
/** Refuses a mix joining a transport with a store unnamed, or under a name history still keys records by. */
|
|
592
|
+
function named(transport, name) {
|
|
593
|
+
if (name === undefined)
|
|
594
|
+
throw new Error('blits: a mix on a transport whose history has a store needs a name, which its records are keyed by');
|
|
595
|
+
const taken = [...transport.members, ...transport.dropped.map((d) => d.mix)];
|
|
596
|
+
if (taken.some((m) => m.name === name))
|
|
597
|
+
throw new Error(`blits: a mix named ${name} is already on this transport, or a seek could bring it back`);
|
|
598
|
+
}
|
|
528
599
|
/**
|
|
529
600
|
* The one that ships: every form on the CPU, probed per subject on demand.
|
|
530
601
|
*
|