@aura3d/audio 3.0.1 → 4.0.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 (67) hide show
  1. package/dist/AudioSource.d.ts +7 -0
  2. package/dist/AudioSource.d.ts.map +1 -1
  3. package/dist/AudioSource.js +21 -0
  4. package/dist/AudioSource.js.map +1 -1
  5. package/dist/PositionalEmitter.js +1 -1
  6. package/dist/PositionalEmitter.js.map +1 -1
  7. package/dist/contracts/gameSound.d.ts +94 -0
  8. package/dist/contracts/gameSound.d.ts.map +1 -0
  9. package/dist/contracts/gameSound.js +63 -0
  10. package/dist/contracts/gameSound.js.map +1 -0
  11. package/dist/game-sound/EngineLoop.d.ts +44 -0
  12. package/dist/game-sound/EngineLoop.d.ts.map +1 -0
  13. package/dist/game-sound/EngineLoop.js +114 -0
  14. package/dist/game-sound/EngineLoop.js.map +1 -0
  15. package/dist/game-sound/GameSoundEngine.d.ts +129 -0
  16. package/dist/game-sound/GameSoundEngine.d.ts.map +1 -0
  17. package/dist/game-sound/GameSoundEngine.js +563 -0
  18. package/dist/game-sound/GameSoundEngine.js.map +1 -0
  19. package/dist/game-sound/LoopHandle.d.ts +30 -0
  20. package/dist/game-sound/LoopHandle.d.ts.map +1 -0
  21. package/dist/game-sound/LoopHandle.js +78 -0
  22. package/dist/game-sound/LoopHandle.js.map +1 -0
  23. package/dist/game-sound/MasterChain.d.ts +57 -0
  24. package/dist/game-sound/MasterChain.d.ts.map +1 -0
  25. package/dist/game-sound/MasterChain.js +144 -0
  26. package/dist/game-sound/MasterChain.js.map +1 -0
  27. package/dist/game-sound/MusicController.d.ts +59 -0
  28. package/dist/game-sound/MusicController.d.ts.map +1 -0
  29. package/dist/game-sound/MusicController.js +210 -0
  30. package/dist/game-sound/MusicController.js.map +1 -0
  31. package/dist/game-sound/ReverbSend.d.ts +30 -0
  32. package/dist/game-sound/ReverbSend.d.ts.map +1 -0
  33. package/dist/game-sound/ReverbSend.js +69 -0
  34. package/dist/game-sound/ReverbSend.js.map +1 -0
  35. package/dist/game-sound/SpatialVoice.d.ts +30 -0
  36. package/dist/game-sound/SpatialVoice.d.ts.map +1 -0
  37. package/dist/game-sound/SpatialVoice.js +115 -0
  38. package/dist/game-sound/SpatialVoice.js.map +1 -0
  39. package/dist/game-sound/Voice.d.ts +139 -0
  40. package/dist/game-sound/Voice.d.ts.map +1 -0
  41. package/dist/game-sound/Voice.js +319 -0
  42. package/dist/game-sound/Voice.js.map +1 -0
  43. package/dist/game-sound/formatProbe.d.ts +19 -0
  44. package/dist/game-sound/formatProbe.d.ts.map +1 -0
  45. package/dist/game-sound/formatProbe.js +36 -0
  46. package/dist/game-sound/formatProbe.js.map +1 -0
  47. package/dist/game-sound/listenerSync.d.ts +55 -0
  48. package/dist/game-sound/listenerSync.d.ts.map +1 -0
  49. package/dist/game-sound/listenerSync.js +37 -0
  50. package/dist/game-sound/listenerSync.js.map +1 -0
  51. package/dist/game-sound/loudness.d.ts +35 -0
  52. package/dist/game-sound/loudness.d.ts.map +1 -0
  53. package/dist/game-sound/loudness.js +135 -0
  54. package/dist/game-sound/loudness.js.map +1 -0
  55. package/dist/game-sound/rng.d.ts +26 -0
  56. package/dist/game-sound/rng.d.ts.map +1 -0
  57. package/dist/game-sound/rng.js +57 -0
  58. package/dist/game-sound/rng.js.map +1 -0
  59. package/dist/game-sound/types.d.ts +96 -0
  60. package/dist/game-sound/types.d.ts.map +1 -0
  61. package/dist/game-sound/types.js +19 -0
  62. package/dist/game-sound/types.js.map +1 -0
  63. package/dist/index.d.ts +16 -0
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +13 -0
  66. package/dist/index.js.map +1 -1
  67. package/package.json +2 -2
