@aura3d/audio 3.0.0 → 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,319 @@
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 { VariantPicker, jitter } from "./rng.js";
15
+ import { clamp, dbToGain } from "./types.js";
16
+ export const PRIORITY_RANK = { critical: 2, normal: 1, ambient: 0 };
17
+ export const priorityOf = (p) => typeof p === "number" ? p : PRIORITY_RANK[p ?? "normal"];
18
+ export class VoicePool {
19
+ o;
20
+ now;
21
+ live = new Set();
22
+ perCue = new Map();
23
+ pickers = new Map();
24
+ lastPlayAt = new Map();
25
+ nextId = 1;
26
+ droppedByCooldown = 0;
27
+ stolenCount = 0;
28
+ evictedCount = 0;
29
+ playedCount = 0;
30
+ constructor(o) {
31
+ this.o = o;
32
+ this.now = o.now ?? (() => o.ctx.currentTime * 1000);
33
+ }
34
+ get liveCount() {
35
+ return this.live.size;
36
+ }
37
+ get stats() {
38
+ return {
39
+ played: this.playedCount,
40
+ cooldownDrops: this.droppedByCooldown,
41
+ stolen: this.stolenCount,
42
+ evicted: this.evictedCount
43
+ };
44
+ }
45
+ /**
46
+ * Allocate a voice for `cueId`. Returns the record plus any steal/eviction
47
+ * performed; `dropped` means the cooldown rejected the play.
48
+ */
49
+ allocate(cueId, spec, destination, opts = {}) {
50
+ const now = this.now();
51
+ const cooldown = spec.cooldownMs ?? 30;
52
+ const last = this.lastPlayAt.get(cueId);
53
+ if (last !== undefined && now - last < cooldown) {
54
+ this.droppedByCooldown += 1;
55
+ return { dropped: true };
56
+ }
57
+ this.lastPlayAt.set(cueId, now);
58
+ // Per-cue steal: already at maxVoices → steal oldest of the same cue.
59
+ let stolen;
60
+ const cueLive = this.perCue.get(cueId) ?? new Set();
61
+ this.perCue.set(cueId, cueLive);
62
+ const maxVoices = spec.maxVoices ?? 4;
63
+ if (cueLive.size >= maxVoices) {
64
+ stolen = oldest(cueLive);
65
+ if (stolen) {
66
+ this.release(stolen);
67
+ this.stolenCount += 1;
68
+ }
69
+ }
70
+ // Global cap: evict the lowest-priority oldest voice iff it is strictly
71
+ // below the incoming priority; otherwise the incoming play is dropped.
72
+ let evicted;
73
+ if (this.live.size >= this.o.voiceCap) {
74
+ const victim = lowestPriorityOldest(this.live);
75
+ const incoming = priorityOf(spec.priority);
76
+ if (!victim || victim.priority >= incoming) {
77
+ return { dropped: true };
78
+ }
79
+ evicted = victim;
80
+ this.release(evicted);
81
+ this.evictedCount += 1;
82
+ }
83
+ const source = this.o.ctx.createBufferSource();
84
+ const loop = opts.loop ?? spec.loop ?? false;
85
+ const raw = resolveBuffer(spec.asset, this.pickerFor(cueId, spec), this.o.bufferFor);
86
+ // Loops whose last sample does not meet the first click once per cycle —
87
+ // play a crossfaded copy instead (see seamlessLoopBuffer).
88
+ const buffer = raw !== undefined && loop ? seamlessLoopBuffer(this.o.ctx, raw) : raw;
89
+ if (buffer !== undefined)
90
+ source.buffer = buffer;
91
+ source.loop = loop;
92
+ const gain = this.o.ctx.createGain();
93
+ const gainDb = (spec.volumeDb ?? 0) + (opts.volumeDb ?? 0) + jitter(this.o.rng, spec.gainJitterDb ?? 1.5);
94
+ // Attack ramp: a voice that jumps from silence to full gain clicks.
95
+ const targetGain = clamp(dbToGain(gainDb), 0, 4);
96
+ rampIn(gain, targetGain, this.o.ctx.currentTime);
97
+ const semitones = jitter(this.o.rng, spec.pitchJitterSemitones ?? 0.6);
98
+ const rate = (opts.rate ?? 1) * Math.pow(2, semitones / 12);
99
+ if (source.playbackRate) {
100
+ source.playbackRate.value = Math.max(0.0625, rate);
101
+ }
102
+ // Bright noise/hiss content (most energy above ~4 kHz) is what reads as
103
+ // "shrill": tame it with a gentle low-pass in front of the voice gain.
104
+ let filter;
105
+ if (buffer !== undefined && isHarsh(buffer) && typeof this.o.ctx.createBiquadFilter === "function") {
106
+ const lp = this.o.ctx.createBiquadFilter();
107
+ lp.type = "lowpass";
108
+ if (lp.frequency)
109
+ lp.frequency.value = HARSH_LOWPASS_HZ;
110
+ if (lp.Q)
111
+ lp.Q.value = 0.7;
112
+ filter = lp;
113
+ }
114
+ if (filter) {
115
+ source.connect(filter);
116
+ filter.connect(gain);
117
+ }
118
+ else {
119
+ source.connect(gain);
120
+ }
121
+ gain.connect(destination);
122
+ const record = {
123
+ id: this.nextId++,
124
+ cueId,
125
+ priority: priorityOf(spec.priority),
126
+ startedAt: now,
127
+ source,
128
+ gain: gain,
129
+ targetGain,
130
+ loop: source.loop ?? false,
131
+ ended: false
132
+ };
133
+ source.onended = () => {
134
+ record.ended = true;
135
+ this.release(record);
136
+ };
137
+ this.live.add(record);
138
+ cueLive.add(record);
139
+ this.playedCount += 1;
140
+ return { dropped: false, record, stolen, evicted };
141
+ }
142
+ /**
143
+ * Stop a voice. The gain ramps to 0 over RELEASE_S before the source stops
144
+ * (a hard stop mid-waveform clicks); nodes disconnect once it has ended.
145
+ */
146
+ release(record) {
147
+ if (!this.live.has(record))
148
+ return;
149
+ this.live.delete(record);
150
+ this.perCue.get(record.cueId)?.delete(record);
151
+ record.ended = true;
152
+ const now = this.o.ctx.currentTime;
153
+ const stopAt = rampOut(record.gain, now);
154
+ const cleanup = () => {
155
+ try {
156
+ record.source.disconnect();
157
+ record.gain.disconnect();
158
+ }
159
+ catch {
160
+ /* noop */
161
+ }
162
+ };
163
+ try {
164
+ record.source.onended = cleanup;
165
+ record.source.stop(stopAt);
166
+ }
167
+ catch {
168
+ /* already stopped */
169
+ record.source.onended = null;
170
+ cleanup();
171
+ }
172
+ }
173
+ releaseAll() {
174
+ for (const record of [...this.live])
175
+ this.release(record);
176
+ }
177
+ pickerFor(cueId, spec) {
178
+ const count = Array.isArray(spec.asset) ? spec.asset.length : spec.asset ? 1 : 0;
179
+ if (count <= 1)
180
+ return undefined;
181
+ let picker = this.pickers.get(cueId);
182
+ if (!picker) {
183
+ picker = new VariantPicker(count, this.o.rng);
184
+ this.pickers.set(cueId, picker);
185
+ }
186
+ return picker;
187
+ }
188
+ }
189
+ const oldest = (records) => {
190
+ let best;
191
+ for (const r of records)
192
+ if (!best || r.startedAt < best.startedAt)
193
+ best = r;
194
+ return best;
195
+ };
196
+ const lowestPriorityOldest = (records) => {
197
+ let best;
198
+ for (const r of records) {
199
+ if (!best ||
200
+ r.priority < best.priority ||
201
+ (r.priority === best.priority && r.startedAt < best.startedAt)) {
202
+ best = r;
203
+ }
204
+ }
205
+ return best;
206
+ };
207
+ const resolveBuffer = (asset, picker, bufferFor) => {
208
+ if (!asset)
209
+ return undefined;
210
+ const list = Array.isArray(asset) ? asset : [asset];
211
+ const index = picker ? picker.next() : 0;
212
+ const chosen = list[index] ?? list[0];
213
+ return chosen.buffer ?? bufferFor(chosen.url);
214
+ };
215
+ // ---- envelopes + content conditioning --------------------------------------
216
+ /** Voice attack (s): long enough to kill the onset click, short enough to keep transients. */
217
+ export const ATTACK_S = 0.004;
218
+ /** Voice release (s) on stop / steal / eviction. */
219
+ export const RELEASE_S = 0.015;
220
+ /** Low-pass corner for bright (noise/hiss) buffers. */
221
+ export const HARSH_LOWPASS_HZ = 6500;
222
+ /** Ramp a fresh voice gain 0 → target over ATTACK_S (falls back to a direct set). */
223
+ export function rampIn(node, target, now) {
224
+ const g = node.gain;
225
+ if (g.setValueAtTime && g.linearRampToValueAtTime) {
226
+ g.setValueAtTime(0, now);
227
+ g.linearRampToValueAtTime(target, now + ATTACK_S);
228
+ }
229
+ else {
230
+ g.value = target;
231
+ }
232
+ }
233
+ /** Ramp a voice gain to 0 over RELEASE_S; returns the context time to stop the source at. */
234
+ export function rampOut(node, now) {
235
+ const g = node.gain;
236
+ if (!g.setValueAtTime || !g.linearRampToValueAtTime)
237
+ return now;
238
+ g.cancelScheduledValues?.(now);
239
+ g.setValueAtTime(g.value, now);
240
+ g.linearRampToValueAtTime(0, now + RELEASE_S);
241
+ return now + RELEASE_S;
242
+ }
243
+ const harshCache = new WeakMap();
244
+ /**
245
+ * Brightness test on channel 0: the energy of the first difference relative to
246
+ * the signal energy. A sine at f gives (2·sin(πf/sr))²: ≈0.03 at 1 kHz, ≈0.32
247
+ * at 4 kHz, 2 for white noise. Above 0.6 most of the energy sits above ~5 kHz.
248
+ */
249
+ export function brightnessRatio(buffer) {
250
+ if (typeof buffer.getChannelData !== "function" || buffer.length < 2)
251
+ return 0;
252
+ const d = buffer.getChannelData(0);
253
+ let e = 0;
254
+ let de = 0;
255
+ for (let i = 1; i < d.length; i++) {
256
+ e += d[i] * d[i];
257
+ const diff = d[i] - d[i - 1];
258
+ de += diff * diff;
259
+ }
260
+ return e > 1e-12 ? de / e : 0;
261
+ }
262
+ export const HARSH_BRIGHTNESS = 0.6;
263
+ export function isHarsh(buffer) {
264
+ const key = buffer;
265
+ let v = harshCache.get(key);
266
+ if (v === undefined) {
267
+ v = brightnessRatio(buffer) > HARSH_BRIGHTNESS;
268
+ harshCache.set(key, v);
269
+ }
270
+ return v;
271
+ }
272
+ const seamCache = new WeakMap();
273
+ /**
274
+ * Loop-seam repair. When |last − first| sample is far above the local step
275
+ * size the loop clicks every cycle. The repaired copy is N samples shorter:
276
+ * its first N samples crossfade (equal power) from the original tail into the
277
+ * original head, so the wrap from sample L−N−1 → 0 is continuous.
278
+ * Buffers that already loop cleanly are returned unchanged.
279
+ */
280
+ export function seamlessLoopBuffer(ctx, buffer) {
281
+ const key = buffer;
282
+ const cached = seamCache.get(key);
283
+ if (cached)
284
+ return cached;
285
+ let out = buffer;
286
+ const b = buffer;
287
+ if (typeof b.getChannelData === "function" && typeof ctx.createBuffer === "function" && b.length >= 64) {
288
+ let worst = 0;
289
+ for (let c = 0; c < b.numberOfChannels; c++) {
290
+ const d = b.getChannelData(c);
291
+ const L = d.length;
292
+ let step = 0;
293
+ for (let i = 1; i < 32; i++)
294
+ step += Math.abs(d[i] - d[i - 1]) + Math.abs(d[L - i] - d[L - i - 1]);
295
+ step /= 62;
296
+ const jump = Math.abs(d[L - 1] - d[0]);
297
+ worst = Math.max(worst, jump > 0.004 && jump > 6 * step ? 1 : 0);
298
+ }
299
+ if (worst > 0) {
300
+ const n = Math.min(Math.round(b.sampleRate * 0.03), Math.floor(b.length / 4));
301
+ const len = b.length - n;
302
+ const fixed = ctx.createBuffer(b.numberOfChannels, len, b.sampleRate);
303
+ for (let c = 0; c < b.numberOfChannels; c++) {
304
+ const src = b.getChannelData(c);
305
+ const dst = fixed.getChannelData(c);
306
+ for (let i = 0; i < len; i++)
307
+ dst[i] = src[i];
308
+ for (let i = 0; i < n; i++) {
309
+ const t = ((i + 0.5) / n) * Math.PI * 0.5;
310
+ dst[i] = src[i] * Math.sin(t) + src[len + i] * Math.cos(t);
311
+ }
312
+ }
313
+ out = fixed;
314
+ }
315
+ }
316
+ seamCache.set(key, out);
317
+ return out;
318
+ }
319
+ //# sourceMappingURL=Voice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Voice.js","sourceRoot":"","sources":["../../../../../packages/audio/src/game-sound/Voice.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * game-sound/formatProbe.ts — §6.8/1735 format probe.
3
+ *
4
+ * Bundled 20 ms probe files (Opus in WebM and AAC in M4A) are decoded once
5
+ * through `decodeAudioData`; the first encoding that decodes wins. The
6
+ * result selects the URL variant for every packed asset
7
+ * (`<name>.opus.webm` vs `<name>.m4a`).
8
+ */
9
+ export type EncodedFormat = "opus-webm" | "aac-m4a";
10
+ export interface FormatProbeOptions {
11
+ readonly decodeAudioData: (data: ArrayBuffer) => Promise<AudioBuffer>;
12
+ readonly fetchProbe?: (url: string) => Promise<ArrayBuffer>;
13
+ /** Base URL of the bundled probe files. */
14
+ readonly probeBase?: string;
15
+ }
16
+ /** Extension appended to packed asset names for the winning format. */
17
+ export declare const formatExtension: (format: EncodedFormat) => string;
18
+ export declare function probeFormat(options: FormatProbeOptions): Promise<EncodedFormat>;
19
+ //# sourceMappingURL=formatProbe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatProbe.d.ts","sourceRoot":"","sources":["../../../../../packages/audio/src/game-sound/formatProbe.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC;AAEpD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IACtE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC5D,2CAA2C;IAC3C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAOD,uEAAuE;AACvE,eAAO,MAAM,eAAe,GAAI,QAAQ,aAAa,KAAG,MACV,CAAC;AAE/C,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC,CAkBrF"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * game-sound/formatProbe.ts — §6.8/1735 format probe.
3
+ *
4
+ * Bundled 20 ms probe files (Opus in WebM and AAC in M4A) are decoded once
5
+ * through `decodeAudioData`; the first encoding that decodes wins. The
6
+ * result selects the URL variant for every packed asset
7
+ * (`<name>.opus.webm` vs `<name>.m4a`).
8
+ */
9
+ const PROBES = [
10
+ { format: "opus-webm", file: "probe-20ms.opus.webm" },
11
+ { format: "aac-m4a", file: "probe-20ms.m4a" }
12
+ ];
13
+ /** Extension appended to packed asset names for the winning format. */
14
+ export const formatExtension = (format) => format === "opus-webm" ? "opus.webm" : "m4a";
15
+ export async function probeFormat(options) {
16
+ const base = options.probeBase ?? "assets/probes/";
17
+ for (const probe of PROBES) {
18
+ try {
19
+ const data = options.fetchProbe
20
+ ? await options.fetchProbe(base + probe.file)
21
+ : await fetch(base + probe.file).then((r) => {
22
+ if (!r.ok)
23
+ throw new Error(`HTTP ${r.status}`);
24
+ return r.arrayBuffer();
25
+ });
26
+ await options.decodeAudioData(data);
27
+ return probe.format;
28
+ }
29
+ catch {
30
+ // Decode or fetch failed — try the next encoding.
31
+ }
32
+ }
33
+ // No decodeable format → AAC/M4A is the safer universal default (Safari).
34
+ return "aac-m4a";
35
+ }
36
+ //# sourceMappingURL=formatProbe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatProbe.js","sourceRoot":"","sources":["../../../../../packages/audio/src/game-sound/formatProbe.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,MAAM,MAAM,GAAuD;IACjE,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,sBAAsB,EAAE;IACrD,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,gBAAgB,EAAE;CAC9C,CAAC;AAEF,uEAAuE;AACvE,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAqB,EAAU,EAAE,CAC/D,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC;AAE/C,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAA2B;IAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,IAAI,gBAAgB,CAAC;IACnD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU;gBAC7B,CAAC,CAAC,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;gBAC7C,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;oBACxC,IAAI,CAAC,CAAC,CAAC,EAAE;wBAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;oBAC/C,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;gBACzB,CAAC,CAAC,CAAC;YACP,MAAM,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YACpC,OAAO,KAAK,CAAC,MAAM,CAAC;QACtB,CAAC;QAAC,MAAM,CAAC;YACP,kDAAkD;QACpD,CAAC;IACH,CAAC;IACD,0EAA0E;IAC1E,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * game-sound/listenerSync.ts — §6.8/1728 listener pose sync.
3
+ *
4
+ * `AudioListener.positionX…upZ` AudioParams ramped with `setTargetAtTime`
5
+ * where they exist; Firefox never implemented them, so
6
+ * `"positionX" in listener` feature-detects and falls back to
7
+ * `setPosition`/`setOrientation`.
8
+ */
9
+ import { type Vec3 } from "./types.js";
10
+ export interface ListenerLike {
11
+ positionX?: {
12
+ value: number;
13
+ setTargetAtTime?(v: number, t: number, tau: number): void;
14
+ };
15
+ positionY?: {
16
+ value: number;
17
+ setTargetAtTime?(v: number, t: number, tau: number): void;
18
+ };
19
+ positionZ?: {
20
+ value: number;
21
+ setTargetAtTime?(v: number, t: number, tau: number): void;
22
+ };
23
+ forwardX?: {
24
+ value: number;
25
+ setTargetAtTime?(v: number, t: number, tau: number): void;
26
+ };
27
+ forwardY?: {
28
+ value: number;
29
+ setTargetAtTime?(v: number, t: number, tau: number): void;
30
+ };
31
+ forwardZ?: {
32
+ value: number;
33
+ setTargetAtTime?(v: number, t: number, tau: number): void;
34
+ };
35
+ upX?: {
36
+ value: number;
37
+ setTargetAtTime?(v: number, t: number, tau: number): void;
38
+ };
39
+ upY?: {
40
+ value: number;
41
+ setTargetAtTime?(v: number, t: number, tau: number): void;
42
+ };
43
+ upZ?: {
44
+ value: number;
45
+ setTargetAtTime?(v: number, t: number, tau: number): void;
46
+ };
47
+ setPosition?(x: number, y: number, z: number): void;
48
+ setOrientation?(fx: number, fy: number, fz: number, ux: number, uy: number, uz: number): void;
49
+ }
50
+ export declare function syncListener(listener: ListenerLike, now: number, pose: {
51
+ position: Vec3;
52
+ forward: Vec3;
53
+ up: Vec3;
54
+ }): "audioparam" | "legacy" | "none";
55
+ //# sourceMappingURL=listenerSync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listenerSync.d.ts","sourceRoot":"","sources":["../../../../../packages/audio/src/game-sound/listenerSync.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAQ,KAAK,IAAI,EAAE,MAAM,SAAS,CAAC;AAE1C,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACzF,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACzF,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACzF,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACxF,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACxF,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACxF,GAAG,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACnF,GAAG,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACnF,GAAG,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACnF,WAAW,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpD,cAAc,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/F;AAID,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,YAAY,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,IAAI,CAAA;CAAE,GAChD,YAAY,GAAG,QAAQ,GAAG,MAAM,CA2BlC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * game-sound/listenerSync.ts — §6.8/1728 listener pose sync.
3
+ *
4
+ * `AudioListener.positionX…upZ` AudioParams ramped with `setTargetAtTime`
5
+ * where they exist; Firefox never implemented them, so
6
+ * `"positionX" in listener` feature-detects and falls back to
7
+ * `setPosition`/`setOrientation`.
8
+ */
9
+ import { vec3 } from "./types.js";
10
+ const TAU = 0.02;
11
+ export function syncListener(listener, now, pose) {
12
+ const p = vec3(pose.position);
13
+ const f = vec3(pose.forward);
14
+ const u = vec3(pose.up);
15
+ if ("positionX" in listener && listener.positionX !== undefined) {
16
+ const ramp = (param, v) => param?.setTargetAtTime
17
+ ? param.setTargetAtTime(v, now, TAU)
18
+ : param !== undefined && (param.value = v);
19
+ ramp(listener.positionX, p.x);
20
+ ramp(listener.positionY, p.y);
21
+ ramp(listener.positionZ, p.z);
22
+ ramp(listener.forwardX, f.x);
23
+ ramp(listener.forwardY, f.y);
24
+ ramp(listener.forwardZ, f.z);
25
+ ramp(listener.upX, u.x);
26
+ ramp(listener.upY, u.y);
27
+ ramp(listener.upZ, u.z);
28
+ return "audioparam";
29
+ }
30
+ if (listener.setPosition && listener.setOrientation) {
31
+ listener.setPosition(p.x, p.y, p.z);
32
+ listener.setOrientation(f.x, f.y, f.z, u.x, u.y, u.z);
33
+ return "legacy";
34
+ }
35
+ return "none";
36
+ }
37
+ //# sourceMappingURL=listenerSync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listenerSync.js","sourceRoot":"","sources":["../../../../../packages/audio/src/game-sound/listenerSync.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAa,MAAM,SAAS,CAAC;AAgB1C,MAAM,GAAG,GAAG,IAAI,CAAC;AAEjB,MAAM,UAAU,YAAY,CAC1B,QAAsB,EACtB,GAAW,EACX,IAAiD;IAEjD,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAExB,IAAI,WAAW,IAAI,QAAQ,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAChE,MAAM,IAAI,GAAG,CAAC,KAAgF,EAAE,CAAS,EAAE,EAAE,CAC3G,KAAK,EAAE,eAAe;YACpB,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC;YACpC,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CAAE,KAA2B,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACtE,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACxB,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,IAAI,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;QACpD,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACtD,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * game-sound/loudness.ts — BS.1770-style loudness metering for `aura3d sfx admit`
3
+ * (PRD-09 1736 / §6.9 mastering targets).
4
+ *
5
+ * The admission pipeline decodes each source file to PCM (ffmpeg on the remote
6
+ * runner) and calls `measureLoudness` here — pure TypeScript, no deps, so unit
7
+ * tests exercise the same math the CLI reports into the pack manifest.
8
+ *
9
+ * Integrated loudness: K-weighted mean square per 400 ms block (75% overlap),
10
+ * two-stage gating (−70 LUFS absolute, then −10 LU relative).
11
+ * True peak: 4× oversampling with a windowed-sinc interpolator, per BS.1770
12
+ * Annex 2. `truePeakDb` is in dBTP (≤ 0 for clips that don't exceed FS).
13
+ */
14
+ export interface LoudnessInput {
15
+ /** Interleaved or per-channel samples, normalized to ±1. */
16
+ readonly channels: readonly (readonly number[] | Float32Array)[];
17
+ readonly sampleRate: number;
18
+ }
19
+ export interface LoudnessMeasurement {
20
+ /** Integrated loudness in LUFS; -Infinity for silence/shorter than one block. */
21
+ readonly lufs: number;
22
+ /** True peak in dBTP. */
23
+ readonly truePeakDb: number;
24
+ readonly durationSeconds: number;
25
+ }
26
+ export declare function measureLoudness(input: LoudnessInput): LoudnessMeasurement;
27
+ /** §6.9 mastering targets: one-shots peak-normalize to −1 dBTP; loops/beds −20 LUFS; music −16 LUFS. */
28
+ export type SfxMasteringClass = "one-shot" | "loop" | "ambience" | "music";
29
+ export declare const MASTERING_TARGETS: Readonly<Record<SfxMasteringClass, {
30
+ readonly lufs?: number;
31
+ readonly truePeakDb?: number;
32
+ }>>;
33
+ /** Gain (dB) to apply so `measured` hits its class target. Returns 0 when nothing applies. */
34
+ export declare function gainForTarget(measured: LoudnessMeasurement, cls: SfxMasteringClass): number;
35
+ //# sourceMappingURL=loudness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loudness.d.ts","sourceRoot":"","sources":["../../../../../packages/audio/src/game-sound/loudness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,GAAG,YAAY,CAAC,EAAE,CAAC;IACjE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,iFAAiF;IACjF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yBAAyB;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAmED,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,mBAAmB,CAoCzE;AAED,wGAAwG;AACxG,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC;AAE3E,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,iBAAiB,EAAE;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAK3H,CAAC;AAEF,8FAA8F;AAC9F,wBAAgB,aAAa,CAAC,QAAQ,EAAE,mBAAmB,EAAE,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAc3F"}