@msb235/blits 0.14.0 → 0.15.1

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 (187) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/README.md +3 -3
  3. package/dist/ahead.d.ts +26 -0
  4. package/dist/ahead.js +264 -0
  5. package/dist/around.d.ts +11 -0
  6. package/dist/around.js +32 -0
  7. package/dist/bare.js +37 -14
  8. package/dist/blank.d.ts +10 -0
  9. package/dist/blank.js +26 -0
  10. package/dist/blend.d.ts +2 -1
  11. package/dist/blend.js +1 -6
  12. package/dist/bound.d.ts +70 -0
  13. package/dist/bound.js +351 -0
  14. package/dist/chain.d.ts +2 -1
  15. package/dist/chain.js +7 -5
  16. package/dist/changes.d.ts +24 -0
  17. package/dist/changes.js +189 -0
  18. package/dist/channels.d.ts +39 -13
  19. package/dist/channels.js +14 -5
  20. package/dist/color.d.ts +12 -10
  21. package/dist/color.js +25 -25
  22. package/dist/columns.d.ts +2 -1
  23. package/dist/columns.js +62 -38
  24. package/dist/contribution.d.ts +30 -0
  25. package/dist/contribution.js +374 -0
  26. package/dist/copyout.d.ts +10 -0
  27. package/dist/copyout.js +69 -24
  28. package/dist/crowd.d.ts +16 -6
  29. package/dist/crowd.js +20 -11
  30. package/dist/cue.d.ts +8 -1
  31. package/dist/cue.js +39 -13
  32. package/dist/cut.d.ts +8 -0
  33. package/dist/cut.js +4 -0
  34. package/dist/delay.d.ts +3 -0
  35. package/dist/delay.js +7 -0
  36. package/dist/drop.d.ts +9 -0
  37. package/dist/drop.js +84 -0
  38. package/dist/due.d.ts +24 -5
  39. package/dist/due.js +114 -34
  40. package/dist/easing.d.ts +6 -4
  41. package/dist/easing.js +7 -3
  42. package/dist/everyone.d.ts +8 -2
  43. package/dist/everyone.js +33 -3
  44. package/dist/fade.d.ts +2 -1
  45. package/dist/fade.js +2 -1
  46. package/dist/fill.d.ts +3 -2
  47. package/dist/fill.js +52 -20
  48. package/dist/fit.d.ts +44 -26
  49. package/dist/fit.js +10 -9
  50. package/dist/fold.d.ts +4 -37
  51. package/dist/fold.js +5 -513
  52. package/dist/gather.d.ts +12 -7
  53. package/dist/gather.js +179 -44
  54. package/dist/gone.js +2 -2
  55. package/dist/grid.d.ts +47 -0
  56. package/dist/grid.js +247 -0
  57. package/dist/handle.d.ts +2 -0
  58. package/dist/handle.js +14 -8
  59. package/dist/held.d.ts +2 -1
  60. package/dist/held.js +2 -1
  61. package/dist/history.d.ts +6 -5
  62. package/dist/history.js +7 -9
  63. package/dist/hosts.js +9 -15
  64. package/dist/index.d.ts +5 -4
  65. package/dist/keyfill.d.ts +1 -1
  66. package/dist/keyfill.js +5 -3
  67. package/dist/keyrows.d.ts +12 -0
  68. package/dist/keyrows.js +99 -12
  69. package/dist/lane.d.ts +38 -11
  70. package/dist/lane.js +46 -11
  71. package/dist/lanes.d.ts +30 -12
  72. package/dist/lanes.js +56 -36
  73. package/dist/leavings.d.ts +2 -2
  74. package/dist/leavings.js +1 -1
  75. package/dist/lives.d.ts +47 -0
  76. package/dist/lives.js +180 -0
  77. package/dist/meet.d.ts +2 -2
  78. package/dist/meet.js +95 -7
  79. package/dist/mixer.d.ts +56 -37
  80. package/dist/mixer.js +57 -171
  81. package/dist/motion.d.ts +14 -118
  82. package/dist/motion.js +16 -8
  83. package/dist/motions.d.ts +62 -155
  84. package/dist/motions.js +56 -370
  85. package/dist/moved.d.ts +1 -1
  86. package/dist/moved.js +16 -7
  87. package/dist/named.d.ts +6 -2
  88. package/dist/named.js +49 -3
  89. package/dist/numbers.d.ts +7 -0
  90. package/dist/numbers.js +33 -0
  91. package/dist/owed.d.ts +2 -0
  92. package/dist/owed.js +4 -0
  93. package/dist/pack.d.ts +2 -1
  94. package/dist/pack.js +2 -3
  95. package/dist/patch.d.ts +7 -3
  96. package/dist/patch.js +6 -2
  97. package/dist/places.d.ts +53 -0
  98. package/dist/places.js +171 -0
  99. package/dist/plot.d.ts +12 -0
  100. package/dist/plot.js +52 -0
  101. package/dist/project.d.ts +4 -4
  102. package/dist/project.js +9 -212
  103. package/dist/pull.js +22 -9
  104. package/dist/qualify.js +93 -31
  105. package/dist/reading.d.ts +6 -0
  106. package/dist/reading.js +5 -0
  107. package/dist/record.d.ts +5 -1
  108. package/dist/record.js +3 -2
  109. package/dist/relink.d.ts +1 -1
  110. package/dist/renumber.d.ts +10 -0
  111. package/dist/renumber.js +75 -0
  112. package/dist/restored.d.ts +2 -2
  113. package/dist/restored.js +1 -1
  114. package/dist/rests.d.ts +9 -0
  115. package/dist/rests.js +76 -0
  116. package/dist/revive.d.ts +3 -2
  117. package/dist/revive.js +2 -2
  118. package/dist/{locus.d.ts → role.d.ts} +10 -9
  119. package/dist/{locus.js → role.js} +3 -3
  120. package/dist/roles.d.ts +10 -0
  121. package/dist/roles.js +158 -0
  122. package/dist/roster.js +3 -3
  123. package/dist/rotation.d.ts +1 -1
  124. package/dist/rotation.js +1 -1
  125. package/dist/rows.js +28 -20
  126. package/dist/sample.d.ts +2 -2
  127. package/dist/sample.js +33 -7
  128. package/dist/seat.d.ts +17 -0
  129. package/dist/seat.js +25 -0
  130. package/dist/seek.js +5 -5
  131. package/dist/setting.d.ts +27 -0
  132. package/dist/setting.js +55 -0
  133. package/dist/shared.d.ts +1 -1
  134. package/dist/shared.js +1 -1
  135. package/dist/signals.d.ts +64 -32
  136. package/dist/signals.js +20 -18
  137. package/dist/stack.d.ts +5 -5
  138. package/dist/stack.js +5 -5
  139. package/dist/standing.d.ts +2 -1
  140. package/dist/standing.js +3 -2
  141. package/dist/store.d.ts +9 -0
  142. package/dist/store.js +24 -0
  143. package/dist/strays.d.ts +2 -1
  144. package/dist/strays.js +23 -15
  145. package/dist/stretch.d.ts +112 -0
  146. package/dist/stretch.js +9 -0
  147. package/dist/subject.d.ts +142 -0
  148. package/dist/subject.js +1 -0
  149. package/dist/tape.d.ts +9 -5
  150. package/dist/tape.js +5 -1
  151. package/dist/then.d.ts +9 -0
  152. package/dist/then.js +57 -0
  153. package/dist/ticker.d.ts +4 -4
  154. package/dist/trace.d.ts +4 -0
  155. package/dist/trace.js +255 -0
  156. package/dist/transport.d.ts +0 -8
  157. package/dist/transport.js +0 -4
  158. package/dist/turns.d.ts +4 -3
  159. package/dist/turns.js +4 -3
  160. package/dist/types/arc.d.ts +12 -11
  161. package/dist/types/channel.d.ts +12 -8
  162. package/dist/types/history.d.ts +35 -19
  163. package/dist/types/mix.d.ts +237 -163
  164. package/dist/types/motion.d.ts +151 -0
  165. package/dist/types/motion.js +1 -0
  166. package/dist/types/patch.d.ts +67 -34
  167. package/dist/types/place.d.ts +51 -36
  168. package/dist/types/trace.d.ts +51 -0
  169. package/dist/types/trace.js +1 -0
  170. package/dist/types/transport.d.ts +20 -18
  171. package/dist/types/voice.d.ts +144 -73
  172. package/dist/types/watch.d.ts +88 -40
  173. package/dist/types.d.ts +2 -0
  174. package/dist/unreached.d.ts +2 -0
  175. package/dist/unreached.js +7 -0
  176. package/dist/voice.d.ts +10 -143
  177. package/dist/voice.js +37 -82
  178. package/dist/warn.d.ts +2 -0
  179. package/dist/warn.js +1 -1
  180. package/dist/watch.d.ts +2 -2
  181. package/dist/watch.js +5 -3
  182. package/dist/wave.d.ts +4 -3
  183. package/dist/wave.js +3 -2
  184. package/dist/weigh.d.ts +7 -1
  185. package/dist/weigh.js +27 -0
  186. package/docs/schema.html +223 -34
  187. package/package.json +2 -2
