@msb235/blits 0.13.0 → 0.15.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 +183 -0
- package/README.md +20 -3
- package/dist/ahead.d.ts +26 -0
- package/dist/ahead.js +264 -0
- package/dist/around.d.ts +11 -0
- package/dist/around.js +32 -0
- package/dist/bare.js +15 -7
- package/dist/blank.d.ts +10 -0
- package/dist/blank.js +26 -0
- package/dist/blend.d.ts +2 -1
- package/dist/blend.js +1 -6
- package/dist/bound.d.ts +70 -0
- package/dist/bound.js +351 -0
- package/dist/chain.d.ts +2 -1
- package/dist/chain.js +7 -5
- package/dist/changes.d.ts +24 -0
- package/dist/changes.js +189 -0
- package/dist/channels.d.ts +39 -13
- package/dist/channels.js +14 -5
- package/dist/closed.d.ts +1 -0
- package/dist/closed.js +45 -1
- package/dist/color.d.ts +12 -10
- package/dist/color.js +25 -25
- package/dist/columns.js +7 -3
- package/dist/contribution.d.ts +30 -0
- package/dist/contribution.js +374 -0
- package/dist/copyout.d.ts +10 -0
- package/dist/copyout.js +69 -24
- package/dist/crowd.d.ts +8 -2
- package/dist/crowd.js +4 -5
- package/dist/cue.d.ts +8 -1
- package/dist/cue.js +47 -14
- package/dist/cut.d.ts +8 -0
- package/dist/cut.js +4 -0
- package/dist/drop.d.ts +9 -0
- package/dist/drop.js +84 -0
- package/dist/due.d.ts +24 -5
- package/dist/due.js +113 -33
- package/dist/easing.d.ts +4 -4
- package/dist/easing.js +3 -3
- package/dist/everyone.d.ts +3 -2
- package/dist/everyone.js +3 -3
- package/dist/fade.d.ts +2 -1
- package/dist/fade.js +2 -1
- package/dist/fill.d.ts +3 -2
- package/dist/fill.js +51 -19
- package/dist/fit.d.ts +44 -26
- package/dist/fit.js +10 -9
- package/dist/fling.d.ts +66 -0
- package/dist/fling.js +162 -0
- package/dist/fold.d.ts +4 -37
- package/dist/fold.js +5 -512
- package/dist/gather.d.ts +12 -7
- package/dist/gather.js +179 -44
- package/dist/gone.js +2 -2
- package/dist/grid.d.ts +47 -0
- package/dist/grid.js +247 -0
- package/dist/handle.d.ts +3 -0
- package/dist/handle.js +19 -8
- package/dist/held.d.ts +2 -1
- package/dist/history.d.ts +6 -5
- package/dist/history.js +9 -9
- package/dist/hosts.js +9 -15
- package/dist/index.d.ts +5 -4
- package/dist/keyfill.d.ts +1 -1
- package/dist/keyfill.js +5 -3
- package/dist/keyrows.d.ts +12 -0
- package/dist/keyrows.js +99 -12
- package/dist/lane.d.ts +36 -6
- package/dist/lane.js +32 -1
- package/dist/lanes.d.ts +23 -8
- package/dist/lanes.js +29 -8
- package/dist/leavings.d.ts +2 -2
- package/dist/leavings.js +1 -1
- package/dist/lives.d.ts +47 -0
- package/dist/lives.js +180 -0
- package/dist/meet.d.ts +2 -2
- package/dist/meet.js +90 -4
- package/dist/mixer.d.ts +61 -35
- package/dist/mixer.js +64 -169
- package/dist/motion.d.ts +19 -106
- package/dist/motion.js +60 -13
- package/dist/motions.d.ts +62 -150
- package/dist/motions.js +57 -369
- package/dist/move.js +24 -1
- package/dist/moved.d.ts +1 -1
- package/dist/moved.js +16 -7
- package/dist/named.d.ts +0 -1
- package/dist/named.js +20 -2
- package/dist/numbers.d.ts +2 -0
- package/dist/numbers.js +4 -0
- package/dist/owed.d.ts +2 -0
- package/dist/owed.js +4 -0
- package/dist/pack.d.ts +3 -1
- package/dist/pack.js +4 -3
- package/dist/patch.d.ts +7 -3
- package/dist/patch.js +6 -2
- package/dist/places.d.ts +53 -0
- package/dist/places.js +171 -0
- package/dist/plot.d.ts +12 -0
- package/dist/plot.js +52 -0
- package/dist/project.d.ts +4 -4
- package/dist/project.js +9 -211
- package/dist/pull.js +11 -8
- package/dist/qualify.js +84 -21
- package/dist/reading.d.ts +6 -0
- package/dist/reading.js +5 -0
- package/dist/record.d.ts +5 -1
- package/dist/record.js +4 -2
- package/dist/relink.d.ts +1 -1
- package/dist/restored.d.ts +2 -2
- package/dist/restored.js +1 -1
- package/dist/rests.d.ts +9 -0
- package/dist/rests.js +76 -0
- package/dist/revive.d.ts +3 -2
- package/dist/revive.js +2 -2
- package/dist/{locus.d.ts → role.d.ts} +10 -9
- package/dist/{locus.js → role.js} +3 -3
- package/dist/roles.d.ts +10 -0
- package/dist/roles.js +158 -0
- package/dist/roster.js +3 -3
- package/dist/rotation.d.ts +1 -1
- package/dist/rotation.js +1 -1
- package/dist/rows.js +1 -1
- package/dist/sample.d.ts +2 -2
- package/dist/sample.js +33 -7
- package/dist/seek.js +13 -5
- package/dist/setting.d.ts +27 -0
- package/dist/setting.js +55 -0
- package/dist/shared.d.ts +1 -1
- package/dist/shared.js +1 -1
- package/dist/signals.d.ts +64 -32
- package/dist/signals.js +20 -18
- package/dist/stack.d.ts +5 -5
- package/dist/stack.js +5 -5
- package/dist/standing.d.ts +2 -1
- package/dist/standing.js +3 -2
- package/dist/store.d.ts +9 -0
- package/dist/store.js +24 -0
- package/dist/strays.d.ts +2 -1
- package/dist/strays.js +21 -14
- package/dist/stretch.d.ts +112 -0
- package/dist/stretch.js +9 -0
- package/dist/subject.d.ts +142 -0
- package/dist/subject.js +1 -0
- package/dist/tape.d.ts +9 -5
- package/dist/tape.js +5 -1
- package/dist/then.d.ts +9 -0
- package/dist/then.js +57 -0
- package/dist/ticker.d.ts +4 -4
- package/dist/transport.d.ts +0 -8
- package/dist/transport.js +0 -4
- package/dist/turns.d.ts +4 -3
- package/dist/turns.js +4 -3
- package/dist/types/arc.d.ts +12 -11
- package/dist/types/channel.d.ts +12 -8
- package/dist/types/history.d.ts +35 -19
- package/dist/types/mix.d.ts +222 -161
- package/dist/types/motion.d.ts +151 -0
- package/dist/types/motion.js +1 -0
- package/dist/types/patch.d.ts +69 -34
- package/dist/types/place.d.ts +51 -36
- package/dist/types/transport.d.ts +20 -18
- package/dist/types/voice.d.ts +145 -72
- package/dist/types/watch.d.ts +88 -40
- package/dist/types.d.ts +1 -0
- package/dist/voice.d.ts +9 -141
- package/dist/voice.js +30 -79
- package/dist/warn.d.ts +2 -0
- package/dist/warn.js +1 -1
- package/dist/watch.d.ts +2 -2
- package/dist/watch.js +5 -3
- package/dist/wave.d.ts +4 -3
- package/dist/wave.js +3 -2
- package/dist/weigh.d.ts +7 -1
- package/dist/weigh.js +27 -0
- package/docs/schema.html +223 -37
- package/package.json +6 -2
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
|
|
37
|
-
* member's stop, the stops spaced evenly from 0 to 1. Weights a voice outside a blend as one of
|
|
38
|
-
*
|
|
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
|
|
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
|
|
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
|
-
*
|
|
97
|
-
* unit climbing or `fallMs` draining. The mix keeps its state per voice and subject, so two
|
|
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
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* sample it are spaced while the input holds still. `riseMs` and `fallMs`
|
|
129
|
-
* one, 63% of the way is covered. 0 follows at once. Within `floor` of
|
|
130
|
-
* subject can reach rest; pass 0 to keep the curve exact. The mix
|
|
131
|
-
* A subject starts on its input, or at `from` unless `dt` is
|
|
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
|
-
*
|
|
159
|
-
* the two it holds whatever it last did, so an input resting on
|
|
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,
|
|
4
|
-
* gives for voices contributing these deltas at these weights. Each
|
|
5
|
-
* weight (default 1, held to 0..1) fades a delta toward rest, and the
|
|
6
|
-
* channel's bounds. A channel with no rest takes the last delta a mix's
|
|
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,
|
|
6
|
-
* gives for voices contributing these deltas at these weights. Each
|
|
7
|
-
* weight (default 1, held to 0..1) fades a delta toward rest, and the
|
|
8
|
-
* channel's bounds. A channel with no rest takes the last delta a mix's
|
|
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
|
*/
|
package/dist/standing.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { Cut } from './cut.js';
|
|
1
2
|
import type { Mixer } from './mixer.js';
|
|
2
|
-
import type {
|
|
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 {
|
|
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.
|
|
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 {
|
|
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;
|
package/dist/strays.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { unpart } from './fade.js';
|
|
2
2
|
import { pageOut } from './paging.js';
|
|
3
|
-
import { record, unplayable } from './tape.js';
|
|
3
|
+
import { record, taping, unplayable } from './tape.js';
|
|
4
4
|
/** Notes a motion patch that may hold state for a subject no voice playing it reaches. */
|
|
5
5
|
export function stray(mix, motion, subject) {
|
|
6
6
|
const list = mix.strays.get(subject);
|
|
@@ -24,7 +24,7 @@ export function unplay(mix, voice) {
|
|
|
24
24
|
if (voice.named === null)
|
|
25
25
|
mix.strayAll.add(motion);
|
|
26
26
|
else
|
|
27
|
-
for (const subject of voice.named)
|
|
27
|
+
for (const subject of voice.named.list)
|
|
28
28
|
stray(mix, motion, subject);
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
@@ -45,19 +45,14 @@ export function motionOwner(mix) {
|
|
|
45
45
|
seq() {
|
|
46
46
|
return mix.deref()?.transport.seq ?? Number.NaN;
|
|
47
47
|
},
|
|
48
|
+
early() {
|
|
49
|
+
const m = mix.deref();
|
|
50
|
+
return m !== undefined && (m.syncing || !m.looked);
|
|
51
|
+
},
|
|
48
52
|
changed(id, motion, subject, change) {
|
|
49
53
|
const m = mix.deref();
|
|
50
|
-
if (m
|
|
51
|
-
|
|
52
|
-
record(m, 'motion', (stage) => {
|
|
53
|
-
const to = stage.mix(m);
|
|
54
|
-
if (to === undefined)
|
|
55
|
-
return;
|
|
56
|
-
if (to !== m)
|
|
57
|
-
return changeAhead(to, id, motion, subject, change);
|
|
58
|
-
const v = cuedById(m, id) ?? m.gone.find((g) => g.id === id);
|
|
59
|
-
(v?.motion ?? motion).change(subject, { ...change });
|
|
60
|
-
});
|
|
54
|
+
if (m !== undefined && taping(m))
|
|
55
|
+
recordChange(m, id, motion, subject, change);
|
|
61
56
|
},
|
|
62
57
|
page(id, stream, subject, out) {
|
|
63
58
|
const m = mix.deref();
|
|
@@ -84,6 +79,18 @@ export function motionOwner(mix) {
|
|
|
84
79
|
},
|
|
85
80
|
};
|
|
86
81
|
}
|
|
82
|
+
// Apart from `changed`, whose body would otherwise make this closure's context on every change.
|
|
83
|
+
function recordChange(m, id, motion, subject, change) {
|
|
84
|
+
record(m, 'motion', (stage) => {
|
|
85
|
+
const to = stage.mix(m);
|
|
86
|
+
if (to === undefined)
|
|
87
|
+
return;
|
|
88
|
+
if (to !== m)
|
|
89
|
+
return changeAhead(to, id, motion, subject, change);
|
|
90
|
+
const v = cuedById(m, id) ?? m.gone.find((g) => g.id === id);
|
|
91
|
+
(v?.motion ?? motion).change(subject, { ...change });
|
|
92
|
+
});
|
|
93
|
+
}
|
|
87
94
|
/**
|
|
88
95
|
* A retarget or push the tape holds, made for a read ahead: kept on its transport for the patch
|
|
89
96
|
* to apply as it reads, since the patch's own state is the live mix's.
|
|
@@ -117,7 +124,7 @@ function changeAhead(to, id, motion, given, change) {
|
|
|
117
124
|
made.set(motion, bySubject);
|
|
118
125
|
}
|
|
119
126
|
const list = bySubject.get(subject);
|
|
120
|
-
const c = { at, to: change.to, v: change.v };
|
|
127
|
+
const c = { at, to: change.to, x: change.x, v: change.v };
|
|
121
128
|
if (list === undefined)
|
|
122
129
|
bySubject.set(subject, [c]);
|
|
123
130
|
else {
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { Curve } from './easing.js';
|
|
2
|
+
import type { Motions } from './motions.js';
|
|
3
|
+
import type { MotionSpec } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* One closed-form stretch: released at `at` (voice ms) from `x0` moving at `v0`, toward `to`, over
|
|
6
|
+
* `ms` for a tween (0 for any other shape).
|
|
7
|
+
*/
|
|
8
|
+
export interface Segment {
|
|
9
|
+
at: number;
|
|
10
|
+
ms: number;
|
|
11
|
+
x0: number[];
|
|
12
|
+
v0: number[];
|
|
13
|
+
to: number[];
|
|
14
|
+
/** The change that released it; undefined for a subject's first stretch. */
|
|
15
|
+
change?: Change;
|
|
16
|
+
}
|
|
17
|
+
export interface Change {
|
|
18
|
+
/**
|
|
19
|
+
* Voice ms it takes effect at; absent, until the subject's next read stamps it with that read's
|
|
20
|
+
* time, for one made while no frame had met the subject.
|
|
21
|
+
*/
|
|
22
|
+
at?: number;
|
|
23
|
+
to?: number[];
|
|
24
|
+
/** Where it moves from, in place of where its stretch has it then. */
|
|
25
|
+
x?: number[];
|
|
26
|
+
v?: number[];
|
|
27
|
+
/** The mix clock when it was made, NaN where no mix was playing the patch. */
|
|
28
|
+
made?: number;
|
|
29
|
+
/** The frame it was made in, which a rewind partitions by; NaN where no mix was playing the patch. */
|
|
30
|
+
seq?: number;
|
|
31
|
+
/** Whether it was made before its frame was read, so a read of what that frame showed has it. */
|
|
32
|
+
early?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/** A record of a motion patch's history leaving memory, at a mix time and frame. */
|
|
35
|
+
export interface Paging {
|
|
36
|
+
at: number;
|
|
37
|
+
seq: number;
|
|
38
|
+
data: unknown;
|
|
39
|
+
}
|
|
40
|
+
/** A subject's run as plain data: its flags, its stretches oldest first, and changes still to apply. */
|
|
41
|
+
export interface Run {
|
|
42
|
+
flags: number;
|
|
43
|
+
stretches: Segment[];
|
|
44
|
+
pending: Change[] | undefined;
|
|
45
|
+
}
|
|
46
|
+
/** `n` numbers of `from` from `at`, as an array of their own. */
|
|
47
|
+
export declare function numbers(from: Float64Array, at: number, n: number): number[];
|
|
48
|
+
/**
|
|
49
|
+
* A subject's flags: its value is a number; it has changes pending; it has older stretches; the
|
|
50
|
+
* stretch it is playing was found landed, which holds until another replaces it.
|
|
51
|
+
*/
|
|
52
|
+
export declare const enum RunFlag {
|
|
53
|
+
SCALAR = 1,
|
|
54
|
+
PENDING = 2,
|
|
55
|
+
OLDER = 4,
|
|
56
|
+
LANDED = 8
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The mix playing a patch, asked with its voice's id: an owner and an id, where a pair of closures
|
|
60
|
+
* cost two functions, their contexts and a weak reference per voice.
|
|
61
|
+
*/
|
|
62
|
+
export interface MotionOwner {
|
|
63
|
+
/** The subject's voice time at the mix's latest frame; NaN where no frame of the voice met it. */
|
|
64
|
+
frame(id: number, subject: unknown): number;
|
|
65
|
+
/**
|
|
66
|
+
* Brings a subject the voice faded out back to it, so a change is not made for nothing: its
|
|
67
|
+
* voice time now, which a change bringing it back is made at; NaN for any other subject.
|
|
68
|
+
*/
|
|
69
|
+
revive(id: number, subject: unknown): number;
|
|
70
|
+
/** The mix clock at its latest frame; NaN before the first. */
|
|
71
|
+
now(): number;
|
|
72
|
+
/** The number of the frame the mix is playing. */
|
|
73
|
+
seq(): number;
|
|
74
|
+
/** Whether a change made now comes before anything read the frame. */
|
|
75
|
+
early(): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* The host made a change, already given its time, which a seek makes again on the patch its
|
|
78
|
+
* voice plays then: a voice a history store paged out comes back with a patch of its own.
|
|
79
|
+
*/
|
|
80
|
+
changed<I>(id: number, motion: Motions<I>, subject: I, change: Change): void;
|
|
81
|
+
/** History leaving memory for a subject, which a mix with a history store pages out. */
|
|
82
|
+
page(id: number, stream: 'released' | 'stretch', subject: unknown, out: Paging[]): void;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* What a mix keeping copies of a patch's stretches is told, with its id and the subject's number,
|
|
86
|
+
* before one changes.
|
|
87
|
+
*/
|
|
88
|
+
export interface Watcher {
|
|
89
|
+
stretchChanged(id: number, s: number): void;
|
|
90
|
+
}
|
|
91
|
+
export interface Shape<I> {
|
|
92
|
+
from: (subject: I) => number[];
|
|
93
|
+
/** How fast each axis starts moving, given where it starts: still unless the options say. */
|
|
94
|
+
velocity: (subject: I, x: number[]) => number[];
|
|
95
|
+
/**
|
|
96
|
+
* Where it heads when released from `x` at `v`, given where it was heading; `put` for a stretch a
|
|
97
|
+
* `set` released.
|
|
98
|
+
*/
|
|
99
|
+
aim: (x: number[], v: number[], was: number[] | null, subject: I, put: boolean) => number[];
|
|
100
|
+
/** `[form, settle, k1, k2, k3]`. */
|
|
101
|
+
law: readonly number[];
|
|
102
|
+
/** Whether a subject's value is a number rather than an array. */
|
|
103
|
+
scalar: (subject: I) => boolean;
|
|
104
|
+
/** Whether every subject's value is a number; undefined where that depends on the subject. */
|
|
105
|
+
scalarEvery(): boolean | undefined;
|
|
106
|
+
/** A tween's easing, which its law cannot hold; undefined, but present, on every other shape. */
|
|
107
|
+
ease: Curve | undefined;
|
|
108
|
+
/** A tween's ms for a stretch the subject starts now; undefined, but present, on the rest. */
|
|
109
|
+
ms: ((subject: I) => number) | undefined;
|
|
110
|
+
/** The patch's kind and constants, as `Patch.motion` reports them. */
|
|
111
|
+
spec(): MotionSpec;
|
|
112
|
+
}
|
package/dist/stretch.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Where `numbers` gathers what it copies, kept an array of doubles so every copy is one too. */
|
|
2
|
+
const gathered = [0.5];
|
|
3
|
+
/** `n` numbers of `from` from `at`, as an array of their own. */
|
|
4
|
+
export function numbers(from, at, n) {
|
|
5
|
+
// `Array.from` walks a typed array by its iterator: about 500 B a call for three axes.
|
|
6
|
+
for (let i = 0; i < n; i++)
|
|
7
|
+
gathered[i] = from[at + i];
|
|
8
|
+
return gathered.slice(0, n);
|
|
9
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { Stamp } from './restored.js';
|
|
2
|
+
/**
|
|
3
|
+
* A subject's record as it left a voice, faded out of it or dropped: the mix time and frame it
|
|
4
|
+
* left at, and `sync` where it shows in its own frame: left in the frame's sync, or before anything
|
|
5
|
+
* read the mix that frame.
|
|
6
|
+
*/
|
|
7
|
+
export interface Left<I> {
|
|
8
|
+
subject: I;
|
|
9
|
+
at: number;
|
|
10
|
+
seq: number;
|
|
11
|
+
sync: boolean;
|
|
12
|
+
held: Subject<unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* The voice's fade of the subject as it stood on leaving, which a seek or read back to before
|
|
15
|
+
* puts back: its ramp out, while running, or its having faded out.
|
|
16
|
+
*/
|
|
17
|
+
ramp: Parting | null;
|
|
18
|
+
parted: Parted | null;
|
|
19
|
+
}
|
|
20
|
+
/** A subject's ramp out of one voice: when it began, how long it runs, and the frame it was asked in. */
|
|
21
|
+
export interface Parting {
|
|
22
|
+
at: number;
|
|
23
|
+
over: number;
|
|
24
|
+
seq: number;
|
|
25
|
+
}
|
|
26
|
+
export interface Parted {
|
|
27
|
+
at: number;
|
|
28
|
+
seq: number;
|
|
29
|
+
ramp: Parting | undefined;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* What a copy of a record keeps for the seeks that put a record back from it: the copy's state
|
|
33
|
+
* stepped on to `ticks`, as it stood near `at`, a moment one of them sought.
|
|
34
|
+
*/
|
|
35
|
+
export interface Ahead<S> {
|
|
36
|
+
/** The record the last seek put back, and the moment it sought, until that record steps. */
|
|
37
|
+
put: Subject<S> | null;
|
|
38
|
+
sought: number;
|
|
39
|
+
/** The `since` of the record put back, which the steps in `state` were counted from. */
|
|
40
|
+
since: number;
|
|
41
|
+
at: number;
|
|
42
|
+
ticks: number;
|
|
43
|
+
/** Undefined until a record has stepped on from the copy. */
|
|
44
|
+
state: S | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* The state the record a seek replaced had stepped on to from the copy, at `leftTicks`; undefined
|
|
47
|
+
* until one had.
|
|
48
|
+
*/
|
|
49
|
+
leftTicks: number;
|
|
50
|
+
left: S | undefined;
|
|
51
|
+
}
|
|
52
|
+
/** A copy of a record kept under `history`, by the mix time and frame it was taken at. */
|
|
53
|
+
export interface Snap<S> {
|
|
54
|
+
at: number;
|
|
55
|
+
seq: number;
|
|
56
|
+
held: Subject<S>;
|
|
57
|
+
ahead?: Ahead<S>;
|
|
58
|
+
/** Set where another record's list holds it too, so a seek cutting it from one lets go of it. */
|
|
59
|
+
shared?: true;
|
|
60
|
+
}
|
|
61
|
+
/** Everything one voice holds for one subject, chained through the next voice that reaches it. */
|
|
62
|
+
export interface Subject<S> {
|
|
63
|
+
/** The voice's `target` answer, fixed on first sight. */
|
|
64
|
+
reaches: boolean;
|
|
65
|
+
/** The voice's `stagger` answer in voice ms, fixed on first sight. */
|
|
66
|
+
delay: number;
|
|
67
|
+
/** The mix timestamp this subject's delay ran out at, which its step grid counts from. */
|
|
68
|
+
since: number;
|
|
69
|
+
/** The mix timestamp its fade in counts from: `since`, or earlier for a voice freezing before. */
|
|
70
|
+
shown: number;
|
|
71
|
+
/** The weight this voice gave this subject the last frame it was probed, 0 where it gave none. */
|
|
72
|
+
weight: number;
|
|
73
|
+
/**
|
|
74
|
+
* The mix time it was left at rest during a handover, so it stops contributing, and which a seek
|
|
75
|
+
* reads to know whether it had by then; NaN while it has not (`isRested`).
|
|
76
|
+
*/
|
|
77
|
+
restedAt: number;
|
|
78
|
+
/**
|
|
79
|
+
* Per written channel, whether a rest-less contribution is on: 0 unknown, 1 on, 2 off; null until
|
|
80
|
+
* a rest-less channel asks.
|
|
81
|
+
*/
|
|
82
|
+
bands: Uint8Array | null;
|
|
83
|
+
state: S;
|
|
84
|
+
/** The `now` this subject last caught up to, for this voice. */
|
|
85
|
+
stepped: number;
|
|
86
|
+
/** Under `stepMs`, how many intervals have run since `since`; `stepped` is where that lands. */
|
|
87
|
+
ticks: number;
|
|
88
|
+
/** The `now` this subject was last probed at, so twelve probes in a frame step once. */
|
|
89
|
+
probed: number;
|
|
90
|
+
/** The last delta computed this frame, handed back to a repeat probe unchanged. */
|
|
91
|
+
delta: Record<string, unknown> | null;
|
|
92
|
+
/** The phase `delta` was read at, to read it again once its voice's scratch has moved on. */
|
|
93
|
+
phase: number;
|
|
94
|
+
/** The voice's `seeks` when `delta` was read, so a seek later in the frame reads it again. */
|
|
95
|
+
seeks: number;
|
|
96
|
+
/** The voice's `rebuilds` when its state was last made, so a seek's rebuild reaches it once. */
|
|
97
|
+
rebuilt: number;
|
|
98
|
+
/** Its kept signal state was stepped on in one go from a copy history kept, by a seek. */
|
|
99
|
+
copied: boolean;
|
|
100
|
+
/** Stop 0 for a `from: 'current'` voice, taken the first frame this subject is seen. */
|
|
101
|
+
base?: Record<string, unknown>;
|
|
102
|
+
/** The pose's velocity per channel at that moment, units per ms, so the first segment leaves at it. */
|
|
103
|
+
slope?: Record<string, unknown>;
|
|
104
|
+
/** What `setting.keep` holds for this voice and subject, by owner; null until it holds any. */
|
|
105
|
+
kept: Map<object, unknown> | null;
|
|
106
|
+
/** Kept state a history store gave back, which each owner takes in turn on its first keep. */
|
|
107
|
+
unkept?: unknown[];
|
|
108
|
+
/** Under `history`, copies of this record by the mix time they were taken, oldest first. */
|
|
109
|
+
snaps?: Snap<S>[];
|
|
110
|
+
/** In a projection: where this record started from, and whether nothing known could be. */
|
|
111
|
+
from?: number;
|
|
112
|
+
unknown?: boolean;
|
|
113
|
+
/** Under `history` with `inputs`, what an input weight signal read for this subject, when it changed. */
|
|
114
|
+
inputs?: {
|
|
115
|
+
at: number;
|
|
116
|
+
seq: number;
|
|
117
|
+
value: number;
|
|
118
|
+
}[];
|
|
119
|
+
/** In a projection reading back: that record, to read in place of the signal. */
|
|
120
|
+
replay?: {
|
|
121
|
+
at: number;
|
|
122
|
+
value: number;
|
|
123
|
+
}[];
|
|
124
|
+
/** The voice this is the record of; null on a subject's stub. */
|
|
125
|
+
voice: object | null;
|
|
126
|
+
/** The next voice's record for this subject, in voice order, among those that reach it. */
|
|
127
|
+
next: Subject<unknown> | null;
|
|
128
|
+
/**
|
|
129
|
+
* On the first record, which the mix keeps per subject so a probe makes one lookup: the `version`
|
|
130
|
+
* the chain was linked at, and per role, by channel slot, whether a rest-less contribution is on:
|
|
131
|
+
* 0 never decided, 1 on, 2 off.
|
|
132
|
+
*/
|
|
133
|
+
version: number;
|
|
134
|
+
roles: Map<string, Uint8Array> | null;
|
|
135
|
+
/** On the first record: the number lanes index this subject by, -1 without one. */
|
|
136
|
+
slot: number;
|
|
137
|
+
/** A motion voice's number for this subject in its patch, good while `freed` is the patch's own. */
|
|
138
|
+
run: number;
|
|
139
|
+
freed: number;
|
|
140
|
+
/** In a voice's store once a seek has gone back, the stamp of the seek it was last put back or written at. */
|
|
141
|
+
restored: Stamp | null;
|
|
142
|
+
}
|
package/dist/subject.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/tape.d.ts
CHANGED
|
@@ -18,15 +18,18 @@ export declare function unplayable(what: string): never;
|
|
|
18
18
|
* at: rate 0 holds mix time still across frames, and host time tells them apart.
|
|
19
19
|
*/
|
|
20
20
|
export declare function tapeOf(transport: Transport): Tape | undefined;
|
|
21
|
+
interface Recorder {
|
|
22
|
+
readonly tape: Tape | undefined;
|
|
23
|
+
readonly replaying: boolean;
|
|
24
|
+
readonly projecting: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** Whether `record` keeps a call made on `on` now, so a caller can skip making one it would drop. */
|
|
27
|
+
export declare function taping(on: Recorder): boolean;
|
|
21
28
|
/**
|
|
22
29
|
* Records a host call, which `again` makes once more when the mix plays past it after a seek, or
|
|
23
30
|
* when a read ahead does, on the stage it is given.
|
|
24
31
|
*/
|
|
25
|
-
export declare function record(on:
|
|
26
|
-
readonly tape: Tape | undefined;
|
|
27
|
-
readonly replaying: boolean;
|
|
28
|
-
readonly projecting: boolean;
|
|
29
|
-
}, label: string, again: (stage: Stage) => void): void;
|
|
32
|
+
export declare function record(on: Recorder, label: string, again: (stage: Stage) => void): void;
|
|
30
33
|
/** The host time at which the mix clock reads `t`, under the rate it has now. */
|
|
31
34
|
export declare function hostAt(clock: {
|
|
32
35
|
readonly pace: Pace | null;
|
|
@@ -47,3 +50,4 @@ export declare function hostAhead(clock: {
|
|
|
47
50
|
* stage of its copies, it makes them on those, and the caller puts the tape back.
|
|
48
51
|
*/
|
|
49
52
|
export declare function replay(transport: Transport, due: (u: number) => boolean, onto?: Transport, on?: Stage): number;
|
|
53
|
+
export {};
|
package/dist/tape.js
CHANGED
|
@@ -24,13 +24,17 @@ export function tapeOf(transport) {
|
|
|
24
24
|
coalesceWindowMs: 0,
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
+
/** Whether `record` keeps a call made on `on` now, so a caller can skip making one it would drop. */
|
|
28
|
+
export function taping(on) {
|
|
29
|
+
return on.tape !== undefined && !on.replaying && !on.projecting;
|
|
30
|
+
}
|
|
27
31
|
/**
|
|
28
32
|
* Records a host call, which `again` makes once more when the mix plays past it after a seek, or
|
|
29
33
|
* when a read ahead does, on the stage it is given.
|
|
30
34
|
*/
|
|
31
35
|
export function record(on, label, again) {
|
|
32
36
|
const tape = on.tape;
|
|
33
|
-
if (tape === undefined || on
|
|
37
|
+
if (tape === undefined || !taping(on))
|
|
34
38
|
return;
|
|
35
39
|
const op = {
|
|
36
40
|
label,
|
package/dist/then.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Cut } from './cut.js';
|
|
2
|
+
import type { Motions } from './motions.js';
|
|
3
|
+
/**
|
|
4
|
+
* `subject`'s position and velocity at voice time `t`, into the patch's `xs` and `vs`, as frame `cut` left it:
|
|
5
|
+
* in the run it had then, with only the changes made by then, as a seek back there plays it. A run
|
|
6
|
+
* with none made since reads as it stands; one with some plays from its earliest stretch kept,
|
|
7
|
+
* the rest applied again in the order they take effect. Commits nothing, and gives the run's flags.
|
|
8
|
+
*/
|
|
9
|
+
export declare function sampleThen<I>(motions: Motions<I>, subject: I, t: number, cut: Cut): number;
|
package/dist/then.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { within } from './cut.js';
|
|
2
|
+
/** Whether `c` was made by frame `cut`; one made with no mix playing its patch was. */
|
|
3
|
+
const madeBy = (c, cut) => c.seq === undefined || Number.isNaN(c.seq) || within(cut, c.seq, c.early === true);
|
|
4
|
+
/** The stretch playing at voice time `t` of a run's, oldest first: the latest released by then, else the first. */
|
|
5
|
+
function playingIn(list, t) {
|
|
6
|
+
const last = list[list.length - 1];
|
|
7
|
+
if (t >= last.at || list.length === 1)
|
|
8
|
+
return last;
|
|
9
|
+
if (list[0].at > t)
|
|
10
|
+
return list[0];
|
|
11
|
+
let lo = 0;
|
|
12
|
+
let hi = list.length - 2;
|
|
13
|
+
while (lo < hi) {
|
|
14
|
+
const mid = (lo + hi + 1) >> 1;
|
|
15
|
+
if (list[mid].at <= t)
|
|
16
|
+
lo = mid;
|
|
17
|
+
else
|
|
18
|
+
hi = mid - 1;
|
|
19
|
+
}
|
|
20
|
+
return list[lo];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* `subject`'s position and velocity at voice time `t`, into the patch's `xs` and `vs`, as frame `cut` left it:
|
|
24
|
+
* in the run it had then, with only the changes made by then, as a seek back there plays it. A run
|
|
25
|
+
* with none made since reads as it stands; one with some plays from its earliest stretch kept,
|
|
26
|
+
* the rest applied again in the order they take effect. Commits nothing, and gives the run's flags.
|
|
27
|
+
*/
|
|
28
|
+
export function sampleThen(motions, subject, t, cut) {
|
|
29
|
+
const run = motions.runThen(subject, cut) ?? motions.freshRun(subject);
|
|
30
|
+
let stretches = run.stretches;
|
|
31
|
+
let pending = run.pending ?? [];
|
|
32
|
+
const kept = [];
|
|
33
|
+
let since = false;
|
|
34
|
+
for (const c of [...stretches.slice(1).map((seg) => seg.change), ...pending])
|
|
35
|
+
if (c === undefined || madeBy(c, cut)) {
|
|
36
|
+
if (c !== undefined)
|
|
37
|
+
kept.push(c);
|
|
38
|
+
}
|
|
39
|
+
else
|
|
40
|
+
since = true;
|
|
41
|
+
if (since) {
|
|
42
|
+
stretches = [stretches[0]];
|
|
43
|
+
pending = kept
|
|
44
|
+
.filter((c) => c.at !== undefined)
|
|
45
|
+
.sort((a, b) => a.at - b.at);
|
|
46
|
+
}
|
|
47
|
+
let seg = playingIn(stretches, t);
|
|
48
|
+
for (const c of pending) {
|
|
49
|
+
const a = c.at;
|
|
50
|
+
if (a === undefined || !(a <= t))
|
|
51
|
+
break;
|
|
52
|
+
if (a >= seg.at)
|
|
53
|
+
seg = motions.applied(subject, seg, c, a);
|
|
54
|
+
}
|
|
55
|
+
motions.evaluateSegment(seg, t, motions.xs, motions.vs);
|
|
56
|
+
return run.flags;
|
|
57
|
+
}
|