Skip to content

Commit 690037f

Browse files
committed
fix(speech): update TTS pipeline to use react-native-blob-util
1 parent 6d23846 commit 690037f

2 files changed

Lines changed: 10 additions & 74 deletions

File tree

packages/react-native-executorch/src/extensions/speech/tasks/supertonicTextToSpeech.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { WorkletRuntime } from 'react-native-worklets';
22

3-
import RNFS from 'react-native-fs';
3+
import RNBlobUtil from 'react-native-blob-util';
44

55
import { tensor, type Tensor } from '../../../core/tensor';
66
import { loadModel } from '../../../core/model';
@@ -242,13 +242,13 @@ export async function createSupertonicTextToSpeech<K extends PropertyKey>(
242242
});
243243

244244
// Parse unicode indexer JSON
245-
const indexStr = await RNFS.readFile(config.unicodeIndexerPath, 'utf8'); // TODO: change after #1328 lands
245+
const indexStr = await RNBlobUtil.fs.readFile(config.unicodeIndexerPath, 'utf8');
246246
const indexer: readonly number[] = JSON.parse(indexStr);
247247

248248
// Pre-parse voice styles map into memory
249249
const parsedVoiceStyles = {} as Record<K, SupertonicVoiceStyle>;
250250
for (const [key, path] of Object.entries(config.voiceStyles) as [K, string][]) {
251-
const jsonStr = await RNFS.readFile(path, 'utf8'); // TODO: change after #1328 lands
251+
const jsonStr = await RNBlobUtil.fs.readFile(path, 'utf8');
252252
parsedVoiceStyles[key] = parseVoiceStyle(JSON.parse(jsonStr));
253253
}
254254

packages/react-native-executorch/src/hooks/useTextToSpeech.ts

Lines changed: 7 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useModel } from './useModel';
2-
import { useResourceDownload } from './useResourceDownload';
2+
import { useResourceDownload, type ResourceOptions } from './useResourceDownload';
33
import {
44
createSupertonicTextToSpeech,
55
type SupertonicTtsModel,
@@ -14,86 +14,22 @@ import {
1414
* @category Hooks
1515
* @typeParam K Voice style keys record constraint.
1616
* @param config The Supertonic TTS model configuration.
17-
* @param options Hook options.
18-
* @param options.preventLoad If true, prevents downloading and compiling the model.
17+
* @param options Load and caching options. See {@link ResourceOptions}.
1918
* @returns An object containing the model's loading state, error, download progress,
2019
* and synthesis functions.
2120
*/
22-
// TODO: change after #1328 lands
2321
export function useTextToSpeech<K extends PropertyKey>(
2422
config: SupertonicTtsModel<K>,
25-
options?: { preventLoad?: boolean }
23+
options?: ResourceOptions
2624
) {
27-
const dpRes = useResourceDownload(config.modelPaths.durationPredictor, options?.preventLoad);
28-
const teRes = useResourceDownload(config.modelPaths.textEncoder, options?.preventLoad);
29-
const veRes = useResourceDownload(config.modelPaths.vectorEstimator, options?.preventLoad);
30-
const vocRes = useResourceDownload(config.modelPaths.vocoder, options?.preventLoad);
31-
const indexerRes = useResourceDownload(config.unicodeIndexerPath, options?.preventLoad);
32-
33-
const voiceKeys = Object.keys(config.voiceStyles) as K[];
34-
const voiceResList = voiceKeys.map((key) =>
35-
// eslint-disable-next-line react-hooks/rules-of-hooks
36-
useResourceDownload(config.voiceStyles[key], options?.preventLoad)
37-
);
38-
39-
const isVoiceReady = voiceResList.every((r) => r.localPath);
40-
const isReady = !!(
41-
dpRes.localPath &&
42-
teRes.localPath &&
43-
veRes.localPath &&
44-
vocRes.localPath &&
45-
indexerRes.localPath &&
46-
isVoiceReady
47-
);
48-
49-
const localVoiceStyles = isReady
50-
? (Object.fromEntries(voiceKeys.map((key, i) => [key, voiceResList[i]!.localPath!])) as Record<
51-
K,
52-
string
53-
>)
54-
: null;
55-
56-
const localConfig = isReady
57-
? {
58-
modelPaths: {
59-
durationPredictor: dpRes.localPath!,
60-
textEncoder: teRes.localPath!,
61-
vectorEstimator: veRes.localPath!,
62-
vocoder: vocRes.localPath!,
63-
},
64-
unicodeIndexerPath: indexerRes.localPath!,
65-
voiceStyles: localVoiceStyles!,
66-
}
67-
: null;
68-
69-
const configKey = localConfig
70-
? JSON.stringify(localConfig.modelPaths) + localConfig.unicodeIndexerPath
71-
: null;
72-
73-
const { model, error } = useModel(createSupertonicTextToSpeech, localConfig, [
74-
isReady,
75-
configKey,
76-
]);
77-
78-
const downloadProgress =
79-
(dpRes.downloadProgress +
80-
teRes.downloadProgress +
81-
veRes.downloadProgress +
82-
vocRes.downloadProgress +
83-
indexerRes.downloadProgress +
84-
voiceResList.reduce((acc, r) => acc + r.downloadProgress, 0)) /
85-
(5 + voiceKeys.length);
25+
const { resource, downloadProgress, downloadError } = useResourceDownload(config, options);
26+
const { model, error } = useModel(createSupertonicTextToSpeech, resource ?? null);
8627

8728
return {
8829
isReady: !!model,
89-
error:
90-
dpRes.downloadError ||
91-
teRes.downloadError ||
92-
veRes.downloadError ||
93-
vocRes.downloadError ||
94-
indexerRes.downloadError ||
95-
error,
30+
error: downloadError || error,
9631
downloadProgress,
32+
resource,
9733
synthesize: model?.synthesize,
9834
synthesizeStop: model?.synthesizeStop,
9935
};

0 commit comments

Comments
 (0)