react-native-wakeword-sid 1.1.55 → 1.1.57

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.
Files changed (25) hide show
  1. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/Headers/KeyWordDetection-Swift.h +45 -0
  2. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/KeyWordDetection +0 -0
  3. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios.abi.json +8485 -2271
  4. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios.private.swiftinterface +130 -0
  5. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  6. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios.swiftinterface +130 -0
  7. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Headers/KeyWordDetection-Swift.h +90 -0
  8. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/KeyWordDetection +0 -0
  9. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios-simulator.abi.json +8485 -2271
  10. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +130 -0
  11. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  12. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios-simulator.swiftinterface +130 -0
  13. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/x86_64-apple-ios-simulator.abi.json +8485 -2271
  14. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +130 -0
  15. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  16. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +130 -0
  17. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/_CodeSignature/CodeDirectory +0 -0
  18. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/_CodeSignature/CodeRequirements-1 +0 -0
  19. package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/_CodeSignature/CodeResources +33 -33
  20. package/ios/KeyWordRNBridge/KeyWordRNBridge.m +332 -2
  21. package/package.json +1 -1
  22. package/wakewords/SpeakerVerificationRNBridge.d.ts +17 -0
  23. package/wakewords/SpeakerVerificationRNBridge.js +156 -19
  24. package/wakewords/index.d.ts +7 -4
  25. package/wakewords/index.js +16 -0
@@ -104,6 +104,46 @@
104
104
  @implementation SVVerifierHolder
105
105
  @end
106
106
 
107
+ // ============================================================
108
+ // MARK: - Speaker Verification Mic Controller holder + delegate proxy
109
+ // ============================================================
110
+
111
+ @interface SVMicHolder : NSObject
112
+ @property (nonatomic, strong) id controller; // opaque Swift object (SpeakerVerificationMicController)
113
+ @property (nonatomic, strong) NSString *controllerId;
114
+ @property (nonatomic, strong) id delegateProxy; // ObjC proxy that receives Swift callbacks
115
+ @end
116
+ @implementation SVMicHolder @end
117
+
118
+ @interface SVMicDelegateProxy : NSObject
119
+ @property (nonatomic, weak) KeyWordRNBridge *bridge;
120
+ @property (nonatomic, strong) NSString *controllerId;
121
+ @end
122
+
123
+ @implementation SVMicDelegateProxy
124
+ - (void)svOnboardingProgress:(NSDictionary *)info {
125
+ NSMutableDictionary *m = info ? [info mutableCopy] : [NSMutableDictionary new];
126
+ m[@"controllerId"] = self.controllerId ?: @"";
127
+ [self.bridge sendEventWithName:@"onSpeakerVerificationOnboardingProgress" body:m];
128
+ }
129
+ - (void)svOnboardingDone:(NSDictionary *)info {
130
+ NSMutableDictionary *m = info ? [info mutableCopy] : [NSMutableDictionary new];
131
+ m[@"controllerId"] = self.controllerId ?: @"";
132
+ [self.bridge sendEventWithName:@"onSpeakerVerificationOnboardingDone" body:m];
133
+ }
134
+ - (void)svVerifyResult:(NSDictionary *)info {
135
+ NSMutableDictionary *m = info ? [info mutableCopy] : [NSMutableDictionary new];
136
+ m[@"controllerId"] = self.controllerId ?: @"";
137
+ [self.bridge sendEventWithName:@"onSpeakerVerificationVerifyResult" body:m];
138
+ }
139
+ - (void)svError:(NSDictionary *)info {
140
+ NSMutableDictionary *m = info ? [info mutableCopy] : [NSMutableDictionary new];
141
+ m[@"controllerId"] = self.controllerId ?: @"";
142
+ [self.bridge sendEventWithName:@"onSpeakerVerificationError" body:m];
143
+ }
144
+ @end
145
+
146
+
107
147
  // Resolve a file path from:
108
148
  // 1) absolute path (exists)
109
149
  // 2) main bundle resource "name.ext"
@@ -175,6 +215,44 @@ static Class SVFacadeClass(void) {
175
215
  return NSClassFromString(@"SpeakerVerificationRNFacade");
176
216
  }
177
217
 
