Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .cspell-wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -424,3 +424,10 @@ denoiser
imageembeddings
unparseable
zipdepth
xiaobei
xiaoxiao
xiaoyi
yunjian
yunxi
yunxia
yunyang
11 changes: 10 additions & 1 deletion apps/benchmarks/scripts/variant-sizes.json
Original file line number Diff line number Diff line change
Expand Up @@ -418,5 +418,14 @@
"https://huggingface.co/software-mansion/react-native-executorch-clip-vit-base-patch32/resolve/v0.11.0/coreml/clip_vit_base_patch32_text_coreml_int8.pte": 63921048,
"https://huggingface.co/software-mansion/react-native-executorch-blazeface/resolve/v0.11.0/vulkan/blazeface_vulkan_fp16.pte": 372866,
"https://huggingface.co/software-mansion/react-native-executorch-all-MiniLM-L6-v2/resolve/v0.11.0/tokenizer.json": 711544,
"https://huggingface.co/software-mansion/react-native-executorch-all-MiniLM-L6-v2/resolve/v0.11.0/coreml/all_minilm_l6_v2_coreml_int8.pte": 22817740
"https://huggingface.co/software-mansion/react-native-executorch-all-MiniLM-L6-v2/resolve/v0.11.0/coreml/all_minilm_l6_v2_coreml_int8.pte": 22817740,
"https://huggingface.co/software-mansion/react-native-executorch-kokoro/resolve/v0.11.0/phonemizer/zh/lexicon.json": 2334963,
"https://huggingface.co/software-mansion/react-native-executorch-kokoro/resolve/v0.11.0/phonemizer/zh/segmenter.json": 8288194,
"https://huggingface.co/software-mansion/react-native-executorch-kokoro/resolve/v0.11.0/voices/zf_xiaobei.bin": 522240,
"https://huggingface.co/software-mansion/react-native-executorch-kokoro/resolve/v0.11.0/voices/zf_xiaoxiao.bin": 522240,
"https://huggingface.co/software-mansion/react-native-executorch-kokoro/resolve/v0.11.0/voices/zf_xiaoyi.bin": 522240,
"https://huggingface.co/software-mansion/react-native-executorch-kokoro/resolve/v0.11.0/voices/zm_yunjian.bin": 522240,
"https://huggingface.co/software-mansion/react-native-executorch-kokoro/resolve/v0.11.0/voices/zm_yunxi.bin": 522240,
"https://huggingface.co/software-mansion/react-native-executorch-kokoro/resolve/v0.11.0/voices/zm_yunxia.bin": 522240,
"https://huggingface.co/software-mansion/react-native-executorch-kokoro/resolve/v0.11.0/voices/zm_yunyang.bin": 522240
}
2 changes: 2 additions & 0 deletions apps/speech/app/kokoro-text-to-speech/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const LANGUAGE_OPTIONS = [
{ label: 'Hindi', value: 'HI' as const },
{ label: 'Polish', value: 'PL' as const },
{ label: 'German', value: 'DE' as const },
{ label: 'Chinese', value: 'ZH' as const },
];

