@msb235/blits 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +163 -0
- package/README.md +32 -8
- package/dist/bare.js +7 -1
- package/dist/book.d.ts +8 -1
- package/dist/book.js +35 -4
- package/dist/chain.d.ts +4 -3
- package/dist/chain.js +10 -6
- package/dist/channels.d.ts +12 -19
- package/dist/channels.js +18 -118
- package/dist/clock.d.ts +12 -5
- package/dist/clock.js +13 -5
- package/dist/closed.d.ts +15 -0
- package/dist/closed.js +144 -0
- package/dist/color.d.ts +61 -0
- package/dist/color.js +229 -0
- package/dist/columns.js +2 -1
- package/dist/crowd.d.ts +4 -4
- package/dist/crowd.js +11 -8
- package/dist/cue.d.ts +2 -0
- package/dist/cue.js +78 -27
- package/dist/due.js +5 -2
- package/dist/fade.d.ts +13 -0
- package/dist/fade.js +55 -4
- package/dist/fill.d.ts +1 -1
- package/dist/fill.js +17 -10
- package/dist/fit.d.ts +144 -0
- package/dist/fit.js +181 -0
- package/dist/fold.d.ts +3 -3
- package/dist/fold.js +25 -18
- package/dist/gather.d.ts +6 -0
- package/dist/gather.js +18 -0
- package/dist/handle.d.ts +27 -2
- package/dist/handle.js +60 -11
- package/dist/held.js +7 -3
- package/dist/history.d.ts +15 -2
- package/dist/history.js +54 -9
- package/dist/hosts.d.ts +2 -2
- package/dist/hosts.js +19 -6
- package/dist/index.d.ts +10 -2
- package/dist/index.js +6 -1
- package/dist/lane.d.ts +4 -4
- package/dist/lane.js +5 -5
- package/dist/lanes.d.ts +32 -9
- package/dist/lanes.js +53 -24
- package/dist/locus.d.ts +3 -3
- package/dist/locus.js +1 -1
- package/dist/marks.d.ts +3 -0
- package/dist/marks.js +81 -13
- package/dist/meet.d.ts +3 -2
- package/dist/meet.js +15 -3
- package/dist/mixer.d.ts +55 -35
- package/dist/mixer.js +173 -83
- package/dist/motion.d.ts +2 -199
- package/dist/motion.js +2 -652
- package/dist/motions.d.ts +266 -0
- package/dist/motions.js +724 -0
- package/dist/move.d.ts +1 -1
- package/dist/move.js +27 -11
- package/dist/owner.d.ts +2 -2
- package/dist/owner.js +5 -5
- package/dist/pace.d.ts +2 -0
- package/dist/pace.js +8 -0
- package/dist/pack.d.ts +31 -0
- package/dist/pack.js +65 -0
- package/dist/paging.d.ts +63 -0
- package/dist/paging.js +110 -0
- package/dist/patch.d.ts +3 -0
- package/dist/patch.js +2 -0
- package/dist/place.js +74 -25
- package/dist/project.d.ts +7 -4
- package/dist/project.js +117 -78
- package/dist/pull.js +4 -3
- package/dist/qualify.js +4 -2
- package/dist/revive.d.ts +58 -0
- package/dist/revive.js +158 -0
- package/dist/sample.d.ts +1 -1
- package/dist/sample.js +6 -1
- package/dist/seek.d.ts +3 -0
- package/dist/seek.js +311 -0
- package/dist/signals.d.ts +3 -2
- package/dist/signals.js +12 -6
- package/dist/spans.d.ts +29 -0
- package/dist/spans.js +165 -0
- package/dist/strays.d.ts +1 -1
- package/dist/strays.js +21 -0
- package/dist/tape.d.ts +21 -0
- package/dist/tape.js +65 -0
- package/dist/ticker.d.ts +73 -0
- package/dist/ticker.js +115 -0
- package/dist/transport.d.ts +121 -0
- package/dist/transport.js +271 -0
- package/dist/types.d.ts +427 -50
- package/dist/unpage.d.ts +8 -0
- package/dist/unpage.js +163 -0
- package/dist/voice.d.ts +78 -12
- package/dist/voice.js +126 -12
- package/dist/weigh.js +2 -2
- package/docs/schema.html +140 -48
- package/package.json +2 -1
package/dist/history.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { elapsedWith } from './clock.js';
|
|
2
2
|
import { clone } from './clone.js';
|
|
3
3
|
import { schedule } from './due.js';
|
|
4
|
+
import { packHeld } from './pack.js';
|
|
5
|
+
import { pageOut } from './paging.js';
|
|
4
6
|
import { none } from './voice.js';
|
|
5
7
|
/**
|
|
6
8
|
* The last entry taken before `t`, or at it where `inclusive` says so, as it does for a change a
|
|
@@ -61,7 +63,15 @@ export function noted(mix, voice) {
|
|
|
61
63
|
while (drop + 1 < log.length && log[drop + 1].at <= reach)
|
|
62
64
|
drop++;
|
|
63
65
|
if (drop > 0)
|
|
64
|
-
log.splice(0, drop);
|
|
66
|
+
pageOut(mix, 'controls', voice.id, undefined, log.splice(0, drop), copied, reach);
|
|
67
|
+
}
|
|
68
|
+
const copied = (e) => ({ ...e });
|
|
69
|
+
/** Takes off the front of a list kept by time what a read back to `reach` no longer needs. */
|
|
70
|
+
function older(list, reach) {
|
|
71
|
+
let n = 0;
|
|
72
|
+
while (n + 1 < list.length && list[n + 1].at <= reach)
|
|
73
|
+
n++;
|
|
74
|
+
return n === 0 ? [] : list.splice(0, n);
|
|
65
75
|
}
|
|
66
76
|
/** A copy of a subject's record that shares nothing a read could change. */
|
|
67
77
|
export function copyHeld(voice, h) {
|
|
@@ -77,6 +87,7 @@ export function copyHeld(voice, h) {
|
|
|
77
87
|
bands: h.bands === null ? null : h.bands.slice(),
|
|
78
88
|
state: h.state === undefined ? undefined : patch.clone ? patch.clone(h.state) : clone(h.state),
|
|
79
89
|
kept,
|
|
90
|
+
unkept: h.unkept?.map(clone),
|
|
80
91
|
probed: Number.NaN,
|
|
81
92
|
delta: null,
|
|
82
93
|
phase: 0,
|
|
@@ -107,11 +118,10 @@ export function recordHost(mix, now) {
|
|
|
107
118
|
return;
|
|
108
119
|
log.push({ at: now, fields });
|
|
109
120
|
const reach = now - mix.opts.history.ms;
|
|
110
|
-
|
|
111
|
-
log.shift();
|
|
121
|
+
pageOut(mix, 'host', undefined, undefined, older(log, reach), (e) => e.fields, reach);
|
|
112
122
|
}
|
|
113
123
|
/** Under `history` with `inputs`, keeps what an input signal read, each time it changes. */
|
|
114
|
-
export function record(mix, held, value) {
|
|
124
|
+
export function record(mix, voice, subject, held, value) {
|
|
115
125
|
const history = mix.opts.history;
|
|
116
126
|
if (!history?.inputs || mix.projecting)
|
|
117
127
|
return;
|
|
@@ -125,11 +135,12 @@ export function record(mix, held, value) {
|
|
|
125
135
|
}
|
|
126
136
|
inputs.push({ at: mix.now, value });
|
|
127
137
|
const reach = mix.now - history.ms;
|
|
128
|
-
|
|
129
|
-
|
|
138
|
+
const out = older(inputs, reach);
|
|
139
|
+
if (out.length > 0)
|
|
140
|
+
pageOut(mix, 'input', voice.id, mix.keys?.key(subject), out, (e) => e.value, reach);
|
|
130
141
|
}
|
|
131
142
|
/** Under `history`, keeps a copy of a stateful voice's record for this subject every so often. */
|
|
132
|
-
export function remember(mix, voice, held) {
|
|
143
|
+
export function remember(mix, voice, subject, held) {
|
|
133
144
|
const history = mix.opts.history;
|
|
134
145
|
if (!history || mix.projecting)
|
|
135
146
|
return;
|
|
@@ -144,6 +155,40 @@ export function remember(mix, voice, held) {
|
|
|
144
155
|
return;
|
|
145
156
|
snaps.push({ at: mix.now, held: copyHeld(voice, held) });
|
|
146
157
|
const reach = mix.now - history.ms;
|
|
147
|
-
|
|
148
|
-
|
|
158
|
+
const out = older(snaps, reach);
|
|
159
|
+
if (out.length > 0)
|
|
160
|
+
pageOut(mix, 'snap', voice.id, mix.keys?.key(subject), out, (e) => packHeld(voice, e.held), reach);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Under history, keeps the record a subject had when it left a voice, faded out of it or dropped,
|
|
164
|
+
* for a seek or a read back to before then; without history, keeps nothing.
|
|
165
|
+
*/
|
|
166
|
+
export function leave(mix, voice, subject, held) {
|
|
167
|
+
const history = mix.opts.history;
|
|
168
|
+
if (history === undefined || held === undefined)
|
|
169
|
+
return;
|
|
170
|
+
const reach = mix.now - history.ms;
|
|
171
|
+
const left = [];
|
|
172
|
+
for (const e of voice.left ?? [])
|
|
173
|
+
if (e.at >= reach)
|
|
174
|
+
left.push(e);
|
|
175
|
+
else if (mix.keys !== null)
|
|
176
|
+
pageOut(mix, 'left', voice.id, mix.keys.key(e.subject), [e], (x) => packHeld(voice, x.held), reach);
|
|
177
|
+
left.push({ subject, at: Number.isNaN(mix.now) ? Number.NEGATIVE_INFINITY : mix.now, held });
|
|
178
|
+
voice.left = left;
|
|
179
|
+
}
|
|
180
|
+
/** The record a subject had at mix time `t` and left a voice with after it, if it left after `t`. */
|
|
181
|
+
export function leftAt(left, subject, t) {
|
|
182
|
+
let found;
|
|
183
|
+
for (const e of left ?? [])
|
|
184
|
+
if (e.at > t && Object.is(e.subject, subject) && (found === undefined || e.at < found.at))
|
|
185
|
+
found = e;
|
|
186
|
+
return found?.held;
|
|
187
|
+
}
|
|
188
|
+
/** The mix time a motion patch keeps a released subject from, and how far back, under history. */
|
|
189
|
+
export function releasing(mix) {
|
|
190
|
+
const history = mix.opts.history;
|
|
191
|
+
if (history === undefined || Number.isNaN(mix.now))
|
|
192
|
+
return [];
|
|
193
|
+
return [mix.now, mix.now - history.ms];
|
|
149
194
|
}
|
package/dist/hosts.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ export declare function laneHost<I, O>(mix: Mixer<I, O>): LaneHost<I, O>;
|
|
|
7
7
|
/**
|
|
8
8
|
* Whether a voice's patch and spec can run on a lane, its channels aside. A voice whose patch has
|
|
9
9
|
* kept state on a record through `setting.keep` is stateful from then on, and leaves its lane;
|
|
10
|
-
* one that starts keeping partway through may advance that state once for one subject
|
|
11
|
-
*
|
|
10
|
+
* one that starts keeping partway through may advance that state once for one subject probed the
|
|
11
|
+
* frame before it starts and not on that frame, which declaring `state` avoids.
|
|
12
12
|
*/
|
|
13
13
|
export declare function fits<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>): boolean;
|
|
14
14
|
/** A voice's patch kept state on a record: it is stateful from now on. */
|
package/dist/hosts.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Book } from './book.js';
|
|
2
|
+
import { numericOf } from './channels.js';
|
|
3
|
+
import { crowdable } from './crowd.js';
|
|
2
4
|
import { schedule } from './due.js';
|
|
3
|
-
import { beginFade, fadeSubject } from './fade.js';
|
|
5
|
+
import { beginFade, beginRise, fadeAt, fadeSubject } from './fade.js';
|
|
4
6
|
import { VoiceHandle } from './handle.js';
|
|
5
7
|
import { noted, remember } from './history.js';
|
|
6
8
|
import { hostOf, listed, markOf, readingAt } from './marks.js';
|
|
@@ -8,6 +10,7 @@ import { nextFrame } from './move.js';
|
|
|
8
10
|
import { descendants, ownWeight, signalled } from './owner.js';
|
|
9
11
|
import { localNow } from './place.js';
|
|
10
12
|
import { reading } from './reading.js';
|
|
13
|
+
import { record } from './tape.js';
|
|
11
14
|
import { unreach } from './unreached.js';
|
|
12
15
|
export function book(mix, opts) {
|
|
13
16
|
if (mix.projecting)
|
|
@@ -42,6 +45,7 @@ export function laneHost(mix) {
|
|
|
42
45
|
},
|
|
43
46
|
channels: mix.channels,
|
|
44
47
|
names: mix.names,
|
|
48
|
+
clock: mix,
|
|
45
49
|
fits: (voice) => fits(mix, voice),
|
|
46
50
|
meet: (voice, subject, head, slot) => {
|
|
47
51
|
for (let held = head; held !== null; held = held.next)
|
|
@@ -56,6 +60,7 @@ export function laneHost(mix) {
|
|
|
56
60
|
for (const voice of mix.gone)
|
|
57
61
|
unreach(voice, slot, false);
|
|
58
62
|
},
|
|
63
|
+
passes: (was, w) => mix.passes(was, w),
|
|
59
64
|
naming: (subject) => (mix.naming === 0 ? undefined : mix.named.get(subject)),
|
|
60
65
|
slotOf: (subject) => mix.chains.get(subject)?.slot ?? -1,
|
|
61
66
|
envelope: (voice, since) => voice.owner === null
|
|
@@ -84,22 +89,22 @@ export function laneHost(mix) {
|
|
|
84
89
|
reading.horizon = mix.horizonFor(voice, delay, mix.now);
|
|
85
90
|
},
|
|
86
91
|
keeps: mix.opts.history !== undefined,
|
|
87
|
-
after: (voice, held) => remember(mix, voice, held),
|
|
92
|
+
after: (voice, subject, held) => remember(mix, voice, subject, held),
|
|
88
93
|
kept: (voice) => stateful(mix, voice),
|
|
89
94
|
};
|
|
90
95
|
}
|
|
91
96
|
/**
|
|
92
97
|
* Whether a voice's patch and spec can run on a lane, its channels aside. A voice whose patch has
|
|
93
98
|
* kept state on a record through `setting.keep` is stateful from then on, and leaves its lane;
|
|
94
|
-
* one that starts keeping partway through may advance that state once for one subject
|
|
95
|
-
*
|
|
99
|
+
* one that starts keeping partway through may advance that state once for one subject probed the
|
|
100
|
+
* frame before it starts and not on that frame, which declaring `state` avoids.
|
|
96
101
|
*/
|
|
97
102
|
export function fits(mix, voice) {
|
|
98
103
|
const spec = voice.spec;
|
|
99
104
|
const patch = voice.patch;
|
|
100
105
|
if (voice.keeping)
|
|
101
106
|
return false;
|
|
102
|
-
if (voice.state === 'pending' && voice.
|
|
107
|
+
if (voice.state === 'pending' && voice.freezesBefore)
|
|
103
108
|
return false;
|
|
104
109
|
// A fill weighs a voice's owners once for every subject, which a signal on one would not be.
|
|
105
110
|
if (voice.owner !== null && signalled(voice))
|
|
@@ -112,6 +117,10 @@ export function fits(mix, voice) {
|
|
|
112
117
|
return false;
|
|
113
118
|
if (spec.from === 'current')
|
|
114
119
|
return false;
|
|
120
|
+
// A rest-less channel's band is read on the record `one` folds; crowds, motions and loci fold elsewhere.
|
|
121
|
+
if ((voice.motion !== undefined || spec.locus !== undefined || crowdable(voice)) &&
|
|
122
|
+
voice.slots.some((s) => numericOf(mix.channels[s])?.op === 'last'))
|
|
123
|
+
return false;
|
|
115
124
|
if (voice.out?.rest)
|
|
116
125
|
return false;
|
|
117
126
|
if (patch.state !== undefined || patch.step !== undefined)
|
|
@@ -159,9 +168,12 @@ function handleHost(mix) {
|
|
|
159
168
|
throw new Error('blits: an owner fades as a whole, not by subject or at rest');
|
|
160
169
|
if (opts !== undefined && 'subject' in opts)
|
|
161
170
|
fadeSubject(mix, voice, opts.subject, opts.over);
|
|
171
|
+
else if (typeof opts?.at === 'number')
|
|
172
|
+
fadeAt(mix, voice, opts.at, opts.over);
|
|
162
173
|
else
|
|
163
|
-
beginFade(mix, voice, opts ?? {});
|
|
174
|
+
beginFade(mix, voice, (opts ?? {}));
|
|
164
175
|
},
|
|
176
|
+
rise: (voice, opts) => beginRise(mix, voice, opts ?? {}),
|
|
165
177
|
weightOf: (voice, subject) => {
|
|
166
178
|
if (voice.state === 'done')
|
|
167
179
|
return 0;
|
|
@@ -174,5 +186,6 @@ function handleHost(mix) {
|
|
|
174
186
|
}
|
|
175
187
|
return voice.subjects.get(subject)?.weight ?? 0;
|
|
176
188
|
},
|
|
189
|
+
record: (label, again) => record(mix, label, again),
|
|
177
190
|
};
|
|
178
191
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
export type { NumberOptions } from './channels.js';
|
|
2
|
-
export {
|
|
2
|
+
export { kit, last, max, mul, sum, vec } from './channels.js';
|
|
3
|
+
export type { ColorOptions } from './color.js';
|
|
4
|
+
export { color, css, hex, mixHex, oklab, toHex } from './color.js';
|
|
5
|
+
export type { Claim, Fit, FitPlan, Order, SpanClaim, Strength } from './fit.js';
|
|
6
|
+
export { conclude, condense, lax, layout, overrun, pipe, plain, shed, } from './fit.js';
|
|
3
7
|
export { mix, mixer } from './mixer.js';
|
|
4
8
|
export type { Motion, Moving, PerSubject, Value } from './motion.js';
|
|
5
9
|
export { glide, spring, tween } from './motion.js';
|
|
10
|
+
export { HistoryMiss } from './paging.js';
|
|
6
11
|
export type { KeysOptions, PatchOptions } from './patch.js';
|
|
7
12
|
export { keys, patch } from './patch.js';
|
|
8
13
|
export { gate, lag, level, peak, slew } from './signals.js';
|
|
9
|
-
export type {
|
|
14
|
+
export type { Ticked, Ticker, TickerOptions } from './ticker.js';
|
|
15
|
+
export { ticker } from './ticker.js';
|
|
16
|
+
export { transport } from './transport.js';
|
|
17
|
+
export type { Anchor, BookedHit, Booker, BookOptions, Channel, Columns, Described, Doubt, Easing, Engine, FadeOptions, FadeSpec, FitResult, Handle, HistoryOptions, HistoryStore, Hit, Keyframe, Kit, Mark, Marked, Mix, MixOptions, MotionSpec, OwnerSpec, Paged, PagedStream, Patch, Placement, Projection, Query, Sent, Setting, Signal, SpanHandle, SpanHints, SpanSpec, Tape, TapeBranch, TapeMaker, TapeOp, Transport, TransportOptions, TransportProjection, VoiceSpec, } from './types.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { kit, last, max, mul, sum, vec } from './channels.js';
|
|
2
|
+
export { color, css, hex, mixHex, oklab, toHex } from './color.js';
|
|
3
|
+
export { conclude, condense, lax, layout, overrun, pipe, plain, shed, } from './fit.js';
|
|
2
4
|
export { mix, mixer } from './mixer.js';
|
|
3
5
|
export { glide, spring, tween } from './motion.js';
|
|
6
|
+
export { HistoryMiss } from './paging.js';
|
|
4
7
|
export { keys, patch } from './patch.js';
|
|
5
8
|
export { gate, lag, level, peak, slew } from './signals.js';
|
|
9
|
+
export { ticker } from './ticker.js';
|
|
10
|
+
export { transport } from './transport.js';
|
package/dist/lane.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Numeric } from './channels.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { Motions } from './motions.js';
|
|
3
3
|
import type { Scratch } from './patch.js';
|
|
4
4
|
import type { Subject, Voice } from './voice.js';
|
|
5
5
|
/** One laned channel: its arithmetic, and every numbered subject's folded value, `axes` per subject. */
|
|
@@ -204,7 +204,7 @@ export declare class Lane<I, O> implements Positions<I, O> {
|
|
|
204
204
|
remove(slot: number): void;
|
|
205
205
|
}
|
|
206
206
|
/**
|
|
207
|
-
* A subject's voice time once its voice's
|
|
208
|
-
* its passes after, and NaN where it shows nothing.
|
|
207
|
+
* A subject's voice time once its voice's freezes apply: frozen at 0 before it starts and at the end
|
|
208
|
+
* of its passes after, and NaN where it shows nothing.
|
|
209
209
|
*/
|
|
210
|
-
export declare function
|
|
210
|
+
export declare function frozenAt<I, O>(voice: Voice<I, O>, elapsed: number): number;
|
package/dist/lane.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { unbareLane } from './bare.js';
|
|
2
|
-
import {
|
|
2
|
+
import { frozenTime } from './clock.js';
|
|
3
3
|
import { motionOf } from './motion.js';
|
|
4
4
|
/**
|
|
5
5
|
* Below what share of its subjects probed last frame a lane stops filling, and above what share it
|
|
@@ -179,9 +179,9 @@ export class Lane {
|
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
/**
|
|
182
|
-
* A subject's voice time once its voice's
|
|
183
|
-
* its passes after, and NaN where it shows nothing.
|
|
182
|
+
* A subject's voice time once its voice's freezes apply: frozen at 0 before it starts and at the end
|
|
183
|
+
* of its passes after, and NaN where it shows nothing.
|
|
184
184
|
*/
|
|
185
|
-
export function
|
|
186
|
-
return
|
|
185
|
+
export function frozenAt(voice, elapsed) {
|
|
186
|
+
return frozenTime(elapsed, voice.freezesBefore, voice.freezesAfter, voice.span);
|
|
187
187
|
}
|
package/dist/lanes.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Column } from './columns.js';
|
|
2
2
|
import { type Crowd, row } from './crowd.js';
|
|
3
3
|
import { call, foldKeys, one, parting, readKeyed, reset, run, signalled, subjectAt } from './fill.js';
|
|
4
|
-
import { fold, foldDelta, foldInto, foldRun, gather } from './gather.js';
|
|
4
|
+
import { fold, foldDelta, foldInto, foldRun, gate, gather } from './gather.js';
|
|
5
5
|
import { type Lane, type Laned, type Locus } from './lane.js';
|
|
6
|
-
import type { Watcher } from './
|
|
6
|
+
import type { Watcher } from './motions.js';
|
|
7
7
|
import { type absent, Numbers } from './numbers.js';
|
|
8
8
|
import { Owed } from './owed.js';
|
|
9
9
|
import type { Channel } from './types.js';
|
|
@@ -13,6 +13,10 @@ export interface LaneHost<I, O> {
|
|
|
13
13
|
readonly voices: readonly Voice<I, O>[];
|
|
14
14
|
readonly channels: readonly Channel<unknown>[];
|
|
15
15
|
readonly names: readonly string[];
|
|
16
|
+
/** The mix itself, for its clock: a getter V8 does not inline would box `now` on return. */
|
|
17
|
+
readonly clock: {
|
|
18
|
+
readonly now: number;
|
|
19
|
+
};
|
|
16
20
|
/** Whether a voice's patch and spec can run on a lane, its channels aside. */
|
|
17
21
|
fits(voice: Voice<I, O>): boolean;
|
|
18
22
|
/** The voice's record for a subject this probe has already linked it to, from `head` if there. */
|
|
@@ -35,12 +39,14 @@ export interface LaneHost<I, O> {
|
|
|
35
39
|
* stateful if the signal keeps state. `slot` is the subject's number.
|
|
36
40
|
*/
|
|
37
41
|
signal(voice: Voice<I, O>, subject: I, held: Subject<unknown>, elapsed: number, pass: number, slot: number): number;
|
|
42
|
+
/** Whether a rest-less channel's contribution passes the band, as the general path decides it. */
|
|
43
|
+
passes(was: boolean | undefined, w: number): boolean;
|
|
38
44
|
/** Sets `reading.horizon` for the voice and a subject delayed `delay` voice ms. */
|
|
39
45
|
horizon(voice: Voice<I, O>, delay: number): void;
|
|
40
46
|
/** Whether the mix keeps history, so a record a patch call changed may need a copy kept. */
|
|
41
47
|
readonly keeps: boolean;
|
|
42
48
|
/** After a patch call, keeps history of a record that grew kept state. */
|
|
43
|
-
after(voice: Voice<I, O>, held: Subject<unknown>): void;
|
|
49
|
+
after(voice: Voice<I, O>, subject: I, held: Subject<unknown>): void;
|
|
44
50
|
/** A patch call left kept state on a record: the voice is stateful from now on. */
|
|
45
51
|
kept(voice: Voice<I, O>): void;
|
|
46
52
|
}
|
|
@@ -62,6 +68,9 @@ export declare class Lanes<I, O> implements Watcher {
|
|
|
62
68
|
overlap: boolean;
|
|
63
69
|
/** The loci whose voices are on lanes, every member of each on one. */
|
|
64
70
|
loci: Locus<I, O>[];
|
|
71
|
+
/** The lane and record `one` is folding, whose band state a rest-less channel reads. */
|
|
72
|
+
folding: Lane<I, O> | null;
|
|
73
|
+
rec: Subject<unknown> | null;
|
|
65
74
|
/** While a fill gathers a locus: that locus and the member gathering, where folds go instead. */
|
|
66
75
|
into: Locus<I, O> | null;
|
|
67
76
|
intoId: number;
|
|
@@ -88,14 +97,17 @@ export declare class Lanes<I, O> implements Watcher {
|
|
|
88
97
|
/** The highest voice id the last qualify or settling of `touched` considered. */
|
|
89
98
|
known: number;
|
|
90
99
|
/**
|
|
91
|
-
* Per subject number, `SLOT` numbers side by side, so a probe reads one place in memory:
|
|
92
|
-
* fill last
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* the
|
|
100
|
+
* Per subject number, `SLOT` numbers side by side, so a probe reads one place in memory: the
|
|
101
|
+
* fill that last left it to the general path, being late, owing, newly numbered or forgotten;
|
|
102
|
+
* the latest lane epoch a probe of it has checked it against; how many idle lanes reach it,
|
|
103
|
+
* since a probe reads lanes only where none do; and the probe count at its last probe read from
|
|
104
|
+
* the lanes and at its last probe folded by the general path, with the fill the former read,
|
|
105
|
+
* which say whether a lane or a record holds what `weightOf` reports.
|
|
96
106
|
*/
|
|
97
107
|
per: Float64Array<ArrayBuffer>;
|
|
98
108
|
fills: number;
|
|
109
|
+
/** The latest fill if it ran a lane, else 0: a subject whose `FILLED` is below it reads it. */
|
|
110
|
+
busyFill: number;
|
|
99
111
|
probes: number;
|
|
100
112
|
/**
|
|
101
113
|
* Each subject by number as last probed, held from that probe until the next fill uses it, so a
|
|
@@ -140,6 +152,11 @@ export declare class Lanes<I, O> implements Watcher {
|
|
|
140
152
|
lastDistinct: number;
|
|
141
153
|
/** Numbered subjects alive. */
|
|
142
154
|
live: number;
|
|
155
|
+
/**
|
|
156
|
+
* The probe count a subject's last probe must pass for this fill to fill it: the start of the
|
|
157
|
+
* last frame with a probe, or -1 while every subject alive was probed then, when it fills all.
|
|
158
|
+
*/
|
|
159
|
+
lately: number;
|
|
143
160
|
filledVersion: number;
|
|
144
161
|
qualifiedVersion: number;
|
|
145
162
|
constructor(host: LaneHost<I, O>);
|
|
@@ -171,7 +188,7 @@ export declare class Lanes<I, O> implements Watcher {
|
|
|
171
188
|
* them. One that a lane has not met yet, newly numbered or newly reached, takes the general path
|
|
172
189
|
* this frame, which is where it is first seen; so does one probed from inside a fill.
|
|
173
190
|
*/
|
|
174
|
-
prepare(slot: number, subject: I,
|
|
191
|
+
prepare(slot: number, subject: I, version: number, head: Subject<unknown> | null): boolean;
|
|
175
192
|
/** Whether this frame's lanes are filled and nothing since asks `begin` to look again. */
|
|
176
193
|
filled(now: number, version: number): boolean;
|
|
177
194
|
/**
|
|
@@ -180,6 +197,11 @@ export declare class Lanes<I, O> implements Watcher {
|
|
|
180
197
|
*/
|
|
181
198
|
begin(now: number, version: number): number;
|
|
182
199
|
general(slot: number): false;
|
|
200
|
+
/**
|
|
201
|
+
* Whether this fill leaves the subject at `slot` to the general path, for not having been probed
|
|
202
|
+
* this frame or the last: a probe of it before the next fill then takes the general path.
|
|
203
|
+
*/
|
|
204
|
+
unread(slot: number): boolean;
|
|
183
205
|
/** Whether a probe has read the subject since the frame began, on either path. */
|
|
184
206
|
probedThisFrame(slot: number): boolean;
|
|
185
207
|
/** Whether the subject at `slot` reads some laned voice from the general path this fill. */
|
|
@@ -221,6 +243,7 @@ declare const methods: {
|
|
|
221
243
|
foldDelta: typeof foldDelta;
|
|
222
244
|
foldRun: typeof foldRun;
|
|
223
245
|
foldInto: typeof foldInto;
|
|
246
|
+
gate: typeof gate;
|
|
224
247
|
};
|
|
225
248
|
type Methods = typeof methods;
|
|
226
249
|
export interface Lanes<I, O> extends Methods {
|
package/dist/lanes.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { row } from './crowd.js';
|
|
2
2
|
import { call, fillAll, foldKeys, one, parting, readKeyed, reset, run, signalled, subjectAt, } from './fill.js';
|
|
3
|
-
import { fold, foldDelta, foldInto, foldRun, gather } from './gather.js';
|
|
4
|
-
import { meet,
|
|
3
|
+
import { fold, foldDelta, foldInto, foldRun, gate, gather } from './gather.js';
|
|
4
|
+
import { meet, reach } from './meet.js';
|
|
5
5
|
import { Numbers } from './numbers.js';
|
|
6
6
|
import { Owed } from './owed.js';
|
|
7
7
|
import { requalify, retouch } from './qualify.js';
|
|
@@ -26,6 +26,9 @@ export class Lanes {
|
|
|
26
26
|
overlap = false;
|
|
27
27
|
/** The loci whose voices are on lanes, every member of each on one. */
|
|
28
28
|
loci = [];
|
|
29
|
+
/** The lane and record `one` is folding, whose band state a rest-less channel reads. */
|
|
30
|
+
folding = null;
|
|
31
|
+
rec = null;
|
|
29
32
|
/** While a fill gathers a locus: that locus and the member gathering, where folds go instead. */
|
|
30
33
|
into = null;
|
|
31
34
|
intoId = 0;
|
|
@@ -52,14 +55,17 @@ export class Lanes {
|
|
|
52
55
|
/** The highest voice id the last qualify or settling of `touched` considered. */
|
|
53
56
|
known = 0;
|
|
54
57
|
/**
|
|
55
|
-
* Per subject number, `SLOT` numbers side by side, so a probe reads one place in memory:
|
|
56
|
-
* fill last
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* the
|
|
58
|
+
* Per subject number, `SLOT` numbers side by side, so a probe reads one place in memory: the
|
|
59
|
+
* fill that last left it to the general path, being late, owing, newly numbered or forgotten;
|
|
60
|
+
* the latest lane epoch a probe of it has checked it against; how many idle lanes reach it,
|
|
61
|
+
* since a probe reads lanes only where none do; and the probe count at its last probe read from
|
|
62
|
+
* the lanes and at its last probe folded by the general path, with the fill the former read,
|
|
63
|
+
* which say whether a lane or a record holds what `weightOf` reports.
|
|
60
64
|
*/
|
|
61
65
|
per = new Float64Array(0);
|
|
62
66
|
fills = 0;
|
|
67
|
+
/** The latest fill if it ran a lane, else 0: a subject whose `FILLED` is below it reads it. */
|
|
68
|
+
busyFill = 0;
|
|
63
69
|
probes = 0;
|
|
64
70
|
/**
|
|
65
71
|
* Each subject by number as last probed, held from that probe until the next fill uses it, so a
|
|
@@ -104,6 +110,11 @@ export class Lanes {
|
|
|
104
110
|
lastDistinct = 0;
|
|
105
111
|
/** Numbered subjects alive. */
|
|
106
112
|
live = 0;
|
|
113
|
+
/**
|
|
114
|
+
* The probe count a subject's last probe must pass for this fill to fill it: the start of the
|
|
115
|
+
* last frame with a probe, or -1 while every subject alive was probed then, when it fills all.
|
|
116
|
+
*/
|
|
117
|
+
lately = -1;
|
|
107
118
|
filledVersion = Number.NaN;
|
|
108
119
|
qualifiedVersion = Number.NaN;
|
|
109
120
|
constructor(host) {
|
|
@@ -132,7 +143,7 @@ export class Lanes {
|
|
|
132
143
|
const slot = this.numbers.take(subject);
|
|
133
144
|
this.live++;
|
|
134
145
|
this.grow(slot + 1);
|
|
135
|
-
this.per[slot * 6 /* Per.SLOT */ + 0 /* Per.FILLED */] =
|
|
146
|
+
this.per[slot * 6 /* Per.SLOT */ + 0 /* Per.FILLED */] = this.fills;
|
|
136
147
|
this.per[slot * 6 /* Per.SLOT */ + 1 /* Per.SEEN */] = -1;
|
|
137
148
|
this.per[slot * 6 /* Per.SLOT */ + 2 /* Per.IDLE */] = 0;
|
|
138
149
|
this.per[slot * 6 /* Per.SLOT */ + 3 /* Per.LANE_PROBE */] = 0;
|
|
@@ -195,9 +206,10 @@ export class Lanes {
|
|
|
195
206
|
* them. One that a lane has not met yet, newly numbered or newly reached, takes the general path
|
|
196
207
|
* this frame, which is where it is first seen; so does one probed from inside a fill.
|
|
197
208
|
*/
|
|
198
|
-
prepare(slot, subject,
|
|
199
|
-
|
|
200
|
-
|
|
209
|
+
prepare(slot, subject, version, head) {
|
|
210
|
+
// Read, not passed: a double passed to a call V8 does not inline is boxed, once a probe.
|
|
211
|
+
const now = this.host.clock.now;
|
|
212
|
+
if (!this.filled(now, version) && this.begin(now, version) === 0 /* Begin.GENERAL */)
|
|
201
213
|
return this.general(slot);
|
|
202
214
|
if (slot < 0)
|
|
203
215
|
return false;
|
|
@@ -206,18 +218,9 @@ export class Lanes {
|
|
|
206
218
|
const probe = ++this.probes;
|
|
207
219
|
const per = this.per;
|
|
208
220
|
const o = slot * 6 /* Per.SLOT */;
|
|
209
|
-
let lane = per[o + 0 /* Per.FILLED */]
|
|
210
|
-
if (per[o + 1 /* Per.SEEN */] < this.wide
|
|
211
|
-
|
|
212
|
-
// after every other laned voice, the general path folds just those onto the lanes' values;
|
|
213
|
-
// otherwise it reads the general path all frame.
|
|
214
|
-
if (lane && per[o + 2 /* Per.IDLE */] === 0 && owable(this, slot))
|
|
215
|
-
this.owed.owe(slot, this.met);
|
|
216
|
-
else {
|
|
217
|
-
per[o + 0 /* Per.FILLED */] = 0;
|
|
218
|
-
lane = false;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
+
let lane = per[o + 0 /* Per.FILLED */] < this.busyFill && per[o + 2 /* Per.IDLE */] === 0;
|
|
222
|
+
if (per[o + 1 /* Per.SEEN */] < this.wide)
|
|
223
|
+
lane = meet(this, slot, subject, head, lane);
|
|
221
224
|
if (lane) {
|
|
222
225
|
per[o + 3 /* Per.LANE_PROBE */] = probe;
|
|
223
226
|
per[o + 5 /* Per.LANE_FILL */] = this.fills;
|
|
@@ -283,6 +286,20 @@ export class Lanes {
|
|
|
283
286
|
this.per[slot * 6 /* Per.SLOT */ + 4 /* Per.GENERAL_PROBE */] = ++this.probes;
|
|
284
287
|
return false;
|
|
285
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* Whether this fill leaves the subject at `slot` to the general path, for not having been probed
|
|
291
|
+
* this frame or the last: a probe of it before the next fill then takes the general path.
|
|
292
|
+
*/
|
|
293
|
+
unread(slot) {
|
|
294
|
+
const per = this.per;
|
|
295
|
+
const o = slot * 6 /* Per.SLOT */;
|
|
296
|
+
const lately = this.lately;
|
|
297
|
+
if (per[o + 3 /* Per.LANE_PROBE */] > lately ||
|
|
298
|
+
per[o + 4 /* Per.GENERAL_PROBE */] > lately)
|
|
299
|
+
return false;
|
|
300
|
+
per[o + 0 /* Per.FILLED */] = this.fills;
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
286
303
|
/** Whether a probe has read the subject since the frame began, on either path. */
|
|
287
304
|
probedThisFrame(slot) {
|
|
288
305
|
const from = this.frameProbes;
|
|
@@ -312,6 +329,11 @@ export class Lanes {
|
|
|
312
329
|
rests(slot) {
|
|
313
330
|
for (const ch of this.laned) {
|
|
314
331
|
const values = ch.values;
|
|
332
|
+
if (ch.op === 'last') {
|
|
333
|
+
if (!Number.isNaN(values[slot * ch.axes]))
|
|
334
|
+
return false;
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
315
337
|
if (ch.scalar) {
|
|
316
338
|
if (!(Math.abs(values[slot] - ch.rest) < 1e-9))
|
|
317
339
|
return false;
|
|
@@ -333,6 +355,12 @@ export class Lanes {
|
|
|
333
355
|
for (let c = 0; c < laned.length; c++) {
|
|
334
356
|
const ch = laned[c];
|
|
335
357
|
const axes = ch.axes;
|
|
358
|
+
// Never `delete`: it drops a reused out object into dictionary mode for good.
|
|
359
|
+
if (ch.op === 'last' && Number.isNaN(ch.values[slot * axes])) {
|
|
360
|
+
if (pose[ch.name] !== undefined)
|
|
361
|
+
pose[ch.name] = undefined;
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
336
364
|
if (ch.scalar) {
|
|
337
365
|
pose[ch.name] = ch.values[slot];
|
|
338
366
|
continue;
|
|
@@ -355,7 +383,7 @@ export class Lanes {
|
|
|
355
383
|
if (c.list[p] === slot)
|
|
356
384
|
unplace(this, c, p);
|
|
357
385
|
if (slot < this.cap)
|
|
358
|
-
this.per[slot * 6 /* Per.SLOT */ + 0 /* Per.FILLED */] =
|
|
386
|
+
this.per[slot * 6 /* Per.SLOT */ + 0 /* Per.FILLED */] = this.fills;
|
|
359
387
|
this.subjects[slot] = undefined;
|
|
360
388
|
}
|
|
361
389
|
grow(size) {
|
|
@@ -417,6 +445,7 @@ const methods = {
|
|
|
417
445
|
foldDelta,
|
|
418
446
|
foldRun,
|
|
419
447
|
foldInto,
|
|
448
|
+
gate,
|
|
420
449
|
};
|
|
421
450
|
for (const [name, value] of Object.entries(methods))
|
|
422
451
|
Object.defineProperty(Lanes.prototype, name, { value, writable: true, configurable: true });
|
package/dist/locus.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Mixer } from './mixer.js';
|
|
2
2
|
import type { Subject, Voice } from './voice.js';
|
|
3
3
|
/**
|
|
4
|
-
* What a fold with a locus in play gathers, kept from one fold to the next: the
|
|
4
|
+
* What a fold with a locus in play gathers, kept from one fold to the next: the contributions in
|
|
5
5
|
* voice order, each a voice's own or a locus's first member; every locus member; and per channel
|
|
6
6
|
* slot, a locus's folding value, the weight taken into it and the array it owns.
|
|
7
7
|
*/
|
|
@@ -11,7 +11,7 @@ export interface LocusScratch<I, O> {
|
|
|
11
11
|
helds: (Subject<unknown> | null)[];
|
|
12
12
|
deltas: (Record<string, unknown> | null)[];
|
|
13
13
|
weights: number[];
|
|
14
|
-
/** Per
|
|
14
|
+
/** Per contribution, -1 for a voice's own, or which of `names` it is. */
|
|
15
15
|
groups: number[];
|
|
16
16
|
names: string[];
|
|
17
17
|
m: number;
|
|
@@ -32,7 +32,7 @@ export declare function locusScratch<I, O>(channels: number): LocusScratch<I, O>
|
|
|
32
32
|
*/
|
|
33
33
|
export declare function own<I, O>(k: LocusScratch<I, O>, slot: number, value: number[], rest: number[]): number[];
|
|
34
34
|
/**
|
|
35
|
-
* Folds one locus's members into one
|
|
35
|
+
* Folds one locus's members into one contribution through each channel's own `lerp`: its value per
|
|
36
36
|
* channel goes in `k.values`, the channels in `k.touched` in the order first met, and its weight
|
|
37
37
|
* is returned. A channel folding by scale takes its value in an array of `k`'s own, lerped in
|
|
38
38
|
* place, since the fold only reads it.
|
package/dist/locus.js
CHANGED
|
@@ -35,7 +35,7 @@ export function own(k, slot, value, rest) {
|
|
|
35
35
|
return out;
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
|
-
* Folds one locus's members into one
|
|
38
|
+
* Folds one locus's members into one contribution through each channel's own `lerp`: its value per
|
|
39
39
|
* channel goes in `k.values`, the channels in `k.touched` in the order first met, and its weight
|
|
40
40
|
* is returned. A channel folding by scale takes its value in an array of `k`'s own, lerped in
|
|
41
41
|
* place, since the fold only reads it.
|
package/dist/marks.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Listed } from './book.js';
|
|
2
2
|
import type { Mixer } from './mixer.js';
|
|
3
|
+
import type { Transport } from './transport.js';
|
|
3
4
|
import type { Mark } from './types.js';
|
|
4
5
|
import { type Voice } from './voice.js';
|
|
5
6
|
/** The host timestamp the mix clock reads mix time `t` at; Infinity where it never will. */
|
|
@@ -8,5 +9,7 @@ export declare function hostOf<I, O>(mix: Mixer<I, O>, t: number): number;
|
|
|
8
9
|
export declare function readingAt<I, O>(mix: Mixer<I, O>, u: number): number;
|
|
9
10
|
/** Every mark between two host timestamps, earliest first, with the order that tells each apart. */
|
|
10
11
|
export declare function listed<I, O>(mix: Mixer<I, O>, from: number, to: number): Listed[];
|
|
12
|
+
/** Every mark of every mix on a transport and every one announced on it, earliest first. */
|
|
13
|
+
export declare function listedAll(transport: Transport, from: number, to: number): Listed[];
|
|
11
14
|
/** When a voice reaches a mark, mix time, or undefined while nothing has fixed it. */
|
|
12
15
|
export declare function markOf<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, mark: Mark): number | undefined;
|