omnivad 0.2.0
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.cjs +504 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +169 -0
- package/dist/index.d.ts +169 -0
- package/dist/index.js +495 -0
- package/dist/index.js.map +1 -0
- package/dist/wasm/omnivad.cjs +22 -0
- package/dist/wasm/omnivad.data +0 -0
- package/dist/wasm/omnivad.js +22 -0
- package/dist/wasm/omnivad.wasm +0 -0
- package/package.json +52 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
4
|
+
// src/wasm-binding.ts
|
|
5
|
+
var _module = null;
|
|
6
|
+
var _loading = null;
|
|
7
|
+
var SIZEOF_POST_CONFIG = 28;
|
|
8
|
+
var SIZEOF_AED_POST_CONFIG = 3 * SIZEOF_POST_CONFIG;
|
|
9
|
+
var SIZEOF_SEGMENT = 8;
|
|
10
|
+
var SIZEOF_AED_SEGMENT = 16;
|
|
11
|
+
async function initWasm(wasmLocator) {
|
|
12
|
+
if (_module) return _module;
|
|
13
|
+
if (_loading) return _loading;
|
|
14
|
+
_loading = (async () => {
|
|
15
|
+
let createOmniVAD;
|
|
16
|
+
let defaultLocateFile;
|
|
17
|
+
if (typeof globalThis.process?.versions?.node === "string") {
|
|
18
|
+
const { createRequire } = await import(
|
|
19
|
+
/* webpackIgnore: true */
|
|
20
|
+
'module'
|
|
21
|
+
);
|
|
22
|
+
const { dirname, join } = await import('path');
|
|
23
|
+
const req = createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
24
|
+
const gluePath = req.resolve("../dist/wasm/omnivad.cjs");
|
|
25
|
+
const wasmDir = dirname(gluePath);
|
|
26
|
+
createOmniVAD = req(gluePath);
|
|
27
|
+
defaultLocateFile = (filename) => join(wasmDir, filename);
|
|
28
|
+
} else {
|
|
29
|
+
const glueUrl = new URL("../dist/wasm/omnivad.js", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
30
|
+
const mod = await import(
|
|
31
|
+
/* webpackIgnore: true */
|
|
32
|
+
glueUrl.href
|
|
33
|
+
);
|
|
34
|
+
createOmniVAD = mod.default || mod;
|
|
35
|
+
const wasmBaseUrl = new URL("./", glueUrl);
|
|
36
|
+
defaultLocateFile = (filename) => new URL(filename, wasmBaseUrl).toString();
|
|
37
|
+
}
|
|
38
|
+
const opts = {};
|
|
39
|
+
const locateFile = wasmLocator ?? defaultLocateFile;
|
|
40
|
+
if (locateFile) {
|
|
41
|
+
opts.locateFile = (path) => locateFile(path);
|
|
42
|
+
}
|
|
43
|
+
_module = await createOmniVAD(opts);
|
|
44
|
+
return _module;
|
|
45
|
+
})();
|
|
46
|
+
return _loading;
|
|
47
|
+
}
|
|
48
|
+
function getModule() {
|
|
49
|
+
if (!_module) throw new Error("WASM not initialized. Call initWasm() first.");
|
|
50
|
+
return _module;
|
|
51
|
+
}
|
|
52
|
+
function copyAudioToHeap(M, audio) {
|
|
53
|
+
const ptr = M._malloc(audio.length * 4);
|
|
54
|
+
const heap = new Float32Array(M.HEAPU8.buffer, ptr, audio.length);
|
|
55
|
+
heap.set(audio);
|
|
56
|
+
return ptr;
|
|
57
|
+
}
|
|
58
|
+
function writePostConfig(M, ptr, cfg) {
|
|
59
|
+
M.setValue(ptr + 0, cfg.threshold, "float");
|
|
60
|
+
M.setValue(ptr + 4, cfg.smoothWindowSize, "i32");
|
|
61
|
+
M.setValue(ptr + 8, cfg.minSpeechFrames, "i32");
|
|
62
|
+
M.setValue(ptr + 12, cfg.minSilenceFrames, "i32");
|
|
63
|
+
M.setValue(ptr + 16, cfg.maxSpeechFrames, "i32");
|
|
64
|
+
M.setValue(ptr + 20, cfg.mergeSilenceFrames, "i32");
|
|
65
|
+
M.setValue(ptr + 24, cfg.extendSpeechFrames, "i32");
|
|
66
|
+
}
|
|
67
|
+
var DEFAULT_VAD_CONFIG = {
|
|
68
|
+
threshold: 0.4,
|
|
69
|
+
smoothWindowSize: 5,
|
|
70
|
+
minSpeechFrames: 20,
|
|
71
|
+
minSilenceFrames: 20,
|
|
72
|
+
maxSpeechFrames: 2e3,
|
|
73
|
+
mergeSilenceFrames: 0,
|
|
74
|
+
extendSpeechFrames: 0
|
|
75
|
+
};
|
|
76
|
+
function vadCreate(M, bundlePath = "models/vad.omnivad") {
|
|
77
|
+
const handle = M.ccall(
|
|
78
|
+
"omni_vad_nonstream_create_from_bundle",
|
|
79
|
+
"number",
|
|
80
|
+
["string"],
|
|
81
|
+
[bundlePath]
|
|
82
|
+
);
|
|
83
|
+
if (!handle) throw new Error("Failed to create VAD model");
|
|
84
|
+
return handle;
|
|
85
|
+
}
|
|
86
|
+
function readSegments(M, segPtrPtr, countPtr) {
|
|
87
|
+
const count = M.getValue(countPtr, "i32");
|
|
88
|
+
const segPtr = M.getValue(segPtrPtr, "i32");
|
|
89
|
+
const segments = [];
|
|
90
|
+
for (let i = 0; i < count; i++) {
|
|
91
|
+
const base = segPtr + i * SIZEOF_SEGMENT;
|
|
92
|
+
segments.push([
|
|
93
|
+
Math.round(M.getValue(base, "float") * 1e3) / 1e3,
|
|
94
|
+
Math.round(M.getValue(base + 4, "float") * 1e3) / 1e3
|
|
95
|
+
]);
|
|
96
|
+
}
|
|
97
|
+
if (segPtr) M._free(segPtr);
|
|
98
|
+
return segments;
|
|
99
|
+
}
|
|
100
|
+
function vadDetect(M, handle, audioPtr, numSamples, cfg, format = "f32") {
|
|
101
|
+
const cfgPtr = M._malloc(SIZEOF_POST_CONFIG);
|
|
102
|
+
const segPtrPtr = M._malloc(4);
|
|
103
|
+
const countPtr = M._malloc(4);
|
|
104
|
+
const fn = format === "int16" ? "omni_vad_nonstream_process_int16" : "omni_vad_nonstream_process";
|
|
105
|
+
try {
|
|
106
|
+
writePostConfig(M, cfgPtr, cfg);
|
|
107
|
+
const ret = M.ccall(
|
|
108
|
+
fn,
|
|
109
|
+
"number",
|
|
110
|
+
["number", "number", "number", "number", "number", "number"],
|
|
111
|
+
[handle, audioPtr, numSamples, cfgPtr, segPtrPtr, countPtr]
|
|
112
|
+
);
|
|
113
|
+
if (ret !== 0) throw new Error(`VAD detect failed: ${ret}`);
|
|
114
|
+
return readSegments(M, segPtrPtr, countPtr);
|
|
115
|
+
} finally {
|
|
116
|
+
M._free(cfgPtr);
|
|
117
|
+
M._free(segPtrPtr);
|
|
118
|
+
M._free(countPtr);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function vadDestroy(M, handle) {
|
|
122
|
+
M.ccall("omni_vad_nonstream_destroy", null, ["number"], [handle]);
|
|
123
|
+
}
|
|
124
|
+
var AED_CLASSES = { 0: "speech", 1: "singing", 2: "music" };
|
|
125
|
+
function aedCreate(M, bundlePath = "models/aed.omnivad") {
|
|
126
|
+
const handle = M.ccall(
|
|
127
|
+
"omni_aed_nonstream_create_from_bundle",
|
|
128
|
+
"number",
|
|
129
|
+
["string"],
|
|
130
|
+
[bundlePath]
|
|
131
|
+
);
|
|
132
|
+
if (!handle) throw new Error("Failed to create AED model");
|
|
133
|
+
return handle;
|
|
134
|
+
}
|
|
135
|
+
function aedDetect(M, handle, audioPtr, numSamples, cfg, format = "f32") {
|
|
136
|
+
const cfgPtr = M._malloc(SIZEOF_AED_POST_CONFIG);
|
|
137
|
+
const segPtrPtr = M._malloc(4);
|
|
138
|
+
const countPtr = M._malloc(4);
|
|
139
|
+
const fn = format === "int16" ? "omni_aed_nonstream_process_int16" : "omni_aed_nonstream_process";
|
|
140
|
+
try {
|
|
141
|
+
writePostConfig(M, cfgPtr, cfg.speech);
|
|
142
|
+
writePostConfig(M, cfgPtr + SIZEOF_POST_CONFIG, cfg.singing);
|
|
143
|
+
writePostConfig(M, cfgPtr + 2 * SIZEOF_POST_CONFIG, cfg.music);
|
|
144
|
+
const ret = M.ccall(
|
|
145
|
+
fn,
|
|
146
|
+
"number",
|
|
147
|
+
["number", "number", "number", "number", "number", "number"],
|
|
148
|
+
[handle, audioPtr, numSamples, cfgPtr, segPtrPtr, countPtr]
|
|
149
|
+
);
|
|
150
|
+
if (ret !== 0) throw new Error(`AED detect failed: ${ret}`);
|
|
151
|
+
const count = M.getValue(countPtr, "i32");
|
|
152
|
+
const segPtr = M.getValue(segPtrPtr, "i32");
|
|
153
|
+
const events = {
|
|
154
|
+
speech: [],
|
|
155
|
+
singing: [],
|
|
156
|
+
music: []
|
|
157
|
+
};
|
|
158
|
+
for (let i = 0; i < count; i++) {
|
|
159
|
+
const base = segPtr + i * SIZEOF_AED_SEGMENT;
|
|
160
|
+
const cls = M.getValue(base + 8, "i32");
|
|
161
|
+
const name = AED_CLASSES[cls];
|
|
162
|
+
if (name && events[name]) {
|
|
163
|
+
events[name].push([
|
|
164
|
+
Math.round(M.getValue(base, "float") * 1e3) / 1e3,
|
|
165
|
+
Math.round(M.getValue(base + 4, "float") * 1e3) / 1e3
|
|
166
|
+
]);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (segPtr) M._free(segPtr);
|
|
170
|
+
return events;
|
|
171
|
+
} finally {
|
|
172
|
+
M._free(cfgPtr);
|
|
173
|
+
M._free(segPtrPtr);
|
|
174
|
+
M._free(countPtr);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function aedDestroy(M, handle) {
|
|
178
|
+
M.ccall("omni_aed_nonstream_destroy", null, ["number"], [handle]);
|
|
179
|
+
}
|
|
180
|
+
function streamVadCreate(M, threshold = 0.5, bundlePath = "models/stream-vad.omnivad") {
|
|
181
|
+
const handle = M.ccall(
|
|
182
|
+
"omni_vad_stream_create_from_bundle",
|
|
183
|
+
"number",
|
|
184
|
+
["string", "number"],
|
|
185
|
+
[bundlePath, threshold]
|
|
186
|
+
);
|
|
187
|
+
if (!handle) throw new Error("Failed to create StreamVAD model");
|
|
188
|
+
return handle;
|
|
189
|
+
}
|
|
190
|
+
function streamVadProcess(M, handle, pcm16Ptr, numSamples) {
|
|
191
|
+
const resultPtr = M._malloc(12);
|
|
192
|
+
try {
|
|
193
|
+
const ret = M.ccall(
|
|
194
|
+
"omni_vad_stream_process",
|
|
195
|
+
"number",
|
|
196
|
+
["number", "number", "number", "number"],
|
|
197
|
+
[handle, pcm16Ptr, numSamples, resultPtr]
|
|
198
|
+
);
|
|
199
|
+
if (ret === -6) return null;
|
|
200
|
+
if (ret !== 0) throw new Error(`StreamVAD process failed: ${ret}`);
|
|
201
|
+
return {
|
|
202
|
+
confidence: M.getValue(resultPtr, "float"),
|
|
203
|
+
isSpeech: M.getValue(resultPtr + 4, "i8") !== 0,
|
|
204
|
+
frameOffset: M.getValue(resultPtr + 8, "i32")
|
|
205
|
+
};
|
|
206
|
+
} finally {
|
|
207
|
+
M._free(resultPtr);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function streamVadReset(M, handle) {
|
|
211
|
+
M.ccall("omni_vad_stream_reset", null, ["number"], [handle]);
|
|
212
|
+
}
|
|
213
|
+
function streamVadDestroy(M, handle) {
|
|
214
|
+
M.ccall("omni_vad_stream_destroy", null, ["number"], [handle]);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/vad.ts
|
|
218
|
+
var SAMPLE_RATE = 16e3;
|
|
219
|
+
var OmniVAD = class _OmniVAD {
|
|
220
|
+
constructor(handle, config) {
|
|
221
|
+
this.handle = handle;
|
|
222
|
+
this.config = config;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Create a new OmniVAD instance.
|
|
226
|
+
* Initializes WASM and loads the bundled ncnn model.
|
|
227
|
+
*/
|
|
228
|
+
static async create(options = {}) {
|
|
229
|
+
await initWasm();
|
|
230
|
+
const M = getModule();
|
|
231
|
+
const handle = vadCreate(M);
|
|
232
|
+
const config = {
|
|
233
|
+
threshold: options.speechThreshold ?? DEFAULT_VAD_CONFIG.threshold,
|
|
234
|
+
smoothWindowSize: options.smoothWindowSize ?? DEFAULT_VAD_CONFIG.smoothWindowSize,
|
|
235
|
+
minSpeechFrames: options.minSpeechFrames ?? DEFAULT_VAD_CONFIG.minSpeechFrames,
|
|
236
|
+
minSilenceFrames: options.minSilenceFrames ?? DEFAULT_VAD_CONFIG.minSilenceFrames,
|
|
237
|
+
maxSpeechFrames: options.maxSpeechFrames ?? DEFAULT_VAD_CONFIG.maxSpeechFrames,
|
|
238
|
+
mergeSilenceFrames: options.mergeSilenceFrames ?? DEFAULT_VAD_CONFIG.mergeSilenceFrames,
|
|
239
|
+
extendSpeechFrames: options.extendSpeechFrames ?? DEFAULT_VAD_CONFIG.extendSpeechFrames
|
|
240
|
+
};
|
|
241
|
+
return new _OmniVAD(handle, config);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Detect speech segments in audio.
|
|
245
|
+
*
|
|
246
|
+
* Accepts Int16Array (PCM) or normalized Float32Array in [-1, 1].
|
|
247
|
+
*/
|
|
248
|
+
detect(audio) {
|
|
249
|
+
const M = getModule();
|
|
250
|
+
const { ptr, length, format } = prepareAudio(M, audio);
|
|
251
|
+
try {
|
|
252
|
+
const timestamps = vadDetect(M, this.handle, ptr, length, this.config, format);
|
|
253
|
+
return {
|
|
254
|
+
duration: Math.round(length / SAMPLE_RATE * 1e3) / 1e3,
|
|
255
|
+
timestamps
|
|
256
|
+
};
|
|
257
|
+
} finally {
|
|
258
|
+
M._free(ptr);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/** Release native resources. */
|
|
262
|
+
dispose() {
|
|
263
|
+
if (this.handle) {
|
|
264
|
+
vadDestroy(getModule(), this.handle);
|
|
265
|
+
this.handle = 0;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
function prepareAudio(M, audio) {
|
|
270
|
+
const f32 = audio instanceof Int16Array ? int16ToNormalizedFloat32(audio) : audio;
|
|
271
|
+
const ptr = copyAudioToHeap(M, f32);
|
|
272
|
+
return { ptr, length: f32.length, format: "f32" };
|
|
273
|
+
}
|
|
274
|
+
function int16ToNormalizedFloat32(i16) {
|
|
275
|
+
const f32 = new Float32Array(i16.length);
|
|
276
|
+
for (let i = 0; i < i16.length; i++) f32[i] = i16[i] / 32768;
|
|
277
|
+
return f32;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// src/stream-vad.ts
|
|
281
|
+
var SAMPLE_RATE2 = 16e3;
|
|
282
|
+
var OmniStreamVAD = class _OmniStreamVAD {
|
|
283
|
+
constructor(handle) {
|
|
284
|
+
this.inSpeech = false;
|
|
285
|
+
this.speechStartFrame = 0;
|
|
286
|
+
this.handle = handle;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Create a new OmniStreamVAD instance.
|
|
290
|
+
* Initializes WASM and loads the bundled ncnn model.
|
|
291
|
+
*/
|
|
292
|
+
static async create(options = {}) {
|
|
293
|
+
await initWasm();
|
|
294
|
+
const M = getModule();
|
|
295
|
+
const threshold = options.speechThreshold ?? 0.5;
|
|
296
|
+
const handle = streamVadCreate(M, threshold);
|
|
297
|
+
return new _OmniStreamVAD(handle);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Process one frame of audio (160 int16 samples = 10ms @ 16kHz).
|
|
301
|
+
* Returns null until enough audio is accumulated.
|
|
302
|
+
*/
|
|
303
|
+
processFrame(pcm160) {
|
|
304
|
+
const M = getModule();
|
|
305
|
+
const ptr = M._malloc(pcm160.length * 2);
|
|
306
|
+
const heap16 = new Int16Array(M.HEAPU8.buffer, ptr, pcm160.length);
|
|
307
|
+
heap16.set(pcm160);
|
|
308
|
+
try {
|
|
309
|
+
const result = streamVadProcess(M, this.handle, ptr, pcm160.length);
|
|
310
|
+
if (!result || result.frameOffset === 0) return null;
|
|
311
|
+
const frameIndex = result.frameOffset;
|
|
312
|
+
const isSpeechStart = result.isSpeech && !this.inSpeech;
|
|
313
|
+
const isSpeechEnd = !result.isSpeech && this.inSpeech;
|
|
314
|
+
if (isSpeechStart) {
|
|
315
|
+
this.speechStartFrame = frameIndex;
|
|
316
|
+
}
|
|
317
|
+
const activeSpeechStartFrame = isSpeechEnd ? this.speechStartFrame : result.isSpeech ? this.speechStartFrame : 0;
|
|
318
|
+
const speechEndFrame = isSpeechEnd ? Math.max(1, frameIndex - 1) : 0;
|
|
319
|
+
this.inSpeech = result.isSpeech;
|
|
320
|
+
if (isSpeechEnd) {
|
|
321
|
+
this.speechStartFrame = 0;
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
confidence: result.confidence,
|
|
325
|
+
smoothedConfidence: result.confidence,
|
|
326
|
+
isSpeech: result.isSpeech,
|
|
327
|
+
frameIndex,
|
|
328
|
+
isSpeechStart,
|
|
329
|
+
isSpeechEnd,
|
|
330
|
+
speechStartFrame: activeSpeechStartFrame,
|
|
331
|
+
speechEndFrame
|
|
332
|
+
};
|
|
333
|
+
} finally {
|
|
334
|
+
M._free(ptr);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Process entire audio at once and return per-frame probabilities.
|
|
339
|
+
* @param audio - Float32Array in [-1, 1] or Int16Array of 16kHz mono PCM
|
|
340
|
+
*/
|
|
341
|
+
detectFull(audio) {
|
|
342
|
+
const M = getModule();
|
|
343
|
+
const f32 = prepareDetectFullAudio(audio);
|
|
344
|
+
const audioPtr = copyAudioToHeap(M, f32);
|
|
345
|
+
const probsPtrPtr = M._malloc(4);
|
|
346
|
+
const framesPtr = M._malloc(4);
|
|
347
|
+
try {
|
|
348
|
+
const ret = M.ccall(
|
|
349
|
+
"omni_vad_stream_detect_full",
|
|
350
|
+
"number",
|
|
351
|
+
["number", "number", "number", "number", "number"],
|
|
352
|
+
[this.handle, audioPtr, f32.length, probsPtrPtr, framesPtr]
|
|
353
|
+
);
|
|
354
|
+
if (ret !== 0) throw new Error(`StreamVAD detectFull failed: ${ret}`);
|
|
355
|
+
const numFrames = M.getValue(framesPtr, "i32");
|
|
356
|
+
const probsPtr = M.getValue(probsPtrPtr, "i32");
|
|
357
|
+
const probabilities = probsPtr ? new Float32Array(new Float32Array(M.HEAPU8.buffer, probsPtr, numFrames)) : new Float32Array(0);
|
|
358
|
+
if (probsPtr) M._free(probsPtr);
|
|
359
|
+
return {
|
|
360
|
+
probabilities,
|
|
361
|
+
numFrames,
|
|
362
|
+
duration: Math.round(f32.length / SAMPLE_RATE2 * 1e3) / 1e3
|
|
363
|
+
};
|
|
364
|
+
} finally {
|
|
365
|
+
M._free(audioPtr);
|
|
366
|
+
M._free(probsPtrPtr);
|
|
367
|
+
M._free(framesPtr);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
/** Reset all internal state. */
|
|
371
|
+
reset() {
|
|
372
|
+
streamVadReset(getModule(), this.handle);
|
|
373
|
+
this.inSpeech = false;
|
|
374
|
+
this.speechStartFrame = 0;
|
|
375
|
+
}
|
|
376
|
+
/** Release native resources. */
|
|
377
|
+
dispose() {
|
|
378
|
+
if (this.handle) {
|
|
379
|
+
streamVadDestroy(getModule(), this.handle);
|
|
380
|
+
this.handle = 0;
|
|
381
|
+
}
|
|
382
|
+
this.inSpeech = false;
|
|
383
|
+
this.speechStartFrame = 0;
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
function int16ToFloat32(i16) {
|
|
387
|
+
const f32 = new Float32Array(i16.length);
|
|
388
|
+
for (let i = 0; i < i16.length; i++) f32[i] = i16[i];
|
|
389
|
+
return f32;
|
|
390
|
+
}
|
|
391
|
+
function prepareDetectFullAudio(audio) {
|
|
392
|
+
if (audio instanceof Int16Array) {
|
|
393
|
+
return int16ToFloat32(audio);
|
|
394
|
+
}
|
|
395
|
+
if (isNormalizedFloat(audio)) {
|
|
396
|
+
const scaled = new Float32Array(audio.length);
|
|
397
|
+
for (let i = 0; i < audio.length; i++) scaled[i] = audio[i] * 32768;
|
|
398
|
+
return scaled;
|
|
399
|
+
}
|
|
400
|
+
return audio;
|
|
401
|
+
}
|
|
402
|
+
function isNormalizedFloat(audio) {
|
|
403
|
+
const step = Math.max(1, Math.floor(audio.length / 1e3));
|
|
404
|
+
let maxAbs = 0;
|
|
405
|
+
for (let i = 0; i < audio.length; i += step) {
|
|
406
|
+
const v = Math.abs(audio[i]);
|
|
407
|
+
if (v > maxAbs) maxAbs = v;
|
|
408
|
+
}
|
|
409
|
+
return maxAbs <= 1;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// src/aed.ts
|
|
413
|
+
var SAMPLE_RATE3 = 16e3;
|
|
414
|
+
var OmniAED = class _OmniAED {
|
|
415
|
+
constructor(handle, config) {
|
|
416
|
+
this.handle = handle;
|
|
417
|
+
this.config = config;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Create a new OmniAED instance.
|
|
421
|
+
* Initializes WASM and loads the bundled ncnn model.
|
|
422
|
+
*/
|
|
423
|
+
static async create(options = {}) {
|
|
424
|
+
await initWasm();
|
|
425
|
+
const M = getModule();
|
|
426
|
+
const handle = aedCreate(M);
|
|
427
|
+
const base = {
|
|
428
|
+
smoothWindowSize: options.smoothWindowSize ?? DEFAULT_VAD_CONFIG.smoothWindowSize,
|
|
429
|
+
minSpeechFrames: options.minSpeechFrames ?? DEFAULT_VAD_CONFIG.minSpeechFrames,
|
|
430
|
+
minSilenceFrames: options.minSilenceFrames ?? DEFAULT_VAD_CONFIG.minSilenceFrames,
|
|
431
|
+
maxSpeechFrames: options.maxSpeechFrames ?? DEFAULT_VAD_CONFIG.maxSpeechFrames,
|
|
432
|
+
mergeSilenceFrames: options.mergeSilenceFrames ?? DEFAULT_VAD_CONFIG.mergeSilenceFrames,
|
|
433
|
+
extendSpeechFrames: options.extendSpeechFrames ?? DEFAULT_VAD_CONFIG.extendSpeechFrames
|
|
434
|
+
};
|
|
435
|
+
const config = {
|
|
436
|
+
speech: { ...base, threshold: options.speechThreshold ?? 0.4 },
|
|
437
|
+
singing: { ...base, threshold: options.singingThreshold ?? 0.5 },
|
|
438
|
+
music: { ...base, threshold: options.musicThreshold ?? 0.5 }
|
|
439
|
+
};
|
|
440
|
+
return new _OmniAED(handle, config);
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Detect audio events (speech, singing, music).
|
|
444
|
+
*
|
|
445
|
+
* Accepts Int16Array (PCM) or normalized Float32Array in [-1, 1].
|
|
446
|
+
*/
|
|
447
|
+
detect(audio) {
|
|
448
|
+
const M = getModule();
|
|
449
|
+
const { ptr, length, format } = prepareAudio2(M, audio);
|
|
450
|
+
const duration = Math.round(length / SAMPLE_RATE3 * 1e3) / 1e3;
|
|
451
|
+
try {
|
|
452
|
+
const events = aedDetect(M, this.handle, ptr, length, this.config, format);
|
|
453
|
+
return {
|
|
454
|
+
duration,
|
|
455
|
+
events,
|
|
456
|
+
ratios: computeCoverageRatios(events, duration)
|
|
457
|
+
};
|
|
458
|
+
} finally {
|
|
459
|
+
M._free(ptr);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
/** Release native resources. */
|
|
463
|
+
dispose() {
|
|
464
|
+
if (this.handle) {
|
|
465
|
+
aedDestroy(getModule(), this.handle);
|
|
466
|
+
this.handle = 0;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
function prepareAudio2(M, audio) {
|
|
471
|
+
const f32 = audio instanceof Int16Array ? int16ToNormalizedFloat322(audio) : audio;
|
|
472
|
+
const ptr = M._malloc(f32.length * 4);
|
|
473
|
+
const heap = new Float32Array(M.HEAPU8.buffer, ptr, f32.length);
|
|
474
|
+
heap.set(f32);
|
|
475
|
+
return { ptr, length: f32.length, format: "f32" };
|
|
476
|
+
}
|
|
477
|
+
function int16ToNormalizedFloat322(i16) {
|
|
478
|
+
const f32 = new Float32Array(i16.length);
|
|
479
|
+
for (let i = 0; i < i16.length; i++) f32[i] = i16[i] / 32768;
|
|
480
|
+
return f32;
|
|
481
|
+
}
|
|
482
|
+
function computeCoverageRatios(events, duration) {
|
|
483
|
+
const ratios = {
|
|
484
|
+
speech: 0,
|
|
485
|
+
singing: 0,
|
|
486
|
+
music: 0
|
|
487
|
+
};
|
|
488
|
+
if (duration <= 0) return ratios;
|
|
489
|
+
for (const cls of Object.keys(ratios)) {
|
|
490
|
+
const covered = (events[cls] ?? []).reduce((sum, [start, end]) => sum + Math.max(0, end - start), 0);
|
|
491
|
+
ratios[cls] = Math.round(Math.min(1, covered / duration) * 1e6) / 1e6;
|
|
492
|
+
}
|
|
493
|
+
return ratios;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
exports.FireRedAED = OmniAED;
|
|
497
|
+
exports.FireRedStreamVAD = OmniStreamVAD;
|
|
498
|
+
exports.FireRedVAD = OmniVAD;
|
|
499
|
+
exports.OmniAED = OmniAED;
|
|
500
|
+
exports.OmniStreamVAD = OmniStreamVAD;
|
|
501
|
+
exports.OmniVAD = OmniVAD;
|
|
502
|
+
exports.initWasm = initWasm;
|
|
503
|
+
//# sourceMappingURL=index.cjs.map
|
|
504
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/wasm-binding.ts","../src/vad.ts","../src/stream-vad.ts","../src/aed.ts"],"names":["SAMPLE_RATE","prepareAudio","int16ToNormalizedFloat32"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Low-level WASM binding for omnivad C API.\n * Loads the Emscripten module and provides typed wrappers.\n */\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype EmscriptenModule = any;\n\nlet _module: EmscriptenModule | null = null;\nlet _loading: Promise<EmscriptenModule> | null = null;\n\n/** Post-processing config matching C struct OmniPostConfig (7 x i32/float, 28 bytes) */\nexport interface PostConfig {\n threshold: number;\n smoothWindowSize: number;\n minSpeechFrames: number;\n minSilenceFrames: number;\n maxSpeechFrames: number;\n mergeSilenceFrames: number;\n extendSpeechFrames: number;\n}\n\nconst SIZEOF_POST_CONFIG = 28; // 7 * 4 bytes\nconst SIZEOF_AED_POST_CONFIG = 3 * SIZEOF_POST_CONFIG; // 84 bytes\nconst SIZEOF_SEGMENT = 8; // start(f32) + end(f32)\nconst SIZEOF_AED_SEGMENT = 16; // start(f32) + end(f32) + cls(i32) + confidence(f32)\n\n/**\n * Initialize the WASM module. Call once before using any other functions.\n * Safe to call multiple times (returns cached module).\n */\nexport async function initWasm(\n wasmLocator?: (filename: string) => string,\n): Promise<EmscriptenModule> {\n if (_module) return _module;\n if (_loading) return _loading;\n\n _loading = (async () => {\n // Dynamic import of the Emscripten glue\n let createOmniVAD: (opts?: Record<string, unknown>) => Promise<EmscriptenModule>;\n let defaultLocateFile: ((filename: string) => string) | undefined;\n\n if (typeof globalThis.process?.versions?.node === \"string\") {\n // Node.js: use require for .cjs (avoids ESM detection issues)\n const { createRequire } = await import(/* webpackIgnore: true */ \"module\");\n const { dirname, join } = await import(\"path\");\n const req = createRequire(import.meta.url);\n const gluePath = req.resolve(\"../dist/wasm/omnivad.cjs\");\n const wasmDir = dirname(gluePath);\n createOmniVAD = req(gluePath);\n defaultLocateFile = (filename: string) => join(wasmDir, filename);\n } else {\n // Browser: dynamic import\n const glueUrl = new URL(\"../dist/wasm/omnivad.js\", import.meta.url);\n const mod = await import(/* webpackIgnore: true */ glueUrl.href);\n createOmniVAD = mod.default || mod;\n const wasmBaseUrl = new URL(\"./\", glueUrl);\n defaultLocateFile = (filename: string) => new URL(filename, wasmBaseUrl).toString();\n }\n\n const opts: Record<string, unknown> = {};\n const locateFile = wasmLocator ?? defaultLocateFile;\n if (locateFile) {\n opts.locateFile = (path: string) => locateFile(path);\n }\n\n _module = await createOmniVAD(opts);\n return _module!;\n })();\n\n return _loading;\n}\n\n/** Get the initialized WASM module (throws if not initialized) */\nexport function getModule(): EmscriptenModule {\n if (!_module) throw new Error(\"WASM not initialized. Call initWasm() first.\");\n return _module;\n}\n\n// -------------------------------------------------------------------------- //\n// Memory helpers //\n// -------------------------------------------------------------------------- //\n\n/** Copy Float32Array audio into WASM heap, returns pointer. Caller must free. */\nexport function copyAudioToHeap(M: EmscriptenModule, audio: Float32Array): number {\n const ptr = M._malloc(audio.length * 4);\n const heap = new Float32Array(M.HEAPU8.buffer, ptr, audio.length);\n heap.set(audio);\n return ptr;\n}\n\n/** Write PostConfig struct to WASM heap at ptr */\nexport function writePostConfig(M: EmscriptenModule, ptr: number, cfg: PostConfig): void {\n M.setValue(ptr + 0, cfg.threshold, \"float\");\n M.setValue(ptr + 4, cfg.smoothWindowSize, \"i32\");\n M.setValue(ptr + 8, cfg.minSpeechFrames, \"i32\");\n M.setValue(ptr + 12, cfg.minSilenceFrames, \"i32\");\n M.setValue(ptr + 16, cfg.maxSpeechFrames, \"i32\");\n M.setValue(ptr + 20, cfg.mergeSilenceFrames, \"i32\");\n M.setValue(ptr + 24, cfg.extendSpeechFrames, \"i32\");\n}\n\nexport const DEFAULT_VAD_CONFIG: PostConfig = {\n threshold: 0.4,\n smoothWindowSize: 5,\n minSpeechFrames: 20,\n minSilenceFrames: 20,\n maxSpeechFrames: 2000,\n mergeSilenceFrames: 0,\n extendSpeechFrames: 0,\n};\n\n// -------------------------------------------------------------------------- //\n// Non-stream VAD //\n// -------------------------------------------------------------------------- //\n\nexport function vadCreate(M: EmscriptenModule, bundlePath = \"models/vad.omnivad\"): number {\n const handle = M.ccall(\n \"omni_vad_nonstream_create_from_bundle\",\n \"number\",\n [\"string\"],\n [bundlePath],\n );\n if (!handle) throw new Error(\"Failed to create VAD model\");\n return handle;\n}\n\n/**\n * Audio format: two types only.\n * \"f32\" — float* in [-1.0, 1.0] (Web Audio API, soundfile, torch)\n * \"int16\" — int16_t* PCM (WAV files, microphones)\n */\nexport type AudioFormat = \"f32\" | \"int16\";\n\nfunction readSegments(M: EmscriptenModule, segPtrPtr: number, countPtr: number): Array<[number, number]> {\n const count = M.getValue(countPtr, \"i32\");\n const segPtr = M.getValue(segPtrPtr, \"i32\");\n const segments: Array<[number, number]> = [];\n for (let i = 0; i < count; i++) {\n const base = segPtr + i * SIZEOF_SEGMENT;\n segments.push([\n Math.round(M.getValue(base, \"float\") * 1000) / 1000,\n Math.round(M.getValue(base + 4, \"float\") * 1000) / 1000,\n ]);\n }\n if (segPtr) M._free(segPtr);\n return segments;\n}\n\nexport function vadDetect(\n M: EmscriptenModule,\n handle: number,\n audioPtr: number,\n numSamples: number,\n cfg: PostConfig,\n format: AudioFormat = \"f32\",\n): Array<[number, number]> {\n const cfgPtr = M._malloc(SIZEOF_POST_CONFIG);\n const segPtrPtr = M._malloc(4);\n const countPtr = M._malloc(4);\n const fn = format === \"int16\" ? \"omni_vad_nonstream_process_int16\" : \"omni_vad_nonstream_process\";\n\n try {\n writePostConfig(M, cfgPtr, cfg);\n const ret = M.ccall(\n fn,\n \"number\",\n [\"number\", \"number\", \"number\", \"number\", \"number\", \"number\"],\n [handle, audioPtr, numSamples, cfgPtr, segPtrPtr, countPtr],\n );\n if (ret !== 0) throw new Error(`VAD detect failed: ${ret}`);\n return readSegments(M, segPtrPtr, countPtr);\n } finally {\n M._free(cfgPtr);\n M._free(segPtrPtr);\n M._free(countPtr);\n }\n}\n\nexport function vadDestroy(M: EmscriptenModule, handle: number): void {\n M.ccall(\"omni_vad_nonstream_destroy\", null, [\"number\"], [handle]);\n}\n\n// -------------------------------------------------------------------------- //\n// Non-stream AED //\n// -------------------------------------------------------------------------- //\n\nconst AED_CLASSES: Record<number, string> = { 0: \"speech\", 1: \"singing\", 2: \"music\" };\n\nexport function aedCreate(M: EmscriptenModule, bundlePath = \"models/aed.omnivad\"): number {\n const handle = M.ccall(\n \"omni_aed_nonstream_create_from_bundle\",\n \"number\",\n [\"string\"],\n [bundlePath],\n );\n if (!handle) throw new Error(\"Failed to create AED model\");\n return handle;\n}\n\nexport interface AedPostConfig {\n speech: PostConfig;\n singing: PostConfig;\n music: PostConfig;\n}\n\nexport function aedDetect(\n M: EmscriptenModule,\n handle: number,\n audioPtr: number,\n numSamples: number,\n cfg: AedPostConfig,\n format: AudioFormat = \"f32\",\n): Record<string, Array<[number, number]>> {\n const cfgPtr = M._malloc(SIZEOF_AED_POST_CONFIG);\n const segPtrPtr = M._malloc(4);\n const countPtr = M._malloc(4);\n const fn = format === \"int16\" ? \"omni_aed_nonstream_process_int16\" : \"omni_aed_nonstream_process\";\n\n try {\n writePostConfig(M, cfgPtr, cfg.speech);\n writePostConfig(M, cfgPtr + SIZEOF_POST_CONFIG, cfg.singing);\n writePostConfig(M, cfgPtr + 2 * SIZEOF_POST_CONFIG, cfg.music);\n\n const ret = M.ccall(\n fn,\n \"number\",\n [\"number\", \"number\", \"number\", \"number\", \"number\", \"number\"],\n [handle, audioPtr, numSamples, cfgPtr, segPtrPtr, countPtr],\n );\n if (ret !== 0) throw new Error(`AED detect failed: ${ret}`);\n\n const count = M.getValue(countPtr, \"i32\");\n const segPtr = M.getValue(segPtrPtr, \"i32\");\n const events: Record<string, Array<[number, number]>> = {\n speech: [],\n singing: [],\n music: [],\n };\n for (let i = 0; i < count; i++) {\n const base = segPtr + i * SIZEOF_AED_SEGMENT;\n const cls = M.getValue(base + 8, \"i32\");\n const name = AED_CLASSES[cls];\n if (name && events[name]) {\n events[name].push([\n Math.round(M.getValue(base, \"float\") * 1000) / 1000,\n Math.round(M.getValue(base + 4, \"float\") * 1000) / 1000,\n ]);\n }\n }\n if (segPtr) M._free(segPtr);\n return events;\n } finally {\n M._free(cfgPtr);\n M._free(segPtrPtr);\n M._free(countPtr);\n }\n}\n\nexport function aedDestroy(M: EmscriptenModule, handle: number): void {\n M.ccall(\"omni_aed_nonstream_destroy\", null, [\"number\"], [handle]);\n}\n\n// -------------------------------------------------------------------------- //\n// Stream VAD //\n// -------------------------------------------------------------------------- //\n\nexport function streamVadCreate(\n M: EmscriptenModule,\n threshold = 0.5,\n bundlePath = \"models/stream-vad.omnivad\",\n): number {\n const handle = M.ccall(\n \"omni_vad_stream_create_from_bundle\",\n \"number\",\n [\"string\", \"number\"],\n [bundlePath, threshold],\n );\n if (!handle) throw new Error(\"Failed to create StreamVAD model\");\n return handle;\n}\n\nexport interface StreamVadResult {\n confidence: number;\n isSpeech: boolean;\n frameOffset: number;\n}\n\n/** Process one chunk of int16 PCM (160 samples = 10ms). Returns null if buffering. */\nexport function streamVadProcess(\n M: EmscriptenModule,\n handle: number,\n pcm16Ptr: number,\n numSamples: number,\n): StreamVadResult | null {\n // OmniVadStreamResult: { float confidence, bool is_speech, int frame_offset } = 12 bytes\n const resultPtr = M._malloc(12);\n try {\n const ret = M.ccall(\n \"omni_vad_stream_process\",\n \"number\",\n [\"number\", \"number\", \"number\", \"number\"],\n [handle, pcm16Ptr, numSamples, resultPtr],\n );\n if (ret === -6) return null; // OMNI_ERR_NO_FRAMES\n if (ret !== 0) throw new Error(`StreamVAD process failed: ${ret}`);\n return {\n confidence: M.getValue(resultPtr, \"float\"),\n isSpeech: M.getValue(resultPtr + 4, \"i8\") !== 0,\n frameOffset: M.getValue(resultPtr + 8, \"i32\"),\n };\n } finally {\n M._free(resultPtr);\n }\n}\n\nexport function streamVadReset(M: EmscriptenModule, handle: number): void {\n M.ccall(\"omni_vad_stream_reset\", null, [\"number\"], [handle]);\n}\n\nexport function streamVadDestroy(M: EmscriptenModule, handle: number): void {\n M.ccall(\"omni_vad_stream_destroy\", null, [\"number\"], [handle]);\n}\n","/**\n * Non-streaming Voice Activity Detection (WASM/ncnn backend).\n *\n * Audio format:\n * - Int16Array: raw 16-bit PCM, converted to normalized float internally\n * - Float32Array in [-1.0, 1.0]: normalized audio (Web Audio API format)\n */\n\nimport type { VADConfig, VADResult } from \"./types.js\";\nimport {\n initWasm,\n getModule,\n copyAudioToHeap,\n vadCreate,\n vadDetect,\n vadDestroy,\n DEFAULT_VAD_CONFIG,\n type AudioFormat,\n type PostConfig,\n} from \"./wasm-binding.js\";\n\nconst SAMPLE_RATE = 16000;\n\nexport class OmniVAD {\n private handle: number;\n private config: PostConfig;\n\n private constructor(handle: number, config: PostConfig) {\n this.handle = handle;\n this.config = config;\n }\n\n /**\n * Create a new OmniVAD instance.\n * Initializes WASM and loads the bundled ncnn model.\n */\n static async create(options: VADConfig = {}): Promise<OmniVAD> {\n await initWasm();\n const M = getModule();\n const handle = vadCreate(M);\n const config: PostConfig = {\n threshold: options.speechThreshold ?? DEFAULT_VAD_CONFIG.threshold,\n smoothWindowSize: options.smoothWindowSize ?? DEFAULT_VAD_CONFIG.smoothWindowSize,\n minSpeechFrames: options.minSpeechFrames ?? DEFAULT_VAD_CONFIG.minSpeechFrames,\n minSilenceFrames: options.minSilenceFrames ?? DEFAULT_VAD_CONFIG.minSilenceFrames,\n maxSpeechFrames: options.maxSpeechFrames ?? DEFAULT_VAD_CONFIG.maxSpeechFrames,\n mergeSilenceFrames: options.mergeSilenceFrames ?? DEFAULT_VAD_CONFIG.mergeSilenceFrames,\n extendSpeechFrames: options.extendSpeechFrames ?? DEFAULT_VAD_CONFIG.extendSpeechFrames,\n };\n return new OmniVAD(handle, config);\n }\n\n /**\n * Detect speech segments in audio.\n *\n * Accepts Int16Array (PCM) or normalized Float32Array in [-1, 1].\n */\n detect(audio: Float32Array | Int16Array): VADResult {\n const M = getModule();\n const { ptr, length, format } = prepareAudio(M, audio);\n\n try {\n const timestamps = vadDetect(M, this.handle, ptr, length, this.config, format);\n return {\n duration: Math.round((length / SAMPLE_RATE) * 1000) / 1000,\n timestamps,\n };\n } finally {\n M._free(ptr);\n }\n }\n\n /** Release native resources. */\n dispose(): void {\n if (this.handle) {\n vadDestroy(getModule(), this.handle);\n this.handle = 0;\n }\n }\n}\n\n/** Copy audio to WASM heap as normalized float audio. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction prepareAudio(M: any, audio: Float32Array | Int16Array): { ptr: number; length: number; format: AudioFormat } {\n const f32 = audio instanceof Int16Array ? int16ToNormalizedFloat32(audio) : audio;\n const ptr = copyAudioToHeap(M, f32);\n return { ptr, length: f32.length, format: \"f32\" };\n}\n\nfunction int16ToNormalizedFloat32(i16: Int16Array): Float32Array {\n const f32 = new Float32Array(i16.length);\n for (let i = 0; i < i16.length; i++) f32[i] = i16[i] / 32768;\n return f32;\n}\n","/**\n * Streaming Voice Activity Detection (WASM/ncnn backend).\n * Processes audio frame-by-frame (10ms chunks of 160 samples @ 16kHz).\n */\n\nimport type { StreamVADConfig, StreamVADFrameResult, StreamVADFullResult } from \"./types.js\";\nimport {\n initWasm,\n getModule,\n copyAudioToHeap,\n streamVadCreate,\n streamVadProcess,\n streamVadReset,\n streamVadDestroy,\n} from \"./wasm-binding.js\";\n\nconst SAMPLE_RATE = 16000;\n\nexport class OmniStreamVAD {\n private handle: number;\n private inSpeech = false;\n private speechStartFrame = 0;\n\n private constructor(handle: number) {\n this.handle = handle;\n }\n\n /**\n * Create a new OmniStreamVAD instance.\n * Initializes WASM and loads the bundled ncnn model.\n */\n static async create(options: StreamVADConfig = {}): Promise<OmniStreamVAD> {\n await initWasm();\n const M = getModule();\n const threshold = options.speechThreshold ?? 0.5;\n const handle = streamVadCreate(M, threshold);\n return new OmniStreamVAD(handle);\n }\n\n /**\n * Process one frame of audio (160 int16 samples = 10ms @ 16kHz).\n * Returns null until enough audio is accumulated.\n */\n processFrame(pcm160: Int16Array): StreamVADFrameResult | null {\n const M = getModule();\n const ptr = M._malloc(pcm160.length * 2);\n const heap16 = new Int16Array(M.HEAPU8.buffer, ptr, pcm160.length);\n heap16.set(pcm160);\n\n try {\n const result = streamVadProcess(M, this.handle, ptr, pcm160.length);\n if (!result || result.frameOffset === 0) return null;\n\n const frameIndex = result.frameOffset;\n const isSpeechStart = result.isSpeech && !this.inSpeech;\n const isSpeechEnd = !result.isSpeech && this.inSpeech;\n\n if (isSpeechStart) {\n this.speechStartFrame = frameIndex;\n }\n\n const activeSpeechStartFrame = isSpeechEnd ? this.speechStartFrame : result.isSpeech ? this.speechStartFrame : 0;\n const speechEndFrame = isSpeechEnd ? Math.max(1, frameIndex - 1) : 0;\n\n this.inSpeech = result.isSpeech;\n if (isSpeechEnd) {\n this.speechStartFrame = 0;\n }\n\n return {\n confidence: result.confidence,\n smoothedConfidence: result.confidence,\n isSpeech: result.isSpeech,\n frameIndex,\n isSpeechStart,\n isSpeechEnd,\n speechStartFrame: activeSpeechStartFrame,\n speechEndFrame,\n };\n } finally {\n M._free(ptr);\n }\n }\n\n /**\n * Process entire audio at once and return per-frame probabilities.\n * @param audio - Float32Array in [-1, 1] or Int16Array of 16kHz mono PCM\n */\n detectFull(audio: Float32Array | Int16Array): StreamVADFullResult {\n const M = getModule();\n const f32 = prepareDetectFullAudio(audio);\n const audioPtr = copyAudioToHeap(M, f32);\n const probsPtrPtr = M._malloc(4);\n const framesPtr = M._malloc(4);\n\n try {\n const ret = M.ccall(\n \"omni_vad_stream_detect_full\",\n \"number\",\n [\"number\", \"number\", \"number\", \"number\", \"number\"],\n [this.handle, audioPtr, f32.length, probsPtrPtr, framesPtr],\n );\n if (ret !== 0) throw new Error(`StreamVAD detectFull failed: ${ret}`);\n\n const numFrames = M.getValue(framesPtr, \"i32\");\n const probsPtr = M.getValue(probsPtrPtr, \"i32\");\n const probabilities = probsPtr\n ? new Float32Array(new Float32Array(M.HEAPU8.buffer, probsPtr, numFrames))\n : new Float32Array(0);\n if (probsPtr) M._free(probsPtr);\n\n return {\n probabilities,\n numFrames,\n duration: Math.round((f32.length / SAMPLE_RATE) * 1000) / 1000,\n };\n } finally {\n M._free(audioPtr);\n M._free(probsPtrPtr);\n M._free(framesPtr);\n }\n }\n\n /** Reset all internal state. */\n reset(): void {\n streamVadReset(getModule(), this.handle);\n this.inSpeech = false;\n this.speechStartFrame = 0;\n }\n\n /** Release native resources. */\n dispose(): void {\n if (this.handle) {\n streamVadDestroy(getModule(), this.handle);\n this.handle = 0;\n }\n this.inSpeech = false;\n this.speechStartFrame = 0;\n }\n}\n\nfunction int16ToFloat32(i16: Int16Array): Float32Array {\n const f32 = new Float32Array(i16.length);\n for (let i = 0; i < i16.length; i++) f32[i] = i16[i];\n return f32;\n}\n\nfunction prepareDetectFullAudio(audio: Float32Array | Int16Array): Float32Array {\n if (audio instanceof Int16Array) {\n return int16ToFloat32(audio);\n }\n if (isNormalizedFloat(audio)) {\n const scaled = new Float32Array(audio.length);\n for (let i = 0; i < audio.length; i++) scaled[i] = audio[i] * 32768;\n return scaled;\n }\n return audio;\n}\n\nfunction isNormalizedFloat(audio: Float32Array): boolean {\n const step = Math.max(1, Math.floor(audio.length / 1000));\n let maxAbs = 0;\n for (let i = 0; i < audio.length; i += step) {\n const v = Math.abs(audio[i]);\n if (v > maxAbs) maxAbs = v;\n }\n return maxAbs <= 1.0;\n}\n","/**\n * Audio Event Detection: speech, singing, music (WASM/ncnn backend).\n *\n * Audio format: same as OmniVAD — Int16Array or normalized Float32Array [-1, 1].\n */\n\nimport type { AEDConfig, AEDResult } from \"./types.js\";\nimport {\n initWasm,\n getModule,\n aedCreate,\n aedDetect,\n aedDestroy,\n DEFAULT_VAD_CONFIG,\n type AudioFormat,\n type AedPostConfig,\n} from \"./wasm-binding.js\";\n\nconst SAMPLE_RATE = 16000;\n\nexport class OmniAED {\n private handle: number;\n private config: AedPostConfig;\n\n private constructor(handle: number, config: AedPostConfig) {\n this.handle = handle;\n this.config = config;\n }\n\n /**\n * Create a new OmniAED instance.\n * Initializes WASM and loads the bundled ncnn model.\n */\n static async create(options: AEDConfig = {}): Promise<OmniAED> {\n await initWasm();\n const M = getModule();\n const handle = aedCreate(M);\n\n const base = {\n smoothWindowSize: options.smoothWindowSize ?? DEFAULT_VAD_CONFIG.smoothWindowSize,\n minSpeechFrames: options.minSpeechFrames ?? DEFAULT_VAD_CONFIG.minSpeechFrames,\n minSilenceFrames: options.minSilenceFrames ?? DEFAULT_VAD_CONFIG.minSilenceFrames,\n maxSpeechFrames: options.maxSpeechFrames ?? DEFAULT_VAD_CONFIG.maxSpeechFrames,\n mergeSilenceFrames: options.mergeSilenceFrames ?? DEFAULT_VAD_CONFIG.mergeSilenceFrames,\n extendSpeechFrames: options.extendSpeechFrames ?? DEFAULT_VAD_CONFIG.extendSpeechFrames,\n };\n\n const config: AedPostConfig = {\n speech: { ...base, threshold: options.speechThreshold ?? 0.4 },\n singing: { ...base, threshold: options.singingThreshold ?? 0.5 },\n music: { ...base, threshold: options.musicThreshold ?? 0.5 },\n };\n return new OmniAED(handle, config);\n }\n\n /**\n * Detect audio events (speech, singing, music).\n *\n * Accepts Int16Array (PCM) or normalized Float32Array in [-1, 1].\n */\n detect(audio: Float32Array | Int16Array): AEDResult {\n const M = getModule();\n const { ptr, length, format } = prepareAudio(M, audio);\n const duration = Math.round((length / SAMPLE_RATE) * 1000) / 1000;\n\n try {\n const events = aedDetect(M, this.handle, ptr, length, this.config, format);\n return {\n duration,\n events,\n ratios: computeCoverageRatios(events, duration),\n };\n } finally {\n M._free(ptr);\n }\n }\n\n /** Release native resources. */\n dispose(): void {\n if (this.handle) {\n aedDestroy(getModule(), this.handle);\n this.handle = 0;\n }\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction prepareAudio(M: any, audio: Float32Array | Int16Array): { ptr: number; length: number; format: AudioFormat } {\n const f32 = audio instanceof Int16Array ? int16ToNormalizedFloat32(audio) : audio;\n const ptr = M._malloc(f32.length * 4);\n const heap = new Float32Array(M.HEAPU8.buffer, ptr, f32.length);\n heap.set(f32);\n return { ptr, length: f32.length, format: \"f32\" };\n}\n\nfunction int16ToNormalizedFloat32(i16: Int16Array): Float32Array {\n const f32 = new Float32Array(i16.length);\n for (let i = 0; i < i16.length; i++) f32[i] = i16[i] / 32768;\n return f32;\n}\n\nfunction computeCoverageRatios(\n events: Record<string, Array<[number, number]>>,\n duration: number,\n): Record<string, number> {\n const ratios: Record<string, number> = {\n speech: 0,\n singing: 0,\n music: 0,\n };\n\n if (duration <= 0) return ratios;\n\n for (const cls of Object.keys(ratios)) {\n const covered = (events[cls] ?? []).reduce((sum, [start, end]) => sum + Math.max(0, end - start), 0);\n ratios[cls] = Math.round(Math.min(1, covered / duration) * 1e6) / 1e6;\n }\n\n return ratios;\n}\n"]}
|