@newgameplusinc/odyssey-audio-video-sdk-dev 1.0.255 → 1.0.257

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.
@@ -50,7 +50,6 @@ export declare class SpatialAudioManager extends EventManager {
50
50
  private localParticipantId;
51
51
  private isMasterMuted;
52
52
  private smoothedPanValues;
53
- private lastGainValue;
54
53
  private lastPanValue;
55
54
  private lastSpatialUpdateTime;
56
55
  private _spatialDebugTimes;
@@ -60,6 +59,8 @@ export declare class SpatialAudioManager extends EventManager {
60
59
  private cachedSpeakerPositions;
61
60
  private cachedListenerPosition;
62
61
  private positionSnapThreshold;
62
+ private cachedGainValues;
63
+ private gainChangeThreshold;
63
64
  private _listenerDebugLogTime?;
64
65
  private mlSuppressor;
65
66
  private mlModelReady;
@@ -23,9 +23,6 @@ class SpatialAudioManager extends EventManager_1.EventManager {
23
23
  // PAN SMOOTHING: Prevents random left/right jumping
24
24
  // Stores the previous smoothed pan value for each participant
25
25
  this.smoothedPanValues = new Map();
26
- // GAIN SMOOTHING: Prevents audio clicks/pops from rapid gain changes
27
- // Tracks last applied gain value per participant for smooth ramping
28
- this.lastGainValue = new Map();
29
26
  // PAN SMOOTHING: Tracks last applied pan value to skip micro-changes
30
27
  this.lastPanValue = new Map();
31
28
  // UPDATE THROTTLE: Tracks last spatial update time to prevent too-frequent updates
@@ -49,6 +46,12 @@ class SpatialAudioManager extends EventManager_1.EventManager {
49
46
  // 0.30m = 30cm - ignores pixel streaming jitter, physics wobble, breathing
50
47
  // ALIGNED with server-side 100cm filter - this catches smaller jitter
51
48
  this.positionSnapThreshold = 0.30;
49
+ // GAIN STABILIZATION: Prevents gain fluctuations when distance is stable
50
+ // Caches last calculated gain value for each participant
51
+ this.cachedGainValues = new Map();
52
+ // Minimum gain change (0-1 scale) to trigger update
53
+ // 0.05 = 5% change required - filters out tiny distance jitter
54
+ this.gainChangeThreshold = 0.05;
52
55
  // NOTE: Rate limiting variables removed - setTargetAtTime provides sufficient smoothing
53
56
  // The smoothPanValue() and position snapping handle jitter reduction
54
57
  // ML Noise Suppressor (TensorFlow.js-based)
@@ -401,7 +404,7 @@ class SpatialAudioManager extends EventManager_1.EventManager {
401
404
  if (distance >= maxDistance) {
402
405
  // Smooth fade to zero - prevents click (100ms fade)
403
406
  nodes.gain.gain.setTargetAtTime(0, this.audioContext.currentTime, 0.033);
404
- this.lastGainValue.set(participantId, 0);
407
+ this.cachedGainValues.set(participantId, 0);
405
408
  return; // Skip all other processing
406
409
  }
407
410
  // Step 3: Calculate relative vector (speaker relative to listener)
@@ -433,23 +436,39 @@ class SpatialAudioManager extends EventManager_1.EventManager {
433
436
  // SMOOTH THE PAN VALUE to prevent random left/right jumping
434
437
  const smoothedPanValue = this.smoothPanValue(participantId, rawPanValue);
435
438
  const panning = this.panningFromPanValue(smoothedPanValue, dxLocal);
436
- // Calculate gain DIRECTLY based on distance - NO complex smoothing
437
- // The gain formula: sqrt curve from 100% (0.5m) to 20% (15m)
438
- const gain = this.calculateLogarithmicGain(distance);
439
+ // Calculate gain based on distance
440
+ const calculatedGain = this.calculateLogarithmicGain(distance);
441
+ const newGainValue = calculatedGain / 100; // Convert to 0-1 range
442
+ // SIMPLE GAIN STABILIZATION: Only update if change exceeds threshold
443
+ const cachedGain = this.cachedGainValues.get(participantId);
444
+ let finalGainValue = newGainValue;
445
+ if (cachedGain !== undefined) {
446
+ const gainChange = Math.abs(newGainValue - cachedGain);
447
+ if (gainChange < this.gainChangeThreshold) {
448
+ // Change too small - keep cached value for stability
449
+ finalGainValue = cachedGain;
450
+ }
451
+ else {
452
+ // Significant change - update cache
453
+ this.cachedGainValues.set(participantId, newGainValue);
454
+ }
455
+ }
456
+ else {
457
+ // First time - cache the value
458
+ this.cachedGainValues.set(participantId, newGainValue);
459
+ }
439
460
  // Apply panning
440
461
  this.applyStereoPanning(participantId, panning);
441
462
  // Apply gain with Web Audio's built-in smoothing (setTargetAtTime)
442
- // This is the ONLY smoothing needed - no additional rate limiting
443
- const gainValue = gain / 100; // Convert to 0-1 range
444
463
  const currentTime = this.audioContext.currentTime;
445
464
  try {
446
465
  // setTargetAtTime provides smooth exponential interpolation
447
- // Time constant 0.1 = ~300ms to settle (prevents clicks)
448
- nodes.gain.gain.setTargetAtTime(gainValue, currentTime, 0.1);
466
+ // Time constant 0.1 = ~300ms to settle
467
+ nodes.gain.gain.setTargetAtTime(finalGainValue, currentTime, 0.1);
449
468
  }
450
469
  catch (err) {
451
470
  // Fallback: If scheduling fails, set value directly (rare edge case)
452
- nodes.gain.gain.value = gainValue;
471
+ nodes.gain.gain.value = finalGainValue;
453
472
  }
454
473
  }
455
474
  }
@@ -469,7 +488,8 @@ class SpatialAudioManager extends EventManager_1.EventManager {
469
488
  // Smooth ramp to 0 (muted) or 1 (unmuted) - prevents click
470
489
  // Time constant 0.05 = ~150ms to reach target (3 time constants)
471
490
  nodes.gain.gain.setTargetAtTime(muted ? 0 : 1, this.audioContext.currentTime, 0.05);
472
- this.lastGainValue.set(participantId, muted ? 0 : 1);
491
+ // Update cached gain value when muting/unmuting
492
+ this.cachedGainValues.set(participantId, muted ? 0 : 1);
473
493
  }
474
494
  }
475
495
  /**
@@ -665,6 +685,10 @@ class SpatialAudioManager extends EventManager_1.EventManager {
665
685
  this.participantNodes.delete(participantId);
666
686
  // Clean up smoothed pan value tracking
667
687
  this.smoothedPanValues.delete(participantId);
688
+ // Clean up cached gain values
689
+ this.cachedGainValues.delete(participantId);
690
+ // Clean up cached speaker position
691
+ this.cachedSpeakerPositions.delete(participantId);
668
692
  }
669
693
  }
670
694
  async resumeAudioContext() {
@@ -1048,8 +1072,8 @@ class SpatialAudioManager extends EventManager_1.EventManager {
1048
1072
  */
1049
1073
  calculateLogarithmicGain(distance) {
1050
1074
  const minDistance = 1.0; // Full volume at 1m or closer
1051
- const minGain = 5; // Minimum 5% at far distances (barely audible)
1052
- const falloffRate = 0.15; // Controls how fast volume drops (higher = faster)
1075
+ const minGain = 15; // Minimum 15% at far distances (still audible)
1076
+ const falloffRate = 0.12; // Controls how fast volume drops (gentler)
1053
1077
  // Full volume within minimum distance
1054
1078
  if (distance <= minDistance)
1055
1079
  return 100;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newgameplusinc/odyssey-audio-video-sdk-dev",
3
- "version": "1.0.255",
3
+ "version": "1.0.257",
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",