@driftengine/audio 3.61.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.
Files changed (88) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +9 -0
  3. package/README.md +11 -0
  4. package/dist/ambientLoop.d.ts +45 -0
  5. package/dist/ambientLoop.js +88 -0
  6. package/dist/audioHarness.d.ts +180 -0
  7. package/dist/audioHarness.js +244 -0
  8. package/dist/filters.d.ts +91 -0
  9. package/dist/filters.js +103 -0
  10. package/dist/formats.d.ts +18 -0
  11. package/dist/formats.js +19 -0
  12. package/dist/graph.d.ts +406 -0
  13. package/dist/graph.js +656 -0
  14. package/dist/index.d.ts +47 -0
  15. package/dist/index.js +39 -0
  16. package/dist/manifest.d.ts +28 -0
  17. package/dist/manifest.js +71 -0
  18. package/dist/mix/bus.d.ts +203 -0
  19. package/dist/mix/bus.js +293 -0
  20. package/dist/mix/console.d.ts +96 -0
  21. package/dist/mix/console.js +131 -0
  22. package/dist/mix/defaultLayout.d.ts +37 -0
  23. package/dist/mix/defaultLayout.js +63 -0
  24. package/dist/mix/inserts.d.ts +64 -0
  25. package/dist/mix/inserts.js +187 -0
  26. package/dist/mix/returns.d.ts +38 -0
  27. package/dist/mix/returns.js +86 -0
  28. package/dist/mix/snapshot.d.ts +30 -0
  29. package/dist/mix/snapshot.js +55 -0
  30. package/dist/positional.d.ts +37 -0
  31. package/dist/positional.js +47 -0
  32. package/dist/registry.d.ts +91 -0
  33. package/dist/registry.js +128 -0
  34. package/dist/rhythm/bands.d.ts +60 -0
  35. package/dist/rhythm/bands.js +12 -0
  36. package/dist/rhythm/beatGrid.d.ts +32 -0
  37. package/dist/rhythm/beatGrid.js +98 -0
  38. package/dist/rhythm/beatMap.d.ts +42 -0
  39. package/dist/rhythm/beatMap.js +405 -0
  40. package/dist/rhythm/kickCore.d.ts +79 -0
  41. package/dist/rhythm/kickCore.js +166 -0
  42. package/dist/rhythm/kickDetector.d.ts +65 -0
  43. package/dist/rhythm/kickDetector.js +202 -0
  44. package/dist/rhythm/renderedPulse.d.ts +15 -0
  45. package/dist/rhythm/renderedPulse.js +138 -0
  46. package/dist/session.d.ts +62 -0
  47. package/dist/session.js +83 -0
  48. package/dist/spatial/ambisonic.d.ts +135 -0
  49. package/dist/spatial/ambisonic.js +299 -0
  50. package/dist/spatial/listener.d.ts +109 -0
  51. package/dist/spatial/listener.js +186 -0
  52. package/dist/spatial/occlusion.d.ts +39 -0
  53. package/dist/spatial/occlusion.js +92 -0
  54. package/dist/spatial/source.d.ts +185 -0
  55. package/dist/spatial/source.js +366 -0
  56. package/dist/spatial/zones.d.ts +129 -0
  57. package/dist/spatial/zones.js +166 -0
  58. package/dist/synth.d.ts +92 -0
  59. package/dist/synth.js +282 -0
  60. package/package.json +54 -0
  61. package/src/ambientLoop.ts +101 -0
  62. package/src/audioHarness.ts +280 -0
  63. package/src/filters.ts +109 -0
  64. package/src/formats.ts +22 -0
  65. package/src/graph.ts +805 -0
  66. package/src/index.ts +84 -0
  67. package/src/manifest.ts +73 -0
  68. package/src/mix/bus.ts +356 -0
  69. package/src/mix/console.ts +181 -0
  70. package/src/mix/defaultLayout.ts +118 -0
  71. package/src/mix/inserts.ts +242 -0
  72. package/src/mix/returns.ts +114 -0
  73. package/src/mix/snapshot.ts +75 -0
  74. package/src/positional.ts +47 -0
  75. package/src/registry.ts +167 -0
  76. package/src/rhythm/bands.ts +45 -0
  77. package/src/rhythm/beatGrid.ts +106 -0
  78. package/src/rhythm/beatMap.ts +514 -0
  79. package/src/rhythm/kickCore.ts +197 -0
  80. package/src/rhythm/kickDetector.ts +233 -0
  81. package/src/rhythm/renderedPulse.ts +147 -0
  82. package/src/session.ts +93 -0
  83. package/src/spatial/ambisonic.ts +358 -0
  84. package/src/spatial/listener.ts +249 -0
  85. package/src/spatial/occlusion.ts +95 -0
  86. package/src/spatial/source.ts +452 -0
  87. package/src/spatial/zones.ts +213 -0
  88. package/src/synth.ts +351 -0
