@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
@@ -1,10 +1,10 @@
1
1
  import type { HistoryOptions } from './history.js';
2
2
  import type { Mix } from './mix.js';
3
3
  import type { Sent } from './patch.js';
4
- import type { Handle } from './voice.js';
4
+ import type { Circle, Finder, Handle } from './voice.js';
5
5
  /**
6
- * Every mix made on the page, for a debugger: shared by every copy of blits loaded, so a mix one
7
- * copy made is listed by another. `watched()` returns it.
6
+ * Every mix made on the page, for a debugger. It is shared by every copy of blits loaded, so a
7
+ * mix one copy made is listed by another. `watched()` returns it.
8
8
  *
9
9
  * @category mix
10
10
  */
@@ -12,19 +12,27 @@ export interface Registry {
12
12
  /** The mixes still alive, in the order they were made. */
13
13
  entries(): Watched[];
14
14
  /** Hears each mix made, and each one collected, by its entry's `id`. Returns an unsubscribe. */
15
- on(fn: (change: {
16
- kind: 'add';
17
- entry: Watched;
18
- } | {
19
- kind: 'gone';
20
- id: number;
21
- }) => void): () => void;
15
+ on(fn: (change: RegistryChange) => void): () => void;
22
16
  /**
23
- * The history a mix made from now on keeps when its options name none and it is on no
24
- * transport, so a debugger present before the page's mixes can scrub them. Undefined by default.
17
+ * The history new mixes keep when they name none. It applies to a mix made from now on whose
18
+ * options name no history and that is on no transport, so a debugger present before the page's
19
+ * mixes can scrub them. Undefined by default.
25
20
  */
26
21
  history: HistoryOptions | undefined;
27
22
  }
23
+ /**
24
+ * What `Registry.on` hears. It is a mix made, with its entry, or a mix collected, by its entry's
25
+ * `id`.
26
+ *
27
+ * @category mix
28
+ */
29
+ export type RegistryChange = {
30
+ kind: 'add';
31
+ entry: Watched;
32
+ } | {
33
+ kind: 'gone';
34
+ id: number;
35
+ };
28
36
  /**
29
37
  * One mix as a debugger sees it. Built by the copy of blits that made the mix, so a debugger
30
38
  * reads mixes from other releases through it alike.
@@ -39,21 +47,17 @@ export interface Watched<I = unknown, O = unknown> {
39
47
  readonly name: string | undefined;
40
48
  readonly mix: Mix<I, O>;
41
49
  /** What history it keeps, and whether the registry's `history` gave it; undefined for none. */
42
- readonly history: {
43
- readonly ms: number;
44
- readonly tape: boolean;
45
- readonly added: boolean;
46
- } | undefined;
47
- /** The earliest mix time `read` reaches back to: `mix.now` without history. */
50
+ readonly history: WatchedHistory | undefined;
51
+ /** The earliest mix time `read` reaches back to. It is `mix.now` without history. */
48
52
  readonly reach: number;
49
53
  /** The kit's channel names, in kit order. */
50
54
  readonly channels: readonly string[];
51
- /** What a voice was cued with that its handle does not carry; undefined for a handle not on this mix. */
52
- voice(handle: Handle<I>): {
53
- readonly name: string | undefined;
54
- readonly tags: readonly string[];
55
- } | undefined;
56
- /** What to call a subject: the mix's `labelOf`, else a guess from the subject itself. */
55
+ /**
56
+ * What a voice was cued with that its handle does not carry. Undefined for a handle not on this
57
+ * mix.
58
+ */
59
+ voice(handle: Handle<I>): WatchedVoice | undefined;
60
+ /** What to call a subject. It is the mix's `labelOf`, else a guess from the subject itself. */
57
61
  label(subject: I): string;
58
62
  /**
59
63
  * Calls `fn` with each frame the host read, once the next one begins. While anyone watches, the
@@ -61,16 +65,46 @@ export interface Watched<I = unknown, O = unknown> {
61
65
  * a function that stops watching.
62
66
  */
63
67
  watch(fn: (frame: WatchedFrame<I, O>) => void): () => void;
64
- /** The subjects its voices still playing name, and those the host read in the last two frames while watched. */
68
+ /**
69
+ * The subjects its voices still playing name. It also lists those the host read in the last two
70
+ * frames while watched.
71
+ */
65
72
  subjects(): I[];
