@newgameplusinc/odyssey-audio-video-sdk-dev 1.0.55 → 1.0.57
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/MLNoiseSuppressor.js +35 -13
- package/dist/MediasoupManager.js +27 -11
- package/package.json +1 -1
|
@@ -351,26 +351,48 @@ class MLNoiseSuppressor {
|
|
|
351
351
|
this.processingNode = this.audioContext.createScriptProcessor(bufferSize, 1, 1);
|
|
352
352
|
let frameCount = 0;
|
|
353
353
|
const startTime = performance.now();
|
|
354
|
+
// Double-buffering for async ML processing
|
|
355
|
+
// We store the PREVIOUS processed result and output it in the NEXT callback
|
|
356
|
+
// This adds one buffer of latency but ensures we never output zeros
|
|
357
|
+
let previousProcessedBuffer = null;
|
|
358
|
+
let processingInFlight = false;
|
|
354
359
|
// Process audio frames with ML model
|
|
355
|
-
|
|
360
|
+
// IMPORTANT: onaudioprocess is synchronous! We use double-buffering to handle async ML
|
|
361
|
+
this.processingNode.onaudioprocess = (event) => {
|
|
356
362
|
const inputData = event.inputBuffer.getChannelData(0);
|
|
357
363
|
const outputData = event.outputBuffer.getChannelData(0);
|
|
358
364
|
frameCount++;
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
outputData.set(processed);
|
|
363
|
-
// Log performance every ~4 seconds
|
|
364
|
-
if (frameCount % 100 === 0) {
|
|
365
|
-
const elapsed = (performance.now() - startTime) / 1000;
|
|
366
|
-
const fps = frameCount / elapsed;
|
|
367
|
-
console.log(`🎤 [ML] BiLSTM: ${frameCount} frames @ ${fps.toFixed(1)} fps`);
|
|
368
|
-
}
|
|
365
|
+
// OUTPUT: Use previously processed audio (or passthrough if not ready yet)
|
|
366
|
+
if (previousProcessedBuffer) {
|
|
367
|
+
outputData.set(previousProcessedBuffer);
|
|
369
368
|
}
|
|
370
|
-
|
|
371
|
-
//
|
|
369
|
+
else {
|
|
370
|
+
// First frame or ML not ready - pass through original audio
|
|
372
371
|
outputData.set(inputData);
|
|
373
372
|
}
|
|
373
|
+
// PROCESS: Start async ML processing for the NEXT frame
|
|
374
|
+
// Only start new processing if previous one is complete
|
|
375
|
+
if (!processingInFlight) {
|
|
376
|
+
processingInFlight = true;
|
|
377
|
+
const inputCopy = new Float32Array(inputData);
|
|
378
|
+
// Fire-and-forget async processing
|
|
379
|
+
this.processAudio(inputCopy)
|
|
380
|
+
.then((processed) => {
|
|
381
|
+
previousProcessedBuffer = processed;
|
|
382
|
+
processingInFlight = false;
|
|
383
|
+
})
|
|
384
|
+
.catch((error) => {
|
|
385
|
+
// On error, store the original audio for passthrough
|
|
386
|
+
previousProcessedBuffer = inputCopy;
|
|
387
|
+
processingInFlight = false;
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
// Log performance every ~4 seconds
|
|
391
|
+
if (frameCount % 100 === 0) {
|
|
392
|
+
const elapsed = (performance.now() - startTime) / 1000;
|
|
393
|
+
const fps = frameCount / elapsed;
|
|
394
|
+
console.log(`🎤 [ML] BiLSTM: ${frameCount} frames @ ${fps.toFixed(1)} fps`);
|
|
395
|
+
}
|
|
374
396
|
};
|
|
375
397
|
// Connect: source -> highpass -> BiLSTM processor -> destination
|
|
376
398
|
source.connect(this.highPassFilter);
|
package/dist/MediasoupManager.js
CHANGED
|
@@ -41,7 +41,7 @@ class MediasoupManager {
|
|
|
41
41
|
this.recvTransport = null;
|
|
42
42
|
this.producers = new Map();
|
|
43
43
|
this.consumers = new Map();
|
|
44
|
-
this.participantId =
|
|
44
|
+
this.participantId = "";
|
|
45
45
|
this.socket = socket;
|
|
46
46
|
this.device = new mediasoupClient.Device();
|
|
47
47
|
}
|
|
@@ -100,7 +100,7 @@ class MediasoupManager {
|
|
|
100
100
|
// Emit event so parent SDK can recreate producers
|
|
101
101
|
this.socket.emit("transport-failed", {
|
|
102
102
|
participantId: this.participantId,
|
|
103
|
-
direction: "send"
|
|
103
|
+
direction: "send",
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
106
|
});
|
|
@@ -126,30 +126,46 @@ class MediasoupManager {
|
|
|
126
126
|
async produce(track, appData) {
|
|
127
127
|
if (!this.sendTransport)
|
|
128
128
|
throw new Error("Send transport not initialized");
|
|
129
|
+
console.log(`📤 [MediaSoup] Producing ${track.kind} track (transport state: ${this.sendTransport.connectionState})`);
|
|
129
130
|
// Configure simulcast for video tracks for adaptive bitrate
|
|
130
131
|
const produceOptions = { track, appData };
|
|
131
|
-
if (track.kind ===
|
|
132
|
+
if (track.kind === "video") {
|
|
132
133
|
produceOptions.encodings = [
|
|
133
134
|
// Low quality layer - 100 kbps, good for poor connections
|
|
134
|
-
{
|
|
135
|
+
{
|
|
136
|
+
rid: "r0",
|
|
137
|
+
active: true,
|
|
138
|
+
maxBitrate: 100000,
|
|
139
|
+
scaleResolutionDownBy: 4,
|
|
140
|
+
},
|
|
135
141
|
// Medium quality layer - 300 kbps, balanced
|
|
136
|
-
{
|
|
142
|
+
{
|
|
143
|
+
rid: "r1",
|
|
144
|
+
active: true,
|
|
145
|
+
maxBitrate: 300000,
|
|
146
|
+
scaleResolutionDownBy: 2,
|
|
147
|
+
},
|
|
137
148
|
// High quality layer - 900 kbps, full resolution
|
|
138
|
-
{
|
|
149
|
+
{
|
|
150
|
+
rid: "r2",
|
|
151
|
+
active: true,
|
|
152
|
+
maxBitrate: 900000,
|
|
153
|
+
scaleResolutionDownBy: 1,
|
|
154
|
+
},
|
|
139
155
|
];
|
|
140
156
|
// VP8 codec for simulcast support
|
|
141
157
|
produceOptions.codecOptions = {
|
|
142
|
-
videoGoogleStartBitrate: 1000
|
|
158
|
+
videoGoogleStartBitrate: 1000,
|
|
143
159
|
};
|
|
144
160
|
}
|
|
145
161
|
const producer = await this.sendTransport.produce(produceOptions);
|
|
146
162
|
// Handle producer events
|
|
147
|
-
producer.on(
|
|
163
|
+
producer.on("transportclose", () => {
|
|
148
164
|
console.warn(`⚠️ [MediaSoup] Producer ${producer.id.substring(0, 8)} transport closed`);
|
|
149
165
|
this.producers.delete(producer.id);
|
|
150
166
|
// The main SDK (index.ts) should handle recreation
|
|
151
167
|
});
|
|
152
|
-
producer.on(
|
|
168
|
+
producer.on("trackended", () => {
|
|
153
169
|
console.warn(`⚠️ [MediaSoup] Producer ${producer.id.substring(0, 8)} track ended`);
|
|
154
170
|
producer.close();
|
|
155
171
|
this.producers.delete(producer.id);
|
|
@@ -167,12 +183,12 @@ class MediasoupManager {
|
|
|
167
183
|
rtpParameters: data.rtpParameters,
|
|
168
184
|
});
|
|
169
185
|
// Handle consumer events
|
|
170
|
-
consumer.on(
|
|
186
|
+
consumer.on("transportclose", () => {
|
|
171
187
|
console.warn(`⚠️ [MediaSoup] Consumer ${consumer.id.substring(0, 8)} transport closed`);
|
|
172
188
|
this.consumers.delete(consumer.id);
|
|
173
189
|
// The main SDK (index.ts) should handle recreation
|
|
174
190
|
});
|
|
175
|
-
consumer.on(
|
|
191
|
+
consumer.on("trackended", () => {
|
|
176
192
|
console.warn(`⚠️ [MediaSoup] Consumer ${consumer.id.substring(0, 8)} track ended`);
|
|
177
193
|
consumer.close();
|
|
178
194
|
this.consumers.delete(consumer.id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newgameplusinc/odyssey-audio-video-sdk-dev",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.57",
|
|
4
4
|
"description": "Odyssey Spatial Audio & Video SDK using MediaSoup for real-time communication with AI-powered noise suppression",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|