react-native-executorch
Version:
An easy way to run AI models in React Native with ExecuTorch
144 lines (141 loc) • 4.65 kB
JavaScript
;
import { useCallback, useEffect, useState } from 'react';
import { TextToSpeechModule } from '../../modules/natural_language_processing/TextToSpeechModule';
import { RnExecutorchErrorCode } from '../../errors/ErrorCodes';
import { RnExecutorchError, parseUnknownError } from '../../errors/errorUtils';
/**
* React hook for managing Text to Speech instance.
* @category Hooks
* @param TextToSpeechProps - Configuration object containing `model` source, `voice` and optional `preventLoad`.
* @returns Ready to use Text to Speech model.
*/
export const useTextToSpeech = ({
model,
voice,
preventLoad = false
}) => {
const [error, setError] = useState(null);
const [isReady, setIsReady] = useState(false);
const [isGenerating, setIsGenerating] = useState(false);
const [downloadProgress, setDownloadProgress] = useState(0);
const [moduleInstance, setModuleInstance] = useState(null);
useEffect(() => {
if (preventLoad) return;
let active = true;
setDownloadProgress(0);
setError(null);
setIsReady(false);
TextToSpeechModule.fromModelName({
model,
voice
}, setDownloadProgress).then(mod => {
if (!active) {
mod.delete();
return;
}
setModuleInstance(prev => {
prev?.delete();
return mod;
});
setIsReady(true);
}).catch(err => {
if (active) setError(parseUnknownError(err));
});
return () => {
active = false;
setModuleInstance(prev => {
prev?.delete();
return null;
});
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [model.modelName, model.durationPredictorSource, model.synthesizerSource, voice?.voiceSource, voice?.extra, preventLoad]);
// Shared guard for all generation methods
const guardReady = useCallback(methodName => {
if (!isReady || !moduleInstance) throw new RnExecutorchError(RnExecutorchErrorCode.ModuleNotLoaded, `The model is currently not loaded. Please load the model before calling ${methodName}().`);
if (isGenerating) throw new RnExecutorchError(RnExecutorchErrorCode.ModelGenerating, 'The model is currently generating. Please wait until previous model run is complete.');
return moduleInstance;
}, [isReady, isGenerating, moduleInstance]);
const forward = async input => {
const instance = guardReady('forward');
try {
setIsGenerating(true);
return await instance.forward(input.text ?? '', input.speed ?? 1.0);
} finally {
setIsGenerating(false);
}
};
const forwardFromPhonemes = async input => {
const instance = guardReady('forwardFromPhonemes');
try {
setIsGenerating(true);
return await instance.forwardFromPhonemes(input.phonemes ?? '', input.speed ?? 1.0);
} finally {
setIsGenerating(false);
}
};
const stream = useCallback(async input => {
const instance = guardReady('stream');
setIsGenerating(true);
try {
if (input.text) {
// If the initial text does not end with an end of sentence character,
// we add an artificial dot to improve output's quality.
instance.streamInsert(input.text + ('.?!;'.includes(input.text.trim().slice(-1)) ? '' : '.'));
}
await input.onBegin?.();
for await (const audio of instance.stream({
speed: input.speed ?? 1.0,
stopAutomatically: input.stopAutomatically ?? true
})) {
if (input.onNext) {
await input.onNext(audio);
}
}
} finally {
await input.onEnd?.();
setIsGenerating(false);
}
}, [guardReady]);
const streamFromPhonemes = useCallback(async input => {
const instance = guardReady('streamFromPhonemes');
setIsGenerating(true);
try {
await input.onBegin?.();
for await (const audio of instance.streamFromPhonemes({
phonemes: input.phonemes ?? '',
speed: input.speed ?? 1.0
})) {
if (input.onNext) {
await input.onNext(audio);
}
}
} finally {
await input.onEnd?.();
setIsGenerating(false);
}
}, [guardReady]);
const streamInsert = useCallback(text => {
if (moduleInstance) {
moduleInstance.streamInsert(text);
}
}, [moduleInstance]);
const streamStop = useCallback((instant = true) => {
if (moduleInstance) {
moduleInstance.streamStop(instant);
}
}, [moduleInstance]);
return {
error,
isReady,
isGenerating,
forward,
forwardFromPhonemes,
stream,
streamFromPhonemes,
streamInsert,
streamStop,
downloadProgress
};
};
//# sourceMappingURL=useTextToSpeech.js.map