@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.
Files changed (102) hide show
  1. package/CHANGELOG.md +262 -0
  2. package/README.md +2 -1
  3. package/dist/bare.d.ts +29 -0
  4. package/dist/bare.js +105 -0
  5. package/dist/blend.d.ts +34 -0
  6. package/dist/blend.js +102 -0
  7. package/dist/book.d.ts +53 -0
  8. package/dist/book.js +218 -0
  9. package/dist/chain.d.ts +22 -0
  10. package/dist/chain.js +150 -0
  11. package/dist/channels.d.ts +2 -0
  12. package/dist/channels.js +3 -1
  13. package/dist/clock.d.ts +50 -3
  14. package/dist/clock.js +102 -9
  15. package/dist/clone.d.ts +7 -0
  16. package/dist/clone.js +57 -0
  17. package/dist/columns.d.ts +29 -0
  18. package/dist/columns.js +183 -0
  19. package/dist/crowd.d.ts +128 -0
  20. package/dist/crowd.js +354 -0
  21. package/dist/cue.d.ts +3 -0
  22. package/dist/cue.js +115 -0
  23. package/dist/due.d.ts +12 -0
  24. package/dist/due.js +93 -0
  25. package/dist/fade.d.ts +19 -0
  26. package/dist/fade.js +133 -0
  27. package/dist/fill.d.ts +42 -0
  28. package/dist/fill.js +381 -0
  29. package/dist/fold.d.ts +51 -0
  30. package/dist/fold.js +475 -0
  31. package/dist/gather.d.ts +24 -0
  32. package/dist/gather.js +186 -0
  33. package/dist/handle.d.ts +31 -0
  34. package/dist/handle.js +56 -0
  35. package/dist/held.d.ts +14 -0
  36. package/dist/held.js +117 -0
  37. package/dist/history.d.ts +22 -0
  38. package/dist/history.js +149 -0
  39. package/dist/hosts.d.ts +16 -0
  40. package/dist/hosts.js +178 -0
  41. package/dist/index.d.ts +1 -1
  42. package/dist/keyrows.d.ts +36 -0
  43. package/dist/keyrows.js +138 -0
  44. package/dist/lane.d.ts +210 -0
  45. package/dist/lane.js +187 -0
  46. package/dist/lanes.d.ts +121 -246
  47. package/dist/lanes.js +150 -1578
  48. package/dist/locus.d.ts +40 -0
  49. package/dist/locus.js +89 -0
  50. package/dist/marks.d.ts +12 -0
  51. package/dist/marks.js +80 -0
  52. package/dist/meet.d.ts +16 -0
  53. package/dist/meet.js +113 -0
  54. package/dist/mixer.d.ts +244 -196
  55. package/dist/mixer.js +242 -2220
  56. package/dist/motion.d.ts +66 -21
  57. package/dist/motion.js +325 -165
  58. package/dist/move.d.ts +9 -0
  59. package/dist/move.js +190 -0
  60. package/dist/named.d.ts +12 -0
  61. package/dist/named.js +39 -0
  62. package/dist/numbers.d.ts +18 -6
  63. package/dist/numbers.js +59 -31
  64. package/dist/owed.d.ts +24 -0
  65. package/dist/owed.js +51 -0
  66. package/dist/owner.d.ts +43 -0
  67. package/dist/owner.js +117 -0
  68. package/dist/pace.d.ts +40 -0
  69. package/dist/pace.js +113 -0
  70. package/dist/patch.d.ts +51 -10
  71. package/dist/patch.js +140 -45
  72. package/dist/place.d.ts +20 -0
  73. package/dist/place.js +196 -0
  74. package/dist/project.d.ts +4 -0
  75. package/dist/project.js +233 -0
  76. package/dist/pull.d.ts +5 -0
  77. package/dist/pull.js +175 -0
  78. package/dist/qualify.d.ts +31 -0
  79. package/dist/qualify.js +345 -0
  80. package/dist/relink.d.ts +25 -0
  81. package/dist/relink.js +59 -0
  82. package/dist/rows.d.ts +33 -0
  83. package/dist/rows.js +282 -0
  84. package/dist/sample.d.ts +21 -0
  85. package/dist/sample.js +232 -0
  86. package/dist/signals.d.ts +12 -9
  87. package/dist/signals.js +8 -5
  88. package/dist/store.d.ts +10 -3
  89. package/dist/store.js +44 -9
  90. package/dist/strays.d.ts +21 -0
  91. package/dist/strays.js +84 -0
  92. package/dist/tweened.d.ts +11 -0
  93. package/dist/tweened.js +48 -0
  94. package/dist/types.d.ts +217 -34
  95. package/dist/unreached.d.ts +5 -0
  96. package/dist/unreached.js +24 -0
  97. package/dist/voice.d.ts +240 -0
  98. package/dist/voice.js +322 -0
  99. package/dist/weigh.d.ts +26 -0
  100. package/dist/weigh.js +89 -0
  101. package/docs/schema.html +118 -40
  102. package/package.json +5 -2
