@msb235/blits 0.3.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 +314 -0
  2. package/README.md +3 -2
  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 +143 -122
  47. package/dist/lanes.js +236 -742
  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 -169
  55. package/dist/mixer.js +321 -1793
  56. package/dist/motion.d.ts +104 -19
  57. package/dist/motion.js +448 -180
  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 +261 -42
  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 +151 -46
  102. package/package.json +5 -2
package/dist/clone.js ADDED
@@ -0,0 +1,57 @@
1
+ const slow = Symbol('slow');
2
+ /**
3
+ * `structuredClone(v)`, made directly for what records mostly hold — numbers, strings, and plain
4
+ * objects and arrays of them two deep — with what `structuredClone` would give; anything else, or
5
+ * anything it would refuse, goes to it. A read back copies a record per voice and subject.
6
+ */
7
+ export function clone(v) {
8
+ const quick = copied(v, 2);
9
+ met.length = 0;
10
+ return quick === slow ? structuredClone(v) : quick;
11
+ }
12
+ /** The objects one `clone` has met: one met twice is shared, which `structuredClone` keeps. */
13
+ const met = [];
14
+ function copied(v, depth) {
15
+ if (typeof v === 'symbol' || typeof v === 'function')
16
+ return slow;
17
+ if (typeof v !== 'object' || v === null)
18
+ return v;
19
+ if (depth === 0 || met.includes(v))
20
+ return slow;
21
+ met.push(v);
22
+ if (Array.isArray(v)) {
23
+ if (Object.getPrototypeOf(v) !== Array.prototype)
24
+ return slow;
25
+ const out = [];
26
+ for (let i = 0; i < v.length; i++) {
27
+ if (!(i in v))
28
+ return slow;
29
+ const x = copied(v[i], depth - 1);
30
+ if (x === slow)
31
+ return slow;
32
+ out.push(x);
33
+ }
34
+ // A property besides the elements, which `structuredClone` keeps.
35
+ let keys = 0;
36
+ for (const _ in v)
37
+ if (++keys > v.length)
38
+ return slow;
39
+ return out;
40
+ }
41
+ const proto = Object.getPrototypeOf(v);
42
+ if (proto !== Object.prototype && proto !== null)
43
+ return slow;
44
+ const out = {};
45
+ for (const k in v) {
46
+ if (k === '__proto__')
47
+ return slow;
48
+ const x = copied(v[k], depth - 1);
49
+ if (x === slow)
50
+ return slow;
51
+ out[k] = x;
52
+ }
53
+ return out;
54
+ }
55
+ export function copy(v) {
56
+ return Array.isArray(v) ? [...v] : v;
57
+ }
@@ -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;