66
73
  /**
67
- * One subject's pose at mix time `at`, default now, read through `project`, which moves nothing:
68
- * never a probe. A time behind now needs history that reaches it.
74
+ * One subject's pose at mix time `at`, default now. It is read through `project`, which moves
75
+ * nothing, never a probe. A time behind now needs history that reaches it.
69
76
  */
70
77
  read(subject: I, at?: number): O;
71
- /** Takes the clock from the host: see `Hold`. Throws while another hold has it. */
78
+ /** Takes the clock from the host. See `Hold`. Throws while another hold has it. */
72
79
  hold(): Hold;
73
80
  }
81
+ /**
82
+ * What history a watched mix keeps, as `Watched.history` reports it.
83
+ *
84
+ * @category mix
85
+ */
86
+ export interface WatchedHistory {
87
+ /** How much stays in memory, ms of mix time. */
88
+ readonly ms: number;
89
+ /** Whether it records a tape. */
90
+ readonly tape: boolean;
91
+ /** Whether the registry's `history` gave it. */
92
+ readonly added: boolean;
93
+ }
94
+ /**
95
+ * What a voice was cued with that its handle does not carry. `Watched.voice` reports it.
96
+ *
97
+ * @category mix
98
+ */
99
+ export interface WatchedVoice {
100
+ readonly name: string | undefined;
101
+ readonly tags: readonly string[];
102
+ /**
103
+ * What it is bounded to, by `within` or an `around` weight. It is a circle, read live, or the
104
+ * host's finder; undefined for none.
105
+ */
106
+ readonly within: Readonly<Circle> | Finder | undefined;
107
+ }
74
108
  /**
75
109
  * What the host read in one frame.
76
110
  *
@@ -83,19 +117,33 @@ export interface WatchedFrame<I, O> {
83
117
  /** The host's timestamp at the frame's sync. */
84
118
  timestamp: number;
85
119
  /** Each probe, in order, with a copy of the pose it returned. */
86
- probed: {
87
- subject: I;
88
- pose: O;
89
- }[];
120
+ probed: Probed<I, O>[];
90
121
  /** Each pull, with copies of the columns it wrote. */
91
- pulled: {
92
- subjects: I[];
93
- columns: Record<string, Float64Array>;
94
- }[];
122
+ pulled: Pulled<I>[];
95
123
  /** What `drain` handed the host. */
96
124
  sent: Sent<I, unknown>[];
97
125
  voices: Handle<I>[];
98
126
  }
127
+ /**
128
+ * One probe a watched frame recorded.
129
+ *
130
+ * @category mix
131
+ */
132
+ export interface Probed<I, O> {
133
+ subject: I;
134
+ /** A copy of the pose the probe returned. */
135
+ pose: O;
136
+ }
137
+ /**
138
+ * One pull a watched frame recorded.
139
+ *
140
+ * @category mix
141
+ */
142
+ export interface Pulled<I> {
143
+ subjects: I[];
144
+ /** Copies of the columns it wrote, by channel. */
145
+ columns: Record<string, Float64Array>;
146
+ }
99
147
  /**
100
148
  * A mix's clock in a debugger's hands, and every mix's on the same transport. The host's `sync`
101
149
  * still begins each frame, but the frame's gap counts at `rate`, so the mix moves at `rate` times
@@ -109,7 +157,7 @@ export interface Hold {
109
157
  readonly mixes: Mix<unknown, unknown>[];
110
158
  /** The mix clock. */
111
159
  readonly now: number;
112
- /** The earliest mix time `seek` reaches: `now` without history and a tape. */
160
+ /** The earliest mix time `seek` reaches. It is `now` without history and a tape. */
113
161
  readonly reach: number;
114
162
  /** How much of each of the host's frames the held mix plays; 0 pauses it. Starts at 0. */
115
163
  rate: number;
@@ -118,9 +166,9 @@ export interface Hold {
118
166
  /** Plays the mixes `ms` of mix time further on, in one frame. */
119
167
  step(ms: number): void;
120
168
  /**
121
- * Gives the clock back: the host's next sync carries on from where the hold left the mix, and
122
- * its clock calls made meanwhile are made, in order. A call made while the tape holds later
123
- * calls acts on the mix but is not recorded, so those calls still play.
169
+ * Gives the clock back to the host. The host's next sync carries on from where the hold left the
170
+ * mix, and its clock calls made meanwhile are made, in order. A call made while the tape holds
171
+ * later calls acts on the mix but is not recorded, so those calls still play.
124
172
  */
125
173
  free(): void;
126
174
  }