package/dist/trace.js ADDED
@@ -0,0 +1,255 @@
1
+ import { straight } from './easing.js';
2
+ /**
3
+ * How far inside a sampled stretch its ends are read, ms, but for the window's own: a stretch ends
4
+ * where a voice may jump, and its end is read on its own side of the jump.
5
+ */
6
+ const NUDGE = 1e-6;
7
+ /** Two values at one time this close are one point, not a jump. */
8
+ const close = (a, b) => Math.abs(a - b) <= 1e-6 * Math.max(1, Math.abs(a), Math.abs(b));
9
+ /** `mix.trace`: see `Mix.trace`. */
10
+ export function traceMix(mix, from, to, opts = {}) {
11
+ if (!(from <= to) || !Number.isFinite(from) || !Number.isFinite(to))
12
+ throw new RangeError(`blits: a trace runs from a time to one no earlier, not ${from} to ${to}`);
13
+ const grid = opts.grid ?? 5;
14
+ if (!(grid > 0 && grid < Number.POSITIVE_INFINITY))
15
+ throw new RangeError(`blits: a trace's grid is a finite number of ms above 0, not ${grid}`);
16
+ const known = knowable(mix, from);
17
+ const projections = new Map();
18
+ const poses = new Map();
19
+ const plans = new Map();
20
+ const poseAt = (subject, t) => {
21
+ let mine = poses.get(subject);
22
+ if (mine === undefined) {
23
+ mine = new Map();
24
+ poses.set(subject, mine);
25
+ }
26
+ let pose = mine.get(t);
27
+ if (pose === undefined) {
28
+ let p = projections.get(t);
29
+ if (p === undefined) {
30
+ p = mix.project(t);
31
+ projections.set(t, p);
32
+ }
33
+ pose = p.probe(subject, mix.pose());
34
+ mine.set(t, pose);
35
+ }
36
+ return pose;
37
+ };
38
+ return {
39
+ from,
40
+ to,
41
+ of(subject, channel) {
42
+ const name = channel;
43
+ const slot = mix.slotOf.get(name);
44
+ if (slot === undefined)
45
+ throw new Error(`blits: the mix has no channel ${name}`);
46
+ const value = (t) => {
47
+ const v = poseAt(subject, t)[name];
48
+ return typeof v === 'number' ? v : Number.NaN;
49
+ };
50
+ let plan = plans.get(subject);
51
+ if (plan === undefined) {
52
+ plan = planOf(mix, subject, from, to);
53
+ plans.set(subject, plan);
54
+ }
55
+ const op = mix.scalars[slot];
56
+ const shaped = known &&
57
+ !plan.unknown.has(slot) &&
58
+ !mix.bounded.includes(slot) &&
59
+ (op === 'sum' || (op === 'mul' && (plan.writers.get(slot) ?? 0) <= 1));
60
+ const bent = plan.bent.get(slot);
61
+ const straightOver = (a, b) => shaped && (bent === undefined || bent.every(([lo, hi]) => !(lo < b && hi > a)));
62
+ if (from === to)
63
+ return { at: [from], value: [value(from)], exact: shaped };
64
+ const at = [];
65
+ const values = [];
66
+ let exact = true;
67
+ const add = (t, v) => {
68
+ const n = at.length;
69
+ if (n > 0 && at[n - 1] === t && close(values[n - 1], v))
70
+ values[n - 1] = v;
71
+ else {
72
+ at.push(t);
73
+ values.push(v);
74
+ }
75
+ };
76
+ const knots = [from, ...(plan.knots.get(slot) ?? []), to];
77
+ for (let i = 0; i + 1 < knots.length; i++) {
78
+ const a = knots[i];
79
+ const b = knots[i + 1];
80
+ if (straightOver(a, b)) {
81
+ // Read inside, so a jump at either end is not read; the line reaches each end exactly.
82
+ const t1 = a + (b - a) / 3;
83
+ const t2 = b - (b - a) / 3;
84
+ const v1 = value(t1);
85
+ const slope = (value(t2) - v1) / (t2 - t1);
86
+ add(a, v1 - slope * (t1 - a));
87
+ add(b, v1 + slope * (b - t1));
88
+ continue;
89
+ }
90
+ exact = false;
91
+ const e = Math.min(NUDGE, (b - a) / 4);
92
+ add(a, value(a === from ? a : a + e));
93
+ for (let k = Math.max(0, Math.ceil((a - from) / grid));; k++) {
94
+ const g = from + k * grid;
95
+ if (g >= b - e)
96
+ break;
97
+ if (g > a + e)
98
+ add(g, value(g));
99
+ }
100
+ add(b, value(b === to ? b : b - e));
101
+ }
102
+ return { at, value: values, exact };
103
+ },
104
+ };
105
+ }
106
+ /**
107
+ * Whether the mix's own state lets a trace know any shape: a window at or after the mix's time,
108
+ * with no recorded call ahead.
109
+ */
110
+ function knowable(mix, from) {
111
+ const transport = mix.transport;
112
+ if (!(from >= mix.now))
113
+ return false;
114
+ const tape = transport.tape;
115
+ return tape === undefined || tape.timestampAt(tape.undoDepth()) === undefined;
116
+ }
117
+ function planOf(mix, subject, from, to) {
118
+ const plan = { knots: new Map(), bent: new Map(), unknown: new Set(), writers: new Map() };
119
+ for (const voice of mix.cued) {
120
+ if (voice.state === 'done' || !mix.aims(voice, subject))
121
+ continue;
122
+ if (shapeOf(mix, voice, subject, from, to, plan))
123
+ continue;
124
+ for (const slot of voice.slots)
125
+ plan.unknown.add(slot);
126
+ }
127
+ for (const [slot, knots] of plan.knots)
128
+ plan.knots.set(slot, knots.sort((a, b) => a - b).filter((t, i, all) => i === 0 || t !== all[i - 1]));
129
+ return plan;
130
+ }
131
+ /** The most breakpoints one voice may add to a window before it is read on the grid instead. */
132
+ const MOST = 4096;
133
+ /**
134
+ * Adds a voice's breakpoints for one subject to `plan`, and returns true, where its shape is known
135
+ * through the window: a `keys` voice on its own clock at a steady rate and a number weight. Returns
136
+ * false, adding nothing, for any other.
137
+ */
138
+ function shapeOf(mix, voice, subject, from, to, plan) {
139
+ const spec = voice.spec;
140
+ const built = voice.built;
141
+ const patch = voice.patch;
142
+ if (patch.form !== 'keys' || built === null || patch.step !== undefined)
143
+ return false;
144
+ if (typeof spec.weight === 'function' || voice.owner !== null || voice.holding !== null)
145
+ return false;
146
+ if (voice.fitting !== null || voice.arc !== null || voice.blend !== null)
147
+ return false;
148
+ if (spec.anchor !== undefined || spec.role !== undefined || spec.within !== undefined)
149
+ return false;
150
+ if (spec.stepMs !== undefined || spec.from !== undefined)
151
+ return false;
152
+ if (voice.ramp !== null || !(voice.rate > 0) || voice.out !== null || voice.back !== null)
153
+ return false;
154
+ if (voice.parts !== null || voice.parted !== null || voice.left !== null)
155
+ return false;
156
+ if (Number.isFinite(voice.outAt) || voice.state === 'fading')
157
+ return false;
158
+ // Under a mix rate, a waiting voice's start is pinned to the host's clock, where a ramp moves it.
159
+ if (voice.state === 'pending' && mix.transport.pace !== null)
160
+ return false;
161
+ const knots = [];
162
+ const bent = [];
163
+ const bentBy = new Map();
164
+ const at = (t) => {
165
+ if (t > from && t < to)
166
+ knots.push(t);
167
+ };
168
+ const reduced = mix.reduced;
169
+ const held = voice.sharing ? undefined : voice.subjects.get(subject);
170
+ const delay = held?.delay ?? (spec.stagger ? spec.stagger(subject) : 0);
171
+ const time = (raw) => voice.timeAt(raw + delay);
172
+ const fadeIn = reduced ? 0 : (voice.fade.in ?? 0);
173
+ if (fadeIn > 0) {
174
+ // Where its fade in counts from is known only once the subject has met the voice.
175
+ if (held === undefined)
176
+ return false;
177
+ if (held.shown + fadeIn > from) {
178
+ bent.push([held.shown, held.shown + fadeIn]);
179
+ at(held.shown);
180
+ at(held.shown + fadeIn);
181
+ }
182
+ }
183
+ const duration = voice.duration;
184
+ const span = voice.span;
185
+ at(voice.start);
186
+ at(time(0));
187
+ if (Number.isFinite(span))
188
+ at(time(span));
189
+ const ends = voice.endsAt();
190
+ at(ends);
191
+ if (!voice.freezesAfter && Number.isFinite(ends)) {
192
+ const over = reduced ? 0 : Number.isNaN(voice.outOver) ? (voice.fade.out ?? 0) : voice.outOver;
193
+ if (over > 0) {
194
+ bent.push([ends, ends + over]);
195
+ at(ends + over);
196
+ }
197
+ // It leaves when its latest subject ends, which a subject first met later can move.
198
+ if (spec.stagger !== undefined)
199
+ bent.push([time(span), Number.POSITIVE_INFINITY]);
200
+ }
201
+ if (duration > 0) {
202
+ const first = Math.max(0, Math.floor((voice.elapsedAt(from) - delay) / duration));
203
+ const last = Math.min(Math.ceil(voice.passes) - 1, Math.floor((voice.elapsedAt(to) - delay) / duration));
204
+ let stops = 0;
205
+ for (const track of built.tracks)
206
+ stops += track.all.length + 1;
207
+ if ((last - first + 1) * stops > MOST)
208
+ return false;
209
+ for (let pass = first; pass <= last; pass++) {
210
+ const base = pass * duration;
211
+ at(time(base));
212
+ at(time(base + duration));
213
+ for (const track of built.tracks) {
214
+ const slot = mix.slotOf.get(track.channel);
215
+ if (track.numbers === null)
216
+ return false;
217
+ const wait = track.delay;
218
+ if (wait >= duration)
219
+ continue;
220
+ if (wait > 0)
221
+ at(time(base + wait));
222
+ let before = Number.NaN;
223
+ for (const pt of track.all) {
224
+ const t = time(base + wait + Math.min(1, Math.max(0, pt.at)) * (duration - wait));
225
+ at(t);
226
+ if (!Number.isNaN(before) && !straight(pt.ease)) {
227
+ const list = bentBy.get(slot);
228
+ if (list === undefined)
229
+ bentBy.set(slot, [[before, t]]);
230
+ else
231
+ list.push([before, t]);
232
+ }
233
+ before = t;
234
+ }
235
+ }
236
+ }
237
+ }
238
+ for (const slot of voice.slots) {
239
+ const times = plan.knots.get(slot);
240
+ if (times === undefined)
241
+ plan.knots.set(slot, [...knots]);
242
+ else
243
+ times.push(...knots);
244
+ plan.writers.set(slot, (plan.writers.get(slot) ?? 0) + 1);
245
+ const mine = [...bent, ...(bentBy.get(slot) ?? [])];
246
+ if (mine.length === 0)
247
+ continue;
248
+ const list = plan.bent.get(slot);
249
+ if (list === undefined)
250
+ plan.bent.set(slot, mine);
251
+ else
252
+ list.push(...mine);
253
+ }
254
+ return true;
255
+ }
@@ -31,14 +31,6 @@ export interface Frame {
31
31
  at: number;
32
32
  u: number;
33
33
  }