218
+
219
+ // =========================
220
+ // MARK: - Mic controller dynamic bridge
221
+ // =========================
222
+
223
+ static id _Nullable SVCreateMicController(NSString *configJson, NSError **error) {
224
+ Class c = SVFacadeClass();
225
+ if (!c) {
226
+ if (error) *error = [NSError errorWithDomain:@"SV" code:10 userInfo:@{NSLocalizedDescriptionKey: @"Swift class SpeakerVerificationRNFacade not found"}];
227
+ return nil;
228
+ }
229
+ SEL sel = NSSelectorFromString(@"createMicControllerWithConfigJson:error:");
230
+ if (![c respondsToSelector:sel]) {
231
+ if (error) *error = [NSError errorWithDomain:@"SV" code:11 userInfo:@{NSLocalizedDescriptionKey: @"Missing selector createMicControllerWithConfigJson:error:"}];
232
+ return nil;
233
+ }
234
+ id (*msgSend)(id, SEL, NSString*, NSError**) = (void*)objc_msgSend;
235
+ return msgSend(c, sel, configJson, error);
236
+ }
237
+
238
+ static BOOL SVCallBool2(id obj, SEL sel, NSString *s1, NSInteger i1, BOOL b1, NSError **error) {
239
+ BOOL (*msgSend)(id, SEL, NSString*, NSInteger, BOOL, NSError**) = (void*)objc_msgSend;
240
+ return msgSend(obj, sel, s1, i1, b1, error);
241
+ }
242
+ static BOOL SVCallBool1(id obj, SEL sel, NSError **error) {
243
+ BOOL (*msgSend)(id, SEL, NSError**) = (void*)objc_msgSend;
244
+ return msgSend(obj, sel, error);
245
+ }
246
+ static BOOL SVCallBoolReset(id obj, SEL sel, BOOL b1, NSError **error) {
247
+ BOOL (*msgSend)(id, SEL, BOOL, NSError**) = (void*)objc_msgSend;
248
+ return msgSend(obj, sel, b1, error);
249
+ }
250
+ static void SVSetDelegate(id obj, id delegateObj) {
251
+ void (*msgSend)(id, SEL, id) = (void*)objc_msgSend;
252
+ msgSend(obj, NSSelectorFromString(@"setDelegate:"), delegateObj);
253
+ }
254
+
255
+
178
256
  static id _Nullable SVCreateEngine(NSString *modelPath, NSString *jsonPath, NSDictionary *options, NSError **error) {
179
257
  Class c = SVFacadeClass();
180
258
  if (!c) {
@@ -209,6 +287,7 @@ static NSDictionary * _Nullable SVVerifyWav(id engine, NSString *wavPath, BOOL r
209
287
 
210
288
  @property (nonatomic, strong) NSMutableDictionary *instances;
211
289
  @property (nonatomic, strong) NSMutableDictionary *speakerVerifiers; // { engineId: SVVerifierHolder }
290
+ @property (nonatomic, strong) NSMutableDictionary *speakerMicControllers; // { controllerId: SVMicHolder }
212
291
 
213
292
  @end
214
293
 
@@ -220,6 +299,7 @@ RCT_EXPORT_MODULE();
220
299
  if (self = [super init]) {
221
300
  _instances = [NSMutableDictionary new];
222
301
  _speakerVerifiers = [NSMutableDictionary new];
302
+ _speakerMicControllers = [NSMutableDictionary new];
223
303
  }
224
304
  return self;
225
305
  }
@@ -229,11 +309,17 @@ RCT_EXPORT_MODULE();
229
309
  return YES;
230
310
  }
231
311
 
312
+ // NOTE: Extend supported events with Speaker Verification mic events
232
313
  - (NSArray<NSString *> *)supportedEvents {
233
314
  return @[@"onKeywordDetectionEvent",
234
- @"onVADDetectionEvent"]; // NEW
315
+ @"onVADDetectionEvent",
316
+ @"onSpeakerVerificationOnboardingProgress",
317
+ @"onSpeakerVerificationOnboardingDone",
318
+ @"onSpeakerVerificationVerifyResult",
319
+ @"onSpeakerVerificationError"];
235
320
  }
236
321
 
322
+
237
323
  // ============================================================
238
324
  // MARK: - Speaker Verification (Swift) - RN APIs
239
325
  // ============================================================
@@ -340,6 +426,250 @@ RCT_EXPORT_METHOD(destroySpeakerVerifier:(NSString *)engineId
340
426
  resolve(@{ @"ok": @YES, @"engineId": engineId });
341
427
  }
342
428
 
