@msb235/blits 0.7.1 → 0.8.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 +180 -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 +3 -0
- package/dist/clone.js +16 -1
- 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 +213 -28
- 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/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 +44 -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 +49 -6
- package/dist/transport.js +89 -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/voice.js
CHANGED
|
@@ -3,7 +3,7 @@ import { curve } from './easing.js';
|
|
|
3
3
|
import { motionOf } from './motion.js';
|
|
4
4
|
import { Named } from './named.js';
|
|
5
5
|
import { childPlayed, Holding, mixTime, ownedElapsed, ownerPatch } from './owner.js';
|
|
6
|
-
import { builtOf,
|
|
6
|
+
import { builtOf, intosOf } from './patch.js';
|
|
7
7
|
import { reading } from './reading.js';
|
|
8
8
|
import { Store } from './store.js';
|
|
9
9
|
export const none = Object.freeze([]);
|
|
@@ -118,17 +118,32 @@ export class Voice {
|
|
|
118
118
|
holder = null;
|
|
119
119
|
/** Whether its channels run as lanes this frame, so the general fold passes it by. */
|
|
120
120
|
laned = false;
|
|
121
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* How many times it has been sought or touched, so a delta read before either is not handed out
|
|
123
|
+
* after it.
|
|
124
|
+
*/
|
|
122
125
|
seeks = 0;
|
|
126
|
+
/** Whether the host has written to its handle, so it no longer plays as it was cued. */
|
|
127
|
+
written = false;
|
|
128
|
+
/** How many of `seeks` were the mix's `touch`, which moves no clock. */
|
|
129
|
+
touches = 0;
|
|
130
|
+
/** How many times its clock has jumped, which a booker counts hits again from. */
|
|
131
|
+
get jumps() {
|
|
132
|
+
return this.seeks - this.touches;
|
|
133
|
+
}
|
|
134
|
+
/** Seeks that rebuild state; a record stamped with fewer makes its state again before stepping. */
|
|
135
|
+
rebuilds = 0;
|
|
136
|
+
/** Whether its span's last fit jumped it to its end, which leaves it no time in that fit. */
|
|
137
|
+
skipped = false;
|
|
123
138
|
/** Whether its patch has kept state on a record through `setting.keep`, which makes it stateful. */
|
|
124
139
|
keeping = false;
|
|
125
140
|
/** Subjects fading out of this voice alone, by the ramp each started; null while none are. */
|
|
126
141
|
parts = null;
|
|
127
|
-
/** Subjects faded out of this voice, by the mix time each left at; null while none have. */
|
|
142
|
+
/** Subjects faded out of this voice, by the mix time and frame each left at; null while none have. */
|
|
128
143
|
parted = null;
|
|
129
144
|
/**
|
|
130
|
-
* Under history, the records of subjects that left this voice, by the mix time
|
|
131
|
-
* while a seek or a read back may reach them; null while none have.
|
|
145
|
+
* Under history, the records of subjects that left this voice, by the mix time and frame each
|
|
146
|
+
* left at, while a seek or a read back may reach them; null while none have.
|
|
132
147
|
*/
|
|
133
148
|
left = null;
|
|
134
149
|
ease;
|
|
@@ -146,8 +161,13 @@ export class Voice {
|
|
|
146
161
|
opened = Number.NaN;
|
|
147
162
|
/** Which of its entries in the mix's due queue is current; older ones are skipped when popped. */
|
|
148
163
|
dueToken = 0;
|
|
149
|
-
/**
|
|
164
|
+
/**
|
|
165
|
+
* Records whose `since` was still ahead when last worked out, which a later start, rate change
|
|
166
|
+
* or seek moves; null while none are.
|
|
167
|
+
*/
|
|
150
168
|
early = null;
|
|
169
|
+
/** The clocks it ran on before its current one, where a subject's origin may lie; null for none. */
|
|
170
|
+
clocks = null;
|
|
151
171
|
/** Reused for every call this voice makes, so it is valid only during the call. */
|
|
152
172
|
setting;
|
|
153
173
|
/** Every subject this voice has been asked about, so a handover knows when it is finished. */
|
|
@@ -157,8 +177,12 @@ export class Voice {
|
|
|
157
177
|
restedCount = 0;
|
|
158
178
|
/** The mix time it was cued at; -Infinity before the first sync. */
|
|
159
179
|
cuedAt = Number.NEGATIVE_INFINITY;
|
|
160
|
-
/** The
|
|
180
|
+
/** The frame it was cued in. */
|
|
181
|
+
cuedSeq = Number.NEGATIVE_INFINITY;
|
|
182
|
+
/** The mix time it left at, which a fade or seek given a time already passed puts behind now. */
|
|
161
183
|
doneAt = Number.POSITIVE_INFINITY;
|
|
184
|
+
/** The frame its leaving was decided in, which a seek or read back partitions by. */
|
|
185
|
+
doneSeq = Number.POSITIVE_INFINITY;
|
|
162
186
|
/** Under `history`, its controls after each change, oldest first. */
|
|
163
187
|
log = null;
|
|
164
188
|
/** Under `history`, its controls as `cue` left them, which a voice parked by a seek takes back. */
|
|
@@ -187,6 +211,20 @@ export class Voice {
|
|
|
187
211
|
unreached = null;
|
|
188
212
|
/** By subject number, a bit set where `unreached` stands for the subject; null until one is. */
|
|
189
213
|
unreachedBits = null;
|
|
214
|
+
/** Whether one record stands for every subject: see `shares` in `everyone.ts`. */
|
|
215
|
+
sharing = false;
|
|
216
|
+
/** That record, null until a chain first asks and once the voice shares no more. */
|
|
217
|
+
everyone = null;
|
|
218
|
+
/** While sharing, by subject number: a bit where a chain has asked about the subject. */
|
|
219
|
+
sighted = null;
|
|
220
|
+
/** While sharing, by subject number: the weight last given the subject on the general path. */
|
|
221
|
+
weights = null;
|
|
222
|
+
/**
|
|
223
|
+
* While sharing a patch that is called, by subject number: when it was last called for the
|
|
224
|
+
* subject and at what `seeks`, side by side, and the delta that call gave.
|
|
225
|
+
*/
|
|
226
|
+
stamps = null;
|
|
227
|
+
deltas = null;
|
|
190
228
|
/** The handle `cue` returned, which `voices` hands back too. */
|
|
191
229
|
handle = null;
|
|
192
230
|
/** With a history store, the key of every subject it has a record of; null without one. */
|
|
@@ -201,8 +239,10 @@ export class Voice {
|
|
|
201
239
|
*/
|
|
202
240
|
finished = false;
|
|
203
241
|
playedAs = undefined;
|
|
204
|
-
/** The mix time `played` was settled at
|
|
242
|
+
/** The mix time `played` was settled at. */
|
|
205
243
|
playedAt = Number.NaN;
|
|
244
|
+
/** The frame `played` was settled in, so a seek back to before it can open it again. */
|
|
245
|
+
playedSeq = Number.NaN;
|
|
206
246
|
donePromise = null;
|
|
207
247
|
doneSettle = null;
|
|
208
248
|
playedPromise = null;
|
|
@@ -241,18 +281,19 @@ export class Voice {
|
|
|
241
281
|
* Its finite loop ended (true) or it left first (false), whichever comes first, noticed at mix
|
|
242
282
|
* time `at`: `played` resolves.
|
|
243
283
|
*/
|
|
244
|
-
play(played, at) {
|
|
284
|
+
play(played, at, seq) {
|
|
245
285
|
if (this.quiet || this.playedAs !== undefined)
|
|
246
286
|
return;
|
|
247
287
|
this.playedAs = played;
|
|
248
288
|
this.playedAt = at;
|
|
289
|
+
this.playedSeq = seq;
|
|
249
290
|
this.playedSettle?.(played);
|
|
250
291
|
if (played && this.owner !== null)
|
|
251
|
-
childPlayed(this.owner, at);
|
|
292
|
+
childPlayed(this.owner, at, seq);
|
|
252
293
|
}
|
|
253
|
-
/** How `played` settled, and the mix time it did; undefined while it has not. */
|
|
294
|
+
/** How `played` settled, and the mix time and frame it did; undefined while it has not. */
|
|
254
295
|
get settled() {
|
|
255
|
-
return { played: this.playedAs, at: this.playedAt };
|
|
296
|
+
return { played: this.playedAs, at: this.playedAt, seq: this.playedSeq };
|
|
256
297
|
}
|
|
257
298
|
/** Whether `played` settled true, which its owner counted. */
|
|
258
299
|
get passed() {
|
|
@@ -263,42 +304,50 @@ export class Voice {
|
|
|
263
304
|
return this.playedAs === false;
|
|
264
305
|
}
|
|
265
306
|
/**
|
|
266
|
-
* A seek back to
|
|
307
|
+
* A seek back to frame `seq`: `done` and `played`, where they settled after it, start over with
|
|
267
308
|
* fresh promises. True where `played` had settled true, which its owner counted.
|
|
268
309
|
*/
|
|
269
|
-
reopen(
|
|
310
|
+
reopen(seq) {
|
|
270
311
|
let unplayed = false;
|
|
271
|
-
if (this.playedAs !== undefined && !(this.
|
|
312
|
+
if (this.playedAs !== undefined && !(this.playedSeq <= seq)) {
|
|
272
313
|
unplayed = this.playedAs;
|
|
273
314
|
this.playedAs = undefined;
|
|
274
315
|
this.playedPromise = null;
|
|
275
316
|
this.playedSettle = null;
|
|
276
317
|
}
|
|
277
|
-
if (this.finished && !(this.
|
|
318
|
+
if (this.finished && !(this.doneSeq <= seq)) {
|
|
278
319
|
this.finished = false;
|
|
279
320
|
this.donePromise = null;
|
|
280
321
|
this.doneSettle = null;
|
|
281
322
|
}
|
|
282
323
|
return unplayed;
|
|
283
324
|
}
|
|
284
|
-
/**
|
|
285
|
-
|
|
286
|
-
* again. What it played since is forgotten, and `done` and `played` start over where they had
|
|
287
|
-
* settled.
|
|
288
|
-
*/
|
|
289
|
-
park() {
|
|
290
|
-
this.reopen(Number.NEGATIVE_INFINITY);
|
|
291
|
-
const c = this.first;
|
|
325
|
+
/** Takes on a set of controls: its clock, weight, fades and where its anchors put it. */
|
|
326
|
+
take(c) {
|
|
292
327
|
this.anchorNow = c.anchorNow;
|
|
293
328
|
this.anchorElapsed = c.anchorElapsed;
|
|
294
329
|
this.rate = c.rate;
|
|
295
330
|
this.ramp = c.ramp;
|
|
296
331
|
this.weight = c.weight;
|
|
297
332
|
this.out = c.out;
|
|
333
|
+
this.back = c.back;
|
|
298
334
|
this.start = c.start;
|
|
299
335
|
this.outAt = c.outAt;
|
|
300
336
|
this.outOver = c.outOver;
|
|
301
337
|
this.outSet = c.outSet;
|
|
338
|
+
this.rebuilds = c.rebuilds;
|
|
339
|
+
const clocks = this.clocks;
|
|
340
|
+
if (clocks !== null && clocks.length !== c.past)
|
|
341
|
+
this.clocks = c.past === 0 ? null : clocks.slice(0, c.past);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* A seek back to before its cue: out of the mix, as `cue` left it, until the mix plays the cue
|
|
345
|
+
* again. What it played since is forgotten, and `done` and `played` start over where they had
|
|
346
|
+
* settled.
|
|
347
|
+
*/
|
|
348
|
+
park() {
|
|
349
|
+
this.reopen(Number.NEGATIVE_INFINITY);
|
|
350
|
+
this.take(this.first);
|
|
302
351
|
this.log = null;
|
|
303
352
|
this.state = 'pending';
|
|
304
353
|
this.subjects = new Store();
|
|
@@ -316,7 +365,9 @@ export class Voice {
|
|
|
316
365
|
this.latest = 0;
|
|
317
366
|
this.restedCount = 0;
|
|
318
367
|
this.cuedAt = Number.NEGATIVE_INFINITY;
|
|
368
|
+
this.cuedSeq = Number.NEGATIVE_INFINITY;
|
|
319
369
|
this.doneAt = Number.POSITIVE_INFINITY;
|
|
370
|
+
this.doneSeq = Number.POSITIVE_INFINITY;
|
|
320
371
|
this.placing = false;
|
|
321
372
|
this.answers = null;
|
|
322
373
|
this.folds = null;
|
|
@@ -324,6 +375,12 @@ export class Voice {
|
|
|
324
375
|
this.ownKept = null;
|
|
325
376
|
this.unreached = null;
|
|
326
377
|
this.unreachedBits = null;
|
|
378
|
+
this.sharing = false;
|
|
379
|
+
this.everyone = null;
|
|
380
|
+
this.sighted = null;
|
|
381
|
+
this.weights = null;
|
|
382
|
+
this.stamps = null;
|
|
383
|
+
this.deltas = null;
|
|
327
384
|
if (this.holding !== null)
|
|
328
385
|
this.holding = new Holding();
|
|
329
386
|
}
|
|
@@ -346,13 +403,13 @@ export class Voice {
|
|
|
346
403
|
: undefined;
|
|
347
404
|
this.ease = fade.ease === undefined ? undefined : curve(fade.ease);
|
|
348
405
|
this.passes = passesOf(spec.loop);
|
|
349
|
-
this.duration =
|
|
406
|
+
this.duration = patch.duration;
|
|
350
407
|
this.span =
|
|
351
408
|
this.duration > 0 && Number.isFinite(this.passes)
|
|
352
409
|
? this.duration * this.passes
|
|
353
410
|
: Number.POSITIVE_INFINITY;
|
|
354
411
|
this.motion = motionOf(patch);
|
|
355
|
-
const freezes = this.motion === undefined ?
|
|
412
|
+
const freezes = this.motion === undefined ? spec.freeze : undefined;
|
|
356
413
|
// A voice with no freeze of its own takes its owner's.
|
|
357
414
|
const inherits = freezes === undefined && this.motion === undefined && owner !== null;
|
|
358
415
|
this.freezesBefore = inherits
|
|
@@ -375,10 +432,11 @@ export class Voice {
|
|
|
375
432
|
const t = timeWith(this, elapsed);
|
|
376
433
|
return this.owner === null ? t : mixTime(this.owner, t);
|
|
377
434
|
}
|
|
378
|
-
/** Records this voice's controls as they stand, from mix time `at`. */
|
|
379
|
-
note(at, sync = false) {
|
|
435
|
+
/** Records this voice's controls as they stand, from mix time `at` in frame `seq`. */
|
|
436
|
+
note(at, seq, sync = false) {
|
|
380
437
|
this.log?.push({
|
|
381
438
|
at,
|
|
439
|
+
seq,
|
|
382
440
|
sync,
|
|
383
441
|
anchorNow: this.anchorNow,
|
|
384
442
|
anchorElapsed: this.anchorElapsed,
|
|
@@ -391,13 +449,17 @@ export class Voice {
|
|
|
391
449
|
outAt: this.outAt,
|
|
392
450
|
outOver: this.outOver,
|
|
393
451
|
outSet: this.outSet,
|
|
452
|
+
rebuilds: this.rebuilds,
|
|
453
|
+
past: this.clocks?.length ?? 0,
|
|
394
454
|
});
|
|
395
455
|
}
|
|
396
456
|
/**
|
|
397
457
|
* A copy for a projection: its own setting, which sends nothing, and its own per-subject records,
|
|
398
|
-
* filled from `fill`. With `controls` it takes those, as the voice stood at an earlier time.
|
|
458
|
+
* filled from `fill`. With `controls` it takes those, as the voice stood at an earlier time. With
|
|
459
|
+
* `share`, a voice keeping one record for every subject gives its copy a copy of that one, which
|
|
460
|
+
* a read ahead starts every subject from; `fill` then serves only a copy that shares no more.
|
|
399
461
|
*/
|
|
400
|
-
copy(fill, controls) {
|
|
462
|
+
copy(fill, controls, share = false) {
|
|
401
463
|
// A literal keeps the copy in fast mode, where `Object.assign` left it a dictionary that every
|
|
402
464
|
// read in a projection looked up by name.
|
|
403
465
|
const v = { __proto__: Voice.prototype, ...this };
|
|
@@ -411,22 +473,22 @@ export class Voice {
|
|
|
411
473
|
v.laned = false;
|
|
412
474
|
v.log = null;
|
|
413
475
|
v.early = null;
|
|
414
|
-
if (controls)
|
|
415
|
-
v.
|
|
416
|
-
|
|
417
|
-
v.
|
|
418
|
-
v.ramp = controls.ramp;
|
|
419
|
-
v.weight = controls.weight;
|
|
420
|
-
v.out = controls.out;
|
|
421
|
-
v.back = controls.back;
|
|
422
|
-
v.start = controls.start;
|
|
423
|
-
v.outAt = controls.outAt;
|
|
424
|
-
v.outOver = controls.outOver;
|
|
425
|
-
v.outSet = controls.outSet;
|
|
426
|
-
}
|
|
476
|
+
if (controls)
|
|
477
|
+
v.take(controls);
|
|
478
|
+
if (v.clocks === this.clocks && v.clocks !== null)
|
|
479
|
+
v.clocks = v.clocks.slice();
|
|
427
480
|
v.quiet = true;
|
|
428
481
|
v.unreached = null;
|
|
429
482
|
v.unreachedBits = null;
|
|
483
|
+
const all = share && this.sharing ? this.everyone : null;
|
|
484
|
+
v.sharing = all !== null;
|
|
485
|
+
// Its delta is the live voice's to write into, and a keys voice's is in the live scratch.
|
|
486
|
+
v.everyone =
|
|
487
|
+
all === null ? null : { ...all, delta: null, probed: Number.NaN, from: all.stepped };
|
|
488
|
+
v.sighted = null;
|
|
489
|
+
v.weights = null;
|
|
490
|
+
v.stamps = null;
|
|
491
|
+
v.deltas = null;
|
|
430
492
|
return v;
|
|
431
493
|
}
|
|
432
494
|
rateAt(now) {
|
package/dist/wave.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Kit, Patch } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The shape of one cycle of a `wave`.
|
|
4
|
+
*
|
|
5
|
+
* @category patch
|
|
6
|
+
*/
|
|
7
|
+
export type WaveShape = 'sine' | 'triangle' | 'saw' | 'square';
|
|
8
|
+
/**
|
|
9
|
+
* What `wave` takes besides its duration.
|
|
10
|
+
*
|
|
11
|
+
* @category patch
|
|
12
|
+
*/
|
|
13
|
+
export interface WaveOptions<O> {
|
|
14
|
+
/** Default 'sine'. */
|
|
15
|
+
shape?: WaveShape;
|
|
16
|
+
/** Whole or fractional cycles per pass of the patch's duration. Default 1. */
|
|
17
|
+
cycles?: number;
|
|
18
|
+
/** Where in its cycle the wave starts, 0..1. Default 0. */
|
|
19
|
+
phase?: number;
|
|
20
|
+
/** Peak swing per channel, either side of the channel's rest. Its keys are the patch's writes,
|
|
21
|
+
* and only a channel holding a number can take one. */
|
|
22
|
+
depth: {
|
|
23
|
+
[K in keyof O as NonNullable<O[K]> extends number ? K : never]?: number;
|
|
24
|
+
};
|
|
25
|
+
/** The channels this wave was written against, which `cue` checks a mix's kit against. Each
|
|
26
|
+
* channel swings around its rest here, so a `mul` channel pulses around 1; without it, around 0. */
|
|
27
|
+
kit?: Partial<Kit<O>>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The unit wave: -1..1 at `x` cycles, starting at 0 and rising (square: +1 for the first half).
|
|
31
|
+
*
|
|
32
|
+
* @category patch
|
|
33
|
+
*/
|
|
34
|
+
export declare function waveAt(shape: WaveShape, x: number): number;
|
|
35
|
+
/**
|
|
36
|
+
* A periodic swing on each channel `depth` names, around its rest: an LFO for a bob, a sway or a
|
|
37
|
+
* pulse. An `fn` patch that keeps its options on `patch.wave`, for an engine reading the wave
|
|
38
|
+
* rather than calling it.
|
|
39
|
+
*
|
|
40
|
+
* @category patch
|
|
41
|
+
*/
|
|
42
|
+
export declare function wave<I, O>(duration: number, opts: WaveOptions<O>): Patch<I, O, void>;
|
package/dist/wave.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { patch } from './patch.js';
|
|
2
|
+
/**
|
|
3
|
+
* The unit wave: -1..1 at `x` cycles, starting at 0 and rising (square: +1 for the first half).
|
|
4
|
+
*
|
|
5
|
+
* @category patch
|
|
6
|
+
*/
|
|
7
|
+
export function waveAt(shape, x) {
|
|
8
|
+
const u = x - Math.floor(x);
|
|
9
|
+
switch (shape) {
|
|
10
|
+
case 'sine':
|
|
11
|
+
return Math.sin(2 * Math.PI * u);
|
|
12
|
+
case 'triangle':
|
|
13
|
+
return u < 0.25 ? 4 * u : u < 0.75 ? 2 - 4 * u : 4 * u - 4;
|
|
14
|
+
case 'saw':
|
|
15
|
+
return u < 0.5 ? 2 * u : 2 * u - 2;
|
|
16
|
+
case 'square':
|
|
17
|
+
return u < 0.5 ? 1 : -1;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const plans = new WeakMap();
|
|
21
|
+
function planOf(opts) {
|
|
22
|
+
let plan = plans.get(opts);
|
|
23
|
+
if (plan === undefined) {
|
|
24
|
+
const depth = opts.depth;
|
|
25
|
+
const writes = Object.keys(depth);
|
|
26
|
+
plan = {
|
|
27
|
+
shape: opts.shape ?? 'sine',
|
|
28
|
+
cycles: opts.cycles ?? 1,
|
|
29
|
+
start: opts.phase ?? 0,
|
|
30
|
+
writes,
|
|
31
|
+
depths: writes.map((c) => depth[c]),
|
|
32
|
+
rests: writes.map((c) => {
|
|
33
|
+
const rest = opts.kit?.[c]?.rest;
|
|
34
|
+
return typeof rest === 'number' ? rest : 0;
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
plans.set(opts, plan);
|
|
38
|
+
}
|
|
39
|
+
return plan;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A periodic swing on each channel `depth` names, around its rest: an LFO for a bob, a sway or a
|
|
43
|
+
* pulse. An `fn` patch that keeps its options on `patch.wave`, for an engine reading the wave
|
|
44
|
+
* rather than calling it.
|
|
45
|
+
*
|
|
46
|
+
* @category patch
|
|
47
|
+
*/
|
|
48
|
+
export function wave(duration, opts) {
|
|
49
|
+
const own = planOf(opts);
|
|
50
|
+
const made = patch(duration,
|
|
51
|
+
// A copy of the patch carries this `at` with a `wave` of its own, which it is read by.
|
|
52
|
+
function at(phase) {
|
|
53
|
+
const plan = this.wave === opts ? own : planOf(this.wave);
|
|
54
|
+
const unit = waveAt(plan.shape, phase * plan.cycles + plan.start);
|
|
55
|
+
const out = {};
|
|
56
|
+
for (let i = 0; i < plan.writes.length; i++)
|
|
57
|
+
out[plan.writes[i]] =
|
|
58
|
+
plan.rests[i] + plan.depths[i] * unit;
|
|
59
|
+
return out;
|
|
60
|
+
}, { writes: own.writes, kit: opts.kit });
|
|
61
|
+
return { ...made, wave: opts };
|
|
62
|
+
}
|
package/dist/weigh.js
CHANGED
|
@@ -39,7 +39,7 @@ export function ownedBy(voice, subject) {
|
|
|
39
39
|
*/
|
|
40
40
|
export function ownerBase(owner, subject) {
|
|
41
41
|
const now = this.now;
|
|
42
|
-
const held = this.held(owner, subject,
|
|
42
|
+
const held = this.held(owner, subject, -1);
|
|
43
43
|
if (held.probed === this.frame)
|
|
44
44
|
return held.phase;
|
|
45
45
|
const kept = reading.kept;
|