@msb235/blits 0.16.0 → 0.16.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.
- package/CHANGELOG.md +149 -0
- package/README.md +1 -1
- package/dist/ahead.js +6 -9
- package/dist/clock.d.ts +11 -1
- package/dist/clock.js +28 -9
- package/dist/contribution.js +11 -4
- package/dist/cue.js +12 -3
- package/dist/due.d.ts +18 -0
- package/dist/due.js +101 -2
- package/dist/fade.d.ts +12 -2
- package/dist/fade.js +41 -8
- package/dist/fill.js +2 -1
- package/dist/fit.js +8 -0
- package/dist/gone.js +1 -0
- package/dist/handle.d.ts +1 -1
- package/dist/handle.js +3 -1
- package/dist/held.d.ts +2 -1
- package/dist/held.js +14 -6
- package/dist/hosts.js +5 -1
- package/dist/lane.d.ts +2 -0
- package/dist/lane.js +2 -0
- package/dist/lanes.js +2 -1
- package/dist/meet.js +9 -0
- package/dist/mixer.d.ts +2 -0
- package/dist/mixer.js +2 -0
- package/dist/motion.js +6 -6
- package/dist/move.js +309 -82
- package/dist/origin.d.ts +2 -0
- package/dist/origin.js +4 -0
- package/dist/owner.js +4 -2
- package/dist/paging.d.ts +5 -1
- package/dist/paging.js +13 -3
- package/dist/patch.js +24 -4
- package/dist/place.js +67 -31
- package/dist/project.d.ts +5 -2
- package/dist/project.js +7 -4
- package/dist/qualify.js +6 -1
- package/dist/rests.js +9 -1
- package/dist/revive.d.ts +11 -1
- package/dist/revive.js +7 -0
- package/dist/roles.js +5 -2
- package/dist/rows.js +4 -1
- package/dist/seat.d.ts +5 -1
- package/dist/seat.js +5 -1
- package/dist/seek.js +6 -4
- package/dist/shared.d.ts +1 -1
- package/dist/shared.js +1 -1
- package/dist/spans.d.ts +10 -4
- package/dist/spans.js +121 -31
- package/dist/trace.js +7 -2
- package/dist/types/transport.d.ts +2 -1
- package/dist/voice.d.ts +42 -4
- package/dist/voice.js +45 -5
- package/dist/weigh.js +1 -1
- package/docs/schema.html +1 -1
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,155 @@ This package follows [semver](https://semver.org). Below 1.0.0, a breaking chang
|
|
|
4
4
|
version and everything else the patch. Each release lists its changes as **Breaking**, **Added** and
|
|
5
5
|
**Fixed**, and the release workflow refuses a tag with no section here.
|
|
6
6
|
|
|
7
|
+
## 0.16.1
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- A child anchored to a sibling under an owner at rate 0 plays where its anchor puts it when the
|
|
12
|
+
owner is sought forward past both. Its anchor's time went through mix time on a clock standing
|
|
13
|
+
still, which maps every time it has passed to the seek's moment, so the child was placed at the
|
|
14
|
+
time sought plus its offset and never played.
|
|
15
|
+
- `inert` reads false while a motion voice has a subject it met inside its `stagger` and has not
|
|
16
|
+
read since, which moves once it is probed. It read true, so a host that stops on `inert` stopped
|
|
17
|
+
a subject that had yet to start.
|
|
18
|
+
- A span's child whose cue a seek back plays again waits for the span's fit, as it did when first
|
|
19
|
+
cued, so the span lays it out as before. It started at once wherever its span's clock had passed
|
|
20
|
+
the start the fit first gave it: the fit then took it for ended, so it skipped nothing else, or,
|
|
21
|
+
in a span running back, the child ran back to its start and left.
|
|
22
|
+
- A child its owner's seek put back before its start stays where it was through a mix sought back to
|
|
23
|
+
after that seek: a later fit keeps it in its place, and an anchor does not place it again. The
|
|
24
|
+
seek took it for one still to start, so a span laid it out afresh, and a child anchored to a
|
|
25
|
+
sibling's end was placed again from that end, and played on as if its owner had not been sought.
|
|
26
|
+
- A voice cued while the mix is paused, its `start` a host time still ahead, waits for that time
|
|
27
|
+
again when a seek back plays its cue again. It started at once, at the paused mix time, since
|
|
28
|
+
the voice was taken for one cued while the mix slept and its start was moved to the next sync.
|
|
29
|
+
- A span whose budget runs out between two frames fits again from that moment, in turn with its
|
|
30
|
+
siblings' starts and ends: a child it places at a start already passed starts in that frame, and
|
|
31
|
+
one queued after it starts where it lapsed at any frame rate. The span lapsed only after the
|
|
32
|
+
frame had started its siblings, so a sibling whose old start the frame passed kept it, and one
|
|
33
|
+
placed in the past waited a frame to start.
|
|
34
|
+
- An owner running back holds a child placed where its clock stands unplayed, whether or not a
|
|
35
|
+
frame lands there. An owner cued backward with a child at 0 started that child, ended it, and
|
|
36
|
+
left with it only when a frame fell exactly on the owner's start; otherwise both waited for good.
|
|
37
|
+
- A read ahead that throws partway, on a call the tape holds that it cannot play, puts the tape
|
|
38
|
+
back. It left the live tape moved past the call, so a later seek restored the wrong state.
|
|
39
|
+
- Under `history`, a voice faded out of one subject is let go when history lets it go. The mix
|
|
40
|
+
held every such voice for good, and each `drop` walked them all.
|
|
41
|
+
- What `prepare` loads is let go once `history.ms` of mix time passes without a seek or read
|
|
42
|
+
taking it. Until one did, the mix kept every record paged since, for good.
|
|
43
|
+
- `trace` of a voice sought past its end, with a `fade.out`, follows the fade from the voice's
|
|
44
|
+
start, where the mix runs it. It called the run exact and read 0 throughout.
|
|
45
|
+
- `weightOf` with lanes on reports the weight a subject was given when its voice moved onto a lane
|
|
46
|
+
after the subject's probe that frame. It reported 0.
|
|
47
|
+
- A `pull` right after two or more cues folds the new voices in voice order, as a probe does. It
|
|
48
|
+
folded them newest first, which on a `mul` or `sum` channel could differ in the last bit.
|
|
49
|
+
- An owner's fade in counts from when the owner started, under an owner turned back or slowed.
|
|
50
|
+
It was worked out again each frame from where the outer owner's clock would next reach the
|
|
51
|
+
inner owner's start, so a clip under an inner owner with a fade in vanished when the outer
|
|
52
|
+
owner turned back, and slowing the outer owner jumped the fade to its end.
|
|
53
|
+
- A fade set for now or earlier, on a voice whose owner runs back, begins then. It was put off to
|
|
54
|
+
when the owner's clock would come back to the voice's start, playing at full weight until then.
|
|
55
|
+
- A fade in stands still while a fade out runs, and climbs on from there after `rise`, rather than
|
|
56
|
+
jumping to where its clock had reached. A subject's own fade out stops its fade in climbing too,
|
|
57
|
+
as the voice's does; before, the two multiplied into a rise.
|
|
58
|
+
- A span child with `faster` or `slower` at `Infinity` is retimed to fill its span. It got a rate
|
|
59
|
+
of Infinity, jumping to its end, or 0, and was skipped as never fitting.
|
|
60
|
+
- A `from: 'current'` voice whose stop lists fewer axes than its `vec` channel reads the missing
|
|
61
|
+
axes as the lerp does. They read NaN.
|
|
62
|
+
- `spring` and `glide` throw for an infinite parameter. A glide with an infinite `ms` or
|
|
63
|
+
`stiffness` read NaN, and a spring with infinite `damping` or `mass` jumped to its target.
|
|
64
|
+
- A role member at weight 0 holds its role's place whatever its patch's form, and a band switched
|
|
65
|
+
off by a frame at weight 0 stays off when the weight comes back between `off` and `on`. A `keys`
|
|
66
|
+
member at weight 0 was read and so held the role's place; any other was not, so its role folded
|
|
67
|
+
at the next member's place, which on a `last` channel changed which value won, and the role's
|
|
68
|
+
band, along with every band in a mix with a role in it, survived the frame at 0.
|
|
69
|
+
- A frame with span children in it costs less than before this release's span-order fixes, which
|
|
70
|
+
had made it about 5% dearer: a child's visit reads its owners' clocks once, and the voices
|
|
71
|
+
visited every frame, which include every voice an owner holds, are kept in a list of their own
|
|
72
|
+
rather than sifted through the due queue's heap and sorted each frame. The new `spans` bench row,
|
|
73
|
+
ten staggered clips cued under one owner every four frames, reads 5.0% faster than with the
|
|
74
|
+
fixes (0.141 to 0.134 ms a frame) and 3.4% faster than before them (0.179 to 0.173 on another
|
|
75
|
+
node), 7 rounds each, U 7.
|
|
76
|
+
- A voice an owner holds, first read while the owner runs back, shows at once instead of waiting
|
|
77
|
+
on its fade in. The fade in counted from where the owner's clock, running back, would next reach
|
|
78
|
+
the voice's start, a time still to come, so the voice read nothing until then.
|
|
79
|
+
- An owner sought forward passes the span children it crosses as played straight through,
|
|
80
|
+
whatever its rate: they start and end in order, and their span fits from each end. Sought ahead
|
|
81
|
+
while turned back, it started them in the order its clock running back put their starts, last
|
|
82
|
+
first, and ended none; turning forward later ended them all at once, out of order, and a span
|
|
83
|
+
holding spans left in a later frame at a time still to come, so a mix sought back left it
|
|
84
|
+
fading and its clips silent. A timeline's reel jumped ahead in reverse played its scenes slowed
|
|
85
|
+
and overlapping.
|
|
86
|
+
- A span child kept quiet at its start, its owner having run back to it, comes back and ends in
|
|
87
|
+
turn when a seek of the owner takes it past its end, and its span fits from that end. It stayed
|
|
88
|
+
quiet, so the children playing past it kept the rate they had before its end.
|
|
89
|
+
- Under `history`, an owner another owner holds no longer leaves once none of its children plays:
|
|
90
|
+
it stays with its own owner, quiet, so turning the outer owner back brings back a clip under an
|
|
91
|
+
owner inside it, as it does a direct child.
|
|
92
|
+
- A span an owner ran back over keeps the layout it made going forward. Before, each child it ran
|
|
93
|
+
back to the start of fitted the span again from there, and so did a seek back into it, counting
|
|
94
|
+
the children waiting for the clock to come back as playing: the ones still to start moved
|
|
95
|
+
earlier, and the playing ones changed speed.
|
|
96
|
+
- A span lays out its children the same at any frame rate. When a child ended between two frames,
|
|
97
|
+
its span fitted again from that moment, and a sibling whose start had passed by then, but which
|
|
98
|
+
no frame had started yet, was placed again from there at a new start and rate: two children of a
|
|
99
|
+
`stagger` span read 420 stepped every 10 ms and 389.47 every 25 ms.
|
|
100
|
+
- A voice under an owner whose rate ramps through 0 (from forward to back) ends where its passes
|
|
101
|
+
run out while the owner still runs forward. The time was taken as never, so it ended at the
|
|
102
|
+
frame that noticed, and a span holding it fitted again from there, its layout following the
|
|
103
|
+
frame rate.
|
|
104
|
+
- A placement whose names make a loop is taken where no mark in it waits on itself: a cue whose
|
|
105
|
+
`out` is its follow's `start`, the follow starting off the cue's `coast`, was refused as waiting
|
|
106
|
+
on itself, since the check followed every name whatever mark it asked for. A voice's `start`,
|
|
107
|
+
`in`, `coast` and the marks placed inside it wait only on what places its start; its `out` and
|
|
108
|
+
`end` on that and what places its out.
|
|
109
|
+
- A placement that waits on itself through a query by tag or by channel is refused at `cue`, as
|
|
110
|
+
one through names is: a voice starting after the one tagged `b`, cued beside one tagged `b`
|
|
111
|
+
starting after the one tagged `a`, was taken. A query is followed to the voices it can pick at
|
|
112
|
+
the cue, the voice being cued among them.
|
|
113
|
+
- A voice no owner holds, placed by a timestamp in its anchor, stays where it was placed through a
|
|
114
|
+
seek back. Each placing read the timestamp against the mix's offset at that moment, which a seek
|
|
115
|
+
moves, so after `seek(1000)` from 2100 an owner anchored to start at 2000 had started at 900, and
|
|
116
|
+
one anchored to end at 4000 left at 0. The timestamp is now read against the offset in force when
|
|
117
|
+
the voice was cued, as a plain `start` is.
|
|
118
|
+
- Under `history`, a span an owner holds no longer leaves once its budget is out and none of its
|
|
119
|
+
children plays: it stays with its owner, quiet, so turning the owner back brings back a clip
|
|
120
|
+
under a span, or under a span in a span, as it does one under an owner.
|
|
121
|
+
- A child cued into a span an owner ran back over queues after the children it holds. The span
|
|
122
|
+
was fitted again at the cue, counting the children waiting for the clock to come back as
|
|
123
|
+
playing, so the new one started beside them. No fit, from a cue, an arc, a sibling leaving or a
|
|
124
|
+
seek, moves a span run back over until the clock passes it again.
|
|
125
|
+
- A child whose rate a fit changed reads, run back over by an owner, what it read going forward. Its
|
|
126
|
+
clock read the new rate all the way back past where the fit changed it; it now runs back on the
|
|
127
|
+
clock it had before, and forward again the fit changes it at the same place.
|
|
128
|
+
- An owner sought past a span's child fits the rest of the span from where that child ended. The
|
|
129
|
+
owner's clock now put the end before the mix began, where a span read its own clock as 0, so the
|
|
130
|
+
children still to come were laid out from the span's start: a timeline's reel jumped into its
|
|
131
|
+
fifth sequence started the third scene beside the second.
|
|
132
|
+
- A span whose children end in one frame fits from each end in the order they came. They were
|
|
133
|
+
taken in cue order, so an end the frame noticed first could be one that a fit from an earlier
|
|
134
|
+
end would have moved, and two children of a `stagger` span read 509.5 stepped every 50 ms where
|
|
135
|
+
1 ms steps read 525.3.
|
|
136
|
+
- A child a fit skipped reads, once its owner is sought or run back before the skip, what it read
|
|
137
|
+
going forward. One skipped while playing kept its jump to its end and read on from there, and
|
|
138
|
+
one skipped before it started, placed at its end at its start, played its passes backward from
|
|
139
|
+
its start.
|
|
140
|
+
- A span child its owner ran or was sought back over takes the change a fit made to it again as
|
|
141
|
+
the owner's clock comes forward to it, as the layout made going forward stands. The span was
|
|
142
|
+
fitted again there instead, with the children it had skipped counted as still playing or left
|
|
143
|
+
out, so the ones it had skipped together with them played on, slowed, beside them.
|
|
144
|
+
- An owner sought past a span lays the span out in that frame as played straight through. Its
|
|
145
|
+
children that started and ended in the frame were taken in cue order once their span had started,
|
|
146
|
+
each started and ended in one step, so a fit from one end missed the children it should have
|
|
147
|
+
moved.
|
|
148
|
+
- A span its owner was sought back before keeps the layout it made going forward, as one run
|
|
149
|
+
back over does. A child's end there fitted it again with the children ahead, left quiet by the
|
|
150
|
+
seek, counted as gone, so the playing ones slowed to fill the span.
|
|
151
|
+
- A mix sought back into a span plays on as it stood at that moment. A seek fitted every span
|
|
152
|
+
again from the moment sought, which a span played straight through is not, so its children's
|
|
153
|
+
rates followed where the seek landed. A span's end and `result` are now kept with its history and
|
|
154
|
+
put back by the seek.
|
|
155
|
+
|
|
7
156
|
## 0.16.0
|
|
8
157
|
|
|
9
158
|
### Added
|
package/README.md
CHANGED
|
@@ -151,7 +151,7 @@ do yet.
|
|
|
151
151
|
|
|
152
152
|
## Status
|
|
153
153
|
|
|
154
|
-
v0.16.
|
|
154
|
+
v0.16.1, with five consumers. magicsmoke runs every fault on it, wod its wheel, tricks and
|
|
155
155
|
transitions, weasel its animator's tweens, sherpa its seams, and
|
|
156
156
|
[klieg](https://github.com/orochi235/klieg) composes all three of its systems — letter motion,
|
|
157
157
|
part effects, and the environment a sign is lit by — on a mix each.
|
package/dist/ahead.js
CHANGED
|
@@ -20,13 +20,12 @@ import { unreached } from './unreached.js';
|
|
|
20
20
|
export function playAhead(transport, c, copies, t, earlier) {
|
|
21
21
|
const tape = transport.tape;
|
|
22
22
|
const depth = tape?.undoDepth() ?? 0;
|
|
23
|
-
let played = false;
|
|
24
23
|
try {
|
|
25
24
|
for (;;) {
|
|
26
25
|
const e = nextEvent(tape, c, t);
|
|
27
26
|
if (!(e < t))
|
|
28
27
|
break;
|
|
29
|
-
|
|
28
|
+
playTo(transport, c, copies, e);
|
|
30
29
|
if (!carries(c))
|
|
31
30
|
continue;
|
|
32
31
|
// Placed now, as the next move would place them, while a target that has just left is here.
|
|
@@ -49,11 +48,12 @@ export function playAhead(transport, c, copies, t, earlier) {
|
|
|
49
48
|
}
|
|
50
49
|
c = next;
|
|
51
50
|
}
|
|
52
|
-
|
|
51
|
+
playTo(transport, c, copies, t);
|
|
53
52
|
}
|
|
54
53
|
finally {
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
// A call that throws partway has moved the tape already, so this asks the tape, not `playTo`.
|
|
55
|
+
if (tape !== undefined && tape.undoDepth() !== depth)
|
|
56
|
+
tape.goto(depth);
|
|
57
57
|
}
|
|
58
58
|
return c;
|
|
59
59
|
}
|
|
@@ -108,14 +108,13 @@ function nextEvent(tape, c, t) {
|
|
|
108
108
|
}
|
|
109
109
|
/**
|
|
110
110
|
* Makes the calls the tape holds up to mix time `to` on a read ahead's copies and moves them
|
|
111
|
-
* there.
|
|
111
|
+
* there.
|
|
112
112
|
*/
|
|
113
113
|
function playTo(transport, c, copies, to) {
|
|
114
114
|
const tape = transport.tape;
|
|
115
115
|
const due = (at) => (c.pace === null ? at : c.pace.reading(at)) <= to;
|
|
116
116
|
const next = tape?.timestampAt(tape.undoDepth());
|
|
117
117
|
c.u = hostAhead(c, to);
|
|
118
|
-
let played = false;
|
|
119
118
|
if (next !== undefined && due(next)) {
|
|
120
119
|
// A call may write what a copy shares with the voice it was copied from.
|
|
121
120
|
for (const copy of c.members)
|
|
@@ -125,7 +124,6 @@ function playTo(transport, c, copies, to) {
|
|
|
125
124
|
if (v.parted !== null)
|
|
126
125
|
v.parted = new Map(v.parted);
|
|
127
126
|
}
|
|
128
|
-
played = true;
|
|
129
127
|
const last = replay(transport, due, c, {
|
|
130
128
|
transport: () => c,
|
|
131
129
|
mix: (of) => copies.get(of),
|
|
@@ -136,7 +134,6 @@ function playTo(transport, c, copies, to) {
|
|
|
136
134
|
if (!(c.now >= to))
|
|
137
135
|
for (const copy of c.members)
|
|
138
136
|
move(copy, to);
|
|
139
|
-
return played;
|
|
140
137
|
}
|
|
141
138
|
/** A transport for a read ahead's copies to go on from where those on `c` stand. */
|
|
142
139
|
function restage(c) {
|
package/dist/clock.d.ts
CHANGED
|
@@ -9,6 +9,16 @@ export declare function passesOf(loop: boolean | number | undefined): number;
|
|
|
9
9
|
export declare function phaseAt(elapsed: number, duration: number, passes: number): number;
|
|
10
10
|
/** The pass `elapsed` voice ms falls in, held at the last once they are done. */
|
|
11
11
|
export declare function passAt(elapsed: number, duration: number, passes: number): number;
|
|
12
|
+
/**
|
|
13
|
+
* A fade in from `since` over `fadeIn` at mix time `now`, 0..1. It stands still where a fade out
|
|
14
|
+
* began, or the two ramps multiply into a rise, from `stop` on, and through `stalls`, pairs of mix
|
|
15
|
+
* times.
|
|
16
|
+
*/
|
|
17
|
+
export declare function fadeInAt(fadeIn: number, out: {
|
|
18
|
+
at: number;
|
|
19
|
+
over: number;
|
|
20
|
+
rest: boolean;
|
|
21
|
+
} | null, ease: Curve | undefined, now: number, since: number, stalls: readonly number[] | null, stop?: number): number;
|
|
12
22
|
/**
|
|
13
23
|
* A voice's own fade this frame, 0..1: in from `since` over `fadeIn`, out along its ramp, and back
|
|
14
24
|
* up the same curve where a fade out was turned around.
|
|
@@ -21,7 +31,7 @@ export declare function envelope(fadeIn: number, out: {
|
|
|
21
31
|
from: number;
|
|
22
32
|
at: number;
|
|
23
33
|
over: number;
|
|
24
|
-
} | null): number;
|
|
34
|
+
} | null, stalls?: readonly number[] | null): number;
|
|
25
35
|
/**
|
|
26
36
|
* A subject's voice time `raw` once the voice's freezes apply: 0 before it starts if it freezes
|
|
27
37
|
* before, else NaN, where it shows nothing; `span` once its passes end if it freezes after. A `raw`
|
package/dist/clock.js
CHANGED
|
@@ -21,19 +21,36 @@ export function passAt(elapsed, duration, passes) {
|
|
|
21
21
|
return 0;
|
|
22
22
|
return over(elapsed, duration, passes) ? passes - 1 : Math.floor(elapsed / duration);
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* A fade in from `since` over `fadeIn` at mix time `now`, 0..1. It stands still where a fade out
|
|
26
|
+
* began, or the two ramps multiply into a rise, from `stop` on, and through `stalls`, pairs of mix
|
|
27
|
+
* times.
|
|
28
|
+
*/
|
|
29
|
+
export function fadeInAt(fadeIn, out, ease, now, since, stalls, stop = Number.POSITIVE_INFINITY) {
|
|
30
|
+
let end = out && !out.rest && out.at < now ? out.at : now;
|
|
31
|
+
if (stop < end)
|
|
32
|
+
end = stop;
|
|
33
|
+
let climbed = end - since;
|
|
34
|
+
if (stalls !== null)
|
|
35
|
+
for (let i = 0; i < stalls.length; i += 2) {
|
|
36
|
+
const a = Math.max(stalls[i], since);
|
|
37
|
+
const b = Math.min(stalls[i + 1], end);
|
|
38
|
+
if (b > a)
|
|
39
|
+
climbed -= b - a;
|
|
40
|
+
}
|
|
41
|
+
const u = climbed / fadeIn;
|
|
42
|
+
if (u >= 1)
|
|
43
|
+
return 1;
|
|
44
|
+
return ease ? ease(Math.max(0, u)) : Math.max(0, u);
|
|
45
|
+
}
|
|
24
46
|
/**
|
|
25
47
|
* A voice's own fade this frame, 0..1: in from `since` over `fadeIn`, out along its ramp, and back
|
|
26
48
|
* up the same curve where a fade out was turned around.
|
|
27
49
|
*/
|
|
28
|
-
export function envelope(fadeIn, out, ease, reduced, now, since, back = null) {
|
|
50
|
+
export function envelope(fadeIn, out, ease, reduced, now, since, back = null, stalls = null) {
|
|
29
51
|
let w = 1;
|
|
30
|
-
if (fadeIn > 0 && !reduced)
|
|
31
|
-
|
|
32
|
-
const end = out && !out.rest && out.at < now ? out.at : now;
|
|
33
|
-
const u = (end - since) / fadeIn;
|
|
34
|
-
if (u < 1)
|
|
35
|
-
w *= ease ? ease(Math.max(0, u)) : Math.max(0, u);
|
|
36
|
-
}
|
|
52
|
+
if (fadeIn > 0 && !reduced)
|
|
53
|
+
w *= fadeInAt(fadeIn, out, ease, now, since, stalls);
|
|
37
54
|
if (back && !reduced) {
|
|
38
55
|
const u = back.from + (now - back.at) / back.over;
|
|
39
56
|
if (u < 1)
|
|
@@ -114,7 +131,9 @@ export function timeWith(c, elapsed) {
|
|
|
114
131
|
return r.from > 0 ? c.anchorNow + (elapsed - e0) / r.from : c.anchorNow;
|
|
115
132
|
const a = (r.to - r.from) / (2 * r.over);
|
|
116
133
|
const atEnd = e0 + r.from * r.over + a * r.over * r.over;
|
|
117
|
-
|
|
134
|
+
// A ramp that turns the clock back reads furthest where its rate crosses 0, not at its end.
|
|
135
|
+
const top = r.from > 0 && r.to < 0 ? e0 - (r.from * r.from) / (4 * a) : atEnd;
|
|
136
|
+
if (elapsed <= top) {
|
|
118
137
|
const k = e0 - elapsed;
|
|
119
138
|
const dt = Math.abs(a) < 1e-12
|
|
120
139
|
? -k / r.from
|
package/dist/contribution.js
CHANGED
|
@@ -41,6 +41,7 @@ function sentBack(mix, voice, held) {
|
|
|
41
41
|
function phaseOf(elapsed, duration, passes) {
|
|
42
42
|
return elapsed < 0 ? phaseBefore(elapsed, duration) : phaseAt(elapsed, duration, passes);
|
|
43
43
|
}
|
|
44
|
+
const NOTHING = Object.freeze({});
|
|
44
45
|
/**
|
|
45
46
|
* One live voice's delta for one subject this frame, through its record for the subject, or null
|
|
46
47
|
* when it does not reach. Its weight is left in `w`.
|
|
@@ -139,9 +140,11 @@ export function contribution(voice, subject, now, held) {
|
|
|
139
140
|
own(voice, held);
|
|
140
141
|
}
|
|
141
142
|
let delta = null;
|
|
142
|
-
// A
|
|
143
|
-
//
|
|
143
|
+
// A voice in a role is a member at weight 0 too, and the role folds at its first member's
|
|
144
|
+
// place. A keys member reads; any other is not asked, and contributes nothing.
|
|
145
|
+
let member = false;
|
|
144
146
|
if (silent(voice, weight) && (voice.built === null || voice.spec.role === undefined)) {
|
|
147
|
+
member = voice.spec.role !== undefined;
|
|
145
148
|
// A `from: 'current'` voice takes its base on its first read, silent or not.
|
|
146
149
|
if (voice.built && voice.spec.from === 'current' && held.base === undefined) {
|
|
147
150
|
held.base = this.baseFor(voice, subject);
|
|
@@ -168,8 +171,12 @@ export function contribution(voice, subject, now, held) {
|
|
|
168
171
|
remember(this, voice, subject, held);
|
|
169
172
|
if (reading.kept !== keptBefore && !voice.keeping)
|
|
170
173
|
stateful(this, voice);
|
|
171
|
-
if (delta === null)
|
|
172
|
-
|
|
174
|
+
if (delta === null) {
|
|
175
|
+
if (!member)
|
|
176
|
+
return null;
|
|
177
|
+
this.w = weight;
|
|
178
|
+
return NOTHING;
|
|
179
|
+
}
|
|
173
180
|
if (voice.out?.rest && this.isRest(delta)) {
|
|
174
181
|
held.weight = 0;
|
|
175
182
|
held.restedAt = now;
|
package/dist/cue.js
CHANGED
|
@@ -52,6 +52,7 @@ export function cue(mix, given) {
|
|
|
52
52
|
? Number.POSITIVE_INFINITY
|
|
53
53
|
: mix.now;
|
|
54
54
|
const voice = new Voice(mix.nextId++, spec, patch, spec.fade ?? noFade, local, start, mix.slotOf, mix.channels, mix.opts.host, mix.send, owner);
|
|
55
|
+
voice.offset = mix.offset;
|
|
55
56
|
if (patch === ownerPatch && mix.fitting !== null)
|
|
56
57
|
voice.fitting = mix.fitting;
|
|
57
58
|
if (patch === ownerPatch && mix.arcing !== null) {
|
|
@@ -62,7 +63,10 @@ export function cue(mix, given) {
|
|
|
62
63
|
checkChild(spec, voice);
|
|
63
64
|
if (mix.pace !== null && owner === null && spec.start !== undefined && voice.state === 'pending')
|
|
64
65
|
voice.pinned = spec.start - mix.offset;
|
|
65
|
-
if (start === Number.POSITIVE_INFINITY &&
|
|
66
|
+
if (start === Number.POSITIVE_INFINITY &&
|
|
67
|
+
!anchored &&
|
|
68
|
+
!owner?.fitting &&
|
|
69
|
+
Number.isNaN(voice.pinned)) {
|
|
66
70
|
voice.waking = true;
|
|
67
71
|
mix.waking = true;
|
|
68
72
|
}
|
|
@@ -180,10 +184,13 @@ export function playable(mix, spec) {
|
|
|
180
184
|
if (spec.as !== undefined && mix.transport.pager !== null && !mix.opts.history?.revive)
|
|
181
185
|
throw new Error('blits: a voice cued with as needs history.revive to rebuild it from');
|
|
182
186
|
}
|
|
183
|
-
/**
|
|
187
|
+
/**
|
|
188
|
+
* A voice parked by a seek, cued again as the mix plays past its cue, on the handle it had. A span's
|
|
189
|
+
* child waits for the fit its cue makes again, as it did when first cued: its start is the last fit's.
|
|
190
|
+
*/
|
|
184
191
|
function recue(mix, voice) {
|
|
185
192
|
const owner = voice.owner;
|
|
186
|
-
if ((owner === null ? mix.now : ownerReading(owner, mix.now)) >= voice.start)
|
|
193
|
+
if (!owner?.fitting && (owner === null ? mix.now : ownerReading(owner, mix.now)) >= voice.start)
|
|
187
194
|
voice.state = 'live';
|
|
188
195
|
enter(mix, voice);
|
|
189
196
|
}
|
|
@@ -234,6 +241,8 @@ function enter(mix, voice) {
|
|
|
234
241
|
voice.cuedAt = mix.now;
|
|
235
242
|
voice.cuedSeq = mix.transport.seq;
|
|
236
243
|
voice.opened = mix.now;
|
|
244
|
+
if (voice.state !== 'pending')
|
|
245
|
+
voice.begunSeq = mix.transport.seq;
|
|
237
246
|
}
|
|
238
247
|
voice.placing = anchored;
|
|
239
248
|
// A start the host gave by its own clock, where the mix's rate puts it.
|
package/dist/due.d.ts
CHANGED
|
@@ -7,6 +7,17 @@ import type { Voice } from './voice.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export declare class DueQueue<I, O> {
|
|
9
9
|
size: number;
|
|
10
|
+
/**
|
|
11
|
+
* The entries due every frame, -Infinity, kept out of the heap in the order they came: an owned
|
|
12
|
+
* voice is filed so each frame, and sifting each to the top and off again cost more than its visit.
|
|
13
|
+
*/
|
|
14
|
+
private every;
|
|
15
|
+
private everyTokens;
|
|
16
|
+
/** The list `takeEvery` last handed out, and its tokens: the next one fills it again. */
|
|
17
|
+
private spare;
|
|
18
|
+
private spareTokens;
|
|
19
|
+
/** Whether the list `takeEvery` last handed out was in id order. */
|
|
20
|
+
everySorted: boolean;
|
|
10
21
|
private at;
|
|
11
22
|
private tokens;
|
|
12
23
|
private voices;
|
|
@@ -15,6 +26,11 @@ export declare class DueQueue<I, O> {
|
|
|
15
26
|
push(at: number, voice: Voice<I, O>, token: number): void;
|
|
16
27
|
/** The earliest entry's time, Infinity while empty. */
|
|
17
28
|
first(): number;
|
|
29
|
+
/**
|
|
30
|
+
* The current entries due every frame, in the order they came, leaving none; `everySorted` says
|
|
31
|
+
* whether that was id order. The list is the queue's own, good until the next call.
|
|
32
|
+
*/
|
|
33
|
+
takeEvery(): Voice<I, O>[];
|
|
18
34
|
/** Takes off the earliest entry, giving its voice where it is current and undefined where stale. */
|
|
19
35
|
pop(): Voice<I, O> | undefined;
|
|
20
36
|
clear(): void;
|
|
@@ -22,6 +38,8 @@ export declare class DueQueue<I, O> {
|
|
|
22
38
|
/** Puts an entry in the hole at `i` and sifts it down to where it belongs. */
|
|
23
39
|
private sink;
|
|
24
40
|
private grow;
|
|
41
|
+
/** Drops the entries due every frame made stale by a later one. */
|
|
42
|
+
private pruneEvery;
|
|
25
43
|
/** Drops the entries made stale by a later one and heapifies what is left. */
|
|
26
44
|
private prune;
|
|
27
45
|
}
|
package/dist/due.js
CHANGED
|
@@ -5,12 +5,30 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export class DueQueue {
|
|
7
7
|
size = 0;
|
|
8
|
+
/**
|
|
9
|
+
* The entries due every frame, -Infinity, kept out of the heap in the order they came: an owned
|
|
10
|
+
* voice is filed so each frame, and sifting each to the top and off again cost more than its visit.
|
|
11
|
+
*/
|
|
12
|
+
every = [];
|
|
13
|
+
everyTokens = [];
|
|
14
|
+
/** The list `takeEvery` last handed out, and its tokens: the next one fills it again. */
|
|
15
|
+
spare = [];
|
|
16
|
+
spareTokens = [];
|
|
17
|
+
/** Whether the list `takeEvery` last handed out was in id order. */
|
|
18
|
+
everySorted = true;
|
|
8
19
|
at = new Float64Array(64);
|
|
9
20
|
tokens = new Float64Array(64);
|
|
10
21
|
voices = [];
|
|
11
22
|
/** The size past which stale entries are pruned, so a voice rescheduled often cannot grow it. */
|
|
12
23
|
limit = 64;
|
|
13
24
|
push(at, voice, token) {
|
|
25
|
+
if (at === Number.NEGATIVE_INFINITY) {
|
|
26
|
+
if (this.every.length >= this.limit)
|
|
27
|
+
this.pruneEvery();
|
|
28
|
+
this.every.push(voice);
|
|
29
|
+
this.everyTokens.push(token);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
14
32
|
if (this.size >= this.limit)
|
|
15
33
|
this.prune();
|
|
16
34
|
if (this.size === this.at.length)
|
|
@@ -30,8 +48,41 @@ export class DueQueue {
|
|
|
30
48
|
}
|
|
31
49
|
/** The earliest entry's time, Infinity while empty. */
|
|
32
50
|
first() {
|
|
51
|
+
if (this.every.length > 0)
|
|
52
|
+
return Number.NEGATIVE_INFINITY;
|
|
33
53
|
return this.size > 0 ? this.at[0] : Number.POSITIVE_INFINITY;
|
|
34
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* The current entries due every frame, in the order they came, leaving none; `everySorted` says
|
|
57
|
+
* whether that was id order. The list is the queue's own, good until the next call.
|
|
58
|
+
*/
|
|
59
|
+
takeEvery() {
|
|
60
|
+
const every = this.every;
|
|
61
|
+
const tokens = this.everyTokens;
|
|
62
|
+
let n = 0;
|
|
63
|
+
let sorted = true;
|
|
64
|
+
let last = Number.NEGATIVE_INFINITY;
|
|
65
|
+
for (let i = 0; i < every.length; i++) {
|
|
66
|
+
const voice = every[i];
|
|
67
|
+
if (tokens[i] !== voice.dueToken || voice.state === 'done')
|
|
68
|
+
continue;
|
|
69
|
+
// A voice filed twice keeps its last entry, so it is taken once.
|
|
70
|
+
voice.dueToken++;
|
|
71
|
+
if (voice.id < last)
|
|
72
|
+
sorted = false;
|
|
73
|
+
last = voice.id;
|
|
74
|
+
every[n++] = voice;
|
|
75
|
+
}
|
|
76
|
+
every.length = n;
|
|
77
|
+
tokens.length = 0;
|
|
78
|
+
this.spare.length = 0;
|
|
79
|
+
this.every = this.spare;
|
|
80
|
+
this.everyTokens = this.spareTokens;
|
|
81
|
+
this.spare = every;
|
|
82
|
+
this.spareTokens = tokens;
|
|
83
|
+
this.everySorted = sorted;
|
|
84
|
+
return every;
|
|
85
|
+
}
|
|
35
86
|
/** Takes off the earliest entry, giving its voice where it is current and undefined where stale. */
|
|
36
87
|
pop() {
|
|
37
88
|
const voice = this.voices[0];
|
|
@@ -44,6 +95,10 @@ export class DueQueue {
|
|
|
44
95
|
}
|
|
45
96
|
clear() {
|
|
46
97
|
this.size = 0;
|
|
98
|
+
this.every.length = 0;
|
|
99
|
+
this.everyTokens.length = 0;
|
|
100
|
+
this.spare.length = 0;
|
|
101
|
+
this.spareTokens.length = 0;
|
|
47
102
|
this.voices = [];
|
|
48
103
|
this.limit = 64;
|
|
49
104
|
}
|
|
@@ -84,6 +139,22 @@ export class DueQueue {
|
|
|
84
139
|
tokens.set(this.tokens);
|
|
85
140
|
this.tokens = tokens;
|
|
86
141
|
}
|
|
142
|
+
/** Drops the entries due every frame made stale by a later one. */
|
|
143
|
+
pruneEvery() {
|
|
144
|
+
const every = this.every;
|
|
145
|
+
const tokens = this.everyTokens;
|
|
146
|
+
let n = 0;
|
|
147
|
+
for (let i = 0; i < every.length; i++) {
|
|
148
|
+
const voice = every[i];
|
|
149
|
+
if (tokens[i] !== voice.dueToken)
|
|
150
|
+
continue;
|
|
151
|
+
every[n] = voice;
|
|
152
|
+
tokens[n++] = tokens[i];
|
|
153
|
+
}
|
|
154
|
+
every.length = n;
|
|
155
|
+
tokens.length = n;
|
|
156
|
+
this.limit = Math.max(this.limit, 2 * n);
|
|
157
|
+
}
|
|
87
158
|
/** Drops the entries made stale by a later one and heapifies what is left. */
|
|
88
159
|
prune() {
|
|
89
160
|
let n = 0;
|
|
@@ -169,7 +240,9 @@ export function schedule(mix, voice) {
|
|
|
169
240
|
/** Takes every current entry due by `now` off the queue, in cue order. */
|
|
170
241
|
export function popDue(mix, now) {
|
|
171
242
|
const queue = mix.due;
|
|
172
|
-
const out =
|
|
243
|
+
const out = queue.takeEvery();
|
|
244
|
+
const sorted = queue.everySorted;
|
|
245
|
+
const every = out.length;
|
|
173
246
|
while (queue.first() <= now) {
|
|
174
247
|
const voice = queue.pop();
|
|
175
248
|
if (voice === undefined)
|
|
@@ -177,7 +250,33 @@ export function popDue(mix, now) {
|
|
|
177
250
|
voice.dueToken++;
|
|
178
251
|
out.push(voice);
|
|
179
252
|
}
|
|
180
|
-
|
|
253
|
+
if (every === 0 || !sorted)
|
|
254
|
+
return byId(out);
|
|
255
|
+
return every === out.length ? out : merged(out, every);
|
|
256
|
+
}
|
|
257
|
+
/** `list` in id order, its first `n` already so and the rest few: those sorted, then merged in. */
|
|
258
|
+
function merged(list, n) {
|
|
259
|
+
const rest = byId(list.slice(n));
|
|
260
|
+
const out = [];
|
|
261
|
+
let i = 0;
|
|
262
|
+
let k = 0;
|
|
263
|
+
while (i < n && k < rest.length) {
|
|
264
|
+
const a = list[i];
|
|
265
|
+
const b = rest[k];
|
|
266
|
+
if (b.id < a.id) {
|
|
267
|
+
out.push(b);
|
|
268
|
+
k++;
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
out.push(a);
|
|
272
|
+
i++;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
while (i < n)
|
|
276
|
+
out.push(list[i++]);
|
|
277
|
+
while (k < rest.length)
|
|
278
|
+
out.push(rest[k++]);
|
|
279
|
+
return out;
|
|
181
280
|
}
|
|
182
281
|
/** Past this many voices the built-in sort, which makes working arrays however few it is given. */
|
|
183
282
|
const SHORT = 8;
|
package/dist/fade.d.ts
CHANGED
|
@@ -3,7 +3,11 @@ import type { Left, Parted, Parting } from './subject.js';
|
|
|
3
3
|
import type { Voice } from './voice.js';
|
|
4
4
|
/** Starts one subject's ramp out of a voice, or takes it out at once for a ramp of 0. */
|
|
5
5
|
export declare function fadeSubject<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I, over?: number): void;
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* What a subject's own ramp out of a voice leaves of its weight this frame. It also holds the
|
|
8
|
+
* voice's fade in where the ramp began, as a fade out of the whole voice does, so it is a ratio to
|
|
9
|
+
* the fade in the voice's envelope gives.
|
|
10
|
+
*/
|
|
7
11
|
export declare function parting<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I, now: number): number;
|
|
8
12
|
/**
|
|
9
13
|
* Takes a subject out of one voice for good, as of mix time `at`: the voice forgets its record,
|
|
@@ -34,9 +38,15 @@ export declare function beginFade<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, op
|
|
|
34
38
|
}, at?: number): void;
|
|
35
39
|
/**
|
|
36
40
|
* Keeps a voice faded at the end of its passes with its owner, live and quiet, rather than letting
|
|
37
|
-
* it leave; the owner
|
|
41
|
+
* it leave; the owner, where it holds no child still playing, leaves at `at`, or stays quiet in
|
|
42
|
+
* turn where an owner holds it.
|
|
38
43
|
*/
|
|
39
44
|
export declare function stay<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, at: number): void;
|
|
45
|
+
/**
|
|
46
|
+
* Lets an owner holding no child still playing leave at `at`; under history one an owner holds
|
|
47
|
+
* stays with it instead, quiet while its children are.
|
|
48
|
+
*/
|
|
49
|
+
export declare function release<I, O>(mix: Mixer<I, O>, owner: Voice<I, O>, at: number): void;
|
|
40
50
|
/**
|
|
41
51
|
* Turns a fading voice around: it climbs back from where its fade out had got to, up the same
|
|
42
52
|
* curve, at the whole curve per `over` ms. A fade waiting for rest is taken back at once. A voice
|