react-native-wakeword-sid 1.1.70
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 +282 -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 +17 -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/keywordspotting/KeyWordRNBridge.java +331 -0
- package/android/src/main/java/com/davoice/keywordspotting/KeyWordRNBridgePackage.java +25 -0
- package/android/src/main/java/com/davoice/speakeridrn/SpeakerIdRNBridge.java +373 -0
- package/android/src/main/java/com/davoice/speakeridrn/SpeakerIdRNBridgePackage.java +24 -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 +399 -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 +5570 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios.private.swiftinterface +157 -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 +157 -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 +794 -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 +5570 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +157 -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 +157 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/x86_64-apple-ios-simulator.abi.json +5570 -0
- package/ios/KeyWordRNBridge/KeyWordDetection.xcframework/ios-arm64_x86_64-simulator/KeyWordDetection.framework/Modules/KeyWordDetection.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +157 -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 +157 -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.mm +395 -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/speakerid/SpeakerIdRNBridge.d.ts +44 -0
- package/speakerid/SpeakerIdRNBridge.js +169 -0
- package/wakewords/KeyWordRNBridge.d.ts +37 -0
- package/wakewords/KeyWordRNBridge.js +221 -0
- package/wakewords/index.d.ts +10 -0
- package/wakewords/index.js +21 -0
- package/wakewords/useModel.d.ts +19 -0
- package/wakewords/useModel.tsx +235 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
package com.davoice.speakeridrn;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.*;
|
|
4
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
5
|
+
import androidx.annotation.Nullable;
|
|
6
|
+
|
|
7
|
+
import android.content.Context;
|
|
8
|
+
import android.net.Uri;
|
|
9
|
+
import android.os.Handler;
|
|
10
|
+
import android.os.Looper;
|
|
11
|
+
|
|
12
|
+
import ai.onnxruntime.OrtEnvironment;
|
|
13
|
+
import ai.onnxruntime.OrtException;
|
|
14
|
+
|
|
15
|
+
import com.davoice.speakerid.*;
|
|
16
|
+
|
|
17
|
+
import java.io.File;
|
|
18
|
+
import java.util.HashMap;
|
|
19
|
+
import java.util.Map;
|
|
20
|
+
import java.util.concurrent.*;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* React Native bridge for the speaker-id Android library.
|
|
24
|
+
* It wraps SpeakerIdApi: onboarding (mic/stream/wav) + verification (mic/stream/wav).
|
|
25
|
+
*/
|
|
26
|
+
public class SpeakerIdRNBridge extends ReactContextBaseJavaModule {
|
|
27
|
+
|
|
28
|
+
private static final String REACT_CLASS = "SpeakerIdRNBridge";
|
|
29
|
+
private static ReactApplicationContext reactContext;
|
|
30
|
+
|
|
31
|
+
// Keep multiple instances if you want (keyed by id)
|
|
32
|
+
private final Map<String, SpeakerIdApi> instances = new HashMap<>();
|
|
33
|
+
|
|
34
|
+
// Streaming onboarding per instance
|
|
35
|
+
private final Map<String, SpeakerIdApi.OnboardingStream> onboardingStreams = new HashMap<>();
|
|
36
|
+
|
|
37
|
+
// Run heavy calls off the UI thread
|
|
38
|
+
private final ExecutorService exec = Executors.newCachedThreadPool();
|
|
39
|
+
private final Handler main = new Handler(Looper.getMainLooper());
|
|
40
|
+
|
|
41
|
+
public SpeakerIdRNBridge(ReactApplicationContext context) {
|
|
42
|
+
super(context);
|
|
43
|
+
reactContext = context;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Override public String getName() { return REACT_CLASS; }
|
|
47
|
+
|
|
48
|
+
// -------------------- Helpers --------------------
|
|
49
|
+
|
|
50
|
+
private static WritableMap toJs(OnboardingResult r) {
|
|
51
|
+
WritableMap m = Arguments.createMap();
|
|
52
|
+
m.putInt("clusterSize", r.clusterSize);
|
|
53
|
+
m.putInt("embeddingDim", r.embDim);
|
|
54
|
+
return m;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private static WritableMap toJs(VerificationResult r) {
|
|
58
|
+
WritableMap m = Arguments.createMap();
|
|
59
|
+
m.putDouble("fullSec", r.fullSec);
|
|
60
|
+
m.putDouble("voicedSec", r.voicedSec);
|
|
61
|
+
m.putDouble("bestScore", r.bestScore);
|
|
62
|
+
m.putString("bestStrategy", r.bestStrategy);
|
|
63
|
+
m.putString("bestTargetLabel", r.bestTargetLabel);
|
|
64
|
+
|
|
65
|
+
// Flatten perTargetStrategy -> { strategyKey: { label: score } }
|
|
66
|
+
WritableMap strat = Arguments.createMap();
|
|
67
|
+
if (r.perTargetStrategy != null) {
|
|
68
|
+
for (Map.Entry<String, Map<String, Float>> e : r.perTargetStrategy.entrySet()) {
|
|
69
|
+
WritableMap col = Arguments.createMap();
|
|
70
|
+
for (Map.Entry<String, Float> colE : e.getValue().entrySet()) {
|
|
71
|
+
col.putDouble(colE.getKey(), colE.getValue());
|
|
72
|
+
}
|
|
73
|
+
strat.putMap(e.getKey(), col);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
m.putMap("perTargetStrategy", strat);
|
|
77
|
+
return m;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private static File fileFromPath(String path) {
|
|
81
|
+
return new File(path);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// -------------------- Lifecycle / Creation --------------------
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Create a SpeakerIdApi instance.
|
|
88
|
+
* @param instanceId unique id you choose
|
|
89
|
+
*/
|
|
90
|
+
@ReactMethod
|
|
91
|
+
public void createInstance(String instanceId, Promise promise) {
|
|
92
|
+
if (instances.containsKey(instanceId)) {
|
|
93
|
+
promise.reject("InstanceExists", "Instance already exists: " + instanceId);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
exec.submit(() -> {
|
|
97
|
+
try {
|
|
98
|
+
SpeakerIdApi api = SpeakerIdApi.create(reactContext); // <-- resolves speaker_id.dm + layer1.dm internally
|
|
99
|
+
synchronized (instances) { instances.put(instanceId, api); }
|
|
100
|
+
main.post(() -> promise.resolve(true));
|
|
101
|
+
} catch (Exception e) {
|
|
102
|
+
main.post(() -> promise.reject("CreateError", e.getMessage(), e));
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@ReactMethod
|
|
108
|
+
public void destroyInstance(String instanceId, Promise promise) {
|
|
109
|
+
SpeakerIdApi api;
|
|
110
|
+
synchronized (instances) { api = instances.remove(instanceId); }
|
|
111
|
+
if (api != null) {
|
|
112
|
+
try { api.close(); } catch (Exception ignore) {}
|
|
113
|
+
onboardingStreams.remove(instanceId);
|
|
114
|
+
promise.resolve(true);
|
|
115
|
+
} else {
|
|
116
|
+
promise.reject("InstanceNotFound", "No such instance: " + instanceId);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// -------------------- Verification init (targets) --------------------
|
|
121
|
+
|
|
122
|
+
@ReactMethod
|
|
123
|
+
public void initVerificationUsingDefaults(String instanceId, Promise promise) {
|
|
124
|
+
SpeakerIdApi api = instances.get(instanceId);
|
|
125
|
+
if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
|
|
126
|
+
promise.resolve(api.initVerificationUsingDefaults(reactContext));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Use explicit files for mean & cluster (paths to .npy files).
|
|
131
|
+
*/
|
|
132
|
+
@ReactMethod
|
|
133
|
+
public void initVerificationWithFiles(String instanceId,
|
|
134
|
+
String meanEmbPath,
|
|
135
|
+
String clusterPath,
|
|
136
|
+
Promise promise) {
|
|
137
|
+
SpeakerIdApi api = instances.get(instanceId);
|
|
138
|
+
if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
|
|
139
|
+
boolean ok = api.initVerificationWithFiles(fileFromPath(meanEmbPath),
|
|
140
|
+
fileFromPath(clusterPath));
|
|
141
|
+
promise.resolve(ok);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// -------------------- Onboarding: Microphone --------------------
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Opens mic, captures first VAD-delimited utterance, enrolls.
|
|
148
|
+
* JS must have RECORD_AUDIO permission granted beforehand.
|
|
149
|
+
* @param maxMillis timeout in ms
|
|
150
|
+
*/
|
|
151
|
+
@ReactMethod
|
|
152
|
+
public void onboardFromMicrophone(String instanceId, double maxMillis, Promise promise) {
|
|
153
|
+
SpeakerIdApi api = instances.get(instanceId);
|
|
154
|
+
if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
|
|
155
|
+
|
|
156
|
+
exec.submit(() -> {
|
|
157
|
+
try {
|
|
158
|
+
OnboardingResult r = api.onboardFromMicrophone((long)maxMillis);
|
|
159
|
+
WritableMap out = toJs(r);
|
|
160
|
+
main.post(() -> promise.resolve(out));
|
|
161
|
+
} catch (SecurityException se) {
|
|
162
|
+
main.post(() -> promise.reject("MicPermission", "RECORD_AUDIO permission missing", se));
|
|
163
|
+
} catch (Exception e) {
|
|
164
|
+
main.post(() -> promise.reject("OnboardMicError", e.getMessage(), e));
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// -------------------- Onboarding: Stream --------------------
|
|
170
|
+
|
|
171
|
+
@ReactMethod
|
|
172
|
+
public void startOnboardingStream(String instanceId, Promise promise) {
|
|
173
|
+
SpeakerIdApi api = instances.get(instanceId);
|
|
174
|
+
if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
|
|
175
|
+
SpeakerIdApi.OnboardingStream stream = api.startOnboardingStream();
|
|
176
|
+
onboardingStreams.put(instanceId, stream);
|
|
177
|
+
promise.resolve(true);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Feed PCM16 block (short[]) as a base64 string? RN doesn’t pass short[] directly.
|
|
182
|
+
* For simplicity here, pass an int array of 16-bit values (range -32768..32767).
|
|
183
|
+
*/
|
|
184
|
+
@ReactMethod
|
|
185
|
+
public void feedOnboardingStream(String instanceId, ReadableArray pcmI16, Promise promise) {
|
|
186
|
+
SpeakerIdApi.OnboardingStream stream = onboardingStreams.get(instanceId);
|
|
187
|
+
if (stream == null) { promise.reject("StreamNotStarted", instanceId); return; }
|
|
188
|
+
|
|
189
|
+
short[] block = new short[pcmI16.size()];
|
|
190
|
+
for (int i = 0; i < pcmI16.size(); i++) block[i] = (short) pcmI16.getInt(i);
|
|
191
|
+
|
|
192
|
+
exec.submit(() -> {
|
|
193
|
+
try {
|
|
194
|
+
OnboardingResult r = stream.feed(block);
|
|
195
|
+
if (r != null) {
|
|
196
|
+
onboardingStreams.remove(instanceId);
|
|
197
|
+
WritableMap out = toJs(r);
|
|
198
|
+
main.post(() -> promise.resolve(out));
|
|
199
|
+
} else {
|
|
200
|
+
main.post(() -> promise.resolve(null)); // not done yet
|
|
201
|
+
}
|
|
202
|
+
} catch (Exception e) {
|
|
203
|
+
main.post(() -> promise.reject("OnboardFeedError", e.getMessage(), e));
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
@ReactMethod
|
|
209
|
+
public void finishOnboardingStream(String instanceId, Promise promise) {
|
|
210
|
+
SpeakerIdApi.OnboardingStream stream = onboardingStreams.remove(instanceId);
|
|
211
|
+
if (stream == null) { promise.reject("StreamNotStarted", instanceId); return; }
|
|
212
|
+
exec.submit(() -> {
|
|
213
|
+
try {
|
|
214
|
+
OnboardingResult r = stream.finish();
|
|
215
|
+
WritableMap out = (r == null) ? null : toJs(r);
|
|
216
|
+
main.post(() -> promise.resolve(out));
|
|
217
|
+
} catch (Exception e) {
|
|
218
|
+
main.post(() -> promise.reject("OnboardFinishError", e.getMessage(), e));
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// -------------------- Onboarding: WAV --------------------
|
|
224
|
+
|
|
225
|
+
@ReactMethod
|
|
226
|
+
public void onboardFromWav(String instanceId, String wavPath, Promise promise) {
|
|
227
|
+
SpeakerIdApi api = instances.get(instanceId);
|
|
228
|
+
if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
|
|
229
|
+
|
|
230
|
+
exec.submit(() -> {
|
|
231
|
+
try {
|
|
232
|
+
OnboardingResult r = api.onboardFromWav(fileFromPath(wavPath));
|
|
233
|
+
WritableMap out = toJs(r);
|
|
234
|
+
main.post(() -> promise.resolve(out));
|
|
235
|
+
} catch (Exception e) {
|
|
236
|
+
main.post(() -> promise.reject("OnboardWavError", e.getMessage(), e));
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// -------------------- Verification: Microphone --------------------
|
|
242
|
+
|
|
243
|
+
@ReactMethod
|
|
244
|
+
public void verifyFromMicrophone(String instanceId, double maxMillis, Promise promise) {
|
|
245
|
+
SpeakerIdApi api = instances.get(instanceId);
|
|
246
|
+
if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
|
|
247
|
+
exec.submit(() -> {
|
|
248
|
+
try {
|
|
249
|
+
VerificationResult r = api.verifyFromMicrophone((long)maxMillis);
|
|
250
|
+
WritableMap out = (r == null) ? null : toJs(r);
|
|
251
|
+
main.post(() -> promise.resolve(out));
|
|
252
|
+
} catch (SecurityException se) {
|
|
253
|
+
main.post(() -> promise.reject("MicPermission", "RECORD_AUDIO permission missing", se));
|
|
254
|
+
} catch (Exception e) {
|
|
255
|
+
main.post(() -> promise.reject("VerifyMicError", e.getMessage(), e));
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// -------------------- Verification: Stream --------------------
|
|
261
|
+
|
|
262
|
+
@ReactMethod
|
|
263
|
+
public void verifyStreamPush(String instanceId, ReadableArray pcmI16, Promise promise) {
|
|
264
|
+
SpeakerIdApi api = instances.get(instanceId);
|
|
265
|
+
if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
|
|
266
|
+
short[] block = new short[pcmI16.size()];
|
|
267
|
+
for (int i = 0; i < pcmI16.size(); i++) block[i] = (short) pcmI16.getInt(i);
|
|
268
|
+
exec.submit(() -> {
|
|
269
|
+
try {
|
|
270
|
+
VerificationResult r = api.verifyStreamPush(block);
|
|
271
|
+
WritableMap out = (r == null) ? null : toJs(r);
|
|
272
|
+
main.post(() -> promise.resolve(out));
|
|
273
|
+
} catch (Exception e) {
|
|
274
|
+
main.post(() -> promise.reject("VerifyStreamPushError", e.getMessage(), e));
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
@ReactMethod
|
|
280
|
+
public void verifyStreamFinish(String instanceId, Promise promise) {
|
|
281
|
+
SpeakerIdApi api = instances.get(instanceId);
|
|
282
|
+
if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
|
|
283
|
+
exec.submit(() -> {
|
|
284
|
+
try {
|
|
285
|
+
VerificationResult r = api.verifyStreamFinish();
|
|
286
|
+
WritableMap out = (r == null) ? null : toJs(r);
|
|
287
|
+
main.post(() -> promise.resolve(out));
|
|
288
|
+
} catch (Exception e) {
|
|
289
|
+
main.post(() -> promise.reject("VerifyStreamFinishError", e.getMessage(), e));
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// -------------------- Verification: WAV --------------------
|
|
295
|
+
|
|
296
|
+
@ReactMethod
|
|
297
|
+
public void verifyFromWav(String instanceId, String wavPath, Promise promise) {
|
|
298
|
+
SpeakerIdApi api = instances.get(instanceId);
|
|
299
|
+
if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
|
|
300
|
+
exec.submit(() -> {
|
|
301
|
+
try {
|
|
302
|
+
VerificationResult r = api.verifyFromWav(fileFromPath(wavPath));
|
|
303
|
+
WritableMap out = (r == null) ? null : toJs(r);
|
|
304
|
+
main.post(() -> promise.resolve(out));
|
|
305
|
+
} catch (Exception e) {
|
|
306
|
+
main.post(() -> promise.reject("VerifyWavError", e.getMessage(), e));
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// -------------------- Export helpers --------------------
|
|
312
|
+
|
|
313
|
+
@ReactMethod
|
|
314
|
+
public void exportDefaultClusterToDownloads(String instanceId, Promise promise) {
|
|
315
|
+
SpeakerIdApi api = instances.get(instanceId);
|
|
316
|
+
if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
|
|
317
|
+
exec.submit(() -> {
|
|
318
|
+
try {
|
|
319
|
+
Uri uri = api.exportDefaultClusterToDownloads(reactContext);
|
|
320
|
+
main.post(() -> promise.resolve(uri.toString()));
|
|
321
|
+
} catch (Exception e) {
|
|
322
|
+
main.post(() -> promise.reject("ExportClusterError", e.getMessage(), e));
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
@ReactMethod
|
|
328
|
+
public void exportDefaultMeanToDownloads(String instanceId, Promise promise) {
|
|
329
|
+
SpeakerIdApi api = instances.get(instanceId);
|
|
330
|
+
if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
|
|
331
|
+
exec.submit(() -> {
|
|
332
|
+
try {
|
|
333
|
+
Uri uri = api.exportDefaultMeanToDownloads(reactContext);
|
|
334
|
+
main.post(() -> promise.resolve(uri.toString()));
|
|
335
|
+
} catch (Exception e) {
|
|
336
|
+
main.post(() -> promise.reject("ExportMeanError", e.getMessage(), e));
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
@ReactMethod
|
|
342
|
+
public void exportDefaultMeanCountToDownloads(String instanceId, Promise promise) {
|
|
343
|
+
SpeakerIdApi api = instances.get(instanceId);
|
|
344
|
+
if (api == null) { promise.reject("InstanceNotFound", instanceId); return; }
|
|
345
|
+
exec.submit(() -> {
|
|
346
|
+
try {
|
|
347
|
+
Uri uri = api.exportDefaultMeanCountToDownloads(reactContext);
|
|
348
|
+
main.post(() -> promise.resolve(uri.toString()));
|
|
349
|
+
} catch (Exception e) {
|
|
350
|
+
main.post(() -> promise.reject("ExportMeanCountError", e.getMessage(), e));
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// RN event helpers (if you later want to emit async events)
|
|
356
|
+
private void sendEvent(String eventName, @Nullable WritableMap params) {
|
|
357
|
+
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
358
|
+
.emit(eventName, params);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// No-op listener hooks
|
|
362
|
+
@ReactMethod public void addListener(String eventName) {}
|
|
363
|
+
@ReactMethod public void removeListeners(Integer count) {}
|
|
364
|
+
|
|
365
|
+
@Override
|
|
366
|
+
public void onCatalystInstanceDestroy() {
|
|
367
|
+
for (SpeakerIdApi api : instances.values()) try { api.close(); } catch (Exception ignore) {}
|
|
368
|
+
instances.clear();
|
|
369
|
+
onboardingStreams.clear();
|
|
370
|
+
exec.shutdownNow();
|
|
371
|
+
super.onCatalystInstanceDestroy();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
package com.davoice.speakeridrn;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage;
|
|
4
|
+
import com.facebook.react.bridge.NativeModule;
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager;
|
|
7
|
+
|
|
8
|
+
import java.util.ArrayList;
|
|
9
|
+
import java.util.Collections;
|
|
10
|
+
import java.util.List;
|
|
11
|
+
|
|
12
|
+
public class SpeakerIdRNBridgePackage implements ReactPackage {
|
|
13
|
+
@Override
|
|
14
|
+
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
|
15
|
+
List<NativeModule> modules = new ArrayList<>();
|
|
16
|
+
modules.add(new SpeakerIdRNBridge(reactContext));
|
|
17
|
+
return modules;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Override
|
|
21
|
+
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
|
22
|
+
return Collections.emptyList();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
Binary file
|
package/app.plugin.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// app.plugin.js
|
|
2
|
+
const { withAndroidManifest, withProjectBuildGradle } = require('@expo/config-plugins');
|
|
3
|
+
|
|
4
|
+
function withReactNativeWakeword(config) {
|
|
5
|
+
//
|
|
6
|
+
// 1. Add necessary permissions to AndroidManifest (if needed).
|
|
7
|
+
// For example, if `react-native-wakeword` needs RECORD_AUDIO.
|
|
8
|
+
//
|
|
9
|
+
config = withAndroidManifest(config, (config) => {
|
|
10
|
+
const androidManifest = config.modResults;
|
|
11
|
+
|
|
12
|
+
// Ensure 'uses-permission' array exists
|
|
13
|
+
if (!androidManifest.manifest['uses-permission']) {
|
|
14
|
+
androidManifest.manifest['uses-permission'] = [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const permissions = androidManifest.manifest['uses-permission'];
|
|
18
|
+
const hasRecordAudio = permissions.some(
|
|
19
|
+
(permission) => permission.$['android:name'] === 'android.permission.RECORD_AUDIO'
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// Add RECORD_AUDIO permission if not present
|
|
23
|
+
if (!hasRecordAudio) {
|
|
24
|
+
permissions.push({ $: { 'android:name': 'android.permission.RECORD_AUDIO' } });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return config;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
//
|
|
31
|
+
// 2. Insert Maven repository or other Gradle config in the project's
|
|
32
|
+
// top-level build.gradle (only if your library requires custom artifacts).
|
|
33
|
+
//
|
|
34
|
+
config = withProjectBuildGradle(config, (config) => {
|
|
35
|
+
const buildGradle = config.modResults.contents;
|
|
36
|
+
|
|
37
|
+
// This snippet references a local "libs" folder inside your library package.
|
|
38
|
+
// Adjust the path if your artifacts are elsewhere or not needed.
|
|
39
|
+
const wakewordMaven = `
|
|
40
|
+
// react-native-wakeword added
|
|
41
|
+
maven { url("$rootDir/../node_modules/react-native-wakeword/libs") }
|
|
42
|
+
// End react-native-wakeword added
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
// Only add the snippet once
|
|
46
|
+
if (!buildGradle.includes('react-native-wakeword added')) {
|
|
47
|
+
config.modResults.contents = buildGradle.replace(
|
|
48
|
+
/repositories\s*{/,
|
|
49
|
+
`repositories {\n${wakewordMaven}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return config;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return config;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = withReactNativeWakeword;
|
|
60
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>AvailableLibraries</key>
|
|
6
|
+
<array>
|
|
7
|
+
<dict>
|
|
8
|
+
<key>BinaryPath</key>
|
|
9
|
+
<string>KeyWordDetection.framework/KeyWordDetection</string>
|
|
10
|
+
<key>LibraryIdentifier</key>
|
|
11
|
+
<string>ios-arm64_x86_64-simulator</string>
|
|
12
|
+
<key>LibraryPath</key>
|
|
13
|
+
<string>KeyWordDetection.framework</string>
|
|
14
|
+
<key>SupportedArchitectures</key>
|
|
15
|
+
<array>
|
|
16
|
+
<string>arm64</string>
|
|
17
|
+
<string>x86_64</string>
|
|
18
|
+
</array>
|
|
19
|
+
<key>SupportedPlatform</key>
|
|
20
|
+
<string>ios</string>
|
|
21
|
+
<key>SupportedPlatformVariant</key>
|
|
22
|
+
<string>simulator</string>
|
|
23
|
+
</dict>
|
|
24
|
+
<dict>
|
|
25
|
+
<key>BinaryPath</key>
|
|
26
|
+
<string>KeyWordDetection.framework/KeyWordDetection</string>
|
|
27
|
+
<key>LibraryIdentifier</key>
|
|
28
|
+
<string>ios-arm64</string>
|
|
29
|
+
<key>LibraryPath</key>
|
|
30
|
+
<string>KeyWordDetection.framework</string>
|
|
31
|
+
<key>SupportedArchitectures</key>
|
|
32
|
+
<array>
|
|
33
|
+
<string>arm64</string>
|
|
34
|
+
</array>
|
|
35
|
+
<key>SupportedPlatform</key>
|
|
36
|
+
<string>ios</string>
|
|
37
|
+
</dict>
|
|
38
|
+
</array>
|
|
39
|
+
<key>CFBundlePackageType</key>
|
|
40
|
+
<string>XFWK</string>
|
|
41
|
+
<key>XCFrameworkFormatVersion</key>
|
|
42
|
+
<string>1.0</string>
|
|
43
|
+
</dict>
|
|
44
|
+
</plist>
|