@newgameplusinc/odyssey-audio-video-sdk-dev 1.0.341 → 1.0.342
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.
|
@@ -209,21 +209,17 @@ tf.serialization.registerClass(GRUCellResetAfterSupport);
|
|
|
209
209
|
//
|
|
210
210
|
// WEIGHT NAMING:
|
|
211
211
|
// model.json weightsManifest uses paths like "gru_96/gru_cell/kernel".
|
|
212
|
-
// TF.js builds variable names as `${
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
// Result: "gru_96" + "/" + "gru_cell" + "/" + "kernel" = "gru_96/gru_cell/kernel" ✅
|
|
216
|
-
// If cell were named "${layerName}/gru_cell" (old, wrong):
|
|
217
|
-
// "gru_96" + "/" + "gru_96/gru_cell" + "/" + "kernel" = "gru_96/gru_96/gru_cell/kernel" ❌
|
|
212
|
+
// TF.js builds variable names as `${layer.name}/${varName}`.
|
|
213
|
+
// So the cell MUST be named `${layerName}/gru_cell` (e.g. "gru_96/gru_cell")
|
|
214
|
+
// so that addWeight('kernel') → "gru_96/gru_cell/kernel" ← matches manifest.
|
|
218
215
|
// =============================================================================
|
|
219
216
|
class GRULayerWithResetAfter {
|
|
220
217
|
static fromConfig(_cls, config) {
|
|
221
218
|
const layerName = config.name;
|
|
222
|
-
//
|
|
223
|
-
// Final weight path: "${layerName}/gru_cell/kernel" ✅
|
|
219
|
+
// Create our cell — named so its weights match the manifest paths
|
|
224
220
|
const cell = new GRUCellResetAfterSupport({
|
|
225
221
|
...config,
|
|
226
|
-
name:
|
|
222
|
+
name: `${layerName}/gru_cell`, // → weights: "gru_96/gru_cell/kernel" ✅
|
|
227
223
|
// Normalise keys: Keras JSON is snake_case; TF.js internals are camelCase
|
|
228
224
|
useBias: config.useBias ?? config.use_bias ?? true,
|
|
229
225
|
recurrentActivation: config.recurrentActivation ?? config.recurrent_activation ?? "sigmoid",
|
|
@@ -351,21 +347,13 @@ class MLNoiseSuppressor {
|
|
|
351
347
|
const nMels = this.config.n_mels || 40;
|
|
352
348
|
console.log(`[MLNoiseSuppressor] Warming up model (${seqLen} × ${nMels})...`);
|
|
353
349
|
const warmupInput = tf.zeros([1, seqLen, nMels]);
|
|
354
|
-
let warmupMax = 0;
|
|
355
350
|
for (let w = 0; w < 3; w++) {
|
|
356
351
|
const warmupOut = this.model.predict(warmupInput);
|
|
357
|
-
|
|
358
|
-
for (let i = 0; i < warmupData.length; i++)
|
|
359
|
-
if (warmupData[i] > warmupMax)
|
|
360
|
-
warmupMax = warmupData[i];
|
|
352
|
+
warmupOut.dataSync(); // force full synchronous execution
|
|
361
353
|
warmupOut.dispose();
|
|
362
354
|
}
|
|
363
355
|
warmupInput.dispose();
|
|
364
|
-
|
|
365
|
-
// If warmupMax === 0, weights failed to load (name mismatch in weightsManifest).
|
|
366
|
-
console.log(`[MLNoiseSuppressor] Warmup done — zero-input output max: ${warmupMax.toFixed(4)} ${warmupMax > 0
|
|
367
|
-
? "✅ weights loaded"
|
|
368
|
-
: "❌ weights NOT loaded — check weight name mapping"}`);
|
|
356
|
+
console.log(`[MLNoiseSuppressor] Warmup done`);
|
|
369
357
|
this.isInitialized = true;
|
|
370
358
|
console.log(`[MLNoiseSuppressor] ✅ Ready — noise suppression is ACTIVE`);
|
|
371
359
|
console.log(`[MLNoiseSuppressor] Config: ${modelSampleRate}Hz, ${this.config.n_mels} mels, n_fft=${this.config.n_fft || 2048}`);
|
|
@@ -1019,13 +1007,6 @@ class MLNoiseSuppressor {
|
|
|
1019
1007
|
if (rawMask[m] > rawMax)
|
|
1020
1008
|
rawMax = rawMask[m];
|
|
1021
1009
|
}
|
|
1022
|
-
// Model failure fallback: if model outputs all-zeros (wrong/cached model files),
|
|
1023
|
-
// returning gains=0 suppresses all speech. Return full passthrough instead.
|
|
1024
|
-
if (rawMax === 0) {
|
|
1025
|
-
const passthrough = new Float32Array(bins).fill(1.0);
|
|
1026
|
-
passthrough[0] = 1.0; // speech flag = on, keeps worklet gate open
|
|
1027
|
-
return passthrough;
|
|
1028
|
-
}
|
|
1029
1010
|
// Threshold 0.108: fan/AC noise consistently scores rawMax=0.094-0.100.
|
|
1030
1011
|
// Speech consistently scores rawMax≥0.111. Gap between 0.100 and 0.111 gives
|
|
1031
1012
|
// 8% margin. Previously 0.10 caused fan frames scoring exactly 0.100 to
|
|
@@ -1084,16 +1065,6 @@ class MLNoiseSuppressor {
|
|
|
1084
1065
|
gains[k] = Math.max(IRM_SPEECH_FLOOR, gains[k]);
|
|
1085
1066
|
}
|
|
1086
1067
|
}
|
|
1087
|
-
// ── Step 6: Set DC bin as explicit speech/noise gate signal ──────────
|
|
1088
|
-
// The worklet reads gains[0] to control its ML gate:
|
|
1089
|
-
// gains[0] >= 0.5 → speech detected → open gate + reset 533ms holdover
|
|
1090
|
-
// gains[0] < 0.5 → noise frame → count down holdover, then close
|
|
1091
|
-
//
|
|
1092
|
-
// DC bin (0 Hz) has no mel filter coverage so it always computes to 1.0
|
|
1093
|
-
// above (no-coverage default). This means the ML gate NEVER closes via ML
|
|
1094
|
-
// — fan noise leaks through during inter-word pauses. Override it explicitly
|
|
1095
|
-
// so the worklet gate actually follows the ML speech decision.
|
|
1096
|
-
gains[0] = isSpeechFrame ? 1.0 : 0.0;
|
|
1097
1068
|
// ── Diagnostic log ────────────────────────────────────────────────────
|
|
1098
1069
|
const now = Date.now();
|
|
1099
1070
|
const lastLog = MLNoiseSuppressor._lastGainLog || new Map();
|
package/package.json
CHANGED