34
- /** Where history is cut for a seek or read: what frame `seq` made, and before it, is kept. */
35
- export interface Cut {
36
- seq: number;
37
- /** For a read of what a frame showed: of that frame's own changes, only those made before it was read. */
38
- strict: boolean;
39
- }
40
- /** Whether a change made in frame `seq` falls inside `cut`; `early` for one made before the frame was read. */
41
- export declare function within(cut: Cut, seq: number, early?: boolean): boolean;
42
34
  /**
43
35
  * The clock its mixes play on: host time less what `rebase` took out, at the rate its `pace`
44
36
  * sets, moved by `sync` and `seek`, and the tape of calls a seek plays again. A mix made alone
package/dist/transport.js CHANGED
@@ -11,10 +11,6 @@ import { hostAt, record, replay, tapeOf } from './tape.js';
11
11
  import { gave, historyFor } from './watch.js';
12
12
  /** How far the tape's reach moves before it is pruned again, ms of mix time. */
13
13
  const TAPE_STEP_MS = 1000;
14
- /** Whether a change made in frame `seq` falls inside `cut`; `early` for one made before the frame was read. */
15
- export function within(cut, seq, early = false) {
16
- return seq < cut.seq || (seq === cut.seq && (!cut.strict || early));
17
- }
18
14
  /**
19
15
  * The clock its mixes play on: host time less what `rebase` took out, at the rate its `pace`
20
16
  * sets, moved by `sync` and `seek`, and the tape of calls a seek plays again. A mix made alone
package/dist/turns.d.ts CHANGED
@@ -7,11 +7,12 @@ export declare function shortWay(d: number, turn: number): number;
7
7
  * `fold`, which takes its axes as one value.
8
8
  */