429
+
430
+ // ============================================================
431
+ // MARK: - Speaker Verification Mic Controller (Swift) - RN APIs
432
+ // ============================================================
433
+
434
+ // Create mic controller from config JSON (SpeakerVerificationConfig).
435
+ // Returns controllerId.
436
+ RCT_EXPORT_METHOD(createSpeakerVerificationMicController:(NSString *)controllerId
437
+ configJson:(NSString *)configJson
438
+ resolver:(RCTPromiseResolveBlock)resolve
439
+ rejecter:(RCTPromiseRejectBlock)reject)
440
+ {
441
+ if (self.speakerMicControllers[controllerId]) {
442
+ reject(@"SVMicExists", [NSString stringWithFormat:@"Speaker mic controller already exists with ID: %@", controllerId], nil);
443
+ return;
444
+ }
445
+
446
+ dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
447
+ @autoreleasepool {
448
+ NSError *err = nil;
449
+ id ctrl = SVCreateMicController(configJson, &err);
450
+ if (err || !ctrl) {
451
+ reject(@"SVMicCreateError",
452
+ [NSString stringWithFormat:@"Failed to create mic controller: %@", err.localizedDescription ?: @"unknown"],
453
+ err);
454
+ return;
455
+ }
456
+
457
+ // Create delegate proxy that will forward Swift callbacks -> RN events
458
+ SVMicDelegateProxy *proxy = [SVMicDelegateProxy new];
459
+ proxy.bridge = self;
460
+ proxy.controllerId = controllerId;
461
+
462
+ // Set delegate dynamically (no header needed)
463
+ SVSetDelegate(ctrl, proxy);
464
+
465
+ SVMicHolder *h = [SVMicHolder new];
466
+ h.controllerId = controllerId;
467
+ h.controller = ctrl;
468
+ h.delegateProxy = proxy; // keep strong ref
469
+ self.speakerMicControllers[controllerId] = h;
470
+
471
+ resolve(@{ @"ok": @YES, @"controllerId": controllerId });
472
+ }
473
+ });
474
+ }
475
+
476
+ RCT_EXPORT_METHOD(destroySpeakerVerificationMicController:(NSString *)controllerId
477
+ resolver:(RCTPromiseResolveBlock)resolve
478
+ rejecter:(RCTPromiseRejectBlock)reject)
479
+ {
480
+ SVMicHolder *h = self.speakerMicControllers[controllerId];
481
+ if (!h) {
482
+ reject(@"SVMicNotFound", [NSString stringWithFormat:@"No speaker mic controller with ID: %@", controllerId], nil);
483
+ return;
484
+ }
485
+
486
+ // Best-effort stop (no throw)
487
+ if (h.controller && [h.controller respondsToSelector:NSSelectorFromString(@"stop")]) {
488
+ void (*msgSend)(id, SEL) = (void*)objc_msgSend;
489
+ msgSend(h.controller, NSSelectorFromString(@"stop"));
490
+ }
491
+
492
+ [self.speakerMicControllers removeObjectForKey:controllerId];
493
+ resolve(@{ @"ok": @YES, @"controllerId": controllerId });
494
+ }
495
+
496
+ // beginOnboarding(enrollmentId, targetEmbeddingCount, reset)
497
+ RCT_EXPORT_METHOD(svBeginOnboarding:(NSString *)controllerId
498
+ enrollmentId:(NSString *)enrollmentId
499
+ targetEmbeddingCount:(NSInteger)targetEmbeddingCount
500
+ reset:(BOOL)reset
501
+ resolver:(RCTPromiseResolveBlock)resolve
502
+ rejecter:(RCTPromiseRejectBlock)reject)
503
+ {
504
+ SVMicHolder *h = self.speakerMicControllers[controllerId];
505
+ if (!h || !h.controller) {
506
+ reject(@"SVMicNotFound", [NSString stringWithFormat:@"No speaker mic controller with ID: %@", controllerId], nil);
507
+ return;
508
+ }
509
+
510
+ dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
511
+ @autoreleasepool {
512
+ NSError *err = nil;
513
+ SEL sel = NSSelectorFromString(@"beginOnboardingWithEnrollmentId:targetEmbeddingCount:reset:error:");
514
+ if (![h.controller respondsToSelector:sel]) {
515
+ reject(@"SVMicMissingSelector", @"Mic controller missing beginOnboardingWithEnrollmentId:targetEmbeddingCount:reset:error:", nil);
516
+ return;
517
+ }
518
+ BOOL ok = SVCallBool2(h.controller, sel, enrollmentId, targetEmbeddingCount, reset, &err);
519
+ if (!ok || err) {
520
+ reject(@"SVMicBeginError",
521
+ [NSString stringWithFormat:@"beginOnboarding failed: %@", err.localizedDescription ?: @"unknown"],
522
+ err);
523
+ return;
524
+ }
525
+ resolve(@{ @"ok": @YES, @"controllerId": controllerId, @"enrollmentId": enrollmentId, @"target": @(targetEmbeddingCount) });
526
+ }
527
+ });
528
+ }
529
+
530
+ // getNextEmbeddingFromMic(): starts mic, collects ONE embedding, then stops mic.
531
+ // Result is delivered via event:
532
+ // onSpeakerVerificationOnboardingProgress / onSpeakerVerificationOnboardingDone
533
+ RCT_EXPORT_METHOD(svGetNextEmbeddingFromMic:(NSString *)controllerId
534
+ resolver:(RCTPromiseResolveBlock)resolve
535
+ rejecter:(RCTPromiseRejectBlock)reject)
536
+ {
537
+ SVMicHolder *h = self.speakerMicControllers[controllerId];
538
+ if (!h || !h.controller) {
539
+ reject(@"SVMicNotFound", [NSString stringWithFormat:@"No speaker mic controller with ID: %@", controllerId], nil);
540
+ return;
541
+ }
542
+
543
+ dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
544
+ @autoreleasepool {
545
+ NSError *err = nil;
546
+ SEL sel = NSSelectorFromString(@"getNextEmbeddingFromMicAndReturnError:");
547
+ if (![h.controller respondsToSelector:sel]) {
548
+ reject(@"SVMicMissingSelector", @"Mic controller missing getNextEmbeddingFromMicAndReturnError:", nil);
549
+ return;
550
+ }
551
+ BOOL ok = SVCallBool1(h.controller, sel, &err);
552
+ if (!ok || err) {
553
+ reject(@"SVMicGetNextError",
554
+ [NSString stringWithFormat:@"getNextEmbeddingFromMic failed: %@", err.localizedDescription ?: @"unknown"],
555
+ err);
556
+ return;
557
+ }
558
+ resolve(@{ @"ok": @YES, @"controllerId": controllerId });
559
+ }
560
+ });
561
+ }
562
+
563
+ // finalizeOnboardingNow(): forces finalize and emits onSpeakerVerificationOnboardingDone (from Swift delegate)
564
+ RCT_EXPORT_METHOD(svFinalizeOnboardingNow:(NSString *)controllerId
565
+ resolver:(RCTPromiseResolveBlock)resolve
566
+ rejecter:(RCTPromiseRejectBlock)reject)
567
+ {
568
+ SVMicHolder *h = self.speakerMicControllers[controllerId];
569
+ if (!h || !h.controller) {
570
+ reject(@"SVMicNotFound", [NSString stringWithFormat:@"No speaker mic controller with ID: %@", controllerId], nil);
571
+ return;
572
+ }
573
+ dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
574
+ @autoreleasepool {
575
+ NSError *err = nil;
576
+ SEL sel = NSSelectorFromString(@"finalizeOnboardingNowAndReturnError:");
577
+ if (![h.controller respondsToSelector:sel]) {
578
+ reject(@"SVMicMissingSelector", @"Mic controller missing finalizeOnboardingNowAndReturnError:", nil);
579
+ return;
580
+ }
581
+ BOOL ok = SVCallBool1(h.controller, sel, &err);
582
+ if (!ok || err) {
583
+ reject(@"SVMicFinalizeError",
584
+ [NSString stringWithFormat:@"finalizeOnboardingNow failed: %@", err.localizedDescription ?: @"unknown"],
585
+ err);
586
+ return;
587
+ }
588
+ resolve(@{ @"ok": @YES, @"controllerId": controllerId });
589
+ }
590
+ });
591
+ }
592
+
593
+ // setEnrollmentJson(enrollmentJson): sets enrollment for verification mode
594
+ RCT_EXPORT_METHOD(svSetEnrollmentJson:(NSString *)controllerId
595
+ enrollmentJson:(NSString *)enrollmentJson
596
+ resolver:(RCTPromiseResolveBlock)resolve
597
+ rejecter:(RCTPromiseRejectBlock)reject)
598
+ {
599
+ SVMicHolder *h = self.speakerMicControllers[controllerId];
600
+ if (!h || !h.controller) {
601
+ reject(@"SVMicNotFound", [NSString stringWithFormat:@"No speaker mic controller with ID: %@", controllerId], nil);
602
+ return;
603
+ }
604
+ dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
605
+ @autoreleasepool {
606
+ NSError *err = nil;
607
+ SEL sel = NSSelectorFromString(@"setEnrollmentJson:error:");
608
+ if (![h.controller respondsToSelector:sel]) {
609
+ reject(@"SVMicMissingSelector", @"Mic controller missing setEnrollmentJson:error:", nil);
610
+ return;
611
+ }
612
+ BOOL (*msgSend)(id, SEL, NSString*, NSError**) = (void*)objc_msgSend;
613
+ BOOL ok = msgSend(h.controller, sel, enrollmentJson, &err);
614
+ if (!ok || err) {
615
+ reject(@"SVMicSetEnrollError",
616
+ [NSString stringWithFormat:@"setEnrollmentJson failed: %@", err.localizedDescription ?: @"unknown"],
617
+ err);
618
+ return;
619
+ }
620
+ resolve(@{ @"ok": @YES, @"controllerId": controllerId });
621
+ }
622
+ });
623
+ }
624
+
625
+ // startVerifyFromMic(resetState): starts mic until a verification result is produced.
626
+ // Result delivered via event: onSpeakerVerificationVerifyResult
627
+ RCT_EXPORT_METHOD(svStartVerifyFromMic:(NSString *)controllerId
628
+ resetState:(BOOL)resetState
629
+ resolver:(RCTPromiseResolveBlock)resolve
630
+ rejecter:(RCTPromiseRejectBlock)reject)
631
+ {
632
+ SVMicHolder *h = self.speakerMicControllers[controllerId];
633
+ if (!h || !h.controller) {
634
+ reject(@"SVMicNotFound", [NSString stringWithFormat:@"No speaker mic controller with ID: %@", controllerId], nil);
635
+ return;
636
+ }
637
+ dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
638
+ @autoreleasepool {
639
+ NSError *err = nil;
640
+ SEL sel = NSSelectorFromString(@"startVerifyFromMicWithResetState:error:");
641
+ if (![h.controller respondsToSelector:sel]) {
642
+ reject(@"SVMicMissingSelector", @"Mic controller missing startVerifyFromMicWithResetState:error:", nil);
643
+ return;
644
+ }
645
+ BOOL ok = SVCallBoolReset(h.controller, sel, resetState, &err);
646
+ if (!ok || err) {
647
+ reject(@"SVMicStartVerifyError",
648
+ [NSString stringWithFormat:@"startVerifyFromMic failed: %@", err.localizedDescription ?: @"unknown"],
649
+ err);
650
+ return;
651
+ }
652
+ resolve(@{ @"ok": @YES, @"controllerId": controllerId, @"resetState": @(resetState) });
653
+ }
654
+ });
655
+ }
656
+
657
+ // stop(): stop mic immediately
658
+ RCT_EXPORT_METHOD(svStopMic:(NSString *)controllerId
659
+ resolver:(RCTPromiseResolveBlock)resolve
660
+ rejecter:(RCTPromiseRejectBlock)reject)
661
+ {
662
+ SVMicHolder *h = self.speakerMicControllers[controllerId];
663
+ if (!h || !h.controller) {
664
+ reject(@"SVMicNotFound", [NSString stringWithFormat:@"No speaker mic controller with ID: %@", controllerId], nil);
665
+ return;
666
+ }
667
+ if ([h.controller respondsToSelector:NSSelectorFromString(@"stop")]) {
668
+ void (*msgSend)(id, SEL) = (void*)objc_msgSend;
669
+ msgSend(h.controller, NSSelectorFromString(@"stop"));
670
+ }
671
+ resolve(@{ @"ok": @YES, @"controllerId": controllerId });
672
+ }
343
673
 
