@msb235/blits 0.7.1 → 0.8.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 +193 -0
- package/README.md +22 -4
- package/dist/book.js +4 -4
- package/dist/chain.d.ts +1 -1
- package/dist/chain.js +57 -54
- package/dist/channels.d.ts +26 -5
- package/dist/channels.js +12 -8
- package/dist/clock.d.ts +8 -1
- package/dist/clock.js +14 -3
- package/dist/clone.d.ts +8 -0
- package/dist/clone.js +22 -0
- package/dist/closed.d.ts +1 -1
- package/dist/closed.js +3 -2
- package/dist/color.d.ts +0 -11
- package/dist/color.js +4 -24
- package/dist/columns.js +0 -1
- package/dist/copyout.d.ts +6 -0
- package/dist/copyout.js +38 -0
- package/dist/crowd.d.ts +7 -5
- package/dist/crowd.js +32 -18
- package/dist/cue.d.ts +3 -1
- package/dist/cue.js +31 -20
- package/dist/departed.d.ts +34 -0
- package/dist/departed.js +73 -0
- package/dist/easing.d.ts +5 -0
- package/dist/easing.js +31 -2
- package/dist/everyone.d.ts +56 -0
- package/dist/everyone.js +221 -0
- package/dist/fade.d.ts +8 -1
- package/dist/fade.js +34 -6
- package/dist/fill.d.ts +22 -13
- package/dist/fill.js +104 -41
- package/dist/fit.js +2 -1
- package/dist/fold.d.ts +37 -0
- package/dist/fold.js +216 -31
- package/dist/frames.d.ts +21 -0
- package/dist/frames.js +59 -0
- package/dist/gather.d.ts +15 -9
- package/dist/gather.js +60 -25
- package/dist/gone.js +10 -0
- package/dist/handle.d.ts +16 -5
- package/dist/handle.js +56 -6
- package/dist/held.d.ts +8 -5
- package/dist/held.js +56 -58
- package/dist/history.d.ts +17 -13
- package/dist/history.js +47 -28
- package/dist/hosts.js +55 -8
- package/dist/index.d.ts +9 -4
- package/dist/index.js +5 -2
- package/dist/keyfill.d.ts +7 -0
- package/dist/keyfill.js +124 -0
- package/dist/keyrows.d.ts +1 -0
- package/dist/keyrows.js +1 -1
- package/dist/lane.d.ts +32 -2
- package/dist/lane.js +5 -0
- package/dist/lanes.d.ts +32 -8
- package/dist/lanes.js +69 -37
- package/dist/leavings.d.ts +26 -0
- package/dist/leavings.js +70 -0
- package/dist/locus.js +2 -2
- package/dist/meet.js +14 -8
- package/dist/mixer.d.ts +39 -7
- package/dist/mixer.js +50 -10
- package/dist/motion.d.ts +2 -0
- package/dist/motion.js +4 -1
- package/dist/motions.d.ts +22 -15
- package/dist/motions.js +40 -34
- package/dist/move.js +23 -16
- package/dist/moved.d.ts +11 -0
- package/dist/moved.js +29 -0
- package/dist/origin.d.ts +19 -0
- package/dist/origin.js +68 -0
- package/dist/owed.d.ts +4 -4
- package/dist/owed.js +8 -8
- package/dist/owner.d.ts +1 -1
- package/dist/owner.js +2 -2
- package/dist/pace.d.ts +13 -5
- package/dist/pace.js +21 -8
- package/dist/pack.d.ts +3 -1
- package/dist/pack.js +15 -24
- package/dist/paging.d.ts +8 -2
- package/dist/paging.js +39 -4
- package/dist/patch.d.ts +14 -9
- package/dist/patch.js +83 -21
- package/dist/place.js +18 -3
- package/dist/project.d.ts +12 -1
- package/dist/project.js +101 -83
- package/dist/pull.d.ts +2 -0
- package/dist/pull.js +8 -0
- package/dist/qualify.js +10 -2
- package/dist/reading.d.ts +5 -0
- package/dist/reading.js +5 -0
- package/dist/record.d.ts +9 -0
- package/dist/record.js +42 -0
- package/dist/relink.d.ts +11 -5
- package/dist/relink.js +7 -5
- package/dist/restored.d.ts +41 -0
- package/dist/restored.js +91 -0
- package/dist/revive.d.ts +10 -3
- package/dist/revive.js +31 -23
- package/dist/roster.d.ts +11 -0
- package/dist/roster.js +46 -0
- package/dist/rotation.d.ts +29 -0
- package/dist/rotation.js +91 -0
- package/dist/rows.d.ts +5 -5
- package/dist/rows.js +5 -5
- package/dist/sample.d.ts +3 -2
- package/dist/sample.js +32 -15
- package/dist/seek.js +97 -114
- package/dist/shared.d.ts +6 -0
- package/dist/shared.js +15 -0
- package/dist/signals.d.ts +24 -3
- package/dist/signals.js +50 -11
- package/dist/spans.js +7 -0
- package/dist/stack.d.ts +11 -0
- package/dist/stack.js +47 -0
- package/dist/standing.d.ts +10 -0
- package/dist/standing.js +79 -0
- package/dist/store.d.ts +3 -3
- package/dist/store.js +3 -3
- package/dist/strays.js +3 -0
- package/dist/tape.d.ts +8 -5
- package/dist/tape.js +21 -13
- package/dist/testing.d.ts +35 -0
- package/dist/testing.js +116 -0
- package/dist/touch.d.ts +6 -0
- package/dist/touch.js +28 -0
- package/dist/transport.d.ts +45 -6
- package/dist/transport.js +64 -19
- package/dist/turns.d.ts +18 -0
- package/dist/turns.js +72 -0
- package/dist/tweened.js +1 -1
- package/dist/types/channel.d.ts +46 -0
- package/dist/types/channel.js +1 -0
- package/dist/types/history.d.ts +139 -0
- package/dist/types/history.js +1 -0
- package/dist/types/mix.d.ts +336 -0
- package/dist/types/mix.js +1 -0
- package/dist/types/patch.d.ts +187 -0
- package/dist/types/patch.js +1 -0
- package/dist/types/place.d.ts +157 -0
- package/dist/types/place.js +1 -0
- package/dist/types/transport.d.ts +71 -0
- package/dist/types/transport.js +1 -0
- package/dist/types/voice.d.ts +323 -0
- package/dist/types/voice.js +1 -0
- package/dist/types.d.ts +7 -1162
- package/dist/unpage.d.ts +2 -2
- package/dist/unpage.js +53 -25
- package/dist/voice.d.ts +90 -22
- package/dist/voice.js +105 -43
- package/dist/wave.d.ts +42 -0
- package/dist/wave.js +62 -0
- package/dist/weigh.js +1 -1
- package/docs/schema.html +42 -36
- package/package.json +8 -3
package/dist/gather.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { foldNumber, lerpNumber } from './channels.js';
|
|
2
|
+
import { shortWay, turnFrom, turnInto, turnToward } from './turns.js';
|
|
2
3
|
/** `xs`, or a copy at least `n` long where it is shorter. */
|
|
3
4
|
function sized(xs, n) {
|
|
4
5
|
if (xs.length >= n)
|
|
@@ -37,13 +38,11 @@ export function gatherLocus(lanes, g) {
|
|
|
37
38
|
continue;
|
|
38
39
|
lanes.intoId = voice.id;
|
|
39
40
|
lanes.reset(lane);
|
|
40
|
-
|
|
41
|
-
const duration = voice.duration;
|
|
42
|
-
const passes = voice.passes;
|
|
41
|
+
lane.elapsedNow = voice.elapsedAt(lanes.now);
|
|
43
42
|
const list = lane.list;
|
|
44
43
|
for (let p = 0; p < list.length; p++) {
|
|
45
44
|
const slot = list[p];
|
|
46
|
-
if (lanes.one(lane, p, slot
|
|
45
|
+
if (lanes.one(lane, p, slot)) {
|
|
47
46
|
meetLocus(lanes, g, slot);
|
|
48
47
|
g.sum[slot] =
|
|
49
48
|
g.sum[slot] + lane.data[p * 8 /* Row.STRIDE */ + 2 /* Row.WEIGHT */];
|
|
@@ -68,8 +67,12 @@ function meetLocus(lanes, g, slot) {
|
|
|
68
67
|
if (taken !== undefined)
|
|
69
68
|
taken[slot] = 0;
|
|
70
69
|
}
|
|
71
|
-
/**
|
|
72
|
-
|
|
70
|
+
/**
|
|
71
|
+
* A member's value for one channel of a subject, lerped into its locus: the step of the general
|
|
72
|
+
* path's `foldLocus`.
|
|
73
|
+
*/
|
|
74
|
+
function gatherInto(lanes, g, ch, slot, value) {
|
|
75
|
+
const w = lanes.arg[0 /* Arg.WEIGHT */];
|
|
73
76
|
meetLocus(lanes, g, slot);
|
|
74
77
|
const k = ch.index;
|
|
75
78
|
let values = g.values[k];
|
|
@@ -83,7 +86,9 @@ function gatherInto(lanes, g, ch, slot, value, w) {
|
|
|
83
86
|
const taken = takenAt[slot];
|
|
84
87
|
const total = taken + w;
|
|
85
88
|
const u = w / total;
|
|
86
|
-
if (ch.
|
|
89
|
+
if (!ch.plain)
|
|
90
|
+
turnToward(ch, values, slot * ch.axes, value, taken, u);
|
|
91
|
+
else if (ch.scalar) {
|
|
87
92
|
values[slot] =
|
|
88
93
|
taken === 0 ? value : lerpNumber(values[slot], value, u);
|
|
89
94
|
}
|
|
@@ -110,15 +115,21 @@ export function foldLocus(lanes, lane, g) {
|
|
|
110
115
|
const sum = g.sum[slot];
|
|
111
116
|
if (sum <= 0)
|
|
112
117
|
continue;
|
|
113
|
-
const w = sum > 1 ? 1 : sum;
|
|
114
118
|
for (let k = 0; k < g.values.length; k++) {
|
|
115
119
|
const takenAt = g.taken[k];
|
|
116
|
-
|
|
120
|
+
const taken = takenAt === undefined ? 0 : takenAt[slot];
|
|
121
|
+
if (!(taken > 0))
|
|
117
122
|
continue;
|
|
123
|
+
// A channel folds at the weight of the members that wrote it, not the whole locus.
|
|
124
|
+
const w = taken > 1 ? 1 : taken;
|
|
118
125
|
const ch = lanes.laned[k];
|
|
119
126
|
const values = g.values[k];
|
|
120
127
|
const into = ch.values;
|
|
121
128
|
const base = slot * ch.axes;
|
|
129
|
+
if (!ch.plain) {
|
|
130
|
+
turnFrom(ch, into, base, values, base, w);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
122
133
|
for (let a = 0; a < ch.axes; a++)
|
|
123
134
|
into[base + a] = foldNumber(ch.op, into[base + a], values[base + a], w);
|
|
124
135
|
}
|
|
@@ -130,49 +141,56 @@ export function gather(lane, delta) {
|
|
|
130
141
|
for (let i = 0; i < chans.length; i++)
|
|
131
142
|
lane.values[i] = delta[chans[i].name];
|
|
132
143
|
}
|
|
133
|
-
/** Folds what `gather` read into a subject's laned values. */
|
|
134
|
-
export function fold(lane, slot
|
|
144
|
+
/** Folds what `gather` read into a subject's laned values, at `Arg.WEIGHT`. */
|
|
145
|
+
export function fold(lane, slot) {
|
|
135
146
|
const chans = lane.chans;
|
|
136
147
|
for (let i = 0; i < chans.length; i++)
|
|
137
|
-
this.foldInto(chans[i], slot, lane.values[i]
|
|
148
|
+
this.foldInto(chans[i], slot, lane.values[i]);
|
|
138
149
|
}
|
|
139
150
|
/**
|
|
140
|
-
* Folds a delta a patch returned into a subject's laned values. The first few
|
|
141
|
-
* at a site of their own, which in most mixes sees one channel name and stays
|
|
142
|
-
* site reading every name in turn slows every read.
|
|
151
|
+
* Folds a delta a patch returned into a subject's laned values, at `Arg.WEIGHT`. The first few
|
|
152
|
+
* channels are read at a site of their own, which in most mixes sees one channel name and stays
|
|
153
|
+
* fast, where one site reading every name in turn slows every read.
|
|
143
154
|
*/
|
|
144
|
-
export function foldDelta(chans, slot, delta
|
|
155
|
+
export function foldDelta(chans, slot, delta) {
|
|
145
156
|
const n = chans.length;
|
|
146
157
|
let ch = chans[0];
|
|
147
158
|
if (n > 0)
|
|
148
|
-
this.foldInto(ch, slot, delta[ch.name]
|
|
159
|
+
this.foldInto(ch, slot, delta[ch.name]);
|
|
149
160
|
ch = chans[1];
|
|
150
161
|
if (n > 1)
|
|
151
|
-
this.foldInto(ch, slot, delta[ch.name]
|
|
162
|
+
this.foldInto(ch, slot, delta[ch.name]);
|
|
152
163
|
ch = chans[2];
|
|
153
164
|
if (n > 2)
|
|
154
|
-
this.foldInto(ch, slot, delta[ch.name]
|
|
165
|
+
this.foldInto(ch, slot, delta[ch.name]);
|
|
155
166
|
for (let i = 3; i < n; i++) {
|
|
156
167
|
ch = chans[i];
|
|
157
|
-
this.foldInto(ch, slot, delta[ch.name]
|
|
168
|
+
this.foldInto(ch, slot, delta[ch.name]);
|
|
158
169
|
}
|
|
159
170
|
}
|
|
160
171
|
/** Folds a motion sample's axes, kept apart from `foldInto` so neither reads two array kinds. */
|
|
161
|
-
export function foldRun(ch, slot, xs
|
|
172
|
+
export function foldRun(ch, slot, xs) {
|
|
173
|
+
if (!ch.plain) {
|
|
174
|
+
this.unplain(ch, slot, ch.scalar ? xs[0] : xs);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const w = this.arg[0 /* Arg.WEIGHT */];
|
|
162
178
|
const values = ch.values;
|
|
163
179
|
const base = slot * ch.axes;
|
|
164
180
|
for (let a = 0; a < ch.axes; a++)
|
|
165
181
|
values[base + a] = foldNumber(ch.op, values[base + a], xs[a], w);
|
|
166
182
|
}
|
|
167
|
-
|
|
183
|
+
/** Folds one channel's value into a subject's laned values, or its locus, at `Arg.WEIGHT`. */
|
|
184
|
+
export function foldInto(ch, slot, value) {
|
|
168
185
|
if (value === undefined)
|
|
169
186
|
return;
|
|
170
187
|
if (this.into !== null) {
|
|
171
|
-
gatherInto(this, this.into, ch, slot, value
|
|
188
|
+
gatherInto(this, this.into, ch, slot, value);
|
|
172
189
|
return;
|
|
173
190
|
}
|
|
174
|
-
if (ch.
|
|
191
|
+
if (!ch.plain && !this.unplain(ch, slot, value))
|
|
175
192
|
return;
|
|
193
|
+
const w = this.arg[0 /* Arg.WEIGHT */];
|
|
176
194
|
const values = ch.values;
|
|
177
195
|
if (ch.scalar) {
|
|
178
196
|
values[slot] = foldNumber(ch.op, values[slot], value, w);
|
|
@@ -191,7 +209,8 @@ export function foldInto(ch, slot, value, w) {
|
|
|
191
209
|
* general path's `apply` decides it from the same band state on the same record, so a subject
|
|
192
210
|
* moving between the paths carries it.
|
|
193
211
|
*/
|
|
194
|
-
export function gate(ch
|
|
212
|
+
export function gate(ch) {
|
|
213
|
+
const w = this.arg[0 /* Arg.WEIGHT */];
|
|
195
214
|
const lane = this.folding;
|
|
196
215
|
const rec = this.rec;
|
|
197
216
|
const i = lane.chans.indexOf(ch);
|
|
@@ -202,3 +221,19 @@ export function gate(ch, w) {
|
|
|
202
221
|
bands[i] = on ? 1 : 2;
|
|
203
222
|
return on;
|
|
204
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* `foldInto` for a channel that is not plain, at `Arg.WEIGHT`. A rest-less one asks its band, and
|
|
226
|
+
* true is a pass, which `foldInto` then folds; a rotation is folded here, so false.
|
|
227
|
+
*/
|
|
228
|
+
export function unplain(ch, slot, value) {
|
|
229
|
+
if (ch.op === 'last')
|
|
230
|
+
return this.gate(ch);
|
|
231
|
+
const w = this.arg[0 /* Arg.WEIGHT */];
|
|
232
|
+
const values = ch.values;
|
|
233
|
+
// An angle here and not through `turnInto`, where its weight would cross a call as a boxed double.
|
|
234
|
+
if (ch.turn > 0)
|
|
235
|
+
values[slot] = values[slot] + shortWay(value, ch.turn) * w;
|
|
236
|
+
else
|
|
237
|
+
turnInto(ch, values, slot * ch.axes, value, w);
|
|
238
|
+
return false;
|
|
239
|
+
}
|
package/dist/gone.js
CHANGED
|
@@ -73,6 +73,14 @@ export function goneIndex(mix) {
|
|
|
73
73
|
ix.count = list.length;
|
|
74
74
|
return ix;
|
|
75
75
|
}
|
|
76
|
+
/** Hands voices let go of from `gone` to the departed index, noting the latest that left. */
|
|
77
|
+
function forgot(mix, out) {
|
|
78
|
+
for (const v of out) {
|
|
79
|
+
mix.departed.add(mix, v);
|
|
80
|
+
if (v.doneAt > mix.transport.forgotTo)
|
|
81
|
+
mix.transport.forgotTo = v.doneAt;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
76
84
|
/**
|
|
77
85
|
* Keeps the gone voices `keep` says yes to, asking it once each, and takes the rest out of the
|
|
78
86
|
* index one by one. Leaves `gone` as it was when every one stays.
|
|
@@ -92,6 +100,7 @@ export function keepGone(mix, keep) {
|
|
|
92
100
|
(keep(v) ? kept : out).push(v);
|
|
93
101
|
}
|
|
94
102
|
remove(ix, out);
|
|
103
|
+
forgot(mix, out);
|
|
95
104
|
mix.gone = kept;
|
|
96
105
|
scoredCut(mix, list, out);
|
|
97
106
|
ix.of = kept;
|
|
@@ -129,6 +138,7 @@ export function expireGone(mix, reach) {
|
|
|
129
138
|
}
|
|
130
139
|
list.splice(0, end, ...kept);
|
|
131
140
|
remove(ix, out);
|
|
141
|
+
forgot(mix, out);
|
|
132
142
|
scoredCut(mix, list, out);
|
|
133
143
|
ix.count = list.length;
|
|
134
144
|
}
|
package/dist/handle.d.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type Motion, type Value } from './motion.js';
|
|
2
|
+
import type { Doubt, FadeOptions, FitResult, Handle, SeekOptions } from './types.js';
|
|
2
3
|
import type { Voice } from './voice.js';
|
|
3
4
|
/** What a voice's handle asks of the mix that cued it: one per mix, shared by every handle. */
|
|
4
5
|
export interface HandleHost<I, O> {
|
|
5
|
-
/** The mix clock, or the voice's start before
|
|
6
|
+
/** The mix clock, or its owner's for a voice one holds; the voice's start before either reads. */
|
|
6
7
|
nowFor(voice: Voice<I, O>): number;
|
|
7
8
|
/** A handle write changed the voice: reschedule it, and refill the lanes. */
|
|
8
9
|
changed(voice: Voice<I, O>): void;
|
|
9
|
-
/**
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* The voice's clock jumped: for an owner, so did every clock it holds. With `rebuild`, their
|
|
12
|
+
* state is made again for where they now are. How sure that leaves it.
|
|
13
|
+
*/
|
|
14
|
+
sought(voice: Voice<I, O>, rebuild: boolean): Doubt;
|
|
11
15
|
fade(voice: Voice<I, O>, opts: FadeOptions<I> | undefined): void;
|
|
12
16
|
rise(voice: Voice<I, O>, opts: {
|
|
13
17
|
over?: number;
|
|
@@ -16,6 +20,10 @@ export interface HandleHost<I, O> {
|
|
|
16
20
|
/** Records a write the host made, which `again` makes once more where a seek replays it. */
|
|
17
21
|
record(label: string, again: () => void): void;
|
|
18
22
|
}
|
|
23
|
+
/** Refuses a voice rate no clock can run at. */
|
|
24
|
+
export declare function finiteRate(rate: number): void;
|
|
25
|
+
/** Refuses a voice weight that is no number: NaN would fold into every channel the voice writes. */
|
|
26
|
+
export declare function plainWeight(weight: number): void;
|
|
19
27
|
/** What a handle answers for a voice a history store paged out, which left long ago. */
|
|
20
28
|
export interface Gone {
|
|
21
29
|
weight: number;
|
|
@@ -47,10 +55,13 @@ export declare class VoiceHandle<I, O> implements Handle<I> {
|
|
|
47
55
|
get rate(): number;
|
|
48
56
|
set rate(r: number);
|
|
49
57
|
ramp(r: number, over: number): void;
|
|
50
|
-
seek(elapsed: number):
|
|
58
|
+
seek(elapsed: number, opts?: SeekOptions): Doubt;
|
|
51
59
|
fade(opts?: FadeOptions<I>): void;
|
|
52
60
|
rise(opts?: {
|
|
53
61
|
over?: number;
|
|
54
62
|
}): void;
|
|
55
63
|
weightOf(subject: I): number;
|
|
64
|
+
to(subject: I, target: Value, at?: number): void;
|
|
65
|
+
push(subject: I, velocity: Value, at?: number): void;
|
|
66
|
+
read(subject: I, at?: number): Motion<Value> | undefined;
|
|
56
67
|
}
|
package/dist/handle.js
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import { retime } from './clock.js';
|
|
2
|
+
import { motionOf } from './motion.js';
|
|
3
|
+
import { leave } from './origin.js';
|
|
4
|
+
/** Refuses a voice rate no clock can run at. */
|
|
5
|
+
export function finiteRate(rate) {
|
|
6
|
+
if (!Number.isFinite(rate))
|
|
7
|
+
throw new RangeError(`blits: a voice's rate is a finite number, not ${rate}`);
|
|
8
|
+
}
|
|
9
|
+
/** Refuses a voice weight that is no number: NaN would fold into every channel the voice writes. */
|
|
10
|
+
export function plainWeight(weight) {
|
|
11
|
+
if (Number.isNaN(weight))
|
|
12
|
+
throw new RangeError('blits: a voice’s weight is a number, not NaN');
|
|
13
|
+
}
|
|
2
14
|
/**
|
|
3
15
|
* A voice's handle: the host's only way to control a voice once cued. A voice a history store
|
|
4
16
|
* pages out is let go of here too, so the handle answers as a gone voice's does, and a seek that
|
|
@@ -49,9 +61,11 @@ export class VoiceHandle {
|
|
|
49
61
|
}
|
|
50
62
|
// A write to a paged voice changes nothing: it had left when the host made it, as it has on replay.
|
|
51
63
|
set weight(w) {
|
|
64
|
+
plainWeight(w);
|
|
52
65
|
const voice = this.#voice;
|
|
53
66
|
if (voice === null)
|
|
54
67
|
return;
|
|
68
|
+
voice.written = true;
|
|
55
69
|
voice.weight = w;
|
|
56
70
|
this.#host.changed(voice);
|
|
57
71
|
this.#host.record('weight', () => {
|
|
@@ -66,28 +80,40 @@ export class VoiceHandle {
|
|
|
66
80
|
this.ramp(r, 0);
|
|
67
81
|
}
|
|
68
82
|
ramp(r, over) {
|
|
83
|
+
finiteRate(r);
|
|
69
84
|
const voice = this.#voice;
|
|
70
85
|
if (voice === null)
|
|
71
86
|
return;
|
|
72
|
-
|
|
87
|
+
voice.written = true;
|
|
88
|
+
const now = this.#host.nowFor(voice);
|
|
89
|
+
// A pending voice's clock reads 0 at its start, and a rate taken from now would cross 0 sooner:
|
|
90
|
+
// a subject it freezes before would play, and step, ahead of a voice that has not started.
|
|
91
|
+
const u = voice.state === 'pending' && voice.start > now && r > 0 ? voice.start : now;
|
|
92
|
+
leave(voice, u);
|
|
93
|
+
retime(voice, u, r, over);
|
|
73
94
|
this.#host.changed(voice);
|
|
74
95
|
this.#host.record('rate', () => this.ramp(r, over));
|
|
75
96
|
}
|
|
76
|
-
seek(elapsed) {
|
|
97
|
+
seek(elapsed, opts) {
|
|
77
98
|
const voice = this.#voice;
|
|
78
99
|
if (voice === null)
|
|
79
|
-
return;
|
|
80
|
-
voice.
|
|
100
|
+
return 'exact';
|
|
101
|
+
voice.written = true;
|
|
102
|
+
const u = this.#host.nowFor(voice);
|
|
103
|
+
leave(voice, u);
|
|
104
|
+
voice.rebase(u);
|
|
81
105
|
voice.anchorElapsed = elapsed;
|
|
82
106
|
voice.seeks++;
|
|
83
|
-
this.#host.sought(voice);
|
|
107
|
+
const doubt = this.#host.sought(voice, opts?.state !== 'keep');
|
|
84
108
|
this.#host.changed(voice);
|
|
85
|
-
this.#host.record('seek', () => this.seek(elapsed));
|
|
109
|
+
this.#host.record('seek', () => this.seek(elapsed, opts));
|
|
110
|
+
return doubt;
|
|
86
111
|
}
|
|
87
112
|
fade(opts) {
|
|
88
113
|
const voice = this.#voice;
|
|
89
114
|
if (voice === null)
|
|
90
115
|
return;
|
|
116
|
+
voice.written = true;
|
|
91
117
|
this.#host.fade(voice, opts);
|
|
92
118
|
this.#host.record('fade', () => this.fade(opts));
|
|
93
119
|
}
|
|
@@ -95,6 +121,7 @@ export class VoiceHandle {
|
|
|
95
121
|
const voice = this.#voice;
|
|
96
122
|
if (voice === null)
|
|
97
123
|
return;
|
|
124
|
+
voice.written = true;
|
|
98
125
|
this.#host.rise(voice, opts);
|
|
99
126
|
this.#host.record('rise', () => this.rise(opts));
|
|
100
127
|
}
|
|
@@ -102,4 +129,27 @@ export class VoiceHandle {
|
|
|
102
129
|
const voice = this.#voice;
|
|
103
130
|
return voice === null ? 0 : this.#host.weightOf(voice, subject);
|
|
104
131
|
}
|
|
132
|
+
// The patch records each of these itself, so a seek plays them again without the handle.
|
|
133
|
+
to(subject, target, at) {
|
|
134
|
+
const voice = this.#voice;
|
|
135
|
+
if (voice !== null)
|
|
136
|
+
moves(voice.patch, 'to', 'a spring or a tween').to(subject, target, at);
|
|
137
|
+
}
|
|
138
|
+
push(subject, velocity, at) {
|
|
139
|
+
const voice = this.#voice;
|
|
140
|
+
if (voice !== null)
|
|
141
|
+
moves(voice.patch, 'push', 'a spring or a glide').push(subject, velocity, at);
|
|
142
|
+
}
|
|
143
|
+
read(subject, at) {
|
|
144
|
+
const voice = this.#voice;
|
|
145
|
+
if (voice === null || motionOf(voice.patch) === undefined)
|
|
146
|
+
return undefined;
|
|
147
|
+
return voice.patch.read(subject, at);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/** A voice's patch as a motion that takes `verb`, or a throw naming the kinds that do. */
|
|
151
|
+
function moves(patch, verb, kinds) {
|
|
152
|
+
if (motionOf(patch) === undefined || typeof patch[verb] !== 'function')
|
|
153
|
+
throw new Error(`blits: ${verb} needs a voice playing ${kinds}`);
|
|
154
|
+
return patch;
|
|
105
155
|
}
|
package/dist/held.d.ts
CHANGED
|
@@ -3,12 +3,15 @@ import type { Subject, Voice } from './voice.js';
|
|
|
3
3
|
/** The record a voice gives every subject it does not reach. */
|
|
4
4
|
export declare function unreachedOf<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, now: number): Subject<unknown>;
|
|
5
5
|
/** What this voice holds for this subject, made on first sight with `target` and `stagger` asked once. */
|
|
6
|
-
export declare function held<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I,
|
|
6
|
+
export declare function held<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I, slot: number): Subject<unknown>;
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* The mix time the voice clock first read `delay`, or will by its controls now. A stateful voice
|
|
9
|
+
* counts from its last seek that rebuilt state, as its records do.
|
|
10
10
|
*/
|
|
11
11
|
export declare function sinceOf<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, delay: number): number;
|
|
12
12
|
export declare function shownOf<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, since: number): number;
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* A voice's start or clock moved, or it went live at `now`: the records whose `since` was still
|
|
15
|
+
* ahead count from where its clock now puts them.
|
|
16
|
+
*/
|
|
17
|
+
export declare function reorigin<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, now: number): void;
|
package/dist/held.js
CHANGED
|
@@ -1,41 +1,32 @@
|
|
|
1
1
|
import { schedule } from './due.js';
|
|
2
|
-
import {
|
|
2
|
+
import { everyoneOf, inherit, sighted } from './everyone.js';
|
|
3
|
+
import { originOf } from './origin.js';
|
|
4
|
+
import { record } from './record.js';
|
|
3
5
|
import { unreach, unreached } from './unreached.js';
|
|
4
6
|
/** The record a voice gives every subject it does not reach. */
|
|
5
7
|
export function unreachedOf(voice, now) {
|
|
6
8
|
const since = this.sinceOf(voice, 0);
|
|
7
|
-
const none =
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
weight: 0,
|
|
13
|
-
rested: false,
|
|
14
|
-
bands: null,
|
|
15
|
-
state: undefined,
|
|
16
|
-
stepped: now,
|
|
17
|
-
ticks: 0,
|
|
18
|
-
probed: Number.NaN,
|
|
19
|
-
delta: null,
|
|
20
|
-
phase: 0,
|
|
21
|
-
seeks: 0,
|
|
22
|
-
kept: null,
|
|
23
|
-
voice,
|
|
24
|
-
next: null,
|
|
25
|
-
version: Number.NaN,
|
|
26
|
-
loci: null,
|
|
27
|
-
slot: -1,
|
|
28
|
-
};
|
|
9
|
+
const none = record(voice, false, undefined);
|
|
10
|
+
none.since = since;
|
|
11
|
+
none.shown = this.shownOf(voice, since);
|
|
12
|
+
none.stepped = now;
|
|
13
|
+
none.rebuilt = voice.rebuilds;
|
|
29
14
|
voice.unreached = none;
|
|
30
15
|
return none;
|
|
31
16
|
}
|
|
32
17
|
/** What this voice holds for this subject, made on first sight with `target` and `stagger` asked once. */
|
|
33
|
-
export function held(voice, subject,
|
|
18
|
+
export function held(voice, subject, slot) {
|
|
19
|
+
if (voice.sharing) {
|
|
20
|
+
const all = everyoneOf(this, voice);
|
|
21
|
+
if (all !== null)
|
|
22
|
+
return all;
|
|
23
|
+
}
|
|
34
24
|
if (unreached(voice, slot))
|
|
35
25
|
return voice.unreached;
|
|
36
26
|
let held = voice.subjects.get(subject);
|
|
37
27
|
if (held !== undefined)
|
|
38
28
|
return held;
|
|
29
|
+
const now = this.now;
|
|
39
30
|
const reaches = this.aims(voice, subject);
|
|
40
31
|
if (!reaches) {
|
|
41
32
|
// Nothing is kept for a subject the voice does not reach, so one record stands for them all:
|
|
@@ -55,38 +46,36 @@ export function held(voice, subject, now, slot) {
|
|
|
55
46
|
}
|
|
56
47
|
const delay = voice.spec.stagger ? voice.spec.stagger(subject) : 0;
|
|
57
48
|
const since = this.sinceOf(voice, delay);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
bands: null,
|
|
66
|
-
state: voice.patch.state ? voice.patch.state(subject) : undefined,
|
|
67
|
-
stepped: this.backward && since < now ? since : now,
|
|
68
|
-
ticks: 0,
|
|
69
|
-
probed: Number.NaN,
|
|
70
|
-
delta: null,
|
|
71
|
-
phase: 0,
|
|
72
|
-
seeks: 0,
|
|
73
|
-
kept: null,
|
|
74
|
-
voice,
|
|
75
|
-
next: null,
|
|
76
|
-
version: Number.NaN,
|
|
77
|
-
loci: null,
|
|
78
|
-
slot: -1,
|
|
79
|
-
};
|
|
49
|
+
const shown = this.shownOf(voice, since);
|
|
50
|
+
held = record(voice, true, voice.patch.state ? voice.patch.state(subject) : undefined);
|
|
51
|
+
held.delay = delay;
|
|
52
|
+
held.since = since;
|
|
53
|
+
held.shown = shown;
|
|
54
|
+
held.stepped = since < now ? since : now;
|
|
55
|
+
held.rebuilt = voice.rebuilds;
|
|
80
56
|
if (this.projecting) {
|
|
81
57
|
held.from = held.stepped;
|
|
82
58
|
held.unknown = this.backward && voice.spec.from === 'current';
|
|
83
59
|
}
|
|
84
60
|
voice.subjects.set(subject, held);
|
|
85
|
-
if (
|
|
61
|
+
if (!(since <= now)) {
|
|
86
62
|
voice.early ??= [];
|
|
87
|
-
voice.early
|
|
63
|
+
const early = voice.early;
|
|
64
|
+
// Prunes at each doubling, so records whose origin has passed are not kept to the voice's end.
|
|
65
|
+
if (early.length >= 64 && (early.length & (early.length - 1)) === 0) {
|
|
66
|
+
let kept = 0;
|
|
67
|
+
for (const h of early)
|
|
68
|
+
if (!(h.since <= now))
|
|
69
|
+
early[kept++] = h;
|
|
70
|
+
early.length = kept;
|
|
71
|
+
}
|
|
72
|
+
early.push(held);
|
|
88
73
|
}
|
|
89
|
-
voice.
|
|
74
|
+
// A subject a voice saw while it shared one record was counted then.
|
|
75
|
+
if (sighted(voice, slot))
|
|
76
|
+
inherit(voice, held, slot);
|
|
77
|
+
else
|
|
78
|
+
voice.seen++;
|
|
90
79
|
if (delay > voice.latest) {
|
|
91
80
|
voice.latest = delay;
|
|
92
81
|
// A later end can take a frozen voice live again.
|
|
@@ -96,26 +85,35 @@ export function held(voice, subject, now, slot) {
|
|
|
96
85
|
return held;
|
|
97
86
|
}
|
|
98
87
|
/**
|
|
99
|
-
*
|
|
100
|
-
*
|
|
88
|
+
* The mix time the voice clock first read `delay`, or will by its controls now. A stateful voice
|
|
89
|
+
* counts from its last seek that rebuilt state, as its records do.
|
|
101
90
|
*/
|
|
102
91
|
export function sinceOf(voice, delay) {
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
: voice.start + delay;
|
|
106
|
-
return voice.owner === null ? t : mixTime(voice.owner, t);
|
|
92
|
+
const p = voice.patch;
|
|
93
|
+
return originOf(voice, delay, p.state !== undefined || p.step !== undefined || voice.keeping);
|
|
107
94
|
}
|
|
108
95
|
export function shownOf(voice, since) {
|
|
109
96
|
return voice.freezesBefore && voice.opened < since ? voice.opened : since;
|
|
110
97
|
}
|
|
111
|
-
/**
|
|
112
|
-
|
|
98
|
+
/**
|
|
99
|
+
* A voice's start or clock moved, or it went live at `now`: the records whose `since` was still
|
|
100
|
+
* ahead count from where its clock now puts them.
|
|
101
|
+
*/
|
|
102
|
+
export function reorigin(mix, voice, now) {
|
|
113
103
|
const early = voice.early;
|
|
114
104
|
if (early === null)
|
|
115
105
|
return;
|
|
116
|
-
|
|
106
|
+
let kept = 0;
|
|
117
107
|
for (const held of early) {
|
|
118
108
|
held.since = mix.sinceOf(voice, held.delay);
|
|
119
109
|
held.shown = mix.shownOf(voice, held.since);
|
|
110
|
+
if (held.ticks === 0 && held.stepped > held.since)
|
|
111
|
+
held.stepped = held.since;
|
|
112
|
+
if (!(held.since <= now))
|
|
113
|
+
early[kept++] = held;
|
|
120
114
|
}
|
|
115
|
+
early.length = kept;
|
|
116
|
+
if (kept === 0)
|
|
117
|
+
voice.early = null;
|
|
118
|
+
mix.lanes?.reshown(voice.id);
|
|
121
119
|
}
|
package/dist/history.d.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import type { Mixer } from './mixer.js';
|
|
2
|
+
import { type Cut } from './transport.js';
|
|
2
3
|
import { type Subject, type Voice } from './voice.js';
|
|
3
|
-
/**
|
|
4
|
-
* The last entry taken before `t`, or at it where `inclusive` says so, as it does for a change a
|
|
5
|
-
* sync made. A host's change between frames is taken strictly: it shows from the next frame on, as
|
|
6
|
-
* it did live.
|
|
7
|
-
*/
|
|
4
|
+
/** The last entry taken before `t`, or at it where `inclusive` says so. */
|
|
8
5
|
export declare function last<T extends {
|
|
9
6
|
at: number;
|
|
10
7
|
}>(list: readonly T[], t: number, inclusive: boolean | ((e: T) => boolean)): T | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* The last entry of a list kept in the order it was made that falls inside `cut`, an entry made
|
|
10
|
+
* before its frame was read counting as `early`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function lastWithin<T extends {
|
|
13
|
+
seq: number;
|
|
14
|
+
sync?: boolean;
|
|
15
|
+
}>(list: readonly T[], cut: Cut): T | undefined;
|
|
16
|
+
/** Deep equality over plain data, for telling whether a host field changed. */
|
|
17
|
+
export declare function same(a: unknown, b: unknown): boolean;
|
|
11
18
|
/** A voice's elapsed at an earlier mix time, by the controls it had then. */
|
|
12
19
|
export declare function elapsedThen<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, t: number): number;
|
|
13
20
|
/** Records a change to a voice's controls under `history`, and lets go of what it no longer reaches. */
|
|
@@ -25,11 +32,8 @@ export declare function remember<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, sub
|
|
|
25
32
|
* for a seek or a read back to before then; without history, keeps nothing.
|
|
26
33
|
*/
|
|
27
34
|
export declare function leave<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I, held: Subject<unknown> | undefined): void;
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}[] | null, subject: I, t: number): Subject<unknown> | undefined;
|
|
34
|
-
/** The mix time a motion patch keeps a released subject from, and how far back, under history. */
|
|
35
|
-
export declare function releasing<I, O>(mix: Mixer<I, O>): [at?: number, reach?: number];
|
|
35
|
+
/**
|
|
36
|
+
* The mix time a motion patch keeps a released subject from, how far back, and the frame, under
|
|
37
|
+
* history.
|
|
38
|
+
*/
|
|
39
|
+
export declare function releasing<I, O>(mix: Mixer<I, O>): [at?: number, reach?: number, seq?: number];
|