@newgameplusinc/odyssey-audio-video-sdk-dev 1.0.35 → 1.0.36
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/index.js +106 -80
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -157,6 +157,86 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
157
157
|
this.spatialAudioManager.setListenerFromLSD(listenerPos, cameraPos, lookAtPos);
|
|
158
158
|
}
|
|
159
159
|
listenForEvents() {
|
|
160
|
+
// CRITICAL: Register all-participants-update listener at initialization
|
|
161
|
+
// This ensures channel data is available when consumers are created
|
|
162
|
+
this.socket.on("all-participants-update", (payload) => {
|
|
163
|
+
if (!this.room) {
|
|
164
|
+
this.room = {
|
|
165
|
+
id: payload.roomId,
|
|
166
|
+
participants: new Map(),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const activeParticipantIds = new Set();
|
|
170
|
+
for (const snapshot of payload.participants) {
|
|
171
|
+
activeParticipantIds.add(snapshot.participantId);
|
|
172
|
+
let participant = this.room?.participants.get(snapshot.participantId);
|
|
173
|
+
const normalizedPosition = {
|
|
174
|
+
x: snapshot.position?.x ?? 0,
|
|
175
|
+
y: snapshot.position?.y ?? 0,
|
|
176
|
+
z: snapshot.position?.z ?? 0,
|
|
177
|
+
};
|
|
178
|
+
const normalizedDirection = {
|
|
179
|
+
x: snapshot.direction?.x ?? 0,
|
|
180
|
+
y: snapshot.direction?.y ?? 0,
|
|
181
|
+
z: snapshot.direction?.z ?? 1,
|
|
182
|
+
};
|
|
183
|
+
const normalizedMediaState = {
|
|
184
|
+
audio: snapshot.mediaState?.audio ?? false,
|
|
185
|
+
video: snapshot.mediaState?.video ?? false,
|
|
186
|
+
sharescreen: snapshot.mediaState?.sharescreen ?? false,
|
|
187
|
+
};
|
|
188
|
+
if (!participant) {
|
|
189
|
+
participant = {
|
|
190
|
+
participantId: snapshot.participantId,
|
|
191
|
+
userId: snapshot.userId,
|
|
192
|
+
deviceId: snapshot.deviceId,
|
|
193
|
+
isLocal: this.localParticipant?.participantId ===
|
|
194
|
+
snapshot.participantId,
|
|
195
|
+
audioTrack: undefined,
|
|
196
|
+
videoTrack: undefined,
|
|
197
|
+
producers: new Map(),
|
|
198
|
+
consumers: new Map(),
|
|
199
|
+
position: normalizedPosition,
|
|
200
|
+
direction: normalizedDirection,
|
|
201
|
+
mediaState: normalizedMediaState,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
participant.userId = snapshot.userId;
|
|
206
|
+
participant.deviceId = snapshot.deviceId;
|
|
207
|
+
participant.position = normalizedPosition;
|
|
208
|
+
participant.direction = normalizedDirection;
|
|
209
|
+
participant.mediaState = normalizedMediaState;
|
|
210
|
+
}
|
|
211
|
+
participant.bodyHeight = snapshot.bodyHeight;
|
|
212
|
+
participant.bodyShape = snapshot.bodyShape;
|
|
213
|
+
participant.userName = snapshot.userName;
|
|
214
|
+
participant.userEmail = snapshot.userEmail;
|
|
215
|
+
participant.isLocal =
|
|
216
|
+
this.localParticipant?.participantId === snapshot.participantId;
|
|
217
|
+
// CRITICAL: Store channel data for huddle detection
|
|
218
|
+
participant.currentChannel = snapshot.currentChannel || "spatial";
|
|
219
|
+
this.room?.participants.set(snapshot.participantId, participant);
|
|
220
|
+
if (participant.isLocal) {
|
|
221
|
+
this.localParticipant = participant;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (this.room) {
|
|
225
|
+
for (const existingId of Array.from(this.room.participants.keys())) {
|
|
226
|
+
if (!activeParticipantIds.has(existingId)) {
|
|
227
|
+
this.room.participants.delete(existingId);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const normalizedParticipants = this.room
|
|
232
|
+
? Array.from(this.room.participants.values())
|
|
233
|
+
: [];
|
|
234
|
+
this.emit("all-participants-update", normalizedParticipants);
|
|
235
|
+
// CRITICAL: After receiving channel data, update ALL mute states
|
|
236
|
+
// This ensures existing audio consumers get the correct mute state
|
|
237
|
+
console.log(`📊 [all-participants-update] Received ${normalizedParticipants.length} participants, updating mute states...`);
|
|
238
|
+
this.updateAllParticipantsMuteState();
|
|
239
|
+
});
|
|
160
240
|
this.socket.on("new-participant", (participantData) => {
|
|
161
241
|
if (this.room) {
|
|
162
242
|
const newParticipant = {
|
|
@@ -187,80 +267,6 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
187
267
|
});
|
|
188
268
|
this.socket.on("consumer-created", async (data) => {
|
|
189
269
|
const { consumer, track } = await this.mediasoupManager.consume(data);
|
|
190
|
-
this.socket.on("all-participants-update", (payload) => {
|
|
191
|
-
if (!this.room) {
|
|
192
|
-
this.room = {
|
|
193
|
-
id: payload.roomId,
|
|
194
|
-
participants: new Map(),
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
const activeParticipantIds = new Set();
|
|
198
|
-
for (const snapshot of payload.participants) {
|
|
199
|
-
activeParticipantIds.add(snapshot.participantId);
|
|
200
|
-
let participant = this.room?.participants.get(snapshot.participantId);
|
|
201
|
-
const normalizedPosition = {
|
|
202
|
-
x: snapshot.position?.x ?? 0,
|
|
203
|
-
y: snapshot.position?.y ?? 0,
|
|
204
|
-
z: snapshot.position?.z ?? 0,
|
|
205
|
-
};
|
|
206
|
-
const normalizedDirection = {
|
|
207
|
-
x: snapshot.direction?.x ?? 0,
|
|
208
|
-
y: snapshot.direction?.y ?? 0,
|
|
209
|
-
z: snapshot.direction?.z ?? 1,
|
|
210
|
-
};
|
|
211
|
-
const normalizedMediaState = {
|
|
212
|
-
audio: snapshot.mediaState?.audio ?? false,
|
|
213
|
-
video: snapshot.mediaState?.video ?? false,
|
|
214
|
-
sharescreen: snapshot.mediaState?.sharescreen ?? false,
|
|
215
|
-
};
|
|
216
|
-
if (!participant) {
|
|
217
|
-
participant = {
|
|
218
|
-
participantId: snapshot.participantId,
|
|
219
|
-
userId: snapshot.userId,
|
|
220
|
-
deviceId: snapshot.deviceId,
|
|
221
|
-
isLocal: this.localParticipant?.participantId ===
|
|
222
|
-
snapshot.participantId,
|
|
223
|
-
audioTrack: undefined,
|
|
224
|
-
videoTrack: undefined,
|
|
225
|
-
producers: new Map(),
|
|
226
|
-
consumers: new Map(),
|
|
227
|
-
position: normalizedPosition,
|
|
228
|
-
direction: normalizedDirection,
|
|
229
|
-
mediaState: normalizedMediaState,
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
participant.userId = snapshot.userId;
|
|
234
|
-
participant.deviceId = snapshot.deviceId;
|
|
235
|
-
participant.position = normalizedPosition;
|
|
236
|
-
participant.direction = normalizedDirection;
|
|
237
|
-
participant.mediaState = normalizedMediaState;
|
|
238
|
-
}
|
|
239
|
-
participant.bodyHeight = snapshot.bodyHeight;
|
|
240
|
-
participant.bodyShape = snapshot.bodyShape;
|
|
241
|
-
participant.userName = snapshot.userName;
|
|
242
|
-
participant.userEmail = snapshot.userEmail;
|
|
243
|
-
participant.isLocal =
|
|
244
|
-
this.localParticipant?.participantId === snapshot.participantId;
|
|
245
|
-
// CRITICAL: Store channel data for huddle detection
|
|
246
|
-
participant.currentChannel = snapshot.currentChannel || "spatial";
|
|
247
|
-
this.room?.participants.set(snapshot.participantId, participant);
|
|
248
|
-
if (participant.isLocal) {
|
|
249
|
-
this.localParticipant = participant;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
if (this.room) {
|
|
253
|
-
for (const existingId of Array.from(this.room.participants.keys())) {
|
|
254
|
-
if (!activeParticipantIds.has(existingId)) {
|
|
255
|
-
this.room.participants.delete(existingId);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
const normalizedParticipants = this.room
|
|
260
|
-
? Array.from(this.room.participants.values())
|
|
261
|
-
: [];
|
|
262
|
-
this.emit("all-participants-update", normalizedParticipants);
|
|
263
|
-
});
|
|
264
270
|
// DON'T resume yet! Resume AFTER setting up the audio pipeline
|
|
265
271
|
let participant = this.room?.participants.get(data.participantId);
|
|
266
272
|
// If participant doesn't exist yet, create it with the data from the event
|
|
@@ -311,18 +317,23 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
311
317
|
const isLocalParticipant = participant.participantId === this.localParticipant?.participantId;
|
|
312
318
|
if (isLocalParticipant) {
|
|
313
319
|
// Do NOT connect this audio to Web Audio API
|
|
320
|
+
console.log(`🚫 Skipping local participant audio setup (prevent loopback)`);
|
|
314
321
|
return; // Exit early to prevent any audio processing
|
|
315
322
|
}
|
|
316
323
|
else {
|
|
317
324
|
// Check if participant is in huddle - if so, skip spatial audio
|
|
318
325
|
const participantChannel = participant.currentChannel || "spatial";
|
|
319
|
-
const
|
|
326
|
+
const myChannel = this.localParticipant?.currentChannel || "spatial";
|
|
327
|
+
const isInHuddle = participantChannel !== "spatial";
|
|
328
|
+
console.log(`🎧 Setting up audio for ${participant.participantId?.substring(0, 8)}`);
|
|
329
|
+
console.log(` My channel: ${myChannel}`);
|
|
330
|
+
console.log(` Their channel: ${participantChannel}`);
|
|
320
331
|
// Setup spatial audio with full 3D positioning (disabled for huddle users)
|
|
321
332
|
await this.spatialAudioManager.setupSpatialAudioForParticipant(participant.participantId, track, isInHuddle // Disable spatial audio for huddle users
|
|
322
333
|
);
|
|
323
334
|
// CRITICAL: Mute if not in same channel
|
|
324
|
-
const myChannel = this.localParticipant?.currentChannel || "spatial";
|
|
325
335
|
const shouldMute = myChannel !== participantChannel;
|
|
336
|
+
console.log(` Should mute: ${shouldMute} (channels ${myChannel === participantChannel ? 'MATCH' : 'DIFFER'})`);
|
|
326
337
|
this.spatialAudioManager.setParticipantMuted(participant.participantId, shouldMute);
|
|
327
338
|
// Only update spatial audio position if NOT in huddle
|
|
328
339
|
if (!isInHuddle) {
|
|
@@ -420,11 +431,12 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
420
431
|
this.emit("huddle-ended", data);
|
|
421
432
|
});
|
|
422
433
|
this.socket.on("participant-channel-changed", (data) => {
|
|
434
|
+
console.log(`📡 participant-channel-changed: ${data.participantId?.substring(0, 8)} → ${data.channelId}`);
|
|
423
435
|
const participant = this.room?.participants.get(data.participantId);
|
|
424
436
|
if (participant) {
|
|
425
437
|
participant.currentChannel = data.channelId;
|
|
426
438
|
// Toggle spatial audio based on channel
|
|
427
|
-
const isInHuddle = data.channelId
|
|
439
|
+
const isInHuddle = data.channelId !== "spatial";
|
|
428
440
|
// Update spatial audio bypass state for this participant
|
|
429
441
|
if (participant.audioTrack) {
|
|
430
442
|
this.spatialAudioManager.setupSpatialAudioForParticipant(participant.participantId, participant.audioTrack, isInHuddle // Disable spatial for huddle, enable for spatial
|
|
@@ -433,18 +445,19 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
433
445
|
});
|
|
434
446
|
}
|
|
435
447
|
// CRITICAL: Mute/unmute based on channel matching
|
|
436
|
-
// If I'm in huddle and they're in spatial -> MUTE them
|
|
437
|
-
// If I'm in spatial and they're in huddle -> MUTE them
|
|
438
|
-
// If we're in the same channel -> UNMUTE them
|
|
439
448
|
const myChannel = this.localParticipant?.currentChannel || "spatial";
|
|
440
449
|
const theirChannel = data.channelId || "spatial";
|
|
441
450
|
const shouldMute = myChannel !== theirChannel;
|
|
451
|
+
console.log(` My channel: ${myChannel}, Their channel: ${theirChannel}`);
|
|
452
|
+
console.log(` ${shouldMute ? '🔇 MUTING' : '🔊 UNMUTING'} ${participant.participantId?.substring(0, 8)}`);
|
|
442
453
|
this.spatialAudioManager.setParticipantMuted(participant.participantId, shouldMute);
|
|
443
454
|
}
|
|
444
455
|
// Update local participant if it's them
|
|
445
456
|
if (this.localParticipant?.participantId === data.participantId && this.localParticipant !== null) {
|
|
457
|
+
console.log(` This is ME changing channel!`);
|
|
446
458
|
this.localParticipant.currentChannel = data.channelId;
|
|
447
459
|
// When LOCAL user changes channel, update ALL other participants' mute state
|
|
460
|
+
console.log(` 🔄 Updating ALL participants mute state`);
|
|
448
461
|
this.updateAllParticipantsMuteState();
|
|
449
462
|
}
|
|
450
463
|
this.emit("participant-channel-changed", data);
|
|
@@ -508,6 +521,7 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
508
521
|
* Join huddle (anyone can join directly without invite)
|
|
509
522
|
*/
|
|
510
523
|
async joinHuddle() {
|
|
524
|
+
console.log(`🎧 [joinHuddle] Joining group huddle...`);
|
|
511
525
|
if (!this.localParticipant || !this.room) {
|
|
512
526
|
return { success: false, error: "Not in a room" };
|
|
513
527
|
}
|
|
@@ -517,10 +531,14 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
517
531
|
roomId: this.room.id,
|
|
518
532
|
}, (response) => {
|
|
519
533
|
if (response.success && this.localParticipant) {
|
|
534
|
+
console.log(`✅ [joinHuddle] Success! New channel: ${response.channelId}`);
|
|
520
535
|
this.localParticipant.currentChannel = response.channelId;
|
|
521
536
|
// CRITICAL: Update mute state for all participants when joining huddle
|
|
522
537
|
this.updateAllParticipantsMuteState();
|
|
523
538
|
}
|
|
539
|
+
else {
|
|
540
|
+
console.error(`❌ [joinHuddle] Failed:`, response.error);
|
|
541
|
+
}
|
|
524
542
|
resolve(response);
|
|
525
543
|
});
|
|
526
544
|
});
|
|
@@ -529,6 +547,7 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
529
547
|
* Leave current huddle and return to spatial audio
|
|
530
548
|
*/
|
|
531
549
|
async leaveHuddle() {
|
|
550
|
+
console.log(`🚪 [leaveHuddle] Leaving huddle...`);
|
|
532
551
|
if (!this.localParticipant || !this.room) {
|
|
533
552
|
return { success: false, error: "Not in a room" };
|
|
534
553
|
}
|
|
@@ -538,10 +557,14 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
538
557
|
roomId: this.room.id,
|
|
539
558
|
}, (response) => {
|
|
540
559
|
if (response.success && this.localParticipant) {
|
|
560
|
+
console.log(`✅ [leaveHuddle] Success! New channel: ${response.channelId}`);
|
|
541
561
|
this.localParticipant.currentChannel = response.channelId;
|
|
542
562
|
// CRITICAL: Update mute state for all participants when leaving huddle
|
|
543
563
|
this.updateAllParticipantsMuteState();
|
|
544
564
|
}
|
|
565
|
+
else {
|
|
566
|
+
console.error(`❌ [leaveHuddle] Failed:`, response.error);
|
|
567
|
+
}
|
|
545
568
|
resolve(response);
|
|
546
569
|
});
|
|
547
570
|
});
|
|
@@ -567,6 +590,7 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
567
590
|
if (!this.localParticipant || !this.room)
|
|
568
591
|
return;
|
|
569
592
|
const myChannel = this.localParticipant.currentChannel || "spatial";
|
|
593
|
+
console.log(`🔄 [updateAllParticipantsMuteState] My channel: ${myChannel}`);
|
|
570
594
|
this.room.participants.forEach((participant) => {
|
|
571
595
|
// Skip local participant (never hear yourself)
|
|
572
596
|
if (participant.participantId === this.localParticipant?.participantId) {
|
|
@@ -574,8 +598,10 @@ class OdysseySpatialComms extends EventManager_1.EventManager {
|
|
|
574
598
|
}
|
|
575
599
|
const theirChannel = participant.currentChannel || "spatial";
|
|
576
600
|
const shouldMute = myChannel !== theirChannel;
|
|
601
|
+
console.log(` ${participant.participantId?.substring(0, 8)}: channel=${theirChannel}, ${shouldMute ? '🔇 MUTE' : '🔊 UNMUTE'}`);
|
|
577
602
|
this.spatialAudioManager.setParticipantMuted(participant.participantId, shouldMute);
|
|
578
603
|
});
|
|
604
|
+
console.log(`✅ [updateAllParticipantsMuteState] Complete`);
|
|
579
605
|
}
|
|
580
606
|
async muteParticipant(participantId) {
|
|
581
607
|
if (!this.localParticipant || !this.room) {
|
package/package.json
CHANGED