1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-15 09:04:04 +02:00
joplin/packages/app-mobile/services/voiceTyping/vosk.android.ts

120 lines
2.6 KiB
TypeScript
Raw Normal View History

import Logger from '@joplin/lib/Logger';
2023-05-07 18:53:19 +02:00
import Vosk from 'react-native-vosk';
const logger = Logger.create('voiceTyping/vosk');
enum State {
Idle = 0,
Recording,
}
interface StartOptions {
onResult: (text: string)=> void;
}
let vosk_: Vosk|null = null;
let state_: State = State.Idle;
export const voskEnabled = true;
export { Vosk };
export interface Recorder {
stop: ()=> Promise<string>;
cleanup: ()=> void;
}
export const getVosk = async () => {
if (vosk_) return vosk_;
vosk_ = new Vosk();
await vosk_.loadModel('model-fr-fr');
return vosk_;
};
export const startRecording = (vosk: Vosk, options: StartOptions): Recorder => {
if (state_ !== State.Idle) throw new Error('Vosk is already recording');
state_ = State.Recording;
const result: string[] = [];
const eventHandlers: any[] = [];
let finalResultPromiseResolve: Function = null;
let finalResultPromiseReject: Function = null;
let finalResultTimeout = false;
const completeRecording = (finalResult: string, error: Error) => {
logger.info(`Complete recording. Final result: ${finalResult}. Error:`, error);
for (const eventHandler of eventHandlers) {
eventHandler.remove();
}
vosk.cleanup(),
state_ = State.Idle;
if (error) {
if (finalResultPromiseReject) finalResultPromiseReject(error);
} else {
if (finalResultPromiseResolve) finalResultPromiseResolve(finalResult);
}
};
eventHandlers.push(vosk.onResult(e => {
const text = e.data;
logger.info('Result', text);
result.push(text);
options.onResult(text);
}));
eventHandlers.push(vosk.onError(e => {
logger.warn('Error', e.data);
}));
eventHandlers.push(vosk.onTimeout(e => {
logger.warn('Timeout', e.data);
}));
eventHandlers.push(vosk.onFinalResult(e => {
logger.info('Final result', e.data);
if (finalResultTimeout) {
logger.warn('Got final result - but already timed out. Not doing anything.');
return;
}
completeRecording(e.data, null);
}));
logger.info('Starting recording...');
void vosk.start();
return {
stop: (): Promise<string> => {
logger.info('Stopping recording...');
vosk.stopOnly();
logger.info('Waiting for final result...');
setTimeout(() => {
finalResultTimeout = true;
logger.warn('Timed out waiting for finalResult event');
completeRecording('', new Error('Could not process your message. Please try again.'));
}, 5000);
return new Promise((resolve: Function, reject: Function) => {
finalResultPromiseResolve = resolve;
finalResultPromiseReject = reject;
});
},
cleanup: () => {
if (state_ !== State.Idle) {
logger.info('Cancelling...');
vosk.stopOnly();
completeRecording('', null);
}
},
};
};