@msb235/blits 0.8.0 → 0.9.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 +95 -0
- package/README.md +12 -2
- package/dist/clock.d.ts +10 -0
- package/dist/clock.js +23 -0
- package/dist/clone.d.ts +5 -0
- package/dist/clone.js +7 -0
- package/dist/cue.js +5 -8
- package/dist/due.js +20 -13
- package/dist/everyone.js +3 -0
- package/dist/fade.d.ts +1 -0
- package/dist/fade.js +10 -4
- package/dist/fill.js +5 -0
- package/dist/fold.js +31 -25
- package/dist/frames.d.ts +21 -0
- package/dist/frames.js +59 -0
- package/dist/gone.d.ts +4 -4
- package/dist/gone.js +11 -10
- package/dist/handle.js +17 -9
- package/dist/held.d.ts +10 -0
- package/dist/held.js +28 -1
- package/dist/hosts.js +5 -1
- package/dist/keyfill.js +5 -1
- package/dist/locus.d.ts +4 -1
- package/dist/locus.js +14 -4
- package/dist/marks.js +4 -4
- package/dist/motion.d.ts +6 -3
- package/dist/motions.d.ts +5 -2
- package/dist/motions.js +3 -1
- package/dist/move.js +51 -14
- package/dist/origin.js +3 -2
- package/dist/owner.d.ts +2 -0
- package/dist/owner.js +10 -2
- package/dist/paging.js +1 -6
- package/dist/place.js +17 -3
- package/dist/project.d.ts +1 -1
- package/dist/project.js +12 -7
- package/dist/record.js +2 -1
- package/dist/revive.d.ts +2 -0
- package/dist/revive.js +2 -0
- package/dist/seek.js +4 -4
- package/dist/spans.d.ts +5 -4
- package/dist/spans.js +11 -10
- package/dist/stack.js +2 -2
- package/dist/strays.js +13 -5
- package/dist/transport.d.ts +2 -6
- package/dist/transport.js +7 -30
- package/dist/types/mix.d.ts +3 -2
- package/dist/types/patch.d.ts +7 -3
- package/dist/types/place.d.ts +4 -1
- package/dist/types/voice.d.ts +22 -4
- package/dist/unpage.d.ts +1 -1
- package/dist/unpage.js +3 -4
- package/dist/voice.d.ts +52 -0
- package/dist/voice.js +103 -2
- package/dist/warn.d.ts +2 -0
- package/dist/warn.js +17 -0
- package/dist/weigh.js +5 -0
- package/docs/schema.html +92 -29
- package/package.json +2 -2
package/dist/project.js
CHANGED
|
@@ -31,7 +31,7 @@ export function projectAll(transport, t) {
|
|
|
31
31
|
const kept = transport.history !== undefined;
|
|
32
32
|
if (!ahead && kept) {
|
|
33
33
|
cover(transport, t);
|
|
34
|
-
const frame = transport.
|
|
34
|
+
const frame = transport.frames.at(t);
|
|
35
35
|
if (frame !== undefined) {
|
|
36
36
|
cut = { seq: frame.seq, strict: frame.at === t };
|
|
37
37
|
if (frame.at === t)
|
|
@@ -68,7 +68,9 @@ export function projectAll(transport, t) {
|
|
|
68
68
|
else
|
|
69
69
|
copyStanding(mix, copy, t, cut);
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
// A read at the mix's own time lands nothing, as a probe does not: what the host has done since
|
|
72
|
+
// the last sync shows at the next one.
|
|
73
|
+
if (ahead && t !== transport.now)
|
|
72
74
|
for (const copy of copies.values())
|
|
73
75
|
move(copy, t);
|
|
74
76
|
const read = (f) => {
|
|
@@ -131,7 +133,7 @@ function copyBack(mix, c, t, cut) {
|
|
|
131
133
|
.sort((a, b) => a.id - b.id)
|
|
132
134
|
.map((v) => {
|
|
133
135
|
const log = v.log;
|
|
134
|
-
const copy = v.copy((subject) => recall(mix, v, subject, t, cut), lastWithin(log, cut) ?? log[0]);
|
|
136
|
+
const copy = v.copy((subject) => recall(mix, v, subject, t, cut, c, copy), lastWithin(log, cut) ?? log[0]);
|
|
135
137
|
if (then !== undefined)
|
|
136
138
|
copy.setting.host = then;
|
|
137
139
|
return copy;
|
|
@@ -199,7 +201,7 @@ function carry(mix, voice, subject, c, copy) {
|
|
|
199
201
|
* A projection back starts each subject from the latest copy kept at or before `t`, else from its
|
|
200
202
|
* voice's start with fresh state, which is exact for a voice stepped at a fixed interval.
|
|
201
203
|
*/
|
|
202
|
-
export function recall(mix, voice, subject, t, cut) {
|
|
204
|
+
export function recall(mix, voice, subject, t, cut, c, copy) {
|
|
203
205
|
const live = voice.left?.at(subject, cut) ?? recordOf(mix, voice, subject);
|
|
204
206
|
if (live === undefined)
|
|
205
207
|
return undefined;
|
|
@@ -210,13 +212,16 @@ export function recall(mix, voice, subject, t, cut) {
|
|
|
210
212
|
h.replay = live.inputs;
|
|
211
213
|
return h;
|
|
212
214
|
}
|
|
213
|
-
|
|
215
|
+
// Its origin by the controls the voice had then: a seek or an anchor has moved a waiting
|
|
216
|
+
// voice's since.
|
|
217
|
+
const since = c === undefined || copy === undefined ? live.since : c.sinceOf(copy, live.delay);
|
|
218
|
+
const stepped = since < t ? since : t;
|
|
214
219
|
const h = record(voice, live.reaches, live.reaches && voice.patch.state
|
|
215
220
|
? voice.patch.state(subject)
|
|
216
221
|
: undefined);
|
|
217
222
|
h.delay = live.delay;
|
|
218
|
-
h.since =
|
|
219
|
-
h.shown = live.shown;
|
|
223
|
+
h.since = since;
|
|
224
|
+
h.shown = c === undefined || copy === undefined ? live.shown : c.shownOf(copy, since);
|
|
220
225
|
h.stepped = stepped;
|
|
221
226
|
h.rebuilt = voice.rebuilds;
|
|
222
227
|
h.from = stepped;
|
package/dist/record.js
CHANGED
|
@@ -15,7 +15,8 @@ export function record(voice, reaches, state) {
|
|
|
15
15
|
rested: false,
|
|
16
16
|
bands: null,
|
|
17
17
|
state,
|
|
18
|
-
|
|
18
|
+
// -0, not 0: a field first given a small integer is boxed on every later write of a double.
|
|
19
|
+
stepped: -0,
|
|
19
20
|
ticks: 0,
|
|
20
21
|
probed: Number.NaN,
|
|
21
22
|
delta: null,
|
package/dist/revive.d.ts
CHANGED
package/dist/revive.js
CHANGED
|
@@ -92,6 +92,7 @@ function packVoice(voice, keys) {
|
|
|
92
92
|
opened: voice.opened,
|
|
93
93
|
pinned: voice.pinned,
|
|
94
94
|
latest: voice.latest,
|
|
95
|
+
soonest: voice.soonest,
|
|
95
96
|
keeping: voice.keeping,
|
|
96
97
|
first: voice.first,
|
|
97
98
|
log: voice.log,
|
|
@@ -128,6 +129,7 @@ export function reviveVoice(mix, id, d) {
|
|
|
128
129
|
voice.opened = d.opened;
|
|
129
130
|
voice.pinned = d.pinned;
|
|
130
131
|
voice.latest = d.latest;
|
|
132
|
+
voice.soonest = d.soonest ?? 0;
|
|
131
133
|
voice.keeping = d.keeping;
|
|
132
134
|
voice.first = d.first;
|
|
133
135
|
voice.log = d.log;
|
package/dist/seek.js
CHANGED
|
@@ -143,7 +143,7 @@ export function seek(transport, t) {
|
|
|
143
143
|
throw new RangeError('blits: a mix seeks to a finite time');
|
|
144
144
|
if (!(t >= transport.born))
|
|
145
145
|
throw new Error(`blits: ${t} is older than this mix's history reaches`);
|
|
146
|
-
|
|
146
|
+
cover(transport, t);
|
|
147
147
|
if (t >= transport.now) {
|
|
148
148
|
// Ahead, under the rate the recorded calls up to `t` set, past any frames rate 0 held at `t`.
|
|
149
149
|
const last = replay(transport, (at) => (transport.pace === null ? at : transport.pace.reading(at)) <= t);
|
|
@@ -155,7 +155,7 @@ export function seek(transport, t) {
|
|
|
155
155
|
move(m, t);
|
|
156
156
|
}
|
|
157
157
|
else {
|
|
158
|
-
const frame = transport.
|
|
158
|
+
const frame = transport.frames.at(t);
|
|
159
159
|
if (frame === undefined)
|
|
160
160
|
throw new Error(`blits: ${t} is older than this mix's history reaches`);
|
|
161
161
|
// On the frame itself, its own host time: rate 0 may have held the clock there for several.
|
|
@@ -165,7 +165,7 @@ export function seek(transport, t) {
|
|
|
165
165
|
transport.undrop(seq);
|
|
166
166
|
// What history kept after the frame is let go; the tape holds the calls that made it.
|
|
167
167
|
transport.announced = transport.announced.filter((a) => a.seq <= seq);
|
|
168
|
-
transport.frames
|
|
168
|
+
transport.frames.set(transport.frames.all().filter((f) => f.seq <= seq));
|
|
169
169
|
if (transport.pacedSeq > seq) {
|
|
170
170
|
transport.pace = null;
|
|
171
171
|
transport.pacedSeq = Number.POSITIVE_INFINITY;
|
|
@@ -176,7 +176,7 @@ export function seek(transport, t) {
|
|
|
176
176
|
transport.now = t;
|
|
177
177
|
for (const m of transport.members)
|
|
178
178
|
back(m, t, seq, frame.at < t);
|
|
179
|
-
cut(transport, seq
|
|
179
|
+
cut(transport, seq);
|
|
180
180
|
}
|
|
181
181
|
transport.tick(t);
|
|
182
182
|
// The host's clock reads on from here: its next sync reads `t` plus its time since its last.
|
package/dist/spans.d.ts
CHANGED
|
@@ -16,11 +16,12 @@ export declare class Fitting {
|
|
|
16
16
|
/** Refuses a voice a span cannot fit: one it would not own the timing of, or one with no end. */
|
|
17
17
|
export declare function checkChild<I, O>(spec: VoiceSpec<I, O>, child: Voice<I, O>): void;
|
|
18
18
|
/**
|
|
19
|
-
* Lays out a span's children and fits them into what is left of its budget, from its clock
|
|
20
|
-
* Only what is still to come moves: a playing child keeps its place and
|
|
21
|
-
* pending one is placed. A span held by a span is fitted again by its own span
|
|
19
|
+
* Lays out a span's children and fits them into what is left of its budget, from its clock at mix
|
|
20
|
+
* time `from`, default now. Only what is still to come moves: a playing child keeps its place and
|
|
21
|
+
* may change rate, a pending one is placed. A span held by a span is fitted again by its own span
|
|
22
|
+
* after.
|
|
22
23
|
*/
|
|
23
|
-
export declare function refit<I, O>(mix: Mixer<I, O>, span: Voice<I, O
|
|
24
|
+
export declare function refit<I, O>(mix: Mixer<I, O>, span: Voice<I, O>, from?: number): void;
|
|
24
25
|
/** Whether a span whose last child just left at mix time `at` stays, its budget not yet out. */
|
|
25
26
|
export declare function lingers<I, O>(span: Voice<I, O>, at: number): boolean;
|
|
26
27
|
/** Lets the spans that are empty and out of budget leave, at the moment their budget ran out. */
|
package/dist/spans.js
CHANGED
|
@@ -46,11 +46,11 @@ export function checkChild(spec, child) {
|
|
|
46
46
|
else if (!Number.isFinite(child.span))
|
|
47
47
|
throw new Error('blits: a span cannot fit a voice that never ends');
|
|
48
48
|
}
|
|
49
|
-
/** A span's own clock at
|
|
50
|
-
function clockOf(
|
|
51
|
-
if (Number.isNaN(
|
|
49
|
+
/** A span's own clock at mix time `at`, from 0 while it has not started. */
|
|
50
|
+
function clockOf(span, at) {
|
|
51
|
+
if (Number.isNaN(at))
|
|
52
52
|
return 0;
|
|
53
|
-
const t = ownerReading(span,
|
|
53
|
+
const t = ownerReading(span, at);
|
|
54
54
|
return Number.isFinite(t) ? Math.max(0, t) : 0;
|
|
55
55
|
}
|
|
56
56
|
/** What a child's own clock reads at its span's `local`, 0 while it waits. */
|
|
@@ -63,15 +63,16 @@ function leftOf(child, local) {
|
|
|
63
63
|
return Math.max(0, length - elapsedIn(child, local));
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
|
-
* Lays out a span's children and fits them into what is left of its budget, from its clock
|
|
67
|
-
* Only what is still to come moves: a playing child keeps its place and
|
|
68
|
-
* pending one is placed. A span held by a span is fitted again by its own span
|
|
66
|
+
* Lays out a span's children and fits them into what is left of its budget, from its clock at mix
|
|
67
|
+
* time `from`, default now. Only what is still to come moves: a playing child keeps its place and
|
|
68
|
+
* may change rate, a pending one is placed. A span held by a span is fitted again by its own span
|
|
69
|
+
* after.
|
|
69
70
|
*/
|
|
70
|
-
export function refit(mix, span) {
|
|
71
|
+
export function refit(mix, span, from = mix.now) {
|
|
71
72
|
const fitting = span.fitting;
|
|
72
73
|
if (mix.projecting || span.state === 'done')
|
|
73
74
|
return;
|
|
74
|
-
const local = clockOf(
|
|
75
|
+
const local = clockOf(span, from);
|
|
75
76
|
const kids = (span.holding?.children ?? []).filter((c) => c.state !== 'done' && c.state !== 'frozen' && c.state !== 'fading' && leftOf(c, local) > 0);
|
|
76
77
|
const claims = kids.map((c) => {
|
|
77
78
|
const s = c.spec;
|
|
@@ -143,7 +144,7 @@ export function refit(mix, span) {
|
|
|
143
144
|
fitting.end = end;
|
|
144
145
|
noted(mix, span);
|
|
145
146
|
if (span.owner?.fitting)
|
|
146
|
-
refit(mix, span.owner);
|
|
147
|
+
refit(mix, span.owner, from);
|
|
147
148
|
}
|
|
148
149
|
}
|
|
149
150
|
/** Whether a span whose last child just left at mix time `at` stays, its budget not yet out. */
|
package/dist/stack.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { copy } from './clone.js';
|
|
1
|
+
import { copy, first } from './clone.js';
|
|
2
2
|
import { bound, merged } from './fold.js';
|
|
3
3
|
/** The weight a mix's default band first switches a rest-less channel on at. */
|
|
4
4
|
const ON = 0.6;
|
|
@@ -35,7 +35,7 @@ export function fold(kit, deltas, weights) {
|
|
|
35
35
|
? channel.fold(pose[key], value, w)
|
|
36
36
|
: merged(channel, pose[key], channel.scale(value, w));
|
|
37
37
|
else if (w >= ON)
|
|
38
|
-
pose[key] = pose[key] === undefined ?
|
|
38
|
+
pose[key] = pose[key] === undefined ? first(value) : merged(channel, pose[key], value);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
for (const key of names) {
|
package/dist/strays.js
CHANGED
|
@@ -64,14 +64,18 @@ export function motionOwner(mix) {
|
|
|
64
64
|
revive(id, subject) {
|
|
65
65
|
const m = mix.deref();
|
|
66
66
|
if (m === undefined)
|
|
67
|
-
return;
|
|
67
|
+
return Number.NaN;
|
|
68
68
|
m.stir();
|
|
69
69
|
const v = cuedById(m, id);
|
|
70
70
|
if (v === undefined)
|
|
71
|
-
return;
|
|
71
|
+
return Number.NaN;
|
|
72
72
|
if (v.motion !== undefined && v.named !== null && !v.named.has(subject))
|
|
73
73
|
stray(m, v.motion, subject);
|
|
74
|
+
if (v.parted?.has(subject) !== true)
|
|
75
|
+
return Number.NaN;
|
|
74
76
|
unpart(m, v, subject);
|
|
77
|
+
// Its record is gone until it is next read, so its stagger is asked here as it will be there.
|
|
78
|
+
return timeIn(m, v, v.spec.stagger ? v.spec.stagger(subject) : 0);
|
|
75
79
|
},
|
|
76
80
|
};
|
|
77
81
|
}
|
|
@@ -98,11 +102,15 @@ export function cuedById(mix, id) {
|
|
|
98
102
|
* subject's own time is still short of its stagger. A retired voice's patch no longer asks.
|
|
99
103
|
*/
|
|
100
104
|
export function frameOf(mix, voice, subject) {
|
|
101
|
-
if (Number.isNaN(mix.now) || voice.state === 'pending')
|
|
102
|
-
return Number.NaN;
|
|
103
105
|
const held = voice.subjects.get(subject);
|
|
104
106
|
if (held === undefined || !held.reaches)
|
|
105
107
|
return Number.NaN;
|
|
106
|
-
|
|
108
|
+
return timeIn(mix, voice, held.delay);
|
|
109
|
+
}
|
|
110
|
+
/** The voice time at the latest frame of a subject staggered by `delay`; NaN before it has any. */
|
|
111
|
+
function timeIn(mix, voice, delay) {
|
|
112
|
+
if (Number.isNaN(mix.now) || voice.state === 'pending')
|
|
113
|
+
return Number.NaN;
|
|
114
|
+
const t = voice.elapsedAt(mix.now) - delay;
|
|
107
115
|
return t < 0 ? Number.NaN : t;
|
|
108
116
|
}
|
package/dist/transport.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Frames } from './frames.js';
|
|
1
2
|
import type { Mixer } from './mixer.js';
|
|
2
3
|
import { Pace } from './pace.js';
|
|
3
4
|
import { Pager } from './paging.js';
|
|
@@ -68,7 +69,7 @@ export declare class Transport implements TransportApi {
|
|
|
68
69
|
*/
|
|
69
70
|
seq: number;
|
|
70
71
|
/** Under history, the frames a seek or read back may reach, oldest first. */
|
|
71
|
-
frames:
|
|
72
|
+
frames: Frames;
|
|
72
73
|
/** Host time at the last sync: the host's timestamp less every gap `rebase` took out. */
|
|
73
74
|
u: number;
|
|
74
75
|
offset: number;
|
|
@@ -131,11 +132,6 @@ export declare class Transport implements TransportApi {
|
|
|
131
132
|
private syncAt;
|
|
132
133
|
/** Begins a frame at mix time `at` and the host time the transport stands at. */
|
|
133
134
|
tick(at: number): void;
|
|
134
|
-
/**
|
|
135
|
-
* The frame a seek or read to mix time `t` lands on: the last one standing at or before `t`,
|
|
136
|
-
* since rate 0 can hold the clock at `t` for many.
|
|
137
|
-
*/
|
|
138
|
-
frameAt(t: number): Frame | undefined;
|
|
139
135
|
rebase(): void;
|
|
140
136
|
/**
|
|
141
137
|
* Moves `floor` past what the mix clock standing at `now` has let go of. With a store nothing is
|
package/dist/transport.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Frames } from './frames.js';
|
|
1
2
|
import { listedAll } from './marks.js';
|
|
2
3
|
import { move, nextFrame, waits } from './move.js';
|
|
3
4
|
import { Pace } from './pace.js';
|
|
@@ -6,7 +7,7 @@ import { pin } from './place.js';
|
|
|
6
7
|
import { projectAll } from './project.js';
|
|
7
8
|
import { scoredDrop, scoredJoin } from './scored.js';
|
|
8
9
|
import { seek } from './seek.js';
|
|
9
|
-
import { record, replay, tapeOf } from './tape.js';
|
|
10
|
+
import { hostAt, record, replay, tapeOf } from './tape.js';
|
|
10
11
|
/** How far the tape's reach moves before it is pruned again, ms of mix time. */
|
|
11
12
|
const TAPE_STEP_MS = 1000;
|
|
12
13
|
/** Whether a change made in frame `seq` falls inside `cut`; `early` for one made before the frame was read. */
|
|
@@ -39,7 +40,7 @@ export class Transport {
|
|
|
39
40
|
*/
|
|
40
41
|
seq = 0;
|
|
41
42
|
/** Under history, the frames a seek or read back may reach, oldest first. */
|
|
42
|
-
frames =
|
|
43
|
+
frames = new Frames();
|
|
43
44
|
/** Host time at the last sync: the host's timestamp less every gap `rebase` took out. */
|
|
44
45
|
u = Number.NaN;
|
|
45
46
|
offset = 0;
|
|
@@ -215,17 +216,14 @@ export class Transport {
|
|
|
215
216
|
// In steps: the tape shifts every entry it keeps to let go of the oldest, and one a second
|
|
216
217
|
// behind reach is never played again.
|
|
217
218
|
if (this.tape !== undefined && history !== undefined && !(reach - this.taped < TAPE_STEP_MS)) {
|
|
218
|
-
this
|
|
219
|
+
const from = hostAt(this, reach);
|
|
220
|
+
if (from < Number.POSITIVE_INFINITY)
|
|
221
|
+
this.tape.prune(from);
|
|
219
222
|
this.taped = reach;
|
|
220
223
|
}
|
|
221
224
|
if (history !== undefined && this.dropped.length > 0)
|
|
222
225
|
this.dropped = this.dropped.filter((d) => d.at >= reach);
|
|
223
|
-
|
|
224
|
-
let n = 0;
|
|
225
|
-
while (n + 1 < frames.length && frames[n + 1].at <= reach)
|
|
226
|
-
n++;
|
|
227
|
-
if (n > 0)
|
|
228
|
-
frames.splice(0, n);
|
|
226
|
+
this.frames.shed(reach);
|
|
229
227
|
pageTransport(this);
|
|
230
228
|
this.woken = false;
|
|
231
229
|
// It is a frame too, which asks every weight signal again, so one reading input follows it.
|
|
@@ -249,27 +247,6 @@ export class Transport {
|
|
|
249
247
|
for (const m of this.members)
|
|
250
248
|
m.looked = false;
|
|
251
249
|
}
|
|
252
|
-
/**
|
|
253
|
-
* The frame a seek or read to mix time `t` lands on: the last one standing at or before `t`,
|
|
254
|
-
* since rate 0 can hold the clock at `t` for many.
|
|
255
|
-
*/
|
|
256
|
-
frameAt(t) {
|
|
257
|
-
const frames = this.frames;
|
|
258
|
-
let lo = 0;
|
|
259
|
-
let hi = frames.length - 1;
|
|
260
|
-
let found;
|
|
261
|
-
while (lo <= hi) {
|
|
262
|
-
const mid = (lo + hi) >> 1;
|
|
263
|
-
const f = frames[mid];
|
|
264
|
-
if (f.at <= t) {
|
|
265
|
-
found = f;
|
|
266
|
-
lo = mid + 1;
|
|
267
|
-
}
|
|
268
|
-
else
|
|
269
|
-
hi = mid - 1;
|
|
270
|
-
}
|
|
271
|
-
return found;
|
|
272
|
-
}
|
|
273
250
|
rebase() {
|
|
274
251
|
this.rebasing = true;
|
|
275
252
|
this.settle();
|
package/dist/types/mix.d.ts
CHANGED
|
@@ -192,8 +192,9 @@ export interface Mix<I, O, H = unknown> {
|
|
|
192
192
|
*/
|
|
193
193
|
pull(subjects: Iterable<I>, into: Columns<O>): void;
|
|
194
194
|
/**
|
|
195
|
-
* Reads the mix at another mix time, without moving it.
|
|
196
|
-
*
|
|
195
|
+
* Reads the mix at another mix time, without moving it. At the mix's own time it reads what
|
|
196
|
+
* `probe` does: what the host has done since the last sync lands at the next. Ahead of the mix
|
|
197
|
+
* it plays what is cued forward, and throws for a time past a call the tape will play again after a `seek` back, which
|
|
197
198
|
* only a seek there makes; behind it, with `history`, it reads the last frame at or before `time`
|
|
198
199
|
* as that frame showed, and throws for a time older than the history reaches. On a transport it
|
|
199
200
|
* reads every mix on it together, so anchors across them answer.
|
package/dist/types/patch.d.ts
CHANGED
|
@@ -39,12 +39,16 @@ export interface Setting<S = void, H = unknown> {
|
|
|
39
39
|
* The gap this subject is catching up by: milliseconds since it last advanced, which is the gap
|
|
40
40
|
* since the previous sync only for a subject sampled every frame. Infinity under reduced motion;
|
|
41
41
|
* no more than `maxDt` where the mix sets one. Inside a `step` under `stepMs`, it is `stepMs`, and
|
|
42
|
-
* `timestamp` is the end of that interval.
|
|
42
|
+
* `timestamp` is the end of that interval. A lane samples a subject for one frame after its last
|
|
43
|
+
* probe. A read by `project` is one sample, so its gap is not the gap a frame of play has.
|
|
43
44
|
*/
|
|
44
45
|
dt: number;
|
|
45
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Milliseconds this voice has been playing, rate applied. Below 0 only for a voice with no end
|
|
48
|
+
* cued backward, which plays into the passes before its first.
|
|
49
|
+
*/
|
|
46
50
|
elapsed: number;
|
|
47
|
-
/** Which pass this is, 0 first. */
|
|
51
|
+
/** Which pass this is, 0 first; below 0 where `elapsed` is. */
|
|
48
52
|
pass: number;
|
|
49
53
|
/** This voice's weight for this subject this frame, after fades, signals and any locus. */
|
|
50
54
|
weight: number;
|
package/dist/types/place.d.ts
CHANGED
|
@@ -55,7 +55,10 @@ export type Anchor = {
|
|
|
55
55
|
* Where a voice sits on the mix clock: at most one of `start` and `in`, and at most one of `out`
|
|
56
56
|
* and `end`, each a timestamp on the host's clock or an anchor to another voice. A voice whose
|
|
57
57
|
* anchor has no answer yet waits pending; one whose anchored mark is already past starts partway
|
|
58
|
-
* through, as playback does from the middle of a region.
|
|
58
|
+
* through, as playback does from the middle of a region. A fade out begins no earlier than the
|
|
59
|
+
* voice starts: an `end` that leaves less room than `fade.out` shortens the fade to fit, and one
|
|
60
|
+
* before the start makes the voice an instant, which starts and ends at its start and shows
|
|
61
|
+
* nothing.
|
|
59
62
|
*
|
|
60
63
|
* @category score
|
|
61
64
|
*/
|
package/dist/types/voice.d.ts
CHANGED
|
@@ -61,15 +61,30 @@ export interface VoiceSpec<I, O, H = unknown> extends SpanHints {
|
|
|
61
61
|
subjects?: readonly I[];
|
|
62
62
|
/**
|
|
63
63
|
* When the voice starts, in ms on the host's clock, the one it passes `sync`: a rAF timestamp and
|
|
64
|
-
* `performance.now()` share an origin, `Date.now()` does not. Default: the
|
|
64
|
+
* `performance.now()` share an origin, `Date.now()` does not. Default: the time the mix last
|
|
65
|
+
* synced to, and 0 for a voice cued before the first sync, which that sync finds already as far
|
|
66
|
+
* in as its timestamp.
|
|
65
67
|
*/
|
|
66
68
|
start?: number;
|
|
67
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* Playback rate. 1 is real time; 0 freezes; below 0 plays backward, to end where the voice
|
|
71
|
+
* starts as a voice playing forward ends where its last pass does: `played` resolves, and it
|
|
72
|
+
* leaves over `fade.out` or, freezing before, holds its first frame.
|
|
73
|
+
*/
|
|
68
74
|
rate?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Where in itself the voice starts, in voice ms, as `seek` on its handle sets. Default 0, or
|
|
77
|
+
* for a voice cued backward its end: `duration` times its passes. A voice with no end cued
|
|
78
|
+
* backward (a loop for good, or a patch with no duration) starts at 0 and has no first pass
|
|
79
|
+
* either: it plays into the pass before, `setting.pass` and `setting.elapsed` below 0, for
|
|
80
|
+
* every subject from the voice's start whatever its stagger. A motion's stretches begin at 0,
|
|
81
|
+
* so one cued backward with no `seek` has ended as it starts.
|
|
82
|
+
*/
|
|
83
|
+
seek?: number;
|
|
69
84
|
/**
|
|
70
85
|
* true loops for good, false plays one pass, n plays n passes. A finite loop leaves when its
|
|
71
86
|
* passes are done for the latest-staggered subject it has seen — over `fade.out` where one is
|
|
72
|
-
* set, at once where none is — unless it freezes after.
|
|
87
|
+
* set, at once where none is — unless it freezes after. Default: true.
|
|
73
88
|
*/
|
|
74
89
|
loop?: boolean | number;
|
|
75
90
|
/**
|
|
@@ -249,7 +264,8 @@ export interface Handle<I = unknown> {
|
|
|
249
264
|
weight: number;
|
|
250
265
|
/**
|
|
251
266
|
* Playback rate now. Setting it changes speed at once; `ramp` eases into a new one. On a voice
|
|
252
|
-
* still pending, a rate
|
|
267
|
+
* still pending, a rate or a ramp applies from the voice's start, which stays where it is: at
|
|
268
|
+
* rate 0 it starts on time and holds its first frame.
|
|
253
269
|
*/
|
|
254
270
|
rate: number;
|
|
255
271
|
/**
|
|
@@ -265,6 +281,8 @@ export interface Handle<I = unknown> {
|
|
|
265
281
|
* it was. Answers how sure the state now is: `exact` for a voice with none, or rebuilt under
|
|
266
282
|
* `stepMs` with no `maxDt`; `stepped` where it was caught up in one step or holds signal state;
|
|
267
283
|
* `held` where it was kept, or depends on host fields or an input weight the rebuild cannot know.
|
|
284
|
+
* A voice still pending starts on time, already at `elapsed`, and one that freezes before shows
|
|
285
|
+
* that frame while it waits.
|
|
268
286
|
*/
|
|
269
287
|
seek(elapsed: number, opts?: SeekOptions): Doubt;
|
|
270
288
|
/**
|
package/dist/unpage.d.ts
CHANGED
|
@@ -5,4 +5,4 @@ import type { Transport } from './transport.js';
|
|
|
5
5
|
*/
|
|
6
6
|
export declare function cover(transport: Transport, t: number): boolean;
|
|
7
7
|
/** A seek back to frame `seq`: what the store and `prepare` hold after it is from a future the tape makes again. */
|
|
8
|
-
export declare function cut(transport: Transport, seq: number
|
|
8
|
+
export declare function cut(transport: Transport, seq: number): void;
|
package/dist/unpage.js
CHANGED
|
@@ -30,12 +30,11 @@ export function cover(transport, t) {
|
|
|
30
30
|
return true;
|
|
31
31
|
}
|
|
32
32
|
/** A seek back to frame `seq`: what the store and `prepare` hold after it is from a future the tape makes again. */
|
|
33
|
-
export function cut(transport, seq
|
|
33
|
+
export function cut(transport, seq) {
|
|
34
34
|
const pager = transport.pager;
|
|
35
35
|
if (pager === null)
|
|
36
36
|
return;
|
|
37
|
-
|
|
38
|
-
pager.store.cut(seq);
|
|
37
|
+
pager.store.cut(seq);
|
|
39
38
|
const loaded = pager.loaded;
|
|
40
39
|
if (loaded !== null)
|
|
41
40
|
loaded.records = loaded.records.filter((r) => r.seq <= seq);
|
|
@@ -69,7 +68,7 @@ function unpageTransport(transport, records) {
|
|
|
69
68
|
const frames = own
|
|
70
69
|
.filter((r) => r.stream === 'frame')
|
|
71
70
|
.map((r) => ({ seq: r.seq, at: r.at, u: r.data }));
|
|
72
|
-
transport.frames
|
|
71
|
+
transport.frames.set(before(frames, transport.frames.all()));
|
|
73
72
|
const held = new Set(transport.announced.map((a) => a.order));
|
|
74
73
|
const marks = own
|
|
75
74
|
.filter((r) => r.stream === 'mark' && !held.has(r.data.order))
|
package/dist/voice.d.ts
CHANGED
|
@@ -127,6 +127,8 @@ export interface Ramp {
|
|
|
127
127
|
over: number;
|
|
128
128
|
rest: boolean;
|
|
129
129
|
deadline?: number;
|
|
130
|
+
/** Begun where the voice ran back to its start, whose first frame it holds through the fade. */
|
|
131
|
+
back?: boolean;
|
|
130
132
|
}
|
|
131
133
|
/**
|
|
132
134
|
* A climb back from a fade out that was turned around: from `from`, the place along the fade's
|
|
@@ -224,6 +226,12 @@ export declare class Voice<I, O> {
|
|
|
224
226
|
readonly span: number;
|
|
225
227
|
/** The state behind a motion patch, undefined for any other. */
|
|
226
228
|
readonly motion: Motions<I> | undefined;
|
|
229
|
+
/**
|
|
230
|
+
* Whether it has no first pass: cued backward with no end to start from, it plays from 0 into
|
|
231
|
+
* the pass before, so no time on its clock is before its start. Never a motion, whose stretches
|
|
232
|
+
* begin at 0.
|
|
233
|
+
*/
|
|
234
|
+
readonly beginless: boolean;
|
|
227
235
|
/** Its `freeze`, for a patch it applies to: motion keeps its target already. */
|
|
228
236
|
readonly freezesBefore: boolean;
|
|
229
237
|
readonly freezesAfter: boolean;
|
|
@@ -244,6 +252,8 @@ export declare class Voice<I, O> {
|
|
|
244
252
|
seen: number;
|
|
245
253
|
/** The longest stagger of any subject seen, so a finite loop waits for the last of them. */
|
|
246
254
|
latest: number;
|
|
255
|
+
/** The least stagger of any subject seen, 0 or below, which a voice running back ends at. */
|
|
256
|
+
soonest: number;
|
|
247
257
|
restedCount: number;
|
|
248
258
|
/** The mix time it was cued at; -Infinity before the first sync. */
|
|
249
259
|
cuedAt: number;
|
|
@@ -253,6 +263,10 @@ export declare class Voice<I, O> {
|
|
|
253
263
|
doneAt: number;
|
|
254
264
|
/** The frame its leaving was decided in, which a seek or read back partitions by. */
|
|
255
265
|
doneSeq: number;
|
|
266
|
+
/** The mix time of that frame, later than `doneAt` for a leaving put behind now. */
|
|
267
|
+
decidedAt: number;
|
|
268
|
+
/** The latest mix time a seek or read back to finds it still playing. */
|
|
269
|
+
get reachedTo(): number;
|
|
256
270
|
/** Under `history`, its controls after each change, oldest first. */
|
|
257
271
|
log: Controls[] | null;
|
|
258
272
|
/** Under `history`, its controls as `cue` left them, which a voice parked by a seek takes back. */
|
|
@@ -361,6 +375,44 @@ export declare class Voice<I, O> {
|
|
|
361
375
|
elapsedAt(now: number): number;
|
|
362
376
|
/** The mix time its clock reads `elapsed`, inverting `elapsedAt`; Infinity where it never will. */
|
|
363
377
|
timeAt(elapsed: number): number;
|
|
378
|
+
/**
|
|
379
|
+
* Whether its clock runs back by its controls: at a rate below 0, or on a ramp between rates
|
|
380
|
+
* neither of which is above it. A ramp through 0 says neither way until it has turned.
|
|
381
|
+
*/
|
|
382
|
+
get backs(): boolean;
|
|
383
|
+
/** Whether its ramp takes its rate through 0, which turns its clock around partway. */
|
|
384
|
+
get turns(): boolean;
|
|
385
|
+
/**
|
|
386
|
+
* The mix time its passes run out by its controls now: its last pass's end running forward, its
|
|
387
|
+
* first pass's start running back. Infinity where they never do, and while a ramp has yet to
|
|
388
|
+
* turn it around.
|
|
389
|
+
*/
|
|
390
|
+
endsAt(): number;
|
|
391
|
+
/**
|
|
392
|
+
* The mix time its clock ran back to `elapsed` by `now`, by its controls now: where a subject
|
|
393
|
+
* `elapsed` into it reached its start. NaN where it has not, and for a voice an owner holds.
|
|
394
|
+
*/
|
|
395
|
+
ranBackTo(elapsed: number, now: number): number;
|
|
396
|
+
/** Whether it has yet to start at mix time `now`: pending, or fading out from before its start. */
|
|
397
|
+
unstarted(now: number): boolean;
|
|
398
|
+
/**
|
|
399
|
+
* A subject's voice time at mix time `now`, `delay` into the voice, before its freezes apply. A
|
|
400
|
+
* voice yet to start reads where it will: its clock is anchored there, and read at any time
|
|
401
|
+
* before would have a voice sought ahead, or running back, play before its start.
|
|
402
|
+
*/
|
|
403
|
+
rawAt(now: number, delay: number): number;
|
|
404
|
+
/**
|
|
405
|
+
* Whether a subject at `raw` is before its start at mix time `now`: it shows nothing, or its
|
|
406
|
+
* first frame frozen.
|
|
407
|
+
*/
|
|
408
|
+
before(raw: number, now: number): boolean;
|
|
409
|
+
/**
|
|
410
|
+
* Whether a subject before its start shows its first frame: the voice freezes before, or is
|
|
411
|
+
* fading out from where it ran back to its start, as one fading from its end shows its last.
|
|
412
|
+
*/
|
|
413
|
+
get holdsFirst(): boolean;
|
|
414
|
+
/** A subject's voice time at `raw` once the voice's freezes apply; NaN where it shows nothing. */
|
|
415
|
+
shownAt(raw: number): number;
|
|
364
416
|
/** Records this voice's controls as they stand, from mix time `at` in frame `seq`. */
|
|
365
417
|
note(at: number, seq: number, sync?: boolean): void;
|
|
366
418
|
/**
|