@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/sample.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { unbareLane } from './bare.js';
2
2
  import { foldNumber } from './channels.js';
3
3
  import { clampWeight, passAt, same, weighed } from './clock.js';
4
+ import { keepProbed } from './lane.js';
4
5
  import { absent } from './numbers.js';
5
6
  import { reading } from './reading.js';
6
7
  import { between, left, progress } from './tweened.js';
@@ -50,6 +51,11 @@ export function runMotion(lanes, lane, run) {
50
51
  }
51
52
  const own = ease !== undefined && !signal && run.watcher === lanes && run.watchId === voice.id;
52
53
  const folds = n === ch.axes && ch.plain;
54
+ // The weight a voice with a fade and no subject ramps gives every subject at one fade: worked
55
+ // out again for a zero, whose sign a hit by `===` would not keep.
56
+ let fadeOf = Number.NaN;
57
+ let baseOf = Number.NaN;
58
+ let faded = 0;
53
59
  for (let p = 0; p < list.length; p++) {
54
60
  // Its signal just made kept state: no further call this fill, the general path makes them.
55
61
  if (signal && voice.keeping)
@@ -57,8 +63,7 @@ export function runMotion(lanes, lane, run) {
57
63
  const slot = list[p];
58
64
  const o = p * 8 /* Row.STRIDE */;
59
65
  const q = slot * 6 /* Per.SLOT */;
60
- if (per[q + 5 /* Per.LANE_FILL */] === fills - 1)
61
- data[o + 3 /* Row.PROBED */] = data[o + 2 /* Row.WEIGHT */];
66
+ keepProbed(data, o, per, q, fills);
62
67
  if (lately >= 0 && lanes.unread(slot)) {
63
68
  unbareLane(lane, p);
64
69
  continue;
@@ -89,8 +94,20 @@ export function runMotion(lanes, lane, run) {
89
94
  }
90
95
  w = arg[0 /* Arg.WEIGHT */];
91
96
  }
92
- else
93
- w = flat && !parts ? whole : weighed(voice.weight, lane.fade, lanes.parting(voice, slot));
97
+ else if (parts)
98
+ w = weighed(voice.weight, lane.fade, lanes.parting(voice, slot));
99
+ else if (flat)
100
+ w = whole;
101
+ else {
102
+ const fade = lane.fade;
103
+ const base = voice.weight;
104
+ if (fade !== fadeOf || base !== baseOf || fade === 0 || base === 0) {
105
+ fadeOf = fade;
106
+ baseOf = base;
107
+ faded = weighed(base, fade, 1);
108
+ }
109
+ w = faded;
110
+ }
94
111
  data[o + 2 /* Row.WEIGHT */] = w;
95
112
  if (data[o + 7 /* Row.MET */] === 0) {
96
113
  if (Number.isNaN(records[p].probed)) {
@@ -101,7 +118,16 @@ export function runMotion(lanes, lane, run) {
101
118
  data[o + 7 /* Row.MET */] = 1;
102
119
  }
103
120
  const ms = data[o + 4 /* Row.MSLOT */];
104
- const b = ms < 0 ? -1 : run.bareAt(ms);
121
+ // `bareAt` and `scalar` written out: V8 leaves them and `base` out of line here.
122
+ let b = -1;
123
+ let scalar = false;
124
+ if (ms >= 0) {
125
+ b = 5 /* Law.HEAD */ + ms * run.stride;
126
+ const f = run.runs[b + 1];
127
+ if ((f & (2 /* RunFlag.PENDING */ | 4 /* RunFlag.OLDER */)) !== 0)
128
+ b = -1;
129
+ scalar = (f & 1 /* RunFlag.SCALAR */) !== 0;
130
+ }
105
131
  if (b < 0 ||
106
132
  (probed &&
107
133
  (per[q + 3 /* Per.LANE_PROBE */] > from ||
@@ -113,7 +139,7 @@ export function runMotion(lanes, lane, run) {
113
139
  continue;
114
140
  }
115
141
  const runs = run.runs;
116
- if (own && folds && run.scalar(ms) === ch.scalar) {
142
+ if (own && folds && scalar === ch.scalar) {
117
143
  if (data[o + 5 /* Row.SAMPLED */] !== -2 /* Sampled.BARE */) {
118
144
  data[o + 5 /* Row.SAMPLED */] = -2 /* Sampled.BARE */;
119
145
  lane.deltas[p] = null;
@@ -147,7 +173,7 @@ export function runMotion(lanes, lane, run) {
147
173
  data[o + 6 /* Row.SEEKS */] = seeks;
148
174
  if (w <= 0)
149
175
  continue;
150
- if (folds && run.scalar(ms) === ch.scalar) {
176
+ if (folds && scalar === ch.scalar) {
151
177
  const values = ch.values;
152
178
  const base = slot * n;
153
179
  for (let a = 0; a < n; a++)
package/dist/seat.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import type { Crowd } from './crowd.js';
2
+ import type { Voice } from './voice.js';
3
+ /**
4
+ * A crowd voice's row, held on the voice and by its id in its lanes' `seats`: one place to read
5
+ * both its crowd and its row, where two maps by id were two cache misses a stop at 40,000 voices.
6
+ * `lanes` is the lanes it was given in, null once they let it go.
7
+ */
8
+ export declare class Seat<I, O> {
9
+ lanes: object | null;
10
+ readonly crowd: Crowd<I, O>;
11
+ row: number;
12
+ constructor(lanes: object | null, crowd: Crowd<I, O>, row: number);
13
+ }
14
+ /** `voice`'s seat in `lanes`, null where it has none there. */
15
+ export declare function seatOf<I, O>(lanes: object, voice: Voice<I, O>): Seat<I, O> | null;
16
+ /** Whether row `p` of `c` is `voice`'s seat in `lanes`, not a row it has left. */
17
+ export declare function seated<I, O>(lanes: object, voice: Voice<I, O>, c: Crowd<I, O>, p: number): boolean;
package/dist/seat.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * A crowd voice's row, held on the voice and by its id in its lanes' `seats`: one place to read
3
+ * both its crowd and its row, where two maps by id were two cache misses a stop at 40,000 voices.
4
+ * `lanes` is the lanes it was given in, null once they let it go.
5
+ */
6
+ export class Seat {
7
+ lanes;
8
+ crowd;
9
+ row;
10
+ constructor(lanes, crowd, row) {
11
+ this.lanes = lanes;
12
+ this.crowd = crowd;
13
+ this.row = row;
14
+ }
15
+ }
16
+ /** `voice`'s seat in `lanes`, null where it has none there. */
17
+ export function seatOf(lanes, voice) {
18
+ const s = voice.seat;
19
+ return s !== null && s.lanes === lanes ? s : null;
20
+ }
21
+ /** Whether row `p` of `c` is `voice`'s seat in `lanes`, not a row it has left. */
22
+ export function seated(lanes, voice, c, p) {
23
+ const s = seatOf(lanes, voice);
24
+ return s !== null && s.crowd === c && s.row === p;
25
+ }
package/dist/seek.js CHANGED
@@ -9,6 +9,7 @@ import { move, nextFrame } from './move.js';
9
9
  import { ownerReading } from './owner.js';
10
10
  import { place } from './place.js';
11
11
  import { unpull } from './pull.js';
12
+ import { isRested } from './record.js';
12
13
  import { Restored } from './restored.js';
13
14
  import { recount } from './roster.js';
14
15
  import { refitAll } from './spans.js';
@@ -113,8 +114,7 @@ function restore(mix, voice, live, at, every, mine) {
113
114
  * subject may have rested and moved on since, so a probe at `t` would not find it at rest again.
114
115
  */
115
116
  function restedBy(h, live, t) {
116
- h.rested = live.restedAt <= t;
117
- h.restedAt = h.rested ? live.restedAt : Number.NaN;
117
+ h.restedAt = live.restedAt <= t ? live.restedAt : Number.NaN;
118
118
  }
119
119
  /** Cuts a record's list of copies to its first `kept`, keeping those no other list holds as spares. */
120
120
  function trim(voice, snaps, kept) {
@@ -196,7 +196,7 @@ function restoreVoice(mix, voice, t, seq) {
196
196
  return h;
197
197
  if (h.reaches)
198
198
  voice.seen++;
199
- if (h.rested)
199
+ if (isRested(h))
200
200
  voice.restedCount++;
201
201
  if (!(h.since <= restored.top)) {
202
202
  voice.early ??= [];
@@ -358,7 +358,7 @@ function back(mix, t, seq, on) {
358
358
  if (voice.parts !== null || voice.parted !== null)
359
359
  mix.parters.add(voice);
360
360
  mix.relinks++;
361
- mix.pose = new Store();
361
+ mix.lastPoses = new Store();
362
362
  if (mix.restStamps !== null)
363
363
  mix.restStamps = new Store();
364
364
  unpull(mix);
@@ -401,7 +401,7 @@ function back(mix, t, seq, on) {
401
401
  mix.pins.set(voice, voice.pinned);
402
402
  }
403
403
  refitAll(mix);
404
- mix.due = [];
404
+ mix.due.clear();
405
405
  for (const voice of kept)
406
406
  schedule(mix, voice);
407
407
  mix.stir();
@@ -0,0 +1,27 @@
1
+ import type { Setting } from './types.js';
2
+ import type { Voice } from './voice.js';
3
+ /**
4
+ * A voice's `setting.keep`, one per voice: a setting is valid only during its call, so the state
5
+ * goes on the record of the subject being called for, `keepOn`, or on the voice before any is.
6
+ */
7
+ export declare function keeping<I, O>(voice: Voice<I, O>): Setting['keep'];
8
+ /**
9
+ * A voice's setting. `keep` is made on first read: most patches keep no state, and a closure and
10
+ * its context per voice cost a one-subject tween voice about 100 bytes.
11
+ */
12
+ export declare class VoiceSetting<I, O> implements Setting<unknown> {
13
+ host: unknown;
14
+ readonly send: (event: unknown) => void;
15
+ private readonly voice;
16
+ timestamp: number;
17
+ dt: number;
18
+ elapsed: number;
19
+ pass: number;
20
+ weight: number;
21
+ state: unknown;
22
+ /** The `Arg.MARK` a lane last filled `timestamp`, `elapsed` and `pass` under; 0 once anything else writes them. */
23
+ mark: number;
24
+ private kept;
25
+ constructor(host: unknown, send: (event: unknown) => void, voice: Voice<I, O>);
26
+ get keep(): Setting['keep'];
27
+ }
@@ -0,0 +1,55 @@
1
+ import { reading } from './reading.js';
2
+ /**
3
+ * A voice's `setting.keep`, one per voice: a setting is valid only during its call, so the state
4
+ * goes on the record of the subject being called for, `keepOn`, or on the voice before any is.
5
+ */
6
+ export function keeping(voice) {
7
+ return function keep(owner, init) {
8
+ const held = voice.keepOn;
9
+ let kept = held === null ? voice.ownKept : held.kept;
10
+ if (kept === null) {
11
+ kept = new Map();
12
+ if (held === null)
13
+ voice.ownKept = kept;
14
+ else
15
+ held.kept = kept;
16
+ }
17
+ if (kept.has(owner))
18
+ return kept.get(owner);
19
+ const unkept = held?.unkept;
20
+ const made = unkept === undefined ? init() : unkept.shift();
21
+ if (unkept?.length === 0 && held !== null)
22
+ held.unkept = undefined;
23
+ kept.set(owner, made);
24
+ reading.kept++;
25
+ return made;
26
+ };
27
+ }
28
+ /**
29
+ * A voice's setting. `keep` is made on first read: most patches keep no state, and a closure and
30
+ * its context per voice cost a one-subject tween voice about 100 bytes.
31
+ */
32
+ export class VoiceSetting {
33
+ host;
34
+ send;
35
+ voice;
36
+ timestamp = 0;
37
+ dt = 0;
38
+ elapsed = 0;
39
+ pass = 0;
40
+ weight = 0;
41
+ state = undefined;
42
+ /** The `Arg.MARK` a lane last filled `timestamp`, `elapsed` and `pass` under; 0 once anything else writes them. */
43
+ mark = 0;
44
+ kept = null;
45
+ constructor(host, send, voice) {
46
+ this.host = host;
47
+ this.send = send;
48
+ this.voice = voice;
49
+ }
50
+ get keep() {
51
+ if (this.kept === null)
52
+ this.kept = keeping(this.voice);
53
+ return this.kept;
54
+ }
55
+ }
package/dist/shared.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * The package version, for a fact whose shape follows the code too closely to version by hand:
3
3
  * copies of different releases keep apart. `test/copies.test.ts` holds it to `package.json`.
4
4
  */
5
- export declare const release = "0.14.0";
5
+ export declare const release = "0.15.1";
6
6
  /**
7
7
  * A WeakMap every copy of blits loaded in one realm shares, so a fact one copy records about a
8
8
  * patch or channel it made is read by another copy's mix. The name carries a version, bumped when
package/dist/shared.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * The package version, for a fact whose shape follows the code too closely to version by hand:
3
3
  * copies of different releases keep apart. `test/copies.test.ts` holds it to `package.json`.
4
4
  */
5
- export const release = '0.14.0';
5
+ export const release = '0.15.1';
6
6
  /**
7
7
  * A WeakMap every copy of blits loaded in one realm shares, so a fact one copy records about a
8
8
  * patch or channel it made is read by another copy's mix. The name carries a version, bumped when
package/dist/signals.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Setting, Signal } from './types.js';
1
+ import type { Band, Setting, Signal } from './types.js';
2
2
  /** Whether a change to an input signal reaches `reading.inputs`, so a lane fill can see it. */
3
3
  export declare const heard: (signal: object) => boolean;
4
4
  /**
@@ -10,15 +10,24 @@ export declare function peak<I, H = unknown>(...signals: readonly Signal<I, H>[]
10
10
  /** A blend member's share of its signal's read `k`: the members' stops sit evenly along 0..1. */
11
11
  export declare function shareOf(k: number, i: number, stops: number): number;
12
12
  /**
13
- * Member `i` of `n`'s share of `by`, as `mix.blend` weighs it: the hat of `by`'s read at the
14
- * member's stop, the stops spaced evenly from 0 to 1. Weights a voice outside a blend as one of
15
- * its members, such as one member played alone.
13
+ * Member `i` of `n`'s share of `by`, as `mix.blend` weighs it. It is the hat of `by`'s read at the
14
+ * member's stop, the stops spaced evenly from 0 to 1. Weights a voice outside a blend as one of its
15
+ * members, such as one member played alone.
16
16
  *
17
17
  * @category signal
18
18
  */
19
19
  export declare function share<I, H = unknown>(by: Signal<I, H>, i: number, n: number): Signal<I, H>;
20
20
  /**
21
- * Marks `read` as a signal driven from outside the clock, which a host's own signal has to be for
21
+ * The signal `input` returns, with `touch` to report a change.
22
+ *
23
+ * @category signal
24
+ */
25
+ export interface Input<I, H = unknown> extends Signal<I, H> {
26
+ /** Reports a change to what the signal reads. */
27
+ touch(): void;
28
+ }
29
+ /**
30
+ * Marks `read` as a signal driven from outside the clock. A host's own signal has to be one for
22
31
  * `assess` and a history's `inputs` to treat it as one. Two uses:
23
32
  *
24
33
  * `input(read)` is an input of the host's own: a pointer, a volume, a value in a store. The host
@@ -32,54 +41,77 @@ export declare function share<I, H = unknown>(by: Signal<I, H>, i: number, n: nu
32
41
  *
33
42
  * @category signal
34
43
  */
35
- export declare function input<I, H = unknown>(read: (subject: I, setting: Setting<void, H>) => number, of?: readonly Signal<I, H>[]): Signal<I, H> & {
36
- touch(): void;
37
- };
44
+ export declare function input<I, H = unknown>(read: (subject: I, setting: Setting<void, H>) => number, of?: readonly Signal<I, H>[]): Input<I, H>;
38
45
  /**
39
- * A signal the host writes: `input` over one number, with `set` reporting each change. klieg's
40
- * `level`.
46
+ * The signal `level` returns, with `set` to write it.
41
47
  *
42
48
  * @category signal
43
49
  */
44
- export declare function level<I, H = unknown>(initial?: number): Signal<I, H> & {
50
+ export interface Level<I, H = unknown> extends Signal<I, H> {
51
+ /** Writes the level, reporting the change; writing the value it holds already does nothing. */
45
52
  set(v: number): void;
46
- };
53
+ }
47
54
  /**
48
- * The package's linear decay primitive: follows its input at a rate limit, no faster than `riseMs` per
49
- * unit climbing or `fallMs` draining. The mix keeps its state per voice and subject, so two voices
50
- * handed the same slew each follow on their own. A subject starts on its input, or at `from`
51
- * unless `dt` is infinite.
55
+ * A signal the host writes. It is `input` over one number, with `set` reporting each change.
56
+ * klieg's `level`.
57
+ *
58
+ * @category signal
59
+ */
60
+ export declare function level<I, H = unknown>(initial?: number): Level<I, H>;
61
+ /**
62
+ * What `slew` takes.
52
63
  *
53
64
  * @category signal
54
65
  */
55
- export declare function slew<I, H = unknown>(of: Signal<I, H>, { riseMs, fallMs, from }: {
66
+ export interface SlewOptions {
67
+ /** Ms per unit climbing; 0 follows at once. Default 0. */
56
68
  riseMs?: number;
69
+ /** Ms per unit draining; 0 follows at once. Default 0. */
57
70
  fallMs?: number;
71
+ /** Where a subject starts, unless `dt` is infinite. Default: on its input. */
58
72
  from?: number;
59
- }): Signal<I, H>;
73
+ }
74
+ /**
75
+ * Follows its input at a rate limit, the package's linear decay primitive. No faster than `riseMs`
76
+ * per unit climbing or `fallMs` draining. The mix keeps its state per voice and subject, so two
77
+ * voices handed the same slew each follow on their own. A subject starts on its input, or at `from`
78
+ * unless `dt` is infinite.
79
+ *
80
+ * @category signal
81
+ */
82
+ export declare function slew<I, H = unknown>(of: Signal<I, H>, { riseMs, fallMs, from }: SlewOptions): Signal<I, H>;
60
83
  /**
61
- * The exponential counterpart to `slew`: closes the same fraction of the distance to its input in
62
- * every equal stretch of time, `1 − exp(−gap / ms)`, so it reads the same however the frames that
63
- * sample it are spaced while the input holds still. `riseMs` and `fallMs` are time constants: after
64
- * one, 63% of the way is covered. 0 follows at once. Within `floor` of its input it lands on it, so a
65
- * subject can reach rest; pass 0 to keep the curve exact. The mix keeps its state per voice and subject.
66
- * A subject starts on its input, or at `from` unless `dt` is infinite.
84
+ * What `lag` takes.
67
85
  *
68
86
  * @category signal
69
87
  */
70
- export declare function lag<I, H = unknown>(of: Signal<I, H>, { riseMs, fallMs, floor, from, }: {
88
+ export interface LagOptions {
89
+ /** The time constant climbing, ms; 0 follows at once. Default 0. */
71
90
  riseMs?: number;
91
+ /** The time constant draining, ms; 0 follows at once. Default 0. */
72
92
  fallMs?: number;
93
+ /** Within this of its input it lands on it; 0 keeps the curve exact. Default 1e-6. */
73
94
  floor?: number;
95
+ /** Where a subject starts, unless `dt` is infinite. Default: on its input. */
74
96
  from?: number;
75
- }): Signal<I, H>;
97
+ }
98
+ /**
99
+ * Closes the same fraction of the distance to its input in every equal stretch of time. The
100
+ * exponential counterpart to `slew`: the fraction is `1 − exp(−gap / ms)`, so it reads the same
101
+ * however the frames that sample it are spaced while the input holds still. `riseMs` and `fallMs`
102
+ * are time constants: after one, 63% of the way is covered. 0 follows at once. Within `floor` of
103
+ * its input it lands on it, so a subject can reach rest; pass 0 to keep the curve exact. The mix
104
+ * keeps its state per voice and subject. A subject starts on its input, or at `from` unless `dt` is
105
+ * infinite.
106
+ *
107
+ * @category signal
108
+ */
109
+ export declare function lag<I, H = unknown>(of: Signal<I, H>, { riseMs, fallMs, floor, from }: LagOptions): Signal<I, H>;
76
110
  /**
77
- * The other half of the pair: a band, not an edge. It drops at `off`, passes at `on`, and between
78
- * the two it holds whatever it last did, so an input resting on a threshold cannot chatter.
111
+ * Passes or drops its input across a band, not an edge. The other half of the pair. It drops at
112
+ * `off`, passes at `on`, and between the two it holds whatever it last did, so an input resting on
113
+ * a threshold cannot chatter.
79
114
  *
80
115
  * @category signal
81
116
  */
82
- export declare function gate<I, H = unknown>(of: Signal<I, H>, band: {
83
- on: number;
84
- off: number;
85
- } | number): Signal<I, H>;
117
+ export declare function gate<I, H = unknown>(of: Signal<I, H>, band: Band | number): Signal<I, H>;
package/dist/signals.js CHANGED
@@ -33,9 +33,9 @@ export function shareOf(k, i, stops) {
33
33
  return d >= 1 ? 0 : 1 - d;
34
34
  }
35
35
  /**
36
- * Member `i` of `n`'s share of `by`, as `mix.blend` weighs it: the hat of `by`'s read at the
37
- * member's stop, the stops spaced evenly from 0 to 1. Weights a voice outside a blend as one of
38
- * its members, such as one member played alone.
36
+ * Member `i` of `n`'s share of `by`, as `mix.blend` weighs it. It is the hat of `by`'s read at the
37
+ * member's stop, the stops spaced evenly from 0 to 1. Weights a voice outside a blend as one of its
38
+ * members, such as one member played alone.
39
39
  *
40
40
  * @category signal
41
41
  */
@@ -48,7 +48,7 @@ const touch = () => {
48
48
  reading.inputs++;
49
49
  };
50
50
  /**
51
- * Marks `read` as a signal driven from outside the clock, which a host's own signal has to be for
51
+ * Marks `read` as a signal driven from outside the clock. A host's own signal has to be one for
52
52
  * `assess` and a history's `inputs` to treat it as one. Two uses:
53
53
  *
54
54
  * `input(read)` is an input of the host's own: a pointer, a volume, a value in a store. The host
@@ -69,8 +69,8 @@ export function input(read, of) {
69
69
  return Object.assign(read, { input: true, touch });
70
70
  }
71
71
  /**
72
- * A signal the host writes: `input` over one number, with `set` reporting each change. klieg's
73
- * `level`.
72
+ * A signal the host writes. It is `input` over one number, with `set` reporting each change.
73
+ * klieg's `level`.
74
74
  *
75
75
  * @category signal
76
76
  */
@@ -93,9 +93,9 @@ const firstSight = (target, from, setting) => from === undefined || !Number.isFi
93
93
  ? { value: target, seen: Number.NaN }
94
94
  : { value: from, seen: setting.timestamp };
95
95
  /**
96
- * The package's linear decay primitive: follows its input at a rate limit, no faster than `riseMs` per
97
- * unit climbing or `fallMs` draining. The mix keeps its state per voice and subject, so two voices
98
- * handed the same slew each follow on their own. A subject starts on its input, or at `from`
96
+ * Follows its input at a rate limit, the package's linear decay primitive. No faster than `riseMs`
97
+ * per unit climbing or `fallMs` draining. The mix keeps its state per voice and subject, so two
98
+ * voices handed the same slew each follow on their own. A subject starts on its input, or at `from`
99
99
  * unless `dt` is infinite.
100
100
  *
101
101
  * @category signal
@@ -123,16 +123,17 @@ export function slew(of, { riseMs = 0, fallMs = 0, from }) {
123
123
  return marked(read, [of]);
124
124
  }
125
125
  /**
126
- * The exponential counterpart to `slew`: closes the same fraction of the distance to its input in
127
- * every equal stretch of time, `1 − exp(−gap / ms)`, so it reads the same however the frames that
128
- * sample it are spaced while the input holds still. `riseMs` and `fallMs` are time constants: after
129
- * one, 63% of the way is covered. 0 follows at once. Within `floor` of its input it lands on it, so a
130
- * subject can reach rest; pass 0 to keep the curve exact. The mix keeps its state per voice and subject.
131
- * A subject starts on its input, or at `from` unless `dt` is infinite.
126
+ * Closes the same fraction of the distance to its input in every equal stretch of time. The
127
+ * exponential counterpart to `slew`: the fraction is `1 − exp(−gap / ms)`, so it reads the same
128
+ * however the frames that sample it are spaced while the input holds still. `riseMs` and `fallMs`
129
+ * are time constants: after one, 63% of the way is covered. 0 follows at once. Within `floor` of
130
+ * its input it lands on it, so a subject can reach rest; pass 0 to keep the curve exact. The mix
131
+ * keeps its state per voice and subject. A subject starts on its input, or at `from` unless `dt` is
132
+ * infinite.
132
133
  *
133
134
  * @category signal
134
135
  */
135
- export function lag(of, { riseMs = 0, fallMs = 0, floor = 1e-6, from, }) {
136
+ export function lag(of, { riseMs = 0, fallMs = 0, floor = 1e-6, from }) {
136
137
  const read = (subject, setting) => {
137
138
  const target = of(subject, setting);
138
139
  const held = setting.keep(read, () => firstSight(target, from, setting));
@@ -155,8 +156,9 @@ export function lag(of, { riseMs = 0, fallMs = 0, floor = 1e-6, from, }) {
155
156
  return marked(read, [of]);
156
157
  }
157
158
  /**
158
- * The other half of the pair: a band, not an edge. It drops at `off`, passes at `on`, and between
159
- * the two it holds whatever it last did, so an input resting on a threshold cannot chatter.
159
+ * Passes or drops its input across a band, not an edge. The other half of the pair. It drops at
160
+ * `off`, passes at `on`, and between the two it holds whatever it last did, so an input resting on
161
+ * a threshold cannot chatter.
160
162
  *
161
163
  * @category signal
162
164
  */
package/dist/stack.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import type { Kit } from './types.js';
2
2
  /**
3
- * Folds deltas into one pose by a kit's arithmetic, in order, with no mix and no clock: what a mix
4
- * gives for voices contributing these deltas at these weights. Each channel starts at its rest, a
5
- * weight (default 1, held to 0..1) fades a delta toward rest, and the pose is clamped to each
6
- * channel's bounds. A channel with no rest takes the last delta a mix's default band switches on, any
7
- * weight above 0, and stays undefined where none is.
3
+ * Folds deltas into one pose by a kit's arithmetic, with no mix and no clock. It folds them in
4
+ * order, and gives what a mix gives for voices contributing these deltas at these weights. Each
5
+ * channel starts at its rest, a weight (default 1, held to 0..1) fades a delta toward rest, and the
6
+ * pose is clamped to each channel's bounds. A channel with no rest takes the last delta a mix's
7
+ * default band switches on, any weight above 0, and stays undefined where none is.
8
8
  *
9
9
  * @category channel
10
10
  */
package/dist/stack.js CHANGED
@@ -2,11 +2,11 @@ import { STOCK_BAND } from './channels.js';
2
2
  import { copy, first } from './clone.js';
3
3
  import { bound, merged } from './fold.js';
4
4
  /**
5
- * Folds deltas into one pose by a kit's arithmetic, in order, with no mix and no clock: what a mix
6
- * gives for voices contributing these deltas at these weights. Each channel starts at its rest, a
7
- * weight (default 1, held to 0..1) fades a delta toward rest, and the pose is clamped to each
8
- * channel's bounds. A channel with no rest takes the last delta a mix's default band switches on, any
9
- * weight above 0, and stays undefined where none is.
5
+ * Folds deltas into one pose by a kit's arithmetic, with no mix and no clock. It folds them in
6
+ * order, and gives what a mix gives for voices contributing these deltas at these weights. Each
7
+ * channel starts at its rest, a weight (default 1, held to 0..1) fades a delta toward rest, and the
8
+ * pose is clamped to each channel's bounds. A channel with no rest takes the last delta a mix's
9
+ * default band switches on, any weight above 0, and stays undefined where none is.
10
10
  *
11
11
  * @category channel
12
12
  */
@@ -1,5 +1,6 @@
1
+ import type { Cut } from './cut.js';
1
2
  import type { Mixer } from './mixer.js';
2
- import type { Cut, Transport } from './transport.js';
3
+ import type { Transport } from './transport.js';
3
4
  /**
4
5
  * Throws unless a read back to mix time `t` is exact with no history: every voice plays as it was
5
6
  * cued and reads the same whenever it is asked, and no mix on the transport has changed since `t`.
package/dist/standing.js CHANGED
@@ -1,7 +1,8 @@
1
+ import { mirror } from './ahead.js';
1
2
  import { ownBlends } from './blend.js';
2
3
  import { relink } from './owner.js';
3
4
  import { pin } from './place.js';
4
- import { mirror, recall, stateAt } from './project.js';
5
+ import { recall, stateAt } from './project.js';
5
6
  import { recount } from './roster.js';
6
7
  /**
7
8
  * Why a voice cannot be read at an earlier time from how it stands now, or undefined where it can:
@@ -28,7 +29,7 @@ function unfit(mix, voice) {
28
29
  return 'it plays under an owner';
29
30
  // A rest-less channel switches on across a band, by the weights its voice has passed through.
30
31
  const steady = typeof spec.weight !== 'function' &&
31
- spec.locus === undefined &&
32
+ spec.role === undefined &&
32
33
  !((voice.fade.in ?? 0) > 0) &&
33
34
  !((voice.fade.out ?? 0) > 0);
34
35
  if (!steady && voice.slots.some((s) => mix.channels[s]?.rest === undefined))
package/dist/store.d.ts CHANGED
@@ -33,3 +33,12 @@ export declare class Masked<K, V> extends Store<K, V> {
33
33
  get(key: K): V | undefined;
34
34
  delete(key: K): void;
35
35
  }
36
+ /** A store that fills a missing entry from `make` on first ask. */
37
+ export declare class Filled<K, V> extends Store<K, V> {
38
+ private readonly make;
39
+ /** Keys deleted here, which are made afresh by whoever asks and never again by `make`. */
40
+ private gone;
41
+ constructor(make: (key: K) => V | undefined);
42
+ get(key: K): V | undefined;
43
+ delete(key: K): void;
44
+ }
package/dist/store.js CHANGED
@@ -89,3 +89,27 @@ export class Masked extends Store {
89
89
  const none = Symbol('none');
90
90
  /** A Map's key equality, SameValueZero: NaN finds NaN. */
91
91
  const same = (a, b) => a === b || Object.is(a, b);
92
+ /** A store that fills a missing entry from `make` on first ask. */
93
+ export class Filled extends Store {
94
+ make;
95
+ /** Keys deleted here, which are made afresh by whoever asks and never again by `make`. */
96
+ gone = null;
97
+ constructor(make) {
98
+ super();
99
+ this.make = make;
100
+ }
101
+ get(key) {
102
+ let v = super.get(key);
103
+ if (v === undefined && this.gone?.get(key) !== true) {
104
+ v = this.make(key);
105
+ if (v !== undefined)
106
+ super.set(key, v);
107
+ }
108
+ return v;
109
+ }
110
+ delete(key) {
111
+ super.delete(key);
112
+ this.gone ??= new Store();
113
+ this.gone.set(key, true);
114
+ }
115
+ }
package/dist/strays.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { Mixer } from './mixer.js';
2
- import type { MotionOwner, Motions } from './motions.js';
2
+ import type { Motions } from './motions.js';
3
+ import type { MotionOwner } from './stretch.js';
3
4
  import type { Voice } from './voice.js';
4
5
  /** Notes a motion patch that may hold state for a subject no voice playing it reaches. */
5
6
  export declare function stray<I, O>(mix: Mixer<I, O>, motion: Motions<I>, subject: I): void;