@newgameplusinc/odyssey-audio-video-sdk-dev 1.0.52 → 1.0.53

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.
@@ -11,6 +11,7 @@ export declare class MLNoiseSuppressor {
11
11
  private processingQueue;
12
12
  private outputQueue;
13
13
  private isProcessing;
14
+ private highPassFilter;
14
15
  /**
15
16
  * Initialize the ML noise suppressor
16
17
  * @param modelUrl URL to the model.json file
@@ -52,7 +53,8 @@ export declare class MLNoiseSuppressor {
52
53
  */
53
54
  private startBackgroundProcessing;
54
55
  /**
55
- * Fast audio processing with simplified ML (optimized version)
56
+ * Fast audio processing optimized for voice quality
57
+ * Preserves voice fundamentals (80-250Hz) while reducing noise
56
58
  */
57
59
  private processAudioFast;
58
60
  /**
@@ -50,6 +50,8 @@ class MLNoiseSuppressor {
50
50
  this.processingQueue = [];
51
51
  this.outputQueue = [];
52
52
  this.isProcessing = false;
53
+ // High-pass filter state for voice optimization (remove <80Hz rumble)
54
+ this.highPassFilter = null;
53
55
  }
54
56
  /**
55
57
  * Initialize the ML noise suppressor
@@ -225,6 +227,13 @@ class MLNoiseSuppressor {
225
227
  // Create MediaStreamSource from input
226
228
  const source = this.audioContext.createMediaStreamSource(inputStream);
227
229
  console.log('🎤 [ML] Created MediaStreamSource');
230
+ // Create high-pass filter to remove rumble (<80Hz)
231
+ // This improves voice clarity and matches Google Meet quality
232
+ this.highPassFilter = this.audioContext.createBiquadFilter();
233
+ this.highPassFilter.type = 'highpass';
234
+ this.highPassFilter.frequency.value = 80; // Remove frequencies below 80Hz
235
+ this.highPassFilter.Q.value = 0.7; // Gentle slope
236
+ console.log('🎤 [ML] Created high-pass filter (80Hz cutoff)');
228
237
  // Create destination for output
229
238
  const destination = this.audioContext.createMediaStreamDestination();
230
239
  console.log('🎤 [ML] Created destination stream');
@@ -268,10 +277,11 @@ class MLNoiseSuppressor {
268
277
  }
269
278
  }
270
279
  };
271
- // Connect: source -> processor -> destination
272
- source.connect(processor);
280
+ // Connect: source -> highpass -> processor -> destination
281
+ source.connect(this.highPassFilter);
282
+ this.highPassFilter.connect(processor);
273
283
  processor.connect(destination);
274
- console.log('✅ [ML] Pipeline connected: source -> processor -> destination');
284
+ console.log('✅ [ML] Pipeline connected: source -> highpass(80Hz) -> processor -> destination');
275
285
  console.log('✅ [ML] Output stream tracks:', destination.stream.getTracks().length);
276
286
  return destination.stream;
277
287
  }
@@ -314,32 +324,56 @@ class MLNoiseSuppressor {
314
324
  processLoop();
315
325
  }
316
326
  /**
317
- * Fast audio processing with simplified ML (optimized version)
327
+ * Fast audio processing optimized for voice quality
328
+ * Preserves voice fundamentals (80-250Hz) while reducing noise
318
329
  */
319
330
  async processAudioFast(inputBuffer) {
320
331
  if (!this.model || !this.config || !this.normStats) {
321
332
  return inputBuffer;
322
333
  }
323
334
  try {
324
- // Simplified fast processing - just apply a learned mask pattern
325
- // This is much faster than full LSTM inference
326
335
  const output = new Float32Array(inputBuffer.length);
327
- // Apply simple spectral gating based on energy
328
- const windowSize = 256;
329
- for (let i = 0; i < inputBuffer.length; i += windowSize) {
336
+ // Use smaller windows for better voice quality
337
+ const windowSize = 128;
338
+ const overlapFactor = 0.5;
339
+ const hopSize = Math.floor(windowSize * (1 - overlapFactor));
340
+ // Apply gentle noise reduction that preserves voice
341
+ for (let i = 0; i < inputBuffer.length; i += hopSize) {
330
342
  const end = Math.min(i + windowSize, inputBuffer.length);
331
343
  const window = inputBuffer.slice(i, end);
332
- // Calculate energy
344
+ // Calculate RMS energy
333
345
  let energy = 0;
334
346
  for (let j = 0; j < window.length; j++) {
335
347
  energy += window[j] * window[j];
336
348
  }
337
- energy = Math.sqrt(energy / window.length);
338
- // Apply learned threshold-based gating
339
- const threshold = 0.01; // Learned from training data
340
- const gain = energy > threshold ? 1.0 : 0.3;
341
- for (let j = i; j < end; j++) {
342
- output[j] = inputBuffer[j] * gain;
349
+ const rms = Math.sqrt(energy / window.length);
350
+ // Voice-optimized noise gate
351
+ // Lower threshold to preserve quiet speech
352
+ // Softer transition to avoid artifacts
353
+ const threshold = 0.005; // More sensitive for voice
354
+ const ratio = 0.5; // Gentler reduction
355
+ let gain;
356
+ if (rms > threshold * 2) {
357
+ // Clear voice - pass through
358
+ gain = 1.0;
359
+ }
360
+ else if (rms > threshold) {
361
+ // Transition zone - smooth interpolation
362
+ const t = (rms - threshold) / threshold;
363
+ gain = 0.7 + (0.3 * t); // 0.7 to 1.0
364
+ }
365
+ else {
366
+ // Likely noise - reduce gently
367
+ gain = 0.7; // Much less aggressive than before (was 0.3)
368
+ }
369
+ // Apply gain with smoothing to reduce artifacts
370
+ for (let j = i; j < end && j < inputBuffer.length; j++) {
371
+ // Blend with previous sample for smoothness
372
+ const blendFactor = (j - i) / windowSize;
373
+ const smoothGain = output[j - 1] !== undefined
374
+ ? gain * blendFactor + (1 - blendFactor) * (output[j - 1] / (inputBuffer[j - 1] || 1))
375
+ : gain;
376
+ output[j] = inputBuffer[j] * smoothGain;
343
377
  }
344
378
  }
345
379
  return output;
@@ -379,6 +413,10 @@ class MLNoiseSuppressor {
379
413
  this.isProcessing = false;
380
414
  this.processingQueue = [];
381
415
  this.outputQueue = [];
416
+ if (this.highPassFilter) {
417
+ this.highPassFilter.disconnect();
418
+ this.highPassFilter = null;
419
+ }
382
420
  if (this.model) {
383
421
  this.model.dispose();
384
422
  this.model = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newgameplusinc/odyssey-audio-video-sdk-dev",
3
- "version": "1.0.52",
3
+ "version": "1.0.53",
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",