@@ -0,0 +1,69 @@
1
+ /**
2
+ * game-sound/ReverbSend.ts — §6.8 reverb send.
3
+ *
4
+ * One convolver send off the bus mix, tier-gated (off on `low`; IR length ≤
5
+ * the §17 budget: medium 0.8 s, high 1.5 s, ultra 2.5 s). Presets pick a
6
+ * bundled mono 24 kHz IR via the format probe. `underwater` adds a 1.8 kHz
7
+ * lowpass on the send path.
8
+ */
9
+ export const IR_BUDGET_SECONDS = {
10
+ low: 0,
11
+ medium: 0.8,
12
+ high: 1.5,
13
+ ultra: 2.5
14
+ };
15
+ /** IR lengths shipped per preset (seconds, mono 24 kHz — from the pack manifest). */
16
+ export const PRESET_IR_SECONDS = {
17
+ "small-room": 0.6,
18
+ hall: 1.8,
19
+ street: 1.2,
20
+ hangar: 2.2,
21
+ underwater: 1.4
22
+ };
23
+ export const presetIrUrl = (preset, extension) => preset === "none" ? undefined : `assets/ir/${preset}.${extension}`;
24
+ export function createReverbSend(options) {
25
+ const { ctx, preset, tier } = options;
26
+ const budget = IR_BUDGET_SECONDS[tier];
27
+ if (preset === "none" || budget <= 0 || typeof ctx.createConvolver !== "function") {
28
+ return { enabled: false, irSeconds: 0, dispose() { } };
29
+ }
30
+ const irSeconds = Math.min(PRESET_IR_SECONDS[preset], budget);
31
+ const convolver = ctx.createConvolver();
32
+ const sendGain = ctx.createGain();
33
+ sendGain.gain.value = 0.25;
34
+ // Send input → convolver → (lowpass if underwater) → sendGain → caller's bus input.
35
+ let tail = convolver;
36
+ if (preset === "underwater" && typeof ctx.createBiquadFilter === "function") {
37
+ const lp = ctx.createBiquadFilter();
38
+ lp.type = "lowpass";
39
+ if (lp.frequency)
40
+ lp.frequency.value = 1800;
41
+ convolver.connect(lp);
42
+ lp.connect(sendGain);
43
+ tail = lp;
44
+ }
45
+ else {
46
+ convolver.connect(sendGain);
47
+ }
48
+ return {
49
+ enabled: true,
50
+ sendInput: convolver,
51
+ irSeconds,
52
+ /** The send tail connects to the master/bus input — set by the engine. */
53
+ ...{ sendTail: sendGain },
54
+ setIrBuffer(buffer) {
55
+ convolver.buffer = buffer;
56
+ },
57
+ dispose() {
58
+ for (const n of [convolver, tail, sendGain]) {
59
+ try {
60
+ n.disconnect();
61
+ }
62
+ catch {
63
+ /* noop */
64
+ }
65
+ }
66
+ }
67
+ };
68
+ }
69
+ //# sourceMappingURL=ReverbSend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReverbSend.js","sourceRoot":"","sources":["../../../../../packages/audio/src/game-sound/ReverbSend.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,MAAM,CAAC,MAAM,iBAAiB,GAAgC;IAC5D,GAAG,EAAE,CAAC;IACN,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,GAAG;IACT,KAAK,EAAE,GAAG;CACX,CAAC;AAEF,qFAAqF;AACrF,MAAM,CAAC,MAAM,iBAAiB,GAAkD;IAC9E,YAAY,EAAE,GAAG;IACjB,IAAI,EAAE,GAAG;IACT,MAAM,EAAE,GAAG;IACX,MAAM,EAAE,GAAG;IACX,UAAU,EAAE,GAAG;CAChB,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAoB,EAAE,SAAiB,EAAsB,EAAE,CACzF,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,MAAM,IAAI,SAAS,EAAE,CAAC;AAiBrE,MAAM,UAAU,gBAAgB,CAAC,OAA0B;IACzD,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IACtC,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,IAAI,CAAC,IAAI,OAAO,GAAG,CAAC,eAAe,KAAK,UAAU,EAAE,CAAC;QAClF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,KAAI,CAAC,EAAE,CAAC;IACxD,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;IAE9D,MAAM,SAAS,GAAG,GAAG,CAAC,eAAe,EAAgE,CAAC;IACtG,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,EAA8B,CAAC;IAC7D,QAAmD,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IAEvE,oFAAoF;IACpF,IAAI,IAAI,GAAkB,SAAS,CAAC;IACpC,IAAI,MAAM,KAAK,YAAY,IAAI,OAAO,GAAG,CAAC,kBAAkB,KAAK,UAAU,EAAE,CAAC;QAC5E,MAAM,EAAE,GAAG,GAAG,CAAC,kBAAkB,EAAgC,CAAC;QAClE,EAAE,CAAC,IAAI,GAAG,SAAS,CAAC;QACpB,IAAI,EAAE,CAAC,SAAS;YAAE,EAAE,CAAC,SAAS,CAAC,KAAK,GAAG,IAAI,CAAC;QAC3C,SAAoD,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACjE,EAA6C,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACjE,IAAI,GAAG,EAAE,CAAC;IACZ,CAAC;SAAM,CAAC;QACL,SAAoD,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1E,CAAC;IAED,OAAO;QACL,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,SAAS;QACpB,SAAS;QACT,0EAA0E;QAC1E,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE;QACzB,WAAW,CAAC,MAAmB;YAC7B,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC;QAC5B,CAAC;QACD,OAAO;YACL,KAAK,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC;gBAC5C,IAAI,CAAC;oBACF,CAAmB,CAAC,UAAU,EAAE,CAAC;gBACpC,CAAC;gBAAC,MAAM,CAAC;oBACP,UAAU;gBACZ,CAAC;YACH,CAAC;QACH,CAAC;KAC6E,CAAC;AACnF,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * game-sound/SpatialVoice.ts — §6.8 spatial voice node.
3
+ *
4
+ * Voice → PannerNode (HRTF on high/ultra, equalpower otherwise; inverse
5
+ * distance, refDistance 1, maxDistance 60, rolloff 1) → occlusion lowpass
6
+ * (BiquadFilterNode, 20 kHz open → 900 Hz occluded, exponential map) → cue
7
+ * gain → bus.
8
+ */
9
+ import type { SoundBiquadLike, SoundGraphContext, SoundNodeLike, SoundPannerLike, Vec3 } from "./types.js";
10
+ export interface SpatialNodeOptions {
11
+ readonly ctx: SoundGraphContext;
12
+ /** "hrtf" resolved by tier: high/ultra → "HRTF", else "equalpower". */
13
+ readonly panningModel: PanningModelType;
14
+ readonly refDistance?: number;
15
+ readonly maxDistance?: number;
16
+ readonly rolloff?: number;
17
+ }
18
+ export interface SpatialNode {
19
+ readonly input: SoundNodeLike;
20
+ readonly panner?: SoundPannerLike;
21
+ readonly occlusionFilter: SoundBiquadLike;
22
+ setPosition(p: Vec3): void;
23
+ setVelocity(v: Vec3): void;
24
+ /** 0 = open (20 kHz), 1 = fully occluded (900 Hz). */
25
+ setOcclusion(amount01: number): void;
26
+ dispose(): void;
27
+ }
28
+ export declare const occlusionHz: (amount01: number) => number;
29
+ export declare function createSpatialNode(options: SpatialNodeOptions): SpatialNode;
30
+ //# sourceMappingURL=SpatialVoice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpatialVoice.d.ts","sourceRoot":"","sources":["../../../../../packages/audio/src/game-sound/SpatialVoice.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,IAAI,EACL,MAAM,SAAS,CAAC;AAGjB,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,GAAG,EAAE,iBAAiB,CAAC;IAChC,uEAAuE;IACvE,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;IACxC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,WAAW,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,WAAW,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,sDAAsD;IACtD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,OAAO,IAAI,IAAI,CAAC;CACjB;AAMD,eAAO,MAAM,WAAW,GAAI,UAAU,MAAM,KAAG,MACmC,CAAC;AAEnF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAuF1E"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * game-sound/SpatialVoice.ts — §6.8 spatial voice node.
3
+ *
4
+ * Voice → PannerNode (HRTF on high/ultra, equalpower otherwise; inverse
5
+ * distance, refDistance 1, maxDistance 60, rolloff 1) → occlusion lowpass
6
+ * (BiquadFilterNode, 20 kHz open → 900 Hz occluded, exponential map) → cue
7
+ * gain → bus.
8
+ */
9
+ import { clamp, vec3 } from "./types.js";
10
+ // C-25 (#208): 20000·(1-occ)² + 400 Hz — fully occluded rolls off at 400 Hz.
11
+ const OCCLUSION_OPEN_HZ = 20_000;
12
+ const OCCLUSION_CLOSED_HZ = 400;
13
+ export const occlusionHz = (amount01) => OCCLUSION_OPEN_HZ * Math.pow(1 - clamp(amount01, 0, 1), 2) + OCCLUSION_CLOSED_HZ;
14
+ export function createSpatialNode(options) {
15
+ const { ctx } = options;
16
+ let panner;
17
+ if (typeof ctx.createPanner === "function") {
18
+ panner = ctx.createPanner();
19
+ if (panner) {
20
+ if ("panningModel" in panner)
21
+ panner.panningModel = options.panningModel;
22
+ if ("distanceModel" in panner)
23
+ panner.distanceModel = "inverse";
24
+ if ("refDistance" in panner)
25
+ panner.refDistance = options.refDistance ?? 1;
26
+ if ("maxDistance" in panner)
27
+ panner.maxDistance = options.maxDistance ?? 60;
28
+ if ("rolloffFactor" in panner)
29
+ panner.rolloffFactor = options.rolloff ?? 1;
30
+ }
31
+ }
32
+ // Fake/headless contexts may lack createBiquadFilter — the occlusion filter
33
+ // degrades to a passthrough gain (still positionable, just no lowpass).
34
+ const filter = (typeof ctx.createBiquadFilter === "function"
35
+ ? ctx.createBiquadFilter()
36
+ : ctx.createGain());
37
+ filter.type = "lowpass";
38
+ if (filter.frequency)
39
+ filter.frequency.value = OCCLUSION_OPEN_HZ;
40
+ if (filter.Q)
41
+ filter.Q.value = 0.707;
42
+ const input = panner ?? filter;
43
+ let occlusionSet = false;
44
+ if (panner) {
45
+ panner.connect(filter);
46
+ }
47
+ return {
48
+ input,
49
+ panner,
50
+ occlusionFilter: filter,
51
+ setPosition(p) {
52
+ if (!panner)
53
+ return;
54
+ const { x, y, z } = vec3(p);
55
+ const now = ctx.currentTime;
56
+ const px = panner.positionX;
57
+ const py = panner.positionY;
58
+ const pz = panner.positionZ;
59
+ if (px?.setTargetAtTime && py?.setTargetAtTime && pz?.setTargetAtTime) {
60
+ px.setTargetAtTime(x, now, 0.02);
61
+ py.setTargetAtTime(y, now, 0.02);
62
+ pz.setTargetAtTime(z, now, 0.02);
63
+ }
64
+ else if (panner.setPosition) {
65
+ panner.setPosition(x, y, z);
66
+ }
67
+ else {
68
+ if (panner.positionX)
69
+ panner.positionX.value = x;
70
+ if (panner.positionY)
71
+ panner.positionY.value = y;
72
+ if (panner.positionZ)
73
+ panner.positionZ.value = z;
74
+ }
75
+ },
76
+ setVelocity(v) {
77
+ if (!panner?.setVelocity)
78
+ return;
79
+ const { x, y, z } = vec3(v);
80
+ panner.setVelocity(x, y, z);
81
+ },
82
+ setOcclusion(amount01) {
83
+ const hz = occlusionHz(amount01);
84
+ const f = filter.frequency;
85
+ if (!f)
86
+ return;
87
+ // The first call sets the voice's starting occlusion: apply it at once.
88
+ // Gliding from the open 20 kHz default let a voice spawned behind a wall
89
+ // play its first ~90 ms unfiltered (only 15 dB high-band cut measured
90
+ // instead of ≥ 18 dB). Later calls glide (τ 30 ms) to avoid zipper noise.
91
+ if (!occlusionSet) {
92
+ occlusionSet = true;
93
+ if (f.setValueAtTime)
94
+ f.setValueAtTime(hz, ctx.currentTime);
95
+ f.value = hz;
96
+ return;
97
+ }
98
+ if (f.setTargetAtTime)
99
+ f.setTargetAtTime(hz, ctx.currentTime, 0.03);
100
+ else
101
+ f.value = hz;
102
+ },
103
+ dispose() {
104
+ for (const n of [panner, filter]) {
105
+ try {
106
+ n?.disconnect();
107
+ }
108
+ catch {
109
+ /* noop */
110
+ }
111
+ }
112
+ }
113
+ };
114
+ }
115
+ //# sourceMappingURL=SpatialVoice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpatialVoice.js","sourceRoot":"","sources":["../../../../../packages/audio/src/game-sound/SpatialVoice.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * game-sound/Voice.ts — voice pool per §6.8/§17.
3
+ *
4
+ * - Variant picks are round-robin-without-immediate-repeat (VariantPicker),
5
+ * seeded from the session seed → deterministic captures.
6
+ * - Pitch jitter ±`pitchJitterSemitones` (default 0.6), gain jitter
7
+ * ±`gainJitterDb` (default 1.5).
8
+ * - Per-cue `maxVoices` (default 4): oldest-playing voice of the same cue is
9
+ * stolen. Per-cue `cooldownMs` (default 30): plays inside the window are
10
+ * dropped and counted.
11
+ * - Global tier voice cap: when full, the lowest-priority oldest voice is
12
+ * evicted — `ambient` first, then `normal`, never `critical`.
13
+ */
14
+ import { type Rng } from "./rng.js";
15
+ import type { SoundGraphContext, SoundNodeLike, SoundSourceLike } from "./types.js";
16
+ export type VoicePriority = "critical" | "normal" | "ambient";
17
+ export declare const PRIORITY_RANK: Record<VoicePriority, number>;
18
+ export declare const priorityOf: (p: VoicePriority | number | undefined) => number;
19
+ export interface VoiceCueSpec {
20
+ readonly asset?: {
21
+ readonly url: string;
22
+ } | readonly {
23
+ readonly url: string;
24
+ }[];
25
+ readonly bus: string;
26
+ readonly volumeDb?: number;
27
+ readonly pitchJitterSemitones?: number;
28
+ readonly gainJitterDb?: number;
29
+ readonly maxVoices?: number;
30
+ readonly cooldownMs?: number;
31
+ readonly loop?: boolean;
32
+ readonly priority?: VoicePriority | number;
33
+ }
34
+ export interface VoiceRecord {
35
+ readonly id: number;
36
+ readonly cueId: string;
37
+ readonly priority: number;
38
+ readonly startedAt: number;
39
+ readonly source: SoundSourceLike;
40
+ readonly gain: SoundNodeLike;
41
+ /** Linear voice gain after volume + jitter (the attack ramp's target). */
42
+ readonly targetGain: number;
43
+ loop: boolean;
44
+ ended: boolean;
45
+ }
46
+ export interface VoicePoolOptions {
47
+ readonly ctx: SoundGraphContext;
48
+ readonly rng: Rng;
49
+ /** Resolve an asset url to a decoded buffer (async load handled upstream). */
50
+ readonly bufferFor: (url: string) => AudioBuffer | undefined;
51
+ /** Now in ms — injected for tests. */
52
+ readonly now?: () => number;
53
+ /** Global cap per §17 tier. */
54
+ readonly voiceCap: number;
55
+ }
56
+ export interface VoiceAllocation {
57
+ readonly dropped: boolean;
58
+ readonly record?: VoiceRecord;
59
+ readonly stolen?: VoiceRecord;
60
+ readonly evicted?: VoiceRecord;
61
+ }
62
+ export declare class VoicePool {
63
+ private readonly o;
64
+ private readonly now;
65
+ private readonly live;
66
+ private readonly perCue;
67
+ private readonly pickers;
68
+ private readonly lastPlayAt;
69
+ private nextId;
70
+ private droppedByCooldown;
71
+ private stolenCount;
72
+ private evictedCount;
73
+ private playedCount;
74
+ constructor(o: VoicePoolOptions);
75
+ get liveCount(): number;
76
+ get stats(): {
77
+ played: number;
78
+ cooldownDrops: number;
79
+ stolen: number;
80
+ evicted: number;
81
+ };
82
+ /**
83
+ * Allocate a voice for `cueId`. Returns the record plus any steal/eviction
84
+ * performed; `dropped` means the cooldown rejected the play.
85
+ */
86
+ allocate(cueId: string, spec: VoiceCueSpec, destination: SoundNodeLike, opts?: {
87
+ volumeDb?: number;
88
+ rate?: number;
89
+ loop?: boolean;
90
+ }): VoiceAllocation;
91
+ /**
92
+ * Stop a voice. The gain ramps to 0 over RELEASE_S before the source stops
93
+ * (a hard stop mid-waveform clicks); nodes disconnect once it has ended.
94
+ */
95
+ release(record: VoiceRecord): void;
96
+ releaseAll(): void;
97
+ private pickerFor;
98
+ }
99
+ /** Voice attack (s): long enough to kill the onset click, short enough to keep transients. */
100
+ export declare const ATTACK_S = 0.004;
101
+ /** Voice release (s) on stop / steal / eviction. */
102
+ export declare const RELEASE_S = 0.015;
103
+ /** Low-pass corner for bright (noise/hiss) buffers. */
104
+ export declare const HARSH_LOWPASS_HZ = 6500;
105
+ export interface GainParamOwner {
106
+ readonly gain: {
107
+ value: number;
108
+ cancelScheduledValues?(t: number): void;
109
+ setValueAtTime?(v: number, t: number): void;
110
+ linearRampToValueAtTime?(v: number, t: number): void;
111
+ };
112
+ }
113
+ /** Ramp a fresh voice gain 0 → target over ATTACK_S (falls back to a direct set). */
114
+ export declare function rampIn(node: GainParamOwner, target: number, now: number): void;
115
+ /** Ramp a voice gain to 0 over RELEASE_S; returns the context time to stop the source at. */
116
+ export declare function rampOut(node: GainParamOwner, now: number): number;
117
+ type BufferLike = Pick<AudioBuffer, "length" | "sampleRate" | "numberOfChannels"> & {
118
+ getChannelData?(channel: number): Float32Array;
119
+ };
120
+ /**
121
+ * Brightness test on channel 0: the energy of the first difference relative to
122
+ * the signal energy. A sine at f gives (2·sin(πf/sr))²: ≈0.03 at 1 kHz, ≈0.32
123
+ * at 4 kHz, 2 for white noise. Above 0.6 most of the energy sits above ~5 kHz.
124
+ */
125
+ export declare function brightnessRatio(buffer: BufferLike): number;
126
+ export declare const HARSH_BRIGHTNESS = 0.6;
127
+ export declare function isHarsh(buffer: AudioBuffer): boolean;
128
+ /**
129
+ * Loop-seam repair. When |last − first| sample is far above the local step
130
+ * size the loop clicks every cycle. The repaired copy is N samples shorter:
131
+ * its first N samples crossfade (equal power) from the original tail into the
132
+ * original head, so the wrap from sample L−N−1 → 0 is continuous.
133
+ * Buffers that already loop cleanly are returned unchanged.
134
+ */
135
+ export declare function seamlessLoopBuffer(ctx: {
136
+ createBuffer?(channels: number, length: number, sampleRate: number): AudioBuffer;
137
+ }, buffer: AudioBuffer): AudioBuffer;
138
+ export {};
139
+ //# sourceMappingURL=Voice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Voice.d.ts","sourceRoot":"","sources":["../../../../../packages/audio/src/game-sound/Voice.ts"],"names":[],"mappings":"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"}