9
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. */
10
+ /** `turnInto` for a value held as numbers at `from` in `source`, as a role holds what it gathered. */
11
11
  export declare function turnFrom(ch: Laned, values: Float64Array, base: number, source: Float64Array, from: number, w: number): void;
12
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.
13
+ * A role member's value for a channel not lerped straight across, lerped by `u` into what the
14
+ * members before it left at `base` through the channel's own `lerp`, or an angle's short way round;
15
+ * the first member's value as it is, where `taken` is 0.
15
16
  */
16
17
  export declare function turnToward(ch: Laned, values: Float64Array, base: number, value: unknown, taken: number, u: number): void;
17
18
  /** Sets every numbered subject's values on a lane to the channel's rest, as a fill starts from. */
package/dist/turns.js CHANGED
@@ -28,7 +28,7 @@ export function turnInto(ch, values, base, value, w) {
28
28
  for (let a = 0; a < axes; a++)
29
29
  values[base + a] = out[a];
30
30
  }
31
- /** `turnInto` for a value held as numbers at `from` in `source`, as a locus holds what it gathered. */
31
+ /** `turnInto` for a value held as numbers at `from` in `source`, as a role holds what it gathered. */
32
32
  export function turnFrom(ch, values, base, source, from, w) {
33
33
  if (ch.turn > 0) {
34
34
  turnInto(ch, values, base, source[from], w);
@@ -38,8 +38,9 @@ export function turnFrom(ch, values, base, source, from, w) {
38
38
  turnInto(ch, values, base, other, w);
39
39
  }
40
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.
41
+ * A role member's value for a channel not lerped straight across, lerped by `u` into what the
42
+ * members before it left at `base` through the channel's own `lerp`, or an angle's short way round;
43
+ * the first member's value as it is, where `taken` is 0.
43
44
  */
44
45
  export function turnToward(ch, values, base, value, taken, u) {
45
46
  if (ch.turn > 0) {
@@ -1,7 +1,7 @@
1
1
  import type { Handle, OwnerSpec, VoiceSpec } from './voice.js';
2
2
  /**
3
- * One voice of an arc's part: a voice's spec, less what the arc sets for it. The arc places it,
4
- * fades it across each boundary by `blend`, and holds its first and last frames for the blend.
3
+ * One voice of an arc's part. It is a voice's spec, less what the arc sets for it. The arc places
4
+ * it, fades it across each boundary by `blend`, and holds its first and last frames for the blend.
5
5
  *
6
6
  * @category score
7
7
  */
@@ -13,15 +13,15 @@ export interface ArcVoice<I, O, H = unknown> extends Omit<VoiceSpec<I, O, H>, 'o
13
13
  duration?: number;
14
14
  }
15
15
  /**
16
- * One part of an arc: a voice, or several layered together that play as one and last as long as
17
- * the longest.
16
+ * One part of an arc. It is a voice, or several layered together that play as one and last as
17
+ * long as the longest.
18
18
  *
19
19
  * @category score
20
20
  */
21
21
  export type ArcPart<I, O, H = unknown> = ArcVoice<I, O, H> | readonly ArcVoice<I, O, H>[];
22
22
  /**
23
- * What `arc` takes: an owner over three parts that hand over in order, `enter`, `active` and
24
- * `exit`, any of which may be left out.
23
+ * What `arc` takes. It is an owner over three parts that hand over in order, `enter`, `active`
24
+ * and `exit`, any of which may be left out.
25
25
  *
26
26
  * @category score
27
27
  */
@@ -33,22 +33,23 @@ export interface ArcSpec<I, O, H = unknown> extends Omit<OwnerSpec<I, H>, 'freez
33
33
  /** What plays as it leaves. Its start is the arc's `out` mark, and its end the arc's `end`. */
34
34
  exit?: ArcPart<I, O, H>;
35
35
  /**
36
- * How long `active` plays, ms on the arc's clock, looping for it unless its `loop` says how many
37
- * passes; or `'open'`, until `release`. Default: `active`'s own length, held open where it loops
38
- * for good, and 0 with no `active`.
36
+ * How long `active` plays, ms on the arc's clock, or `'open'`. A number loops `active` for it
37
+ * unless its `loop` says how many passes; `'open'` plays it until `release`. Default: `active`'s
38
+ * own length, held open where it loops for good, and 0 with no `active`.
39
39
  */
40
40
  hold?: number | 'open';
41
41
  /** A crossfade across each boundary, ms, half before it and half after. Default 0. */
42
42
  blend?: number;
43
43
  }
44
44
  /**
45
- * An arc's handle: an owner's, and the release of its hold. `fade` takes it out with no exit.
45
+ * An arc's handle, which can release its hold. It is an owner's handle, and `release` ends the
46
+ * hold. `fade` takes it out with no exit.
46
47
  *
47
48
  * @category score
48
49
  */
49
50
  export interface ArcHandle<I = unknown> extends Handle<I> {
50
51
  /**
51
- * Ends the hold now and plays the exit, its blend starting here; where the enter is still
52
+ * Ends the hold now and plays the exit. Its blend starts here; where the enter is still
52
53
  * playing, the exit follows it. Nothing once the exit has begun or the arc is fading.
53
54
  */
54
55
  release(): void;
@@ -5,9 +5,9 @@
5
5
  */
6
6
  export interface Channel<V> {
7
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.
8
+ * Names this channel's arithmetic. A patch written against one channel can then be checked
9
+ * against another of the same name. Two channels of one kind must fold identically; the stock
10
+ * ones set it, as `'sum'` or `'vec(3, sum)'`. Absent, a channel matches only itself.
11
11
  */
12
12
  kind?: string;
13
13
  /**
@@ -16,21 +16,25 @@ export interface Channel<V> {
16
16
  * carries speed can push it out; overshoot stops flat at the bound.
17
17
  */
18
18
  bounds?: readonly [min: number, max: number];
19
- /** Identity. Absent means the channel has none: it replaces rather than contributes. */
19
+ /**
20
+ * The value that changes nothing when folded in, such as 0 for `sum` or 1 for `mul`. Absent means
21
+ * the channel has none: it replaces rather than contributes.
22
+ */
20
23
  rest?: V;
21
24
  /** Fold two contributions into one. */
22
25
  merge(a: V, b: V): V;
23
26
  /** Fade toward `rest` by weight 0..1. Required when `rest` is set; absent otherwise. */
24
27
  scale?(v: V, w: number): V;
25
- /** Interpolate, for retargeting, for blending alternatives, and for folding a locus. */
28
+ /** Interpolate, for retargeting, for blending alternatives, and for folding a role. */
26
29
  lerp(a: V, b: V, u: number): V;
27
30
  /**
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.
31
+ * Folds a weighted contribution into `into` in place. Optional. It computes
32
+ * `merge(into, scale(v, w))`, written into `into` and returned, so a channel whose values are
33
+ * objects need not allocate per contribution. The mix only hands it a value it made.
30
34
  */
31
35
  fold?(into: V, v: V, w: number): V;
32
36
  /**
33
- * Optional: a fresh copy of `v`, which the mix makes of `rest` for `fold` to write into. The mix
37
+ * A fresh copy of `v`. Optional. The mix makes one of `rest` for `fold` to write into. The mix
34
38
  * copies arrays, typed arrays and plain objects itself; a channel whose `rest` is anything else,
35
39
  * such as a class instance, and that has `fold`, needs this or `cue` refuses it.
36
40
  */
@@ -1,6 +1,6 @@
1
1
  import type { VoiceSpec } from './voice.js';
2
2
  /**
3
- * What a mix or transport remembers, for `seek` and `project` to read back: see
3
+ * What a mix or transport remembers, for `seek` and `project` to read back. See
4
4
  * `MixOptions.history`.
5
5
  *
6
6
  * @category mix
@@ -17,11 +17,11 @@ export interface HistoryOptions {
17
17
  * the tape: the host bounds it.
18
18
  */
19
19
  store?: HistoryStore;
20
- /** Rebuilds a cued voice's spec from the descriptor it was cued with, so the voice can be paged out. */
20
+ /** Rebuilds a cued voice's spec from its descriptor. That lets the voice be paged out. */
21
21
  revive?(d: Described): VoiceSpec<any, any>;
22
22
  }
23
23
  /**
24
- * A voice as plain data: `kind` names what built it, and `data` survives `structuredClone`.
24
+ * A voice as plain data. `kind` names what built it, and `data` survives `structuredClone`.
25
25
  *
26
26
  * @category mix
27
27
  */
@@ -39,21 +39,23 @@ export interface HistoryStore {
39
39
  /** Records leaving memory. One brought back by a seek may be paged again: keep one per key and `seq`. */
40
40
  page(out: readonly Paged[]): void;
41
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.
42
+ * Loads what a restore to mix time `t` needs, for every mix. That is, from each stream, per voice
43
+ * and subject, the latest record at or before `t` and every record after it. Returning
44
+ * everything is fine.
44
45
  */
45
46
  load(t: number): Promise<readonly Paged[]>;
46
47
  /**
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.
48
+ * Forgets every record after frame `seq`, which a seek went back to. Every record whose `seq` is
49
+ * greater is from a future the tape makes again.
49
50
  */
50
51
  cut(seq: number): void;
51
52
  }
52
53
  /**
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.
54
+ * Which kind of thing a history record holds. It is a voice's `controls`, a stateful record's
55
+ * `snap`, an `input` signal's reading, a subject's record when it `left` a voice, a motion
56
+ * subject's `released` run or older `stretch`, the `host` fields patches read, a whole `voice`
57
+ * that has left, or the transport's own: a `mark` announced on it, a change of its `pace`, and a
58
+ * `frame` it played.
57
59
  *
58
60
  * @category mix
59
61
  */
@@ -64,12 +66,17 @@ export type PagedStream = 'voice' | 'snap' | 'input' | 'left' | 'released' | 'st
64
66
  * @category mix
65
67
  */
66
68
  export interface Paged {
67
- /** The mix's `name`; '' for a mix made alone without one, and for the transport's own records. */
69
+ /**
70
+ * The mix's `name`. It is '' for a mix made alone without one, and for the transport's own
71
+ * records.
72
+ */
68
73
  mix: string;
69
74
  stream: PagedStream;
70
75
  /** The voice's id, for every stream but `host` and the transport's own. */
71
76
  voice?: number;
72
- /** The subject's key, for a record of one subject; for a `mark`, the number it was announced as. */
77
+ /**
78
+ * The subject's key, for a record of one subject. For a `mark`, the number it was announced as.
79
+ */
73
80
  subject?: string | number;
74
81
  /** Mix time. */
75
82
  at: number;
@@ -81,7 +88,7 @@ export interface Paged {
81
88
  data: unknown;
82
89
  }
83
90
  /**
84
- * One host call a mix recorded on its tape: `apply` makes it again, and `invert` does nothing,
91
+ * One host call a mix recorded on its tape. `apply` makes it again, and `invert` does nothing,
85
92
  * since a mix goes back by restoring what it kept rather than by undoing calls.
86
93
  *
87
94
  * @category mix
@@ -92,8 +99,8 @@ export interface TapeOp {
92
99
  label?: string;
93
100
  }
94
101
  /**
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.
102
+ * One future a tape keeps at its current entry. It holds the calls the host made after the mix
103
+ * last went back here, before it made new ones.
97
104
  *
98
105
  * @category mix
99
106
  */
@@ -126,14 +133,23 @@ export interface Tape {
126
133
  switchBranch(id: number): void;
127
134
  }
128
135
  /**
129
- * Makes a mix's tape: weasel-history's `createHistory` is one. The mix passes the clock it stamps
136
+ * Makes a mix's tape. weasel-history's `createHistory` is one. The mix passes the clock it stamps
130
137
  * calls with, branching on, so a call made after going back keeps the old future as a branch, and
131
138
  * coalescing off.
132
139
  *
133
140
  * @category mix
134
141
  */
135
- export type TapeMaker = (adapter: unknown, opts: {
142
+ export type TapeMaker = (adapter: unknown, opts: TapeOptions) => Tape;
143
+ /**
144
+ * What a mix passes its `TapeMaker` besides the adapter.
145
+ *
146
+ * @category mix
147
+ */
148
+ export interface TapeOptions {
149
+ /** The clock the tape stamps calls with. */
136
150
  now: () => number;
151
+ /** Always `true`, so a call made after going back keeps the old future as a branch. */
137
152
  branching: boolean;
153
+ /** Always `0`, which turns coalescing off. */
138
154
  coalesceWindowMs: number;
139
- }) => Tape;
155
+ }