Files
Fangjun Kuang 0b3647632f Remove espeak-ng and piper-phonemize from sherpa-onnx (#4055)
Fixes #3731

espeak-ng and piper-phonemize are no longer built into sherpa-onnx.

Text frontends are now:

- **lexicon** — built in, no extra dependency
- **phonemize** — phonemize outside sherpa-onnx (e.g. [piper-phonemize](https://github.com/csukuangfj/piper-phonemize)) and pass the result in. That library is GPL, so it stays optional: sherpa-onnx is GPL-free after this PR.

## API

- `SherpaOnnxGenerationConfig` gains `phoneme_codepoints` and `tokens` (mutually exclusive alternatives to `text`)
- `data_dir` on the TTS model configs is deprecated and ignored; the ABI slot is kept
- all bindings updated: C/C++, Python, Java, Kotlin, Go, Rust, C#, Swift, Pascal, Dart/Flutter, node-addon, WASM, HarmonyOS
2026-10-20 11:09:04 +08:00

111 lines
3.6 KiB
Python

#!/usr/bin/env python3
#
# Copyright (c) 2026 Xiaomi Corporation
"""
This file demonstrates how to use sherpa-onnx Python API
for Chinese/English zero-shot TTS with ZipVoice.
Usage:
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/sherpa-onnx-zipvoice-distill-int8-zh-en-emilia.tar.bz2
tar xvf sherpa-onnx-zipvoice-distill-int8-zh-en-emilia.tar.bz2
rm sherpa-onnx-zipvoice-distill-int8-zh-en-emilia.tar.bz2
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/vocoder-models/vocos_24khz.onnx
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/lexicon-en-us.txt
python3 ./python-api-examples/zipvoice-tts.py
You can find more models at
https://github.com/k2-fsa/sherpa-onnx/releases/tag/tts-models
Please see
https://k2-fsa.github.io/sherpa/onnx/tts/zipvoice.html
for details.
"""
import time
from pathlib import Path
import sherpa_onnx
import soundfile as sf
def create_tts():
tts_config = sherpa_onnx.OfflineTtsConfig(
model=sherpa_onnx.OfflineTtsModelConfig(
zipvoice=sherpa_onnx.OfflineTtsZipvoiceModelConfig(
tokens="./sherpa-onnx-zipvoice-distill-int8-zh-en-emilia/tokens.txt",
encoder="./sherpa-onnx-zipvoice-distill-int8-zh-en-emilia/encoder.int8.onnx",
decoder="./sherpa-onnx-zipvoice-distill-int8-zh-en-emilia/decoder.int8.onnx",
lexicon="./sherpa-onnx-zipvoice-distill-int8-zh-en-emilia/lexicon.txt,./lexicon-en-us.txt",
vocoder="./vocos_24khz.onnx",
),
debug=False,
num_threads=2,
provider="cpu",
)
)
if not tts_config.validate():
raise ValueError(
"Please read the previous error messages and re-check your config"
)
return sherpa_onnx.OfflineTts(tts_config)
def main():
reference_audio_file = (
"./sherpa-onnx-zipvoice-distill-int8-zh-en-emilia/test_wavs/leijun-1.wav"
)
if not Path(reference_audio_file).is_file():
raise ValueError(f"Reference audio {reference_audio_file} does not exist")
tts = create_tts()
reference_audio, sample_rate = sf.read(reference_audio_file, dtype="float32")
if reference_audio.ndim > 1:
reference_audio = reference_audio[:, 0] # only use the first channel
reference_text = "那还是三十六年前, 一九八七年. 我呢考上了武汉大学的计算机系."
text = "小米的价值观是真诚, 热爱. 真诚,就是不欺人也不自欺. 热爱, 就是全心投入并享受其中."
gen_config = sherpa_onnx.GenerationConfig()
gen_config.reference_audio = reference_audio
gen_config.reference_sample_rate = sample_rate
gen_config.reference_text = reference_text
gen_config.num_steps = 4
gen_config.extra["min_char_in_sentence"] = "30"
start = time.time()
audio = tts.generate(text, gen_config)
end = time.time()
if len(audio.samples) == 0:
print("Error in generating audios. Please read previous error messages.")
return
elapsed_seconds = end - start
audio_duration = len(audio.samples) / audio.sample_rate
real_time_factor = elapsed_seconds / audio_duration
output_filename = "./generated-zipvoice-zh-en-python.wav"
sf.write(
output_filename,
audio.samples,
samplerate=audio.sample_rate,
subtype="PCM_16",
)
print(f"Saved to {output_filename}")
print(f"The text is '{text}'")
print(f"Elapsed seconds: {elapsed_seconds:.3f}")
print(f"Audio duration in seconds: {audio_duration:.3f}")
print(f"RTF: {elapsed_seconds:.3f}/{audio_duration:.3f} = {real_time_factor:.3f}")
if __name__ == "__main__":
main()