@newgameplusinc/odyssey-audio-video-sdk-dev 1.0.257 → 1.0.259
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 +45 -1
- package/dist/audio/AudioNodeFactory.d.ts +130 -0
- package/dist/audio/AudioNodeFactory.js +158 -0
- package/dist/audio/AudioPipeline.d.ts +89 -0
- package/dist/audio/AudioPipeline.js +138 -0
- package/dist/{MLNoiseSuppressor.d.ts → audio/MLNoiseSuppressor.d.ts} +7 -7
- package/dist/{MLNoiseSuppressor.js → audio/MLNoiseSuppressor.js} +13 -41
- package/dist/audio/index.d.ts +6 -0
- package/dist/audio/index.js +22 -0
- package/dist/channels/huddle/HuddleChannel.d.ts +87 -0
- package/dist/channels/huddle/HuddleChannel.js +152 -0
- package/dist/channels/huddle/HuddleTypes.d.ts +85 -0
- package/dist/channels/huddle/HuddleTypes.js +25 -0
- package/dist/channels/huddle/index.d.ts +5 -0
- package/dist/channels/huddle/index.js +21 -0
- package/dist/channels/index.d.ts +5 -0
- package/dist/channels/index.js +21 -0
- package/dist/channels/spatial/SpatialAudioChannel.d.ts +144 -0
- package/dist/channels/spatial/SpatialAudioChannel.js +455 -0
- package/dist/channels/spatial/SpatialAudioTypes.d.ts +85 -0
- package/dist/channels/spatial/SpatialAudioTypes.js +42 -0
- package/dist/channels/spatial/index.d.ts +5 -0
- package/dist/channels/spatial/index.js +21 -0
- package/dist/{EventManager.d.ts → core/EventManager.d.ts} +4 -2
- package/dist/{EventManager.js → core/EventManager.js} +5 -3
- package/dist/{MediasoupManager.d.ts → core/MediasoupManager.d.ts} +10 -4
- package/dist/{MediasoupManager.js → core/MediasoupManager.js} +31 -42
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +21 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +30 -4
- package/dist/sdk/index.d.ts +36 -0
- package/dist/sdk/index.js +121 -0
- package/dist/types/events.d.ts +154 -0
- package/dist/{types.js → types/events.js} +3 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.js +23 -0
- package/dist/types/participant.d.ts +65 -0
- package/dist/types/participant.js +5 -0
- package/dist/types/position.d.ts +47 -0
- package/dist/types/position.js +9 -0
- package/dist/types/room.d.ts +82 -0
- package/dist/types/room.js +5 -0
- package/dist/utils/audio/clarity-score.d.ts +33 -0
- package/dist/utils/audio/clarity-score.js +81 -0
- package/dist/utils/audio/index.d.ts +5 -0
- package/dist/utils/audio/index.js +21 -0
- package/dist/utils/audio/voice-filter.d.ts +30 -0
- package/dist/utils/audio/voice-filter.js +70 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.js +23 -0
- package/dist/utils/position/coordinates.d.ts +37 -0
- package/dist/utils/position/coordinates.js +61 -0
- package/dist/utils/position/index.d.ts +6 -0
- package/dist/utils/position/index.js +22 -0
- package/dist/utils/position/normalize.d.ts +37 -0
- package/dist/utils/position/normalize.js +78 -0
- package/dist/utils/position/snap.d.ts +51 -0
- package/dist/utils/position/snap.js +81 -0
- package/dist/utils/smoothing/gain-smoothing.d.ts +45 -0
- package/dist/utils/smoothing/gain-smoothing.js +77 -0
- package/dist/utils/smoothing/index.d.ts +5 -0
- package/dist/utils/smoothing/index.js +21 -0
- package/dist/utils/smoothing/pan-smoothing.d.ts +43 -0
- package/dist/utils/smoothing/pan-smoothing.js +85 -0
- package/dist/utils/spatial/angle-calc.d.ts +24 -0
- package/dist/utils/spatial/angle-calc.js +69 -0
- package/dist/utils/spatial/distance-calc.d.ts +33 -0
- package/dist/utils/spatial/distance-calc.js +48 -0
- package/dist/utils/spatial/gain-calc.d.ts +37 -0
- package/dist/utils/spatial/gain-calc.js +52 -0
- package/dist/utils/spatial/head-position.d.ts +32 -0
- package/dist/utils/spatial/head-position.js +76 -0
- package/dist/utils/spatial/index.d.ts +9 -0
- package/dist/utils/spatial/index.js +25 -0
- package/dist/utils/spatial/listener-calc.d.ts +28 -0
- package/dist/utils/spatial/listener-calc.js +74 -0
- package/dist/utils/spatial/pan-calc.d.ts +48 -0
- package/dist/utils/spatial/pan-calc.js +80 -0
- package/package.json +1 -1
- package/dist/SpatialAudioManager.d.ts +0 -272
- package/dist/SpatialAudioManager.js +0 -1537
- package/dist/types.d.ts +0 -73
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* ML Noise Suppressor - TensorFlow.js-based Real-Time Audio Enhancement
|
|
4
|
-
*
|
|
4
|
+
*
|
|
5
|
+
* Integrates trained ML model for noise suppression
|
|
6
|
+
* Falls back to traditional DSP if ML is unavailable
|
|
5
7
|
*/
|
|
6
8
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
9
|
if (k2 === undefined) k2 = k;
|
|
@@ -45,9 +47,9 @@ class MLNoiseSuppressor {
|
|
|
45
47
|
this.config = null;
|
|
46
48
|
this.normStats = null;
|
|
47
49
|
this.isInitialized = false;
|
|
48
|
-
// Temporal smoothing state
|
|
50
|
+
// Temporal smoothing state
|
|
49
51
|
this.prevMask = null;
|
|
50
|
-
this.SMOOTHING_ALPHA = 0.85;
|
|
52
|
+
this.SMOOTHING_ALPHA = 0.85;
|
|
51
53
|
// Voice frequency preservation
|
|
52
54
|
this.VOICE_FUNDAMENTAL_MIN = 80;
|
|
53
55
|
this.VOICE_FUNDAMENTAL_MAX = 500;
|
|
@@ -57,19 +59,14 @@ class MLNoiseSuppressor {
|
|
|
57
59
|
* @param modelUrl Path to model.json file
|
|
58
60
|
*/
|
|
59
61
|
async initialize(modelUrl) {
|
|
60
|
-
//console.log('🤖 [ML] Initializing ML Noise Suppressor...');
|
|
61
62
|
try {
|
|
62
|
-
// Set TensorFlow.js backend
|
|
63
|
+
// Set TensorFlow.js backend
|
|
63
64
|
await tf.setBackend('webgl');
|
|
64
65
|
await tf.ready();
|
|
65
|
-
//
|
|
66
|
-
// The model's JSON file is missing the input layer configuration, which causes
|
|
67
|
-
// TensorFlow.js to throw a ValueError. This workaround intercepts the loading
|
|
68
|
-
// process and injects the correct input shape before the model is fully parsed.
|
|
66
|
+
// Load model with input shape fix
|
|
69
67
|
const modelJson = await (await fetch(modelUrl)).json();
|
|
70
68
|
if (modelJson.modelTopology?.config?.layers?.[0]?.config
|
|
71
69
|
?.batch_input_shape === undefined) {
|
|
72
|
-
// Model JSON is missing inputShape - apply fix
|
|
73
70
|
modelJson.modelTopology.config.layers[0].config.batch_input_shape = [
|
|
74
71
|
null,
|
|
75
72
|
257,
|
|
@@ -88,7 +85,6 @@ class MLNoiseSuppressor {
|
|
|
88
85
|
this.normStats = await normResponse.json();
|
|
89
86
|
}
|
|
90
87
|
catch (e) {
|
|
91
|
-
// No normalization stats found, use defaults
|
|
92
88
|
this.normStats = { mean: 0, std: 1 };
|
|
93
89
|
}
|
|
94
90
|
this.isInitialized = true;
|
|
@@ -99,43 +95,31 @@ class MLNoiseSuppressor {
|
|
|
99
95
|
}
|
|
100
96
|
/**
|
|
101
97
|
* Process audio buffer through ML model
|
|
102
|
-
* @param inputBuffer Audio samples to process
|
|
103
|
-
* @returns Denoised audio samples
|
|
104
98
|
*/
|
|
105
99
|
async processAudio(inputBuffer) {
|
|
106
100
|
if (!this.isInitialized || !this.model || !this.config || !this.normStats) {
|
|
107
|
-
// Not initialized, return original audio
|
|
108
101
|
return inputBuffer;
|
|
109
102
|
}
|
|
110
103
|
try {
|
|
111
104
|
return await tf.tidy(() => {
|
|
112
|
-
// 1. Extract features (simplified mel-spectrogram approximation)
|
|
113
105
|
const features = this.extractFeatures(inputBuffer);
|
|
114
|
-
// 2. Normalize features
|
|
115
106
|
const normalizedTensor = tf.tensor2d(features);
|
|
116
107
|
const normalized = normalizedTensor
|
|
117
108
|
.sub(this.normStats.mean)
|
|
118
109
|
.div(Math.max(this.normStats.std, 1e-8));
|
|
119
|
-
// 3. Create sequences for LSTM
|
|
120
110
|
const sequences = this.createSequences(normalized);
|
|
121
|
-
// 4. Run ML inference to get noise suppression mask
|
|
122
111
|
const maskTensor = this.model.predict(sequences);
|
|
123
|
-
// 5. Extract mask values
|
|
124
112
|
const maskArray = maskTensor.dataSync();
|
|
125
|
-
// 6. Apply temporal smoothing (critical for quality!)
|
|
126
113
|
const smoothedMask = this.applyTemporalSmoothing(Array.from(maskArray));
|
|
127
|
-
|
|
128
|
-
const enhanced = this.applyMaskToAudio(inputBuffer, smoothedMask);
|
|
129
|
-
return enhanced;
|
|
114
|
+
return this.applyMaskToAudio(inputBuffer, smoothedMask);
|
|
130
115
|
});
|
|
131
116
|
}
|
|
132
117
|
catch (error) {
|
|
133
|
-
// Return original audio on error (graceful degradation)
|
|
134
118
|
return inputBuffer;
|
|
135
119
|
}
|
|
136
120
|
}
|
|
137
121
|
/**
|
|
138
|
-
* Extract audio features
|
|
122
|
+
* Extract audio features
|
|
139
123
|
*/
|
|
140
124
|
extractFeatures(audio) {
|
|
141
125
|
if (!this.config)
|
|
@@ -148,18 +132,15 @@ class MLNoiseSuppressor {
|
|
|
148
132
|
for (let i = 0; i < numFrames; i++) {
|
|
149
133
|
const start = i * hopLength;
|
|
150
134
|
const frame = audio.slice(start, Math.min(start + frameSize, audio.length));
|
|
151
|
-
// Compute simplified mel bins
|
|
152
135
|
const melBins = [];
|
|
153
136
|
for (let j = 0; j < nMels; j++) {
|
|
154
137
|
const binStart = Math.floor((j / nMels) * frame.length);
|
|
155
138
|
const binEnd = Math.floor(((j + 1) / nMels) * frame.length);
|
|
156
|
-
// Compute energy in this bin
|
|
157
139
|
let energy = 0;
|
|
158
140
|
for (let k = binStart; k < binEnd; k++) {
|
|
159
141
|
energy += frame[k] * frame[k];
|
|
160
142
|
}
|
|
161
143
|
energy = Math.sqrt(energy / (binEnd - binStart));
|
|
162
|
-
// Convert to log scale (dB-like)
|
|
163
144
|
const logEnergy = Math.log10(energy + 1e-10) * 10;
|
|
164
145
|
melBins.push(logEnergy);
|
|
165
146
|
}
|
|
@@ -178,51 +159,42 @@ class MLNoiseSuppressor {
|
|
|
178
159
|
const numFrames = shape[0];
|
|
179
160
|
const numFeatures = shape[1];
|
|
180
161
|
if (numFrames < seqLength) {
|
|
181
|
-
// Pad if needed
|
|
182
162
|
const padding = tf.zeros([seqLength - numFrames, numFeatures]);
|
|
183
163
|
const padded = tf.concat([featureTensor, padding], 0);
|
|
184
164
|
return padded.expandDims(0);
|
|
185
165
|
}
|
|
186
|
-
// Take the last seqLength frames
|
|
187
166
|
const sequence = featureTensor.slice([numFrames - seqLength, 0], [seqLength, numFeatures]);
|
|
188
167
|
return sequence.expandDims(0);
|
|
189
168
|
}
|
|
190
169
|
/**
|
|
191
|
-
* Apply temporal smoothing to mask
|
|
170
|
+
* Apply temporal smoothing to mask
|
|
192
171
|
*/
|
|
193
172
|
applyTemporalSmoothing(currentMask) {
|
|
194
173
|
const smoothed = new Float32Array(currentMask.length);
|
|
195
174
|
if (!this.prevMask || this.prevMask.length !== currentMask.length) {
|
|
196
|
-
// First frame - no smoothing
|
|
197
175
|
this.prevMask = new Float32Array(currentMask);
|
|
198
176
|
return this.prevMask;
|
|
199
177
|
}
|
|
200
|
-
// Exponential moving average
|
|
201
178
|
for (let i = 0; i < currentMask.length; i++) {
|
|
202
179
|
smoothed[i] =
|
|
203
180
|
this.SMOOTHING_ALPHA * currentMask[i] +
|
|
204
181
|
(1 - this.SMOOTHING_ALPHA) * this.prevMask[i];
|
|
205
|
-
// Clamp to valid range [0.05, 1.0]
|
|
206
|
-
// Never completely mute (min 5% to preserve voice quality)
|
|
207
182
|
smoothed[i] = Math.max(0.05, Math.min(1.0, smoothed[i]));
|
|
208
183
|
}
|
|
209
184
|
this.prevMask = smoothed;
|
|
210
185
|
return smoothed;
|
|
211
186
|
}
|
|
212
187
|
/**
|
|
213
|
-
* Apply noise suppression mask to audio
|
|
188
|
+
* Apply noise suppression mask to audio
|
|
214
189
|
*/
|
|
215
190
|
applyMaskToAudio(audio, mask) {
|
|
216
191
|
const output = new Float32Array(audio.length);
|
|
217
|
-
// Apply mask with simple interpolation
|
|
218
192
|
for (let i = 0; i < audio.length; i++) {
|
|
219
|
-
// Map audio sample to mask index
|
|
220
193
|
const maskIdx = Math.floor((i / audio.length) * mask.length);
|
|
221
194
|
const gain = mask[Math.min(maskIdx, mask.length - 1)];
|
|
222
|
-
// Apply gain with voice frequency boost
|
|
223
195
|
output[i] = audio[i] * gain;
|
|
224
196
|
}
|
|
225
|
-
//
|
|
197
|
+
// Fade edges
|
|
226
198
|
const fadeLength = Math.min(128, output.length / 20);
|
|
227
199
|
for (let i = 0; i < fadeLength; i++) {
|
|
228
200
|
const fade = i / fadeLength;
|
|
@@ -234,7 +206,7 @@ class MLNoiseSuppressor {
|
|
|
234
206
|
return output;
|
|
235
207
|
}
|
|
236
208
|
/**
|
|
237
|
-
* Reset processing state
|
|
209
|
+
* Reset processing state
|
|
238
210
|
*/
|
|
239
211
|
reset() {
|
|
240
212
|
this.prevMask = null;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Audio module exports
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
17
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
__exportStar(require("./AudioPipeline"), exports);
|
|
21
|
+
__exportStar(require("./AudioNodeFactory"), exports);
|
|
22
|
+
__exportStar(require("./MLNoiseSuppressor"), exports);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Huddle Channel
|
|
3
|
+
*
|
|
4
|
+
* Manages private huddle conversations between participants
|
|
5
|
+
*/
|
|
6
|
+
import { Socket } from 'socket.io-client';
|
|
7
|
+
import { HuddleAcceptResponse, HuddleRejectResponse, HuddleResponse } from './HuddleTypes';
|
|
8
|
+
/**
|
|
9
|
+
* Huddle channel configuration
|
|
10
|
+
*/
|
|
11
|
+
export interface HuddleChannelConfig {
|
|
12
|
+
socket: Socket;
|
|
13
|
+
localParticipantId: string;
|
|
14
|
+
roomId: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Huddle Channel class
|
|
18
|
+
* Handles all huddle-related socket communication
|
|
19
|
+
*/
|
|
20
|
+
export declare class HuddleChannel {
|
|
21
|
+
private socket;
|
|
22
|
+
private localParticipantId;
|
|
23
|
+
private roomId;
|
|
24
|
+
private currentChannel;
|
|
25
|
+
constructor(config: HuddleChannelConfig);
|
|
26
|
+
/**
|
|
27
|
+
* Get current channel ID
|
|
28
|
+
*/
|
|
29
|
+
getCurrentChannel(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Set current channel (called when channel changes)
|
|
32
|
+
*/
|
|
33
|
+
setCurrentChannel(channelId: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Check if currently in a huddle
|
|
36
|
+
*/
|
|
37
|
+
isInHuddle(): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Send huddle invite to another participant
|
|
40
|
+
*/
|
|
41
|
+
sendInvite(toParticipantId: string): Promise<{
|
|
42
|
+
success?: boolean;
|
|
43
|
+
inviteId?: string;
|
|
44
|
+
huddleId?: string;
|
|
45
|
+
error?: string;
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* Accept a huddle invite
|
|
49
|
+
*/
|
|
50
|
+
acceptInvite(inviteId: string): Promise<HuddleAcceptResponse>;
|
|
51
|
+
/**
|
|
52
|
+
* Reject a huddle invite
|
|
53
|
+
*/
|
|
54
|
+
rejectInvite(inviteId: string): Promise<HuddleRejectResponse>;
|
|
55
|
+
/**
|
|
56
|
+
* Join the global huddle channel
|
|
57
|
+
*/
|
|
58
|
+
join(): Promise<{
|
|
59
|
+
success?: boolean;
|
|
60
|
+
channelId?: string;
|
|
61
|
+
error?: string;
|
|
62
|
+
}>;
|
|
63
|
+
/**
|
|
64
|
+
* Leave current huddle and return to spatial audio
|
|
65
|
+
*/
|
|
66
|
+
leave(): Promise<{
|
|
67
|
+
success?: boolean;
|
|
68
|
+
channelId?: string;
|
|
69
|
+
error?: string;
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Get a participant's current channel
|
|
73
|
+
*/
|
|
74
|
+
getParticipantChannel(participantId: string): Promise<string>;
|
|
75
|
+
/**
|
|
76
|
+
* Request another participant to mute (owner only)
|
|
77
|
+
*/
|
|
78
|
+
muteParticipant(participantId: string, requesterId: string): Promise<HuddleResponse>;
|
|
79
|
+
/**
|
|
80
|
+
* Update local participant ID (after reconnection)
|
|
81
|
+
*/
|
|
82
|
+
updateLocalParticipantId(participantId: string): void;
|
|
83
|
+
/**
|
|
84
|
+
* Update room ID (after room change)
|
|
85
|
+
*/
|
|
86
|
+
updateRoomId(roomId: string): void;
|
|
87
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Huddle Channel
|
|
4
|
+
*
|
|
5
|
+
* Manages private huddle conversations between participants
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.HuddleChannel = void 0;
|
|
9
|
+
const HuddleTypes_1 = require("./HuddleTypes");
|
|
10
|
+
/**
|
|
11
|
+
* Huddle Channel class
|
|
12
|
+
* Handles all huddle-related socket communication
|
|
13
|
+
*/
|
|
14
|
+
class HuddleChannel {
|
|
15
|
+
constructor(config) {
|
|
16
|
+
this.currentChannel = HuddleTypes_1.SPATIAL_CHANNEL_ID;
|
|
17
|
+
this.socket = config.socket;
|
|
18
|
+
this.localParticipantId = config.localParticipantId;
|
|
19
|
+
this.roomId = config.roomId;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get current channel ID
|
|
23
|
+
*/
|
|
24
|
+
getCurrentChannel() {
|
|
25
|
+
return this.currentChannel;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Set current channel (called when channel changes)
|
|
29
|
+
*/
|
|
30
|
+
setCurrentChannel(channelId) {
|
|
31
|
+
this.currentChannel = channelId;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Check if currently in a huddle
|
|
35
|
+
*/
|
|
36
|
+
isInHuddle() {
|
|
37
|
+
return this.currentChannel !== HuddleTypes_1.SPATIAL_CHANNEL_ID;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Send huddle invite to another participant
|
|
41
|
+
*/
|
|
42
|
+
async sendInvite(toParticipantId) {
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
this.socket.emit('send-huddle-invite', {
|
|
45
|
+
fromParticipantId: this.localParticipantId,
|
|
46
|
+
toParticipantId,
|
|
47
|
+
roomId: this.roomId,
|
|
48
|
+
}, (response) => {
|
|
49
|
+
resolve(response);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Accept a huddle invite
|
|
55
|
+
*/
|
|
56
|
+
async acceptInvite(inviteId) {
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
this.socket.emit('accept-huddle-invite', {
|
|
59
|
+
inviteId,
|
|
60
|
+
participantId: this.localParticipantId,
|
|
61
|
+
}, (response) => {
|
|
62
|
+
if (response.success) {
|
|
63
|
+
// Server may send huddleId OR channelId - handle both
|
|
64
|
+
this.currentChannel = response.channelId || response.huddleId || 'odyssey-huddle';
|
|
65
|
+
}
|
|
66
|
+
resolve(response);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Reject a huddle invite
|
|
72
|
+
*/
|
|
73
|
+
async rejectInvite(inviteId) {
|
|
74
|
+
return new Promise((resolve) => {
|
|
75
|
+
this.socket.emit('reject-huddle-invite', {
|
|
76
|
+
inviteId,
|
|
77
|
+
participantId: this.localParticipantId,
|
|
78
|
+
}, (response) => {
|
|
79
|
+
resolve(response);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Join the global huddle channel
|
|
85
|
+
*/
|
|
86
|
+
async join() {
|
|
87
|
+
return new Promise((resolve) => {
|
|
88
|
+
this.socket.emit('join-huddle', {
|
|
89
|
+
participantId: this.localParticipantId,
|
|
90
|
+
roomId: this.roomId,
|
|
91
|
+
}, (response) => {
|
|
92
|
+
if (response.success && response.channelId) {
|
|
93
|
+
this.currentChannel = response.channelId;
|
|
94
|
+
}
|
|
95
|
+
resolve(response);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Leave current huddle and return to spatial audio
|
|
101
|
+
*/
|
|
102
|
+
async leave() {
|
|
103
|
+
return new Promise((resolve) => {
|
|
104
|
+
this.socket.emit('leave-huddle', {
|
|
105
|
+
participantId: this.localParticipantId,
|
|
106
|
+
roomId: this.roomId,
|
|
107
|
+
}, (response) => {
|
|
108
|
+
if (response.success) {
|
|
109
|
+
this.currentChannel = response.channelId || HuddleTypes_1.SPATIAL_CHANNEL_ID;
|
|
110
|
+
}
|
|
111
|
+
resolve(response);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get a participant's current channel
|
|
117
|
+
*/
|
|
118
|
+
async getParticipantChannel(participantId) {
|
|
119
|
+
return new Promise((resolve) => {
|
|
120
|
+
this.socket.emit('get-participant-channel', { participantId }, (response) => {
|
|
121
|
+
resolve(response.channelId || HuddleTypes_1.SPATIAL_CHANNEL_ID);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Request another participant to mute (owner only)
|
|
127
|
+
*/
|
|
128
|
+
async muteParticipant(participantId, requesterId) {
|
|
129
|
+
return new Promise((resolve) => {
|
|
130
|
+
this.socket.emit('mute-participant', {
|
|
131
|
+
targetParticipantId: participantId,
|
|
132
|
+
roomId: this.roomId,
|
|
133
|
+
requesterId,
|
|
134
|
+
}, (response) => {
|
|
135
|
+
resolve(response);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Update local participant ID (after reconnection)
|
|
141
|
+
*/
|
|
142
|
+
updateLocalParticipantId(participantId) {
|
|
143
|
+
this.localParticipantId = participantId;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Update room ID (after room change)
|
|
147
|
+
*/
|
|
148
|
+
updateRoomId(roomId) {
|
|
149
|
+
this.roomId = roomId;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
exports.HuddleChannel = HuddleChannel;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Huddle Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for huddle (private conversation) functionality
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Spatial channel ID constant
|
|
8
|
+
*/
|
|
9
|
+
export declare const SPATIAL_CHANNEL_ID = "spatial";
|
|
10
|
+
/**
|
|
11
|
+
* Huddle channel ID prefix
|
|
12
|
+
*/
|
|
13
|
+
export declare const HUDDLE_CHANNEL_PREFIX = "odyssey-huddle";
|
|
14
|
+
/**
|
|
15
|
+
* Check if a channel ID is a huddle channel
|
|
16
|
+
*/
|
|
17
|
+
export declare function isHuddleChannel(channelId?: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Huddle invite data
|
|
20
|
+
*/
|
|
21
|
+
export interface HuddleInvite {
|
|
22
|
+
inviteId: string;
|
|
23
|
+
fromParticipantId: string;
|
|
24
|
+
toParticipantId: string;
|
|
25
|
+
roomId: string;
|
|
26
|
+
huddleId?: string;
|
|
27
|
+
timestamp: number;
|
|
28
|
+
fromUserName?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Huddle response data
|
|
32
|
+
*/
|
|
33
|
+
export interface HuddleResponse {
|
|
34
|
+
success: boolean;
|
|
35
|
+
error?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Huddle accept response
|
|
39
|
+
*/
|
|
40
|
+
export interface HuddleAcceptResponse extends HuddleResponse {
|
|
41
|
+
huddleId?: string;
|
|
42
|
+
channelId?: string;
|
|
43
|
+
participants?: string[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Huddle reject response
|
|
47
|
+
*/
|
|
48
|
+
export interface HuddleRejectResponse extends HuddleResponse {
|
|
49
|
+
inviteId?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Huddle started event data
|
|
53
|
+
*/
|
|
54
|
+
export interface HuddleStartedEvent {
|
|
55
|
+
huddleId: string;
|
|
56
|
+
channelId: string;
|
|
57
|
+
participants: string[];
|
|
58
|
+
initiatorId: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Huddle updated event data
|
|
62
|
+
*/
|
|
63
|
+
export interface HuddleUpdatedEvent {
|
|
64
|
+
huddleId: string;
|
|
65
|
+
channelId: string;
|
|
66
|
+
participants: string[];
|
|
67
|
+
action: 'joined' | 'left';
|
|
68
|
+
participantId: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Huddle ended event data
|
|
72
|
+
*/
|
|
73
|
+
export interface HuddleEndedEvent {
|
|
74
|
+
huddleId: string;
|
|
75
|
+
channelId: string;
|
|
76
|
+
reason: 'all_left' | 'timeout' | 'forced';
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Participant channel changed event data
|
|
80
|
+
*/
|
|
81
|
+
export interface ParticipantChannelChangedEvent {
|
|
82
|
+
participantId: string;
|
|
83
|
+
channelId: string;
|
|
84
|
+
previousChannel?: string;
|
|
85
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Huddle Types
|
|
4
|
+
*
|
|
5
|
+
* Type definitions for huddle (private conversation) functionality
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.HUDDLE_CHANNEL_PREFIX = exports.SPATIAL_CHANNEL_ID = void 0;
|
|
9
|
+
exports.isHuddleChannel = isHuddleChannel;
|
|
10
|
+
/**
|
|
11
|
+
* Spatial channel ID constant
|
|
12
|
+
*/
|
|
13
|
+
exports.SPATIAL_CHANNEL_ID = 'spatial';
|
|
14
|
+
/**
|
|
15
|
+
* Huddle channel ID prefix
|
|
16
|
+
*/
|
|
17
|
+
exports.HUDDLE_CHANNEL_PREFIX = 'odyssey-huddle';
|
|
18
|
+
/**
|
|
19
|
+
* Check if a channel ID is a huddle channel
|
|
20
|
+
*/
|
|
21
|
+
function isHuddleChannel(channelId) {
|
|
22
|
+
if (!channelId)
|
|
23
|
+
return false;
|
|
24
|
+
return channelId !== exports.SPATIAL_CHANNEL_ID && channelId.includes('huddle');
|
|
25
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Huddle module - Re-exports huddle channel functionality
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
17
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
__exportStar(require("./HuddleTypes"), exports);
|
|
21
|
+
__exportStar(require("./HuddleChannel"), exports);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Channels module - Re-exports all channel functionality
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
17
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
__exportStar(require("./huddle"), exports);
|
|
21
|
+
__exportStar(require("./spatial"), exports);
|