344
674
  RCT_EXPORT_METHOD(createInstanceMulti:(NSString *)instanceId
345
675
  modelPaths:(NSArray<NSString *> *)modelPaths
@@ -411,7 +741,7 @@ RCT_EXPORT_METHOD(setAudioRoutingConfig:(NSString *)jsonConfig
411
741
  RCT_EXPORT_METHOD(disableDucking:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
412
742
  {
413
743
  [AudioSessionAndDuckingManager.shared disableDucking];
414
- resolve(@"enabled");
744
+ resolve(@"disabled");
415
745
  }
416
746
 
417
747
  RCT_EXPORT_METHOD(initAudioSessAndDuckManage:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-wakeword-sid",
3
- "version": "1.1.55",
3
+ "version": "1.1.57",
4
4
  "description": "Voice/Wake-word detection library for React Native",
5
5
  "main": "wakewords/index.js",
6
6
  "types": "wakewords/index.d.ts",
@@ -30,3 +30,20 @@ export class SpeakerVerificationRNBridgeInstance {
30
30
  destroy(): Promise<any>;
31
31
  }
32
32
  export function createSpeakerVerificationInstance(engineId: any): Promise<SpeakerVerificationRNBridgeInstance>;
33
+ export class SpeakerVerificationMicController {
34
+ constructor(controllerId: any);
35
+ controllerId: any;
36
+ create(configJson: any): Promise<any>;
37
+ beginOnboarding(enrollmentId: any, targetEmbeddingCount: any, reset?: boolean): Promise<any>;
38
+ getNextEmbeddingFromMic(): Promise<any>;
39
+ finalizeOnboardingNow(): Promise<any>;
40
+ setEnrollmentJson(enrollmentJson: any): Promise<any>;
41
+ startVerifyFromMic(resetState?: boolean): Promise<any>;
42
+ stop(): Promise<any>;
43
+ destroy(): Promise<any>;
44
+ }
45
+ export function createSpeakerVerificationMicController(controllerId: any): Promise<SpeakerVerificationMicController>;
46
+ export function onSpeakerVerificationOnboardingProgress(cb: any): () => void;
47
+ export function onSpeakerVerificationOnboardingDone(cb: any): () => void;
48
+ export function onSpeakerVerificationVerifyResult(cb: any): () => void;
49
+ export function onSpeakerVerificationError(cb: any): () => void;
@@ -1,9 +1,15 @@
1
1
  // wakewords/SpeakerVerificationRNBridge.js
2
- import { NativeModules, Platform } from 'react-native';
2
+ import { NativeModules, NativeEventEmitter, Platform } from 'react-native';
3
3
  import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';
4
4
 
5
5
  // Keep using the SAME native module (KeyWordRNBridge.m already exports these methods)
6
6
  const { KeyWordRNBridge } = NativeModules;
7
+ const emitter = KeyWordRNBridge ? new NativeEventEmitter(KeyWordRNBridge) : null;
8
+ +const VERBOSE = true;
9
+ +const PFX = '[SVJS]';
10
+ +const ts = () => new Date().toISOString();
11
+ +function dbg(...args) { if (VERBOSE) console.log(ts(), PFX, ...args); }
12
+ +function dbgErr(...args) { console.log(ts(), PFX, '❌', ...args); }
7
13
 
8
14
  function stripFileScheme(uri) {
9
15
  if (!uri) return uri;
@@ -35,6 +41,18 @@ function normalizePathOrName(input) {
35
41
  return String(input);
36
42
  }
37
43
 
44
+ function assertIOS() {
45
+ if (Platform.OS !== 'ios') {
46
+ throw new Error('Speaker verification bridge is currently implemented for iOS only.');
47
+ }
48
+ }
49
+
50
+ function assertMethod(name) {
51
+ if (!KeyWordRNBridge?.[name]) {
52
+ throw new Error(`KeyWordRNBridge.${name} is not available (native not linked / iOS only?)`);
53
+ }
54
+ }
55
+
38
56
  export class SpeakerVerificationRNBridgeInstance {
39
57
  engineId;
40
58
 
@@ -57,17 +75,15 @@ export class SpeakerVerificationRNBridgeInstance {
57
75
  * { decisionThreshold, frameSize, tailSeconds, maxTailSeconds, cmn, expectedLayoutBDT, logLevel }
58
76
  */
59
77
  async create(modelPathOrName, enrollmentJsonPathOrName, options = {}) {
60
- if (!KeyWordRNBridge?.createSpeakerVerifier) {
61
- throw new Error('KeyWordRNBridge.createSpeakerVerifier is not available (native not linked / iOS only?)');
62
- }
63
-
64
- if (Platform.OS !== 'ios') {
65
- throw new Error('Speaker verification bridge is currently implemented for iOS only.');
66
- }
78
+ assertIOS();
79
+ assertMethod('createSpeakerVerifier');
67
80
 
68
81
  const modelArg = normalizePathOrName(modelPathOrName);
69
82
  const jsonArg = normalizePathOrName(enrollmentJsonPathOrName);
70
83
 
84
+ dbg('createSpeakerVerifier args:',
85
+ { engineId: this.engineId, modelArg, jsonArg, options });
86
+
71
87
  return await KeyWordRNBridge.createSpeakerVerifier(
72
88
  this.engineId,
73
89
  modelArg,
@@ -87,15 +103,11 @@ export class SpeakerVerificationRNBridgeInstance {
87
103
  * - if true, clears internal streaming state before verification
88
104
  */
89
105
  async verifyWavStreaming(wavPathOrName, resetState = true) {
90
- if (!KeyWordRNBridge?.verifySpeakerWavStreaming) {
91
- throw new Error('KeyWordRNBridge.verifySpeakerWavStreaming is not available (native not linked / iOS only?)');
92
- }
93
-
94
- if (Platform.OS !== 'ios') {
95
- throw new Error('Speaker verification bridge is currently implemented for iOS only.');
96
- }
106
+ assertIOS();
107
+ assertMethod('verifySpeakerWavStreaming');
97
108
 
98
109
  const wavArg = normalizePathOrName(wavPathOrName);
110
+ dbg('verifySpeakerWavStreaming args:', { engineId: this.engineId, wavArg, resetState: !!resetState });
99
111
 
100
112
  return await KeyWordRNBridge.verifySpeakerWavStreaming(
101
113
  this.engineId,
@@ -105,15 +117,12 @@ export class SpeakerVerificationRNBridgeInstance {
105
117
  }
106
118
 
107
119
  async destroy() {
108
- if (!KeyWordRNBridge?.destroySpeakerVerifier) {
109
- throw new Error('KeyWordRNBridge.destroySpeakerVerifier is not available (native not linked / iOS only?)');
110
- }
111
120
 
112
121
  if (Platform.OS !== 'ios') {
113
122
  // no-op for now
114
123
  return { ok: true, engineId: this.engineId, noop: true };
115
124
  }
116
-
125
+ assertMethod('destroySpeakerVerifier');
117
126
  return await KeyWordRNBridge.destroySpeakerVerifier(this.engineId);
118
127
  }
119
128
  }
@@ -122,3 +131,131 @@ export class SpeakerVerificationRNBridgeInstance {
122
131
  export const createSpeakerVerificationInstance = async (engineId) => {
123
132
  return new SpeakerVerificationRNBridgeInstance(engineId);
124
133
  };
134
+
135
+ // ============================================================
136
+ // MARK: - Speaker Verification Mic Controller (Swift) - RN APIs
137
+ // ============================================================
138
+ export class SpeakerVerificationMicController {
139
+ controllerId;
140
+
141
+ constructor(controllerId) {
142
+ this.controllerId = controllerId;
143
+ }
144
+
145
+ async create(configJson) {
146
+ assertIOS();
147
+ assertMethod('createSpeakerVerificationMicController');
148
+ const cfg =
149
+ typeof configJson === 'string'
150
+ ? configJson
151
+ : JSON.stringify(configJson ?? {});
152
+ // IMPORTANT: must pass a REAL JSON string, not "[object Object]"
153
+ const jsonStr = (typeof configJson === 'string')
154
+ ? configJson
155
+ : JSON.stringify(configJson ?? {});
156
+ dbg('createSpeakerVerificationMicController args:',
157
+ { controllerId: this.controllerId, jsonStrLen: jsonStr.length, jsonStr });
158
+ return await KeyWordRNBridge.createSpeakerVerificationMicController(this.controllerId, jsonStr);
159
+ }
160
+
161
+ async beginOnboarding(enrollmentId, targetEmbeddingCount, reset = true) {
162
+ assertIOS();
163
+ assertMethod('svBeginOnboarding');
164
+ dbg('svBeginOnboarding args:', { controllerId: this.controllerId, enrollmentId, targetEmbeddingCount, reset: !!reset });
165
+
166
+ return await KeyWordRNBridge.svBeginOnboarding(
167
+ this.controllerId,
168
+ String(enrollmentId ?? ''),
169
+ Number(targetEmbeddingCount ?? 0),
170
+ !!reset
171
+ );
172
+ }
173
+
174
+ async getNextEmbeddingFromMic() {
175
+ assertIOS();
176
+ assertMethod('svGetNextEmbeddingFromMic');
177
+ dbg('svGetNextEmbeddingFromMic args:', { controllerId: this.controllerId });
178
+
179
+ return await KeyWordRNBridge.svGetNextEmbeddingFromMic(this.controllerId);
180
+ }
181
+
182
+ async finalizeOnboardingNow() {
183
+ assertIOS();
184
+ assertMethod('svFinalizeOnboardingNow');
185
+ dbg('svFinalizeOnboardingNow args:', { controllerId: this.controllerId });
186
+
187
+ return await KeyWordRNBridge.svFinalizeOnboardingNow(this.controllerId);
188
+ }
189
+
190
+ async setEnrollmentJson(enrollmentJson) {
191
+ assertIOS();
192
+ assertMethod('svSetEnrollmentJson');
193
+ dbg('svSetEnrollmentJson args:', { controllerId: this.controllerId, len: String(enrollmentJson ?? '').length });
194
+
195
+ // if caller passes object, stringify it (native expects JSON string)
196
+ const s =
197
+ typeof enrollmentJson === 'string'
198
+ ? enrollmentJson
199
+ : JSON.stringify(enrollmentJson ?? {});
200
+
201
+ return await KeyWordRNBridge.svSetEnrollmentJson(
202
+ this.controllerId,
203
+ s
204
+ );
205
+ }
206
+
207
+ async startVerifyFromMic(resetState = true) {
208
+ assertIOS();
209
+ assertMethod('svStartVerifyFromMic');
210
+ dbg('svStartVerifyFromMic args:', { controllerId: this.controllerId, resetState: !!resetState });
211
+
212
+ return await KeyWordRNBridge.svStartVerifyFromMic(
213
+ this.controllerId,
214
+ !!resetState
215
+ );
216
+ }
217
+
218
+ async stop() {
219
+ if (Platform.OS !== 'ios') {
220
+ return { ok: true, controllerId: this.controllerId, noop: true };
221
+ }
222
+ assertMethod('svStopMic');
223
+ return await KeyWordRNBridge.svStopMic(this.controllerId);
224
+ }
225
+
226
+ async destroy() {
227
+ if (Platform.OS !== 'ios') {
228
+ return { ok: true, controllerId: this.controllerId, noop: true };
229
+ }
230
+ assertMethod('destroySpeakerVerificationMicController');
231
+ return await KeyWordRNBridge.destroySpeakerVerificationMicController(this.controllerId);
232
+ }
233
+ }
234
+
235
+ export const createSpeakerVerificationMicController = async (controllerId) => {
236
+ return new SpeakerVerificationMicController(controllerId);
237
+ };
238
+
239
+ // ============================================================
240
+ // MARK: - Event helpers
241
+ // ============================================================
242
+
243
+ function onEvent(eventName, cb) {
244
+ if (!emitter) {
245
+ throw new Error('NativeEventEmitter unavailable: KeyWordRNBridge is not linked.');
246
+ }
247
+ const sub = emitter.addListener(eventName, cb);
248
+ return () => sub.remove();
249
+ }
250
+
251
+ export const onSpeakerVerificationOnboardingProgress = (cb) =>
252
+ onEvent('onSpeakerVerificationOnboardingProgress', cb);
253
+
254
+ export const onSpeakerVerificationOnboardingDone = (cb) =>
255
+ onEvent('onSpeakerVerificationOnboardingDone', cb);
256
+
257
+ export const onSpeakerVerificationVerifyResult = (cb) =>
258
+ onEvent('onSpeakerVerificationVerifyResult', cb);
259
+
260
+ export const onSpeakerVerificationError = (cb) =>
261
+ onEvent('onSpeakerVerificationError', cb);
@@ -9,8 +9,11 @@ import { KeyWordRNBridgeInstance } from './KeyWordRNBridge';
9
9
  import { setWakewordAudioRoutingConfig } from './KeyWordRNBridge';
10
10
  import { createSpeakerVerificationInstance } from './SpeakerVerificationRNBridge';
11
11
  import { SpeakerVerificationRNBridgeInstance } from './SpeakerVerificationRNBridge';
12
+ import { createSpeakerVerificationMicController } from './SpeakerVerificationRNBridge';
13
+ import { SpeakerVerificationMicController } from './SpeakerVerificationRNBridge';
14
+ import { onSpeakerVerificationOnboardingProgress } from './SpeakerVerificationRNBridge';
15
+ import { onSpeakerVerificationOnboardingDone } from './SpeakerVerificationRNBridge';
16
+ import { onSpeakerVerificationVerifyResult } from './SpeakerVerificationRNBridge';
17
+ import { onSpeakerVerificationError } from './SpeakerVerificationRNBridge';
12
18
  import useModel from './useModel';
13
- export { enableDucking, disableDucking, initAudioSessAndDuckManage, restartListeningAfterDucking, removeAllRNBridgeListeners, createKeyWordRNBridgeInstance, KeyWordRNBridgeInstance, setWakewordAudioRoutingConfig, createSpeakerVerificationInstance, SpeakerVerificationRNBridgeInstance, useModel };
14
-
15
- // Re-export routing types for consumers:
16
- export type { AudioRoutingConfig, RouteConfigEntry } from './audioRoutingConfig';
19
+ export { enableDucking, disableDucking, initAudioSessAndDuckManage, restartListeningAfterDucking, removeAllRNBridgeListeners, createKeyWordRNBridgeInstance, KeyWordRNBridgeInstance, setWakewordAudioRoutingConfig, createSpeakerVerificationInstance, SpeakerVerificationRNBridgeInstance, createSpeakerVerificationMicController, SpeakerVerificationMicController, onSpeakerVerificationOnboardingProgress, onSpeakerVerificationOnboardingDone, onSpeakerVerificationVerifyResult, onSpeakerVerificationError, useModel };
@@ -16,6 +16,12 @@ import {
16
16
  import {
17
17
  createSpeakerVerificationInstance,
18
18
  SpeakerVerificationRNBridgeInstance,
19
+ createSpeakerVerificationMicController,
20
+ SpeakerVerificationMicController,
21
+ onSpeakerVerificationOnboardingProgress,
22
+ onSpeakerVerificationOnboardingDone,
23
+ onSpeakerVerificationVerifyResult,
24
+ onSpeakerVerificationError,
19
25
  } from './SpeakerVerificationRNBridge';
20
26
 
21
27
  export { enableDucking }
@@ -30,5 +36,15 @@ export { setWakewordAudioRoutingConfig } // ← NEW
30
36
  export { createSpeakerVerificationInstance }; // ✅ NEW
31
37
  export { SpeakerVerificationRNBridgeInstance }; // ✅ NEW
32
38
 
39
+ // ✅ NEW speaker verification mic controller exports
40
+ export { createSpeakerVerificationMicController };
41
+ export { SpeakerVerificationMicController };
42
+ export {
43
+ onSpeakerVerificationOnboardingProgress,
44
+ onSpeakerVerificationOnboardingDone,
45
+ onSpeakerVerificationVerifyResult,
46
+ onSpeakerVerificationError,
47
+ };
48
+
33
49
  export { useModel }; // Export only useModel
34
50
  export default useModel; // Allow default import