@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.
|
|
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
|
|
437
|
-
|
|
438
|
-
const
|
|
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
|
|
448
|
-
nodes.gain.gain.setTargetAtTime(
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
1052
|
-
const falloffRate = 0.
|
|
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