@newgameplusinc/odyssey-audio-video-sdk-dev 1.0.10 → 1.0.11

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.
@@ -26,6 +26,8 @@ export declare class SpatialAudioManager extends EventManager {
26
26
  private denoiseWorkletReady;
27
27
  private denoiseWorkletUrl?;
28
28
  private denoiserWasmBytes?;
29
+ private listenerPosition;
30
+ private listenerInitialized;
29
31
  private listenerDirection;
30
32
  constructor(options?: SpatialAudioOptions);
31
33
  getAudioContext(): AudioContext;
@@ -85,10 +87,14 @@ export declare class SpatialAudioManager extends EventManager {
85
87
  * @param lookAtPos Look-at position (where camera is pointing)
86
88
  */
87
89
  setListenerFromLSD(listenerPos: Position, cameraPos: Position, lookAtPos: Position): void;
90
+ private applyListenerTransform;
88
91
  removeParticipant(participantId: string): void;
89
92
  resumeAudioContext(): Promise<void>;
90
93
  getAudioContextState(): AudioContextState;
91
94
  private getDistanceConfig;
95
+ private applySpatialBoostIfNeeded;
96
+ private getDistanceBetween;
97
+ private calculateDistanceGain;
92
98
  private normalizePositionUnits;
93
99
  private isDenoiserEnabled;
94
100
  private ensureDenoiseWorklet;
@@ -8,6 +8,8 @@ class SpatialAudioManager extends EventManager_1.EventManager {
8
8
  this.participantNodes = new Map();
9
9
  this.monitoringIntervals = new Map();
10
10
  this.denoiseWorkletReady = null;
11
+ this.listenerPosition = { x: 0, y: 0, z: 0 };
12
+ this.listenerInitialized = false;
11
13
  this.listenerDirection = {
12
14
  forward: { x: 0, y: 1, z: 0 },
13
15
  up: { x: 0, y: 0, z: 1 },
@@ -227,11 +229,13 @@ class SpatialAudioManager extends EventManager_1.EventManager {
227
229
  updateSpatialAudio(participantId, position, direction) {
228
230
  const nodes = this.participantNodes.get(participantId);
229
231
  if (nodes?.panner) {
232
+ const distanceConfig = this.getDistanceConfig();
230
233
  const normalizedPosition = this.normalizePositionUnits(position);
234
+ const targetPosition = this.applySpatialBoostIfNeeded(normalizedPosition);
231
235
  // Update position (where the sound is coming from)
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);
236
+ nodes.panner.positionX.setValueAtTime(targetPosition.x, this.audioContext.currentTime);
237
+ nodes.panner.positionY.setValueAtTime(targetPosition.y, this.audioContext.currentTime);
238
+ nodes.panner.positionZ.setValueAtTime(targetPosition.z, this.audioContext.currentTime);
235
239
  // Update orientation (where the participant is facing)
236
240
  // This makes the audio source directional based on participant's direction
237
241
  if (direction) {
@@ -248,57 +252,23 @@ class SpatialAudioManager extends EventManager_1.EventManager {
248
252
  nodes.panner.orientationZ.setValueAtTime(normZ, this.audioContext.currentTime);
249
253
  }
250
254
  }
251
- }
252
- }
253
- setListenerPosition(position, orientation) {
254
- const { listener } = this.audioContext;
255
- if (listener) {
256
- const normalizedPosition = this.normalizePositionUnits(position);
257
- // Store listener direction for reference
258
- this.listenerDirection = {
259
- forward: {
260
- x: orientation.forwardX,
261
- y: orientation.forwardY,
262
- z: orientation.forwardZ,
263
- },
264
- up: {
265
- x: orientation.upX,
266
- y: orientation.upY,
267
- z: orientation.upZ,
268
- },
269
- };
270
- // Use setPosition and setOrientation for atomic updates if available
271
- if (listener.positionX) {
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);
275
- }
276
- if (listener.forwardX) {
277
- listener.forwardX.setValueAtTime(orientation.forwardX, this.audioContext.currentTime);
278
- listener.forwardY.setValueAtTime(orientation.forwardY, this.audioContext.currentTime);
279
- listener.forwardZ.setValueAtTime(orientation.forwardZ, this.audioContext.currentTime);
280
- listener.upX.setValueAtTime(orientation.upX, this.audioContext.currentTime);
281
- listener.upY.setValueAtTime(orientation.upY, this.audioContext.currentTime);
282
- listener.upZ.setValueAtTime(orientation.upZ, this.audioContext.currentTime);
283
- }
284
- // Log spatial audio updates occasionally
285
- if (Math.random() < 0.01) {
286
- console.log(`🎧 [Spatial Audio] Listener updated:`, {
287
- position: { x: position.x.toFixed(1), y: position.y.toFixed(1), z: position.z.toFixed(1) },
288
- forward: {
289
- x: orientation.forwardX.toFixed(2),
290
- y: orientation.forwardY.toFixed(2),
291
- z: orientation.forwardZ.toFixed(2),
292
- },
293
- up: {
294
- x: orientation.upX.toFixed(2),
295
- y: orientation.upY.toFixed(2),
296
- z: orientation.upZ.toFixed(2),
297
- },
255
+ const listenerPos = this.listenerPosition;
256
+ const distance = this.getDistanceBetween(listenerPos, targetPosition);
257
+ const distanceGain = this.calculateDistanceGain(distanceConfig, distance);
258
+ nodes.gain.gain.setTargetAtTime(distanceGain, this.audioContext.currentTime, 0.05);
259
+ if (Math.random() < 0.02) {
260
+ console.log("🎚️ [Spatial Audio] Distance gain", {
261
+ participantId,
262
+ distance: distance.toFixed(2),
263
+ gain: distanceGain.toFixed(2),
298
264
  });
299
265
  }
300
266
  }
301
267
  }
268
+ setListenerPosition(position, orientation) {
269
+ const normalizedPosition = this.normalizePositionUnits(position);
270
+ this.applyListenerTransform(normalizedPosition, orientation);
271
+ }
302
272
  /**
303
273
  * Update listener orientation from LSD camera direction
304
274
  * @param cameraPos Camera position in world space
@@ -329,7 +299,7 @@ class SpatialAudioManager extends EventManager_1.EventManager {
329
299
  const rightLen = Math.sqrt(rightX * rightX + rightY * rightY + rightZ * rightZ);
330
300
  if (rightLen < 0.001) {
331
301
  // Forward is parallel to world up, use fallback
332
- this.setListenerPosition(normalizedListener, {
302
+ this.applyListenerTransform(normalizedListener, {
333
303
  forwardX: fwdX,
334
304
  forwardY: fwdY,
335
305
  forwardZ: fwdZ,
@@ -346,7 +316,7 @@ class SpatialAudioManager extends EventManager_1.EventManager {
346
316
  const upX = fwdY * rZ - fwdZ * rY;
347
317
  const upY = fwdZ * rX - fwdX * rZ;
348
318
  const upZ = fwdX * rY - fwdY * rX;
349
- this.setListenerPosition(normalizedListener, {
319
+ this.applyListenerTransform(normalizedListener, {
350
320
  forwardX: fwdX,
351
321
  forwardY: fwdY,
352
322
  forwardZ: fwdZ,
@@ -355,6 +325,58 @@ class SpatialAudioManager extends EventManager_1.EventManager {
355
325
  upZ,
356
326
  });
357
327
  }
328
+ applyListenerTransform(normalizedPosition, orientation) {
329
+ const { listener } = this.audioContext;
330
+ if (!listener) {
331
+ return;
332
+ }
333
+ this.listenerPosition = { ...normalizedPosition };
334
+ this.listenerInitialized = true;
335
+ this.listenerDirection = {
336
+ forward: {
337
+ x: orientation.forwardX,
338
+ y: orientation.forwardY,
339
+ z: orientation.forwardZ,
340
+ },
341
+ up: {
342
+ x: orientation.upX,
343
+ y: orientation.upY,
344
+ z: orientation.upZ,
345
+ },
346
+ };
347
+ if (listener.positionX) {
348
+ listener.positionX.setValueAtTime(normalizedPosition.x, this.audioContext.currentTime);
349
+ listener.positionY.setValueAtTime(normalizedPosition.y, this.audioContext.currentTime);
350
+ listener.positionZ.setValueAtTime(normalizedPosition.z, this.audioContext.currentTime);
351
+ }
352
+ if (listener.forwardX) {
353
+ listener.forwardX.setValueAtTime(orientation.forwardX, this.audioContext.currentTime);
354
+ listener.forwardY.setValueAtTime(orientation.forwardY, this.audioContext.currentTime);
355
+ listener.forwardZ.setValueAtTime(orientation.forwardZ, this.audioContext.currentTime);
356
+ listener.upX.setValueAtTime(orientation.upX, this.audioContext.currentTime);
357
+ listener.upY.setValueAtTime(orientation.upY, this.audioContext.currentTime);
358
+ listener.upZ.setValueAtTime(orientation.upZ, this.audioContext.currentTime);
359
+ }
360
+ if (Math.random() < 0.01) {
361
+ console.log(`🎧 [Spatial Audio] Listener updated:`, {
362
+ position: {
363
+ x: normalizedPosition.x.toFixed(2),
364
+ y: normalizedPosition.y.toFixed(2),
365
+ z: normalizedPosition.z.toFixed(2),
366
+ },
367
+ forward: {
368
+ x: orientation.forwardX.toFixed(2),
369
+ y: orientation.forwardY.toFixed(2),
370
+ z: orientation.forwardZ.toFixed(2),
371
+ },
372
+ up: {
373
+ x: orientation.upX.toFixed(2),
374
+ y: orientation.upY.toFixed(2),
375
+ z: orientation.upZ.toFixed(2),
376
+ },
377
+ });
378
+ }
379
+ }
358
380
  removeParticipant(participantId) {
359
381
  // Stop monitoring
360
382
  if (this.monitoringIntervals.has(participantId)) {
@@ -392,6 +414,42 @@ class SpatialAudioManager extends EventManager_1.EventManager {
392
414
  unit: this.options.distance?.unit ?? "auto",
393
415
  };
394
416
  }
417
+ applySpatialBoostIfNeeded(position) {
418
+ if (!this.listenerInitialized) {
419
+ return position;
420
+ }
421
+ const boost = (this.options.distance?.rolloffFactor || 1) * 0.85;
422
+ if (!isFinite(boost) || boost <= 1.01) {
423
+ return position;
424
+ }
425
+ const listener = this.listenerPosition;
426
+ return {
427
+ x: listener.x + (position.x - listener.x) * boost,
428
+ y: listener.y + (position.y - listener.y) * Math.min(boost, 1.2),
429
+ z: listener.z + (position.z - listener.z) * boost,
430
+ };
431
+ }
432
+ getDistanceBetween(a, b) {
433
+ const dx = b.x - a.x;
434
+ const dy = b.y - a.y;
435
+ const dz = b.z - a.z;
436
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
437
+ }
438
+ calculateDistanceGain(config, distance) {
439
+ if (!this.listenerInitialized) {
440
+ return 1;
441
+ }
442
+ if (distance <= config.refDistance) {
443
+ return 1;
444
+ }
445
+ if (distance >= config.maxDistance) {
446
+ return 0;
447
+ }
448
+ const normalized = (distance - config.refDistance) /
449
+ Math.max(config.maxDistance - config.refDistance, 0.001);
450
+ const shaped = Math.pow(Math.max(0, 1 - normalized), Math.max(1.2, config.rolloffFactor * 1.05));
451
+ return Math.min(1, Math.max(0.01, shaped));
452
+ }
395
453
  normalizePositionUnits(position) {
396
454
  const distanceConfig = this.getDistanceConfig();
397
455
  if (distanceConfig.unit === "meters") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newgameplusinc/odyssey-audio-video-sdk-dev",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "Odyssey Spatial Audio & Video SDK using MediaSoup for real-time communication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",