@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/transport.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { listedAll } from './marks.js';
|
|
2
2
|
import { move, nextFrame, waits } from './move.js';
|
|
3
3
|
import { Pace } from './pace.js';
|
|
4
|
-
import { Pager, prepare } from './paging.js';
|
|
4
|
+
import { Pager, pageTransport, prepare } from './paging.js';
|
|
5
5
|
import { pin } from './place.js';
|
|
6
6
|
import { projectAll } from './project.js';
|
|
7
7
|
import { scoredDrop, scoredJoin } from './scored.js';
|
|
@@ -9,6 +9,10 @@ import { seek } from './seek.js';
|
|
|
9
9
|
import { record, replay, tapeOf } from './tape.js';
|
|
10
10
|
/** How far the tape's reach moves before it is pruned again, ms of mix time. */
|
|
11
11
|
const TAPE_STEP_MS = 1000;
|
|
12
|
+
/** Whether a change made in frame `seq` falls inside `cut`; `early` for one made before the frame was read. */
|
|
13
|
+
export function within(cut, seq, early = false) {
|
|
14
|
+
return seq < cut.seq || (seq === cut.seq && (!cut.strict || early));
|
|
15
|
+
}
|
|
12
16
|
/**
|
|
13
17
|
* The clock its mixes play on: host time less what `rebase` took out, at the rate its `pace`
|
|
14
18
|
* sets, moved by `sync` and `seek`, and the tape of calls a seek plays again. A mix made alone
|
|
@@ -29,6 +33,13 @@ export class Transport {
|
|
|
29
33
|
woken = false;
|
|
30
34
|
/** The mix clock at the last sync or seek. */
|
|
31
35
|
now = Number.NaN;
|
|
36
|
+
/**
|
|
37
|
+
* The number of the frame being played: each sync and seek begins one, and so does each host
|
|
38
|
+
* time the tape plays calls again at.
|
|
39
|
+
*/
|
|
40
|
+
seq = 0;
|
|
41
|
+
/** Under history, the frames a seek or read back may reach, oldest first. */
|
|
42
|
+
frames = [];
|
|
32
43
|
/** Host time at the last sync: the host's timestamp less every gap `rebase` took out. */
|
|
33
44
|
u = Number.NaN;
|
|
34
45
|
offset = 0;
|
|
@@ -42,6 +53,20 @@ export class Transport {
|
|
|
42
53
|
taped = Number.NEGATIVE_INFINITY;
|
|
43
54
|
/** The rate; null while it has never been set, when mix time is host time. */
|
|
44
55
|
pace = null;
|
|
56
|
+
/** The latest mix time a voice any mix on it let go of from `gone` had left at. */
|
|
57
|
+
forgotTo = Number.NEGATIVE_INFINITY;
|
|
58
|
+
/**
|
|
59
|
+
* The latest mix time a host changed a mix on it, or a voice or a subject left one: without
|
|
60
|
+
* history, the earliest time a read back still finds the mixes as they stand now.
|
|
61
|
+
*/
|
|
62
|
+
settled = Number.NEGATIVE_INFINITY;
|
|
63
|
+
/** Records a change or a leaving at the mix time now; before the first sync, nothing. */
|
|
64
|
+
settle() {
|
|
65
|
+
if (this.now > this.settled)
|
|
66
|
+
this.settled = this.now;
|
|
67
|
+
}
|
|
68
|
+
/** The frame the rate was first set in, which a seek or read back to before it finds unset. */
|
|
69
|
+
pacedSeq = Number.POSITIVE_INFINITY;
|
|
45
70
|
/** True while the tape makes a recorded call again, so it is not recorded twice. */
|
|
46
71
|
replaying = false;
|
|
47
72
|
announced = [];
|
|
@@ -64,7 +89,7 @@ export class Transport {
|
|
|
64
89
|
get projecting() {
|
|
65
90
|
return this.members.some((m) => m.projecting);
|
|
66
91
|
}
|
|
67
|
-
/** Takes a mix on, after every mix that joined before it; `slot` keeps a
|
|
92
|
+
/** Takes a mix on, after every mix that joined before it; `slot` keeps one a seek puts back in place. */
|
|
68
93
|
join(mix, slot = this.slots++) {
|
|
69
94
|
mix.slot = slot;
|
|
70
95
|
const i = this.members.findIndex((m) => m.slot > slot);
|
|
@@ -83,16 +108,16 @@ export class Transport {
|
|
|
83
108
|
scoredDrop(m);
|
|
84
109
|
m.dropped = true;
|
|
85
110
|
if (this.history !== undefined) {
|
|
86
|
-
this.dropped.push({ mix: m, at: this.now });
|
|
111
|
+
this.dropped.push({ mix: m, at: this.now, seq: this.seq });
|
|
87
112
|
record(this, 'drop', () => this.drop(mix));
|
|
88
113
|
}
|
|
89
114
|
}
|
|
90
|
-
/** A seek back to `
|
|
91
|
-
undrop(
|
|
92
|
-
const back = this.dropped.filter((d) => d.
|
|
115
|
+
/** A seek back to frame `seq`: every mix dropped after it is back on, as it stood then. */
|
|
116
|
+
undrop(seq) {
|
|
117
|
+
const back = this.dropped.filter((d) => d.seq > seq);
|
|
93
118
|
if (back.length === 0)
|
|
94
119
|
return;
|
|
95
|
-
this.dropped = this.dropped.filter((d) => d.
|
|
120
|
+
this.dropped = this.dropped.filter((d) => d.seq <= seq);
|
|
96
121
|
for (const d of back) {
|
|
97
122
|
d.mix.dropped = false;
|
|
98
123
|
this.join(d.mix, d.mix.slot);
|
|
@@ -151,6 +176,8 @@ export class Transport {
|
|
|
151
176
|
}
|
|
152
177
|
}
|
|
153
178
|
syncAt(timestamp) {
|
|
179
|
+
if (Number.isNaN(timestamp))
|
|
180
|
+
throw new RangeError('blits: sync was given NaN; it takes the host clock as a number');
|
|
154
181
|
if (timestamp < this.last && !this.rebasing)
|
|
155
182
|
throw new RangeError(`blits: sync went back from ${this.last} to ${timestamp}; the host's clock only goes forward, and seek moves the mix`);
|
|
156
183
|
if (this.rebasing && !Number.isNaN(this.last))
|
|
@@ -167,17 +194,21 @@ export class Transport {
|
|
|
167
194
|
this.born = now;
|
|
168
195
|
// Calls the host made before, played again where a seek went back past them; one may set the
|
|
169
196
|
// rate, which moves where this sync lands.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
197
|
+
// A call played again at this sync's own host time began its frame, moved there before it.
|
|
198
|
+
let begun = false;
|
|
199
|
+
if (this.tape !== undefined && later) {
|
|
200
|
+
begun = replay(this, (at) => at <= u) === u;
|
|
173
201
|
pace = this.pace;
|
|
174
|
-
now = reading();
|
|
202
|
+
now = pace === null ? u : pace.reading(u);
|
|
203
|
+
}
|
|
204
|
+
if (!begun) {
|
|
205
|
+
this.tick(now);
|
|
206
|
+
// Host time moving while the mix clock stands still lands what waits on host time or the host.
|
|
207
|
+
const moved = now !== this.now;
|
|
208
|
+
for (const m of this.members)
|
|
209
|
+
if (moved || (later && pace !== null && waits(m)))
|
|
210
|
+
move(m, now);
|
|
175
211
|
}
|
|
176
|
-
// Host time moving while the mix clock stands still lands what waits on host time or the host.
|
|
177
|
-
const moved = now !== this.now;
|
|
178
|
-
for (const m of this.members)
|
|
179
|
-
if (moved || (later && pace !== null && waits(m)))
|
|
180
|
-
move(m, now);
|
|
181
212
|
const history = this.history;
|
|
182
213
|
this.kept();
|
|
183
214
|
const reach = this.keepsFrom();
|
|
@@ -189,6 +220,13 @@ export class Transport {
|
|
|
189
220
|
}
|
|
190
221
|
if (history !== undefined && this.dropped.length > 0)
|
|
191
222
|
this.dropped = this.dropped.filter((d) => d.at >= reach);
|
|
223
|
+
const frames = this.frames;
|
|
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);
|
|
229
|
+
pageTransport(this);
|
|
192
230
|
this.woken = false;
|
|
193
231
|
// It is a frame too, which asks every weight signal again, so one reading input follows it.
|
|
194
232
|
if (still)
|
|
@@ -203,8 +241,38 @@ export class Transport {
|
|
|
203
241
|
b.sync();
|
|
204
242
|
}
|
|
205
243
|
}
|
|
244
|
+
/** Begins a frame at mix time `at` and the host time the transport stands at. */
|
|
245
|
+
tick(at) {
|
|
246
|
+
const seq = ++this.seq;
|
|
247
|
+
if (this.history !== undefined)
|
|
248
|
+
this.frames.push({ seq, at, u: this.u });
|
|
249
|
+
for (const m of this.members)
|
|
250
|
+
m.looked = false;
|
|
251
|
+
}
|
|
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
|
+
}
|
|
206
273
|
rebase() {
|
|
207
274
|
this.rebasing = true;
|
|
275
|
+
this.settle();
|
|
208
276
|
}
|
|
209
277
|
/**
|
|
210
278
|
* Moves `floor` past what the mix clock standing at `now` has let go of. With a store nothing is
|
|
@@ -242,6 +310,7 @@ export class Transport {
|
|
|
242
310
|
throw new RangeError(`blits: a mix's rate is a finite number, 0 or more, not ${rate}`);
|
|
243
311
|
if (this.pace === null) {
|
|
244
312
|
this.pace = new Pace(this.history !== undefined);
|
|
313
|
+
this.pacedSeq = this.seq;
|
|
245
314
|
// Until now mix time was host time, so a pending voice's start is the host time it was given.
|
|
246
315
|
for (const m of this.members)
|
|
247
316
|
for (const v of m.cued)
|
|
@@ -249,7 +318,7 @@ export class Transport {
|
|
|
249
318
|
pin(m, v, v.start);
|
|
250
319
|
}
|
|
251
320
|
const reach = this.history === undefined ? Number.NEGATIVE_INFINITY : this.keepsFrom();
|
|
252
|
-
this.pace.change(this.u, rate, over, reach);
|
|
321
|
+
this.pace.change(this.u, rate, over, reach, this.seq);
|
|
253
322
|
for (const m of this.members)
|
|
254
323
|
m.stir();
|
|
255
324
|
record(this, 'rate', () => this.ramp(rate, over));
|
|
@@ -263,14 +332,15 @@ export function announce(transport, name, opts, slot, mix) {
|
|
|
263
332
|
score: opts.score,
|
|
264
333
|
tags: opts.tags ?? [],
|
|
265
334
|
at,
|
|
266
|
-
made: Number.isNaN(transport.now) ? Number.NEGATIVE_INFINITY : transport.now,
|
|
267
335
|
order: transport.nextId++,
|
|
336
|
+
seq: transport.seq,
|
|
268
337
|
slot,
|
|
269
338
|
mix,
|
|
270
339
|
};
|
|
271
340
|
transport.announced.push(mark);
|
|
341
|
+
transport.settle();
|
|
272
342
|
record(transport, 'announce', () => {
|
|
273
|
-
transport.announced.push({ ...mark });
|
|
343
|
+
transport.announced.push({ ...mark, seq: transport.seq });
|
|
274
344
|
});
|
|
275
345
|
}
|
|
276
346
|
/**
|
package/dist/turns.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Laned } from './lane.js';
|
|
2
|
+
/** `d` less whole turns, so within half a turn of 0: the short way round a circle of `turn`. */
|
|
3
|
+
export declare function shortWay(d: number, turn: number): number;
|
|
4
|
+
/**
|
|
5
|
+
* Folds a rotation's value into a subject's numbers at `base`, at weight `w`, in the channel's own
|
|
6
|
+
* arithmetic: an angle the short way round and then as a sum, anything else through the channel's
|
|
7
|
+
* `fold`, which takes its axes as one value.
|
|
8
|
+
*/
|
|
9
|
+
export declare function turnInto(ch: Laned, values: Float64Array, base: number, value: unknown, w: number): void;
|
|
10
|
+
/** `turnInto` for a value held as numbers at `from` in `source`, as a locus holds what it gathered. */
|
|
11
|
+
export declare function turnFrom(ch: Laned, values: Float64Array, base: number, source: Float64Array, from: number, w: number): void;
|
|
12
|
+
/**
|
|
13
|
+
* A locus member's rotation lerped by `u` into what the members before it left at `base`, through
|
|
14
|
+
* the channel's own `lerp`; the first member's value as it is, where `taken` is 0.
|
|
15
|
+
*/
|
|
16
|
+
export declare function turnToward(ch: Laned, values: Float64Array, base: number, value: unknown, taken: number, u: number): void;
|
|
17
|
+
/** Sets every numbered subject's values on a lane to the channel's rest, as a fill starts from. */
|
|
18
|
+
export declare function rested(ch: Laned, size: number): void;
|
package/dist/turns.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { doubles } from './channels.js';
|
|
2
|
+
/** `d` less whole turns, so within half a turn of 0: the short way round a circle of `turn`. */
|
|
3
|
+
export function shortWay(d, turn) {
|
|
4
|
+
return d - turn * Math.round(d / turn);
|
|
5
|
+
}
|
|
6
|
+
let held = doubles(4, 0);
|
|
7
|
+
let other = doubles(4, 0);
|
|
8
|
+
/** A subject's numbers on a lane as an array a channel's own functions take: `to`, or one as long. */
|
|
9
|
+
function lift(values, base, axes, to) {
|
|
10
|
+
const out = to.length === axes ? to : doubles(axes, 0);
|
|
11
|
+
for (let a = 0; a < axes; a++)
|
|
12
|
+
out[a] = values[base + a];
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Folds a rotation's value into a subject's numbers at `base`, at weight `w`, in the channel's own
|
|
17
|
+
* arithmetic: an angle the short way round and then as a sum, anything else through the channel's
|
|
18
|
+
* `fold`, which takes its axes as one value.
|
|
19
|
+
*/
|
|
20
|
+
export function turnInto(ch, values, base, value, w) {
|
|
21
|
+
if (ch.turn > 0) {
|
|
22
|
+
values[base] = values[base] + shortWay(value, ch.turn) * w;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const axes = ch.axes;
|
|
26
|
+
held = lift(values, base, axes, held);
|
|
27
|
+
const out = ch.channel.fold(held, value, w);
|
|
28
|
+
for (let a = 0; a < axes; a++)
|
|
29
|
+
values[base + a] = out[a];
|
|
30
|
+
}
|
|
31
|
+
/** `turnInto` for a value held as numbers at `from` in `source`, as a locus holds what it gathered. */
|
|
32
|
+
export function turnFrom(ch, values, base, source, from, w) {
|
|
33
|
+
if (ch.turn > 0) {
|
|
34
|
+
turnInto(ch, values, base, source[from], w);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
other = lift(source, from, ch.axes, other);
|
|
38
|
+
turnInto(ch, values, base, other, w);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A locus member's rotation lerped by `u` into what the members before it left at `base`, through
|
|
42
|
+
* the channel's own `lerp`; the first member's value as it is, where `taken` is 0.
|
|
43
|
+
*/
|
|
44
|
+
export function turnToward(ch, values, base, value, taken, u) {
|
|
45
|
+
if (ch.turn > 0) {
|
|
46
|
+
const was = values[base];
|
|
47
|
+
const v = value;
|
|
48
|
+
values[base] = taken === 0 ? v : was + shortWay(v - was, ch.turn) * u;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const axes = ch.axes;
|
|
52
|
+
let out = value;
|
|
53
|
+
if (taken !== 0) {
|
|
54
|
+
held = lift(values, base, axes, held);
|
|
55
|
+
out = ch.channel.lerp(held, value, u);
|
|
56
|
+
}
|
|
57
|
+
for (let a = 0; a < axes; a++)
|
|
58
|
+
values[base + a] = out[a];
|
|
59
|
+
}
|
|
60
|
+
/** Sets every numbered subject's values on a lane to the channel's rest, as a fill starts from. */
|
|
61
|
+
export function rested(ch, size) {
|
|
62
|
+
const values = ch.values;
|
|
63
|
+
const axes = ch.axes;
|
|
64
|
+
if (ch.op !== 'own') {
|
|
65
|
+
values.fill(ch.rest, 0, size * axes);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const start = ch.start;
|
|
69
|
+
for (let i = 0; i < size * axes; i += axes)
|
|
70
|
+
for (let a = 0; a < axes; a++)
|
|
71
|
+
values[i + a] = start[a];
|
|
72
|
+
}
|
package/dist/tweened.js
CHANGED
|
@@ -5,7 +5,7 @@ const SPAN = 1e-4;
|
|
|
5
5
|
* `read` asks for it; a mix and a retarget never use it.
|
|
6
6
|
*/
|
|
7
7
|
let slope = false;
|
|
8
|
-
/** The last tween sample's
|
|
8
|
+
/** The last tween sample's progress and curve, and the share of the way it had left to go. */
|
|
9
9
|
const memo = { u: Number.NaN, ease: undefined, left: 0 };
|
|
10
10
|
/** Turns the velocity `eased` works out on or off. */
|
|
11
11
|
export function sloped(on) {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One field of a delta, with its own arithmetic.
|
|
3
|
+
*
|
|
4
|
+
* @category channel
|
|
5
|
+
*/
|
|
6
|
+
export interface Channel<V> {
|
|
7
|
+
/**
|
|
8
|
+
* Names this channel's arithmetic, so a patch written against one channel can be checked against
|
|
9
|
+
* another of the same name. Two channels of one kind must fold identically; the stock ones set it,
|
|
10
|
+
* as `'sum'` or `'vec(3, sum)'`. Absent, a channel matches only itself.
|
|
11
|
+
*/
|
|
12
|
+
kind?: string;
|
|
13
|
+
/**
|
|
14
|
+
* The range a numeric value means anything in, such as 0..1 for an opacity. The mix clamps the
|
|
15
|
+
* folded value to it, axis by axis for an array, so neither stacked voices nor a retarget that
|
|
16
|
+
* carries speed can push it out; overshoot stops flat at the bound.
|
|
17
|
+
*/
|
|
18
|
+
bounds?: readonly [min: number, max: number];
|
|
19
|
+
/** Identity. Absent means the channel has none: it replaces rather than contributes. */
|
|
20
|
+
rest?: V;
|
|
21
|
+
/** Fold two contributions into one. */
|
|
22
|
+
merge(a: V, b: V): V;
|
|
23
|
+
/** Fade toward `rest` by weight 0..1. Required when `rest` is set; absent otherwise. */
|
|
24
|
+
scale?(v: V, w: number): V;
|
|
25
|
+
/** Interpolate, for retargeting, for blending alternatives, and for folding a locus. */
|
|
26
|
+
lerp(a: V, b: V, u: number): V;
|
|
27
|
+
/**
|
|
28
|
+
* Optional: `merge(into, scale(v, w))`, written into `into` and returned, so a channel whose
|
|
29
|
+
* values are objects need not allocate per contribution. The mix only hands it a value it made.
|
|
30
|
+
*/
|
|
31
|
+
fold?(into: V, v: V, w: number): V;
|
|
32
|
+
/**
|
|
33
|
+
* Optional: a fresh copy of `v`, which the mix makes of `rest` for `fold` to write into. The mix
|
|
34
|
+
* copies arrays, typed arrays and plain objects itself; a channel whose `rest` is anything else,
|
|
35
|
+
* such as a class instance, and that has `fold`, needs this or `cue` refuses it.
|
|
36
|
+
*/
|
|
37
|
+
copy?(v: V): V;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The channel set for one kind of delta.
|
|
41
|
+
*
|
|
42
|
+
* @category channel
|
|
43
|
+
*/
|
|
44
|
+
export type Kit<O> = {
|
|
45
|
+
readonly [K in keyof O]-?: Channel<NonNullable<O[K]>>;
|
|
46
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { VoiceSpec } from './voice.js';
|
|
2
|
+
/**
|
|
3
|
+
* What a mix or transport remembers, for `seek` and `project` to read back: see
|
|
4
|
+
* `MixOptions.history`.
|
|
5
|
+
*
|
|
6
|
+
* @category mix
|
|
7
|
+
*/
|
|
8
|
+
export interface HistoryOptions {
|
|
9
|
+
/** How much stays in memory, ms of mix time. With a `store`, the rest is paged out, not dropped. */
|
|
10
|
+
ms: number;
|
|
11
|
+
every?: number;
|
|
12
|
+
inputs?: boolean;
|
|
13
|
+
tape?: TapeMaker;
|
|
14
|
+
/**
|
|
15
|
+
* Where history older than `ms` goes instead of being dropped. A `seek` or `project` reaching
|
|
16
|
+
* past memory needs `prepare` first, or throws `HistoryMiss`. With a store, blits never prunes
|
|
17
|
+
* the tape: the host bounds it.
|
|
18
|
+
*/
|
|
19
|
+
store?: HistoryStore;
|
|
20
|
+
/** Rebuilds a cued voice's spec from the descriptor it was cued with, so the voice can be paged out. */
|
|
21
|
+
revive?(d: Described): VoiceSpec<any, any>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A voice as plain data: `kind` names what built it, and `data` survives `structuredClone`.
|
|
25
|
+
*
|
|
26
|
+
* @category mix
|
|
27
|
+
*/
|
|
28
|
+
export interface Described {
|
|
29
|
+
kind: string;
|
|
30
|
+
data: unknown;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Where a history `store` keeps what memory let go of. blits decides what is kept and when it is
|
|
34
|
+
* read back; the store decides where it lives.
|
|
35
|
+
*
|
|
36
|
+
* @category mix
|
|
37
|
+
*/
|
|
38
|
+
export interface HistoryStore {
|
|
39
|
+
/** Records leaving memory. One brought back by a seek may be paged again: keep one per key and `seq`. */
|
|
40
|
+
page(out: readonly Paged[]): void;
|
|
41
|
+
/**
|
|
42
|
+
* What a restore to mix time `t` needs, for every mix: from each stream, per voice and subject,
|
|
43
|
+
* the latest record at or before `t` and every record after it. Returning everything is fine.
|
|
44
|
+
*/
|
|
45
|
+
load(t: number): Promise<readonly Paged[]>;
|
|
46
|
+
/**
|
|
47
|
+
* A seek went back to frame `seq`: every record whose `seq` is greater is from a future the tape
|
|
48
|
+
* makes again. Forget them.
|
|
49
|
+
*/
|
|
50
|
+
cut(seq: number): void;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* What a history record holds: a voice's `controls`, a stateful record's `snap`, an `input`
|
|
54
|
+
* signal's reading, a subject's record when it `left` a voice, a motion subject's `released` run
|
|
55
|
+
* or older `stretch`, the `host` fields patches read, a whole `voice` that has left, or the
|
|
56
|
+
* transport's own: a `mark` announced on it, a change of its `pace`, and a `frame` it played.
|
|
57
|
+
*
|
|
58
|
+
* @category mix
|
|
59
|
+
*/
|
|
60
|
+
export type PagedStream = 'voice' | 'snap' | 'input' | 'left' | 'released' | 'stretch' | 'host' | 'controls' | 'mark' | 'pace' | 'frame';
|
|
61
|
+
/**
|
|
62
|
+
* One history record leaving memory, as plain data.
|
|
63
|
+
*
|
|
64
|
+
* @category mix
|
|
65
|
+
*/
|
|
66
|
+
export interface Paged {
|
|
67
|
+
/** The mix's `name`; '' for a mix made alone without one, and for the transport's own records. */
|
|
68
|
+
mix: string;
|
|
69
|
+
stream: PagedStream;
|
|
70
|
+
/** The voice's id, for every stream but `host` and the transport's own. */
|
|
71
|
+
voice?: number;
|
|
72
|
+
/** The subject's key, for a record of one subject; for a `mark`, the number it was announced as. */
|
|
73
|
+
subject?: string | number;
|
|
74
|
+
/** Mix time. */
|
|
75
|
+
at: number;
|
|
76
|
+
/**
|
|
77
|
+
* The transport's frame it was made in. Rate 0 holds mix time still across frames, and a fade
|
|
78
|
+
* given a time already passed is filed behind the frame that decided it, so a cut goes by this.
|
|
79
|
+
*/
|
|
80
|
+
seq: number;
|
|
81
|
+
data: unknown;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* One host call a mix recorded on its tape: `apply` makes it again, and `invert` does nothing,
|
|
85
|
+
* since a mix goes back by restoring what it kept rather than by undoing calls.
|
|
86
|
+
*
|
|
87
|
+
* @category mix
|
|
88
|
+
*/
|
|
89
|
+
export interface TapeOp {
|
|
90
|
+
apply(adapter: unknown): void;
|
|
91
|
+
invert(): TapeOp;
|
|
92
|
+
label?: string;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* One future a tape keeps at its current entry: the calls the host made after the mix last went
|
|
96
|
+
* back here, before it made new ones.
|
|
97
|
+
*
|
|
98
|
+
* @category mix
|
|
99
|
+
*/
|
|
100
|
+
export interface TapeBranch {
|
|
101
|
+
/** Its first entry's id, which `switchBranch` takes. */
|
|
102
|
+
id: number;
|
|
103
|
+
label: string;
|
|
104
|
+
timestamp: number;
|
|
105
|
+
/** How many entries run along it from here. */
|
|
106
|
+
length: number;
|
|
107
|
+
/** Whether the mix plays it on from here. */
|
|
108
|
+
current: boolean;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Where a mix keeps the host's calls, by host time, for `seek`. weasel-history's `History`
|
|
112
|
+
* (`@weasel-js/history`) has this shape. A host reads `branches` and calls `switchBranch` to pick
|
|
113
|
+
* which recorded future plays on; moving it any other way is the mix's job.
|
|
114
|
+
*
|
|
115
|
+
* @category mix
|
|
116
|
+
*/
|
|
117
|
+
export interface Tape {
|
|
118
|
+
recordEntry(ops: TapeOp[], label: string): void;
|
|
119
|
+
undoDepth(): number;
|
|
120
|
+
timestampAt(i: number): number | undefined;
|
|
121
|
+
depthAt(t: number): number;
|
|
122
|
+
goto(n: number): void;
|
|
123
|
+
redo(): void;
|
|
124
|
+
prune(t: number): void;
|
|
125
|
+
branches(): readonly TapeBranch[];
|
|
126
|
+
switchBranch(id: number): void;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Makes a mix's tape: weasel-history's `createHistory` is one. The mix passes the clock it stamps
|
|
130
|
+
* calls with, branching on, so a call made after going back keeps the old future as a branch, and
|
|
131
|
+
* coalescing off.
|
|
132
|
+
*
|
|
133
|
+
* @category mix
|
|
134
|
+
*/
|
|
135
|
+
export type TapeMaker = (adapter: unknown, opts: {
|
|
136
|
+
now: () => number;
|
|
137
|
+
branching: boolean;
|
|
138
|
+
coalesceWindowMs: number;
|
|
139
|
+
}) => Tape;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|