react-native-wakeword 1.0.22
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/LICENSE +21 -0
- package/README.md +9 -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 +45 -0
- package/android/settings.gradle +1 -0
- package/android/src/main/AndroidManifest.xml +13 -0
- package/android/src/main/assets/embedding_model.onnx +0 -0
- package/android/src/main/assets/melspectrogram.onnx +0 -0
- package/android/src/main/assets/need_help_now.onnx +0 -0
- package/android/src/main/java/com/davoice/keywordspotting/KeyWordRNBridge.java +168 -0
- package/android/src/main/java/com/davoice/keywordspotting/KeyWordRNBridgePackage.java +25 -0
- package/android/src/main/libs/MyLibrary-release.aar +0 -0
- package/package.json +36 -0
- package/react-native.config.js +14 -0
- package/wakewords/KeyWordRNBridge.d.ts +17 -0
- package/wakewords/KeyWordRNBridge.js +89 -0
- package/wakewords/index.d.ts +3 -0
- package/wakewords/index.d.ts.chat_idiot +2 -0
- package/wakewords/index.js +4 -0
- package/wakewords/useModel.d.ts +12 -0
- package/wakewords/useModel.tsx +227 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 frymanofer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
File without changes
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
apply plugin: 'com.android.library'
|
|
2
|
+
|
|
3
|
+
android {
|
|
4
|
+
compileSdkVersion 33
|
|
5
|
+
|
|
6
|
+
defaultConfig {
|
|
7
|
+
minSdkVersion 21
|
|
8
|
+
targetSdkVersion 33
|
|
9
|
+
consumerProguardFiles 'consumer-rules.pro'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
sourceSets {
|
|
13
|
+
main {
|
|
14
|
+
assets.srcDirs = ['src/main/assets']
|
|
15
|
+
jniLibs.srcDirs = ['src/main/jniLibs'] // Add this line if needed
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
repositories {
|
|
21
|
+
google()
|
|
22
|
+
mavenCentral()
|
|
23
|
+
flatDir {
|
|
24
|
+
dirs 'src/main/libs' // Update this line
|
|
25
|
+
}
|
|
26
|
+
maven {
|
|
27
|
+
url 'https://maven.pkg.github.com/microsoft/onnxruntime'
|
|
28
|
+
}
|
|
29
|
+
mavenLocal()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
dependencies {
|
|
33
|
+
implementation "com.facebook.react:react-native:+"
|
|
34
|
+
implementation 'com.microsoft.onnxruntime:onnxruntime-android:1.13.1'
|
|
35
|
+
implementation 'ai.picovoice:android-voice-processor:1.0.2'
|
|
36
|
+
implementation fileTree(dir: "src/main/libs", include: ["*.aar"])
|
|
37
|
+
// implementation(name: 'MyLibrary-release', ext: 'aar') // Add this line
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Run this once to be able to run the application with BUCK
|
|
41
|
+
// puts all compile dependencies into folder libs for BUCK to use
|
|
42
|
+
task copyDownloadableDepsToLibs(type: Copy) {
|
|
43
|
+
from configurations.implementation
|
|
44
|
+
into 'libs'
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include ':app'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+
package="com.davoice.keywordspotting">
|
|
3
|
+
|
|
4
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
5
|
+
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
|
6
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
|
7
|
+
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
|
8
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
|
|
9
|
+
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
|
10
|
+
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
|
11
|
+
|
|
12
|
+
</manifest>
|
|
13
|
+
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
package com.davoice.keywordspotting;
|
|
2
|
+
|
|
3
|
+
import com.davoice.keywordsdetection.keywordslibrary.KeyWordsDetection;
|
|
4
|
+
import com.facebook.react.bridge.*;
|
|
5
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
6
|
+
import androidx.annotation.Nullable;
|
|
7
|
+
import ai.onnxruntime.*;
|
|
8
|
+
import android.util.Log;
|
|
9
|
+
import java.util.HashMap;
|
|
10
|
+
import java.util.Map;
|
|
11
|
+
|
|
12
|
+
public class KeyWordRNBridge extends ReactContextBaseJavaModule {
|
|
13
|
+
|
|
14
|
+
private final String TAG = "KeyWordsDetection";
|
|
15
|
+
private static final String REACT_CLASS = "KeyWordRNBridge";
|
|
16
|
+
private static ReactApplicationContext reactContext;
|
|
17
|
+
|
|
18
|
+
// Map to hold multiple instances
|
|
19
|
+
private Map<String, KeyWordsDetection> instances = new HashMap<>();
|
|
20
|
+
|
|
21
|
+
public KeyWordRNBridge(ReactApplicationContext context) {
|
|
22
|
+
super(context);
|
|
23
|
+
reactContext = context;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@Override
|
|
27
|
+
public String getName() {
|
|
28
|
+
return REACT_CLASS;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@ReactMethod
|
|
32
|
+
public Boolean setKeywordDetectionLicense(String instanceId, String licenseKey, Promise promise) {
|
|
33
|
+
KeyWordsDetection instance = instances.get(instanceId);
|
|
34
|
+
Log.d(TAG, "setKeywordDetectionLicense()");
|
|
35
|
+
|
|
36
|
+
Boolean isLicesed = false;
|
|
37
|
+
if (instance != null) {
|
|
38
|
+
isLicesed = instance.setLicenseKey(licenseKey);
|
|
39
|
+
}
|
|
40
|
+
Log.d(TAG, "setKeywordDetectionLicense(): " + (isLicesed ? "Licensed" : "Not Licensed"));
|
|
41
|
+
|
|
42
|
+
promise.resolve(isLicesed);
|
|
43
|
+
return isLicesed;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Create a new instance
|
|
47
|
+
@ReactMethod
|
|
48
|
+
public void createInstance(String instanceId, String modelName, float threshold, int bufferCnt, Promise promise) {
|
|
49
|
+
if (instances.containsKey(instanceId)) {
|
|
50
|
+
promise.reject("InstanceExists", "Instance already exists with ID: " + instanceId);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
KeyWordsDetection keyWordsDetection = new KeyWordsDetection(reactContext, modelName, threshold, bufferCnt);
|
|
56
|
+
keyWordsDetection.initialize(detected -> onKeywordDetected(instanceId, detected));
|
|
57
|
+
instances.put(instanceId, keyWordsDetection);
|
|
58
|
+
promise.resolve("Instance created with ID: " + instanceId);
|
|
59
|
+
} catch (Exception e) {
|
|
60
|
+
promise.reject("CreateError", "Failed to create instance: " + e.getMessage());
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Create a new instance
|
|
65
|
+
@ReactMethod
|
|
66
|
+
public void replaceKeywordDetectionModel(String instanceId, String modelName, float threshold, int bufferCnt, Promise promise) {
|
|
67
|
+
KeyWordsDetection instance = instances.get(instanceId);
|
|
68
|
+
if (instance == null) {
|
|
69
|
+
promise.reject("Instance not Exists", "Instance does not exists with ID: " + instanceId);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
instance.replaceKeywordDetectionModel(reactContext, modelName, threshold, bufferCnt);
|
|
75
|
+
promise.resolve("Instance ID: " + instanceId + " change model " + modelName);
|
|
76
|
+
} catch (Exception e) {
|
|
77
|
+
promise.reject("CreateError", "Failed to create instance: " + e.getMessage());
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Start detection for a specific instance
|
|
82
|
+
@ReactMethod
|
|
83
|
+
public void startKeywordDetection(String instanceId, float threshold, Promise promise) throws OrtException {
|
|
84
|
+
KeyWordsDetection instance = instances.get(instanceId);
|
|
85
|
+
if (instance != null) {
|
|
86
|
+
instance.startListening(threshold);
|
|
87
|
+
promise.resolve("Started detection for instance: " + instanceId);
|
|
88
|
+
} else {
|
|
89
|
+
promise.reject("InstanceNotFound", "No instance found with ID: " + instanceId);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Stop detection for a specific instance
|
|
94
|
+
@ReactMethod
|
|
95
|
+
public void stopForegroundService(String instanceId, Promise promise) {
|
|
96
|
+
KeyWordsDetection instance = instances.get(instanceId);
|
|
97
|
+
if (instance != null) {
|
|
98
|
+
instance.stopForegroundService();
|
|
99
|
+
promise.resolve("stopForegroundService" + instanceId);
|
|
100
|
+
} else {
|
|
101
|
+
promise.reject("stopForegroundService", "No instance found with ID: " + instanceId);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Stop detection for a specific instance
|
|
106
|
+
@ReactMethod
|
|
107
|
+
public void startForegroundService(String instanceId, Promise promise) {
|
|
108
|
+
KeyWordsDetection instance = instances.get(instanceId);
|
|
109
|
+
if (instance != null) {
|
|
110
|
+
instance.startForegroundService();
|
|
111
|
+
promise.resolve("startForegroundService" + instanceId);
|
|
112
|
+
} else {
|
|
113
|
+
promise.reject("startForegroundService", "No instance found with ID: " + instanceId);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Stop detection for a specific instance
|
|
118
|
+
@ReactMethod
|
|
119
|
+
public void stopKeywordDetection(String instanceId, Promise promise) {
|
|
120
|
+
KeyWordsDetection instance = instances.get(instanceId);
|
|
121
|
+
if (instance != null) {
|
|
122
|
+
instance.stopListening();
|
|
123
|
+
promise.resolve("Stopped detection for instance: " + instanceId);
|
|
124
|
+
} else {
|
|
125
|
+
promise.reject("InstanceNotFound", "No instance found with ID: " + instanceId);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Destroy an instance
|
|
130
|
+
@ReactMethod
|
|
131
|
+
public void destroyInstance(String instanceId, Promise promise) {
|
|
132
|
+
KeyWordsDetection instance = instances.remove(instanceId);
|
|
133
|
+
if (instance != null) {
|
|
134
|
+
instance.stopListening();
|
|
135
|
+
// Additional cleanup if necessary
|
|
136
|
+
promise.resolve("Destroyed instance: " + instanceId);
|
|
137
|
+
} else {
|
|
138
|
+
promise.reject("InstanceNotFound", "No instance found with ID: " + instanceId);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Handle keyword detection event
|
|
143
|
+
private void onKeywordDetected(String instanceId, Boolean detected) {
|
|
144
|
+
if (detected) {
|
|
145
|
+
WritableMap params = Arguments.createMap();
|
|
146
|
+
params.putString("instanceId", instanceId);
|
|
147
|
+
params.putString("phrase", instanceId);
|
|
148
|
+
sendEvent("onKeywordDetectionEvent", params);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Send event to JavaScript
|
|
153
|
+
private void sendEvent(String eventName, @Nullable WritableMap params) {
|
|
154
|
+
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
155
|
+
.emit(eventName, params);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@ReactMethod
|
|
159
|
+
public void addListener(String eventName) {
|
|
160
|
+
// Set up any upstream listeners or background tasks as necessary
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@ReactMethod
|
|
164
|
+
public void removeListeners(Integer count) {
|
|
165
|
+
// Remove upstream listeners, stop unnecessary background tasks
|
|
166
|
+
}
|
|
167
|
+
// Implement other methods as needed, ensuring to use instanceId
|
|
168
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package com.davoice.keywordspotting;
|
|
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 KeyWordRNBridgePackage implements ReactPackage {
|
|
13
|
+
|
|
14
|
+
@Override
|
|
15
|
+
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
|
16
|
+
List<NativeModule> modules = new ArrayList<>();
|
|
17
|
+
modules.add(new KeyWordRNBridge(reactContext));
|
|
18
|
+
return modules;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Override
|
|
22
|
+
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
|
23
|
+
return Collections.emptyList();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-wakeword",
|
|
3
|
+
"version": "1.0.22",
|
|
4
|
+
"description": "Voice/Wake-word detection library for React Native",
|
|
5
|
+
"main": "wakewords/index.js",
|
|
6
|
+
"types": "wakewords/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"wakewords/",
|
|
9
|
+
"android/",
|
|
10
|
+
"react-native.config.js"
|
|
11
|
+
],
|
|
12
|
+
"react-native": {
|
|
13
|
+
"android": "android"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "gradlew assembleRelease"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/react": "^18.3.12",
|
|
20
|
+
"typescript": "^4.0.0"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"react": "^18.3.1",
|
|
24
|
+
"react-native": "^0.75.2"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"react-native",
|
|
28
|
+
"voice commands",
|
|
29
|
+
"wake word",
|
|
30
|
+
"keyword detection",
|
|
31
|
+
"keyword spotting",
|
|
32
|
+
"phrase spotting"
|
|
33
|
+
],
|
|
34
|
+
"author": "Davoice.io",
|
|
35
|
+
"license": "MIT"
|
|
36
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
dependencies: {
|
|
3
|
+
'react-native-wakeword': {
|
|
4
|
+
platforms: {
|
|
5
|
+
android: {
|
|
6
|
+
sourceDir: './android',
|
|
7
|
+
packageImportPath: 'import com.davoice.keywordspotting.KeyWordRNBridgePackage;',
|
|
8
|
+
packageInstance: 'new KeyWordRNBridgePackage()',
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class KeyWordRNBridgeInstance {
|
|
2
|
+
constructor(instanceId: any, isSticky: any);
|
|
3
|
+
instanceId: any;
|
|
4
|
+
listeners: any[];
|
|
5
|
+
isFirstInstance: boolean;
|
|
6
|
+
isSticky: any;
|
|
7
|
+
createInstance(modelName: any, threshold: any, bufferCnt: any): any;
|
|
8
|
+
setKeywordDetectionLicense(license: any): any;
|
|
9
|
+
replaceKeywordDetectionModel(modelName: any, threshold: any, bufferCnt: any): any;
|
|
10
|
+
setKeywordLicense(license: any): any;
|
|
11
|
+
startKeywordDetection(threshold: any): any;
|
|
12
|
+
stopKeywordDetection(): any;
|
|
13
|
+
destroyInstance(): any;
|
|
14
|
+
onKeywordDetectionEvent(callback: any): void;
|
|
15
|
+
removeListeners(): void;
|
|
16
|
+
}
|
|
17
|
+
export function createKeyWordRNBridgeInstance(instanceId: any, isSticky: any): Promise<KeyWordRNBridgeInstance>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// KeyWordRNBridge.js
|
|
2
|
+
import { NativeModules, NativeEventEmitter, Platform } from 'react-native';
|
|
3
|
+
|
|
4
|
+
const { KeyWordRNBridge } = NativeModules;
|
|
5
|
+
const keywordRNBridgeEmitter = new NativeEventEmitter(KeyWordRNBridge);
|
|
6
|
+
|
|
7
|
+
if (KeyWordRNBridge) {
|
|
8
|
+
console.log("KeyWordRNBridge is loaded:", KeyWordRNBridge);
|
|
9
|
+
} else {
|
|
10
|
+
console.error("KeyWordRNBridge is not linked correctly.");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class KeyWordRNBridgeInstance {
|
|
14
|
+
instanceId;
|
|
15
|
+
listeners = [];
|
|
16
|
+
isFirstInstance = false;
|
|
17
|
+
|
|
18
|
+
constructor(instanceId, isSticky) {
|
|
19
|
+
this.instanceId = instanceId;
|
|
20
|
+
this.isSticky = isSticky;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
createInstance(
|
|
24
|
+
modelName,
|
|
25
|
+
threshold,
|
|
26
|
+
bufferCnt)
|
|
27
|
+
{
|
|
28
|
+
instance = KeyWordRNBridge.createInstance(
|
|
29
|
+
this.instanceId,
|
|
30
|
+
modelName,
|
|
31
|
+
threshold,
|
|
32
|
+
bufferCnt
|
|
33
|
+
);
|
|
34
|
+
if (instance && this.isFirstInstance)
|
|
35
|
+
{
|
|
36
|
+
this.isFirstInstance = false;
|
|
37
|
+
KeyWordRNBridge.startForegroundService(this.instanceId);
|
|
38
|
+
}
|
|
39
|
+
return instance;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
setKeywordDetectionLicense(license) {
|
|
43
|
+
return KeyWordRNBridge.setKeywordDetectionLicense(this.instanceId, license);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
replaceKeywordDetectionModel(modelName, threshold, bufferCnt) {
|
|
47
|
+
return KeyWordRNBridge.replaceKeywordDetectionModel(this.instanceId, modelName, threshold, bufferCnt);
|
|
48
|
+
}
|
|
49
|
+
/*startForegroundService() {
|
|
50
|
+
return KeyWordRNBridge.startForegroundService(this.instanceId);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
stopForegroundService() {
|
|
54
|
+
return KeyWordRNBridge.stopForegroundService(this.instanceId);
|
|
55
|
+
}*/
|
|
56
|
+
setKeywordLicense(license) {
|
|
57
|
+
return KeyWordRNBridge.setKeywordLicense(this.instanceId, license);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
startKeywordDetection(threshold) {
|
|
61
|
+
return KeyWordRNBridge.startKeywordDetection(this.instanceId, threshold);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
stopKeywordDetection() {
|
|
65
|
+
return KeyWordRNBridge.stopKeywordDetection(this.instanceId);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
destroyInstance() {
|
|
69
|
+
return KeyWordRNBridge.destroyInstance(this.instanceId);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
onKeywordDetectionEvent(callback) {
|
|
73
|
+
const listener = keywordRNBridgeEmitter.addListener('onKeywordDetectionEvent', (event) => {
|
|
74
|
+
if (event.instanceId === this.instanceId) {
|
|
75
|
+
callback(event.phrase);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
this.listeners.push(listener);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
removeListeners() {
|
|
82
|
+
this.listeners.forEach((listener) => listener.remove());
|
|
83
|
+
this.listeners = [];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const createKeyWordRNBridgeInstance = async (instanceId, isSticky) => {
|
|
88
|
+
return new KeyWordRNBridgeInstance(instanceId, isSticky);
|
|
89
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom hook for handling keyword detection using KeyWordRNBridge
|
|
3
|
+
* @returns An object with functions and state for keyword detection
|
|
4
|
+
*/
|
|
5
|
+
export declare const useModel: () => {
|
|
6
|
+
isListening: boolean;
|
|
7
|
+
startListening: () => Promise<void>;
|
|
8
|
+
loadModel: (models: [string], callback: (phrase: string) => void) => Promise<void>;
|
|
9
|
+
setKeywordDetectionLicense: (licenseKey: string) => Promise<void>;
|
|
10
|
+
stopListening: () => Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
export default useModel;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from "react";
|
|
2
|
+
import {
|
|
3
|
+
createKeyWordRNBridgeInstance,
|
|
4
|
+
KeyWordRNBridgeInstance,
|
|
5
|
+
} from './KeyWordRNBridge';
|
|
6
|
+
import { Platform } from "react-native";
|
|
7
|
+
|
|
8
|
+
type DetectionCallback = (event: any) => void;
|
|
9
|
+
|
|
10
|
+
var license = "MTczMjkxNzYwMDAwMA==-DDwBWs914KpHbWBBSqi28vhiM4l5CYG+YgS2n9Z3DMI=";
|
|
11
|
+
interface keyWordRNBridgeInstanceConfig {
|
|
12
|
+
id: string;
|
|
13
|
+
instance: KeyWordRNBridgeInstance;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface instanceConfig {
|
|
17
|
+
id: string;
|
|
18
|
+
modelName: string;
|
|
19
|
+
threshold: number;
|
|
20
|
+
bufferCnt: number;
|
|
21
|
+
sticky: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const keyWordRNBridgeInstances: keyWordRNBridgeInstanceConfig[] = [];
|
|
25
|
+
function findInstanceById(id: string): keyWordRNBridgeInstanceConfig | undefined {
|
|
26
|
+
return keyWordRNBridgeInstances.find(config => config.id === id);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Create an array of instance configurations
|
|
30
|
+
const instanceConfigs:instanceConfig[] = [
|
|
31
|
+
{ id: 'need_help_now', modelName: 'need_help_now.onnx', threshold: 0.9999, bufferCnt: 3 , sticky: false },
|
|
32
|
+
{ id: 'default', modelName: "", threshold: 0.9999, bufferCnt: 2 , sticky: false }
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
// Function to add a new instance dynamically
|
|
36
|
+
//async function addInstance(
|
|
37
|
+
// conf: instanceConfig)
|
|
38
|
+
async function addInstance(
|
|
39
|
+
conf: instanceConfig,
|
|
40
|
+
callback: (phrase: string) => void
|
|
41
|
+
): Promise<KeyWordRNBridgeInstance | null> {
|
|
42
|
+
const id = conf.id;
|
|
43
|
+
const instanceConf = findInstanceById(id);
|
|
44
|
+
if (instanceConf != null) {
|
|
45
|
+
console.log("Found Instance: ", id, "starting to listen");
|
|
46
|
+
const instance = instanceConf.instance;
|
|
47
|
+
instance.startKeywordDetection(conf.threshold);
|
|
48
|
+
return instance;
|
|
49
|
+
}
|
|
50
|
+
const instance = await createKeyWordRNBridgeInstance(id, conf.sticky);
|
|
51
|
+
let isLicesed = false;
|
|
52
|
+
|
|
53
|
+
if (!instance) {
|
|
54
|
+
console.error(`Failed to create instance ${id}`);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
console.log(`Instance ${id} created ${instance}`);
|
|
58
|
+
await instance.createInstance(conf.modelName, conf.threshold, conf.bufferCnt);
|
|
59
|
+
console.log(`Instance ${id} createInstance() called`);
|
|
60
|
+
|
|
61
|
+
isLicesed = await instance.setKeywordDetectionLicense(license);
|
|
62
|
+
console.log(`Instance ${id} created ${instance} and licensed ${isLicesed}`);
|
|
63
|
+
|
|
64
|
+
keyWordRNBridgeInstances.push({ id, instance });
|
|
65
|
+
// Set up event listener
|
|
66
|
+
instance.onKeywordDetectionEvent((phrase: string) => {
|
|
67
|
+
console.log(`Instance ${id} detected: ${id} with phrase`, phrase);
|
|
68
|
+
// callback(phrase); Does not work on IOS
|
|
69
|
+
callback(id);
|
|
70
|
+
});
|
|
71
|
+
console.log(`Instance ${id} calling startKeywordDetection()`);
|
|
72
|
+
instance.startKeywordDetection(conf.threshold);
|
|
73
|
+
console.log(`Instance ${id} started Keyword Detection`);
|
|
74
|
+
return instance;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Function to remove an instance dynamically
|
|
78
|
+
function removeInstance(id: string): void {
|
|
79
|
+
const instanceIndex = keyWordRNBridgeInstances.findIndex((item) => item.id === id);
|
|
80
|
+
|
|
81
|
+
if (instanceIndex !== -1) {
|
|
82
|
+
const { instance } = keyWordRNBridgeInstances[instanceIndex];
|
|
83
|
+
|
|
84
|
+
instance
|
|
85
|
+
.stopKeywordDetection()
|
|
86
|
+
.then(() => instance.destroyInstance())
|
|
87
|
+
.then(() => {
|
|
88
|
+
instance.removeListeners();
|
|
89
|
+
console.log(`Instance ${id} stopped and destroyed`);
|
|
90
|
+
keyWordRNBridgeInstances.splice(instanceIndex, 1);
|
|
91
|
+
})
|
|
92
|
+
.catch((error: Error) =>
|
|
93
|
+
console.error(`Error stopping instance ${id}: ${error.message}`)
|
|
94
|
+
);
|
|
95
|
+
} else {
|
|
96
|
+
console.error(`Instance ${id} not found`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Custom hook for handling keyword detection using KeyWordRNBridge
|
|
102
|
+
* @returns An object with functions and state for keyword detection
|
|
103
|
+
*/
|
|
104
|
+
export const useModel = () => {
|
|
105
|
+
// State to track whether the keyword detection is currently active
|
|
106
|
+
const [isListening, setIsListening] = useState(false);
|
|
107
|
+
let currentEventListener: any[] = [];
|
|
108
|
+
/**
|
|
109
|
+
* Set the keyword detection license
|
|
110
|
+
* @param licenseKey - The license key
|
|
111
|
+
*/
|
|
112
|
+
// const setLicense = useCallback(async (licenseKey: any) => {
|
|
113
|
+
// try {
|
|
114
|
+
// await KeyWordRNBridge.setKeywordDetectionLicense(licenseKey);
|
|
115
|
+
// } catch (error) {
|
|
116
|
+
// console.error("[useModel] Error setting license:", error);
|
|
117
|
+
// }
|
|
118
|
+
// }, []);
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Sets the keyword detection license
|
|
122
|
+
* @param licenseKey - The linceseKey
|
|
123
|
+
*/
|
|
124
|
+
const setKeywordDetectionLicense = useCallback(
|
|
125
|
+
async (licenseKey: string): Promise<void> => {
|
|
126
|
+
license = licenseKey;
|
|
127
|
+
}, []);
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Load the keyword detection model
|
|
132
|
+
* @param modelFileName - The name of the model file to load
|
|
133
|
+
* @param threshold - The detection threshold
|
|
134
|
+
* @param bufferCount - The number of audio buffers
|
|
135
|
+
*/
|
|
136
|
+
const loadModel = useCallback(
|
|
137
|
+
async (models: [string], callback: (phrase: string) => void): Promise<void> => {
|
|
138
|
+
|
|
139
|
+
console.log("loadModel()");
|
|
140
|
+
let searchIds = models;
|
|
141
|
+
let element:any = null;
|
|
142
|
+
console.log("loadModel() - models == ", models)
|
|
143
|
+
try {
|
|
144
|
+
stopListening();
|
|
145
|
+
searchIds.forEach(sId => {
|
|
146
|
+
element = instanceConfigs.find(element => element.id === sId.split(".")[0]);
|
|
147
|
+
if (element == null || element == undefined) {
|
|
148
|
+
//console.error('element id' + sId + " not found in instanceConfigs");
|
|
149
|
+
//return;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
element = Object.assign({}, instanceConfigs.find(element => element.id === 'default'));
|
|
153
|
+
element.modelName = sId;
|
|
154
|
+
element.id = sId.split(".")[0];
|
|
155
|
+
}
|
|
156
|
+
console.log('element:', element);
|
|
157
|
+
const id = addInstance(element, callback);
|
|
158
|
+
});
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error("[useModel] Error loading model:", error);
|
|
161
|
+
}
|
|
162
|
+
}, []);
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Stop listening for the keyword
|
|
166
|
+
*/
|
|
167
|
+
const startListening = useCallback(async () => {
|
|
168
|
+
try {
|
|
169
|
+
keyWordRNBridgeInstances.forEach(element => {
|
|
170
|
+
const instance = element.instance;
|
|
171
|
+
instance.startKeywordDetection();
|
|
172
|
+
/*if (instance.isSticky == false) {
|
|
173
|
+
instance.stopKeywordDetection();
|
|
174
|
+
} else if (Platform.OS != 'ios') {
|
|
175
|
+
instance.stopKeywordDetection();
|
|
176
|
+
}*/
|
|
177
|
+
});
|
|
178
|
+
setIsListening(true);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error("Error starting keyword detection:", error);
|
|
181
|
+
}
|
|
182
|
+
}, []);
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Stop listening for the keyword
|
|
186
|
+
*/
|
|
187
|
+
const stopListening = useCallback(async () => {
|
|
188
|
+
try {
|
|
189
|
+
keyWordRNBridgeInstances.forEach(element => {
|
|
190
|
+
const instance = element.instance;
|
|
191
|
+
instance.stopKeywordDetection();
|
|
192
|
+
/*if (instance.isSticky == false) {
|
|
193
|
+
instance.stopKeywordDetection();
|
|
194
|
+
} else if (Platform.OS != 'ios') {
|
|
195
|
+
instance.stopKeywordDetection();
|
|
196
|
+
}*/
|
|
197
|
+
});
|
|
198
|
+
setIsListening(false);
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error("Error stopping keyword detection:", error);
|
|
201
|
+
}
|
|
202
|
+
}, []);
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Cleanup effect to stop listening when the component unmounts
|
|
206
|
+
* or when the isListening state changes
|
|
207
|
+
*/
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
console.log("isListening updated:", isListening);
|
|
210
|
+
return () => {
|
|
211
|
+
if (isListening) {
|
|
212
|
+
stopListening();
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}, [isListening, stopListening]);
|
|
216
|
+
|
|
217
|
+
// Return an object with the necessary functions and state
|
|
218
|
+
return {
|
|
219
|
+
isListening,
|
|
220
|
+
startListening,
|
|
221
|
+
loadModel,
|
|
222
|
+
setKeywordDetectionLicense,
|
|
223
|
+
stopListening,
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export default useModel; // Add this line to allow default import
|