react-native-wakeword-sid 1.1.55 → 1.1.56

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 +134 -19
  24. package/wakewords/index.d.ts +7 -1
  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.56",
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,10 @@
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;
7
8
 
8
9
  function stripFileScheme(uri) {
9
10
  if (!uri) return uri;
@@ -35,6 +36,18 @@ function normalizePathOrName(input) {
35
36
  return String(input);
36
37
  }
37
38
 
39
+ function assertIOS() {
40
+ if (Platform.OS !== 'ios') {
41
+ throw new Error('Speaker verification bridge is currently implemented for iOS only.');
42
+ }
43
+ }
44
+
45
+ function assertMethod(name) {
46
+ if (!KeyWordRNBridge?.[name]) {
47
+ throw new Error(`KeyWordRNBridge.${name} is not available (native not linked / iOS only?)`);
48
+ }
49
+ }
50
+
38
51
  export class SpeakerVerificationRNBridgeInstance {
39
52
  engineId;
40
53
 
@@ -57,13 +70,8 @@ export class SpeakerVerificationRNBridgeInstance {
57
70
  * { decisionThreshold, frameSize, tailSeconds, maxTailSeconds, cmn, expectedLayoutBDT, logLevel }
58
71
  */
59
72
  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
- }
73
+ assertIOS();
74
+ assertMethod('createSpeakerVerifier');
67
75
 
68
76
  const modelArg = normalizePathOrName(modelPathOrName);
69
77
  const jsonArg = normalizePathOrName(enrollmentJsonPathOrName);
@@ -87,13 +95,8 @@ export class SpeakerVerificationRNBridgeInstance {
87
95
  * - if true, clears internal streaming state before verification
88
96
  */
89
97
  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
- }
98
+ assertIOS();
99
+ assertMethod('verifySpeakerWavStreaming');
97
100
 
98
101
  const wavArg = normalizePathOrName(wavPathOrName);
99
102
 
@@ -105,15 +108,12 @@ export class SpeakerVerificationRNBridgeInstance {
105
108
  }
106
109
 
107
110
  async destroy() {
108
- if (!KeyWordRNBridge?.destroySpeakerVerifier) {
109
- throw new Error('KeyWordRNBridge.destroySpeakerVerifier is not available (native not linked / iOS only?)');
110
- }
111
111
 
112
112
  if (Platform.OS !== 'ios') {
113
113
  // no-op for now
114
114
  return { ok: true, engineId: this.engineId, noop: true };
115
115
  }
116
-
116
+ assertMethod('destroySpeakerVerifier');
117
117
  return await KeyWordRNBridge.destroySpeakerVerifier(this.engineId);
118
118
  }
119
119
  }
@@ -122,3 +122,118 @@ export class SpeakerVerificationRNBridgeInstance {
122
122
  export const createSpeakerVerificationInstance = async (engineId) => {
123
123
  return new SpeakerVerificationRNBridgeInstance(engineId);
124
124
  };
125
+
126
+ // ============================================================
127
+ // MARK: - Speaker Verification Mic Controller (Swift) - RN APIs
128
+ // ============================================================
129
+ export class SpeakerVerificationMicController {
130
+ controllerId;
131
+
132
+ constructor(controllerId) {
133
+ this.controllerId = controllerId;
134
+ }
135
+
136
+ async create(configJson) {
137
+ assertIOS();
138
+ assertMethod('createSpeakerVerificationMicController');
139
+ const cfg =
140
+ typeof configJson === 'string'
141
+ ? configJson
142
+ : JSON.stringify(configJson ?? {});
143
+ return await KeyWordRNBridge.createSpeakerVerificationMicController(
144
+ this.controllerId,
145
+ cfg
146
+ );
147
+ }
148
+
149
+ async beginOnboarding(enrollmentId, targetEmbeddingCount, reset = true) {
150
+ assertIOS();
151
+ assertMethod('svBeginOnboarding');
152
+ return await KeyWordRNBridge.svBeginOnboarding(
153
+ this.controllerId,
154
+ String(enrollmentId ?? ''),
155
+ Number(targetEmbeddingCount ?? 0),
156
+ !!reset
157
+ );
158
+ }
159
+
160
+ async getNextEmbeddingFromMic() {
161
+ assertIOS();
162
+ assertMethod('svGetNextEmbeddingFromMic');
163
+ return await KeyWordRNBridge.svGetNextEmbeddingFromMic(this.controllerId);
164
+ }
165
+
166
+ async finalizeOnboardingNow() {
167
+ assertIOS();
168
+ assertMethod('svFinalizeOnboardingNow');
169
+ return await KeyWordRNBridge.svFinalizeOnboardingNow(this.controllerId);
170
+ }
171
+
172
+ async setEnrollmentJson(enrollmentJson) {
173
+ assertIOS();
174
+ assertMethod('svSetEnrollmentJson');
175
+ // if caller passes object, stringify it (native expects JSON string)
176
+ const s =
177
+ typeof enrollmentJson === 'string'
178
+ ? enrollmentJson
179
+ : JSON.stringify(enrollmentJson ?? {});
180
+
181
+ return await KeyWordRNBridge.svSetEnrollmentJson(
182
+ this.controllerId,
183
+ s
184
+ );
185
+ }
186
+
187
+ async startVerifyFromMic(resetState = true) {
188
+ assertIOS();
189
+ assertMethod('svStartVerifyFromMic');
190
+ return await KeyWordRNBridge.svStartVerifyFromMic(
191
+ this.controllerId,
192
+ !!resetState
193
+ );
194
+ }
195
+
196
+ async stop() {
197
+ if (Platform.OS !== 'ios') {
198
+ return { ok: true, controllerId: this.controllerId, noop: true };
199
+ }
200
+ assertMethod('svStopMic');
201
+ return await KeyWordRNBridge.svStopMic(this.controllerId);
202
+ }
203
+
204
+ async destroy() {
205
+ if (Platform.OS !== 'ios') {
206
+ return { ok: true, controllerId: this.controllerId, noop: true };
207
+ }
208
+ assertMethod('destroySpeakerVerificationMicController');
209
+ return await KeyWordRNBridge.destroySpeakerVerificationMicController(this.controllerId);
210
+ }
211
+ }
212
+
213
+ export const createSpeakerVerificationMicController = async (controllerId) => {
214
+ return new SpeakerVerificationMicController(controllerId);
215
+ };
216
+
217
+ // ============================================================
218
+ // MARK: - Event helpers
219
+ // ============================================================
220
+
221
+ function onEvent(eventName, cb) {
222
+ if (!emitter) {
223
+ throw new Error('NativeEventEmitter unavailable: KeyWordRNBridge is not linked.');
224
+ }
225
+ const sub = emitter.addListener(eventName, cb);
226
+ return () => sub.remove();
227
+ }
228
+
229
+ export const onSpeakerVerificationOnboardingProgress = (cb) =>
230
+ onEvent('onSpeakerVerificationOnboardingProgress', cb);
231
+
232
+ export const onSpeakerVerificationOnboardingDone = (cb) =>
233
+ onEvent('onSpeakerVerificationOnboardingDone', cb);
234
+
235
+ export const onSpeakerVerificationVerifyResult = (cb) =>
236
+ onEvent('onSpeakerVerificationVerifyResult', cb);
237
+
238
+ export const onSpeakerVerificationError = (cb) =>
239
+ onEvent('onSpeakerVerificationError', cb);
@@ -9,8 +9,14 @@ 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 };
19
+ export { enableDucking, disableDucking, initAudioSessAndDuckManage, restartListeningAfterDucking, removeAllRNBridgeListeners, createKeyWordRNBridgeInstance, KeyWordRNBridgeInstance, setWakewordAudioRoutingConfig, createSpeakerVerificationInstance, SpeakerVerificationRNBridgeInstance, createSpeakerVerificationMicController, SpeakerVerificationMicController, onSpeakerVerificationOnboardingProgress, onSpeakerVerificationOnboardingDone, onSpeakerVerificationVerifyResult, onSpeakerVerificationError, useModel };
14
20
 
15
21
  // Re-export routing types for consumers:
16
22
  export type { AudioRoutingConfig, RouteConfigEntry } from './audioRoutingConfig';
@@ -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