@newgameplusinc/odyssey-audio-video-sdk-dev 1.0.75 → 1.0.78
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 +15 -3
- package/dist/SpatialAudioManager.js +107 -94
- package/dist/index.d.ts +10 -1
- package/dist/index.js +6 -4
- package/package.json +1 -1
|
@@ -135,14 +135,19 @@ export declare class SpatialAudioManager extends EventManager {
|
|
|
135
135
|
upZ: number;
|
|
136
136
|
}): void;
|
|
137
137
|
/**
|
|
138
|
-
*
|
|
138
|
+
* POSITION-ONLY MODE: Set listener HEAD position (no direction needed)
|
|
139
139
|
* IMPORTANT: Uses CAMERA position (head) as listener, not body position!
|
|
140
140
|
*
|
|
141
141
|
* @param listenerPos Player body position (for reference, not used as listener)
|
|
142
142
|
* @param cameraPos Camera/HEAD position - THIS is the actual listener position for audio
|
|
143
|
-
* @param lookAtPos Look-at position (where camera is pointing)
|
|
143
|
+
* @param lookAtPos Look-at position (where camera is pointing) - stored but not used for panning
|
|
144
|
+
* @param rot Rotation data (pitch, yaw, roll) - stored but not used for panning
|
|
144
145
|
*/
|
|
145
|
-
setListenerFromLSD(listenerPos: Position, cameraPos: Position, lookAtPos: Position
|
|
146
|
+
setListenerFromLSD(listenerPos: Position, cameraPos: Position, lookAtPos: Position, rot?: {
|
|
147
|
+
x: number;
|
|
148
|
+
y: number;
|
|
149
|
+
z: number;
|
|
150
|
+
}): void;
|
|
146
151
|
private applyListenerTransform;
|
|
147
152
|
removeParticipant(participantId: string): void;
|
|
148
153
|
resumeAudioContext(): Promise<void>;
|
|
@@ -172,6 +177,13 @@ export declare class SpatialAudioManager extends EventManager {
|
|
|
172
177
|
*/
|
|
173
178
|
private computeHeadPosition;
|
|
174
179
|
/**
|
|
180
|
+
* Calculate stereo panning based on X-axis position difference ONLY
|
|
181
|
+
* @param dx Horizontal difference (speaker.x - listener.x)
|
|
182
|
+
* @returns { left: 0-100, right: 0-100 }
|
|
183
|
+
*/
|
|
184
|
+
private calculatePositionBasedPanning;
|
|
185
|
+
/**
|
|
186
|
+
* OLD METHOD - Kept for reference but not used in position-only mode
|
|
175
187
|
* Calculate angle between listener and sound source in degrees (0-360)
|
|
176
188
|
* 0° = front, 90° = right, 180° = back, 270° = left
|
|
177
189
|
*/
|
|
@@ -122,6 +122,9 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
122
122
|
const monoGainR = this.audioContext.createGain();
|
|
123
123
|
monoGainL.gain.value = 0.5;
|
|
124
124
|
monoGainR.gain.value = 0.5;
|
|
125
|
+
// CRITICAL: Convert mono back to stereo for StereoPanner
|
|
126
|
+
// monoMerger outputs 1 channel, but StereoPanner needs 2 channels
|
|
127
|
+
const stereoUpmixer = this.audioContext.createChannelMerger(2);
|
|
125
128
|
const analyser = this.audioContext.createAnalyser();
|
|
126
129
|
const gain = this.audioContext.createGain();
|
|
127
130
|
const proximityGain = this.audioContext.createGain();
|
|
@@ -228,13 +231,16 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
228
231
|
voiceBandFilter.connect(lowpassFilter);
|
|
229
232
|
lowpassFilter.connect(dynamicLowpass);
|
|
230
233
|
dynamicLowpass.connect(proximityGain);
|
|
231
|
-
// Base routing (always): proximityGain -> mono downmix -> analyser
|
|
234
|
+
// Base routing (always): proximityGain -> mono downmix -> stereo upmix -> analyser
|
|
232
235
|
proximityGain.connect(monoSplitter);
|
|
233
236
|
monoSplitter.connect(monoGainL, 0);
|
|
234
237
|
monoSplitter.connect(monoGainR, 1);
|
|
235
238
|
monoGainL.connect(monoMerger, 0, 0);
|
|
236
239
|
monoGainR.connect(monoMerger, 0, 0);
|
|
237
|
-
|
|
240
|
+
// Convert mono to stereo (same signal on both channels) for StereoPanner
|
|
241
|
+
monoMerger.connect(stereoUpmixer, 0, 0); // mono -> left channel
|
|
242
|
+
monoMerger.connect(stereoUpmixer, 0, 1); // mono -> right channel
|
|
243
|
+
stereoUpmixer.connect(analyser);
|
|
238
244
|
// Output routing depends on spatialization mode:
|
|
239
245
|
// - Spatial: analyser -> stereoPanner -> gain -> master
|
|
240
246
|
// - Non-spatial: analyser -> gain -> master
|
|
@@ -254,6 +260,7 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
254
260
|
monoGainL,
|
|
255
261
|
monoGainR,
|
|
256
262
|
monoMerger,
|
|
263
|
+
stereoUpmixer,
|
|
257
264
|
analyser,
|
|
258
265
|
gain,
|
|
259
266
|
proximityGain,
|
|
@@ -348,17 +355,20 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
348
355
|
return;
|
|
349
356
|
}
|
|
350
357
|
if (nodes?.panner) {
|
|
351
|
-
//
|
|
352
|
-
// Compute speaker's head
|
|
358
|
+
// POSITION-ONLY SPATIAL AUDIO
|
|
359
|
+
// Compute speaker's head position from body position
|
|
353
360
|
const normalizedBodyPosition = this.normalizePositionUnits(position);
|
|
354
361
|
const speakerHeadPosition = this.computeHeadPosition(normalizedBodyPosition);
|
|
355
|
-
const listenerPos = this.listenerPosition; // This is HEAD position
|
|
356
|
-
//
|
|
362
|
+
const listenerPos = this.listenerPosition; // This is listener HEAD position
|
|
363
|
+
// 🎧 [SDK Rx] Log received data
|
|
364
|
+
console.log(`🎧 [SDK Rx] ${participantId.substring(0, 8)} ` +
|
|
365
|
+
`bodyPos=(${normalizedBodyPosition.x.toFixed(2)}, ${normalizedBodyPosition.y.toFixed(2)}, ${normalizedBodyPosition.z.toFixed(2)}) ` +
|
|
366
|
+
`rot=(${direction?.x.toFixed(1) || 'N/A'}, ${direction?.y.toFixed(1) || 'N/A'}, ${direction?.z.toFixed(1) || 'N/A'})`);
|
|
367
|
+
// Calculate 3D distance (includes all axes)
|
|
357
368
|
const distance = this.getDistanceBetween(listenerPos, speakerHeadPosition);
|
|
358
|
-
// Calculate
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
const panning = this.calculatePanning(angle);
|
|
369
|
+
// Calculate panning based ONLY on X-axis position difference
|
|
370
|
+
const dx = speakerHeadPosition.x - listenerPos.x;
|
|
371
|
+
const panning = this.calculatePositionBasedPanning(dx);
|
|
362
372
|
// Calculate logarithmic gain based on distance
|
|
363
373
|
const gain = this.calculateLogarithmicGain(distance);
|
|
364
374
|
// Apply panning
|
|
@@ -383,46 +393,15 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
383
393
|
// Log every 1000ms (1 second) for easier debugging
|
|
384
394
|
if (now - lastLog > 1000) {
|
|
385
395
|
this.lastLogTime.set(participantId, now);
|
|
396
|
+
// Position-based spatial audio log (already calculated dx above)
|
|
386
397
|
const panValue = ((panning.right - panning.left) / 100).toFixed(2);
|
|
387
|
-
const dx = speakerHeadPosition.x - listenerPos.x;
|
|
388
|
-
const dz = speakerHeadPosition.z - listenerPos.z;
|
|
389
|
-
const fwd = this.listenerDirection.forward;
|
|
390
|
-
const fLen = Math.hypot(fwd.x, fwd.z);
|
|
391
|
-
const fx = fLen > 1e-4 ? fwd.x / fLen : 0;
|
|
392
|
-
const fz = fLen > 1e-4 ? fwd.z / fLen : 1;
|
|
393
|
-
const vLen = Math.hypot(dx, dz);
|
|
394
|
-
const nx = vLen > 1e-4 ? dx / vLen : 0;
|
|
395
|
-
const nz = vLen > 1e-4 ? dz / vLen : 1;
|
|
396
|
-
const dot = fx * nx + fz * nz;
|
|
397
|
-
const cross = fx * nz - fz * nx;
|
|
398
|
-
// Right vector in X/Z plane (clockwise rotation of forward)
|
|
399
|
-
const rx = fz;
|
|
400
|
-
const rz = -fx;
|
|
401
|
-
const dotRight = rx * nx + rz * nz;
|
|
402
|
-
const dotForward = dot;
|
|
403
|
-
const side = Math.abs(dotRight) < 0.15 && dotForward > 0.35
|
|
404
|
-
? "front"
|
|
405
|
-
: Math.abs(dotRight) < 0.15 && dotForward < -0.35
|
|
406
|
-
? "back"
|
|
407
|
-
: dotRight > 0
|
|
408
|
-
? "right"
|
|
409
|
-
: "left";
|
|
410
|
-
let rawDeg = (Math.atan2(cross, dot) * 180) / Math.PI;
|
|
411
|
-
if (rawDeg < 0)
|
|
412
|
-
rawDeg += 360;
|
|
413
|
-
const computedAngle = (360 - rawDeg) % 360;
|
|
414
|
-
// Avoid console.group() because some consoles hide/collapse it.
|
|
415
398
|
console.log(`🎧 SPATIAL AUDIO [${participantId.substring(0, 8)}] ` +
|
|
416
|
-
`dist=${distance.toFixed(2)}m
|
|
399
|
+
`dist=${distance.toFixed(2)}m ` +
|
|
400
|
+
`dx=${dx.toFixed(2)}m ` +
|
|
417
401
|
`pan(L=${panning.left.toFixed(0)}%,R=${panning.right.toFixed(0)}%,v=${panValue}) ` +
|
|
418
402
|
`gain=${gain.toFixed(0)}% ` +
|
|
419
|
-
`
|
|
420
|
-
`
|
|
421
|
-
`dotF=${dotForward.toFixed(2)} dotR=${dotRight.toFixed(2)} side=${side} ` +
|
|
422
|
-
`raw=${rawDeg.toFixed(0)}° calc=${computedAngle.toFixed(0)}° ` +
|
|
423
|
-
`you=(${listenerPos.x.toFixed(1)},${listenerPos.y.toFixed(1)},${listenerPos.z.toFixed(1)}) ` +
|
|
424
|
-
`them=(${speakerHeadPosition.x.toFixed(1)},${speakerHeadPosition.y.toFixed(1)},${speakerHeadPosition.z.toFixed(1)}) ` +
|
|
425
|
-
`body=(${normalizedBodyPosition.x.toFixed(1)},${normalizedBodyPosition.y.toFixed(1)},${normalizedBodyPosition.z.toFixed(1)})`);
|
|
403
|
+
`you=(${listenerPos.x.toFixed(2)},${listenerPos.y.toFixed(2)},${listenerPos.z.toFixed(2)}) ` +
|
|
404
|
+
`them=(${speakerHeadPosition.x.toFixed(2)},${speakerHeadPosition.y.toFixed(2)},${speakerHeadPosition.z.toFixed(2)})`);
|
|
426
405
|
}
|
|
427
406
|
}
|
|
428
407
|
}
|
|
@@ -455,65 +434,77 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
455
434
|
this.applyListenerTransform(normalizedPosition, orientation);
|
|
456
435
|
}
|
|
457
436
|
/**
|
|
458
|
-
*
|
|
437
|
+
* POSITION-ONLY MODE: Set listener HEAD position (no direction needed)
|
|
459
438
|
* IMPORTANT: Uses CAMERA position (head) as listener, not body position!
|
|
460
439
|
*
|
|
461
440
|
* @param listenerPos Player body position (for reference, not used as listener)
|
|
462
441
|
* @param cameraPos Camera/HEAD position - THIS is the actual listener position for audio
|
|
463
|
-
* @param lookAtPos Look-at position (where camera is pointing)
|
|
442
|
+
* @param lookAtPos Look-at position (where camera is pointing) - stored but not used for panning
|
|
443
|
+
* @param rot Rotation data (pitch, yaw, roll) - stored but not used for panning
|
|
464
444
|
*/
|
|
465
|
-
setListenerFromLSD(listenerPos, cameraPos, lookAtPos) {
|
|
445
|
+
setListenerFromLSD(listenerPos, cameraPos, lookAtPos, rot) {
|
|
466
446
|
// USE CAMERA POSITION AS LISTENER (head position, not body!)
|
|
467
|
-
const normalizedListener = this.normalizePositionUnits(cameraPos);
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
//
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
//
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
447
|
+
const normalizedListener = this.normalizePositionUnits(cameraPos);
|
|
448
|
+
// Store listener position (used for X-axis panning and distance calculation)
|
|
449
|
+
this.listenerPosition = normalizedListener;
|
|
450
|
+
// 📍 [SDK Listener] Log listener position update
|
|
451
|
+
console.log(`📍 [SDK Listener] ` +
|
|
452
|
+
`pos=(${normalizedListener.x.toFixed(2)}, ${normalizedListener.y.toFixed(2)}, ${normalizedListener.z.toFixed(2)}) ` +
|
|
453
|
+
`rot=(${rot?.x.toFixed(1) || 'N/A'}, ${rot?.y.toFixed(1) || 'N/A'}, ${rot?.z.toFixed(1) || 'N/A'})`);
|
|
454
|
+
// Position-only mode: No forward vector calculations needed
|
|
455
|
+
// Panning is calculated purely from X-axis position difference in updateSpatialAudio()
|
|
456
|
+
// LEGACY CODE BELOW - Kept for reference but not executed
|
|
457
|
+
if (false) {
|
|
458
|
+
const normalizedCamera = this.normalizePositionUnits(cameraPos);
|
|
459
|
+
const normalizedLookAt = this.normalizePositionUnits(lookAtPos);
|
|
460
|
+
// Calculate forward vector (from camera to look-at point)
|
|
461
|
+
const forwardX = normalizedLookAt.x - normalizedCamera.x;
|
|
462
|
+
const forwardY = normalizedLookAt.y - normalizedCamera.y;
|
|
463
|
+
const forwardZ = normalizedLookAt.z - normalizedCamera.z;
|
|
464
|
+
// Normalize forward vector
|
|
465
|
+
const forwardLen = Math.sqrt(forwardX * forwardX + forwardY * forwardY + forwardZ * forwardZ);
|
|
466
|
+
if (forwardLen < 0.001) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const fwdX = forwardX / forwardLen;
|
|
470
|
+
const fwdY = forwardY / forwardLen;
|
|
471
|
+
const fwdZ = forwardZ / forwardLen;
|
|
472
|
+
// Calculate right vector (cross product of world up and forward)
|
|
473
|
+
// Web Audio API uses Y-up coordinate system, Unreal uses Z-up
|
|
474
|
+
// We need to transform: Unreal (X,Y,Z) -> WebAudio (X,Z,-Y)
|
|
475
|
+
const worldUp = { x: 0, y: 1, z: 0 }; // Web Audio Y-up
|
|
476
|
+
const rightX = worldUp.y * fwdZ - worldUp.z * fwdY;
|
|
477
|
+
const rightY = worldUp.z * fwdX - worldUp.x * fwdZ;
|
|
478
|
+
const rightZ = worldUp.x * fwdY - worldUp.y * fwdX;
|
|
479
|
+
const rightLen = Math.sqrt(rightX * rightX + rightY * rightY + rightZ * rightZ);
|
|
480
|
+
if (rightLen < 0.001) {
|
|
481
|
+
// Forward is parallel to world up, use fallback
|
|
482
|
+
this.applyListenerTransform(normalizedListener, {
|
|
483
|
+
forwardX: fwdX,
|
|
484
|
+
forwardY: fwdY,
|
|
485
|
+
forwardZ: fwdZ,
|
|
486
|
+
upX: 0,
|
|
487
|
+
upY: 1,
|
|
488
|
+
upZ: 0,
|
|
489
|
+
});
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
const rX = rightX / rightLen;
|
|
493
|
+
const rY = rightY / rightLen;
|
|
494
|
+
const rZ = rightZ / rightLen;
|
|
495
|
+
// Calculate true up vector (cross product of forward and right)
|
|
496
|
+
const upX = fwdY * rZ - fwdZ * rY;
|
|
497
|
+
const upY = fwdZ * rX - fwdX * rZ;
|
|
498
|
+
const upZ = fwdX * rY - fwdY * rX;
|
|
492
499
|
this.applyListenerTransform(normalizedListener, {
|
|
493
500
|
forwardX: fwdX,
|
|
494
501
|
forwardY: fwdY,
|
|
495
502
|
forwardZ: fwdZ,
|
|
496
|
-
upX
|
|
497
|
-
upY
|
|
498
|
-
upZ
|
|
503
|
+
upX,
|
|
504
|
+
upY,
|
|
505
|
+
upZ,
|
|
499
506
|
});
|
|
500
|
-
|
|
501
|
-
}
|
|
502
|
-
const rX = rightX / rightLen;
|
|
503
|
-
const rY = rightY / rightLen;
|
|
504
|
-
const rZ = rightZ / rightLen;
|
|
505
|
-
// Calculate true up vector (cross product of forward and right)
|
|
506
|
-
const upX = fwdY * rZ - fwdZ * rY;
|
|
507
|
-
const upY = fwdZ * rX - fwdX * rZ;
|
|
508
|
-
const upZ = fwdX * rY - fwdY * rX;
|
|
509
|
-
this.applyListenerTransform(normalizedListener, {
|
|
510
|
-
forwardX: fwdX,
|
|
511
|
-
forwardY: fwdY,
|
|
512
|
-
forwardZ: fwdZ,
|
|
513
|
-
upX,
|
|
514
|
-
upY,
|
|
515
|
-
upZ,
|
|
516
|
-
});
|
|
507
|
+
} // End of legacy code block
|
|
517
508
|
}
|
|
518
509
|
applyListenerTransform(normalizedPosition, orientation) {
|
|
519
510
|
const { listener } = this.audioContext;
|
|
@@ -563,6 +554,7 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
563
554
|
nodes.monoGainL.disconnect();
|
|
564
555
|
nodes.monoGainR.disconnect();
|
|
565
556
|
nodes.monoMerger.disconnect();
|
|
557
|
+
nodes.stereoUpmixer.disconnect();
|
|
566
558
|
nodes.stereoPanner.disconnect();
|
|
567
559
|
nodes.analyser.disconnect();
|
|
568
560
|
nodes.gain.disconnect();
|
|
@@ -760,6 +752,27 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
760
752
|
};
|
|
761
753
|
}
|
|
762
754
|
/**
|
|
755
|
+
* Calculate stereo panning based on X-axis position difference ONLY
|
|
756
|
+
* @param dx Horizontal difference (speaker.x - listener.x)
|
|
757
|
+
* @returns { left: 0-100, right: 0-100 }
|
|
758
|
+
*/
|
|
759
|
+
calculatePositionBasedPanning(dx) {
|
|
760
|
+
const threshold = 0.1; // 10cm threshold for "centered"
|
|
761
|
+
if (dx > threshold) {
|
|
762
|
+
// Speaker is to the RIGHT → Pan RIGHT
|
|
763
|
+
return { left: 0, right: 100 };
|
|
764
|
+
}
|
|
765
|
+
else if (dx < -threshold) {
|
|
766
|
+
// Speaker is to the LEFT → Pan LEFT
|
|
767
|
+
return { left: 100, right: 0 };
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
// Centered (same X position)
|
|
771
|
+
return { left: 100, right: 100 };
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* OLD METHOD - Kept for reference but not used in position-only mode
|
|
763
776
|
* Calculate angle between listener and sound source in degrees (0-360)
|
|
764
777
|
* 0° = front, 90° = right, 180° = back, 270° = left
|
|
765
778
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -48,6 +48,11 @@ export declare class OdysseySpatialComms extends EventManager {
|
|
|
48
48
|
x: number;
|
|
49
49
|
y: number;
|
|
50
50
|
} | null;
|
|
51
|
+
rot?: {
|
|
52
|
+
x: number;
|
|
53
|
+
y: number;
|
|
54
|
+
z: number;
|
|
55
|
+
};
|
|
51
56
|
}): void;
|
|
52
57
|
updateMediaState(mediaState: MediaState): void;
|
|
53
58
|
setListenerPosition(position: Position, orientation: {
|
|
@@ -58,7 +63,11 @@ export declare class OdysseySpatialComms extends EventManager {
|
|
|
58
63
|
upY: number;
|
|
59
64
|
upZ: number;
|
|
60
65
|
}): void;
|
|
61
|
-
setListenerFromLSD(listenerPos: Position, cameraPos: Position, lookAtPos: Position
|
|
66
|
+
setListenerFromLSD(listenerPos: Position, cameraPos: Position, lookAtPos: Position, rot?: {
|
|
67
|
+
x: number;
|
|
68
|
+
y: number;
|
|
69
|
+
z: number;
|
|
70
|
+
}): void;
|
|
62
71
|
private listenForEvents;
|
|
63
72
|
/**
|
|
64
73
|
* Send huddle invite to another participant
|
package/dist/index.js
CHANGED
|
@@ -201,6 +201,7 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
201
201
|
roomId: this.room.id,
|
|
202
202
|
position,
|
|
203
203
|
direction,
|
|
204
|
+
rot: spatialData?.rot,
|
|
204
205
|
cameraDistance: spatialData?.cameraDistance,
|
|
205
206
|
screenPos: spatialData?.screenPos,
|
|
206
207
|
};
|
|
@@ -221,8 +222,8 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
221
222
|
setListenerPosition(position, orientation) {
|
|
222
223
|
this.spatialAudioManager.setListenerPosition(position, orientation);
|
|
223
224
|
}
|
|
224
|
-
setListenerFromLSD(listenerPos, cameraPos, lookAtPos) {
|
|
225
|
-
this.spatialAudioManager.setListenerFromLSD(listenerPos, cameraPos, lookAtPos);
|
|
225
|
+
setListenerFromLSD(listenerPos, cameraPos, lookAtPos, rot) {
|
|
226
|
+
this.spatialAudioManager.setListenerFromLSD(listenerPos, cameraPos, lookAtPos, rot);
|
|
226
227
|
}
|
|
227
228
|
listenForEvents() {
|
|
228
229
|
// CRITICAL: Register all-participants-update listener at initialization
|
|
@@ -453,8 +454,9 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
453
454
|
const participantChannel = participant.currentChannel;
|
|
454
455
|
const isInHuddle = participantChannel === "odyssey-huddle";
|
|
455
456
|
if (!isInHuddle) {
|
|
456
|
-
// Update spatial audio with
|
|
457
|
-
this.spatialAudioManager.updateSpatialAudio(data.participantId, data.position, data.direction
|
|
457
|
+
// Update spatial audio with position, direction, AND rotation
|
|
458
|
+
this.spatialAudioManager.updateSpatialAudio(data.participantId, data.position, data.rot || data.direction // Prefer rot, fallback to direction
|
|
459
|
+
);
|
|
458
460
|
}
|
|
459
461
|
this.emit("participant-position-updated", participant);
|
|
460
462
|
}
|
package/package.json
CHANGED