@msb235/blits 0.4.0 → 0.5.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 +262 -0
- package/README.md +2 -1
- package/dist/bare.d.ts +29 -0
- package/dist/bare.js +105 -0
- package/dist/blend.d.ts +34 -0
- package/dist/blend.js +102 -0
- package/dist/book.d.ts +53 -0
- package/dist/book.js +218 -0
- package/dist/chain.d.ts +22 -0
- package/dist/chain.js +150 -0
- package/dist/channels.d.ts +2 -0
- package/dist/channels.js +3 -1
- package/dist/clock.d.ts +50 -3
- package/dist/clock.js +102 -9
- package/dist/clone.d.ts +7 -0
- package/dist/clone.js +57 -0
- package/dist/columns.d.ts +29 -0
- package/dist/columns.js +183 -0
- package/dist/crowd.d.ts +128 -0
- package/dist/crowd.js +354 -0
- package/dist/cue.d.ts +3 -0
- package/dist/cue.js +115 -0
- package/dist/due.d.ts +12 -0
- package/dist/due.js +93 -0
- package/dist/fade.d.ts +19 -0
- package/dist/fade.js +133 -0
- package/dist/fill.d.ts +42 -0
- package/dist/fill.js +381 -0
- package/dist/fold.d.ts +51 -0
- package/dist/fold.js +475 -0
- package/dist/gather.d.ts +24 -0
- package/dist/gather.js +186 -0
- package/dist/handle.d.ts +31 -0
- package/dist/handle.js +56 -0
- package/dist/held.d.ts +14 -0
- package/dist/held.js +117 -0
- package/dist/history.d.ts +22 -0
- package/dist/history.js +149 -0
- package/dist/hosts.d.ts +16 -0
- package/dist/hosts.js +178 -0
- package/dist/index.d.ts +1 -1
- package/dist/keyrows.d.ts +36 -0
- package/dist/keyrows.js +138 -0
- package/dist/lane.d.ts +210 -0
- package/dist/lane.js +187 -0
- package/dist/lanes.d.ts +121 -246
- package/dist/lanes.js +150 -1578
- package/dist/locus.d.ts +40 -0
- package/dist/locus.js +89 -0
- package/dist/marks.d.ts +12 -0
- package/dist/marks.js +80 -0
- package/dist/meet.d.ts +16 -0
- package/dist/meet.js +113 -0
- package/dist/mixer.d.ts +244 -196
- package/dist/mixer.js +242 -2220
- package/dist/motion.d.ts +66 -21
- package/dist/motion.js +325 -165
- package/dist/move.d.ts +9 -0
- package/dist/move.js +190 -0
- package/dist/named.d.ts +12 -0
- package/dist/named.js +39 -0
- package/dist/numbers.d.ts +18 -6
- package/dist/numbers.js +59 -31
- package/dist/owed.d.ts +24 -0
- package/dist/owed.js +51 -0
- package/dist/owner.d.ts +43 -0
- package/dist/owner.js +117 -0
- package/dist/pace.d.ts +40 -0
- package/dist/pace.js +113 -0
- package/dist/patch.d.ts +51 -10
- package/dist/patch.js +140 -45
- package/dist/place.d.ts +20 -0
- package/dist/place.js +196 -0
- package/dist/project.d.ts +4 -0
- package/dist/project.js +233 -0
- package/dist/pull.d.ts +5 -0
- package/dist/pull.js +175 -0
- package/dist/qualify.d.ts +31 -0
- package/dist/qualify.js +345 -0
- package/dist/relink.d.ts +25 -0
- package/dist/relink.js +59 -0
- package/dist/rows.d.ts +33 -0
- package/dist/rows.js +282 -0
- package/dist/sample.d.ts +21 -0
- package/dist/sample.js +232 -0
- package/dist/signals.d.ts +12 -9
- package/dist/signals.js +8 -5
- package/dist/store.d.ts +10 -3
- package/dist/store.js +44 -9
- package/dist/strays.d.ts +21 -0
- package/dist/strays.js +84 -0
- package/dist/tweened.d.ts +11 -0
- package/dist/tweened.js +48 -0
- package/dist/types.d.ts +217 -34
- package/dist/unreached.d.ts +5 -0
- package/dist/unreached.js +24 -0
- package/dist/voice.d.ts +240 -0
- package/dist/voice.js +322 -0
- package/dist/weigh.d.ts +26 -0
- package/dist/weigh.js +89 -0
- package/docs/schema.html +118 -40
- package/package.json +5 -2
package/dist/fill.js
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { foldNumber, lerpNumber } from './channels.js';
|
|
2
|
+
import { passAt, phaseAt, silent, weighed } from './clock.js';
|
|
3
|
+
import { flush } from './columns.js';
|
|
4
|
+
import { crowdsUpTo, freshen } from './crowd.js';
|
|
5
|
+
import { foldLocus, gatherLocus } from './gather.js';
|
|
6
|
+
import { held } from './lane.js';
|
|
7
|
+
import { reach } from './meet.js';
|
|
8
|
+
import { absent } from './numbers.js';
|
|
9
|
+
import { AT, NOTHING, readKeys, seg, segment, shifted } from './patch.js';
|
|
10
|
+
import { reading } from './reading.js';
|
|
11
|
+
import { move, runMotion } from './sample.js';
|
|
12
|
+
/** The least prime at least `n`, and 1 below 2. */
|
|
13
|
+
function primeFrom(n) {
|
|
14
|
+
if (n < 2)
|
|
15
|
+
return 1;
|
|
16
|
+
for (let c = n;; c++) {
|
|
17
|
+
let prime = true;
|
|
18
|
+
for (let d = 2; d * d <= c; d++)
|
|
19
|
+
if (c % d === 0) {
|
|
20
|
+
prime = false;
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
if (prime)
|
|
24
|
+
return c;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function fillAll(lanes, now, version) {
|
|
28
|
+
flush(lanes);
|
|
29
|
+
const host = lanes.host;
|
|
30
|
+
lanes.filling = true;
|
|
31
|
+
try {
|
|
32
|
+
lanes.fills++;
|
|
33
|
+
lanes.owed.begin(lanes.fills);
|
|
34
|
+
lanes.now = now;
|
|
35
|
+
lanes.filledAt = now;
|
|
36
|
+
lanes.filledVersion = version;
|
|
37
|
+
const size = lanes.numbers.size;
|
|
38
|
+
lanes.keeps = host.keeps;
|
|
39
|
+
const decide = lanes.fresh;
|
|
40
|
+
lanes.fresh = false;
|
|
41
|
+
const share = lanes.live > 0 ? lanes.lastDistinct / lanes.live : 1;
|
|
42
|
+
// Deciding which lanes and crowds go idle first, then running them, in voice order: a crowd's
|
|
43
|
+
// rows are folded between the lanes on either side of their voices, as the general path
|
|
44
|
+
// folds every voice in order.
|
|
45
|
+
let busy = false;
|
|
46
|
+
for (const lane of lanes.lanes) {
|
|
47
|
+
if (decide) {
|
|
48
|
+
// A lane with few subjects, such as one naming its own, goes by the share of all probed;
|
|
49
|
+
// one with a single subject shares nothing a fill could save, as a solo lane does not.
|
|
50
|
+
const line = lane.line;
|
|
51
|
+
const n = lane.list.length;
|
|
52
|
+
if (n >= 64)
|
|
53
|
+
pace(lanes, lane);
|
|
54
|
+
else if (n === 1 && lane.motion === undefined) {
|
|
55
|
+
if (!lane.idle)
|
|
56
|
+
rest(lanes, lane);
|
|
57
|
+
}
|
|
58
|
+
else if (lane.idle ? share >= line.start : share < line.stop)
|
|
59
|
+
flip(lanes, lane);
|
|
60
|
+
}
|
|
61
|
+
if (!lane.idle)
|
|
62
|
+
busy = true;
|
|
63
|
+
}
|
|
64
|
+
for (const c of lanes.crowds) {
|
|
65
|
+
if (decide) {
|
|
66
|
+
if (c.size >= 64)
|
|
67
|
+
pace(lanes, c);
|
|
68
|
+
else if (c.idle ? share >= c.line.start : share < c.line.stop)
|
|
69
|
+
flip(lanes, c);
|
|
70
|
+
}
|
|
71
|
+
c.cursor = 0;
|
|
72
|
+
if (c.stale)
|
|
73
|
+
freshen(c);
|
|
74
|
+
if (!c.idle && c.size > 0)
|
|
75
|
+
busy = true;
|
|
76
|
+
}
|
|
77
|
+
if (busy) {
|
|
78
|
+
for (const ch of lanes.laned)
|
|
79
|
+
ch.values.fill(ch.rest, 0, size * ch.axes);
|
|
80
|
+
for (const g of lanes.loci)
|
|
81
|
+
gatherLocus(lanes, g);
|
|
82
|
+
for (const lane of lanes.lanes) {
|
|
83
|
+
if (lane.idle)
|
|
84
|
+
continue;
|
|
85
|
+
if (lanes.crowds.length > 0)
|
|
86
|
+
crowdsUpTo(lanes, lane.voice.id);
|
|
87
|
+
lanes.run(lane);
|
|
88
|
+
}
|
|
89
|
+
if (lanes.crowds.length > 0)
|
|
90
|
+
crowdsUpTo(lanes, Number.POSITIVE_INFINITY);
|
|
91
|
+
for (const c of lanes.crowds)
|
|
92
|
+
if (!c.idle) {
|
|
93
|
+
c.bareFill = lanes.fills;
|
|
94
|
+
c.bareNow = now;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// With every lane idle there was nothing to fill, and no subject reads a lane this frame.
|
|
98
|
+
if (busy)
|
|
99
|
+
for (let s = 0; s < size; s++)
|
|
100
|
+
lanes.per[s * 6 /* Per.SLOT */ + 0 /* Per.FILLED */] = lanes.fills;
|
|
101
|
+
for (const slot of lanes.late)
|
|
102
|
+
lanes.per[slot * 6 /* Per.SLOT */ + 0 /* Per.FILLED */] = 0;
|
|
103
|
+
if (lanes.holding) {
|
|
104
|
+
lanes.subjects.fill(undefined, 0, size);
|
|
105
|
+
lanes.holding = false;
|
|
106
|
+
}
|
|
107
|
+
lanes.moved = reading.moved;
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
lanes.filling = false;
|
|
111
|
+
lanes.late.length = 0;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Sets a lane idle or busy by the share of its subjects probed in the last frame that had a probe,
|
|
116
|
+
* read from about 256 of them a prime stride apart, from an offset that moves each frame, so a host
|
|
117
|
+
* probing every second or third subject is not read as probing all or none. Both paths give the
|
|
118
|
+
* same values, so this only picks the cheaper.
|
|
119
|
+
*/
|
|
120
|
+
function pace(lanes, lane) {
|
|
121
|
+
const list = lane.list;
|
|
122
|
+
const line = lane.line;
|
|
123
|
+
const from = lanes.lastFrom;
|
|
124
|
+
const to = lanes.lastTo;
|
|
125
|
+
const step = primeFrom(Math.floor(list.length / 256));
|
|
126
|
+
let looked = 0;
|
|
127
|
+
let probed = 0;
|
|
128
|
+
for (let p = lanes.fills % step; p < list.length; p += step) {
|
|
129
|
+
const slot = list[p];
|
|
130
|
+
if (slot < 0)
|
|
131
|
+
continue;
|
|
132
|
+
const a = lanes.per[slot * 6 /* Per.SLOT */ + 3 /* Per.LANE_PROBE */];
|
|
133
|
+
const b = lanes.per[slot * 6 /* Per.SLOT */ + 4 /* Per.GENERAL_PROBE */];
|
|
134
|
+
if ((a > from && a <= to) || (b > from && b <= to))
|
|
135
|
+
probed++;
|
|
136
|
+
looked++;
|
|
137
|
+
}
|
|
138
|
+
const share = looked > 0 ? probed / looked : 1;
|
|
139
|
+
if (lane.idle ? share >= line.start : share < line.stop)
|
|
140
|
+
flip(lanes, lane);
|
|
141
|
+
}
|
|
142
|
+
function flip(lanes, lane) {
|
|
143
|
+
if (lane.idle)
|
|
144
|
+
wake(lanes, lane);
|
|
145
|
+
else
|
|
146
|
+
rest(lanes, lane);
|
|
147
|
+
}
|
|
148
|
+
/** Leaves a lane's subjects to the general path until it wakes, keeping what `weightOf` reports. */
|
|
149
|
+
export function rest(lanes, lane) {
|
|
150
|
+
lane.idle = true;
|
|
151
|
+
const list = lane.list;
|
|
152
|
+
const data = lane.data;
|
|
153
|
+
for (let p = 0; p < list.length; p++) {
|
|
154
|
+
const slot = list[p];
|
|
155
|
+
if (slot < 0)
|
|
156
|
+
continue;
|
|
157
|
+
reach(lanes, slot, 1);
|
|
158
|
+
// A probe read this subject from the last fill, which this lane's weight stays from.
|
|
159
|
+
if (lanes.per[slot * 6 /* Per.SLOT */ + 5 /* Per.LANE_FILL */] === lanes.fills - 1) {
|
|
160
|
+
const o = p * 8 /* Row.STRIDE */;
|
|
161
|
+
data[o + 3 /* Row.PROBED */] = data[o + 2 /* Row.WEIGHT */];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
export function wake(lanes, lane) {
|
|
166
|
+
lane.idle = false;
|
|
167
|
+
for (const slot of lane.list)
|
|
168
|
+
if (slot >= 0)
|
|
169
|
+
reach(lanes, slot, -1);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* A voice with a signal weight: its weight for the subject at `slot` under the voice's fade `fade`,
|
|
173
|
+
* through the general path's own arithmetic; NaN where that path has not made the voice's first
|
|
174
|
+
* call for the subject, which it then makes this frame, so a signal that keeps state is first
|
|
175
|
+
* called where the general path would call it.
|
|
176
|
+
*/
|
|
177
|
+
export function signalled(voice, slot, rec, elapsed, pass, fade) {
|
|
178
|
+
if (Number.isNaN(rec.probed)) {
|
|
179
|
+
this.late.push(slot);
|
|
180
|
+
return Number.NaN;
|
|
181
|
+
}
|
|
182
|
+
const subject = this.subjectAt(slot);
|
|
183
|
+
if (subject === absent)
|
|
184
|
+
return 0;
|
|
185
|
+
const base = this.host.signal(voice, subject, rec, elapsed, pass, slot);
|
|
186
|
+
return weighed(base, fade, voice.parts === null ? 1 : this.host.parting(voice, subject));
|
|
187
|
+
}
|
|
188
|
+
/** What a subject's own ramp out of a voice leaves of its weight: 1 with none. */
|
|
189
|
+
export function parting(voice, slot) {
|
|
190
|
+
if (voice.parts === null)
|
|
191
|
+
return 1;
|
|
192
|
+
const subject = this.subjectAt(slot);
|
|
193
|
+
return subject === absent ? 1 : this.host.parting(voice, subject);
|
|
194
|
+
}
|
|
195
|
+
export function subjectAt(slot) {
|
|
196
|
+
let subject = this.subjects[slot];
|
|
197
|
+
if (subject === undefined) {
|
|
198
|
+
subject = this.numbers.subject(slot);
|
|
199
|
+
this.subjects[slot] = subject;
|
|
200
|
+
this.holding = true;
|
|
201
|
+
}
|
|
202
|
+
return subject;
|
|
203
|
+
}
|
|
204
|
+
/** What a lane remembers within one fill, cleared before the fill runs it. */
|
|
205
|
+
export function reset(lane) {
|
|
206
|
+
const voice = lane.voice;
|
|
207
|
+
lane.placed = false;
|
|
208
|
+
lane.read = false;
|
|
209
|
+
lane.weighed = false;
|
|
210
|
+
// With no fade in or out the envelope is 1 for every subject, which is what it would return.
|
|
211
|
+
lane.flat = !((voice.fade.in ?? 0) > 0) && voice.out === null && voice.owner === null;
|
|
212
|
+
lane.fade = 1;
|
|
213
|
+
}
|
|
214
|
+
/** One voice's contribution to every subject it plays on. */
|
|
215
|
+
export function run(lane) {
|
|
216
|
+
const voice = lane.voice;
|
|
217
|
+
if (voice.state === 'pending' || voice.state === 'done')
|
|
218
|
+
return;
|
|
219
|
+
if (lane.group !== null) {
|
|
220
|
+
foldLocus(this, lane, lane.group);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
this.reset(lane);
|
|
224
|
+
if (lane.motion !== undefined && !voice.keeping) {
|
|
225
|
+
runMotion(this, lane, lane.motion);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const elapsed = voice.elapsedAt(this.now);
|
|
229
|
+
const duration = voice.duration;
|
|
230
|
+
const passes = voice.passes;
|
|
231
|
+
const list = lane.list;
|
|
232
|
+
for (let p = 0; p < list.length; p++) {
|
|
233
|
+
this.one(lane, p, list[p], elapsed, duration, passes);
|
|
234
|
+
// Its patch just made kept state: no further call this fill, the general path makes them.
|
|
235
|
+
if (voice.keeping)
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* One subject's contribution from a lane's voice. True where the voice gave the subject a delta,
|
|
241
|
+
* as the general path's `influence` does where it returns one, which is what makes a voice in a
|
|
242
|
+
* locus one of its members for the subject.
|
|
243
|
+
*/
|
|
244
|
+
export function one(lane, p, slot, elapsedNow, duration, passes) {
|
|
245
|
+
const host = this.host;
|
|
246
|
+
const voice = lane.voice;
|
|
247
|
+
const data = lane.data;
|
|
248
|
+
const o = p * 8 /* Row.STRIDE */;
|
|
249
|
+
// A probe read this subject from the last fill: keep the weight it read for `weightOf`.
|
|
250
|
+
if (this.per[slot * 6 /* Per.SLOT */ + 5 /* Per.LANE_FILL */] === this.fills - 1)
|
|
251
|
+
data[o + 3 /* Row.PROBED */] = data[o + 2 /* Row.WEIGHT */];
|
|
252
|
+
const delay = data[o + 0 /* Row.DELAY */];
|
|
253
|
+
const elapsed = held(voice, elapsedNow - delay);
|
|
254
|
+
if (!(elapsed >= 0)) {
|
|
255
|
+
data[o + 2 /* Row.WEIGHT */] = 0;
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
if (!lane.placed || !Object.is(elapsed, lane.placedAt)) {
|
|
259
|
+
lane.placed = true;
|
|
260
|
+
lane.placedAt = elapsed;
|
|
261
|
+
lane.phase = phaseAt(elapsed, duration, passes);
|
|
262
|
+
lane.pass = passAt(elapsed, duration, passes);
|
|
263
|
+
}
|
|
264
|
+
const since = data[o + 1 /* Row.SINCE */];
|
|
265
|
+
if (!lane.flat && (!lane.weighed || !Object.is(since, lane.weighedSince))) {
|
|
266
|
+
lane.fade = host.envelope(voice, since);
|
|
267
|
+
lane.weighed = true;
|
|
268
|
+
lane.weighedSince = since;
|
|
269
|
+
}
|
|
270
|
+
let w;
|
|
271
|
+
if (typeof voice.spec.weight === 'function') {
|
|
272
|
+
w = this.signalled(voice, slot, lane.records[p], elapsed, lane.pass, lane.fade);
|
|
273
|
+
if (Number.isNaN(w)) {
|
|
274
|
+
data[o + 2 /* Row.WEIGHT */] = 0;
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else
|
|
279
|
+
w = weighed(voice.weight, lane.fade, this.parting(voice, slot));
|
|
280
|
+
data[o + 2 /* Row.WEIGHT */] = w;
|
|
281
|
+
if (voice.built !== null) {
|
|
282
|
+
if (!lane.read || !Object.is(elapsed, lane.readAt)) {
|
|
283
|
+
this.readKeyed(voice, lane.phase, lane.delta, lane.scratch);
|
|
284
|
+
lane.read = true;
|
|
285
|
+
lane.readAt = elapsed;
|
|
286
|
+
this.gather(lane, lane.delta);
|
|
287
|
+
}
|
|
288
|
+
if (w > 0)
|
|
289
|
+
this.fold(lane, slot, w);
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
const rec = lane.records[p];
|
|
293
|
+
// The general path makes a voice's first call for a subject, where it first sees it play.
|
|
294
|
+
if (Number.isNaN(rec.probed)) {
|
|
295
|
+
this.late.push(slot);
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
// A motion patch needs the subject only to number it, so the lookup waits until then.
|
|
299
|
+
if (lane.motion !== undefined) {
|
|
300
|
+
move(this, lane, lane.motion, p, slot, rec, elapsed, delay, w);
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
return this.call(voice, lane.chans, rec, slot, elapsed, lane.phase, lane.pass, delay, w);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* A keys voice's stops at `phase` folded straight into a subject's values, without a delta: what
|
|
307
|
+
* `readKeyed` then `foldDelta` give, through the same segment search and the stock channels' own
|
|
308
|
+
* lerp, which is all a laned keys voice can use.
|
|
309
|
+
*/
|
|
310
|
+
export function foldKeys(voice, chans, phase, slot, w) {
|
|
311
|
+
const built = voice.built;
|
|
312
|
+
const tracks = built.tracks;
|
|
313
|
+
for (let i = 0; i < tracks.length; i++) {
|
|
314
|
+
const track = tracks[i];
|
|
315
|
+
const ch = chans[i];
|
|
316
|
+
const found = segment(track, shifted(track, phase, built.duration), undefined);
|
|
317
|
+
if (found === NOTHING)
|
|
318
|
+
continue;
|
|
319
|
+
if (found === AT) {
|
|
320
|
+
this.foldInto(ch, slot, seg.a, w);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
const a = seg.a;
|
|
324
|
+
const b = seg.b;
|
|
325
|
+
const u = seg.eased;
|
|
326
|
+
if (ch.scalar) {
|
|
327
|
+
this.foldInto(ch, slot, voice.lerps[i](a, b, u), w);
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
// `vec`'s lerp, reading each end past its length as the rest, then its fold.
|
|
331
|
+
const as = a;
|
|
332
|
+
const bs = b;
|
|
333
|
+
const values = ch.values;
|
|
334
|
+
const base = slot * ch.axes;
|
|
335
|
+
const rest = ch.rest;
|
|
336
|
+
for (let x = 0; x < ch.axes; x++) {
|
|
337
|
+
const v = lerpNumber(as[x] ?? rest, bs[x] ?? rest, u);
|
|
338
|
+
values[base + x] = foldNumber(ch.op, values[base + x], v, w);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/** Reads a keys voice's stops at `phase` into `delta`, interpolating into the reader's `scratch`. */
|
|
343
|
+
export function readKeyed(voice, phase, delta, scratch) {
|
|
344
|
+
readKeys(voice.built, phase, delta, undefined, voice.lerps, undefined, voice.intos, scratch);
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Calls a stateless fn voice's patch for a subject its general path has met, and folds the delta;
|
|
348
|
+
* false for a subject the host has let go of.
|
|
349
|
+
*/
|
|
350
|
+
export function call(voice, chans, rec, slot, elapsed, phase, pass, delay, w) {
|
|
351
|
+
const host = this.host;
|
|
352
|
+
const subject = this.subjectAt(slot);
|
|
353
|
+
if (subject === absent)
|
|
354
|
+
return false;
|
|
355
|
+
// Called already this frame, by the general path or a fill before a refill: reuse, as a probe does.
|
|
356
|
+
if (rec.probed === this.now && rec.delta !== null && rec.seeks === voice.seeks) {
|
|
357
|
+
if (w > 0)
|
|
358
|
+
this.foldDelta(chans, slot, rec.delta, w);
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
if (silent(voice, w))
|
|
362
|
+
return true;
|
|
363
|
+
const kept = reading.kept;
|
|
364
|
+
host.ready(voice, subject, rec, elapsed, pass, w);
|
|
365
|
+
if (this.keeps)
|
|
366
|
+
host.horizon(voice, delay);
|
|
367
|
+
else
|
|
368
|
+
reading.horizon = Number.POSITIVE_INFINITY;
|
|
369
|
+
const delta = voice.patch.at(phase, subject, voice.setting);
|
|
370
|
+
// What `influence` leaves on the record, so a probe on the general path this frame reuses it.
|
|
371
|
+
rec.delta = delta;
|
|
372
|
+
rec.probed = this.now;
|
|
373
|
+
rec.seeks = voice.seeks;
|
|
374
|
+
if (this.keeps)
|
|
375
|
+
host.after(voice, rec);
|
|
376
|
+
if (reading.kept !== kept && !voice.keeping)
|
|
377
|
+
host.kept(voice);
|
|
378
|
+
if (w > 0)
|
|
379
|
+
this.foldDelta(chans, slot, delta, w);
|
|
380
|
+
return true;
|
|
381
|
+
}
|
package/dist/fold.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Mixer } from './mixer.js';
|
|
2
|
+
import type { Subject, Voice } from './voice.js';
|
|
3
|
+
export type Key<O> = keyof O & string;
|
|
4
|
+
export declare function near(a: unknown, b: unknown): boolean;
|
|
5
|
+
export type Values = Record<string, unknown>;
|
|
6
|
+
/**
|
|
7
|
+
* One live voice's delta for one subject this frame, through its record for the subject, or null
|
|
8
|
+
* when it does not reach. Its weight is left in `w`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function influence<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I, now: number, held: Subject<unknown>): Record<string, unknown> | null;
|
|
11
|
+
/** Reads a keys voice's stops at `held.phase` into the subject's delta, through the voice's scratch. */
|
|
12
|
+
export declare function keyed<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I, held: Subject<unknown>): Record<string, unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* Runs `step` once per whole interval between where this subject last stopped and now, on a grid
|
|
15
|
+
* counted from when its delay ran out, and leaves the remainder for the next sample. Counting
|
|
16
|
+
* rather than adding keeps the grid where it is however many samples it is reached through.
|
|
17
|
+
*/
|
|
18
|
+
export declare function tick<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I, held: Subject<unknown>, tick: number, to: number): void;
|
|
19
|
+
/**
|
|
20
|
+
* Stop 0 for a retargeting voice: the subject's pose in the frame before this voice contributes.
|
|
21
|
+
* With no such frame on record — a voice cued before the subject was ever probed, or a host that
|
|
22
|
+
* samples into its own object — it is this frame's pose with every other voice folded in.
|
|
23
|
+
*/
|
|
24
|
+
export declare function baseFor<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I): Record<string, unknown>;
|
|
25
|
+
/**
|
|
26
|
+
* How fast each numeric channel this voice writes was moving, from the subject's last two probed
|
|
27
|
+
* poses, in units per ms. Undefined with fewer than two on record, which starts the voice at rest.
|
|
28
|
+
*/
|
|
29
|
+
export declare function slopeFor<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I): Record<string, unknown> | undefined;
|
|
30
|
+
export declare function isRest<I, O>(this: Mixer<I, O>, delta: Record<string, unknown>): boolean;
|
|
31
|
+
/** Whether a rest-less channel's influence is switched on, across a band rather than an edge. */
|
|
32
|
+
export declare function passes<I, O>(this: Mixer<I, O>, was: boolean | undefined, w: number): boolean;
|
|
33
|
+
/** Folds one voice's delta into the pose, its band state kept on the subject's record. */
|
|
34
|
+
export declare function apply<I, O>(this: Mixer<I, O>, pose: Record<string, unknown>, voice: Voice<I, O>, held: Subject<unknown>, delta: Record<string, unknown>, weight: number): void;
|
|
35
|
+
/** Clamps every bounded channel of a folded pose into its range, in place. */
|
|
36
|
+
export declare function clamp<I, O>(this: Mixer<I, O>, pose: Record<string, unknown>): Record<string, unknown>;
|
|
37
|
+
export declare function fold<I, O>(this: Mixer<I, O>, subject: I, out?: O, dry?: boolean, except?: number): O;
|
|
38
|
+
export declare function folded<I, O>(this: Mixer<I, O>, subject: I, out?: O, dry?: boolean, except?: number): O;
|
|
39
|
+
/**
|
|
40
|
+
* A subject's chain, linked for this frame, and in `linkedLaned` whether its lanes answer for the
|
|
41
|
+
* voices on them; a subject numbered since the frame's fill folds every voice, laned ones included.
|
|
42
|
+
*/
|
|
43
|
+
export declare function linked<I, O>(this: Mixer<I, O>, subject: I): Subject<unknown> | null;
|
|
44
|
+
/** The fold after a subject's chain is linked and its lanes asked whether it reads from them. */
|
|
45
|
+
export declare function foldWith<I, O>(this: Mixer<I, O>, subject: I, out: O, head: Subject<unknown> | null, laned: boolean, dry: boolean, except?: number): O;
|
|
46
|
+
/** Whether the subject numbered `owed` reads laned `voice` from the general path this fill. */
|
|
47
|
+
export declare function owedBy<I, O>(this: Mixer<I, O>, owed: number, voice: Voice<I, O>): boolean;
|
|
48
|
+
/** `foldWith`'s voices with a locus in play: each locus folds at its first member's place. */
|
|
49
|
+
export declare function foldLoci<I, O>(this: Mixer<I, O>, subject: I, pose: Record<string, unknown>, head: Subject<unknown>, laned: boolean, dry: boolean, except: number | undefined, owed: number): O;
|
|
50
|
+
/** `influence`, or for a dry fold a subject already probed this frame, without advancing it. */
|
|
51
|
+
export declare function read<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I, now: number, dry: boolean, held: Subject<unknown>): Record<string, unknown> | null;
|