react-native-wakeword-sid 1.1.100 → 1.1.102

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.
@@ -18,6 +18,13 @@ import java.io.File;
18
18
  import java.util.HashMap;
19
19
  import java.util.Map;
20
20
  import java.util.concurrent.*;
21
+ import android.content.res.AssetManager;
22
+ import java.io.InputStream;
23
+ import java.io.BufferedInputStream;
24
+ import java.io.ByteArrayOutputStream;
25
+ import java.io.IOException;
26
+ import java.util.Locale;
27
+ import java.util.Log;
21
28
 
22
29
  /**
23
30
  * React Native bridge for the speaker-id Android library.
@@ -105,7 +112,9 @@ public class SpeakerIdRNBridge extends ReactContextBaseJavaModule {
105
112
  }
106
113
 
107
114
  @ReactMethod
108
- public void initVerificationUsingCurrentConfig(Promise promise) {
115
+ public void initVerificationUsingCurrentConfig(String instanceId, Promise promise) {
116
+ SpeakerIdApi api = instances.get(instanceId);
117
+ if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
109
118
  try {
110
119
  boolean ok = api.initVerificationUsingCurrentConfig();
111
120
  promise.resolve(ok);
@@ -114,6 +123,59 @@ public class SpeakerIdRNBridge extends ReactContextBaseJavaModule {
114
123
  }
115
124
  }
116
125
 
126
+ // ======= External-audio cluster API (RN) =======
127
+
128
+ @ReactMethod
129
+ public void initCluster(String instanceId, int numOfEmb, Promise promise) {
130
+ SpeakerIdApi api = instances.get(instanceId);
131
+ if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
132
+ exec.submit(() -> {
133
+ try {
134
+ int cid = api.initCluster(numOfEmb);
135
+ main.post(() -> promise.resolve(cid));
136
+ } catch (Throwable t) {
137
+ main.post(() -> promise.reject("InitClusterError", t.getMessage(), t));
138
+ }
139
+ });
140
+ }
141
+
142
+ @ReactMethod
143
+ public void createAndPushEmbeddingsToCluster(String instanceId, int clusterId, ReadableArray pcmI16, int length, Promise promise) {
144
+ SpeakerIdApi api = instances.get(instanceId);
145
+ if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
146
+ // Convert RN int[] -> short[]
147
+ int n = Math.min(length, pcmI16.size());
148
+ final short[] block = new short[n];
149
+ for (int i = 0; i < n; i++) block[i] = (short) pcmI16.getInt(i);
150
+
151
+ exec.submit(() -> {
152
+ try {
153
+ api.createAndPushEmbeddingsToCluster(clusterId, block, n);
154
+ main.post(() -> promise.resolve(null));
155
+ } catch (Throwable t) {
156
+ main.post(() -> promise.reject("PushClusterError", t.getMessage(), t));
157
+ }
158
+ });
159
+ }
160
+
161
+ @ReactMethod
162
+ public void createAndVerifyEmbeddingsFromCluster(String instanceId, int clusterId, ReadableArray pcmI16, int length, Promise promise) {
163
+ SpeakerIdApi api = instances.get(instanceId);
164
+ if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
165
+ int n = Math.min(length, pcmI16.size());
166
+ final short[] block = new short[n];
167
+ for (int i = 0; i < n; i++) block[i] = (short) pcmI16.getInt(i);
168
+
169
+ exec.submit(() -> {
170
+ try {
171
+ float score = api.createAndVerifyEmbeddingsFromCluster(clusterId, block, n);
172
+ main.post(() -> promise.resolve(score));
173
+ } catch (Throwable t) {
174
+ main.post(() -> promise.reject("VerifyClusterError", t.getMessage(), t));
175
+ }
176
+ });
177
+ }
178
+
117
179
  @ReactMethod
118
180
  public void destroyInstance(String instanceId, Promise promise) {
119
181
  SpeakerIdApi api;
@@ -319,17 +381,31 @@ public class SpeakerIdRNBridge extends ReactContextBaseJavaModule {
319
381
  }
320
382
 
321
383
  // ======= WWD: create instance tuned for wake-word =======
384
+
385
+ private static final boolean DEBUG_SID_ASSETS = true; // set false for production
386
+
322
387
  @ReactMethod
323
388
  public void createInstanceWWD(String instanceId, Promise promise) {
324
389
  if (instances.containsKey(instanceId)) {
325
390
  promise.reject("InstanceExists", "Instance already exists: " + instanceId);
326
391
  return;
327
392
  }
393
+ Log.d("WWD DEBUG", "createInstanceWWD")
394
+
328
395
  exec.submit(() -> {
329
396
  try {
397
+ Log.d("WWD DEBUG", "createInstanceWWD2")
330
398
  SpeakerIdApi api = SpeakerIdApi.createWWD(reactContext);
399
+ Log.d("WWD DEBUG", "createInstanceWWD3")
331
400
  synchronized (instances) { instances.put(instanceId, api); }
401
+ Log.d("WWD DEBUG", "createInstanceWWD4")
332
402
  main.post(() -> promise.resolve(true));
403
+ // Then run debug async if enabled
404
+ if (DEBUG_SID_ASSETS) {
405
+ Log.d("WWD DEBUG", "Calling debugOnAssetsWWD")
406
+ exec.submit(() -> debugOnAssetsWWD(instanceId));
407
+ }
408
+
333
409
  } catch (Exception e) {
334
410
  main.post(() -> promise.reject("CreateWWDError", e.getMessage(), e));
335
411
  }
@@ -434,4 +510,79 @@ public class SpeakerIdRNBridge extends ReactContextBaseJavaModule {
434
510
  exec.shutdownNow();
435
511
  super.onCatalystInstanceDestroy();
436
512
  }
513
+
514
+ // ======= DEBUG harness (assets) =======
515
+
516
+ private void emitDebug(String msg) {
517
+ Log.d("WWD DEBUG", msg)
518
+ WritableMap m = Arguments.createMap();
519
+ m.putString("line", msg);
520
+ sendEvent("SpeakerIdDebug", m);
521
+ }
522
+
523
+ private void debugOnAssetsWWD(String instanceId) {
524
+ try {
525
+ SpeakerIdApi api = instances.get(instanceId);
526
+ if (api == null) { emitDebug("debugOnAssetsWWD: instance gone: " + instanceId); return; }
527
+
528
+ // 1) Init a small FIFO cluster (K=3)
529
+ final int K = 3;
530
+ int clusterId = api.initCluster(K);
531
+ emitDebug("WWD DEBUG: initCluster -> " + clusterId + " (K=" + K + ")");
532
+
533
+ // 2) Onboarding from assets
534
+ String[] onboard = new String[] {
535
+ "onboard1.raw","onboard2.raw","onboard3.raw","onboard4.raw"
536
+ };
537
+ for (int i = 0; i < onboard.length; i++) {
538
+ short[] pcm = readPcm16LeRawAsset(onboard[i]);
539
+ api.createAndPushEmbeddingsToCluster(clusterId, pcm, pcm.length);
540
+ String line = String.format(Locale.US, "WWD DEBUG: push #%d '%s' samples=%d", i+1, onboard[i], pcm.length);
541
+ emitDebug(line);
542
+ }
543
+ emitDebug("WWD DEBUG: onboarding done; cluster+mean saved.");
544
+
545
+ // 3) Verification from assets
546
+ String[] tests = new String[] {
547
+ "test1.raw","test2.raw","test3.raw","test4.raw","test5.raw","test6.raw","test7.raw"
548
+ };
549
+ for (String tf : tests) {
550
+ short[] pcm = readPcm16LeRawAsset(tf);
551
+ float score = api.createAndVerifyEmbeddingsFromCluster(clusterId, pcm, pcm.length);
552
+ String line = String.format(Locale.US, "WWD DEBUG: verify '%s' samples=%d -> score=%.4f",
553
+ tf, pcm.length, score);
554
+ emitDebug(line);
555
+ }
556
+ emitDebug("WWD DEBUG: verification pass completed.");
557
+ } catch (Throwable t) {
558
+ emitDebug("WWD DEBUG ERROR: " + t.getMessage());
559
+ }
560
+ }
561
+
562
+ /** Read RAW little-endian PCM16 asset → short[] (exactly your requested method) */
563
+ private short[] readPcm16LeRawAsset(String assetName) throws IOException {
564
+ AssetManager am = reactContext.getAssets();
565
+ try (InputStream in = am.open(assetName);
566
+ BufferedInputStream bis = new BufferedInputStream(in);
567
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(32 * 1024)) {
568
+
569
+ byte[] buf = new byte[8192];
570
+ int n;
571
+ while ((n = bis.read(buf)) != -1) {
572
+ baos.write(buf, 0, n);
573
+ }
574
+ byte[] all = baos.toByteArray();
575
+ if ((all.length & 1) != 0) {
576
+ throw new IOException("Asset RAW has odd byte length: " + assetName + " (" + all.length + " bytes)");
577
+ }
578
+ int samples = all.length / 2;
579
+ short[] pcm = new short[samples];
580
+ for (int i = 0, s = 0; i < all.length; i += 2, s++) {
581
+ int lo = (all[i] & 0xFF);
582
+ int hi = (all[i + 1] << 8);
583
+ pcm[s] = (short) (hi | lo);
584
+ }
585
+ return pcm;
586
+ }
587
+ }
437
588
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-wakeword-sid",
3
- "version": "1.1.100",
3
+ "version": "1.1.102",
4
4
  "description": "Voice/Wake-word detection library for React Native",
5
5
  "main": "wakewords/index.js",
6
6
  "types": "wakewords/index.d.ts",