openwakeword-js 0.1.19 → 0.1.21
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/index.html +85 -19
- package/models/hello_deepa.onnx +0 -0
- package/package.json +1 -1
- package/scripts/download_models.js +2 -2
package/index.html
CHANGED
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
<meta charset="utf-8" />
|
|
6
6
|
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
|
|
7
7
|
<title>AI Wake Word Detector | OpenWakeWord JS</title>
|
|
8
|
+
<script type="importmap">
|
|
9
|
+
{
|
|
10
|
+
"imports": {
|
|
11
|
+
"onnxruntime-web": "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.20.1/dist/ort.mjs"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
</script>
|
|
15
|
+
|
|
8
16
|
<!-- Fonts -->
|
|
9
17
|
<link href="https://fonts.googleapis.com" rel="preconnect" />
|
|
10
18
|
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect" />
|
|
@@ -286,7 +294,7 @@
|
|
|
286
294
|
class="h-44 overflow-y-auto rounded-xl bg-[#030406] p-4 font-mono text-[10px] text-slate-600 border border-white/5 custom-scrollbar leading-relaxed">
|
|
287
295
|
<div
|
|
288
296
|
class="opacity-40 animate-pulse font-bold text-blue-900 border border-blue-900/40 p-1 inline-block rounded mb-2">
|
|
289
|
-
OPENWAKEWORD-JS V0.1.
|
|
297
|
+
OPENWAKEWORD-JS V0.1.20 READY</div>
|
|
290
298
|
</div>
|
|
291
299
|
</div>
|
|
292
300
|
</details>
|
|
@@ -305,9 +313,12 @@
|
|
|
305
313
|
model: null,
|
|
306
314
|
audioContext: null,
|
|
307
315
|
processor: null,
|
|
308
|
-
startTime: Date.now()
|
|
316
|
+
startTime: Date.now(),
|
|
317
|
+
lastActivationTime: 0,
|
|
318
|
+
cooldownSeconds: 2.0
|
|
309
319
|
};
|
|
310
320
|
|
|
321
|
+
|
|
311
322
|
const ui = {
|
|
312
323
|
btn: document.getElementById('toggleBtn'),
|
|
313
324
|
btnText: document.getElementById('btnText'),
|
|
@@ -348,9 +359,10 @@
|
|
|
348
359
|
wakewordModels: ['./models/hello_deepa.onnx', './models/namaste_deepa.onnx'],
|
|
349
360
|
melspectrogramModelPath: './models/melspectrogram.onnx',
|
|
350
361
|
embeddingModelPath: './models/embedding_model.onnx',
|
|
351
|
-
vadModelPath: './models/silero_vad.onnx',
|
|
352
362
|
inferenceFramework: 'onnx',
|
|
353
363
|
wasmPaths: './models/'
|
|
364
|
+
|
|
365
|
+
|
|
354
366
|
});
|
|
355
367
|
await state.model.init();
|
|
356
368
|
log('Neural intelligence hub connected.', 'SYSTEM');
|
|
@@ -363,16 +375,33 @@
|
|
|
363
375
|
|
|
364
376
|
function pushDetection(name, score) {
|
|
365
377
|
if (ui.empty) ui.empty.style.display = 'none';
|
|
378
|
+
const isHello = name.includes('hello_deepa');
|
|
379
|
+
const isNamaste = name.includes('namaste');
|
|
380
|
+
|
|
381
|
+
let displayText = name.replace(/_/g, ' ');
|
|
382
|
+
let langLabel = "Neural Activation Triggered";
|
|
383
|
+
let langColor = "text-slate-500";
|
|
384
|
+
|
|
385
|
+
if (isHello) {
|
|
386
|
+
displayText = "Hello Deepa Activated..";
|
|
387
|
+
langLabel = "Language: English";
|
|
388
|
+
langColor = "text-blue-400";
|
|
389
|
+
} else if (isNamaste) {
|
|
390
|
+
displayText = "Namaste Deepa Activated..";
|
|
391
|
+
langLabel = "Language: Nepali";
|
|
392
|
+
langColor = "text-red-400";
|
|
393
|
+
}
|
|
394
|
+
|
|
366
395
|
const card = document.createElement('div');
|
|
367
|
-
card.className = "
|
|
396
|
+
card.className = "group flex flex-row items-center justify-between p-5 bg-white/[0.03] border border-white/5 rounded-2xl hover:bg-white/[0.05] hover:border-white/10 transition-all duration-300 transform animate-in fade-in slide-in-from-right-4";
|
|
368
397
|
card.innerHTML = `
|
|
369
398
|
<div class="flex items-center gap-4">
|
|
370
|
-
<div class="size-12 rounded-full
|
|
399
|
+
<div class="size-12 rounded-full overflow-hidden flex items-center justify-center bg-emerald-500/10 text-emerald-500 border border-emerald-500/20 shadow-[0_0_20px_rgba(16,185,129,0.1)]">
|
|
371
400
|
<svg class="size-7" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path d="M5 13l4 4L19 7" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
372
401
|
</div>
|
|
373
402
|
<div class="flex flex-col">
|
|
374
|
-
<span class="text-white font-bold capitalize text-base tracking-tight">${
|
|
375
|
-
<span class="text-[9px]
|
|
403
|
+
<span class="text-white font-bold capitalize text-base tracking-tight">${displayText}</span>
|
|
404
|
+
<span class="text-[9px] ${langColor} font-black uppercase tracking-[0.1em]">${langLabel}</span>
|
|
376
405
|
</div>
|
|
377
406
|
</div>
|
|
378
407
|
<div class="flex flex-col items-end gap-1">
|
|
@@ -384,6 +413,7 @@
|
|
|
384
413
|
if (ui.stack.children.length > 5) ui.stack.lastElementChild.remove();
|
|
385
414
|
}
|
|
386
415
|
|
|
416
|
+
|
|
387
417
|
async function toggle() {
|
|
388
418
|
if (state.isListening) {
|
|
389
419
|
state.isListening = false;
|
|
@@ -406,26 +436,62 @@
|
|
|
406
436
|
state.audioContext = new AudioContext({ sampleRate: 16000 });
|
|
407
437
|
const source = state.audioContext.createMediaStreamSource(stream);
|
|
408
438
|
|
|
409
|
-
//
|
|
410
|
-
|
|
439
|
+
// Robust processing queue to prevent re-entrancy and state corruption
|
|
440
|
+
const audioQueue = [];
|
|
441
|
+
let isProcessing = false;
|
|
442
|
+
|
|
443
|
+
async function processQueue() {
|
|
444
|
+
if (isProcessing || audioQueue.length === 0) return;
|
|
445
|
+
isProcessing = true;
|
|
446
|
+
|
|
447
|
+
while (audioQueue.length > 0) {
|
|
448
|
+
const data = audioQueue.shift();
|
|
411
449
|
|
|
450
|
+
// Python-style Cooldown Check
|
|
451
|
+
const currentTime = Date.now();
|
|
452
|
+
if (currentTime - state.lastActivationTime < state.cooldownSeconds * 1000) {
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
try {
|
|
457
|
+
const results = await state.model.predict(data);
|
|
458
|
+
for (const [n, s] of Object.entries(results)) {
|
|
459
|
+
if (s > state.threshold) {
|
|
460
|
+
const clean = n.split('/').pop().replace('.onnx', '');
|
|
461
|
+
|
|
462
|
+
// Detection Triggered
|
|
463
|
+
state.lastActivationTime = Date.now();
|
|
464
|
+
pushDetection(clean, s);
|
|
465
|
+
log(`Match Found: ${clean} (${s.toFixed(2)})`, 'MATCH');
|
|
466
|
+
|
|
467
|
+
// Python-style Reset & Clear Queue
|
|
468
|
+
if (state.model.reset) state.model.reset();
|
|
469
|
+
audioQueue.length = 0; // Clear the queue
|
|
470
|
+
break; // Only trigger one wake word at a time
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
} catch (err) {
|
|
474
|
+
log(`Inference Error: ${err.message}`, 'ERROR');
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
isProcessing = false;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
state.processor = state.audioContext.createScriptProcessor(2048, 1, 1);
|
|
412
483
|
source.connect(state.processor);
|
|
413
484
|
state.processor.connect(state.audioContext.destination);
|
|
414
485
|
|
|
415
|
-
state.processor.onaudioprocess =
|
|
486
|
+
state.processor.onaudioprocess = (e) => {
|
|
416
487
|
if (!state.isListening) return;
|
|
417
|
-
const data = e.inputBuffer.getChannelData(0);
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
if (s > state.threshold) {
|
|
421
|
-
const clean = n.split('/').pop().replace('.onnx', '');
|
|
422
|
-
pushDetection(clean, s);
|
|
423
|
-
log(`Match Found: ${clean} (${s.toFixed(2)})`, 'MATCH');
|
|
424
|
-
}
|
|
425
|
-
}
|
|
488
|
+
const data = new Float32Array(e.inputBuffer.getChannelData(0));
|
|
489
|
+
audioQueue.push(data);
|
|
490
|
+
processQueue();
|
|
426
491
|
};
|
|
427
492
|
|
|
428
493
|
state.isListening = true;
|
|
494
|
+
|
|
429
495
|
ui.orbRing.classList.add('animate-pulse-ring');
|
|
430
496
|
ui.orbIcon.classList.remove('opacity-40');
|
|
431
497
|
ui.orbLabel.textContent = "listening";
|
package/models/hello_deepa.onnx
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@ const EXTERNAL_MODELS = {
|
|
|
14
14
|
'silero_vad.onnx': 'https://github.com/dscripka/openWakeWord/releases/download/v0.5.1/silero_vad.onnx'
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
const SAMPLE_MODELS = ['hello_deepa.onnx', 'namaste_deepa.onnx'];
|
|
17
|
+
const SAMPLE_MODELS = ['hello_deepa.onnx', 'hello_deepa_old.onnx', 'namaste_deepa.onnx'];
|
|
18
18
|
|
|
19
19
|
async function downloadFile(url, dest) {
|
|
20
20
|
return new Promise((resolve, reject) => {
|
|
@@ -100,7 +100,7 @@ async function main() {
|
|
|
100
100
|
copyIfExists(path.join(packageRoot, 'models', 'test.html'), path.join(MODELS_DIR, 'test.html'), 'Debug UI');
|
|
101
101
|
|
|
102
102
|
console.log('\n----------------------------------------------------');
|
|
103
|
-
console.log('SETUP COMPLETE (v0.1.
|
|
103
|
+
console.log('SETUP COMPLETE (v0.1.20)');
|
|
104
104
|
console.log('----------------------------------------------------');
|
|
105
105
|
console.log('Your precision AI wake word interface is ready.');
|
|
106
106
|
console.log('\nTo start the demo:');
|