@@ -0,0 +1,29 @@
1
+ import type { Lanes } from './lanes.js';
2
+ /** One channel `pull` writes: where in the kit, how many numbers a subject, and its rest. */
3
+ export interface Column {
4
+ key: string;
5
+ slot: number;
6
+ axes: number;
7
+ out: Float64Array;
8
+ /** The rest, clamped, or NaN for a channel with none. */
9
+ rest: Float64Array;
10
+ bounds: readonly [number, number] | undefined;
11
+ }
12
+ export declare function clampRun(out: Float64Array, at: number, n: number, [lo, hi]: readonly [number, number]): void;
13
+ /**
14
+ * For a `pull` of a list it read last time in the same order, with every voice on a lane, where
15
+ * `heads` holds each position's remembered chain head unless all are known current: what
16
+ * `prepare` and `writeLater` do for each subject from position `from`, while the subject reads
17
+ * from the lanes; returns the first position that does not, or the list's length.
18
+ */
19
+ export declare function pullRun<I, O>(lanes: Lanes<I, O>, slots: Int32Array, list: readonly I[], was: readonly I[], heads: readonly ({
20
+ version: number;
21
+ } | undefined)[] | null, from: number, columns: readonly Column[], now: number, version: number): number;
22
+ /**
23
+ * Queues a subject's values for each column at subject `n`'s places, for a `pull` that reads
24
+ * from the lanes while every voice is on one; `flush` writes what is queued, column by column. A
25
+ * channel no lane holds is at rest. Anything queued is written before the lanes fill again.
26
+ */
27
+ export declare function writeLater<I, O>(lanes: Lanes<I, O>, slot: number, columns: readonly Column[], n: number): void;
28
+ /** Writes every queued run into its columns: a channel's values as a block, a rest as a fill. */
29
+ export declare function flush<I, O>(lanes: Lanes<I, O>): void;
@@ -0,0 +1,183 @@
1
+ export function clampRun(out, at, n, [lo, hi]) {
2
+ for (let i = at; i < at + n; i++) {
3
+ const x = out[i];
4
+ out[i] = x < lo ? lo : x > hi ? hi : x;
5
+ }
6
+ }
7
+ /**
8
+ * For a `pull` of a list it read last time in the same order, with every voice on a lane, where
9
+ * `heads` holds each position's remembered chain head unless all are known current: what
10
+ * `prepare` and `writeLater` do for each subject from position `from`, while the subject reads
11
+ * from the lanes; returns the first position that does not, or the list's length.
12
+ */
13
+ export function pullRun(lanes, slots, list, was, heads, from, columns, now, version) {
14
+ const ready = lanes.filled(now, version) ? 1 /* Begin.READY */ : lanes.begin(now, version);
15
+ if (ready !== 1 /* Begin.READY */ || !lanes.whole)
16
+ return from;
17
+ const per = lanes.per;
18
+ const fills = lanes.fills;
19
+ const wide = lanes.wide;
20
+ const frame = lanes.frameProbes;
21
+ const subjects = lanes.subjects;
22
+ if (columns !== lanes.queuedFor) {
23
+ flush(lanes);
24
+ lanes.queuedFor = columns;
25
+ }
26
+ room(lanes, lanes.queued + list.length - from);
27
+ const qSlots = lanes.queueSlots;
28
+ const qRows = lanes.queueRows;
29
+ const qCounts = lanes.queueCounts;
30
+ let k = lanes.queued;
31
+ let n = from;
32
+ // The run being gathered: its first slot and row, and how far it has reached.
33
+ let first = -1;
34
+ let start = from;
35
+ for (; n < list.length; n++) {
36
+ const subject = list[n];
37
+ if (was[n] !== subject || (heads !== null && heads[n]?.version !== version))
38
+ break;
39
+ const slot = slots[n];
40
+ const o = slot * 6 /* Per.SLOT */;
41
+ if (slot < 0 ||
42
+ per[o + 0 /* Per.FILLED */] !== fills ||
43
+ per[o + 2 /* Per.IDLE */] !== 0 ||
44
+ per[o + 1 /* Per.SEEN */] < wide ||
45
+ lanes.owes(slot))
46
+ break;
47
+ if (!(per[o + 3 /* Per.LANE_PROBE */] > frame ||
48
+ per[o + 4 /* Per.GENERAL_PROBE */] > frame))
49
+ lanes.distinct++;
50
+ per[o + 3 /* Per.LANE_PROBE */] = ++lanes.probes;
51
+ per[o + 5 /* Per.LANE_FILL */] = fills;
52
+ subjects[slot] = subject;
53
+ if (slot !== first + (n - start)) {
54
+ if (first >= 0) {
55
+ qSlots[k] = first;
56
+ qRows[k] = start;
57
+ qCounts[k] = n - start;
58
+ k++;
59
+ }
60
+ first = slot;
61
+ start = n;
62
+ }
63
+ }
64
+ if (first >= 0) {
65
+ qSlots[k] = first;
66
+ qRows[k] = start;
67
+ qCounts[k] = n - start;
68
+ k++;
69
+ }
70
+ lanes.queued = k;
71
+ if (n > from)
72
+ lanes.holding = true;
73
+ return n;
74
+ }
75
+ /**
76
+ * Queues a subject's values for each column at subject `n`'s places, for a `pull` that reads
77
+ * from the lanes while every voice is on one; `flush` writes what is queued, column by column. A
78
+ * channel no lane holds is at rest. Anything queued is written before the lanes fill again.
79
+ */
80
+ export function writeLater(lanes, slot, columns, n) {
81
+ queue(lanes, columns, slot, n, 1);
82
+ }
83
+ /** Queues `count` subjects from lane slot `slot` for rows from `row`, one apart in both. */
84
+ function queue(lanes, columns, slot, row, count) {
85
+ if (columns !== lanes.queuedFor) {
86
+ flush(lanes);
87
+ lanes.queuedFor = columns;
88
+ }
89
+ const k = lanes.queued;
90
+ if (k > 0) {
91
+ const last = k - 1;
92
+ const had = lanes.queueCounts[last];
93
+ if (slot === lanes.queueSlots[last] + had &&
94
+ row === lanes.queueRows[last] + had) {
95
+ lanes.queueCounts[last] = had + count;
96
+ return;
97
+ }
98
+ }
99
+ room(lanes, k + 1);
100
+ lanes.queueSlots[k] = slot;
101
+ lanes.queueRows[k] = row;
102
+ lanes.queueCounts[k] = count;
103
+ lanes.queued = k + 1;
104
+ }
105
+ /** Makes room in the queue for `runs` runs. */
106
+ function room(lanes, runs) {
107
+ if (runs <= lanes.queueSlots.length)
108
+ return;
109
+ const size = Math.max(64, runs, lanes.queueSlots.length * 2);
110
+ lanes.queueSlots = grown(lanes.queueSlots, size);
111
+ lanes.queueRows = grown(lanes.queueRows, size);
112
+ lanes.queueCounts = grown(lanes.queueCounts, size);
113
+ }
114
+ function grown(a, size) {
115
+ const b = new Int32Array(size);
116
+ b.set(a);
117
+ return b;
118
+ }
119
+ /** Writes every queued run into its columns: a channel's values as a block, a rest as a fill. */
120
+ export function flush(lanes) {
121
+ const runs = lanes.queued;
122
+ const columns = lanes.queuedFor;
123
+ if (runs === 0 || columns === null)
124
+ return;
125
+ lanes.queued = 0;
126
+ for (let k = 0; k < columns.length; k++) {
127
+ const c = columns[k];
128
+ const ch = lanes.bySlot[c.slot];
129
+ if (ch === undefined)
130
+ rests(lanes, c, runs);
131
+ else
132
+ copies(lanes, c, ch.values, runs);
133
+ if (c.bounds !== undefined)
134
+ for (let r = 0; r < runs; r++)
135
+ clampRun(c.out, lanes.queueRows[r] * c.axes, lanes.queueCounts[r] * c.axes, c.bounds);
136
+ }
137
+ }
138
+ // A typed array's `fill` and `set` cost more to call than a short run costs to copy.
139
+ /** Writes a column's rest into every queued run's rows. */
140
+ function rests(lanes, c, runs) {
141
+ const rows = lanes.queueRows;
142
+ const counts = lanes.queueCounts;
143
+ const out = c.out;
144
+ const axes = c.axes;
145
+ if (axes === 1) {
146
+ const v = c.rest[0];
147
+ for (let r = 0; r < runs; r++) {
148
+ const at = rows[r];
149
+ const end = at + counts[r];
150
+ if (end - at > 64)
151
+ out.fill(v, at, end);
152
+ else
153
+ for (let i = at; i < end; i++)
154
+ out[i] = v;
155
+ }
156
+ return;
157
+ }
158
+ for (let r = 0; r < runs; r++) {
159
+ const at = rows[r] * axes;
160
+ const end = at + counts[r] * axes;
161
+ for (let i = at; i < end; i += axes)
162
+ for (let a = 0; a < axes; a++)
163
+ out[i + a] = c.rest[a];
164
+ }
165
+ }
166
+ /** Copies a laned channel's values for every queued run into its rows. */
167
+ function copies(lanes, c, values, runs) {
168
+ const slots = lanes.queueSlots;
169
+ const rows = lanes.queueRows;
170
+ const counts = lanes.queueCounts;
171
+ const out = c.out;
172
+ const axes = c.axes;
173
+ for (let r = 0; r < runs; r++) {
174
+ const at = rows[r] * axes;
175
+ const base = slots[r] * axes;
176
+ const span = counts[r] * axes;
177
+ if (span > 64)
178
+ out.set(values.subarray(base, base + span), at);
179
+ else
180
+ for (let i = 0; i < span; i++)
181
+ out[at + i] = values[base + i];
182
+ }
183
+ }
@@ -0,0 +1,128 @@
1
+ import type { Curve } from './easing.js';
2
+ import { KeyRows } from './keyrows.js';
3
+ import { type Laned, type Positions } from './lane.js';
4
+ import type { Lanes } from './lanes.js';
5
+ import { type Motions } from './motion.js';
6
+ import type { Scratch } from './patch.js';
7
+ import type { Subject, Voice } from './voice.js';
8
+ /**
9
+ * Whether a laned voice belongs in a crowd: one naming a single subject, which shares nothing
10
+ * across subjects for a lane to save. A motion voice writes one channel; a keys or fn voice may
11
+ * write several, and joins the crowd of voices writing those same channels.
12
+ */
13
+ export declare function crowdable<I, O>(v: Voice<I, O>): boolean;
14
+ /** The law of a row that has none, a keys or fn voice's. */
15
+ export declare const none: Float64Array<ArrayBuffer>;
16
+ /** Per row of a crowd's `hot`: what the fill reads of the row's voice and its current stretch. */
17
+ export declare const enum Hot {
18
+ FLAGS = 0,
19
+ NOW = 1,
20
+ ELAPSED = 2,
21
+ RATE = 3,
22
+ /** The voice's weight, held to 0..1. */
23
+ WEIGHT = 4,
24
+ SEEKS = 5,
25
+ EPOCH = 6,
26
+ ID = 7,
27
+ AT = 8,
28
+ MS = 9,
29
+ /** `x0`, `v0` and `to`, `axes` numbers each. */
30
+ X0 = 10
31
+ }
32
+ /** A crowd row's flags, at `Hot.FLAGS`. */
33
+ export declare const enum Flag {
34
+ /** The voice is playing: live, held or fading. */
35
+ PLAYING = 1,
36
+ /** Its weight and clock are `hot`'s: no fade in or out, rate ramp, subject ramp or kept state. */
37
+ FAST = 2,
38
+ /** Its stretch changed since `hot` copied it. */
39
+ STALE = 4,
40
+ /** `hot` holds its stretch, with nothing pending and no earlier stretch kept. */
41
+ BARE = 8,
42
+ /** A probe has met its subject, so its per-subject numbers are set. */
43
+ PLACED = 16,
44
+ /** Its samples fold as the channel's axes, a number for a number and an array for an array. */
45
+ FOLDS = 32,
46
+ /** Something on the voice changed since `hot` copied it. */
47
+ VOICE = 64,
48
+ /** The row's voice is a motion voice; otherwise its patch is keys or a stateless fn. */
49
+ MOTION = 128,
50
+ /** The voice holds before or after, so its clock is read from the voice. */
51
+ HOLDS = 256,
52
+ /** A keys row whose stops `keys` holds, at `Hot.AT`, with its duration at `Hot.X0` and passes at `Hot.MS`. */
53
+ FLAT = 512,
54
+ /** The row's patch is a tween. */
55
+ TWEEN = 1024
56
+ }
57
+ /** Whether a row's flags let `bareRows` take it. */
58
+ export declare function bare(f: number): boolean;
59
+ /**
60
+ * Every laned voice on one channel that reaches a single subject, a row each, in voice order: the
61
+ * per-subject numbers a lane keeps, and a copy of what a fill reads of the voice and, for a motion
62
+ * voice, its patch, so a fill over 100k of them reads a few flat arrays instead of 100k voices,
63
+ * lanes and patch buffers. Each copy is written when its source changes: the voice's by
64
+ * `voiceChanged`, the stretch by `stretchChanged`.
65
+ */
66
+ export declare class Crowd<I, O> implements Positions<I, O> {
67
+ readonly chans: Laned[];
68
+ readonly axes: number;
69
+ /** The subject number at each row. */
70
+ list: number[];
71
+ /** `STRIDE` numbers per row, laid out as a lane's. */
72
+ data: Float64Array<ArrayBuffer>;
73
+ hot: Float64Array<ArrayBuffer>;
74
+ readonly stride: number;
75
+ samples: Float64Array<ArrayBuffer>;
76
+ deltas: (Record<string, unknown> | null)[];
77
+ records: (Subject<unknown> | undefined)[];
78
+ voices: Voice<I, O>[];
79
+ motions: (Motions<I> | undefined)[];
80
+ eases: (Curve | undefined)[];
81
+ /** Each row's patch law, one array shared by every row whose law is the same. */
82
+ laws: Float64Array[];
83
+ readonly rowOf: Map<number, number>;
84
+ idle: boolean;
85
+ readonly line: {
86
+ stop: number;
87
+ start: number;
88
+ };
89
+ /** Where the fill now under way has reached. */
90
+ cursor: number;
91
+ /** Rows whose voices have left, kept in place until the crowds are next rebuilt. */
92
+ dead: number;
93
+ /** Whether some row is `Flag.STALE`, for `freshen` to copy before the fill's loop. */
94
+ stale: boolean;
95
+ readonly xs: Float64Array;
96
+ readonly vs: Float64Array;
97
+ /** What a keys row reads into, folded before the next row reads. */
98
+ readonly delta: Record<string, unknown>;
99
+ /** The stops of its `Flag.FLAT` keys rows. */
100
+ keys: KeyRows;
101
+ readonly scratch: Scratch;
102
+ /** The last fill that ran the crowd's rows, and its `now`, which a `Sampled.BARE` row was sampled at. */
103
+ bareFill: number;
104
+ bareNow: number;
105
+ constructor(chans: Laned[], axes: number);
106
+ voiceAt(p: number): Voice<I, O>;
107
+ fix(p: number): void;
108
+ numbered(): void;
109
+ /** Marks row `p` to copy its stretch from its patch again before the next fill reads it. */
110
+ restale(p: number, flags: number): void;
111
+ /** Samples of another width than the channel's, which `samples` cannot hold, by row. */
112
+ odd: Map<number, Float64Array> | null;
113
+ keep(p: number, xs: Float64Array, n: number): void;
114
+ kept(p: number, xs: Float64Array): void;
115
+ motionAt(p: number): Motions<I> | undefined;
116
+ get size(): number;
117
+ /** Makes room for `rows` rows. */
118
+ reserve(rows: number): void;
119
+ }
120
+ /** Runs every crowd's rows whose voices come before voice `id`, from where each crowd reached. */
121
+ export declare function crowdsUpTo<I, O>(lanes: Lanes<I, O>, id: number): void;
122
+ /** A keys or fn row's contribution, as `one` makes a lane position's. */
123
+ export declare function row<I, O>(this: Lanes<I, O>, c: Crowd<I, O>, p: number, voice: Voice<I, O>, slot: number, elapsed: number, delay: number, w: number): void;
124
+ /**
125
+ * Copies stale motion rows' stretches as a fill begins: inside `runCrowd`, once voices came and
126
+ * went, TurboFan spent its inlining on the copy and stopped inlining every row's ease.
127
+ */
128
+ export declare function freshen<I, O>(c: Crowd<I, O>): void;
package/dist/crowd.js ADDED
@@ -0,0 +1,354 @@
1
+ import { bareRows, clock, unbare } from './bare.js';
2
+ import { clampWeight, passAt, phaseAt, weighed } from './clock.js';
3
+ import { KeyRows } from './keyrows.js';
4
+ import { held, SPARSE } from './lane.js';
5
+ import { closed } from './motion.js';
6
+ import { move } from './sample.js';
7
+ /**
8
+ * Whether a laned voice belongs in a crowd: one naming a single subject, which shares nothing
9
+ * across subjects for a lane to save. A motion voice writes one channel; a keys or fn voice may
10
+ * write several, and joins the crowd of voices writing those same channels.
11
+ */
12
+ export function crowdable(v) {
13
+ return (v.named !== null &&
14
+ v.named.size === 1 &&
15
+ v.spec.locus === undefined &&
16
+ (v.slots.length === 1 || v.motion === undefined));
17
+ }
18
+ /** The law of a row that has none, a keys or fn voice's. */
19
+ export const none = new Float64Array(0);
20
+ /** Whether a row's flags let `bareRows` take it. */
21
+ export function bare(f) {
22
+ return (f & 2043 /* Bare.MASK */) === 1211 /* Bare.WANT */;
23
+ }
24
+ /**
25
+ * Every laned voice on one channel that reaches a single subject, a row each, in voice order: the
26
+ * per-subject numbers a lane keeps, and a copy of what a fill reads of the voice and, for a motion
27
+ * voice, its patch, so a fill over 100k of them reads a few flat arrays instead of 100k voices,
28
+ * lanes and patch buffers. Each copy is written when its source changes: the voice's by
29
+ * `voiceChanged`, the stretch by `stretchChanged`.
30
+ */
31
+ export class Crowd {
32
+ chans;
33
+ axes;
34
+ /** The subject number at each row. */
35
+ list = [];
36
+ /** `STRIDE` numbers per row, laid out as a lane's. */
37
+ data = new Float64Array(0);
38
+ hot = new Float64Array(0);
39
+ stride;
40
+ samples = new Float64Array(0);
41
+ deltas = [];
42
+ records = [];
43
+ voices = [];
44
+ motions = [];
45
+ eases = [];
46
+ /** Each row's patch law, one array shared by every row whose law is the same. */
47
+ laws = [];
48
+ rowOf = new Map();
49
+ idle = false;
50
+ line = SPARSE.other;
51
+ /** Where the fill now under way has reached. */
52
+ cursor = 0;
53
+ /** Rows whose voices have left, kept in place until the crowds are next rebuilt. */
54
+ dead = 0;
55
+ /** Whether some row is `Flag.STALE`, for `freshen` to copy before the fill's loop. */
56
+ stale = false;
57
+ xs;
58
+ vs;
59
+ /** What a keys row reads into, folded before the next row reads. */
60
+ delta = {};
61
+ /** The stops of its `Flag.FLAT` keys rows. */
62
+ keys = new KeyRows();
63
+ scratch = [];
64
+ /** The last fill that ran the crowd's rows, and its `now`, which a `Sampled.BARE` row was sampled at. */
65
+ bareFill = -1;
66
+ bareNow = Number.NaN;
67
+ constructor(chans, axes) {
68
+ this.chans = chans;
69
+ this.axes = axes;
70
+ this.stride = 10 /* Hot.X0 */ + 3 * axes;
71
+ this.xs = new Float64Array(axes);
72
+ this.vs = new Float64Array(axes);
73
+ }
74
+ voiceAt(p) {
75
+ return this.voices[p];
76
+ }
77
+ fix(p) {
78
+ unbare(this, p);
79
+ }
80
+ numbered() { }
81
+ /** Marks row `p` to copy its stretch from its patch again before the next fill reads it. */
82
+ restale(p, flags) {
83
+ this.hot[p * this.stride + 0 /* Hot.FLAGS */] = flags | 4 /* Flag.STALE */;
84
+ this.stale = true;
85
+ }
86
+ /** Samples of another width than the channel's, which `samples` cannot hold, by row. */
87
+ odd = null;
88
+ keep(p, xs, n) {
89
+ if (n === this.axes) {
90
+ for (let a = 0; a < n; a++)
91
+ this.samples[p * n + a] = xs[a];
92
+ this.odd?.delete(p);
93
+ return;
94
+ }
95
+ this.odd ??= new Map();
96
+ this.odd.set(p, xs.slice(0, n));
97
+ }
98
+ kept(p, xs) {
99
+ const odd = this.odd?.get(p);
100
+ if (odd !== undefined) {
101
+ xs.set(odd);
102
+ return;
103
+ }
104
+ const n = this.axes;
105
+ for (let a = 0; a < n; a++)
106
+ xs[a] = this.samples[p * n + a];
107
+ }
108
+ motionAt(p) {
109
+ return this.motions[p];
110
+ }
111
+ get size() {
112
+ return this.list.length;
113
+ }
114
+ /** Makes room for `rows` rows. */
115
+ reserve(rows) {
116
+ if (rows * 8 /* Row.STRIDE */ <= this.data.length)
117
+ return;
118
+ const cap = Math.max(rows, (this.data.length / 8 /* Row.STRIDE */) * 2, 16);
119
+ const data = new Float64Array(cap * 8 /* Row.STRIDE */);
120
+ data.set(this.data);
121
+ this.data = data;
122
+ const hot = new Float64Array(cap * this.stride);
123
+ hot.set(this.hot);
124
+ this.hot = hot;
125
+ const samples = new Float64Array(cap * this.axes);
126
+ samples.set(this.samples);
127
+ this.samples = samples;
128
+ }
129
+ }
130
+ /** Runs every crowd's rows whose voices come before voice `id`, from where each crowd reached. */
131
+ export function crowdsUpTo(lanes, id) {
132
+ if (!lanes.overlap) {
133
+ for (const c of lanes.crowds)
134
+ if (!c.idle && c.cursor < c.size)
135
+ runCrowd(lanes, c, id);
136
+ return;
137
+ }
138
+ // Crowds sharing a channel take turns in voice order, so a subject's rows fold as one crowd's do.
139
+ for (;;) {
140
+ let first;
141
+ let a = id;
142
+ let b = id;
143
+ for (const c of lanes.crowds) {
144
+ if (c.idle || c.cursor >= c.size)
145
+ continue;
146
+ const next = c.hot[c.cursor * c.stride + 7 /* Hot.ID */];
147
+ if (next < a) {
148
+ b = a;
149
+ a = next;
150
+ first = c;
151
+ }
152
+ else if (next < b)
153
+ b = next;
154
+ }
155
+ if (first === undefined)
156
+ return;
157
+ runCrowd(lanes, first, b);
158
+ }
159
+ }
160
+ /** A crowd's rows from its cursor up to voice `id`, bare tweens in `bareRows` and the rest here. */
161
+ function runCrowd(lanes, c, id) {
162
+ const list = c.list;
163
+ const hot = c.hot;
164
+ let p = c.cursor;
165
+ for (;;) {
166
+ p = bareRows(lanes, c, p, id);
167
+ if (p >= list.length || hot[p * c.stride + 7 /* Hot.ID */] >= id)
168
+ break;
169
+ p = rows(lanes, c, p, id);
170
+ }
171
+ c.cursor = p;
172
+ }
173
+ /**
174
+ * A crowd's rows from `p` up to voice `id`, until one `bareRows` takes after the first: what
175
+ * `runMotion` does for a lane's subjects, reading each row's voice and stretch from `hot`, and
176
+ * through `move` for whatever `hot` cannot answer, as `runMotion` does. Returns where it stopped.
177
+ */
178
+ function rows(lanes, c, first, id) {
179
+ const data = c.data;
180
+ const hot = c.hot;
181
+ const H = c.stride;
182
+ const per = lanes.per;
183
+ const list = c.list;
184
+ const ch = c.chans[0];
185
+ const n = c.axes;
186
+ const fills = lanes.fills;
187
+ const from = lanes.frameProbes;
188
+ const probed = lanes.probes !== from;
189
+ const now = lanes.now;
190
+ const xs = c.xs;
191
+ const vs = c.vs;
192
+ const samples = c.samples;
193
+ let p = first;
194
+ for (; p < list.length; p++) {
195
+ const h = p * H;
196
+ if (hot[h + 7 /* Hot.ID */] >= id)
197
+ break;
198
+ let f = hot[h + 0 /* Hot.FLAGS */];
199
+ if (p > first && bare(f))
200
+ break;
201
+ if (data[p * 8 /* Row.STRIDE */ + 5 /* Row.SAMPLED */] === -2 /* Sampled.BARE */)
202
+ unbare(c, p);
203
+ if ((f & 64 /* Flag.VOICE */) !== 0)
204
+ f = copyVoice(c, p);
205
+ if ((f & 1 /* Flag.PLAYING */) === 0 || (f & 16 /* Flag.PLACED */) === 0)
206
+ continue;
207
+ const slot = list[p];
208
+ const o = p * 8 /* Row.STRIDE */;
209
+ const q = slot * 6 /* Per.SLOT */;
210
+ if (per[q + 5 /* Per.LANE_FILL */] === fills - 1)
211
+ data[o + 3 /* Row.PROBED */] = data[o + 2 /* Row.WEIGHT */];
212
+ const delay = data[o + 0 /* Row.DELAY */];
213
+ const fast = (f & 2 /* Flag.FAST */) !== 0;
214
+ const voice = c.voices[p];
215
+ const elapsedNow = fast ? clock(hot, h, now) : voice.elapsedAt(now);
216
+ let elapsed = elapsedNow - delay;
217
+ if ((f & 256 /* Flag.HOLDS */) !== 0)
218
+ elapsed = held(voice, elapsed);
219
+ if (!(elapsed >= 0)) {
220
+ data[o + 2 /* Row.WEIGHT */] = 0;
221
+ continue;
222
+ }
223
+ let w;
224
+ if (fast)
225
+ w = hot[h + 4 /* Hot.WEIGHT */];
226
+ else if (typeof voice.spec.weight === 'function') {
227
+ w = lanes.signalled(voice, slot, c.records[p], elapsed, passAt(elapsed, voice.duration, voice.passes), lanes.host.envelope(voice, data[o + 1 /* Row.SINCE */]));
228
+ if (Number.isNaN(w)) {
229
+ data[o + 2 /* Row.WEIGHT */] = 0;
230
+ continue;
231
+ }
232
+ }
233
+ else
234
+ w = weighed(voice.weight, lanes.host.envelope(voice, data[o + 1 /* Row.SINCE */]), lanes.parting(voice, slot));
235
+ data[o + 2 /* Row.WEIGHT */] = w;
236
+ if ((f & 512 /* Flag.FLAT */) !== 0) {
237
+ if (w > 0)
238
+ c.keys.fold(hot[h + 8 /* Hot.AT */], phaseAt(elapsed, hot[h + 10 /* Hot.X0 */], hot[h + 9 /* Hot.MS */]), c.chans, slot, w);
239
+ continue;
240
+ }
241
+ if ((f & 128 /* Flag.MOTION */) === 0) {
242
+ lanes.row(c, p, voice, slot, elapsed, delay, w);
243
+ continue;
244
+ }
245
+ if (data[o + 7 /* Row.MET */] === 0) {
246
+ if (Number.isNaN(c.records[p].probed)) {
247
+ lanes.late.push(slot);
248
+ continue;
249
+ }
250
+ data[o + 7 /* Row.MET */] = 1;
251
+ }
252
+ const ms = data[o + 4 /* Row.MSLOT */];
253
+ if (ms < 0 ||
254
+ (f & 8 /* Flag.BARE */) === 0 ||
255
+ (probed &&
256
+ (per[q + 3 /* Per.LANE_PROBE */] > from ||
257
+ per[q + 4 /* Per.GENERAL_PROBE */] > from))) {
258
+ const run = c.motions[p];
259
+ move(lanes, c, run, p, slot, c.records[p], elapsed, delay, w);
260
+ // The first sample numbered the subject in the patch: copy its stretch from the next fill.
261
+ if (ms < 0)
262
+ c.restale(p, hot[h + 0 /* Hot.FLAGS */]);
263
+ continue;
264
+ }
265
+ closed(c.laws[p], c.eases[p], n, hot[h + 8 /* Hot.AT */], hot[h + 9 /* Hot.MS */], hot, h + 10 /* Hot.X0 */, hot, h + 10 /* Hot.X0 */ + n, hot, h + 10 /* Hot.X0 */ + 2 * n, elapsed, xs, vs);
266
+ for (let a = 0; a < n; a++)
267
+ samples[p * n + a] = xs[a];
268
+ if (c.deltas[p] !== null)
269
+ c.deltas[p] = null;
270
+ data[o + 5 /* Row.SAMPLED */] = fills;
271
+ data[o + 6 /* Row.SEEKS */] = hot[h + 5 /* Hot.SEEKS */];
272
+ if (w <= 0)
273
+ continue;
274
+ if ((f & 32 /* Flag.FOLDS */) !== 0)
275
+ lanes.foldRun(ch, slot, xs, w);
276
+ else
277
+ lanes.foldInto(ch, slot, c.motions[p].value(ms, xs), w);
278
+ }
279
+ return p;
280
+ }
281
+ /** A keys or fn row's contribution, as `one` makes a lane position's. */
282
+ export function row(c, p, voice, slot, elapsed, delay, w) {
283
+ const duration = voice.duration;
284
+ const phase = phaseAt(elapsed, duration, voice.passes);
285
+ if (voice.built !== null) {
286
+ if (w > 0)
287
+ this.foldKeys(voice, c.chans, phase, slot, w);
288
+ return;
289
+ }
290
+ const rec = c.records[p];
291
+ if (Number.isNaN(rec.probed)) {
292
+ this.late.push(slot);
293
+ return;
294
+ }
295
+ this.call(voice, c.chans, rec, slot, elapsed, phase, passAt(elapsed, duration, voice.passes), delay, w);
296
+ }
297
+ /** Copies what a fill reads of a crowd row's voice into `hot`; returns the row's flags. */
298
+ function copyVoice(c, p) {
299
+ const v = c.voices[p];
300
+ const h = p * c.stride;
301
+ let f = c.hot[h + 0 /* Hot.FLAGS */] & ~(64 /* Flag.VOICE */ | 1 /* Flag.PLAYING */ | 2 /* Flag.FAST */ | 256 /* Flag.HOLDS */);
302
+ if (v.state === 'live' || v.state === 'held' || v.state === 'fading')
303
+ f |= 1 /* Flag.PLAYING */;
304
+ if (v.holdsBefore || v.holdsAfter)
305
+ f |= 256 /* Flag.HOLDS */;
306
+ if (v.ramp === null &&
307
+ v.owner === null &&
308
+ !((v.fade.in ?? 0) > 0) &&
309
+ v.out === null &&
310
+ v.parts === null &&
311
+ !v.keeping &&
312
+ typeof v.spec.weight !== 'function')
313
+ f |= 2 /* Flag.FAST */;
314
+ c.hot[h + 1 /* Hot.NOW */] = v.anchorNow;
315
+ c.hot[h + 2 /* Hot.ELAPSED */] = v.anchorElapsed;
316
+ c.hot[h + 3 /* Hot.RATE */] = v.rate;
317
+ c.hot[h + 4 /* Hot.WEIGHT */] = clampWeight(v.weight);
318
+ c.hot[h + 5 /* Hot.SEEKS */] = v.seeks;
319
+ c.hot[h + 0 /* Hot.FLAGS */] = f;
320
+ return f;
321
+ }
322
+ /**
323
+ * Copies stale motion rows' stretches as a fill begins: inside `runCrowd`, once voices came and
324
+ * went, TurboFan spent its inlining on the copy and stopped inlining every row's ease.
325
+ */
326
+ export function freshen(c) {
327
+ c.stale = false;
328
+ const hot = c.hot;
329
+ const H = c.stride;
330
+ for (let p = 0; p < c.list.length; p++) {
331
+ const f = hot[p * H + 0 /* Hot.FLAGS */];
332
+ if ((f & 4 /* Flag.STALE */) === 0 || (f & 128 /* Flag.MOTION */) === 0)
333
+ continue;
334
+ // A row with no stretch yet is marked again once its first sample numbers it.
335
+ const ms = c.data[p * 8 /* Row.STRIDE */ + 4 /* Row.MSLOT */];
336
+ if (ms >= 0)
337
+ copyStretch(c, p, ms);
338
+ }
339
+ }
340
+ /** Copies a crowd row's current stretch from its patch into `hot`; returns the row's flags. */
341
+ function copyStretch(c, p, ms) {
342
+ unbare(c, p);
343
+ const run = c.motions[p];
344
+ const h = p * c.stride;
345
+ let f = c.hot[h + 0 /* Hot.FLAGS */] & ~(4 /* Flag.STALE */ | 8 /* Flag.BARE */ | 32 /* Flag.FOLDS */);
346
+ const ch = c.chans[0];
347
+ if (run.n === c.axes && run.stretchInto(ms, c.hot, h + 8 /* Hot.AT */)) {
348
+ f |= 8 /* Flag.BARE */;
349
+ if (run.scalar(ms) === ch.scalar)
350
+ f |= 32 /* Flag.FOLDS */;
351
+ }
352
+ c.hot[h + 0 /* Hot.FLAGS */] = f;
353
+ return f;
354
+ }