@@ -0,0 +1,47 @@
1
+ /*! DriftEngine | Copyright 2026 Drift Technologies | Apache-2.0 | https://github.com/drftrun/driftengine */
2
+ /**
3
+ * Sound: layered stems, synthesis, rhythm analysis, and where a source sits.
4
+ *
5
+ * A package because nothing in the renderer ever imported it — the cleanest boundary in the
6
+ * tree, and 3,151 lines a silent game does not carry.
7
+ */
8
+ export { SoundRegistry } from './registry.ts';
9
+ export { AudioGraph } from './graph.ts';
10
+ export { cutoffForSpeed } from './filters.ts';
11
+ export { audioContextConstructor, claimPlaybackSession } from './session.ts';
12
+ export { AUDIO_FORMATS, audioCandidateUrls } from './formats.ts';
13
+ export { fetchAudioManifest, parseAudioManifest } from './manifest.ts';
14
+ export type { AudioFormat } from './formats.ts';
15
+ export { AmbientLoop } from './ambientLoop.ts';
16
+ export { analyseTrack, emptyBeatMap } from './rhythm/beatMap.ts';
17
+ export { TapTempo, beatGrid } from './rhythm/beatGrid.ts';
18
+ export { KickDetector, kickPulseAfter } from './rhythm/kickDetector.ts';
19
+ export type { KickDetectorNodes } from './rhythm/kickDetector.ts';
20
+ export type { BeatMap } from './rhythm/beatMap.ts';
21
+ export { RHYTHM_BANDS } from './rhythm/bands.ts';
22
+ export type { Band, BandEnergies, BandName } from './rhythm/bands.ts';
23
+ export { distanceGain, stereoPan } from './positional.ts';
24
+ export { MixConsole } from './mix/console.ts';
25
+ export { MixBus } from './mix/bus.ts';
26
+ export type { MixInsert, InsertOptions, BusOptions } from './mix/bus.ts';
27
+ export type { MixConsoleOptions, MixSnapshot } from './mix/console.ts';
28
+ export { liftInsert, masterFilterInsert, slamInsert } from './mix/inserts.ts';
29
+ export type { LiftInsert, MasterFilterInsert, SlamInsert } from './mix/inserts.ts';
30
+ export { convolverInsert, delayInsert, impulseResponse } from './mix/returns.ts';
31
+ export type { DelayInsert } from './mix/returns.ts';
32
+ export { defaultLayout } from './mix/defaultLayout.ts';
33
+ export type { DefaultLayout } from './mix/defaultLayout.ts';
34
+ export { createListener, AudioListenerGraph } from './spatial/listener.ts';
35
+ export type { OcclusionProbe } from './spatial/listener.ts';
36
+ export { createSpatialSource, SpatialSource } from './spatial/source.ts';
37
+ export type { SpatialOptions } from './spatial/source.ts';
38
+ export { occlusionCutoffHz, occlusionGainFor, smoothToward, ProbeScheduler, } from './spatial/occlusion.ts';
39
+ export { MAX_OPEN_ZONES, addReverbZone, ReverbZone, SourceZoneSend } from './spatial/zones.ts';
40
+ export { ACN_W, ACN_X, ACN_Y, ACN_Z, AmbisonicSoundfield, FOA_CHANNELS, FOA_SPEAKERS, ambisonicFromWorld, createAmbisonicSoundfield, encodeFoa, foaDecodeGain, foaDecodeMatrix, } from './spatial/ambisonic.ts';
41
+ export type { AmbisonicOptions } from './spatial/ambisonic.ts';
42
+ export type { ZoneShape, ZoneOptions } from './spatial/zones.ts';
43
+ export { ambienceBuffer, driftScrapeBuffer, fireLoopBuffer, metalBuffer, noiseBuffer, silentBuffer, toneBuffer, waterLoopBuffer, windLoopBuffer, } from './synth.ts';
44
+ export type { AmbienceOptions } from './synth.ts';
45
+ export type { AudioGraphOptions, MixLevels } from './graph.ts';
46
+ export type { SoundSlot, SoundSource, SoundOrigin, FetchLike } from './registry.ts';
47
+ export { RenderedPulse } from './rhythm/renderedPulse.ts';
package/dist/index.js ADDED
@@ -0,0 +1,39 @@
1
+ /*! DriftEngine | Copyright 2026 Drift Technologies | Apache-2.0 | https://github.com/drftrun/driftengine */
2
+ /**
3
+ * Sound: layered stems, synthesis, rhythm analysis, and where a source sits.
4
+ *
5
+ * A package because nothing in the renderer ever imported it — the cleanest boundary in the
6
+ * tree, and 3,151 lines a silent game does not carry.
7
+ */
8
+ export { SoundRegistry } from './registry.js';
9
+ export { AudioGraph } from './graph.js';
10
+ export { cutoffForSpeed } from './filters.js';
11
+ /* The two lines that make Web Audio audible on an iPhone, for a consumer whose audio is one
12
+ decoded file through its own chain rather than a stem player. `AudioGraph` already calls both
13
+ and in this order; anything building its own context has to do the same. */
14
+ export { audioContextConstructor, claimPlaybackSession } from './session.js';
15
+ export { AUDIO_FORMATS, audioCandidateUrls } from './formats.js';
16
+ export { fetchAudioManifest, parseAudioManifest } from './manifest.js';
17
+ export { AmbientLoop } from './ambientLoop.js';
18
+ export { analyseTrack, emptyBeatMap } from './rhythm/beatMap.js';
19
+ export { TapTempo, beatGrid } from './rhythm/beatGrid.js';
20
+ export { KickDetector, kickPulseAfter } from './rhythm/kickDetector.js';
21
+ export { RHYTHM_BANDS } from './rhythm/bands.js';
22
+ export { distanceGain, stereoPan } from './positional.js';
23
+ /* The mix as a tree. `AudioGraph` is built on this and forwards to it, so a consumer only reaches
24
+ for these when it wants a bus of its own. */
25
+ export { MixConsole } from './mix/console.js';
26
+ export { MixBus } from './mix/bus.js';
27
+ export { liftInsert, masterFilterInsert, slamInsert } from './mix/inserts.js';
28
+ export { convolverInsert, delayInsert, impulseResponse } from './mix/returns.js';
29
+ export { defaultLayout } from './mix/defaultLayout.js';
30
+ /* Placing a sound in the world. Standalone rather than methods on the console, so a consumer that
31
+ never places one does not carry the panner — see `SpatialSource` and the size gate. */
32
+ export { createListener, AudioListenerGraph } from './spatial/listener.js';
33
+ export { createSpatialSource, SpatialSource } from './spatial/source.js';
34
+ export { occlusionCutoffHz, occlusionGainFor, smoothToward, ProbeScheduler, } from './spatial/occlusion.js';
35
+ export { MAX_OPEN_ZONES, addReverbZone, ReverbZone, SourceZoneSend } from './spatial/zones.js';
36
+ /* A soundfield rather than a source: four channels of direction, decoded at a fixed cost. */
37
+ export { ACN_W, ACN_X, ACN_Y, ACN_Z, AmbisonicSoundfield, FOA_CHANNELS, FOA_SPEAKERS, ambisonicFromWorld, createAmbisonicSoundfield, encodeFoa, foaDecodeGain, foaDecodeMatrix, } from './spatial/ambisonic.js';
38
+ export { ambienceBuffer, driftScrapeBuffer, fireLoopBuffer, metalBuffer, noiseBuffer, silentBuffer, toneBuffer, waterLoopBuffer, windLoopBuffer, } from './synth.js';
39
+ export { RenderedPulse } from './rhythm/renderedPulse.js';
@@ -0,0 +1,28 @@
1
+ import type { FetchLike } from './registry.ts';
2
+ /**
3
+ * Discovering how many sound assets exist, without a hard limit.
4
+ *
5
+ * A browser cannot list a directory, so an asset folder has to announce itself
6
+ * somehow. The three options are a fixed list in code (which caps the library
7
+ * at whatever number somebody typed), probing upward until requests start
8
+ * failing (a burst of 404s on every load, and one gap in the numbering silently
9
+ * truncates the set), or a manifest.
10
+ *
11
+ * A manifest, generated from the folder at build time, is the only one of the
12
+ * three with no ceiling and no wasted requests. Adding an asset stays a matter
13
+ * of dropping the file in; the manifest catches up on the next build.
14
+ *
15
+ * Deliberately not a game concept: this returns names, and the caller decides
16
+ * that some of them are music.
17
+ */
18
+ /** Every name in a manifest, or an empty list if there is not one. */
19
+ export declare function fetchAudioManifest(url: string, fetchImpl?: FetchLike): Promise<string[]>;
20
+ /**
21
+ * Validate a manifest's contents.
22
+ *
23
+ * Separated from fetching so the rules can be tested without a network, and
24
+ * because the rules are the part that matters: a manifest is generated by a
25
+ * script, and a script that emits one bad entry must not take the rest of the
26
+ * library down with it.
27
+ */
28
+ export declare function parseAudioManifest(value: unknown): string[];
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Discovering how many sound assets exist, without a hard limit.
3
+ *
4
+ * A browser cannot list a directory, so an asset folder has to announce itself
5
+ * somehow. The three options are a fixed list in code (which caps the library
6
+ * at whatever number somebody typed), probing upward until requests start
7
+ * failing (a burst of 404s on every load, and one gap in the numbering silently
8
+ * truncates the set), or a manifest.
9
+ *
10
+ * A manifest, generated from the folder at build time, is the only one of the
11
+ * three with no ceiling and no wasted requests. Adding an asset stays a matter
12
+ * of dropping the file in; the manifest catches up on the next build.
13
+ *
14
+ * Deliberately not a game concept: this returns names, and the caller decides
15
+ * that some of them are music.
16
+ */
17
+ /** Every name in a manifest, or an empty list if there is not one. */
18
+ export async function fetchAudioManifest(url, fetchImpl = (target) => fetch(target)) {
19
+ let parsed;
20
+ try {
21
+ const response = await fetchImpl(url);
22
+ if (!response.ok)
23
+ return [];
24
+ parsed = await response.json();
25
+ }
26
+ catch {
27
+ /*
28
+ * A missing or unreadable manifest is silence, never a failure. The whole
29
+ * asset system is built so a game runs with no files at all — a folder that
30
+ * has not been scanned yet must behave exactly like an empty one.
31
+ */
32
+ return [];
33
+ }
34
+ return parseAudioManifest(parsed);
35
+ }
36
+ /**
37
+ * Validate a manifest's contents.
38
+ *
39
+ * Separated from fetching so the rules can be tested without a network, and
40
+ * because the rules are the part that matters: a manifest is generated by a
41
+ * script, and a script that emits one bad entry must not take the rest of the
42
+ * library down with it.
43
+ */
44
+ export function parseAudioManifest(value) {
45
+ if (!Array.isArray(value))
46
+ return [];
47
+ const names = [];
48
+ const seen = new Set();
49
+ for (const entry of value) {
50
+ if (typeof entry !== 'string')
51
+ continue;
52
+ const name = entry.trim();
53
+ if (name.length === 0 || seen.has(name))
54
+ continue;
55
+ // A name is a slot, not a path. Anything with a separator or an extension
56
+ // in it is a generator bug, and following it would mean fetching whatever
57
+ // the manifest asked for from wherever it asked.
58
+ if (name.includes('/') || name.includes('\\') || name.includes('.'))
59
+ continue;
60
+ seen.add(name);
61
+ names.push(name);
62
+ }
63
+ /*
64
+ * Sorted, because the caller picks one by seed and that pick has to be the
65
+ * same everywhere. Directory order is filesystem order, which differs between
66
+ * machines — two players on the same day would get different music, and the
67
+ * daily would quietly stop being shared.
68
+ */
69
+ names.sort();
70
+ return names;
71
+ }
@@ -0,0 +1,203 @@
1
+ import type { ScheduleClock } from '../ambientLoop.ts';
2
+ /**
3
+ * One bus: everything that reaches it, through whatever it inserts, at whatever level it is set to.
4
+ *
5
+ * Three nodes rather than one, and each earns its place:
6
+ *
7
+ * ```
8
+ * input ─ [inserts] ─ tap ─ [post-send inserts] ─ output ─→ parent.input
9
+ * └─ send gain ─→ a return bus
10
+ * ```
11
+ *
12
+ * **`input` is the fader**, and it is at the *top* of the chain rather than the bottom. That is not
13
+ * a preference: it is what makes a send post-fader, and it is what the mix this replaces already
14
+ * did — the level sat ahead of the lift, and the sends hung off the lift. A fader at the bottom
15
+ * would leave every insert and every send working on unattenuated signal, so turning a bus down
16
+ * would leave its reverb at full strength.
17
+ *
18
+ * **`tap` is where sends listen from**, and it exists as a real node so that rewiring the chain
19
+ * cannot silently move what the sends hear. Inserts added normally land above it and are heard by
20
+ * the sends; an insert added `postSend` lands below it and is not. The slam is the reason that flag
21
+ * exists: `graph.ts` says the sends hang off the stage before it "so the reverb tail never hears
22
+ * the slam", because a six-second convolution of a clipped bass hit is a mess still arriving three
23
+ * gates later.
24
+ *
25
+ * **`output` is a unity sum**, for the reason `AudioGraph.out` gives: what a parent hears has to be
26
+ * a node and not an implicit sum at somebody else's input, or nothing can ever be inserted between
27
+ * a bus and its parent.
28
+ *
29
+ * Cost: two multiplications by unity per bus that a hand-wired graph would not have. Both are exact
30
+ * in floating point, so they cannot move a mix — measured, by the gate in `scripts/audio-baseline.mjs`,
31
+ * which is bit-exact and passes across this change. What would make this wrong is a tree deep
32
+ * enough for the node count itself to matter, which at the depth a game mixes at — a master, a few
33
+ * groups, their children — it is not.
34
+ */
35
+ /**
36
+ * Smoothing for parameter moves, seconds.
37
+ *
38
+ * The same 0.08 the mix has always used, and it lives here now because a bus is where every level
39
+ * move goes. Long enough to never click; short enough that a fader feels immediate.
40
+ */
41
+ export declare const RAMP = 0.08;
42
+ /** A stage a bus can put in its own signal path. Internally it may be anything, including parallel. */
43
+ export interface MixInsert {
44
+ readonly input: AudioNode;
45
+ readonly output: AudioNode;
46
+ }
47
+ export interface InsertOptions {
48
+ /**
49
+ * Place this insert *below* the send tap, so the sends do not hear it.
50
+ *
51
+ * Cost: an insert down here cannot be heard in a reverb tail even when a caller wants it to be.
52
+ * What would make this wrong is an insert that is a colour rather than an event — a bus-wide EQ
53
+ * belongs above the tap, because a reverb of an unequalised signal is a reverb of a different
54
+ * instrument.
55
+ */
56
+ readonly postSend?: boolean;
57
+ }
58
+ export interface BusOptions {
59
+ /**
60
+ * Where this bus sends its output. Omitted, the console parents it to `master`.
61
+ *
62
+ * **`null` means nowhere**, and the caller wires the output itself. That is not a hole in the
63
+ * model: a return bus joins the mix *downstream* of the master filter, so parenting one to master
64
+ * would put every reverb tail through a filter the dry signal has already been through. The mix
65
+ * this replaces has always done it that way, and `mixOutput.test.ts` exists because getting it
66
+ * wrong once meant every exported clip carried the dry track and nothing wet.
67
+ */
68
+ readonly parent?: MixBus | null;
69
+ /**
70
+ * Where the fader starts. Unity when omitted.
71
+ *
72
+ * Assigned rather than ramped, for the reason `AudioGraphOptions.levels` gives: a level that does
73
+ * not change for the whole of a render is not a move, it is where the parameter starts, and
74
+ * ramping it would open every rendered clip with a glide down from unity.
75
+ */
76
+ readonly level?: number;
77
+ }
78
+ export declare class MixBus {
79
+ readonly name: string;
80
+ private readonly context;
81
+ private readonly scheduleAt;
82
+ /** The fader. Sources and child buses connect here. */
83
+ readonly input: GainNode;
84
+ /** Where sends listen from. Unity, always. */
85
+ readonly tap: GainNode;
86
+ /** What the parent hears. Unity, always. */
87
+ readonly output: GainNode;
88
+ readonly parent: MixBus | null;
89
+ private readonly kids;
90
+ private readonly inserts;
91
+ /**
92
+ * Nodes fed from this bus's pre-insert signal, restored whenever the chain is rewired.
93
+ *
94
+ * For a stage that needs the bus as it arrives rather than as its own position in the chain would
95
+ * give it. The slam is the case: its wet path is tapped upstream of the lift's high-pass, because
96
+ * a low-cut ahead of the tap would make the effect vanish exactly when the player is in the air,
97
+ * which is where half of it is used. Registered rather than connected once, because `rebuild`
98
+ * disconnects `input` and would otherwise silently drop it the next time an insert is added.
99
+ */
100
+ private readonly inputTaps;
101
+ private readonly sends;
102
+ /**
103
+ * What each send was last asked for.
104
+ *
105
+ * Mirrored rather than read back off the parameter, for the reason `AudioGraph.mixLevels` gives:
106
+ * a send is ramped, and mid-ramp `gain.value` is somewhere between where it was and where it is
107
+ * going. A snapshot capturing sends would otherwise record whatever instant it happened to ask on.
108
+ */
109
+ private readonly sendAmounts;
110
+ private levelValue;
111
+ private mutedValue;
112
+ private soloedValue;
113
+ /**
114
+ * What solo has decided about this bus, from the console that can see the whole tree.
115
+ *
116
+ * A separate factor rather than a second write to the level, because a bus silenced by somebody
117
+ * else's solo must come back to the level its own fader is at, and a single value cannot remember
118
+ * two decisions.
119
+ */
120
+ private soloGateValue;
121
+ /**
122
+ * A temporary move over the top of the fader, without disturbing it.
123
+ *
124
+ * The distinction `AudioGraph.fadeMusic` and `AudioGraph.levels` spent two paragraphs on: a fade
125
+ * is part of an *edit* and has to end by returning to whatever the player chose, so it cannot be
126
+ * allowed to overwrite that choice. Here the choice stays in `levelValue` and the edit is a
127
+ * factor beside it, which means the value to come back to is `1` rather than something the caller
128
+ * has to have remembered.
129
+ */
130
+ private duckFactor;
131
+ constructor(name: string, context: BaseAudioContext, scheduleAt: ScheduleClock, options?: BusOptions);
132
+ get children(): readonly MixBus[];
133
+ get level(): number;
134
+ get muted(): boolean;
135
+ get soloed(): boolean;
136
+ /** Feed a node from this bus's signal as it arrives, before any insert. See `inputTaps`. */
137
+ feedFromInput(node: AudioNode): void;
138
+ /** Append a stage to this bus's own signal path. See `InsertOptions.postSend`. */
139
+ insert(insert: MixInsert, options?: InsertOptions): void;
140
+ /**
141
+ * Feed a return bus from this one, at `amount`.
142
+ *
143
+ * Idempotent per target: asking twice moves the existing send rather than building a second one,
144
+ * because this is called from per-frame code in every consumer that has ever used it and a send
145
+ * node per frame is a graph that grows until the page stops.
146
+ */
147
+ send(returnBus: MixBus, amount: number): void;
148
+ /** What this bus was last asked to send to that return, or 0 if it has never sent to it. */
149
+ sendAmount(returnBus: MixBus): number;
150
+ /** Every return this bus feeds, for a snapshot to capture. */
151
+ get sendTargets(): readonly MixBus[];
152
+ setLevel(level: number): void;
153
+ /**
154
+ * Move to a level over an explicit time, rather than at the fader's own smoothing.
155
+ *
156
+ * A fader is a control and wants to feel immediate; a snapshot recall is an edit and takes as
157
+ * long as it was asked to take. Both write the same mirrored level, so the mix knows where it is
158
+ * either way — which `AudioGraph.fadeMusic` deliberately does *not* do, because a fade there is
159
+ * part of an edit that has to return to the player's setting when it is over.
160
+ */
161
+ fadeLevel(level: number, seconds: number): void;
162
+ /**
163
+ * Move over the top of the fader and back, without moving the fader.
164
+ *
165
+ * `duck(0, 0.4)` takes this bus away over four tenths of a second; `duck(1, 0.4)` brings it back
166
+ * to exactly whatever the fader is set to, including a setting the player changed in between.
167
+ * Cost: this is a second thing multiplying into one parameter, so a caller that ducks and forgets
168
+ * to release leaves a bus quiet with a fader that says otherwise — which is why `duckedTo` is
169
+ * readable rather than private.
170
+ */
171
+ duck(factor: number, seconds: number): void;
172
+ /** What this bus is ducked to, 1 when it is not. See `duck`. */
173
+ get duckedTo(): number;
174
+ setMute(muted: boolean): void;
175
+ setSolo(soloed: boolean): void;
176
+ /** Set by the console when it adopts this bus, so a solo anywhere re-resolves the whole tree. */
177
+ onSoloChanged: (() => void) | null;
178
+ /** Written by the console alone. 1 is audible, 0 is silenced by somebody else's solo. */
179
+ setSoloGate(gate: number): void;
180
+ /**
181
+ * Level, mute and the solo gate are one number, written once.
182
+ *
183
+ * Three writers to one parameter race, and the loser is whichever ran first — which is heard as a
184
+ * fader that sometimes does not take, and is nearly impossible to reproduce deliberately.
185
+ */
186
+ private applyGain;
187
+ /**
188
+ * Rewire the series path.
189
+ *
190
+ * Only `input`, the insert outputs and `tap` are disconnected — never `output`, which carries this
191
+ * bus's connection to its parent and would take the whole subtree with it. The sends are
192
+ * reconnected here because `tap` was just disconnected, and a send silently dropped by a later
193
+ * insert is exactly the kind of fault that reads as "the reverb stopped working" days afterwards.
194
+ */
195
+ private rebuild;
196
+ /**
197
+ * Every parameter move is ramped and every one lands on the console's instant.
198
+ *
199
+ * `scheduleAt`, never `currentTime`: offline there is no now, and a move left to the clock lands
200
+ * on instant zero along with every other move a render ever makes.
201
+ */
202
+ private ramp;
203
+ }