@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
@@ -0,0 +1,40 @@
1
+ import type { Mixer } from './mixer.js';
2
+ import type { Subject, Voice } from './voice.js';
3
+ /**
4
+ * What a fold with a locus in play gathers, kept from one fold to the next: the influences in
5
+ * voice order, each a voice's own or a locus's first member; every locus member; and per channel
6
+ * slot, a locus's folding value, the weight taken into it and the array it owns.
7
+ */
8
+ export interface LocusScratch<I, O> {
9
+ n: number;
10
+ voices: (Voice<I, O> | null)[];
11
+ helds: (Subject<unknown> | null)[];
12
+ deltas: (Record<string, unknown> | null)[];
13
+ weights: number[];
14
+ /** Per influence, -1 for a voice's own, or which of `names` it is. */
15
+ groups: number[];
16
+ names: string[];
17
+ m: number;
18
+ mVoices: (Voice<I, O> | null)[];
19
+ mDeltas: (Record<string, unknown> | null)[];
20
+ mWeights: number[];
21
+ mGroups: number[];
22
+ values: unknown[];
23
+ taken: Float64Array;
24
+ met: Uint8Array;
25
+ touched: number[];
26
+ owned: (number[] | undefined)[];
27
+ }
28
+ export declare function locusScratch<I, O>(channels: number): LocusScratch<I, O>;
29
+ /**
30
+ * `value` copied into the array `k` owns for the slot, as long as the channel's rest, where a
31
+ * shorter value leaves the rest's own fill: what the channel's fold and lerp read past its end.
32
+ */
33
+ export declare function own<I, O>(k: LocusScratch<I, O>, slot: number, value: number[], rest: number[]): number[];
34
+ /**
35
+ * Folds one locus's members into one influence through each channel's own `lerp`: its value per
36
+ * channel goes in `k.values`, the channels in `k.touched` in the order first met, and its weight
37
+ * is returned. A channel folding by scale takes its value in an array of `k`'s own, lerped in
38
+ * place, since the fold only reads it.
39
+ */
40
+ export declare function foldLocus<I, O>(this: Mixer<I, O>, k: LocusScratch<I, O>, group: number): number;
package/dist/locus.js ADDED
@@ -0,0 +1,89 @@
1
+ import { copy } from './clone.js';
2
+ export function locusScratch(channels) {
3
+ return {
4
+ n: 0,
5
+ voices: [],
6
+ helds: [],
7
+ deltas: [],
8
+ weights: [],
9
+ groups: [],
10
+ names: [],
11
+ m: 0,
12
+ mVoices: [],
13
+ mDeltas: [],
14
+ mWeights: [],
15
+ mGroups: [],
16
+ values: new Array(channels).fill(undefined),
17
+ taken: new Float64Array(channels),
18
+ met: new Uint8Array(channels),
19
+ touched: [],
20
+ owned: new Array(channels).fill(undefined),
21
+ };
22
+ }
23
+ /**
24
+ * `value` copied into the array `k` owns for the slot, as long as the channel's rest, where a
25
+ * shorter value leaves the rest's own fill: what the channel's fold and lerp read past its end.
26
+ */
27
+ export function own(k, slot, value, rest) {
28
+ let out = k.owned[slot];
29
+ if (out === undefined) {
30
+ out = rest.slice();
31
+ k.owned[slot] = out;
32
+ }
33
+ for (let i = 0; i < out.length; i++)
34
+ out[i] = i < value.length ? value[i] : rest[i];
35
+ return out;
36
+ }
37
+ /**
38
+ * Folds one locus's members into one influence through each channel's own `lerp`: its value per
39
+ * channel goes in `k.values`, the channels in `k.touched` in the order first met, and its weight
40
+ * is returned. A channel folding by scale takes its value in an array of `k`'s own, lerped in
41
+ * place, since the fold only reads it.
42
+ */
43
+ export function foldLocus(k, group) {
44
+ k.touched.length = 0;
45
+ let sum = 0;
46
+ for (let m = 0; m < k.m; m++)
47
+ if (k.mGroups[m] === group)
48
+ sum += k.mWeights[m];
49
+ if (sum <= 0)
50
+ return 0;
51
+ const taken = k.taken;
52
+ for (let m = 0; m < k.m; m++) {
53
+ if (k.mGroups[m] !== group)
54
+ continue;
55
+ const weight = k.mWeights[m];
56
+ if (weight <= 0)
57
+ continue;
58
+ const delta = k.mDeltas[m];
59
+ const slots = k.mVoices[m].slots;
60
+ for (let i = 0; i < slots.length; i++) {
61
+ const slot = slots[i];
62
+ const value = delta[this.names[slot]];
63
+ if (value === undefined)
64
+ continue;
65
+ const channel = this.channels[slot];
66
+ if (k.met[slot] === 0) {
67
+ k.met[slot] = 1;
68
+ k.touched.push(slot);
69
+ taken[slot] = weight;
70
+ k.values[slot] =
71
+ this.lerpsInto[slot] !== undefined && channel.scale && Array.isArray(value)
72
+ ? own(k, slot, value, channel.rest)
73
+ : copy(value);
74
+ continue;
75
+ }
76
+ const total = taken[slot] + weight;
77
+ const was = k.values[slot];
78
+ const lerp = this.lerpsInto[slot];
79
+ k.values[slot] =
80
+ lerp !== undefined && was === k.owned[slot]
81
+ ? lerp(was, was, value, weight / total)
82
+ : channel.lerp(was, value, weight / total);
83
+ taken[slot] = total;
84
+ }
85
+ }
86
+ for (let t = 0; t < k.touched.length; t++)
87
+ k.met[k.touched[t]] = 0;
88
+ return sum > 1 ? 1 : sum;
89
+ }
@@ -0,0 +1,12 @@
1
+ import type { Listed } from './book.js';
2
+ import type { Mixer } from './mixer.js';
3
+ import type { Mark } from './types.js';
4
+ import { type Voice } from './voice.js';
5
+ /** The host timestamp the mix clock reads mix time `t` at; Infinity where it never will. */
6
+ export declare function hostOf<I, O>(mix: Mixer<I, O>, t: number): number;
7
+ /** What the mix clock reads at host time `u`, by the clock in force then. */
8
+ export declare function readingAt<I, O>(mix: Mixer<I, O>, u: number): number;
9
+ /** Every mark between two host timestamps, earliest first, with the order that tells each apart. */
10
+ export declare function listed<I, O>(mix: Mixer<I, O>, from: number, to: number): Listed[];
11
+ /** When a voice reaches a mark, mix time, or undefined while nothing has fixed it. */
12
+ export declare function markOf<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, mark: Mark): number | undefined;
package/dist/marks.js ADDED
@@ -0,0 +1,80 @@
1
+ import { startOf } from './place.js';
2
+ import { none } from './voice.js';
3
+ /** The host timestamp the mix clock reads mix time `t` at; Infinity where it never will. */
4
+ export function hostOf(mix, t) {
5
+ return (mix.pace === null ? t : mix.pace.timeOf(t)) + mix.offset;
6
+ }
7
+ /** What the mix clock reads at host time `u`, by the clock in force then. */
8
+ export function readingAt(mix, u) {
9
+ return mix.pace === null ? u : mix.pace.reading(u);
10
+ }
11
+ /** Every mark between two host timestamps, earliest first, with the order that tells each apart. */
12
+ export function listed(mix, from, to) {
13
+ const lo = from - mix.offset;
14
+ const hi = to - mix.offset;
15
+ const out = [];
16
+ for (const a of mix.announced)
17
+ if (a.at >= lo && a.at <= hi)
18
+ out.push({
19
+ timestamp: a.at + mix.offset,
20
+ mark: undefined,
21
+ voice: undefined,
22
+ score: a.score,
23
+ name: a.name,
24
+ tags: a.tags,
25
+ order: a.order,
26
+ });
27
+ for (const voice of [...mix.cued, ...mix.gone]) {
28
+ for (const mark of ['start', 'in', 'out', 'end']) {
29
+ const m = markOf(mix, voice, mark);
30
+ const t = m === undefined ? m : hostOf(mix, m) - mix.offset;
31
+ if (t === undefined || t < lo || t > hi)
32
+ continue;
33
+ out.push({
34
+ timestamp: t + mix.offset,
35
+ mark,
36
+ voice: voice.id,
37
+ score: voice.spec.score,
38
+ name: voice.spec.name,
39
+ tags: voice.spec.tags ?? none,
40
+ order: voice.id,
41
+ });
42
+ }
43
+ }
44
+ out.sort((a, b) => a.timestamp - b.timestamp || a.order - b.order);
45
+ return out;
46
+ }
47
+ /** When a voice reaches a mark, mix time, or undefined while nothing has fixed it. */
48
+ export function markOf(mix, voice, mark) {
49
+ if (!Number.isFinite(voice.start))
50
+ return undefined;
51
+ // A mark is known only while every clock above it is fixed.
52
+ const start = startOf(voice);
53
+ if (!Number.isFinite(start))
54
+ return undefined;
55
+ if (mark === 'start')
56
+ return start;
57
+ if (mark === 'in')
58
+ return start + (voice.fade.in ?? 0);
59
+ const out = voice.out;
60
+ let outAt;
61
+ if (out !== null)
62
+ outAt = out.at;
63
+ else if (Number.isFinite(voice.outAt))
64
+ outAt = voice.outAt;
65
+ else if (!voice.holdsAfter && Number.isFinite(voice.span)) {
66
+ const t = voice.timeAt(voice.span + voice.latest);
67
+ if (Number.isFinite(t))
68
+ outAt = Math.max(start, t);
69
+ }
70
+ if (mark === 'out')
71
+ return voice.state === 'done' && outAt === undefined ? voice.doneAt : outAt;
72
+ if (voice.state === 'done')
73
+ return voice.doneAt;
74
+ if (out !== null) {
75
+ if (out.rest)
76
+ return out.deadline === undefined ? undefined : out.at + out.deadline;
77
+ return out.at + out.over;
78
+ }
79
+ return outAt === undefined ? undefined : outAt + (mix.reduced ? 0 : (voice.fade.out ?? 0));
80
+ }
package/dist/meet.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import type { Lanes } from './lanes.js';
2
+ import type { Subject } from './voice.js';
3
+ /**
4
+ * Whether the lanes a probe of the subject at `slot` just met (`met`) can be folded on the general
5
+ * path after the lanes' values: none is in a locus or a crowd, the subject owes none already this
6
+ * fill, and each comes after every other laned voice in voice order, so folding it last folds in
7
+ * the general path's order.
8
+ */
9
+ export declare function owable<I, O>(lanes: Lanes<I, O>, slot: number): boolean;
10
+ /**
11
+ * Gives a probed subject a position on every lane that started playing since the subject was last
12
+ * checked and that reaches it. True when any did, so the probe takes the general path.
13
+ */
14
+ export declare function meet<I, O>(lanes: Lanes<I, O>, slot: number, subject: I, head: Subject<unknown> | null): boolean;
15
+ /** Counts an idle lane more or fewer reaching a subject. */
16
+ export declare function reach<I, O>(lanes: Lanes<I, O>, slot: number, by: number): void;
package/dist/meet.js ADDED
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Whether the lanes a probe of the subject at `slot` just met (`met`) can be folded on the general
3
+ * path after the lanes' values: none is in a locus or a crowd, the subject owes none already this
4
+ * fill, and each comes after every other laned voice in voice order, so folding it last folds in
5
+ * the general path's order.
6
+ */
7
+ export function owable(lanes, slot) {
8
+ if (lanes.metCrowd || lanes.owes(slot))
9
+ return false;
10
+ // Subjects meeting the same lanes in one fill get the same answer, and a list is kept only once true.
11
+ if (lanes.owed.last(lanes.met))
12
+ return true;
13
+ const met = lanes.met;
14
+ let other = Number.NEGATIVE_INFINITY;
15
+ for (const lane of lanes.lanes) {
16
+ const id = lane.voice.id;
17
+ if (id > other && !met.includes(id))
18
+ other = id;
19
+ }
20
+ for (const c of lanes.crowds)
21
+ if (c.size > 0)
22
+ other = Math.max(other, c.hot[(c.size - 1) * c.stride + 7 /* Hot.ID */]);
23
+ for (const id of met)
24
+ if (id <= other || lanes.byId.get(id).group !== null)
25
+ return false;
26
+ return true;
27
+ }
28
+ /**
29
+ * Gives a probed subject a position on every lane that started playing since the subject was last
30
+ * checked and that reaches it. True when any did, so the probe takes the general path.
31
+ */
32
+ export function meet(lanes, slot, subject, head) {
33
+ const seen = lanes.per[slot * 6 /* Per.SLOT */ + 1 /* Per.SEEN */];
34
+ const from = seen < 0 ? -1 - seen : seen;
35
+ lanes.per[slot * 6 /* Per.SLOT */ + 1 /* Per.SEEN */] = lanes.epochs;
36
+ let met = false;
37
+ lanes.met.length = 0;
38
+ lanes.metCrowd = false;
39
+ const dense = lanes.dense;
40
+ for (let i = dense.length - 1; i >= 0; i--) {
41
+ const lane = dense[i];
42
+ if (lane.epoch <= from)
43
+ break;
44
+ if (lane.positionOf(slot) >= 0)
45
+ continue;
46
+ const held = lanes.host.meet(lane.voice, subject, head, slot);
47
+ if (!held.reaches)
48
+ continue;
49
+ lane.add(slot, held);
50
+ if (lane.idle)
51
+ reach(lanes, slot, 1);
52
+ lanes.met.push(lane.voice.id);
53
+ met = true;
54
+ }
55
+ // `open` marks every subject a voice naming it starts on, so an unmarked one has none to meet.
56
+ const naming = seen < 0 ? lanes.host.naming(subject) : undefined;
57
+ if (naming !== undefined)
58
+ for (const voice of naming) {
59
+ const lane = lanes.byId.get(voice.id);
60
+ if (lane === undefined) {
61
+ if (place(lanes, voice, slot, subject, from, head)) {
62
+ met = true;
63
+ lanes.metCrowd = true;
64
+ }
65
+ continue;
66
+ }
67
+ if (lane.epoch <= from || lane.positionOf(slot) >= 0)
68
+ continue;
69
+ const held = lanes.host.meet(voice, subject, head, slot);
70
+ if (!held.reaches)
71
+ continue;
72
+ lane.add(slot, held);
73
+ if (lane.idle)
74
+ reach(lanes, slot, 1);
75
+ lanes.met.push(voice.id);
76
+ met = true;
77
+ }
78
+ return met;
79
+ }
80
+ /** Places a crowd voice's row on the subject a probe met, as `Lane.add` adds a position. */
81
+ function place(lanes, voice, slot, subject, from, head) {
82
+ const c = lanes.crowdOf.get(voice.id);
83
+ const p = c?.rowOf.get(voice.id);
84
+ if (c === undefined || p === undefined)
85
+ return false;
86
+ const h = p * c.stride;
87
+ const flags = c.hot[h + 0 /* Hot.FLAGS */];
88
+ if (c.hot[h + 6 /* Hot.EPOCH */] <= from || (flags & 16 /* Flag.PLACED */) !== 0)
89
+ return false;
90
+ const held = lanes.host.meet(voice, subject, head, slot);
91
+ if (!held.reaches)
92
+ return false;
93
+ const o = p * 8 /* Row.STRIDE */;
94
+ c.list[p] = slot;
95
+ c.data[o + 0 /* Row.DELAY */] = held.delay;
96
+ c.data[o + 1 /* Row.SINCE */] = held.shown;
97
+ c.data[o + 2 /* Row.WEIGHT */] = 0;
98
+ c.data[o + 3 /* Row.PROBED */] = 0;
99
+ c.data[o + 4 /* Row.MSLOT */] = -1;
100
+ c.data[o + 5 /* Row.SAMPLED */] = -1;
101
+ c.data[o + 7 /* Row.MET */] = 0;
102
+ c.records[p] = held;
103
+ c.deltas[p] = null;
104
+ c.restale(p, flags | 16 /* Flag.PLACED */);
105
+ if (c.idle)
106
+ reach(lanes, slot, 1);
107
+ return true;
108
+ }
109
+ /** Counts an idle lane more or fewer reaching a subject. */
110
+ export function reach(lanes, slot, by) {
111
+ const i = slot * 6 /* Per.SLOT */ + 2 /* Per.IDLE */;
112
+ lanes.per[i] = lanes.per[i] + by;
113
+ }