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 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.18 READY</div>
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 = "detection-card flex items-center justify-between p-5 bg-white/5 backdrop-blur-md rounded-[1.5rem] border border-white/10 shadow-lg";
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 bg-emerald-500/10 flex items-center justify-center text-emerald-400 border border-emerald-500/20 shadow-inner">
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">${name.replace(/_/g, ' ')}</span>
375
- <span class="text-[9px] text-slate-500 font-black uppercase tracking-[0.1em]">Neural Activation Triggered</span>
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
- // FIXED: Power of two buffer
410
- state.processor = state.audioContext.createScriptProcessor(1024, 1, 1);
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 = async (e) => {
486
+ state.processor.onaudioprocess = (e) => {
416
487
  if (!state.isListening) return;
417
- const data = e.inputBuffer.getChannelData(0);
418
- const results = await state.model.predict(data);
419
- for (const [n, s] of Object.entries(results)) {
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";
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openwakeword-js",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "Port of openWakeWord to JavaScript/TypeScript using ONNX Runtime",
5
5
  "bin": {
6
6
  "openwakeword-js-setup": "scripts/download_models.js"
@@ -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.18)');
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:');