package/dist/types.d.ts CHANGED
@@ -2,8 +2,10 @@ export type * from './types/arc.js';
2
2
  export type * from './types/channel.js';
3
3
  export type * from './types/history.js';
4
4
  export type * from './types/mix.js';
5
+ export type * from './types/motion.js';
5
6
  export type * from './types/patch.js';
6
7
  export type * from './types/place.js';
8
+ export type * from './types/trace.js';
7
9
  export type * from './types/transport.js';
8
10
  export type * from './types/voice.js';
9
11
  export type * from './types/watch.js';
@@ -3,3 +3,5 @@ import type { Voice } from './voice.js';
3
3
  export declare function unreached<I, O>(voice: Voice<I, O>, slot: number): boolean;
4
4
  /** Marks or clears the subject numbered `slot` as one the voice does not reach. */
5
5
  export declare function unreach<I, O>(voice: Voice<I, O>, slot: number, on: boolean): void;
6
+ /** The subject numbered `from` is numbered `to` now, a number let go of: its mark moves along. */
7
+ export declare function reunreach<I, O>(voice: Voice<I, O>, from: number, to: number): void;
package/dist/unreached.js CHANGED
@@ -22,3 +22,10 @@ export function unreach(voice, slot, on) {
22
22
  const bit = 1 << (slot & 31);
23
23
  bits[word] = on ? bits[word] | bit : bits[word] & ~bit;
24
24
  }
25
+ /** The subject numbered `from` is numbered `to` now, a number let go of: its mark moves along. */
26
+ export function reunreach(voice, from, to) {
27
+ if (!unreached(voice, from))
28
+ return;
29
+ unreach(voice, to, true);
30
+ unreach(voice, from, false);
31
+ }
package/dist/voice.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { Arc } from './arcs.js';
2
2
  import type { Blend } from './blend.js';
3
+ import type { Bound } from './bound.js';
3
4
  import { type Clock } from './clock.js';
4
5
  import { type Curve } from './easing.js';
5
6
  import type { Leavings } from './leavings.js';
@@ -8,151 +9,13 @@ import { Named } from './named.js';
8
9
  import type { Past } from './origin.js';
9
10
  import { Holding } from './owner.js';
10
11
  import { type Built, intosOf, type Scratch } from './patch.js';
11
- import type { Stamp } from './restored.js';
12
+ import type { Seat } from './seat.js';
13
+ import { VoiceSetting } from './setting.js';
12
14
  import type { Fitting } from './spans.js';
13
15
  import { Store } from './store.js';
14
- import type { Anchor, Channel, FadeSpec, Handle, Patch, Setting, VoiceSpec } from './types.js';
15
- /**
16
- * A subject's record as it left a voice, faded out of it or dropped: the mix time and frame it
17
- * left at, and `sync` where it shows in its own frame: left in the frame's sync, or before anything
18
- * read the mix that frame.
19
- */
20
- export interface Left<I> {
21
- subject: I;
22
- at: number;
23
- seq: number;
24
- sync: boolean;
25
- held: Subject<unknown>;
26
- /**
27
- * The voice's fade of the subject as it stood on leaving, which a seek or read back to before
28
- * puts back: its ramp out, while running, or its having faded out.
29
- */
30
- ramp: Parting | null;
31
- parted: Parted | null;
32
- }
33
- /** A subject's ramp out of one voice: when it began, how long it runs, and the frame it was asked in. */
34
- export interface Parting {
35
- at: number;
36
- over: number;
37
- seq: number;
38
- }
39
- export interface Parted {
40
- at: number;
41
- seq: number;
42
- ramp: Parting | undefined;
43
- }
16
+ import type { Parted, Parting, Snap, Subject } from './subject.js';
17
+ import type { Anchor, Channel, FadeSpec, Handle, Patch, VoiceSpec } from './types.js';
44
18
  export declare const none: readonly string[];