type KokoroLanguage = (typeof LANGUAGE_OPTIONS)[number]['value'];
Expand Down Expand Up @@ -56,6 +57,7 @@ const SAMPLE_TEXTS: Record<KokoroLanguage, string> = {
HI: 'कोकोरो एक छोटा टेक्स्ट-टू-स्पीच मॉडल है जो पूरी तरह से आपके डिवाइस पर चलता है।',
PL: 'Kokoro to niewielki model syntezy mowy, który działa w całości na twoim urządzeniu, bez połączenia z internetem.',
DE: 'Kokoro ist ein kompaktes Sprachsynthesemodell, das vollständig auf deinem Gerät läuft, ganz ohne Internetverbindung.',
ZH: 'Kokoro 是一个小巧的语音合成模型,完全在你的设备上运行,无需联网。',
};
// cspell:enable

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/02-extensions/speech/02-text-to-speech.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ The library provides ready-to-use Text-to-Speech models from the [Software Mansi
| Model | Variants | Sub-Models & Assets | Sample Rate | Size Range | Supported Backends | Supported Languages | Notes |
| :----------------------------- | :----------------------------------------------------------------------- | :--------------------------------------------------------------------------- | :---------- | :---------- | :------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Supertonic 3** | [See](../../06-api-reference/variables/models.md#texttospeechsupertonic) | Text Encoder, Duration Predictor, Vector Estimator, Vocoder, Voice Styles | 44.1 kHz | 398 MB | XNNPACK (CPU), MLX (Apple), Vulkan (Android) | [English, Spanish, French, German, Korean, Japanese & more](../../06-api-reference/react-native-executorch/namespaces/speech/variables/SUPERTONIC_SUPPORTED_LANGUAGES.md) | Faster generation and broad multilingual coverage across 10 bundled [speaker styles](../../06-api-reference/variables/SUPERTONIC_DEFAULT_VOICE_NAMES.md), with slightly lower voice naturalness than Kokoro. |
| **Kokoro (Language Packages)** | [See](../../06-api-reference/variables/models.md#texttospeechkokoro) | Duration Predictor, Synthesizer, G2P Lexicon / Neural Model, Language Voices | 24.0 kHz | 332 MB each | XNNPACK (CPU), Core ML (Apple) | English (US/GB), Spanish, French, Italian, Portuguese, Hindi, Polish, German | Exceptional voice naturalness and intonation per language package, but heavier compute per synthesized chunk. |
| **Kokoro (Language Packages)** | [See](../../06-api-reference/variables/models.md#texttospeechkokoro) | Duration Predictor, Synthesizer, G2P Lexicon / Neural Model, Language Voices | 24.0 kHz | 332 MB each | XNNPACK (CPU), Core ML (Apple) | English (US/GB), Spanish, French, Italian, Portuguese, Hindi, Polish, German, Chinese | Exceptional voice naturalness and intonation per language package, but heavier compute per synthesized chunk. |

:::note Model-Specific Pipelines
Because Text-to-Speech architectures require distinct multi-model orchestration in TypeScript (coordinating phonemizers, duration predictors, flow-matching loops, and neural vocoders), TTS pipelines are model-specific. To use custom voices or fine-tuned weights, provide your modified `.pte` models or custom voice JSON/BIN files matching the [`SupertonicTtsModel`](../../06-api-reference/type-aliases/SupertonicTtsModel.md) or [`KokoroTtsModel`](../../06-api-reference/type-aliases/KokoroTtsModel.md) specifications.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ TEST_F(PhonemizerTest, AcceptsEverySupportedLanguage) {
// The language string is passed straight through to phonemis, which picks
// the pipeline from it. A profile it does not know throws (below), so this
// is what catches a name drifting apart from the TS side.
for (const auto *lang : {"en-us", "en-gb", "de", "fr", "es", "it", "pl", "pt", "hi"}) {
for (const auto *lang : {"en-us", "en-gb", "de", "fr", "es", "it", "pl", "pt", "hi", "zh"}) {
EXPECT_TRUE(evalBool(std::string("__rnexecutorch_jsi__.speech.createPhonemizer({ lang: '") +
lang + "' }); return true;"))
<< "rejected language: " << lang;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,17 @@ declare const phonemizerBrand: unique symbol;
* Union of all supported language codes in the G2P pipeline.
* @category Speech / Types
*/
export type PhonemizerLanguage = 'en-us' | 'en-gb' | 'fr' | 'es' | 'it' | 'pt' | 'de' | 'pl' | 'hi';
export type PhonemizerLanguage =
| 'en-us'
| 'en-gb'
| 'fr'
| 'es'
| 'it'
| 'pt'
| 'de'
| 'pl'
| 'hi'
| 'zh';

/**
* Configuration options and asset paths for initializing a {@link Phonemizer}.
Expand Down Expand Up @@ -51,9 +61,10 @@ export type PhonemizedText = {
/** IPA transcription of the whole text, the same as `phonemize(text)` returns. */
readonly phonemes: string;
/**
* The whitespace-separated words of the input text, in order. Each maps onto
* one space-separated phoneme group, except numbers, which map onto as many as
* they were spelled out into. Empty if the words could not be matched.
* The whitespace-separated words of the input text (segmented words for
* Chinese), in order. Each maps onto one space-separated phoneme group,
* except numbers, which map onto as many as they were spelled out into.
* Empty if the words could not be matched.
*/
readonly words: readonly PhonemizedWord[];
};
Expand Down
40 changes: 37 additions & 3 deletions packages/react-native-executorch/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1848,9 +1848,9 @@
synthesizer: `${KOKORO_ROOT}/${backend}/${dir}/synthesizer_${variant}_${backend}_fp32.pte`,
});

const kokoroVoices = <const N extends string>(names: readonly N[]) =>
const kokoroVoices = <const N extends string>(names: readonly N[], root = KOKORO_ROOT) =>
names.reduce(
(acc, name) => ({ ...acc, [name]: `${KOKORO_ROOT}/voices/${name}.bin` }),
(acc, name) => ({ ...acc, [name]: `${root}/voices/${name}.bin` }),
{} as Record<N, string>
);

Expand All @@ -1863,13 +1863,31 @@
neuralModelSource: `${KOKORO_PHONEMIZER_ROOT}/${lang}/phonemizer_${lang.replace('-', '_')}.pte`,
});

const kokoroNeuralPhonemizer = <const L extends Exclude<PhonemizerLanguage, 'en-us' | 'en-gb'>>(
const kokoroNeuralPhonemizer = <
const L extends Exclude<PhonemizerLanguage, 'en-us' | 'en-gb' | 'zh'>,
>(
lang: L
) => ({
lang,
neuralModelSource: `${KOKORO_PHONEMIZER_ROOT}/${lang}/phonemizer_${lang}.pte`,
});

// Chinese is published from the next tag. Its phonemizer segments the text
// into words, with the segmenter data in the tagger slot, and reads each word
// from the lexicon. Upstream's zf_xiaoni is left out: it is unintelligible even

Check warning on line 1877 in packages/react-native-executorch/src/models.ts

View workflow job for this annotation

GitHub Actions / lint

Unknown word: "xiaoni"
// in the reference PyTorch model.
const KOKORO_NEXT_ROOT = `${BASE_URL}-kokoro/${NEXT_VERSION_TAG}`;
const KOKORO_ZH_PHONEMIZER = {
lang: 'zh' as const,
taggerSource: `${KOKORO_NEXT_ROOT}/phonemizer/zh/segmenter.json`,
lexiconSource: `${KOKORO_NEXT_ROOT}/phonemizer/zh/lexicon.json`,
};
const KOKORO_ZH_VOICES = kokoroVoices(
['zf_xiaobei', 'zf_xiaoxiao', 'zf_xiaoyi', 'zm_yunjian', 'zm_yunxi', 'zm_yunxia', 'zm_yunyang'],
KOKORO_NEXT_ROOT
);
type KokoroZhVoice = keyof typeof KOKORO_ZH_VOICES;

const KOKORO_EN_US_XNNPACK_FP32: KokoroTtsModel<
'af_heart' | 'af_river' | 'af_sarah' | 'am_adam' | 'am_michael' | 'am_santa'
> = {
Expand Down Expand Up @@ -1926,6 +1944,12 @@
phonemizer: kokoroNeuralPhonemizer('de'),
voices: kokoroVoices(['df_anna']),
};
const KOKORO_ZH_XNNPACK_FP32: KokoroTtsModel<KokoroZhVoice> = {
name: 'kokoro',
modelPaths: kokoroModelPaths('xnnpack', 'std', 'standard'),
phonemizer: KOKORO_ZH_PHONEMIZER,
voices: KOKORO_ZH_VOICES,
};

// Core ML counterparts: the same weights, phonemizer and voices as the XNNPACK
// presets, only the `.pte` files differ. Their token axis is fixed at 128, so
Expand Down Expand Up @@ -1987,6 +2011,12 @@
phonemizer: kokoroNeuralPhonemizer('de'),
voices: kokoroVoices(['df_anna']),
};
const KOKORO_ZH_COREML_FP32: KokoroTtsModel<KokoroZhVoice> = {
name: 'kokoro',
modelPaths: kokoroModelPaths('coreml', 'std', 'standard'),
phonemizer: KOKORO_ZH_PHONEMIZER,
voices: KOKORO_ZH_VOICES,
};

// =============================================================================
// Privacy Filter
Expand Down Expand Up @@ -3622,6 +3652,10 @@
XNNPACK_FP32: KOKORO_DE_XNNPACK_FP32,
COREML_FP32: KOKORO_DE_COREML_FP32,
}),
ZH: variants({
XNNPACK_FP32: KOKORO_ZH_XNNPACK_FP32,
COREML_FP32: KOKORO_ZH_COREML_FP32,
}),
},
},

Expand Down
Loading