@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/project.js
CHANGED
|
@@ -1,85 +1,60 @@
|
|
|
1
1
|
import { ownBlends } from './blend.js';
|
|
2
2
|
import { index } from './chain.js';
|
|
3
|
-
import { copyHeld, last } from './history.js';
|
|
4
|
-
import {
|
|
3
|
+
import { copyHeld, last, leftAt } from './history.js';
|
|
4
|
+
import { Mixer } from './mixer.js';
|
|
5
5
|
import { move } from './move.js';
|
|
6
6
|
import { heldByInput, ownerReading, relink } from './owner.js';
|
|
7
7
|
import { pin } from './place.js';
|
|
8
8
|
import { reading } from './reading.js';
|
|
9
9
|
import { Store } from './store.js';
|
|
10
|
+
import { hostAt } from './tape.js';
|
|
11
|
+
import { Transport } from './transport.js';
|
|
12
|
+
import { cover } from './unpage.js';
|
|
10
13
|
import { unreached } from './unreached.js';
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (Number.isNaN(mix.now) || t >= mix.now) {
|
|
24
|
-
c.now = mix.now;
|
|
25
|
-
c.pace = pace === null ? null : pace.until(Number.POSITIVE_INFINITY);
|
|
26
|
-
c.cued = mix.cued
|
|
27
|
-
.filter((v) => v.state !== 'done')
|
|
28
|
-
.map((v) => {
|
|
29
|
-
const copy = v.copy((subject) => carry(mix, v, subject));
|
|
30
|
-
const at = mix.pins?.get(v);
|
|
31
|
-
if (at !== undefined)
|
|
32
|
-
pin(c, copy, at);
|
|
33
|
-
return copy;
|
|
34
|
-
});
|
|
35
|
-
ownBlends(c.cued);
|
|
36
|
-
if (mix.owners !== null)
|
|
37
|
-
relink(c.cued);
|
|
38
|
-
c.announced = mix.announced.map((a) => ({ ...a }));
|
|
39
|
-
count(c);
|
|
40
|
-
move(c, t);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
const history = mix.opts.history;
|
|
14
|
+
/**
|
|
15
|
+
* Every mix on a transport read at mix time `t`, through copies of them on a transport of their
|
|
16
|
+
* own, moved together so an anchor across mixes answers as it would live.
|
|
17
|
+
*/
|
|
18
|
+
export function projectAll(transport, t) {
|
|
19
|
+
const ahead = Number.isNaN(transport.now) || t >= transport.now;
|
|
20
|
+
const tape = transport.tape;
|
|
21
|
+
const next = ahead && tape !== undefined ? tape.timestampAt(tape.undoDepth()) : undefined;
|
|
22
|
+
if (next !== undefined && next <= t)
|
|
23
|
+
throw new Error(`blits: the tape holds calls at ${next} that a read ahead to ${t} cannot play; seek there to see them`);
|
|
24
|
+
if (!ahead) {
|
|
25
|
+
const history = transport.history;
|
|
44
26
|
if (!history)
|
|
45
27
|
throw new Error('blits: reading back needs a mix made with history');
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const copy = v.copy((subject) => recall(mix, v, subject, t), last(log, t, (e) => e.sync) ?? log[0]);
|
|
65
|
-
if (then !== undefined)
|
|
66
|
-
copy.setting.host = then;
|
|
67
|
-
return copy;
|
|
28
|
+
cover(transport, t);
|
|
29
|
+
}
|
|
30
|
+
const u = hostAt(transport, t);
|
|
31
|
+
const pace = transport.pace;
|
|
32
|
+
const c = new Transport(undefined, true);
|
|
33
|
+
c.offset = transport.offset;
|
|
34
|
+
c.u = u;
|
|
35
|
+
c.nextId = transport.nextId;
|
|
36
|
+
c.now = ahead ? transport.now : t;
|
|
37
|
+
c.pace = pace === null ? null : pace.until(ahead ? Number.POSITIVE_INFINITY : u);
|
|
38
|
+
c.announced = (ahead ? transport.announced : transport.announced.filter((a) => a.made < t)).map((a) => ({ ...a }));
|
|
39
|
+
const copies = new Map();
|
|
40
|
+
for (const mix of transport.members) {
|
|
41
|
+
const copy = new Mixer(mix.kit, {
|
|
42
|
+
...mix.opts,
|
|
43
|
+
history: undefined,
|
|
44
|
+
lanes: false,
|
|
45
|
+
transport: c,
|
|
68
46
|
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
copy
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
: copy.out && copy.out.at <= t
|
|
77
|
-
? 'fading'
|
|
78
|
-
: copy.freezesAfter && copy.elapsedAt(t) >= copy.span + copy.latest
|
|
79
|
-
? 'frozen'
|
|
80
|
-
: 'live';
|
|
81
|
-
count(c);
|
|
47
|
+
copy.slot = mix.slot;
|
|
48
|
+
copy.projecting = true;
|
|
49
|
+
copies.set(mix, copy);
|
|
50
|
+
if (ahead)
|
|
51
|
+
copyAhead(mix, copy);
|
|
52
|
+
else
|
|
53
|
+
copyBack(mix, copy, t);
|
|
82
54
|
}
|
|
55
|
+
if (ahead)
|
|
56
|
+
for (const copy of copies.values())
|
|
57
|
+
move(copy, t);
|
|
83
58
|
const read = (f) => {
|
|
84
59
|
reading.live = false;
|
|
85
60
|
try {
|
|
@@ -90,14 +65,78 @@ export function project(mix, c, timestamp) {
|
|
|
90
65
|
}
|
|
91
66
|
};
|
|
92
67
|
return {
|
|
93
|
-
timestamp,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
68
|
+
timestamp: t,
|
|
69
|
+
of(mix) {
|
|
70
|
+
const copy = copies.get(mix);
|
|
71
|
+
if (copy === undefined)
|
|
72
|
+
throw new Error('blits: that mix is not on this transport');
|
|
73
|
+
return {
|
|
74
|
+
timestamp: t,
|
|
75
|
+
probe: (subject, out) => read(() => copy.fold(subject, out)),
|
|
76
|
+
assess: (subject) => read(() => {
|
|
77
|
+
copy.fold(subject);
|
|
78
|
+
return doubts(copy, subject);
|
|
79
|
+
}),
|
|
80
|
+
};
|
|
81
|
+
},
|
|
99
82
|
};
|
|
100
83
|
}
|
|
84
|
+
/** A mix's copy for a read ahead: its voices as they stand, each subject carried from the live one. */
|
|
85
|
+
function copyAhead(mix, c) {
|
|
86
|
+
c.pose = mix.pose;
|
|
87
|
+
c.wantsPose = mix.wantsPose;
|
|
88
|
+
c.reducedNow = mix.reducedNow;
|
|
89
|
+
c.cued = mix.cued
|
|
90
|
+
.filter((v) => v.state !== 'done')
|
|
91
|
+
.map((v) => {
|
|
92
|
+
const copy = v.copy((subject) => carry(mix, v, subject));
|
|
93
|
+
const at = mix.pins?.get(v);
|
|
94
|
+
if (at !== undefined)
|
|
95
|
+
pin(c, copy, at);
|
|
96
|
+
return copy;
|
|
97
|
+
});
|
|
98
|
+
ownBlends(c.cued);
|
|
99
|
+
if (mix.owners !== null)
|
|
100
|
+
relink(c.cued);
|
|
101
|
+
count(c);
|
|
102
|
+
}
|
|
103
|
+
/** A mix's copy for a read back to `t`: its voices then, from the controls and copies kept. */
|
|
104
|
+
function copyBack(mix, c, t) {
|
|
105
|
+
c.pose = mix.pose;
|
|
106
|
+
c.wantsPose = mix.wantsPose;
|
|
107
|
+
c.reducedNow = mix.reducedNow;
|
|
108
|
+
c.backward = true;
|
|
109
|
+
const was = last(mix.hostLog, t, true);
|
|
110
|
+
const host = mix.opts.host;
|
|
111
|
+
const then = was && typeof host === 'object' && host !== null
|
|
112
|
+
? Object.assign(Object.create(host), was.fields)
|
|
113
|
+
: undefined;
|
|
114
|
+
if (was)
|
|
115
|
+
c.hostThen = was;
|
|
116
|
+
c.cued = [...mix.cued, ...mix.gone]
|
|
117
|
+
.filter((v) => v.cuedAt <= t && v.doneAt > t)
|
|
118
|
+
.sort((a, b) => a.id - b.id)
|
|
119
|
+
.map((v) => {
|
|
120
|
+
const log = v.log;
|
|
121
|
+
const copy = v.copy((subject) => recall(mix, v, subject, t), last(log, t, (e) => e.sync) ?? log[0]);
|
|
122
|
+
if (then !== undefined)
|
|
123
|
+
copy.setting.host = then;
|
|
124
|
+
return copy;
|
|
125
|
+
});
|
|
126
|
+
ownBlends(c.cued);
|
|
127
|
+
if (mix.owners !== null)
|
|
128
|
+
relink(c.cued);
|
|
129
|
+
for (const copy of c.cued)
|
|
130
|
+
copy.state =
|
|
131
|
+
(copy.owner === null ? t : ownerReading(copy.owner, t)) < copy.start
|
|
132
|
+
? 'pending'
|
|
133
|
+
: copy.out && copy.out.at <= t
|
|
134
|
+
? 'fading'
|
|
135
|
+
: copy.freezesAfter && copy.elapsedAt(t) >= copy.span + copy.latest
|
|
136
|
+
? 'frozen'
|
|
137
|
+
: 'live';
|
|
138
|
+
count(c);
|
|
139
|
+
}
|
|
101
140
|
/** Recounts what the fold's shortcuts depend on, for a projection's freshly copied voices. */
|
|
102
141
|
function count(mix) {
|
|
103
142
|
mix.named = new Store();
|
|
@@ -132,7 +171,7 @@ function carry(mix, voice, subject) {
|
|
|
132
171
|
* voice's start with fresh state, which is exact for a voice stepped at a fixed interval.
|
|
133
172
|
*/
|
|
134
173
|
function recall(mix, voice, subject, t) {
|
|
135
|
-
const live = recordOf(mix, voice, subject);
|
|
174
|
+
const live = leftAt(voice.left, subject, t) ?? recordOf(mix, voice, subject);
|
|
136
175
|
if (live === undefined)
|
|
137
176
|
return undefined;
|
|
138
177
|
const snap = live.snaps && last(live.snaps, t, true);
|
|
@@ -192,7 +231,7 @@ function doubts(mix, subject) {
|
|
|
192
231
|
for (const slot of voice.slots)
|
|
193
232
|
out[mix.names[slot]] = 'held';
|
|
194
233
|
}
|
|
195
|
-
for (let held = mix.chain(subject
|
|
234
|
+
for (let held = mix.chain(subject); held !== null; held = held.next) {
|
|
196
235
|
const voice = held.voice;
|
|
197
236
|
if (voice === null || voice.state === 'done')
|
|
198
237
|
continue;
|
package/dist/pull.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { axesOf } from './channels.js';
|
|
1
2
|
import { copy } from './clone.js';
|
|
2
3
|
import { clampRun, flush, pullRun, writeLater } from './columns.js';
|
|
3
4
|
/** Writes a folded pose's value into subject `n`'s places in a column. */
|
|
@@ -128,14 +129,14 @@ function readAll(mix, list, columns, live, current, whole) {
|
|
|
128
129
|
const kept = heads[n];
|
|
129
130
|
const head = was[n] === subject && kept !== undefined && kept.version === version
|
|
130
131
|
? kept
|
|
131
|
-
: mix.chain(subject
|
|
132
|
+
: mix.chain(subject);
|
|
132
133
|
was[n] = subject;
|
|
133
134
|
heads[n] = head;
|
|
134
135
|
slot = head.slot;
|
|
135
136
|
slots[n] = slot;
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
|
-
const laned = live && lanes?.prepare(slot, subject,
|
|
139
|
+
const laned = live && lanes?.prepare(slot, subject, mix.version, heads[n] ?? null) === true;
|
|
139
140
|
if (laned && whole && lanes.whole && !lanes.owes(slot)) {
|
|
140
141
|
writeLater(lanes, slot, columns, n);
|
|
141
142
|
continue;
|
|
@@ -160,7 +161,7 @@ function columnsOf(mix, into) {
|
|
|
160
161
|
throw new Error(`blits: pull was handed ${key}, which the kit lacks`);
|
|
161
162
|
const channel = mix.channels[slot];
|
|
162
163
|
const rest = channel.rest;
|
|
163
|
-
const axes =
|
|
164
|
+
const axes = axesOf(channel);
|
|
164
165
|
const bounds = channel.bounds;
|
|
165
166
|
const at = new Float64Array(axes).fill(Number.NaN);
|
|
166
167
|
if (typeof rest === 'number')
|
package/dist/qualify.js
CHANGED
|
@@ -163,11 +163,13 @@ export function requalify(lanes, version) {
|
|
|
163
163
|
return;
|
|
164
164
|
const channel = host.channels[slot];
|
|
165
165
|
const n = numericOf(channel);
|
|
166
|
-
|
|
166
|
+
// A rest-less channel's values are NaN until some voice passes the band: absent from the pose.
|
|
167
|
+
const rest = (channel.rest ?? Number.NaN);
|
|
168
|
+
const scalar = typeof rest === 'number' && n.axes === 1;
|
|
167
169
|
const ch = {
|
|
168
170
|
name: host.names[slot],
|
|
169
171
|
op: n.op,
|
|
170
|
-
scalar
|
|
172
|
+
scalar,
|
|
171
173
|
rest: typeof rest === 'number' ? rest : rest[0],
|
|
172
174
|
axes: n.axes,
|
|
173
175
|
values: new Float64Array(lanes.cap * n.axes),
|
package/dist/revive.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Mixer } from './mixer.js';
|
|
2
|
+
import type { Run } from './motions.js';
|
|
3
|
+
import { type PackedRecord } from './pack.js';
|
|
4
|
+
import { type Key } from './paging.js';
|
|
5
|
+
import type { Described } from './types.js';
|
|
6
|
+
import { type Controls, type Ramp, type Rise, Voice } from './voice.js';
|
|
7
|
+
/** A voice that has left, as plain data: the descriptor it was cued with, its controls and its records. */
|
|
8
|
+
interface PackedVoice {
|
|
9
|
+
as: Described;
|
|
10
|
+
start: number;
|
|
11
|
+
anchorNow: number;
|
|
12
|
+
anchorElapsed: number;
|
|
13
|
+
rate: number;
|
|
14
|
+
ramp: {
|
|
15
|
+
from: number;
|
|
16
|
+
to: number;
|
|
17
|
+
over: number;
|
|
18
|
+
} | null;
|
|
19
|
+
weight: number;
|
|
20
|
+
out: Ramp | null;
|
|
21
|
+
back: Rise | null;
|
|
22
|
+
outAt: number;
|
|
23
|
+
outOver: number;
|
|
24
|
+
outSet: boolean;
|
|
25
|
+
cuedAt: number;
|
|
26
|
+
doneAt: number;
|
|
27
|
+
opened: number;
|
|
28
|
+
pinned: number;
|
|
29
|
+
latest: number;
|
|
30
|
+
keeping: boolean;
|
|
31
|
+
first: Controls | null;
|
|
32
|
+
log: Controls[] | null;
|
|
33
|
+
played: boolean | undefined;
|
|
34
|
+
playedAt: number;
|
|
35
|
+
records: [Key, PackedRecord][];
|
|
36
|
+
runs: [Key, Run][];
|
|
37
|
+
left: [Key, number, PackedRecord][];
|
|
38
|
+
parts: [Key, number, number][];
|
|
39
|
+
parted: [Key, number][];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Voices a history store holds whose handles something still holds, by id: a seek that revives
|
|
43
|
+
* one puts it behind its handle, and needs it back if it left after the moment sought.
|
|
44
|
+
*/
|
|
45
|
+
export declare class Outs {
|
|
46
|
+
private readonly out;
|
|
47
|
+
private readonly gone;
|
|
48
|
+
add(id: number, doneAt: number, h: object): void;
|
|
49
|
+
/** Takes a revived voice's handle back, undefined where none is held. */
|
|
50
|
+
take(id: number): object | undefined;
|
|
51
|
+
/** The id of a voice that left after `t` and that `has` lacks; undefined for none. */
|
|
52
|
+
missing(t: number, has: (id: number) => boolean): number | undefined;
|
|
53
|
+
}
|
|
54
|
+
/** Pages out a voice that left before `reach`; false for one that has to stay in memory. */
|
|
55
|
+
export declare function pageVoice<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, reach: number): boolean;
|
|
56
|
+
/** A voice a history store paged out, rebuilt from its descriptor and put back as it left. */
|
|
57
|
+
export declare function reviveVoice<I, O>(mix: Mixer<I, O>, id: number, d: PackedVoice): Voice<I, O>;
|
|
58
|
+
export type { PackedVoice };
|
package/dist/revive.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { playable } from './cue.js';
|
|
2
|
+
import { VoiceHandle } from './handle.js';
|
|
3
|
+
import { handle } from './hosts.js';
|
|
4
|
+
import { packHeld, unpackHeld } from './pack.js';
|
|
5
|
+
import { pageOut } from './paging.js';
|
|
6
|
+
import { Voice } from './voice.js';
|
|
7
|
+
/**
|
|
8
|
+
* Voices a history store holds whose handles something still holds, by id: a seek that revives
|
|
9
|
+
* one puts it behind its handle, and needs it back if it left after the moment sought.
|
|
10
|
+
*/
|
|
11
|
+
export class Outs {
|
|
12
|
+
out = new Map();
|
|
13
|
+
gone = new FinalizationRegistry((id) => {
|
|
14
|
+
if (this.out.get(id)?.handle.deref() === undefined)
|
|
15
|
+
this.out.delete(id);
|
|
16
|
+
});
|
|
17
|
+
add(id, doneAt, h) {
|
|
18
|
+
this.out.set(id, { doneAt, handle: new WeakRef(h) });
|
|
19
|
+
this.gone.register(h, id);
|
|
20
|
+
}
|
|
21
|
+
/** Takes a revived voice's handle back, undefined where none is held. */
|
|
22
|
+
take(id) {
|
|
23
|
+
const e = this.out.get(id);
|
|
24
|
+
this.out.delete(id);
|
|
25
|
+
return e?.handle.deref();
|
|
26
|
+
}
|
|
27
|
+
/** The id of a voice that left after `t` and that `has` lacks; undefined for none. */
|
|
28
|
+
missing(t, has) {
|
|
29
|
+
for (const [id, e] of this.out)
|
|
30
|
+
if (e.doneAt > t && !has(id))
|
|
31
|
+
return id;
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Whether a voice can leave memory: it was cued with `as`, and nothing else in the mix holds it. */
|
|
36
|
+
function pageable(voice) {
|
|
37
|
+
return (voice.spec.as !== undefined &&
|
|
38
|
+
voice.owner === null &&
|
|
39
|
+
voice.holding === null &&
|
|
40
|
+
voice.blend === null &&
|
|
41
|
+
voice.answers === null &&
|
|
42
|
+
voice.fitting === null);
|
|
43
|
+
}
|
|
44
|
+
/** Pages out a voice that left before `reach`; false for one that has to stay in memory. */
|
|
45
|
+
export function pageVoice(mix, voice, reach) {
|
|
46
|
+
if (!pageable(voice))
|
|
47
|
+
return false;
|
|
48
|
+
const keys = mix.keys;
|
|
49
|
+
const data = packVoice(voice, keys);
|
|
50
|
+
pageOut(mix, 'voice', voice.id, undefined, [{ at: voice.doneAt }], () => data, reach);
|
|
51
|
+
const h = voice.handle;
|
|
52
|
+
VoiceHandle.page(h, { weight: voice.weight, played: voice.settled.played === true });
|
|
53
|
+
mix.outs.add(voice.id, voice.doneAt, h);
|
|
54
|
+
mix.parters.delete(voice);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
function packVoice(voice, keys) {
|
|
58
|
+
const subjectOf = (key) => keys.subject(key);
|
|
59
|
+
const records = [];
|
|
60
|
+
const runs = [];
|
|
61
|
+
for (const key of voice.keyed ?? []) {
|
|
62
|
+
const subject = subjectOf(key);
|
|
63
|
+
const held = voice.subjects.get(subject);
|
|
64
|
+
if (held?.reaches)
|
|
65
|
+
records.push([key, packHeld(voice, held)]);
|
|
66
|
+
const run = voice.motion?.pack(subject);
|
|
67
|
+
if (run !== undefined)
|
|
68
|
+
runs.push([key, run]);
|
|
69
|
+
}
|
|
70
|
+
const settled = voice.settled;
|
|
71
|
+
return {
|
|
72
|
+
as: voice.spec.as,
|
|
73
|
+
start: voice.start,
|
|
74
|
+
anchorNow: voice.anchorNow,
|
|
75
|
+
anchorElapsed: voice.anchorElapsed,
|
|
76
|
+
rate: voice.rate,
|
|
77
|
+
ramp: voice.ramp,
|
|
78
|
+
weight: voice.weight,
|
|
79
|
+
out: voice.out,
|
|
80
|
+
back: voice.back,
|
|
81
|
+
outAt: voice.outAt,
|
|
82
|
+
outOver: voice.outOver,
|
|
83
|
+
outSet: voice.outSet,
|
|
84
|
+
cuedAt: voice.cuedAt,
|
|
85
|
+
doneAt: voice.doneAt,
|
|
86
|
+
opened: voice.opened,
|
|
87
|
+
pinned: voice.pinned,
|
|
88
|
+
latest: voice.latest,
|
|
89
|
+
keeping: voice.keeping,
|
|
90
|
+
first: voice.first,
|
|
91
|
+
log: voice.log,
|
|
92
|
+
played: settled.played,
|
|
93
|
+
playedAt: settled.at,
|
|
94
|
+
records,
|
|
95
|
+
runs,
|
|
96
|
+
left: (voice.left ?? []).map((e) => [keys.key(e.subject), e.at, packHeld(voice, e.held)]),
|
|
97
|
+
parts: [...(voice.parts ?? [])].map(([s, r]) => [keys.key(s), r.at, r.over]),
|
|
98
|
+
parted: [...(voice.parted ?? [])].map(([s, at]) => [keys.key(s), at]),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** A voice a history store paged out, rebuilt from its descriptor and put back as it left. */
|
|
102
|
+
export function reviveVoice(mix, id, d) {
|
|
103
|
+
const revive = mix.opts.history?.revive;
|
|
104
|
+
if (revive === undefined)
|
|
105
|
+
throw new Error('blits: a paged voice needs history.revive');
|
|
106
|
+
const spec = { ...revive(d.as), as: d.as };
|
|
107
|
+
playable(mix, spec);
|
|
108
|
+
const voice = new Voice(id, spec, spec.patch, spec.fade ?? {}, Number.NEGATIVE_INFINITY, d.start, mix.slotOf, mix.channels, mix.opts.host, mix.send, null);
|
|
109
|
+
voice.anchorNow = d.anchorNow;
|
|
110
|
+
voice.anchorElapsed = d.anchorElapsed;
|
|
111
|
+
voice.rate = d.rate;
|
|
112
|
+
voice.ramp = d.ramp;
|
|
113
|
+
voice.weight = d.weight;
|
|
114
|
+
voice.out = d.out;
|
|
115
|
+
voice.back = d.back;
|
|
116
|
+
voice.outAt = d.outAt;
|
|
117
|
+
voice.outOver = d.outOver;
|
|
118
|
+
voice.outSet = d.outSet;
|
|
119
|
+
voice.cuedAt = d.cuedAt;
|
|
120
|
+
voice.doneAt = d.doneAt;
|
|
121
|
+
voice.opened = d.opened;
|
|
122
|
+
voice.pinned = d.pinned;
|
|
123
|
+
voice.latest = d.latest;
|
|
124
|
+
voice.keeping = d.keeping;
|
|
125
|
+
voice.first = d.first;
|
|
126
|
+
voice.log = d.log;
|
|
127
|
+
voice.state = 'done';
|
|
128
|
+
if (d.played !== undefined)
|
|
129
|
+
voice.play(d.played, d.playedAt);
|
|
130
|
+
voice.resolve();
|
|
131
|
+
const keys = mix.keys;
|
|
132
|
+
const subjectOf = (key) => keys.subject(key);
|
|
133
|
+
voice.keyed = new Set();
|
|
134
|
+
for (const [key, p] of d.records) {
|
|
135
|
+
voice.subjects.set(subjectOf(key), unpackHeld(voice, p));
|
|
136
|
+
voice.keyed.add(key);
|
|
137
|
+
}
|
|
138
|
+
for (const [key, run] of d.runs)
|
|
139
|
+
voice.motion?.unpack(subjectOf(key), run);
|
|
140
|
+
if (d.left.length > 0)
|
|
141
|
+
voice.left = d.left.map(([key, at, p]) => ({
|
|
142
|
+
subject: subjectOf(key),
|
|
143
|
+
at,
|
|
144
|
+
held: unpackHeld(voice, p),
|
|
145
|
+
}));
|
|
146
|
+
if (d.parts.length > 0)
|
|
147
|
+
voice.parts = new Map(d.parts.map(([key, at, over]) => [subjectOf(key), { at, over }]));
|
|
148
|
+
if (d.parted.length > 0)
|
|
149
|
+
voice.parted = new Map(d.parted.map(([key, at]) => [subjectOf(key), at]));
|
|
150
|
+
const h = mix.outs?.take(id);
|
|
151
|
+
if (h === undefined)
|
|
152
|
+
voice.handle = handle(mix, voice);
|
|
153
|
+
else {
|
|
154
|
+
VoiceHandle.rehome(h, voice);
|
|
155
|
+
voice.handle = h;
|
|
156
|
+
}
|
|
157
|
+
return voice;
|
|
158
|
+
}
|
package/dist/sample.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Lane, type Positions } from './lane.js';
|
|
2
2
|
import type { Lanes } from './lanes.js';
|
|
3
|
-
import type { Motions } from './
|
|
3
|
+
import type { Motions } from './motions.js';
|
|
4
4
|
import type { Subject } from './voice.js';
|
|
5
5
|
/**
|
|
6
6
|
* A motion voice's fill: what `one` and `move` do for each subject, in one loop for the common
|
package/dist/sample.js
CHANGED
|
@@ -17,6 +17,7 @@ export function runMotion(lanes, lane, run) {
|
|
|
17
17
|
const ch = lane.chans[0];
|
|
18
18
|
const fills = lanes.fills;
|
|
19
19
|
const from = lanes.frameProbes;
|
|
20
|
+
const lately = lanes.lately;
|
|
20
21
|
const seeks = voice.seeks;
|
|
21
22
|
const elapsedNow = voice.elapsedAt(lanes.now);
|
|
22
23
|
const flat = lane.flat;
|
|
@@ -57,6 +58,10 @@ export function runMotion(lanes, lane, run) {
|
|
|
57
58
|
const q = slot * 6 /* Per.SLOT */;
|
|
58
59
|
if (per[q + 5 /* Per.LANE_FILL */] === fills - 1)
|
|
59
60
|
data[o + 3 /* Row.PROBED */] = data[o + 2 /* Row.WEIGHT */];
|
|
61
|
+
if (lately >= 0 && lanes.unread(slot)) {
|
|
62
|
+
unbareLane(lane, p);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
60
65
|
const delay = data[o + 0 /* Row.DELAY */];
|
|
61
66
|
const elapsed = elapsedNow - delay;
|
|
62
67
|
if (elapsed < 0) {
|
|
@@ -106,9 +111,9 @@ export function runMotion(lanes, lane, run) {
|
|
|
106
111
|
data[o + 5 /* Row.SAMPLED */] = -2 /* Sampled.BARE */;
|
|
107
112
|
lane.deltas[p] = null;
|
|
108
113
|
}
|
|
109
|
-
const share = left(ease, progress(elapsed, runs[b], runs[b + 2]));
|
|
110
114
|
if (w <= 0)
|
|
111
115
|
continue;
|
|
116
|
+
const share = left(ease, progress(elapsed, runs[b], runs[b + 2]));
|
|
112
117
|
const values = ch.values;
|
|
113
118
|
const base = slot * n;
|
|
114
119
|
const x = b + 3;
|
package/dist/seek.d.ts
ADDED