45
- /**
46
- * What a copy of a record keeps for the seeks that put a record back from it: the copy's state
47
- * stepped on to `ticks`, as it stood near `at`, a moment one of them sought.
48
- */
49
- export interface Ahead<S> {
50
- /** The record the last seek put back, and the moment it sought, until that record steps. */
51
- put: Subject<S> | null;
52
- sought: number;
53
- /** The `since` of the record put back, which the steps in `state` were counted from. */
54
- since: number;
55
- at: number;
56
- ticks: number;
57
- /** Undefined until a record has stepped on from the copy. */
58
- state: S | undefined;
59
- /**
60
- * The state the record a seek replaced had stepped on to from the copy, at `leftTicks`; undefined
61
- * until one had.
62
- */
63
- leftTicks: number;
64
- left: S | undefined;
65
- }
66
- /** A copy of a record kept under `history`, by the mix time and frame it was taken at. */
67
- export interface Snap<S> {
68
- at: number;
69
- seq: number;
70
- held: Subject<S>;
71
- ahead?: Ahead<S>;
72
- /** Set where another record's list holds it too, so a seek cutting it from one lets go of it. */
73
- shared?: true;
74
- }
75
- /** Everything one voice holds for one subject, chained through the next voice that reaches it. */
76
- export interface Subject<S> {
77
- /** The voice's `target` answer, fixed on first sight. */
78
- reaches: boolean;
79
- /** The voice's `stagger` answer in voice ms, fixed on first sight. */
80
- delay: number;
81
- /** The mix timestamp this subject's delay ran out at, which its step grid counts from. */
82
- since: number;
83
- /** The mix timestamp its fade in counts from: `since`, or earlier for a voice freezing before. */
84
- shown: number;
85
- /** The weight this voice gave this subject the last frame it was probed, 0 where it gave none. */
86
- weight: number;
87
- /** Left at rest during a handover, so it stops contributing. */
88
- rested: boolean;
89
- /** The mix time it rested at, which a seek reads to know whether it had by then; NaN until it does. */
90
- restedAt: number;
91
- /**
92
- * Per written channel, whether a rest-less contribution is on: 0 unknown, 1 on, 2 off; null until
93
- * a rest-less channel asks.
94
- */
95
- bands: Uint8Array | null;
96
- state: S;
97
- /** The `now` this subject last caught up to, for this voice. */
98
- stepped: number;
99
- /** Under `stepMs`, how many intervals have run since `since`; `stepped` is where that lands. */
100
- ticks: number;
101
- /** The `now` this subject was last probed at, so twelve probes in a frame step once. */
102
- probed: number;
103
- /** The last delta computed this frame, handed back to a repeat probe unchanged. */
104
- delta: Record<string, unknown> | null;
105
- /** The phase `delta` was read at, to read it again once its voice's scratch has moved on. */
106
- phase: number;
107
- /** The voice's `seeks` when `delta` was read, so a seek later in the frame reads it again. */
108
- seeks: number;
109
- /** The voice's `rebuilds` when its state was last made, so a seek's rebuild reaches it once. */
110
- rebuilt: number;
111
- /** Its kept signal state was stepped on in one go from a copy history kept, by a seek. */
112
- copied: boolean;
113
- /** Stop 0 for a `from: 'current'` voice, taken the first frame this subject is seen. */
114
- base?: Record<string, unknown>;
115
- /** The pose's velocity per channel at that moment, units per ms, so the first segment leaves at it. */
116
- slope?: Record<string, unknown>;
117
- /** What `setting.keep` holds for this voice and subject, by owner; null until it holds any. */
118
- kept: Map<object, unknown> | null;
119
- /** Kept state a history store gave back, which each owner takes in turn on its first keep. */
120
- unkept?: unknown[];
121
- /** Under `history`, copies of this record by the mix time they were taken, oldest first. */
122
- snaps?: Snap<S>[];
123
- /** In a projection: where this record started from, and whether nothing known could be. */
124
- from?: number;
125
- unknown?: boolean;
126
- /** Under `history` with `inputs`, what an input weight signal read for this subject, when it changed. */
127
- inputs?: {
128
- at: number;
129
- seq: number;
130
- value: number;
131
- }[];
132
- /** In a projection reading back: that record, to read in place of the signal. */
133
- replay?: {
134
- at: number;
135
- value: number;
136
- }[];
137
- /** The voice this is the record of; null on a subject's stub. */
138
- voice: object | null;
139
- /** The next voice's record for this subject, in voice order, among those that reach it. */
140
- next: Subject<unknown> | null;
141
- /**
142
- * On the first record, which the mix keeps per subject so a probe makes one lookup: the `version`
143
- * the chain was linked at, and per locus, by channel slot, whether a rest-less contribution is on:
144
- * 0 never decided, 1 on, 2 off.
145
- */
146
- version: number;
147
- loci: Map<string, Uint8Array> | null;
148
- /** On the first record: the number lanes index this subject by, -1 without one. */
149
- slot: number;
150
- /** A motion voice's number for this subject in its patch, good while `freed` is the patch's own. */
151
- run: number;
152
- freed: number;
153
- /** In a voice's store once a seek has gone back, the stamp of the seek it was last put back or written at. */
154
- restored: Stamp | null;
155
- }
156
19
  /** A voice's clock, weight and fade from one mix time on, kept under `history`. */
