@newgameplusinc/odyssey-audio-video-sdk-dev 1.0.26 → 1.0.28
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/README.md +1 -1
- package/dist/MediasoupManager.js +0 -19
- package/dist/SpatialAudioManager.js +5 -93
- package/dist/index.js +2 -165
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ It mirrors the production SDK used by Odyssey V2 and ships ready-to-drop into an
|
|
|
11
11
|
## Feature Highlights
|
|
12
12
|
- 🔌 **One class to rule it all** – `OdysseySpatialComms` wires transports, producers, consumers, and room state.
|
|
13
13
|
- 🧭 **Accurate pose propagation** – `updatePosition()` streams listener pose to the SFU while `participant-position-updated` keeps the local store in sync.
|
|
14
|
-
- 🎧 **Studio-grade spatial audio** – each remote participant gets a dedicated Web Audio graph: denoiser → high-pass → low-pass → HRTF `PannerNode` → adaptive gain → master compressor.
|
|
14
|
+
- 🎧 **Studio-grade spatial audio** – each remote participant gets a dedicated Web Audio graph: denoiser → high-pass → low-pass → HRTF `PannerNode` → adaptive gain → master compressor. Uses Web Audio API's HRTF panning model for accurate left/right/front/back positioning based on distance and direction, with custom AudioWorklet processors for noise cancellation and voice tuning.
|
|
15
15
|
- 🎥 **Camera-ready streams** – video tracks are exposed separately so UI layers can render muted `<video>` tags while audio stays inside Web Audio.
|
|
16
16
|
- 🔁 **EventEmitter contract** – subscribe to `room-joined`, `consumer-created`, `participant-position-updated`, etc., without touching Socket.IO directly.
|
|
17
17
|
|
package/dist/MediasoupManager.js
CHANGED
|
@@ -46,7 +46,6 @@ class MediasoupManager {
|
|
|
46
46
|
}
|
|
47
47
|
async loadDevice(routerRtpCapabilities) {
|
|
48
48
|
if (this.device.loaded) {
|
|
49
|
-
console.warn("Device already loaded.");
|
|
50
49
|
return;
|
|
51
50
|
}
|
|
52
51
|
await this.device.load({ routerRtpCapabilities });
|
|
@@ -110,11 +109,6 @@ class MediasoupManager {
|
|
|
110
109
|
async consume(data) {
|
|
111
110
|
if (!this.recvTransport)
|
|
112
111
|
throw new Error("Receive transport not set up");
|
|
113
|
-
console.log(`📥 Creating consumer for ${data.participantId}:`, {
|
|
114
|
-
consumerId: data.consumerId,
|
|
115
|
-
producerId: data.producerId,
|
|
116
|
-
kind: data.kind,
|
|
117
|
-
});
|
|
118
112
|
const consumer = await this.recvTransport.consume({
|
|
119
113
|
id: data.consumerId,
|
|
120
114
|
producerId: data.producerId,
|
|
@@ -122,28 +116,15 @@ class MediasoupManager {
|
|
|
122
116
|
rtpParameters: data.rtpParameters,
|
|
123
117
|
});
|
|
124
118
|
this.consumers.set(consumer.id, consumer);
|
|
125
|
-
console.log(`✅ Consumer created, track details:`, {
|
|
126
|
-
consumerId: consumer.id,
|
|
127
|
-
trackId: consumer.track.id,
|
|
128
|
-
trackKind: consumer.track.kind,
|
|
129
|
-
trackEnabled: consumer.track.enabled,
|
|
130
|
-
trackMuted: consumer.track.muted,
|
|
131
|
-
trackReadyState: consumer.track.readyState,
|
|
132
|
-
consumerPaused: consumer.paused,
|
|
133
|
-
consumerClosed: consumer.closed,
|
|
134
|
-
});
|
|
135
119
|
return { consumer, track: consumer.track };
|
|
136
120
|
}
|
|
137
121
|
async resumeConsumer(consumerId) {
|
|
138
|
-
console.log(`▶️ Resuming consumer ${consumerId}...`);
|
|
139
122
|
return new Promise((resolve, reject) => {
|
|
140
123
|
this.socket.emit("resume-consumer", { consumerId }, (response) => {
|
|
141
124
|
if (response.error) {
|
|
142
|
-
console.error(`❌ Failed to resume consumer ${consumerId}:`, response.error);
|
|
143
125
|
reject(new Error(response.error));
|
|
144
126
|
}
|
|
145
127
|
else {
|
|
146
|
-
console.log(`✅ Consumer ${consumerId} resumed successfully on server`);
|
|
147
128
|
resolve();
|
|
148
129
|
}
|
|
149
130
|
});
|
|
@@ -30,15 +30,6 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
30
30
|
// Connect master chain: masterGain -> compressor -> destination
|
|
31
31
|
this.masterGainNode.connect(this.compressor);
|
|
32
32
|
this.compressor.connect(this.audioContext.destination);
|
|
33
|
-
console.log(`🔊 SpatialAudioManager initialized with advanced audio processing:`, {
|
|
34
|
-
sampleRate: this.audioContext.sampleRate,
|
|
35
|
-
gain: this.masterGainNode.gain.value,
|
|
36
|
-
compressor: {
|
|
37
|
-
threshold: this.compressor.threshold.value,
|
|
38
|
-
ratio: this.compressor.ratio.value,
|
|
39
|
-
},
|
|
40
|
-
state: this.audioContext.state,
|
|
41
|
-
});
|
|
42
33
|
}
|
|
43
34
|
getAudioContext() {
|
|
44
35
|
return this.audioContext;
|
|
@@ -102,7 +93,6 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
102
93
|
});
|
|
103
94
|
}
|
|
104
95
|
catch (error) {
|
|
105
|
-
console.warn("⚠️ Failed to initialize denoiser worklet. Falling back to raw audio.", error);
|
|
106
96
|
denoiseNode = undefined;
|
|
107
97
|
}
|
|
108
98
|
}
|
|
@@ -143,7 +133,6 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
143
133
|
lowpassFilter.connect(dynamicLowpass);
|
|
144
134
|
dynamicLowpass.connect(proximityGain);
|
|
145
135
|
if (bypassSpatialization) {
|
|
146
|
-
console.log(`🔊 TESTING: Connecting audio directly to destination (bypassing spatial audio) for ${participantId}`);
|
|
147
136
|
proximityGain.connect(analyser);
|
|
148
137
|
analyser.connect(this.masterGainNode);
|
|
149
138
|
}
|
|
@@ -167,22 +156,6 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
167
156
|
denoiseNode,
|
|
168
157
|
stream,
|
|
169
158
|
});
|
|
170
|
-
console.log(`🎧 Spatial audio setup complete for ${participantId}:`, {
|
|
171
|
-
audioContextState: this.audioContext.state,
|
|
172
|
-
sampleRate: this.audioContext.sampleRate,
|
|
173
|
-
gain: this.masterGainNode.gain.value,
|
|
174
|
-
trackEnabled: track.enabled,
|
|
175
|
-
trackMuted: track.muted,
|
|
176
|
-
trackReadyState: track.readyState,
|
|
177
|
-
isBypassed: bypassSpatialization,
|
|
178
|
-
pannerConfig: {
|
|
179
|
-
model: panner.panningModel,
|
|
180
|
-
distanceModel: panner.distanceModel,
|
|
181
|
-
refDistance: panner.refDistance,
|
|
182
|
-
maxDistance: panner.maxDistance,
|
|
183
|
-
rolloffFactor: panner.rolloffFactor,
|
|
184
|
-
},
|
|
185
|
-
});
|
|
186
159
|
// Start monitoring audio levels
|
|
187
160
|
this.startMonitoring(participantId);
|
|
188
161
|
}
|
|
@@ -204,29 +177,8 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
204
177
|
}
|
|
205
178
|
const average = sum / dataArray.length;
|
|
206
179
|
const audioLevel = (average / 128) * 255; // Scale to 0-255
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
console.warn(`⚠️ NO AUDIO DATA detected for ${participantId}! Track may be silent or not transmitting.`);
|
|
210
|
-
console.info(`💡 Check: 1) Is microphone unmuted? 2) Is correct mic selected? 3) Is mic working in system settings?`);
|
|
211
|
-
}
|
|
212
|
-
// Check track status after 2 seconds
|
|
213
|
-
setTimeout(() => {
|
|
214
|
-
const track = stream.getAudioTracks()[0];
|
|
215
|
-
if (track) {
|
|
216
|
-
console.log(`🔊 Audio track status after 2s for ${participantId}:`, {
|
|
217
|
-
trackEnabled: track.enabled,
|
|
218
|
-
trackMuted: track.muted,
|
|
219
|
-
trackReadyState: track.readyState,
|
|
220
|
-
audioContextState: this.audioContext.state,
|
|
221
|
-
pannerPosition: {
|
|
222
|
-
x: nodes.panner.positionX.value,
|
|
223
|
-
y: nodes.panner.positionY.value,
|
|
224
|
-
z: nodes.panner.positionZ.value,
|
|
225
|
-
},
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
}, 2000);
|
|
229
|
-
}, 2000); // Log every 2 seconds
|
|
180
|
+
// Silent monitoring - no logs needed
|
|
181
|
+
}, 2000); // Check every 2 seconds
|
|
230
182
|
this.monitoringIntervals.set(participantId, interval);
|
|
231
183
|
}
|
|
232
184
|
/**
|
|
@@ -281,13 +233,6 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
281
233
|
this.applyDirectionalSuppression(participantId, distance, vectorToSource);
|
|
282
234
|
const distanceGain = this.calculateDistanceGain(distanceConfig, distance);
|
|
283
235
|
nodes.gain.gain.setTargetAtTime(distanceGain, this.audioContext.currentTime, 0.05);
|
|
284
|
-
if (Math.random() < 0.02) {
|
|
285
|
-
console.log("🎚️ [Spatial Audio] Distance gain", {
|
|
286
|
-
participantId,
|
|
287
|
-
distance: distance.toFixed(2),
|
|
288
|
-
gain: distanceGain.toFixed(2),
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
236
|
}
|
|
292
237
|
}
|
|
293
238
|
setListenerPosition(position, orientation) {
|
|
@@ -310,7 +255,6 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
310
255
|
// Normalize forward vector
|
|
311
256
|
const forwardLen = Math.sqrt(forwardX * forwardX + forwardY * forwardY + forwardZ * forwardZ);
|
|
312
257
|
if (forwardLen < 0.001) {
|
|
313
|
-
console.warn("⚠️ Forward vector too small, using default orientation");
|
|
314
258
|
return;
|
|
315
259
|
}
|
|
316
260
|
const fwdX = forwardX / forwardLen;
|
|
@@ -382,25 +326,6 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
382
326
|
listener.upY.setValueAtTime(orientation.upY, this.audioContext.currentTime);
|
|
383
327
|
listener.upZ.setValueAtTime(orientation.upZ, this.audioContext.currentTime);
|
|
384
328
|
}
|
|
385
|
-
if (Math.random() < 0.01) {
|
|
386
|
-
console.log(`🎧 [Spatial Audio] Listener updated:`, {
|
|
387
|
-
position: {
|
|
388
|
-
x: normalizedPosition.x.toFixed(2),
|
|
389
|
-
y: normalizedPosition.y.toFixed(2),
|
|
390
|
-
z: normalizedPosition.z.toFixed(2),
|
|
391
|
-
},
|
|
392
|
-
forward: {
|
|
393
|
-
x: orientation.forwardX.toFixed(2),
|
|
394
|
-
y: orientation.forwardY.toFixed(2),
|
|
395
|
-
z: orientation.forwardZ.toFixed(2),
|
|
396
|
-
},
|
|
397
|
-
up: {
|
|
398
|
-
x: orientation.upX.toFixed(2),
|
|
399
|
-
y: orientation.upY.toFixed(2),
|
|
400
|
-
z: orientation.upZ.toFixed(2),
|
|
401
|
-
},
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
329
|
}
|
|
405
330
|
removeParticipant(participantId) {
|
|
406
331
|
// Stop monitoring
|
|
@@ -419,13 +344,11 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
419
344
|
}
|
|
420
345
|
nodes.stream.getTracks().forEach((track) => track.stop());
|
|
421
346
|
this.participantNodes.delete(participantId);
|
|
422
|
-
console.log(`🗑️ Removed participant ${participantId} from spatial audio.`);
|
|
423
347
|
}
|
|
424
348
|
}
|
|
425
349
|
async resumeAudioContext() {
|
|
426
350
|
if (this.audioContext.state === "suspended") {
|
|
427
351
|
await this.audioContext.resume();
|
|
428
|
-
console.log("✅ Audio context has been resumed successfully.");
|
|
429
352
|
}
|
|
430
353
|
}
|
|
431
354
|
getAudioContextState() {
|
|
@@ -433,9 +356,9 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
433
356
|
}
|
|
434
357
|
getDistanceConfig() {
|
|
435
358
|
return {
|
|
436
|
-
refDistance: this.options.distance?.refDistance ??
|
|
437
|
-
maxDistance: this.options.distance?.maxDistance ??
|
|
438
|
-
rolloffFactor: this.options.distance?.rolloffFactor ??
|
|
359
|
+
refDistance: this.options.distance?.refDistance ?? 0.5, // Normal volume within 0.5m (very close)
|
|
360
|
+
maxDistance: this.options.distance?.maxDistance ?? 5, // Cannot hear after 5 meters
|
|
361
|
+
rolloffFactor: this.options.distance?.rolloffFactor ?? 2.5, // Aggressive falloff for realistic distance attenuation
|
|
439
362
|
unit: this.options.distance?.unit ?? "auto",
|
|
440
363
|
};
|
|
441
364
|
}
|
|
@@ -518,15 +441,6 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
518
441
|
const targetLowpass = 3600 + clarityScore * 4600; // 3.6kHz → ~8.2kHz
|
|
519
442
|
nodes.proximityGain.gain.setTargetAtTime(targetGain, this.audioContext.currentTime, 0.08);
|
|
520
443
|
nodes.dynamicLowpass.frequency.setTargetAtTime(targetLowpass, this.audioContext.currentTime, 0.12);
|
|
521
|
-
if (Math.random() < 0.005) {
|
|
522
|
-
console.log("🎚️ [Spatial Audio] Directional tuning", {
|
|
523
|
-
participantId,
|
|
524
|
-
distance: distance.toFixed(2),
|
|
525
|
-
clarityScore: clarityScore.toFixed(2),
|
|
526
|
-
targetGain: targetGain.toFixed(2),
|
|
527
|
-
lowpassHz: targetLowpass.toFixed(0),
|
|
528
|
-
});
|
|
529
|
-
}
|
|
530
444
|
}
|
|
531
445
|
calculateClarityScore(distance, vectorToSource) {
|
|
532
446
|
const proximityWeight = this.calculateProximityWeight(distance);
|
|
@@ -575,7 +489,6 @@ class SpatialAudioManager extends EventManager_1.EventManager {
|
|
|
575
489
|
return;
|
|
576
490
|
}
|
|
577
491
|
if (!("audioWorklet" in this.audioContext)) {
|
|
578
|
-
console.warn("⚠️ AudioWorklet not supported in this browser. Disabling denoiser.");
|
|
579
492
|
this.options.denoiser = {
|
|
580
493
|
...(this.options.denoiser || {}),
|
|
581
494
|
enabled: false,
|
|
@@ -876,7 +789,6 @@ registerProcessor('odyssey-denoise', OdysseyDenoiseProcessor);
|
|
|
876
789
|
this.denoiseWorkletReady = this.audioContext.audioWorklet
|
|
877
790
|
.addModule(this.denoiseWorkletUrl)
|
|
878
791
|
.catch((error) => {
|
|
879
|
-
console.error("❌ Failed to register denoise worklet", error);
|
|
880
792
|
this.options.denoiser = {
|
|
881
793
|
...(this.options.denoiser || {}),
|
|
882
794
|
enabled: false,
|
package/dist/index.js
CHANGED
|
@@ -35,15 +35,6 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
35
35
|
// Create a one-time listener for room-joined event
|
|
36
36
|
const handleRoomJoined = async (roomData) => {
|
|
37
37
|
try {
|
|
38
|
-
console.log("✅ Successfully joined room:", roomData);
|
|
39
|
-
console.log("👥 [SDK] Participants received from server:", roomData.participants);
|
|
40
|
-
console.log("🔍 [SDK] Each participant data:", roomData.participants.map((p) => ({
|
|
41
|
-
participantId: p.participantId,
|
|
42
|
-
bodyHeight: p.bodyHeight,
|
|
43
|
-
bodyShape: p.bodyShape,
|
|
44
|
-
userName: p.userName,
|
|
45
|
-
userEmail: p.userEmail,
|
|
46
|
-
})));
|
|
47
38
|
// Remove the listener to prevent memory leaks
|
|
48
39
|
this.socket.off("room-joined", handleRoomJoined);
|
|
49
40
|
// 1. Load Mediasoup device
|
|
@@ -79,7 +70,6 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
79
70
|
}
|
|
80
71
|
// 6. Note: Device RTP capabilities will be sent after producing tracks
|
|
81
72
|
// This ensures transports are fully connected via DTLS handshake
|
|
82
|
-
console.log("⏳ Transports created, will send RTP capabilities after producing tracks");
|
|
83
73
|
this.emit("room-joined", roomData);
|
|
84
74
|
resolve(this.localParticipant);
|
|
85
75
|
}
|
|
@@ -90,19 +80,6 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
90
80
|
};
|
|
91
81
|
// Register the listener
|
|
92
82
|
this.socket.on("room-joined", handleRoomJoined);
|
|
93
|
-
// Log the exact data being sent to the server
|
|
94
|
-
console.log("🚀 [SDK] Emitting 'join-room' event to server with data:", {
|
|
95
|
-
roomId: data.roomId,
|
|
96
|
-
userId: data.userId,
|
|
97
|
-
deviceId: data.deviceId,
|
|
98
|
-
participantId: data.participantId,
|
|
99
|
-
position: data.position,
|
|
100
|
-
direction: data.direction,
|
|
101
|
-
bodyHeight: data.bodyHeight,
|
|
102
|
-
bodyShape: data.bodyShape,
|
|
103
|
-
userName: data.userName,
|
|
104
|
-
userEmail: data.userEmail,
|
|
105
|
-
});
|
|
106
83
|
// Emit join-room request
|
|
107
84
|
this.socket.emit("join-room", data);
|
|
108
85
|
});
|
|
@@ -116,7 +93,6 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
116
93
|
this.room = null;
|
|
117
94
|
this.localParticipant = null;
|
|
118
95
|
this.removeAllListeners(); // Clean up all listeners
|
|
119
|
-
console.log("🚪 SDK: Left room and cleaned up resources.");
|
|
120
96
|
}
|
|
121
97
|
async resumeAudio() {
|
|
122
98
|
await this.spatialAudioManager.resumeAudioContext();
|
|
@@ -125,20 +101,7 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
125
101
|
return this.spatialAudioManager.getAudioContextState();
|
|
126
102
|
}
|
|
127
103
|
async produceTrack(track) {
|
|
128
|
-
console.log("🎤 SDK: produceTrack called:", {
|
|
129
|
-
kind: track.kind,
|
|
130
|
-
trackId: track.id,
|
|
131
|
-
enabled: track.enabled,
|
|
132
|
-
muted: track.muted,
|
|
133
|
-
readyState: track.readyState,
|
|
134
|
-
localParticipantId: this.localParticipant?.participantId
|
|
135
|
-
});
|
|
136
104
|
const producer = await this.mediasoupManager.produce(track);
|
|
137
|
-
console.log("✅ SDK: Producer created:", {
|
|
138
|
-
producerId: producer.id,
|
|
139
|
-
kind: track.kind,
|
|
140
|
-
localParticipantId: this.localParticipant?.participantId
|
|
141
|
-
});
|
|
142
105
|
if (this.localParticipant) {
|
|
143
106
|
const isFirstProducer = this.localParticipant.producers.size === 0;
|
|
144
107
|
this.localParticipant.producers.set(producer.id, producer);
|
|
@@ -153,7 +116,6 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
153
116
|
// Send device RTP capabilities after first track is produced
|
|
154
117
|
// This ensures transports are fully connected via DTLS
|
|
155
118
|
if (isFirstProducer) {
|
|
156
|
-
console.log("📡 Sending device RTP capabilities after first track produced");
|
|
157
119
|
this.mediasoupManager.sendDeviceRtpCapabilities(this.localParticipant.participantId);
|
|
158
120
|
}
|
|
159
121
|
// Notify others of media state change
|
|
@@ -174,13 +136,6 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
174
136
|
cameraDistance: spatialData?.cameraDistance,
|
|
175
137
|
screenPos: spatialData?.screenPos,
|
|
176
138
|
};
|
|
177
|
-
console.log("📤 [SDK → SERVER] Sending position update:", {
|
|
178
|
-
participantId: this.localParticipant.participantId,
|
|
179
|
-
position,
|
|
180
|
-
direction,
|
|
181
|
-
cameraDistance: spatialData?.cameraDistance,
|
|
182
|
-
screenPos: spatialData?.screenPos,
|
|
183
|
-
});
|
|
184
139
|
this.socket.emit("update-position", updateData);
|
|
185
140
|
}
|
|
186
141
|
}
|
|
@@ -203,25 +158,6 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
203
158
|
}
|
|
204
159
|
listenForEvents() {
|
|
205
160
|
this.socket.on("new-participant", (participantData) => {
|
|
206
|
-
console.log("👋 SDK: new-participant event received:", {
|
|
207
|
-
participantId: participantData.participantId,
|
|
208
|
-
conferenceId: participantData.conferenceId,
|
|
209
|
-
roomId: participantData.roomId,
|
|
210
|
-
position: participantData.position,
|
|
211
|
-
direction: participantData.direction,
|
|
212
|
-
mediaState: participantData.mediaState,
|
|
213
|
-
timestamp: participantData.timestamp,
|
|
214
|
-
bodyHeight: participantData.bodyHeight,
|
|
215
|
-
bodyShape: participantData.bodyShape,
|
|
216
|
-
userName: participantData.userName,
|
|
217
|
-
userEmail: participantData.userEmail,
|
|
218
|
-
});
|
|
219
|
-
console.log("🔍 [SDK] New participant USER DATA:", {
|
|
220
|
-
bodyHeight: participantData.bodyHeight,
|
|
221
|
-
bodyShape: participantData.bodyShape,
|
|
222
|
-
userName: participantData.userName,
|
|
223
|
-
userEmail: participantData.userEmail,
|
|
224
|
-
});
|
|
225
161
|
if (this.room) {
|
|
226
162
|
const newParticipant = {
|
|
227
163
|
participantId: participantData.participantId,
|
|
@@ -239,8 +175,6 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
239
175
|
userEmail: participantData.userEmail,
|
|
240
176
|
};
|
|
241
177
|
this.room.participants.set(participantData.participantId, newParticipant);
|
|
242
|
-
console.log("✅ SDK: New participant added to room, total participants:", this.room.participants.size);
|
|
243
|
-
console.log("✅ SDK: New participant full data:", newParticipant);
|
|
244
178
|
this.emit("new-participant", newParticipant);
|
|
245
179
|
}
|
|
246
180
|
});
|
|
@@ -252,25 +186,8 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
252
186
|
}
|
|
253
187
|
});
|
|
254
188
|
this.socket.on("consumer-created", async (data) => {
|
|
255
|
-
console.log("📥 SDK: consumer-created event received:", {
|
|
256
|
-
consumerId: data.consumerId,
|
|
257
|
-
producerId: data.producerId,
|
|
258
|
-
kind: data.kind,
|
|
259
|
-
participantId: data.participantId,
|
|
260
|
-
conferenceId: data.conferenceId,
|
|
261
|
-
roomId: data.roomId,
|
|
262
|
-
position: data.position,
|
|
263
|
-
direction: data.direction,
|
|
264
|
-
mediaState: data.mediaState,
|
|
265
|
-
timestamp: data.timestamp,
|
|
266
|
-
bodyHeight: data.bodyHeight,
|
|
267
|
-
bodyShape: data.bodyShape,
|
|
268
|
-
userName: data.userName,
|
|
269
|
-
userEmail: data.userEmail,
|
|
270
|
-
});
|
|
271
189
|
const { consumer, track } = await this.mediasoupManager.consume(data);
|
|
272
190
|
this.socket.on("all-participants-update", (payload) => {
|
|
273
|
-
console.log("📦 SDK: all-participants-update event received:", payload);
|
|
274
191
|
if (!this.room) {
|
|
275
192
|
this.room = {
|
|
276
193
|
id: payload.roomId,
|
|
@@ -345,16 +262,11 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
345
262
|
// Resume the consumer to start receiving media (non-blocking)
|
|
346
263
|
this.mediasoupManager
|
|
347
264
|
.resumeConsumer(consumer.id)
|
|
348
|
-
.then(() => {
|
|
349
|
-
|
|
350
|
-
})
|
|
351
|
-
.catch((err) => {
|
|
352
|
-
console.error(`❌ SDK: Failed to resume consumer for ${data.participantId}:`, err);
|
|
353
|
-
});
|
|
265
|
+
.then(() => { })
|
|
266
|
+
.catch((err) => { });
|
|
354
267
|
let participant = this.room?.participants.get(data.participantId);
|
|
355
268
|
// If participant doesn't exist yet, create it with the data from the event
|
|
356
269
|
if (!participant && this.room) {
|
|
357
|
-
console.log("🆕 SDK: Creating participant entry from consumer-created event with user profile data");
|
|
358
270
|
participant = {
|
|
359
271
|
participantId: data.participantId,
|
|
360
272
|
userId: data.participantId.split(":")[0] || data.participantId,
|
|
@@ -372,20 +284,7 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
372
284
|
userEmail: data.userEmail,
|
|
373
285
|
};
|
|
374
286
|
this.room.participants.set(data.participantId, participant);
|
|
375
|
-
console.log("✅ SDK: Created participant WITH user profile data:", {
|
|
376
|
-
participantId: data.participantId,
|
|
377
|
-
bodyHeight: data.bodyHeight,
|
|
378
|
-
bodyShape: data.bodyShape,
|
|
379
|
-
userName: data.userName,
|
|
380
|
-
userEmail: data.userEmail,
|
|
381
|
-
});
|
|
382
287
|
}
|
|
383
|
-
console.log("🔍 SDK: Participant lookup result:", {
|
|
384
|
-
found: !!participant,
|
|
385
|
-
participantId: data.participantId,
|
|
386
|
-
totalParticipants: this.room?.participants.size,
|
|
387
|
-
participantIds: Array.from(this.room?.participants.keys() || []),
|
|
388
|
-
});
|
|
389
288
|
if (participant) {
|
|
390
289
|
// Update participant data with latest from server
|
|
391
290
|
participant.position = data.position;
|
|
@@ -410,72 +309,29 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
410
309
|
// CRITICAL: Do NOT setup spatial audio for local participant (yourself)
|
|
411
310
|
// This prevents hearing your own microphone (loopback)
|
|
412
311
|
const isLocalParticipant = participant.participantId === this.localParticipant?.participantId;
|
|
413
|
-
console.log("🔍 SDK: Checking if local participant:", {
|
|
414
|
-
participantId: participant.participantId,
|
|
415
|
-
localParticipantId: this.localParticipant?.participantId,
|
|
416
|
-
isLocalParticipant,
|
|
417
|
-
producerId: data.producerId,
|
|
418
|
-
trackKind: track.kind
|
|
419
|
-
});
|
|
420
312
|
if (isLocalParticipant) {
|
|
421
|
-
console.warn("🔇 SDK: SKIPPING spatial audio for LOCAL participant (yourself) - prevents loopback", participant.participantId);
|
|
422
313
|
// Do NOT connect this audio to Web Audio API
|
|
423
314
|
return; // Exit early to prevent any audio processing
|
|
424
315
|
}
|
|
425
316
|
else {
|
|
426
|
-
console.log("🎧 SDK: Setting up spatial audio for REMOTE participant", participant.participantId);
|
|
427
317
|
// Setup spatial audio with full 3D positioning
|
|
428
318
|
await this.spatialAudioManager.setupSpatialAudioForParticipant(participant.participantId, track, false // Enable spatial audio
|
|
429
319
|
);
|
|
430
320
|
// Update spatial audio position for this participant
|
|
431
321
|
this.spatialAudioManager.updateSpatialAudio(participant.participantId, data.position);
|
|
432
|
-
console.log("📍 SDK: Spatial audio position set:", data.position);
|
|
433
322
|
}
|
|
434
323
|
}
|
|
435
324
|
else if (track.kind === "video") {
|
|
436
325
|
participant.videoTrack = track;
|
|
437
|
-
console.log("📹 SDK: Video track set for", participant.participantId);
|
|
438
326
|
}
|
|
439
|
-
console.log("✅ SDK: Emitting consumer-created event with participant:", {
|
|
440
|
-
participantId: participant.participantId,
|
|
441
|
-
hasPosition: !!participant.position,
|
|
442
|
-
position: participant.position,
|
|
443
|
-
trackKind: track.kind,
|
|
444
|
-
conferenceId: data.conferenceId,
|
|
445
|
-
roomId: data.roomId,
|
|
446
|
-
});
|
|
447
327
|
this.emit("consumer-created", {
|
|
448
328
|
participant,
|
|
449
329
|
track,
|
|
450
330
|
consumer,
|
|
451
331
|
});
|
|
452
332
|
}
|
|
453
|
-
else {
|
|
454
|
-
console.error("❌ SDK: Participant not found for consumer-created event:", data.participantId);
|
|
455
|
-
}
|
|
456
333
|
});
|
|
457
334
|
this.socket.on("participant-position-updated", (data) => {
|
|
458
|
-
console.log("📍 SDK: participant-position-updated event received:", {
|
|
459
|
-
participantId: data.participantId,
|
|
460
|
-
conferenceId: data.conferenceId,
|
|
461
|
-
roomId: data.roomId,
|
|
462
|
-
position: data.position,
|
|
463
|
-
direction: data.direction,
|
|
464
|
-
mediaState: data.mediaState,
|
|
465
|
-
consumerIds: data.consumerIds,
|
|
466
|
-
timestamp: data.timestamp,
|
|
467
|
-
bodyHeight: data.bodyHeight,
|
|
468
|
-
bodyShape: data.bodyShape,
|
|
469
|
-
userName: data.userName,
|
|
470
|
-
userEmail: data.userEmail,
|
|
471
|
-
});
|
|
472
|
-
console.log("🔍 [SDK] Position update USER DATA:", {
|
|
473
|
-
participantId: data.participantId,
|
|
474
|
-
bodyHeight: data.bodyHeight,
|
|
475
|
-
bodyShape: data.bodyShape,
|
|
476
|
-
userName: data.userName,
|
|
477
|
-
userEmail: data.userEmail,
|
|
478
|
-
});
|
|
479
335
|
const participant = this.room?.participants.get(data.participantId);
|
|
480
336
|
if (participant) {
|
|
481
337
|
participant.position = data.position;
|
|
@@ -492,38 +348,19 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
492
348
|
participant.userEmail = data.userEmail;
|
|
493
349
|
// Update spatial audio with BOTH position AND direction from socket
|
|
494
350
|
this.spatialAudioManager.updateSpatialAudio(data.participantId, data.position, data.direction);
|
|
495
|
-
console.log("✅ SDK: Updated participant position and spatial audio with direction");
|
|
496
351
|
this.emit("participant-position-updated", participant);
|
|
497
352
|
}
|
|
498
|
-
else {
|
|
499
|
-
console.warn("⚠️ SDK: Participant not found for position update:", data.participantId);
|
|
500
|
-
console.warn("⚠️ SDK: Available participants in room:", Array.from(this.room?.participants.keys() || []));
|
|
501
|
-
}
|
|
502
353
|
});
|
|
503
354
|
this.socket.on("participant-media-state-updated", (data) => {
|
|
504
|
-
console.log("🔄 SDK: participant-media-state-updated event received:", {
|
|
505
|
-
participantId: data.participantId,
|
|
506
|
-
conferenceId: data.conferenceId,
|
|
507
|
-
roomId: data.roomId,
|
|
508
|
-
mediaState: data.mediaState,
|
|
509
|
-
position: data.position,
|
|
510
|
-
direction: data.direction,
|
|
511
|
-
timestamp: data.timestamp,
|
|
512
|
-
});
|
|
513
355
|
const participant = this.room?.participants.get(data.participantId);
|
|
514
356
|
if (participant) {
|
|
515
357
|
participant.mediaState = data.mediaState;
|
|
516
358
|
participant.position = data.position;
|
|
517
359
|
participant.direction = data.direction;
|
|
518
|
-
console.log("✅ SDK: Updated participant media state");
|
|
519
360
|
this.emit("participant-media-state-updated", participant);
|
|
520
361
|
}
|
|
521
|
-
else {
|
|
522
|
-
console.warn("⚠️ SDK: Participant not found for media state update:", data.participantId);
|
|
523
|
-
}
|
|
524
362
|
});
|
|
525
363
|
this.socket.on("error", (error) => {
|
|
526
|
-
console.error("Socket error:", error);
|
|
527
364
|
this.emit("error", error);
|
|
528
365
|
});
|
|
529
366
|
this.socket.on("disconnect", () => {
|
package/package.json
CHANGED