react-native-wakeword
Version:
Voice/Wake-word detection library for React Native
316 lines (271 loc) • 10.5 kB
JavaScript
// KeyWordRNBridge.js
import { NativeModules, NativeEventEmitter, Platform } from 'react-native';
const { KeyWordRNBridge } = NativeModules;
const keywordRNBridgeEmitter = new NativeEventEmitter(KeyWordRNBridge);
if (KeyWordRNBridge) {
console.log("KeyWordRNBridge is loaded:", KeyWordRNBridge);
} else {
console.error("KeyWordRNBridge is not linked correctly.");
}
export class KeyWordRNBridgeInstance {
instanceId;
listeners = [];
isFirstInstance = false;
constructor(instanceId, isSticky) {
this.instanceId = instanceId;
this.isSticky = isSticky;
}
async createInstanceMulti(modelNames, thresholds, bufferCnts, msBetweenCallbacks) {
const instance = await KeyWordRNBridge.createInstanceMulti(
this.instanceId,
modelNames,
thresholds,
bufferCnts,
msBetweenCallbacks
);
if (instance && this.isFirstInstance) {
this.isFirstInstance = false;
await KeyWordRNBridge.startForegroundService(this.instanceId);
}
return instance;
}
async createInstance(modelName, threshold, bufferCnt) {
const instance = await KeyWordRNBridge.createInstance(
this.instanceId,
modelName,
threshold,
bufferCnt);
if (instance && this.isFirstInstance)
{
this.isFirstInstance = false;
await KeyWordRNBridge.startForegroundService(this.instanceId);
}
return instance;
}
// 1) Voice props (parity with VAD)
async getVoiceProps() {
try {
const voiceProps = await KeyWordRNBridge.getVoiceProps(this.instanceId);
return {
error: voiceProps.error,
voiceProbability: voiceProps.voiceProbability,
lastTimeHumanVoiceHeard: voiceProps.lastTimeHumanVoiceHeard,
};
} catch (error) {
console.error('Error getting voice properties:', error);
throw error;
}
}
// 2) Silent VAD control (iOS-only, optional but useful)
async startSilentVADDetection() {
if (Platform.OS === 'ios') {
return await KeyWordRNBridge.startSilentVADDetection(this.instanceId);
}
}
async stopSilentVADDetection() {
if (Platform.OS === 'ios') {
return await KeyWordRNBridge.stopSilentVADDetection(this.instanceId);
}
}
// 3) Explicit VAD start/stop (if exposed by your native bridge)
async startVADDetection() {
return await KeyWordRNBridge.startVADDetection(this.instanceId);
}
async stopVADDetection() {
return await KeyWordRNBridge.stopVADDetection(this.instanceId);
}
// 4) (Optional) VAD event listener if your native layer emits it
onVADDetectionEvent(callback) {
const listener = keywordRNBridgeEmitter.addListener('onVADDetectionEvent', (event) => {
if (event.instanceId === this.instanceId) {
callback(event.phrase);
}
});
this.listeners.push(listener);
return listener;
}
// Configure VAD runtime parameters
async setVADParams(threshold, msWindow) {
return await KeyWordRNBridge.setVADParams(this.instanceId, threshold, msWindow);
}
async getVADParams() {
// implement only if native exposes it
return await KeyWordRNBridge.getVADParams(this.instanceId);
}
async setKeywordDetectionLicense(license) {
return await KeyWordRNBridge.setKeywordDetectionLicense(this.instanceId, license);
}
async replaceKeywordDetectionModel(modelName, threshold, bufferCnt) {
return KeyWordRNBridge.replaceKeywordDetectionModel(this.instanceId, modelName, threshold, bufferCnt);
}
/*startForegroundService() {
return KeyWordRNBridge.startForegroundService(this.instanceId);
}
stopForegroundService() {
return KeyWordRNBridge.stopForegroundService(this.instanceId);
}*/
async setKeywordLicense(license) {
return KeyWordRNBridge.setKeywordLicense(this.instanceId, license);
}
/**
* @param {any} threshold
* @param {string | boolean | undefined} [speakerVerificationEnrollmentJsonOrPathOrSetActive]
* @param {boolean} [setActive]
* @param {boolean} [duckOthers]
* @param {boolean} [mixWithOthers]
* @param {boolean} [defaultToSpeaker]
*/
async startKeywordDetection(
threshold,
speakerVerificationEnrollmentJsonOrPathOrSetActive = undefined,
setActive = true,
duckOthers = false,
mixWithOthers = true,
defaultToSpeaker = true
) {
/** @type {string | undefined} */
let speakerVerificationEnrollmentJsonOrPath = undefined;
let resolvedSetActive = setActive;
let resolvedDuckOthers = duckOthers;
let resolvedMixWithOthers = mixWithOthers;
let resolvedDefaultToSpeaker = defaultToSpeaker;
if (
typeof speakerVerificationEnrollmentJsonOrPathOrSetActive === 'string' ||
speakerVerificationEnrollmentJsonOrPathOrSetActive == null
) {
speakerVerificationEnrollmentJsonOrPath = speakerVerificationEnrollmentJsonOrPathOrSetActive;
} else {
resolvedSetActive = speakerVerificationEnrollmentJsonOrPathOrSetActive;
}
if (Platform.OS === 'ios') {
if (speakerVerificationEnrollmentJsonOrPath != null) {
return KeyWordRNBridge.startKeywordDetectionWithSpeakerVerification(
this.instanceId,
threshold,
speakerVerificationEnrollmentJsonOrPath,
resolvedSetActive,
resolvedDuckOthers,
resolvedMixWithOthers,
resolvedDefaultToSpeaker
);
}
return KeyWordRNBridge.startKeywordDetection(
this.instanceId,
threshold,
resolvedSetActive,
resolvedDuckOthers,
resolvedMixWithOthers,
resolvedDefaultToSpeaker
);
} else {
return KeyWordRNBridge.startKeywordDetection(
this.instanceId,
threshold,
speakerVerificationEnrollmentJsonOrPath
);
}
}
async getRecordingWavArray() {
return await KeyWordRNBridge.getRecordingWavArray(this.instanceId);
}
async getRecordingWav() {
return await KeyWordRNBridge.getRecordingWav(this.instanceId);
}
async stopKeywordDetection() {
return KeyWordRNBridge.stopKeywordDetection(this.instanceId);
}
async pauseDetection(stopMic = false) {
// XXX - check if needs await
// return await KeyWordRNBridge.pauseDetection(this.instanceId, stopMic);
return await KeyWordRNBridge.pauseDetection(this.instanceId, stopMic);
}
async unPauseDetection() {
// XXX BUG - FIX pause with false not working on unpause on Android.
if (Platform.OS === 'android') {
await KeyWordRNBridge.unPauseDetection(this.instanceId);
await KeyWordRNBridge.pauseDetection(this.instanceId, true);
}
// Check if needs await
// return await KeyWordRNBridge.unPauseDetection(this.instanceId);
return await KeyWordRNBridge.unPauseDetection(this.instanceId);
}
async destroyInstance() {
return KeyWordRNBridge.destroyInstance(this.instanceId);
}
onKeywordDetectionEvent(callback) {
const listener = keywordRNBridgeEmitter.addListener('onKeywordDetectionEvent', (event) => {
if (event.instanceId === this.instanceId) {
console.log("event == .", event);
if (Platform.OS === 'ios') {
console.log("event == .", event);
callback(event.info);
} else {
callback(event.phrase);
}
}
});
this.listeners.push(listener);
return listener;
}
removeListeners() {
this.listeners.forEach((listener) => listener.remove());
this.listeners = [];
}
}
export const removeAllRNBridgeListeners = async () => {
keywordRNBridgeEmitter.removeAllListeners('onKeywordDetectionEvent');
keywordRNBridgeEmitter.removeAllListeners('onVADDetectionEvent'); // NEW
}
export const pauseDetection = async (instanceId, stopMic = false) => {
return KeyWordRNBridge.pauseDetection(instanceId, stopMic);
};
export const unPauseDetection = async (instanceId) => {
return KeyWordRNBridge.unPauseDetection(instanceId);
};
export const createKeyWordRNBridgeInstance = async (instanceId, isSticky) => {
return await new KeyWordRNBridgeInstance(instanceId, isSticky);
};
export const enableDucking = async () => {
if (Platform.OS === 'ios') {
await KeyWordRNBridge.enableAggressiveDucking();
}
}
export const disableDucking = async () => {
if (Platform.OS === 'ios') {
await KeyWordRNBridge.disableDucking();
}
}
export const restartListeningAfterDucking = async () => {
if (Platform.OS === 'ios') {
await KeyWordRNBridge.restartListeningAfterDucking();
}
}
export const initAudioSessAndDuckManage = async () => {
if (Platform.OS === 'ios') {
await KeyWordRNBridge.initAudioSessAndDuckManage();
}
}
export const hasIOSMicPermissions = async () => {
return await KeyWordRNBridge.hasMicPermissions();
}
export const requestIOSMicPermissions = async (wait_timeout) => {
return await KeyWordRNBridge.requestMicPermissions(wait_timeout);
}
export const hasIOSSpeechRecognitionPermissions = async () => {
return await KeyWordRNBridge.hasSpeechRecognitionPermissions();
}
export const requestIOSSpeechRecognitionPermissions = async (wait_timeout) => {
return await KeyWordRNBridge.requestSpeechRecognitionPermissions(wait_timeout);
}
export const disableDuckingAndCleanup = async () => {
if (Platform.OS === 'ios') {
await KeyWordRNBridge.disableDuckingAndCleanup();
}
}
// NEW: global audio routing config setter (per package, not per instance)
export const setWakewordAudioRoutingConfig = async (config) => {
if (Platform.OS === 'ios') {
// Native: implement KeyWordRNBridge.setAudioRoutingConfig(jsonString)
return KeyWordRNBridge.setAudioRoutingConfig(JSON.stringify(config));
}
};