diff --git a/.github/workflows/c-api.yaml b/.github/workflows/c-api.yaml index 0143851c69..dadc7532e1 100644 --- a/.github/workflows/c-api.yaml +++ b/.github/workflows/c-api.yaml @@ -85,6 +85,41 @@ jobs: otool -L ./install/lib/libsherpa-onnx-c-api.dylib fi + - name: Test KittenTTS + shell: bash + run: | + name=kitten-tts-en-c-api + gcc -o $name ./c-api-examples/$name.c \ + -I ./build/install/include \ + -L ./build/install/lib/ \ + -l sherpa-onnx-c-api \ + -l onnxruntime + + ls -lh $name + + if [[ ${{ matrix.os }} == ubuntu-latest || ${{ matrix.os }} == ubuntu-22.04-arm ]]; then + ldd ./$name + echo "----" + readelf -d ./$name + fi + + curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/kitten-nano-en-v0_1-fp16.tar.bz2 + tar xf kitten-nano-en-v0_1-fp16.tar.bz2 + rm kitten-nano-en-v0_1-fp16.tar.bz2 + + export LD_LIBRARY_PATH=$PWD/build/install/lib:$LD_LIBRARY_PATH + export DYLD_LIBRARY_PATH=$PWD/build/install/lib:$DYLD_LIBRARY_PATH + + ./$name + + rm $name + rm -rf kitten-nano-en-v0_1-fp16 + + - uses: actions/upload-artifact@v4 + with: + name: kitten-tts-wavs-${{ matrix.os }} + path: ./generated-kitten-en.wav + - name: Test streaming zipformer with homophone replacer shell: bash run: | diff --git a/c-api-examples/CMakeLists.txt b/c-api-examples/CMakeLists.txt index 4e62d5e5bb..45e9871621 100644 --- a/c-api-examples/CMakeLists.txt +++ b/c-api-examples/CMakeLists.txt @@ -23,6 +23,9 @@ if(SHERPA_ONNX_ENABLE_TTS) add_executable(kokoro-tts-en-c-api kokoro-tts-en-c-api.c) target_link_libraries(kokoro-tts-en-c-api sherpa-onnx-c-api) + add_executable(kitten-tts-en-c-api kitten-tts-en-c-api.c) + target_link_libraries(kitten-tts-en-c-api sherpa-onnx-c-api) + add_executable(kokoro-tts-zh-en-c-api kokoro-tts-zh-en-c-api.c) target_link_libraries(kokoro-tts-zh-en-c-api sherpa-onnx-c-api) endif() diff --git a/c-api-examples/kitten-tts-en-c-api.c b/c-api-examples/kitten-tts-en-c-api.c new file mode 100644 index 0000000000..8a1842ef46 --- /dev/null +++ b/c-api-examples/kitten-tts-en-c-api.c @@ -0,0 +1,83 @@ +// c-api-examples/kitten-tts-en-c-api.c +// +// Copyright (c) 2025 Xiaomi Corporation + +// This file shows how to use sherpa-onnx C API +// for English TTS with Kitten. +// +// clang-format off +/* +Usage + +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/kitten-nano-en-v0_1-fp16.tar.bz2 +tar xf kitten-nano-en-v0_1-fp16.tar.bz2 +rm kitten-nano-en-v0_1-fp16.tar.bz2 + +./kitten-tts-en-c-api + + */ +// clang-format on + +#include +#include +#include + +#include "sherpa-onnx/c-api/c-api.h" + +static int32_t ProgressCallback(const float *samples, int32_t num_samples, + float progress) { + fprintf(stderr, "Progress: %.3f%%\n", progress * 100); + // return 1 to continue generating + // return 0 to stop generating + return 1; +} + +int32_t main(int32_t argc, char *argv[]) { + SherpaOnnxOfflineTtsConfig config; + memset(&config, 0, sizeof(config)); + config.model.kitten.model = "./kitten-nano-en-v0_1-fp16/model.fp16.onnx"; + config.model.kitten.voices = "./kitten-nano-en-v0_1-fp16/voices.bin"; + config.model.kitten.tokens = "./kitten-nano-en-v0_1-fp16/tokens.txt"; + config.model.kitten.data_dir = "./kitten-nano-en-v0_1-fp16/espeak-ng-data"; + + config.model.num_threads = 2; + + // If you don't want to see debug messages, please set it to 0 + config.model.debug = 1; + + const char *filename = "./generated-kitten-en.wav"; + const char *text = + "Today as always, men fall into two groups: slaves and free men. Whoever " + "does not have two-thirds of his day for himself, is a slave, whatever " + "he may be: a statesman, a businessman, an official, or a scholar. " + "Friends fell out often because life was changing so fast. The easiest " + "thing in the world was to lose touch with someone."; + + const SherpaOnnxOfflineTts *tts = SherpaOnnxCreateOfflineTts(&config); + // mapping of sid to voice name + // 0->af, 1->af_bella, 2->af_nicole, 3->af_sarah, 4->af_sky, 5->am_adam + // 6->am_michael, 7->bf_emma, 8->bf_isabella, 9->bm_george, 10->bm_lewis + int32_t sid = 0; + float speed = 1.0; // larger -> faster in speech speed + +#if 0 + // If you don't want to use a callback, then please enable this branch + const SherpaOnnxGeneratedAudio *audio = + SherpaOnnxOfflineTtsGenerate(tts, text, sid, speed); +#else + const SherpaOnnxGeneratedAudio *audio = + SherpaOnnxOfflineTtsGenerateWithProgressCallback(tts, text, sid, speed, + ProgressCallback); +#endif + + SherpaOnnxWriteWave(audio->samples, audio->n, audio->sample_rate, filename); + + SherpaOnnxDestroyOfflineTtsGeneratedAudio(audio); + SherpaOnnxDestroyOfflineTts(tts); + + fprintf(stderr, "Input text is: %s\n", text); + fprintf(stderr, "Speaker ID is: %d\n", sid); + fprintf(stderr, "Saved to: %s\n", filename); + + return 0; +} diff --git a/c-api-examples/kokoro-tts-en-c-api.c b/c-api-examples/kokoro-tts-en-c-api.c index 44e6c28d89..581b125fcf 100644 --- a/c-api-examples/kokoro-tts-en-c-api.c +++ b/c-api-examples/kokoro-tts-en-c-api.c @@ -77,7 +77,7 @@ int32_t main(int32_t argc, char *argv[]) { SherpaOnnxDestroyOfflineTts(tts); fprintf(stderr, "Input text is: %s\n", text); - fprintf(stderr, "Speaker ID is is: %d\n", sid); + fprintf(stderr, "Speaker ID is: %d\n", sid); fprintf(stderr, "Saved to: %s\n", filename); return 0; diff --git a/c-api-examples/kokoro-tts-zh-en-c-api.c b/c-api-examples/kokoro-tts-zh-en-c-api.c index 4d998fb71d..4657b49b8c 100644 --- a/c-api-examples/kokoro-tts-zh-en-c-api.c +++ b/c-api-examples/kokoro-tts-zh-en-c-api.c @@ -75,7 +75,7 @@ int32_t main(int32_t argc, char *argv[]) { SherpaOnnxDestroyOfflineTts(tts); fprintf(stderr, "Input text is: %s\n", text); - fprintf(stderr, "Speaker ID is is: %d\n", sid); + fprintf(stderr, "Speaker ID is: %d\n", sid); fprintf(stderr, "Saved to: %s\n", filename); return 0; diff --git a/c-api-examples/matcha-tts-en-c-api.c b/c-api-examples/matcha-tts-en-c-api.c index 2a4a815092..56531fc665 100644 --- a/c-api-examples/matcha-tts-en-c-api.c +++ b/c-api-examples/matcha-tts-en-c-api.c @@ -80,7 +80,7 @@ int32_t main(int32_t argc, char *argv[]) { SherpaOnnxDestroyOfflineTts(tts); fprintf(stderr, "Input text is: %s\n", text); - fprintf(stderr, "Speaker ID is is: %d\n", sid); + fprintf(stderr, "Speaker ID is: %d\n", sid); fprintf(stderr, "Saved to: %s\n", filename); return 0; diff --git a/c-api-examples/matcha-tts-zh-c-api.c b/c-api-examples/matcha-tts-zh-c-api.c index 68cba28527..cfd031d40f 100644 --- a/c-api-examples/matcha-tts-zh-c-api.c +++ b/c-api-examples/matcha-tts-zh-c-api.c @@ -80,7 +80,7 @@ int32_t main(int32_t argc, char *argv[]) { SherpaOnnxDestroyOfflineTts(tts); fprintf(stderr, "Input text is: %s\n", text); - fprintf(stderr, "Speaker ID is is: %d\n", sid); + fprintf(stderr, "Speaker ID is: %d\n", sid); fprintf(stderr, "Saved to: %s\n", filename); return 0; diff --git a/c-api-examples/offline-tts-c-api.c b/c-api-examples/offline-tts-c-api.c index eaa25af392..c0e6f214bf 100644 --- a/c-api-examples/offline-tts-c-api.c +++ b/c-api-examples/offline-tts-c-api.c @@ -240,7 +240,7 @@ int32_t main(int32_t argc, char *argv[]) { SherpaOnnxDestroyOfflineTts(tts); fprintf(stderr, "Input text is: %s\n", text); - fprintf(stderr, "Speaker ID is is: %d\n", sid); + fprintf(stderr, "Speaker ID is: %d\n", sid); fprintf(stderr, "Saved to: %s\n", filename); free((void *)filename); diff --git a/sherpa-onnx/c-api/c-api.cc b/sherpa-onnx/c-api/c-api.cc index e4d22ca8a8..28bd4ae5b7 100644 --- a/sherpa-onnx/c-api/c-api.cc +++ b/sherpa-onnx/c-api/c-api.cc @@ -1207,6 +1207,18 @@ static sherpa_onnx::OfflineTtsConfig GetOfflineTtsConfig( SHERPA_ONNX_OR(config->model.kokoro.lexicon, ""); tts_config.model.kokoro.lang = SHERPA_ONNX_OR(config->model.kokoro.lang, ""); + // kitten + tts_config.model.kitten.model = + SHERPA_ONNX_OR(config->model.kitten.model, ""); + tts_config.model.kitten.voices = + SHERPA_ONNX_OR(config->model.kitten.voices, ""); + tts_config.model.kitten.tokens = + SHERPA_ONNX_OR(config->model.kitten.tokens, ""); + tts_config.model.kitten.data_dir = + SHERPA_ONNX_OR(config->model.kitten.data_dir, ""); + tts_config.model.kitten.length_scale = + SHERPA_ONNX_OR(config->model.kitten.length_scale, 1.0); + tts_config.model.num_threads = SHERPA_ONNX_OR(config->model.num_threads, 1); tts_config.model.debug = config->model.debug; tts_config.model.provider = SHERPA_ONNX_OR(config->model.provider, "cpu"); diff --git a/sherpa-onnx/c-api/c-api.h b/sherpa-onnx/c-api/c-api.h index afb1717886..e75e9e1b06 100644 --- a/sherpa-onnx/c-api/c-api.h +++ b/sherpa-onnx/c-api/c-api.h @@ -1032,6 +1032,15 @@ SHERPA_ONNX_API typedef struct SherpaOnnxOfflineTtsKokoroModelConfig { const char *lang; } SherpaOnnxOfflineTtsKokoroModelConfig; +SHERPA_ONNX_API typedef struct SherpaOnnxOfflineTtsKittenModelConfig { + const char *model; + const char *voices; + const char *tokens; + const char *data_dir; + + float length_scale; // < 1, faster in speech speed; > 1, slower in speed +} SherpaOnnxOfflineTtsKittenModelConfig; + SHERPA_ONNX_API typedef struct SherpaOnnxOfflineTtsModelConfig { SherpaOnnxOfflineTtsVitsModelConfig vits; int32_t num_threads; @@ -1039,6 +1048,7 @@ SHERPA_ONNX_API typedef struct SherpaOnnxOfflineTtsModelConfig { const char *provider; SherpaOnnxOfflineTtsMatchaModelConfig matcha; SherpaOnnxOfflineTtsKokoroModelConfig kokoro; + SherpaOnnxOfflineTtsKittenModelConfig kitten; } SherpaOnnxOfflineTtsModelConfig; SHERPA_ONNX_API typedef struct SherpaOnnxOfflineTtsConfig {