react-native-wakeword-sid 1.1.55
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/KeyWordRNBridge.podspec +58 -0
- package/LICENSE +21 -0
- package/README.md +281 -0
- package/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.9/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/build.gradle +48 -0
- package/android/libs/com/davoice/keyworddetection/1.0.0/keyworddetection-1.0.0.aar +0 -0
- package/android/libs/com/davoice/keyworddetection/1.0.0/keyworddetection-1.0.0.aar.md5 +1 -0
- package/android/libs/com/davoice/keyworddetection/1.0.0/keyworddetection-1.0.0.aar.sha1 +1 -0
- package/android/libs/com/davoice/keyworddetection/1.0.0/keyworddetection-1.0.0.pom +10 -0
- package/android/libs/com/davoice/keyworddetection/1.0.0/keyworddetection-1.0.0.pom.md5 +1 -0
- package/android/libs/com/davoice/keyworddetection/1.0.0/keyworddetection-1.0.0.pom.sha1 +1 -0
- package/android/settings.gradle +2 -0
- package/android/src/main/AndroidManifest.xml +13 -0
- package/android/src/main/assets/hey_lookdeep.dm +0 -0
- package/android/src/main/assets/layer1.dm +0 -0
- package/android/src/main/assets/need_help_now.dm +0 -0
- package/android/src/main/java/com/davoice/DaVoiceUnifiedPackage.java +29 -0
- package/android/src/main/java/com/davoice/keywordspotting/KeyWordRNBridge.java +335 -0
- package/android/src/main/java/com/davoice/speakeridrn/SpeakerIdRNBridge.java_not_used_yet +588 -0
- package/android/src/main/libs/MyLibrary-release.aar +0 -0
- package/app.plugin.js +60 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/Info.plist +44 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/Headers/KeyWordDetection-Swift.h +386 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/Headers/KeyWordDetection.h +18 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/Info.plist +0 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/KeyWordDetection +0 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios.abi.json +5758 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios.private.swiftinterface +159 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios.swiftinterface +159 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/Modules/module.modulemap +11 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Headers/KeyWordDetection-Swift.h +768 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Headers/KeyWordDetection.h +18 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Info.plist +0 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/KeyWordDetection +0 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios-simulator.abi.json +5758 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +159 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios-simulator.swiftinterface +159 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/x86_64-apple-ios-simulator.abi.json +5758 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +159 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +159 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/module.modulemap +11 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/_CodeSignature/CodeDirectory +0 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/_CodeSignature/CodeRequirements +0 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/_CodeSignature/CodeRequirements-1 +0 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/_CodeSignature/CodeResources +297 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/_CodeSignature/CodeSignature +0 -0
- package/ios/KeyWordRNBridge/KeyWordRNBridge.h +19 -0
- package/ios/KeyWordRNBridge/KeyWordRNBridge.m +642 -0
- package/ios/KeyWordRNBridge/KeyWordRNBridge.mm +416 -0
- package/ios/KeyWordRNBridge/models/coca_cola_model_28_05052025.onnx +0 -0
- package/ios/KeyWordRNBridge/models/embedding_model.onnx +0 -0
- package/ios/KeyWordRNBridge/models/hey_lookdeep.onnx +0 -0
- package/ios/KeyWordRNBridge/models/melspectrogram.onnx +0 -0
- package/ios/KeyWordRNBridge/models/need_help_now.onnx +0 -0
- package/ios/KeyWordRNBridge/models/silero_vad.onnx +0 -0
- package/package.json +74 -0
- package/react-native.config.js +10 -0
- package/wakewords/KeyWordRNBridge.d.ts +38 -0
- package/wakewords/KeyWordRNBridge.js +228 -0
- package/wakewords/SpeakerVerificationRNBridge.d.ts +32 -0
- package/wakewords/SpeakerVerificationRNBridge.js +124 -0
- package/wakewords/audioRoutingConfig.d.ts +17 -0
- package/wakewords/audioRoutingConfig.ts +28 -0
- package/wakewords/index.d.ts +16 -0
- package/wakewords/index.js +34 -0
- package/wakewords/useModel.d.ts +19 -0
- package/wakewords/useModel.tsx +235 -0
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
//ios/KeyWordRNBridge.m
|
|
2
|
+
|
|
3
|
+
#import "KeyWordRNBridge.h"
|
|
4
|
+
#import <React/RCTBridge.h>
|
|
5
|
+
#import <React/RCTLog.h>
|
|
6
|
+
#import <React/RCTEventEmitter.h>
|
|
7
|
+
//#import "KeyWordsDetection.h" // Import your KeyWordsDetection library header
|
|
8
|
+
// Speaker verification bridge (Swift) is called dynamically via objc_msgSend
|
|
9
|
+
#import <objc/message.h>
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
// Ensure the protocol is correctly imported or declared
|
|
13
|
+
// Assuming the protocol is named 'KeywordDetectionRNDelegate'
|
|
14
|
+
@interface KeyWordsDetectionWrapper : NSObject <KeywordDetectionRNDelegate>
|
|
15
|
+
|
|
16
|
+
@property (nonatomic, strong) KeyWordsDetection *keyWordsDetection;
|
|
17
|
+
@property (nonatomic, strong) NSString *instanceId;
|
|
18
|
+
@property (nonatomic, weak) KeyWordRNBridge *bridge;
|
|
19
|
+
|
|
20
|
+
- (instancetype)initWithInstanceId:(NSString *)instanceId
|
|
21
|
+
modelName:(NSString *)modelName
|
|
22
|
+
threshold:(float)threshold
|
|
23
|
+
bufferCnt:(NSInteger)bufferCnt
|
|
24
|
+
bridge:(KeyWordRNBridge *)bridge
|
|
25
|
+
error:(NSError **)error;
|
|
26
|
+
|
|
27
|
+
- (instancetype)initWithInstanceId:(NSString *)instanceId
|
|
28
|
+
modelNames:(NSArray<NSString *> *)modelNames
|
|
29
|
+
thresholds:(NSArray<NSNumber *> *)thresholds
|
|
30
|
+
bufferCnts:(NSArray<NSNumber *> *)bufferCnts
|
|
31
|
+
msBetweenCallback:(NSArray<NSNumber *> *)msBetweenCallback
|
|
32
|
+
bridge:(KeyWordRNBridge *)bridge
|
|
33
|
+
error:(NSError **)error;
|
|
34
|
+
|
|
35
|
+
@end
|
|
36
|
+
|
|
37
|
+
@implementation KeyWordsDetectionWrapper
|
|
38
|
+
|
|
39
|
+
- (instancetype)initWithInstanceId:(NSString *)instanceId
|
|
40
|
+
modelName:(NSString *)modelName
|
|
41
|
+
threshold:(float)threshold
|
|
42
|
+
bufferCnt:(NSInteger)bufferCnt
|
|
43
|
+
bridge:(KeyWordRNBridge *)bridge
|
|
44
|
+
error:(NSError **)error
|
|
45
|
+
{
|
|
46
|
+
if (self = [super init]) {
|
|
47
|
+
_instanceId = instanceId;
|
|
48
|
+
_bridge = bridge;
|
|
49
|
+
_keyWordsDetection = [[KeyWordsDetection alloc] initWithModelPath:modelName threshold:threshold bufferCnt:bufferCnt error:error];
|
|
50
|
+
if (*error) {
|
|
51
|
+
return nil;
|
|
52
|
+
}
|
|
53
|
+
_keyWordsDetection.delegate = self;
|
|
54
|
+
}
|
|
55
|
+
return self;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
- (instancetype)initWithInstanceId:(NSString *)instanceId
|
|
59
|
+
modelNames:(NSArray<NSString *> *)modelNames
|
|
60
|
+
thresholds:(NSArray<NSNumber *> *)thresholds
|
|
61
|
+
bufferCnts:(NSArray<NSNumber *> *)bufferCnts
|
|
62
|
+
msBetweenCallback:(NSArray<NSNumber *> *)msBetweenCallback
|
|
63
|
+
bridge:(KeyWordRNBridge *)bridge
|
|
64
|
+
error:(NSError **)error {
|
|
65
|
+
if (self = [super init]) {
|
|
66
|
+
_instanceId = instanceId;
|
|
67
|
+
_bridge = bridge;
|
|
68
|
+
|
|
69
|
+
NSMutableArray<NSNumber *> *floatThresholds = [NSMutableArray array];
|
|
70
|
+
for (NSNumber *num in thresholds) {
|
|
71
|
+
[floatThresholds addObject:@(num.floatValue)];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
_keyWordsDetection = [[KeyWordsDetection alloc] initWithModelPaths:modelNames
|
|
75
|
+
thresholds:floatThresholds
|
|
76
|
+
bufferCnts:bufferCnts
|
|
77
|
+
msBetweenCallback:msBetweenCallback
|
|
78
|
+
error:error];
|
|
79
|
+
if (*error) return nil;
|
|
80
|
+
_keyWordsDetection.delegate = self;
|
|
81
|
+
}
|
|
82
|
+
return self;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Implement the delegate method
|
|
86
|
+
- (void)KeywordDetectionDidDetectEvent:(NSDictionary *)eventInfo {
|
|
87
|
+
NSMutableDictionary *mutableEventInfo = [eventInfo mutableCopy];
|
|
88
|
+
mutableEventInfo[@"instanceId"] = self.instanceId;
|
|
89
|
+
[_bridge sendEventWithName:@"onKeywordDetectionEvent" body:mutableEventInfo];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@end
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
// ============================================================
|
|
96
|
+
// MARK: - Speaker Verification: native holder (opaque Swift object)
|
|
97
|
+
// ============================================================
|
|
98
|
+
|
|
99
|
+
@interface SVVerifierHolder : NSObject
|
|
100
|
+
@property (nonatomic, strong) id engine; // opaque Swift object
|
|
101
|
+
@property (nonatomic, strong) NSString *engineId;
|
|
102
|
+
@end
|
|
103
|
+
|
|
104
|
+
@implementation SVVerifierHolder
|
|
105
|
+
@end
|
|
106
|
+
|
|
107
|
+
// Resolve a file path from:
|
|
108
|
+
// 1) absolute path (exists)
|
|
109
|
+
// 2) main bundle resource "name.ext"
|
|
110
|
+
// 3) bundle scan by lastPathComponent (RN sometimes hashes folders)
|
|
111
|
+
// 4) if found in bundle but not a stable path, copy to tmp and return tmp path
|
|
112
|
+
static NSString * _Nullable SVResolveFilePath(NSString *input) {
|
|
113
|
+
if (!input || input.length == 0) return nil;
|
|
114
|
+
|
|
115
|
+
// Absolute path?
|
|
116
|
+
if ([input hasPrefix:@"/"] && [[NSFileManager defaultManager] fileExistsAtPath:input]) {
|
|
117
|
+
return input;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
NSString *fileName = [input lastPathComponent];
|
|
121
|
+
NSString *base = [fileName stringByDeletingPathExtension];
|
|
122
|
+
NSString *ext = [fileName pathExtension];
|
|
123
|
+
|
|
124
|
+
// Direct bundle lookup
|
|
125
|
+
NSString *p = [[NSBundle mainBundle] pathForResource:base ofType:ext.length ? ext : nil];
|
|
126
|
+
if (p && [[NSFileManager defaultManager] fileExistsAtPath:p]) {
|
|
127
|
+
return p;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Scan bundle for lastPathComponent (covers RN "assets/..." or nested resources)
|
|
131
|
+
NSArray<NSString *> *candidatesExt = ext.length ? @[ext] : @[@"onnx", @"json", @"wav"];
|
|
132
|
+
for (NSString *e in candidatesExt) {
|
|
133
|
+
NSArray<NSString *> *paths = [[NSBundle mainBundle] pathsForResourcesOfType:e inDirectory:nil];
|
|
134
|
+
for (NSString *pp in paths) {
|
|
135
|
+
if ([[pp lastPathComponent] isEqualToString:fileName]) {
|
|
136
|
+
return pp;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return nil;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
static NSString * _Nullable SVCopyToTempIfNeeded(NSString *path, NSString *preferredName) {
|
|
145
|
+
if (!path) return nil;
|
|
146
|
+
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
|
147
|
+
// Bundle paths are valid; ORT needs a file path, bundle path is fine.
|
|
148
|
+
// But keep copy logic for safety (some resources could be non-file URLs in edge cases).
|
|
149
|
+
return path;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Fallback: try to copy from bundle URL
|
|
153
|
+
NSURL *url = [[NSBundle mainBundle] URLForResource:[preferredName stringByDeletingPathExtension]
|
|
154
|
+
withExtension:[preferredName pathExtension]];
|
|
155
|
+
if (!url) return nil;
|
|
156
|
+
|
|
157
|
+
NSString *tmp = [NSTemporaryDirectory() stringByAppendingPathComponent:preferredName];
|
|
158
|
+
[[NSFileManager defaultManager] removeItemAtPath:tmp error:nil];
|
|
159
|
+
NSError *err = nil;
|
|
160
|
+
BOOL ok = [[NSFileManager defaultManager] copyItemAtURL:url toURL:[NSURL fileURLWithPath:tmp] error:&err];
|
|
161
|
+
if (!ok || err) return nil;
|
|
162
|
+
return tmp;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
static NSDictionary *SVErrDict(NSString *code, NSString *msg) {
|
|
166
|
+
return @{ @"code": code ?: @"Error", @"message": msg ?: @"Unknown error" };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Dynamic Swift bridge:
|
|
170
|
+
// Expect a Swift class annotated: @objc(SpeakerVerificationRNFacade)
|
|
171
|
+
// with ObjC-visible selectors:
|
|
172
|
+
// + (id)createEngineWithModelPath:(NSString*)modelPath enrollmentJsonPath:(NSString*)jsonPath options:(NSDictionary*)options error:(NSError**)error;
|
|
173
|
+
// + (NSDictionary*)verifyWavWithEngine:(id)engine wavPath:(NSString*)wavPath reset:(BOOL)reset error:(NSError**)error;
|
|
174
|
+
static Class SVFacadeClass(void) {
|
|
175
|
+
return NSClassFromString(@"SpeakerVerificationRNFacade");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
static id _Nullable SVCreateEngine(NSString *modelPath, NSString *jsonPath, NSDictionary *options, NSError **error) {
|
|
179
|
+
Class c = SVFacadeClass();
|
|
180
|
+
if (!c) {
|
|
181
|
+
if (error) *error = [NSError errorWithDomain:@"SV" code:1 userInfo:@{NSLocalizedDescriptionKey: @"Swift class SpeakerVerificationRNFacade not found (did you add it?)"}];
|
|
182
|
+
return nil;
|
|
183
|
+
}
|
|
184
|
+
SEL sel = NSSelectorFromString(@"createEngineWithModelPath:enrollmentJsonPath:options:error:");
|
|
185
|
+
if (![c respondsToSelector:sel]) {
|
|
186
|
+
if (error) *error = [NSError errorWithDomain:@"SV" code:2 userInfo:@{NSLocalizedDescriptionKey: @"Missing selector createEngineWithModelPath:enrollmentJsonPath:options:error:"}];
|
|
187
|
+
return nil;
|
|
188
|
+
}
|
|
189
|
+
id (*msgSend)(id, SEL, NSString*, NSString*, NSDictionary*, NSError**) = (void*)objc_msgSend;
|
|
190
|
+
return msgSend(c, sel, modelPath, jsonPath, options ?: @{}, error);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
static NSDictionary * _Nullable SVVerifyWav(id engine, NSString *wavPath, BOOL reset, NSError **error) {
|
|
194
|
+
Class c = SVFacadeClass();
|
|
195
|
+
if (!c) {
|
|
196
|
+
if (error) *error = [NSError errorWithDomain:@"SV" code:3 userInfo:@{NSLocalizedDescriptionKey: @"Swift class SpeakerVerificationRNFacade not found"}];
|
|
197
|
+
return nil;
|
|
198
|
+
}
|
|
199
|
+
SEL sel = NSSelectorFromString(@"verifyWavWithEngine:wavPath:reset:error:");
|
|
200
|
+
if (![c respondsToSelector:sel]) {
|
|
201
|
+
if (error) *error = [NSError errorWithDomain:@"SV" code:4 userInfo:@{NSLocalizedDescriptionKey: @"Missing selector verifyWavWithEngine:wavPath:reset:error:"}];
|
|
202
|
+
return nil;
|
|
203
|
+
}
|
|
204
|
+
NSDictionary* (*msgSend)(id, SEL, id, NSString*, BOOL, NSError**) = (void*)objc_msgSend;
|
|
205
|
+
return msgSend(c, sel, engine, wavPath, reset, error);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
@interface KeyWordRNBridge () <RCTBridgeModule>
|
|
209
|
+
|
|
210
|
+
@property (nonatomic, strong) NSMutableDictionary *instances;
|
|
211
|
+
@property (nonatomic, strong) NSMutableDictionary *speakerVerifiers; // { engineId: SVVerifierHolder }
|
|
212
|
+
|
|
213
|
+
@end
|
|
214
|
+
|
|
215
|
+
@implementation KeyWordRNBridge
|
|
216
|
+
|
|
217
|
+
RCT_EXPORT_MODULE();
|
|
218
|
+
|
|
219
|
+
- (instancetype)init {
|
|
220
|
+
if (self = [super init]) {
|
|
221
|
+
_instances = [NSMutableDictionary new];
|
|
222
|
+
_speakerVerifiers = [NSMutableDictionary new];
|
|
223
|
+
}
|
|
224
|
+
return self;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
+ (BOOL)requiresMainQueueSetup
|
|
228
|
+
{
|
|
229
|
+
return YES;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
- (NSArray<NSString *> *)supportedEvents {
|
|
233
|
+
return @[@"onKeywordDetectionEvent",
|
|
234
|
+
@"onVADDetectionEvent"]; // NEW
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ============================================================
|
|
238
|
+
// MARK: - Speaker Verification (Swift) - RN APIs
|
|
239
|
+
// ============================================================
|
|
240
|
+
|
|
241
|
+
// Create a speaker verifier engine from:
|
|
242
|
+
// - modelPathOrName: absolute path OR "speaker_model.onnx" in app bundle
|
|
243
|
+
// - enrollmentJsonPathOrName: absolute path OR "kesku_enrollment.json" in app bundle
|
|
244
|
+
//
|
|
245
|
+
// JS should call this once, then call verifySpeakerWavStreaming(engineId, wavPath)
|
|
246
|
+
RCT_EXPORT_METHOD(createSpeakerVerifier:(NSString *)engineId
|
|
247
|
+
modelPathOrName:(NSString *)modelPathOrName
|
|
248
|
+
enrollmentJsonPathOrName:(NSString *)enrollmentJsonPathOrName
|
|
249
|
+
options:(NSDictionary *)options
|
|
250
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
251
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
252
|
+
{
|
|
253
|
+
if (self.speakerVerifiers[engineId]) {
|
|
254
|
+
reject(@"SVEngineExists", [NSString stringWithFormat:@"Speaker verifier already exists with ID: %@", engineId], nil);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
259
|
+
@autoreleasepool {
|
|
260
|
+
NSString *modelResolved = SVResolveFilePath(modelPathOrName);
|
|
261
|
+
NSString *jsonResolved = SVResolveFilePath(enrollmentJsonPathOrName);
|
|
262
|
+
|
|
263
|
+
if (!modelResolved) {
|
|
264
|
+
reject(@"SVModelNotFound", [NSString stringWithFormat:@"Model file not found: %@", modelPathOrName], nil);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (!jsonResolved) {
|
|
268
|
+
reject(@"SVEnrollmentNotFound", [NSString stringWithFormat:@"Enrollment JSON not found: %@", enrollmentJsonPathOrName], nil);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Optional: ensure stable file path by copying to tmp if needed
|
|
273
|
+
NSString *modelPath = SVCopyToTempIfNeeded(modelResolved, [modelPathOrName lastPathComponent]) ?: modelResolved;
|
|
274
|
+
NSString *jsonPath = SVCopyToTempIfNeeded(jsonResolved, [enrollmentJsonPathOrName lastPathComponent]) ?: jsonResolved;
|
|
275
|
+
|
|
276
|
+
NSError *err = nil;
|
|
277
|
+
id engine = SVCreateEngine(modelPath, jsonPath, options ?: @{}, &err);
|
|
278
|
+
if (err || !engine) {
|
|
279
|
+
reject(@"SVCreateError",
|
|
280
|
+
[NSString stringWithFormat:@"Failed to create speaker verifier: %@", err.localizedDescription ?: @"unknown"],
|
|
281
|
+
err);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
SVVerifierHolder *h = [SVVerifierHolder new];
|
|
286
|
+
h.engineId = engineId;
|
|
287
|
+
h.engine = engine;
|
|
288
|
+
self.speakerVerifiers[engineId] = h;
|
|
289
|
+
resolve(@{ @"ok": @YES, @"engineId": engineId, @"modelPath": modelPath, @"enrollmentJsonPath": jsonPath });
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Verify a WAV file by streaming frames internally via the Swift engine.
|
|
295
|
+
// wavPathOrName can be absolute path OR "test.wav" in bundle.
|
|
296
|
+
RCT_EXPORT_METHOD(verifySpeakerWavStreaming:(NSString *)engineId
|
|
297
|
+
wavPathOrName:(NSString *)wavPathOrName
|
|
298
|
+
resetState:(BOOL)resetState
|
|
299
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
300
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
301
|
+
{
|
|
302
|
+
SVVerifierHolder *h = self.speakerVerifiers[engineId];
|
|
303
|
+
if (!h || !h.engine) {
|
|
304
|
+
reject(@"SVEngineNotFound", [NSString stringWithFormat:@"No speaker verifier with ID: %@", engineId], nil);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
309
|
+
@autoreleasepool {
|
|
310
|
+
NSString *wavResolved = SVResolveFilePath(wavPathOrName);
|
|
311
|
+
if (!wavResolved) {
|
|
312
|
+
reject(@"SVWavNotFound", [NSString stringWithFormat:@"WAV not found: %@", wavPathOrName], nil);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
NSString *wavPath = SVCopyToTempIfNeeded(wavResolved, [wavPathOrName lastPathComponent]) ?: wavResolved;
|
|
316
|
+
|
|
317
|
+
NSError *err = nil;
|
|
318
|
+
NSDictionary *out = SVVerifyWav(h.engine, wavPath, resetState, &err);
|
|
319
|
+
if (err || !out) {
|
|
320
|
+
reject(@"SVVerifyError",
|
|
321
|
+
[NSString stringWithFormat:@"Failed to verify wav: %@", err.localizedDescription ?: @"unknown"],
|
|
322
|
+
err);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
resolve(out);
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
RCT_EXPORT_METHOD(destroySpeakerVerifier:(NSString *)engineId
|
|
331
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
332
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
333
|
+
{
|
|
334
|
+
SVVerifierHolder *h = self.speakerVerifiers[engineId];
|
|
335
|
+
if (!h) {
|
|
336
|
+
reject(@"SVEngineNotFound", [NSString stringWithFormat:@"No speaker verifier with ID: %@", engineId], nil);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
[self.speakerVerifiers removeObjectForKey:engineId];
|
|
340
|
+
resolve(@{ @"ok": @YES, @"engineId": engineId });
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
RCT_EXPORT_METHOD(createInstanceMulti:(NSString *)instanceId
|
|
345
|
+
modelPaths:(NSArray<NSString *> *)modelPaths
|
|
346
|
+
thresholds:(NSArray<NSNumber *> *)thresholds
|
|
347
|
+
bufferCnts:(NSArray<NSNumber *> *)bufferCnts
|
|
348
|
+
msBetweenCallback:(NSArray<NSNumber *> *)msBetweenCallback
|
|
349
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
350
|
+
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
351
|
+
if (self.instances[instanceId]) {
|
|
352
|
+
reject(@"InstanceExists", [NSString stringWithFormat:@"Instance already exists with ID: %@", instanceId], nil);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
NSError *error = nil;
|
|
357
|
+
KeyWordsDetectionWrapper *wrapper = [[KeyWordsDetectionWrapper alloc]
|
|
358
|
+
initWithInstanceId:instanceId
|
|
359
|
+
modelNames:modelPaths
|
|
360
|
+
thresholds:thresholds
|
|
361
|
+
bufferCnts:bufferCnts
|
|
362
|
+
msBetweenCallback:msBetweenCallback
|
|
363
|
+
bridge:self
|
|
364
|
+
error:&error];
|
|
365
|
+
if (error) {
|
|
366
|
+
reject(@"CreateError", [NSString stringWithFormat:@"Failed to create multi-model instance: %@", error.localizedDescription], nil);
|
|
367
|
+
} else {
|
|
368
|
+
self.instances[instanceId] = wrapper;
|
|
369
|
+
resolve([NSString stringWithFormat:@"Multi-model instance created with ID: %@", instanceId]);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
RCT_EXPORT_METHOD(createInstance:(NSString *)instanceId modelName:(NSString *)modelName threshold:(float)threshold bufferCnt:(NSInteger)bufferCnt resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
374
|
+
{
|
|
375
|
+
if (self.instances[instanceId]) {
|
|
376
|
+
reject(@"InstanceExists", [NSString stringWithFormat:@"Instance already exists with ID: %@", instanceId], nil);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
NSError *error = nil;
|
|
381
|
+
KeyWordsDetectionWrapper *wrapper = [[KeyWordsDetectionWrapper alloc] initWithInstanceId:instanceId modelName:modelName threshold:threshold bufferCnt:bufferCnt bridge:self error:&error];
|
|
382
|
+
if (error) {
|
|
383
|
+
reject(@"CreateError", [NSString stringWithFormat:@"Failed to create instance: %@", error.localizedDescription], nil);
|
|
384
|
+
} else {
|
|
385
|
+
self.instances[instanceId] = wrapper;
|
|
386
|
+
resolve([NSString stringWithFormat:@"Instance created with ID: %@", instanceId]);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// NEW: receive global wakeword audio routing config from JS
|
|
391
|
+
RCT_EXPORT_METHOD(setAudioRoutingConfig:(NSString *)jsonConfig
|
|
392
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
393
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
394
|
+
{
|
|
395
|
+
@try {
|
|
396
|
+
// Hand off to your audio/session manager (you implement this)
|
|
397
|
+
// e.g. in AudioSessionAndDuckingManager:
|
|
398
|
+
// - (void)setWakewordAudioRoutingConfigFromJSONString:(NSString *)jsonConfig;
|
|
399
|
+
[AudioSessionAndDuckingManager.shared setWakewordAudioRoutingConfigFromJSONString:jsonConfig];
|
|
400
|
+
|
|
401
|
+
NSLog(@"[KeyWordRNBridge] setAudioRoutingConfig JSON = %@", jsonConfig);
|
|
402
|
+
resolve(@"ok");
|
|
403
|
+
}
|
|
404
|
+
@catch (NSException *e) {
|
|
405
|
+
reject(@"AudioRoutingConfigError",
|
|
406
|
+
[NSString stringWithFormat:@"Failed to set audio routing config: %@", e.reason],
|
|
407
|
+
nil);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
RCT_EXPORT_METHOD(disableDucking:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
412
|
+
{
|
|
413
|
+
[AudioSessionAndDuckingManager.shared disableDucking];
|
|
414
|
+
resolve(@"enabled");
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
RCT_EXPORT_METHOD(initAudioSessAndDuckManage:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
418
|
+
{
|
|
419
|
+
[AudioSessionAndDuckingManager.shared initAudioSessAndDuckManage];
|
|
420
|
+
resolve(@"enabled");
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
RCT_EXPORT_METHOD(restartListeningAfterDucking:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
424
|
+
{
|
|
425
|
+
[AudioSessionAndDuckingManager.shared restartListeningAfterDucking];
|
|
426
|
+
resolve(@"disabled");
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
RCT_EXPORT_METHOD(enableAggressiveDucking:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
430
|
+
{
|
|
431
|
+
[AudioSessionAndDuckingManager.shared enableAggressiveDucking];
|
|
432
|
+
resolve(@"enabled");
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
RCT_EXPORT_METHOD(disableDuckingAndCleanup:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
436
|
+
{
|
|
437
|
+
[AudioSessionAndDuckingManager.shared disableDuckingAndCleanup];
|
|
438
|
+
resolve(@"disabled");
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
RCT_EXPORT_METHOD(setKeywordDetectionLicense:(NSString *)instanceId licenseKey:(NSString *)licenseKey resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
442
|
+
{
|
|
443
|
+
KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
|
|
444
|
+
KeyWordsDetection *instance = wrapper.keyWordsDetection;
|
|
445
|
+
BOOL isLicensed = NO;
|
|
446
|
+
if (instance) {
|
|
447
|
+
isLicensed = [instance setLicenseWithLicenseKey:licenseKey];
|
|
448
|
+
NSLog(@"License is valid?: %@", isLicensed ? @"YES" : @"NO");
|
|
449
|
+
resolve(@(isLicensed)); // Wrap BOOL in NSNumber
|
|
450
|
+
} else {
|
|
451
|
+
reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
RCT_EXPORT_METHOD(replaceKeywordDetectionModel:(NSString *)instanceId modelName:(NSString *)modelName threshold:(float)threshold bufferCnt:(NSInteger)bufferCnt resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
456
|
+
{
|
|
457
|
+
KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
|
|
458
|
+
KeyWordsDetection *instance = wrapper.keyWordsDetection;
|
|
459
|
+
if (instance) {
|
|
460
|
+
NSError *error = nil;
|
|
461
|
+
[instance replaceKeywordDetectionModelWithModelPath:modelName threshold:threshold bufferCnt:bufferCnt error:&error];
|
|
462
|
+
if (error) {
|
|
463
|
+
reject(@"ReplaceError", [NSString stringWithFormat:@"Failed to replace model: %@", error.localizedDescription], nil);
|
|
464
|
+
} else {
|
|
465
|
+
resolve([NSString stringWithFormat:@"Instance ID: %@ changed model to %@", instanceId, modelName]);
|
|
466
|
+
}
|
|
467
|
+
} else {
|
|
468
|
+
reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
RCT_EXPORT_METHOD(startKeywordDetection:(NSString *)instanceId
|
|
473
|
+
threshold:(float)threshold
|
|
474
|
+
noExternalActivation:(BOOL)noExternalActivation
|
|
475
|
+
duckOthers:(BOOL)duckOthers
|
|
476
|
+
mixWithOthers:(BOOL)mixWithOthers
|
|
477
|
+
defaultToSpeaker:(BOOL)defaultToSpeaker
|
|
478
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
479
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
480
|
+
{
|
|
481
|
+
KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
|
|
482
|
+
KeyWordsDetection *instance = wrapper.keyWordsDetection;
|
|
483
|
+
if (instance) {
|
|
484
|
+
BOOL success = [instance startListeningWithNoExternalActivation:noExternalActivation
|
|
485
|
+
duckOthers:duckOthers
|
|
486
|
+
mixWithOthers:mixWithOthers
|
|
487
|
+
defaultToSpeaker:defaultToSpeaker];
|
|
488
|
+
if (success == false) {
|
|
489
|
+
reject(@"StartError", [NSString stringWithFormat:@"Failed to start detection"], nil);
|
|
490
|
+
} else {
|
|
491
|
+
resolve([NSString stringWithFormat:@"Started detection for instance: %@", instanceId]);
|
|
492
|
+
}
|
|
493
|
+
} else {
|
|
494
|
+
reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
RCT_EXPORT_METHOD(stopKeywordDetection:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
499
|
+
{
|
|
500
|
+
KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
|
|
501
|
+
KeyWordsDetection *instance = wrapper.keyWordsDetection;
|
|
502
|
+
if (instance) {
|
|
503
|
+
[instance stopListening];
|
|
504
|
+
resolve([NSString stringWithFormat:@"Stopped detection for instance: %@", instanceId]);
|
|
505
|
+
} else {
|
|
506
|
+
reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
RCT_EXPORT_METHOD(destroyInstance:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
511
|
+
{
|
|
512
|
+
KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
|
|
513
|
+
if (wrapper) {
|
|
514
|
+
[wrapper.keyWordsDetection stopListening];
|
|
515
|
+
[self.instances removeObjectForKey:instanceId];
|
|
516
|
+
resolve([NSString stringWithFormat:@"Destroyed instance: %@", instanceId]);
|
|
517
|
+
} else {
|
|
518
|
+
reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Keeping all APIs even if not called in JS yet
|
|
523
|
+
|
|
524
|
+
RCT_EXPORT_METHOD(getKeywordDetectionModel:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
525
|
+
{
|
|
526
|
+
KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
|
|
527
|
+
KeyWordsDetection *instance = wrapper.keyWordsDetection;
|
|
528
|
+
if (instance) {
|
|
529
|
+
NSString *modelName = [instance getKeywordDetectionModel];
|
|
530
|
+
resolve(modelName);
|
|
531
|
+
} else {
|
|
532
|
+
reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
RCT_EXPORT_METHOD(getRecordingWav:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
537
|
+
{
|
|
538
|
+
KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
|
|
539
|
+
KeyWordsDetection *instance = wrapper.keyWordsDetection;
|
|
540
|
+
if (instance) {
|
|
541
|
+
NSString *recWavPath = [instance getRecordingWav];
|
|
542
|
+
resolve(recWavPath);
|
|
543
|
+
} else {
|
|
544
|
+
reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
RCT_EXPORT_METHOD(getVoiceProps:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
549
|
+
{
|
|
550
|
+
KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
|
|
551
|
+
KeyWordsDetection *instance = wrapper.keyWordsDetection;
|
|
552
|
+
if (instance) {
|
|
553
|
+
@try {
|
|
554
|
+
NSDictionary *voiceProps = [instance getVoiceProps];
|
|
555
|
+
NSMutableDictionary *result = [NSMutableDictionary dictionary];
|
|
556
|
+
result[@"error"] = voiceProps[@"error"] ?: @"No Error";
|
|
557
|
+
result[@"voiceProbability"] = @([voiceProps[@"voiceProbability"] floatValue]);
|
|
558
|
+
result[@"lastTimeHumanVoiceHeard"] = @([voiceProps[@"lastTimeHumanVoiceHeard"] longLongValue]);
|
|
559
|
+
resolve(result);
|
|
560
|
+
} @catch (NSException *exception) {
|
|
561
|
+
reject(@"VoicePropsError", [NSString stringWithFormat:@"Failed to get voice properties: %@", exception.reason], nil);
|
|
562
|
+
}
|
|
563
|
+
} else {
|
|
564
|
+
reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Start/stop silent VAD (iOS only)
|
|
569
|
+
RCT_EXPORT_METHOD(startSilentVADDetection:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
570
|
+
{
|
|
571
|
+
KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
|
|
572
|
+
if (wrapper && wrapper.keyWordsDetection) {
|
|
573
|
+
BOOL success = [wrapper.keyWordsDetection startSilentListening];
|
|
574
|
+
success ? resolve(@"Started silent VAD detection") :
|
|
575
|
+
reject(@"StartError", @"Failed to start silent VAD detection", nil);
|
|
576
|
+
} else {
|
|
577
|
+
reject(@"InstanceNotFound", @"No instance found", nil);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
RCT_EXPORT_METHOD(stopSilentVADDetection:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
582
|
+
{
|
|
583
|
+
KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
|
|
584
|
+
if (wrapper && wrapper.keyWordsDetection) {
|
|
585
|
+
[wrapper.keyWordsDetection stopSilentListening];
|
|
586
|
+
resolve(@"Stopped silent VAD detection");
|
|
587
|
+
} else {
|
|
588
|
+
reject(@"InstanceNotFound", @"No instance found", nil);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Start/stop explicit VAD
|
|
593
|
+
RCT_EXPORT_METHOD(startVADDetection:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
594
|
+
{
|
|
595
|
+
KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
|
|
596
|
+
if (wrapper && wrapper.keyWordsDetection) {
|
|
597
|
+
BOOL success = [wrapper.keyWordsDetection startVADListening];
|
|
598
|
+
success ? resolve(@"Started VAD detection") :
|
|
599
|
+
reject(@"StartError", @"Failed to start VAD detection", nil);
|
|
600
|
+
} else {
|
|
601
|
+
reject(@"InstanceNotFound", @"No instance found", nil);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
RCT_EXPORT_METHOD(stopVADDetection:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
606
|
+
{
|
|
607
|
+
KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
|
|
608
|
+
if (wrapper && wrapper.keyWordsDetection) {
|
|
609
|
+
[wrapper.keyWordsDetection stopVADListening];
|
|
610
|
+
resolve(@"Stopped VAD detection");
|
|
611
|
+
} else {
|
|
612
|
+
reject(@"InstanceNotFound", @"No instance found", nil);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
RCT_EXPORT_METHOD(setVADParams:(NSString *)instanceId
|
|
617
|
+
threshold:(float)threshold
|
|
618
|
+
msWindow:(NSInteger)msWindow
|
|
619
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
620
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
621
|
+
{
|
|
622
|
+
KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
|
|
623
|
+
if (wrapper && wrapper.keyWordsDetection) {
|
|
624
|
+
NSError *err = nil;
|
|
625
|
+
BOOL ok = [wrapper.keyWordsDetection setVADParamsWithThreshold:threshold
|
|
626
|
+
msWindow:msWindow
|
|
627
|
+
error:&err];
|
|
628
|
+
if (!ok || err) {
|
|
629
|
+
reject(@"VADParamsError",
|
|
630
|
+
err ? err.localizedDescription : @"Failed to set VAD params",
|
|
631
|
+
err);
|
|
632
|
+
} else {
|
|
633
|
+
resolve(@"VAD params updated");
|
|
634
|
+
}
|
|
635
|
+
} else {
|
|
636
|
+
reject(@"InstanceNotFound", @"No instance found", nil);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// You can add more methods here as needed, ensuring they use the instanceId
|
|
641
|
+
|
|
642
|
+
@end
|