157
20
  export interface Controls extends Clock {
158
21
  at: number;
@@ -239,6 +102,8 @@ export declare class Voice<I, O> {
239
102
  holder: Subject<unknown> | null;
240
103
  /** Whether its channels run as lanes this frame, so the general fold passes it by. */
241
104
  laned: boolean;
105
+ /** Its crowd row, while its channels run in a crowd. */
106
+ seat: Seat<I, O> | null;
242
107
  /**
243
108
  * How many times it has been sought or touched, so a delta read before either is not handed out
244
109
  * after it.
@@ -301,7 +166,7 @@ export declare class Voice<I, O> {
301
166
  /** The clocks it ran on before its current one, where a subject's origin may lie; null for none. */
302
167
  clocks: Past[] | null;
303
168
  /** Reused for every call this voice makes, so it is valid only during the call. */
304
- readonly setting: Setting<unknown>;
169
+ readonly setting: VoiceSetting<I, O>;
305
170
  /** Every subject this voice has been asked about, so a handover knows when it is finished. */
306
171
  seen: number;
307
172
  /** The longest stagger of any subject seen, so a finite loop waits for the last of them. */
@@ -363,6 +228,8 @@ export declare class Voice<I, O> {
363
228
  unreachedBits: Uint32Array | null;
364
229
  /** Whether one record stands for every subject: see `shares` in `everyone.ts`. */
365
230
  sharing: boolean;
231
+ /** Where its signal is declared 0, its cue's `within`, so it is not read there: see `bound.ts`. */
232
+ bound: Bound<I, O> | null;
366
233
  /** The frames it has played sharing it. */
367
234
  shared: number;
368
235
  /** That record, null until a chain first asks and once the voice shares no more. */
package/dist/voice.js CHANGED
@@ -4,87 +4,33 @@ import { motionOf } from './motion.js';
4
4
  import { Named } from './named.js';
5
5
  import { childPlayed, Holding, mixTime, ownedElapsed, ownerPatch, ownerReading, turnedBack, } from './owner.js';
6
6
  import { builtOf, intosOf } from './patch.js';
7
- import { reading } from './reading.js';
8
- import { Store } from './store.js';
7
+ import { keeping, VoiceSetting } from './setting.js';
8
+ import { Filled, Store } from './store.js';
9
9
  import { warnOnce } from './warn.js';
10
10
  export const none = Object.freeze([]);
11
- /**
12
- * A voice's `setting.keep`, one per voice: a setting is valid only during its call, so the state
13
- * goes on the record of the subject being called for, `keepOn`, or on the voice before any is.
14
- */
15
- function keeping(voice) {
16
- return function keep(owner, init) {
17
- const held = voice.keepOn;
18
- let kept = held === null ? voice.ownKept : held.kept;
19
- if (kept === null) {
20
- kept = new Map();
21
- if (held === null)
22
- voice.ownKept = kept;
23
- else
24
- held.kept = kept;
25
- }
26
- if (kept.has(owner))
27
- return kept.get(owner);
28
- const unkept = held?.unkept;
29
- const made = unkept === undefined ? init() : unkept.shift();
30
- if (unkept?.length === 0 && held !== null)
31
- held.unkept = undefined;
32
- kept.set(owner, made);
33
- reading.kept++;
34
- return made;
35
- };
36
- }
37
- /**
38
- * A voice's setting. `keep` is made on first read: most patches keep no state, and a closure and
39
- * its context per voice cost a one-subject tween voice about 100 bytes.
40
- */
41
- class VoiceSetting {
42
- host;
43
- send;
44
- voice;
45
- timestamp = 0;
46
- dt = 0;
47
- elapsed = 0;
48
- pass = 0;
49
- weight = 0;
50
- state = undefined;
51
- kept = null;
52
- constructor(host, send, voice) {
53
- this.host = host;
54
- this.send = send;
55
- this.voice = voice;
56
- }
57
- get keep() {
58
- if (this.kept === null)
59
- this.kept = keeping(this.voice);
60
- return this.kept;
61
- }
11
+ const noSend = () => { };
12
+ /** By a mix's `slotOf`, the slots and lerps of a patch writing one channel, shared by its voices. */
13
+ const writings = new WeakMap();
14
+ function writing(writes, slotOf, channels) {
15
+ if (writes.length !== 1)
16
+ return written(writes, slotOf, channels);
17
+ let known = writings.get(slotOf);
18
+ if (known === undefined) {
19
+ known = new Map();
20
+ writings.set(slotOf, known);
21
+ }
22
+ const key = writes[0];
23
+ let found = known.get(key);
24
+ if (found === undefined) {
25
+ found = written(writes, slotOf, channels);
26
+ known.set(key, found);
27
+ }
28
+ return found;
62
29
  }
63
- /** A store that fills a missing entry from `make` on first ask. */
64
- class Filled extends Store {
65
- make;
66
- /** Keys deleted here, which are made afresh by whoever asks and never again by `make`. */
67
- gone = null;
68
- constructor(make) {
69
- super();
70
- this.make = make;
71
- }
72
- get(key) {
73
- let v = super.get(key);
74
- if (v === undefined && this.gone?.get(key) !== true) {
75
- v = this.make(key);
76
- if (v !== undefined)
77
- super.set(key, v);
78
- }
79
- return v;
80
- }
81
- delete(key) {
82
- super.delete(key);
83
- this.gone ??= new Store();
84
- this.gone.set(key, true);
85
- }
30
+ function written(writes, slotOf, channels) {
31
+ const slots = writes.map((k) => slotOf.get(k));
32
+ return { slots, lerps: slots.map((slot) => channels[slot].lerp) };
86
33
  }
87
- const noSend = () => { };
88
34
  export class Voice {
89
35
  id;
90
36
  spec;
@@ -126,6 +72,8 @@ export class Voice {
126
72
  holder = null;
127
73
  /** Whether its channels run as lanes this frame, so the general fold passes it by. */
128
74
  laned = false;
75
+ /** Its crowd row, while its channels run in a crowd. */
76
+ seat = null;
129
77
  /**
130
78
  * How many times it has been sought or touched, so a delta read before either is not handed out
131
79
  * after it.
@@ -251,6 +199,8 @@ export class Voice {
251
199
  unreachedBits = null;
252
200
  /** Whether one record stands for every subject: see `shares` in `everyone.ts`. */
253
201
  sharing = false;
202
+ /** Where its signal is declared 0, its cue's `within`, so it is not read there: see `bound.ts`. */
203
+ bound = null;
254
204
  /** The frames it has played sharing it. */
255
205
  shared = 0;
256
206
  /** That record, null until a chain first asks and once the voice shares no more. */
@@ -410,8 +360,8 @@ export class Voice {
410
360
  this.dueToken++;
411
361
  this.early = null;
412
362
  this.seen = 0;
413
- this.latest = 0;
414
- this.soonest = 0;
363
+ this.latest = this.named?.latest ?? 0;
364
+ this.soonest = this.named?.soonest ?? 0;
415
365
  this.restedCount = 0;
416
366
  this.cuedAt = Number.NEGATIVE_INFINITY;
417
367
  this.cuedSeq = Number.NEGATIVE_INFINITY;
@@ -443,10 +393,14 @@ export class Voice {
443
393
  this.start = start;
444
394
  this.owner = owner;
445
395
  this.holding = patch === ownerPatch ? new Holding() : null;
446
- this.slots = patch.writes.map((k) => slotOf.get(k));
447
- this.named = spec.subjects ? new Named(spec.subjects) : null;
396
+ const wrote = writing(patch.writes, slotOf, channels);
397
+ this.slots = wrote.slots;
398
+ this.named = spec.subjects ? new Named(spec.subjects, spec.stagger) : null;
399
+ // Every subject's stagger is known at cue, so a finite loop ends by them all, read or not.
400
+ this.latest = this.named?.latest ?? 0;
401
+ this.soonest = this.named?.soonest ?? 0;
448
402
  this.built = patch.form === 'keys' && patch.keys ? builtOf(patch) : null;
449
- this.lerps = this.slots.map((slot) => channels[slot].lerp);
403
+ this.lerps = wrote.lerps;
450
404
  this.intos = this.built
451
405
  ? intosOf(this.built, this.slots.map((slot) => channels[slot]))
452
406
  : undefined;
@@ -618,6 +572,7 @@ export class Voice {
618
572
  v.scratch = [];
619
573
  v.holder = null;
620
574
  v.laned = false;
575
+ v.seat = null;
621
576
  v.log = null;
622
577
  v.early = null;
623
578
  if (controls)
package/dist/warn.d.ts CHANGED
@@ -1,2 +1,4 @@
1
+ /** Whether this is a production build: a bundler's `NODE_ENV`, or Node's; false where neither is. */
2
+ export declare function production(): boolean;
1
3
  /** Warns outside production, once for each `about`. */
2
4
  export declare function warnOnce(about: object, message: string): void;
package/dist/warn.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Whether this is a production build: a bundler's `NODE_ENV`, or Node's; false where neither is. */
2
- function production() {
2
+ export function production() {
3
3
  try {
4
4
  return process.env.NODE_ENV === 'production';
5
5
  }
package/dist/watch.d.ts CHANGED
@@ -2,8 +2,8 @@ import type { Mixer } from './mixer.js';
2
2
  import type { MixOptions, Registry } from './types.js';
3
3
  type Any = Mixer<unknown, unknown>;
4
4
  /**
5
- * Every mix made on the page, shared by every copy of blits loaded: what a debugger reads to find
6
- * them. See `Registry`.
5
+ * The registry a debugger reads to find every mix made on the page. It is shared by every copy of
6
+ * blits loaded. See `Registry`.
7
7
  *
8
8
  * @category mix
9
9
  */
package/dist/watch.js CHANGED
@@ -4,8 +4,8 @@ import { hostAhead } from './tape.js';
4
4
  const KEY = Symbol.for('@msb235/blits/watch/1');
5
5
  const slot = globalThis;
6
6
  /**
7
- * Every mix made on the page, shared by every copy of blits loaded: what a debugger reads to find
8
- * them. See `Registry`.
7
+ * The registry a debugger reads to find every mix made on the page. It is shared by every copy of
8
+ * blits loaded. See `Registry`.
9
9
  *
10
10
  * @category mix
11
11
  */
@@ -90,7 +90,9 @@ export function register(mix) {
90
90
  channels: mix.names,
91
91
  voice: (handle) => {
92
92
  const v = mix.cued.find((c) => c.handle === handle) ?? mix.gone.find((c) => c.handle === handle);
93
- return v === undefined ? undefined : { name: v.spec.name, tags: v.spec.tags ?? [] };
93
+ return v === undefined
94
+ ? undefined
95
+ : { name: v.spec.name, tags: v.spec.tags ?? [], within: v.bound?.area };
94
96
  },
95
97
  label: (subject) => labelOf(mix, subject),
96
98
  watch: (fn) => tap(mix, fn),
package/dist/wave.d.ts CHANGED
@@ -11,7 +11,7 @@ export type WaveShape = 'sine' | 'triangle' | 'saw' | 'square';
11
11
  * @category patch
12
12
  */
13
13
  export interface WaveOptions<O> {
14
- /** Default 'sine'. */
14
+ /** The wave's form: 'sine', 'triangle', 'saw' or 'square'. Default 'sine'. */
15
15
  shape?: WaveShape;
16
16
  /** Whole or fractional cycles per pass of the patch's duration. Default 1. */
17
17
  cycles?: number;
@@ -27,13 +27,14 @@ export interface WaveOptions<O> {
27
27
  kit?: Partial<Kit<O>>;
28
28
  }
29
29
  /**
30
- * The unit wave: -1..1 at `x` cycles, starting at 0 and rising (square: +1 for the first half).
30
+ * The unit wave's value at `x` cycles, from -1 to 1. It starts at 0 and rises (square: +1 for the
31
+ * first half).
31
32
  *
32
33
  * @category patch
33
34
  */
34
35
  export declare function waveAt(shape: WaveShape, x: number): number;
35
36
  /**
36
- * A periodic swing on each channel `depth` names, around its rest: an LFO for a bob, a sway or a
37
+ * A periodic swing on each channel `depth` names, around its rest. An LFO for a bob, a sway or a
37
38
  * pulse. An `fn` patch that keeps its options on `patch.wave`, for an engine reading the wave
38
39
  * rather than calling it.
39
40
  *