@newgameplusinc/odyssey-audio-video-sdk-dev 1.0.8 → 1.0.10
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/dist/SpatialAudioManager.d.ts +28 -2
- package/dist/SpatialAudioManager.js +222 -19
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
import { EventManager } from "./EventManager";
|
|
2
2
|
import { Position } from "./types";
|
|
3
|
+
type SpatialAudioDistanceConfig = {
|
|
4
|
+
refDistance?: number;
|
|
5
|
+
maxDistance?: number;
|
|
6
|
+
rolloffFactor?: number;
|
|
7
|
+
unit?: "auto" | "meters" | "centimeters";
|
|
8
|
+
};
|
|
9
|
+
type DenoiserOptions = {
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
threshold?: number;
|
|
12
|
+
noiseFloor?: number;
|
|
13
|
+
release?: number;
|
|
14
|
+
};
|
|
15
|
+
type SpatialAudioOptions = {
|
|
16
|
+
distance?: SpatialAudioDistanceConfig;
|
|
17
|
+
denoiser?: DenoiserOptions;
|
|
18
|
+
};
|
|
3
19
|
export declare class SpatialAudioManager extends EventManager {
|
|
4
20
|
private audioContext;
|
|
5
21
|
private participantNodes;
|
|
6
22
|
private masterGainNode;
|
|
7
23
|
private monitoringIntervals;
|
|
8
24
|
private compressor;
|
|
25
|
+
private options;
|
|
26
|
+
private denoiseWorkletReady;
|
|
27
|
+
private denoiseWorkletUrl?;
|
|
28
|
+
private denoiserWasmBytes?;
|
|
9
29
|
private listenerDirection;
|
|
10
|
-
constructor();
|
|
30
|
+
constructor(options?: SpatialAudioOptions);
|
|
11
31
|
getAudioContext(): AudioContext;
|
|
12
32
|
/**
|
|
13
33
|
* Setup spatial audio for a participant
|
|
@@ -24,7 +44,7 @@ export declare class SpatialAudioManager extends EventManager {
|
|
|
24
44
|
* @param track Audio track from MediaSoup consumer
|
|
25
45
|
* @param bypassSpatialization For testing - bypasses 3D positioning
|
|
26
46
|
*/
|
|
27
|
-
setupSpatialAudioForParticipant(participantId: string, track: MediaStreamTrack, bypassSpatialization?: boolean): void
|
|
47
|
+
setupSpatialAudioForParticipant(participantId: string, track: MediaStreamTrack, bypassSpatialization?: boolean): Promise<void>;
|
|
28
48
|
private startMonitoring;
|
|
29
49
|
/**
|
|
30
50
|
* Update spatial audio position and orientation for a participant
|
|
@@ -68,4 +88,10 @@ export declare class SpatialAudioManager extends EventManager {
|
|
|
68
88
|
removeParticipant(participantId: string): void;
|
|
69
89
|
resumeAudioContext(): Promise<void>;
|
|
70
90
|
getAudioContextState(): AudioContextState;
|
|
91
|
+
private getDistanceConfig;
|
|
92
|
+
private normalizePositionUnits;
|
|
93
|
+
private isDenoiserEnabled;
|
|
94
|
+
private ensureDenoiseWorklet;
|
|
95
|
+
private resolveOptions;
|
|
71
96
|
}
|
|
97
|
+
export {};
|
|
@@ -3,14 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.SpatialAudioManager = void 0;
|
|
4
4
|
const EventManager_1 = require("./EventManager");
|
|
5
5
|
class SpatialAudioManager extends EventManager_1.EventManager {
|
|
6
|
-
constructor() {
|
|
6
|
+
constructor(options) {
|
|
7
7
|
super();
|
|
8
8
|
this.participantNodes = new Map();
|
|
9
9
|
this.monitoringIntervals = new Map();
|
|
10
|
+
this.denoiseWorkletReady = null;
|
|
10
11
|
this.listenerDirection = {
|
|
11
12
|
forward: { x: 0, y: 1, z: 0 },
|
|
12
13
|
up: { x: 0, y: 0, z: 1 },
|
|
13
14
|
};
|
|
15
|
+
this.options = this.resolveOptions(options);
|
|
14
16
|
// Use high sample rate for best audio quality
|
|
15
17
|
this.audioContext = new AudioContext({ sampleRate: 48000 });
|
|
16
18
|
// Master gain
|
|
@@ -54,10 +56,10 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
54
56
|
* @param track Audio track from MediaSoup consumer
|
|
55
57
|
* @param bypassSpatialization For testing - bypasses 3D positioning
|
|
56
58
|
*/
|
|
57
|
-
setupSpatialAudioForParticipant(participantId, track, bypassSpatialization = false // Default to false
|
|
59
|
+
async setupSpatialAudioForParticipant(participantId, track, bypassSpatialization = false // Default to false
|
|
58
60
|
) {
|
|
59
61
|
if (this.audioContext.state === "suspended") {
|
|
60
|
-
this.audioContext.resume();
|
|
62
|
+
await this.audioContext.resume();
|
|
61
63
|
}
|
|
62
64
|
// Create stream with noise suppression constraints
|
|
63
65
|
const stream = new MediaStream([track]);
|
|
@@ -65,25 +67,67 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
65
67
|
const panner = this.audioContext.createPanner();
|
|
66
68
|
const analyser = this.audioContext.createAnalyser();
|
|
67
69
|
const gain = this.audioContext.createGain();
|
|
70
|
+
let denoiseNode;
|
|
71
|
+
if (this.isDenoiserEnabled() && typeof this.audioContext.audioWorklet !== "undefined") {
|
|
72
|
+
try {
|
|
73
|
+
await this.ensureDenoiseWorklet();
|
|
74
|
+
denoiseNode = new AudioWorkletNode(this.audioContext, "odyssey-denoise", {
|
|
75
|
+
numberOfInputs: 1,
|
|
76
|
+
numberOfOutputs: 1,
|
|
77
|
+
processorOptions: {
|
|
78
|
+
enabled: this.options.denoiser?.enabled !== false,
|
|
79
|
+
threshold: this.options.denoiser?.threshold,
|
|
80
|
+
noiseFloor: this.options.denoiser?.noiseFloor,
|
|
81
|
+
release: this.options.denoiser?.release,
|
|
82
|
+
wasmBytes: this.denoiserWasmBytes
|
|
83
|
+
? this.denoiserWasmBytes.slice(0)
|
|
84
|
+
: null,
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
console.warn("⚠️ Failed to initialize denoiser worklet. Falling back to raw audio.", error);
|
|
90
|
+
denoiseNode = undefined;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Create BiquadFilter nodes for static/noise reduction
|
|
94
|
+
// Based on: https://tagdiwalaviral.medium.com/struggles-of-noise-reduction-in-rtc-part-2-2526f8179442
|
|
95
|
+
const highpassFilter = this.audioContext.createBiquadFilter();
|
|
96
|
+
highpassFilter.type = "highpass";
|
|
97
|
+
highpassFilter.frequency.value = 85; // Conservative value to preserve male voice depth
|
|
98
|
+
highpassFilter.Q.value = 1.0; // Quality factor
|
|
99
|
+
const lowpassFilter = this.audioContext.createBiquadFilter();
|
|
100
|
+
lowpassFilter.type = "lowpass";
|
|
101
|
+
lowpassFilter.frequency.value = 7500; // Below 8kHz to avoid flat/muffled sound
|
|
102
|
+
lowpassFilter.Q.value = 1.0; // Quality factor
|
|
68
103
|
// Configure Panner for realistic 3D spatial audio
|
|
104
|
+
const distanceConfig = this.getDistanceConfig();
|
|
69
105
|
panner.panningModel = "HRTF"; // Head-Related Transfer Function for realistic 3D
|
|
70
106
|
panner.distanceModel = "inverse"; // Natural distance falloff
|
|
71
|
-
panner.refDistance =
|
|
72
|
-
panner.maxDistance =
|
|
73
|
-
panner.rolloffFactor = 1.
|
|
107
|
+
panner.refDistance = distanceConfig.refDistance ?? 1.2;
|
|
108
|
+
panner.maxDistance = distanceConfig.maxDistance ?? 30;
|
|
109
|
+
panner.rolloffFactor = distanceConfig.rolloffFactor ?? 1.35; // How quickly sound fades with distance
|
|
74
110
|
panner.coneInnerAngle = 360; // Omnidirectional sound source
|
|
75
111
|
panner.coneOuterAngle = 360;
|
|
76
112
|
panner.coneOuterGain = 0.3; // Some sound even outside cone
|
|
77
113
|
// Configure gain for individual participant volume control
|
|
78
114
|
gain.gain.value = 1.0;
|
|
115
|
+
let currentNode = source;
|
|
116
|
+
if (denoiseNode) {
|
|
117
|
+
currentNode.connect(denoiseNode);
|
|
118
|
+
currentNode = denoiseNode;
|
|
119
|
+
}
|
|
120
|
+
currentNode.connect(highpassFilter);
|
|
121
|
+
highpassFilter.connect(lowpassFilter);
|
|
79
122
|
if (bypassSpatialization) {
|
|
80
123
|
console.log(`🔊 TESTING: Connecting audio directly to destination (bypassing spatial audio) for ${participantId}`);
|
|
81
|
-
|
|
124
|
+
lowpassFilter.connect(analyser);
|
|
82
125
|
analyser.connect(this.masterGainNode);
|
|
83
126
|
}
|
|
84
127
|
else {
|
|
85
128
|
// Standard spatialized path with full audio chain
|
|
86
|
-
source
|
|
129
|
+
// Audio Chain: source -> filters -> panner -> analyser -> gain -> masterGain -> compressor -> destination
|
|
130
|
+
lowpassFilter.connect(panner);
|
|
87
131
|
panner.connect(analyser);
|
|
88
132
|
analyser.connect(gain);
|
|
89
133
|
gain.connect(this.masterGainNode);
|
|
@@ -93,6 +137,9 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
93
137
|
panner,
|
|
94
138
|
analyser,
|
|
95
139
|
gain,
|
|
140
|
+
highpassFilter,
|
|
141
|
+
lowpassFilter,
|
|
142
|
+
denoiseNode,
|
|
96
143
|
stream,
|
|
97
144
|
});
|
|
98
145
|
console.log(`🎧 Spatial audio setup complete for ${participantId}:`, {
|
|
@@ -180,10 +227,11 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
180
227
|
updateSpatialAudio(participantId, position, direction) {
|
|
181
228
|
const nodes = this.participantNodes.get(participantId);
|
|
182
229
|
if (nodes?.panner) {
|
|
230
|
+
const normalizedPosition = this.normalizePositionUnits(position);
|
|
183
231
|
// Update position (where the sound is coming from)
|
|
184
|
-
nodes.panner.positionX.setValueAtTime(
|
|
185
|
-
nodes.panner.positionY.setValueAtTime(
|
|
186
|
-
nodes.panner.positionZ.setValueAtTime(
|
|
232
|
+
nodes.panner.positionX.setValueAtTime(normalizedPosition.x, this.audioContext.currentTime);
|
|
233
|
+
nodes.panner.positionY.setValueAtTime(normalizedPosition.y, this.audioContext.currentTime);
|
|
234
|
+
nodes.panner.positionZ.setValueAtTime(normalizedPosition.z, this.audioContext.currentTime);
|
|
187
235
|
// Update orientation (where the participant is facing)
|
|
188
236
|
// This makes the audio source directional based on participant's direction
|
|
189
237
|
if (direction) {
|
|
@@ -205,6 +253,7 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
205
253
|
setListenerPosition(position, orientation) {
|
|
206
254
|
const { listener } = this.audioContext;
|
|
207
255
|
if (listener) {
|
|
256
|
+
const normalizedPosition = this.normalizePositionUnits(position);
|
|
208
257
|
// Store listener direction for reference
|
|
209
258
|
this.listenerDirection = {
|
|
210
259
|
forward: {
|
|
@@ -220,9 +269,9 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
220
269
|
};
|
|
221
270
|
// Use setPosition and setOrientation for atomic updates if available
|
|
222
271
|
if (listener.positionX) {
|
|
223
|
-
listener.positionX.setValueAtTime(
|
|
224
|
-
listener.positionY.setValueAtTime(
|
|
225
|
-
listener.positionZ.setValueAtTime(
|
|
272
|
+
listener.positionX.setValueAtTime(normalizedPosition.x, this.audioContext.currentTime);
|
|
273
|
+
listener.positionY.setValueAtTime(normalizedPosition.y, this.audioContext.currentTime);
|
|
274
|
+
listener.positionZ.setValueAtTime(normalizedPosition.z, this.audioContext.currentTime);
|
|
226
275
|
}
|
|
227
276
|
if (listener.forwardX) {
|
|
228
277
|
listener.forwardX.setValueAtTime(orientation.forwardX, this.audioContext.currentTime);
|
|
@@ -256,10 +305,13 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
256
305
|
* @param lookAtPos Look-at position (where camera is pointing)
|
|
257
306
|
*/
|
|
258
307
|
setListenerFromLSD(listenerPos, cameraPos, lookAtPos) {
|
|
308
|
+
const normalizedListener = this.normalizePositionUnits(listenerPos);
|
|
309
|
+
const normalizedCamera = this.normalizePositionUnits(cameraPos);
|
|
310
|
+
const normalizedLookAt = this.normalizePositionUnits(lookAtPos);
|
|
259
311
|
// Calculate forward vector (from camera to look-at point)
|
|
260
|
-
const forwardX =
|
|
261
|
-
const forwardY =
|
|
262
|
-
const forwardZ =
|
|
312
|
+
const forwardX = normalizedLookAt.x - normalizedCamera.x;
|
|
313
|
+
const forwardY = normalizedLookAt.y - normalizedCamera.y;
|
|
314
|
+
const forwardZ = normalizedLookAt.z - normalizedCamera.z;
|
|
263
315
|
// Normalize forward vector
|
|
264
316
|
const forwardLen = Math.sqrt(forwardX * forwardX + forwardY * forwardY + forwardZ * forwardZ);
|
|
265
317
|
if (forwardLen < 0.001) {
|
|
@@ -277,7 +329,7 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
277
329
|
const rightLen = Math.sqrt(rightX * rightX + rightY * rightY + rightZ * rightZ);
|
|
278
330
|
if (rightLen < 0.001) {
|
|
279
331
|
// Forward is parallel to world up, use fallback
|
|
280
|
-
this.setListenerPosition(
|
|
332
|
+
this.setListenerPosition(normalizedListener, {
|
|
281
333
|
forwardX: fwdX,
|
|
282
334
|
forwardY: fwdY,
|
|
283
335
|
forwardZ: fwdZ,
|
|
@@ -294,7 +346,7 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
294
346
|
const upX = fwdY * rZ - fwdZ * rY;
|
|
295
347
|
const upY = fwdZ * rX - fwdX * rZ;
|
|
296
348
|
const upZ = fwdX * rY - fwdY * rX;
|
|
297
|
-
this.setListenerPosition(
|
|
349
|
+
this.setListenerPosition(normalizedListener, {
|
|
298
350
|
forwardX: fwdX,
|
|
299
351
|
forwardY: fwdY,
|
|
300
352
|
forwardZ: fwdZ,
|
|
@@ -315,6 +367,9 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
315
367
|
nodes.panner.disconnect();
|
|
316
368
|
nodes.analyser.disconnect();
|
|
317
369
|
nodes.gain.disconnect();
|
|
370
|
+
if (nodes.denoiseNode) {
|
|
371
|
+
nodes.denoiseNode.disconnect();
|
|
372
|
+
}
|
|
318
373
|
nodes.stream.getTracks().forEach((track) => track.stop());
|
|
319
374
|
this.participantNodes.delete(participantId);
|
|
320
375
|
console.log(`🗑️ Removed participant ${participantId} from spatial audio.`);
|
|
@@ -329,5 +384,153 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
329
384
|
getAudioContextState() {
|
|
330
385
|
return this.audioContext.state;
|
|
331
386
|
}
|
|
387
|
+
getDistanceConfig() {
|
|
388
|
+
return {
|
|
389
|
+
refDistance: this.options.distance?.refDistance ?? 1.2,
|
|
390
|
+
maxDistance: this.options.distance?.maxDistance ?? 30,
|
|
391
|
+
rolloffFactor: this.options.distance?.rolloffFactor ?? 1.35,
|
|
392
|
+
unit: this.options.distance?.unit ?? "auto",
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
normalizePositionUnits(position) {
|
|
396
|
+
const distanceConfig = this.getDistanceConfig();
|
|
397
|
+
if (distanceConfig.unit === "meters") {
|
|
398
|
+
return { ...position };
|
|
399
|
+
}
|
|
400
|
+
if (distanceConfig.unit === "centimeters") {
|
|
401
|
+
return {
|
|
402
|
+
x: position.x / 100,
|
|
403
|
+
y: position.y / 100,
|
|
404
|
+
z: position.z / 100,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
const maxAxis = Math.max(Math.abs(position.x), Math.abs(position.y), Math.abs(position.z));
|
|
408
|
+
if (maxAxis > 50) {
|
|
409
|
+
// Likely centimeters coming from server
|
|
410
|
+
return {
|
|
411
|
+
x: position.x / 100,
|
|
412
|
+
y: position.y / 100,
|
|
413
|
+
z: position.z / 100,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
return { ...position };
|
|
417
|
+
}
|
|
418
|
+
isDenoiserEnabled() {
|
|
419
|
+
return this.options.denoiser?.enabled !== false;
|
|
420
|
+
}
|
|
421
|
+
async ensureDenoiseWorklet() {
|
|
422
|
+
if (!this.isDenoiserEnabled()) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
if (!("audioWorklet" in this.audioContext)) {
|
|
426
|
+
console.warn("⚠️ AudioWorklet not supported in this browser. Disabling denoiser.");
|
|
427
|
+
this.options.denoiser = {
|
|
428
|
+
...(this.options.denoiser || {}),
|
|
429
|
+
enabled: false,
|
|
430
|
+
};
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
if (this.denoiseWorkletReady) {
|
|
434
|
+
return this.denoiseWorkletReady;
|
|
435
|
+
}
|
|
436
|
+
const processorSource = `class OdysseyDenoiseProcessor extends AudioWorkletProcessor {
|
|
437
|
+
constructor(options) {
|
|
438
|
+
super();
|
|
439
|
+
const cfg = (options && options.processorOptions) || {};
|
|
440
|
+
this.enabled = cfg.enabled !== false;
|
|
441
|
+
this.threshold = typeof cfg.threshold === 'number' ? cfg.threshold : 0.012;
|
|
442
|
+
this.noiseFloor = typeof cfg.noiseFloor === 'number' ? cfg.noiseFloor : 0.004;
|
|
443
|
+
this.release = typeof cfg.release === 'number' ? cfg.release : 0.18;
|
|
444
|
+
this.smoothedLevel = this.noiseFloor;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
process(inputs, outputs) {
|
|
448
|
+
const input = inputs[0];
|
|
449
|
+
const output = outputs[0];
|
|
450
|
+
if (!input || input.length === 0 || !output || output.length === 0) {
|
|
451
|
+
return true;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
for (let channel = 0; channel < input.length; channel++) {
|
|
455
|
+
const inChannel = input[channel];
|
|
456
|
+
const outChannel = output[channel];
|
|
457
|
+
if (!inChannel || !outChannel) {
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
let sum = 0;
|
|
462
|
+
for (let i = 0; i < inChannel.length; i++) {
|
|
463
|
+
const sample = inChannel[i];
|
|
464
|
+
sum += sample * sample;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const rms = Math.sqrt(sum / inChannel.length);
|
|
468
|
+
this.smoothedLevel += (rms - this.smoothedLevel) * this.release;
|
|
469
|
+
const dynamicThreshold = Math.max(
|
|
470
|
+
this.noiseFloor,
|
|
471
|
+
this.threshold * 0.6 + this.smoothedLevel * 0.4
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
if (!this.enabled || rms >= dynamicThreshold) {
|
|
475
|
+
for (let i = 0; i < inChannel.length; i++) {
|
|
476
|
+
outChannel[i] = inChannel[i];
|
|
477
|
+
}
|
|
478
|
+
} else {
|
|
479
|
+
for (let i = 0; i < inChannel.length; i++) {
|
|
480
|
+
outChannel[i] = 0;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return true;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
registerProcessor('odyssey-denoise', OdysseyDenoiseProcessor);
|
|
490
|
+
`;
|
|
491
|
+
const blob = new Blob([processorSource], {
|
|
492
|
+
type: "application/javascript",
|
|
493
|
+
});
|
|
494
|
+
this.denoiseWorkletUrl = URL.createObjectURL(blob);
|
|
495
|
+
this.denoiseWorkletReady = this.audioContext.audioWorklet
|
|
496
|
+
.addModule(this.denoiseWorkletUrl)
|
|
497
|
+
.catch((error) => {
|
|
498
|
+
console.error("❌ Failed to register denoise worklet", error);
|
|
499
|
+
this.options.denoiser = {
|
|
500
|
+
...(this.options.denoiser || {}),
|
|
501
|
+
enabled: false,
|
|
502
|
+
};
|
|
503
|
+
throw error;
|
|
504
|
+
});
|
|
505
|
+
return this.denoiseWorkletReady;
|
|
506
|
+
}
|
|
507
|
+
resolveOptions(options) {
|
|
508
|
+
const distanceDefaults = {
|
|
509
|
+
refDistance: 1.2,
|
|
510
|
+
maxDistance: 30,
|
|
511
|
+
rolloffFactor: 1.35,
|
|
512
|
+
unit: "auto",
|
|
513
|
+
};
|
|
514
|
+
const denoiserDefaults = {
|
|
515
|
+
enabled: true,
|
|
516
|
+
threshold: 0.012,
|
|
517
|
+
noiseFloor: 0.004,
|
|
518
|
+
release: 0.18,
|
|
519
|
+
};
|
|
520
|
+
return {
|
|
521
|
+
distance: {
|
|
522
|
+
refDistance: options?.distance?.refDistance ?? distanceDefaults.refDistance,
|
|
523
|
+
maxDistance: options?.distance?.maxDistance ?? distanceDefaults.maxDistance,
|
|
524
|
+
rolloffFactor: options?.distance?.rolloffFactor ?? distanceDefaults.rolloffFactor,
|
|
525
|
+
unit: options?.distance?.unit ?? distanceDefaults.unit,
|
|
526
|
+
},
|
|
527
|
+
denoiser: {
|
|
528
|
+
enabled: options?.denoiser?.enabled ?? denoiserDefaults.enabled,
|
|
529
|
+
threshold: options?.denoiser?.threshold ?? denoiserDefaults.threshold,
|
|
530
|
+
noiseFloor: options?.denoiser?.noiseFloor ?? denoiserDefaults.noiseFloor,
|
|
531
|
+
release: options?.denoiser?.release ?? denoiserDefaults.release,
|
|
532
|
+
},
|
|
533
|
+
};
|
|
534
|
+
}
|
|
332
535
|
}
|
|
333
536
|
exports.SpatialAudioManager = SpatialAudioManager;
|
package/dist/index.js
CHANGED
|
@@ -425,7 +425,7 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
425
425
|
else {
|
|
426
426
|
console.log("🎧 SDK: Setting up spatial audio for REMOTE participant", participant.participantId);
|
|
427
427
|
// Setup spatial audio with full 3D positioning
|
|
428
|
-
this.spatialAudioManager.setupSpatialAudioForParticipant(participant.participantId, track, false // Enable spatial audio
|
|
428
|
+
await this.spatialAudioManager.setupSpatialAudioForParticipant(participant.participantId, track, false // Enable spatial audio
|
|
429
429
|
);
|
|
430
430
|
// Update spatial audio position for this participant
|
|
431
431
|
this.spatialAudioManager.updateSpatialAudio(participant.participantId, data.position);
|
package/package.json
CHANGED