@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.
- package/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +11 -0
- package/dist/ambientLoop.d.ts +45 -0
- package/dist/ambientLoop.js +88 -0
- package/dist/audioHarness.d.ts +180 -0
- package/dist/audioHarness.js +244 -0
- package/dist/filters.d.ts +91 -0
- package/dist/filters.js +103 -0
- package/dist/formats.d.ts +18 -0
- package/dist/formats.js +19 -0
- package/dist/graph.d.ts +406 -0
- package/dist/graph.js +656 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +39 -0
- package/dist/manifest.d.ts +28 -0
- package/dist/manifest.js +71 -0
- package/dist/mix/bus.d.ts +203 -0
- package/dist/mix/bus.js +293 -0
- package/dist/mix/console.d.ts +96 -0
- package/dist/mix/console.js +131 -0
- package/dist/mix/defaultLayout.d.ts +37 -0
- package/dist/mix/defaultLayout.js +63 -0
- package/dist/mix/inserts.d.ts +64 -0
- package/dist/mix/inserts.js +187 -0
- package/dist/mix/returns.d.ts +38 -0
- package/dist/mix/returns.js +86 -0
- package/dist/mix/snapshot.d.ts +30 -0
- package/dist/mix/snapshot.js +55 -0
- package/dist/positional.d.ts +37 -0
- package/dist/positional.js +47 -0
- package/dist/registry.d.ts +91 -0
- package/dist/registry.js +128 -0
- package/dist/rhythm/bands.d.ts +60 -0
- package/dist/rhythm/bands.js +12 -0
- package/dist/rhythm/beatGrid.d.ts +32 -0
- package/dist/rhythm/beatGrid.js +98 -0
- package/dist/rhythm/beatMap.d.ts +42 -0
- package/dist/rhythm/beatMap.js +405 -0
- package/dist/rhythm/kickCore.d.ts +79 -0
- package/dist/rhythm/kickCore.js +166 -0
- package/dist/rhythm/kickDetector.d.ts +65 -0
- package/dist/rhythm/kickDetector.js +202 -0
- package/dist/rhythm/renderedPulse.d.ts +15 -0
- package/dist/rhythm/renderedPulse.js +138 -0
- package/dist/session.d.ts +62 -0
- package/dist/session.js +83 -0
- package/dist/spatial/ambisonic.d.ts +135 -0
- package/dist/spatial/ambisonic.js +299 -0
- package/dist/spatial/listener.d.ts +109 -0
- package/dist/spatial/listener.js +186 -0
- package/dist/spatial/occlusion.d.ts +39 -0
- package/dist/spatial/occlusion.js +92 -0
- package/dist/spatial/source.d.ts +185 -0
- package/dist/spatial/source.js +366 -0
- package/dist/spatial/zones.d.ts +129 -0
- package/dist/spatial/zones.js +166 -0
- package/dist/synth.d.ts +92 -0
- package/dist/synth.js +282 -0
- package/package.json +54 -0
- package/src/ambientLoop.ts +101 -0
- package/src/audioHarness.ts +280 -0
- package/src/filters.ts +109 -0
- package/src/formats.ts +22 -0
- package/src/graph.ts +805 -0
- package/src/index.ts +84 -0
- package/src/manifest.ts +73 -0
- package/src/mix/bus.ts +356 -0
- package/src/mix/console.ts +181 -0
- package/src/mix/defaultLayout.ts +118 -0
- package/src/mix/inserts.ts +242 -0
- package/src/mix/returns.ts +114 -0
- package/src/mix/snapshot.ts +75 -0
- package/src/positional.ts +47 -0
- package/src/registry.ts +167 -0
- package/src/rhythm/bands.ts +45 -0
- package/src/rhythm/beatGrid.ts +106 -0
- package/src/rhythm/beatMap.ts +514 -0
- package/src/rhythm/kickCore.ts +197 -0
- package/src/rhythm/kickDetector.ts +233 -0
- package/src/rhythm/renderedPulse.ts +147 -0
- package/src/session.ts +93 -0
- package/src/spatial/ambisonic.ts +358 -0
- package/src/spatial/listener.ts +249 -0
- package/src/spatial/occlusion.ts +95 -0
- package/src/spatial/source.ts +452 -0
- package/src/spatial/zones.ts +213 -0
- package/src/synth.ts +351 -0
package/dist/index.d.ts
ADDED
|
@@ -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[];
|
package/dist/manifest.js
ADDED
|
@@ -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
|
+
}
|