-
Notifications
You must be signed in to change notification settings - Fork 1.7k
add TTS generation with GenerationConfig params C API #3115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| // c-api-examples/pocket-tts-en-c-api.c | ||
| // | ||
| // Copyright (c) 2026 Xiaoyingtao Corporation | ||
|
|
||
| // This file shows how to use sherpa-onnx C API | ||
| // for English TTS with Pocket TTS. | ||
| // | ||
| // clang-format off | ||
| /* | ||
| Usage | ||
|
|
||
|
|
||
| wget https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/sherpa-onnx-pocket-tts-int8-2026-01-26.tar.bz2 | ||
| tar xf sherpa-onnx-pocket-tts-int8-2026-01-26.tar.bz2 | ||
| rm sherpa-onnx-pocket-tts-int8-2026-01-26.tar.bz2 | ||
|
|
||
| ./pocket-tts-en-c-api | ||
|
|
||
| */ | ||
| // clang-format on | ||
|
|
||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
|
|
||
| #include "sherpa-onnx/c-api/c-api.h" | ||
|
|
||
| static int32_t ProgressCallback(const float *samples, int32_t num_samples, | ||
| float progress, void *arg) { | ||
| 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.pocket.lm_flow = | ||
| "./sherpa-onnx-pocket-tts-int8-2026-01-26/lm_flow.int8.onnx"; | ||
| config.model.pocket.lm_main = | ||
| "./sherpa-onnx-pocket-tts-int8-2026-01-26/lm_main.int8.onnx"; | ||
| config.model.pocket.encoder = | ||
| "./sherpa-onnx-pocket-tts-int8-2026-01-26/encoder.onnx"; | ||
| config.model.pocket.decoder = | ||
| "./sherpa-onnx-pocket-tts-int8-2026-01-26/decoder.int8.onnx"; | ||
| config.model.pocket.text_conditioner = | ||
| "./sherpa-onnx-pocket-tts-int8-2026-01-26/text_conditioner.onnx"; | ||
| config.model.pocket.vocab_json = | ||
| "./sherpa-onnx-pocket-tts-int8-2026-01-26/vocab.json"; | ||
| config.model.pocket.token_scores_json = | ||
| "./sherpa-onnx-pocket-tts-int8-2026-01-26/token_scores.json"; | ||
|
|
||
| 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-pocket-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); | ||
| if (!tts) { | ||
| fprintf(stderr, "Error create Offline TTS\n"); | ||
| return -1; | ||
| } | ||
| int32_t sid = 0; | ||
| float speed = 1.0; // larger -> faster in speech speed | ||
| GenerationConfig cfg = {0}; | ||
| const char *reference_audio_file = | ||
| "./sherpa-onnx-pocket-tts-int8-2026-01-26/test_wavs/bria.wav"; | ||
| const SherpaOnnxWave *wave = NULL; | ||
| wave = SherpaOnnxReadWave(reference_audio_file); | ||
| if (!wave) { | ||
| fprintf(stderr, "Failed to read %s\n", reference_audio_file); | ||
| SherpaOnnxDestroyOfflineTts(tts); | ||
| return -1; | ||
| } | ||
| cfg.reference_audio = wave->samples; | ||
| cfg.reference_audio_len = wave->num_samples; | ||
| cfg.reference_sample_rate = wave->sample_rate; | ||
| cfg.extra = "{\"max_reference_audio_len\": 10.0}"; | ||
|
|
||
| #if 0 | ||
| // If you don't want to use a callback, then please enable this branch | ||
| const SherpaOnnxGeneratedAudio *audio = | ||
| SherpaOnnxOfflineTtsGenerateWithConfig(tts, text, &cfg, NULL, NULL); | ||
| #else | ||
| const SherpaOnnxGeneratedAudio *audio = | ||
| SherpaOnnxOfflineTtsGenerateWithConfig(tts, text, &cfg, ProgressCallback, | ||
| NULL); | ||
| #endif | ||
|
|
||
| if (wave) SherpaOnnxFreeWave(wave); | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| fprintf(stderr, "Input text is: %s\n", text); | ||
| fprintf(stderr, "Speaker ID is: %d\n", sid); | ||
|
|
||
| if (audio) { | ||
| SherpaOnnxWriteWave(audio->samples, audio->n, audio->sample_rate, filename); | ||
| fprintf(stderr, "Saved to: %s\n", filename); | ||
| SherpaOnnxDestroyOfflineTtsGeneratedAudio(audio); | ||
| } | ||
|
|
||
| SherpaOnnxDestroyOfflineTts(tts); | ||
|
Comment on lines
+67
to
+110
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
const SherpaOnnxOfflineTts *tts = SherpaOnnxCreateOfflineTts(&config);
if (!tts) {
fprintf(stderr, "Failed to create TTS engine.\n");
return -1;
}
// 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
SherpaOnnxTtsGenerationConfig *gen_config =
SherpaOnnxTtsGenerationConfigCreate();
if (!gen_config) {
fprintf(stderr, "Error create config\n");
SherpaOnnxDestroyOfflineTts(tts);
return -1;
}
SherpaOnnxTtsGenerationConfigSetInt(gen_config, "sid", sid);
SherpaOnnxTtsGenerationConfigSetFloat(gen_config, "speed", speed);
SherpaOnnxTtsGenerationConfigSetStr(
gen_config, "reference_audio_file",
"./sherpa-onnx-pocket-tts-2026-01-26/test_wavs/bria.wav");
SherpaOnnxTtsGenerationConfigSetFloat(gen_config, "max_reference_audio_len", 10.0);
const char *config_str = SherpaOnnxTtsGenerationConfigToString(gen_config);
fprintf(stderr, "generation config: %s\n", config_str);
free((void *)config_str);
#if 0
// If you don't want to use a callback, then please enable this branch
const SherpaOnnxGeneratedAudio *audio =
SherpaOnnxOfflineTtsGenerateWithConfig(tts, text, gen_config);
#else
const SherpaOnnxGeneratedAudio *audio =
SherpaOnnxOfflineTtsGenerateWithConfigAndProgressCallback(
tts, text, gen_config, ProgressCallback);
#endif
SherpaOnnxTtsGenerationConfigDestroy(&gen_config);
if (!audio) {
fprintf(stderr, "Failed to generate audio.\n");
SherpaOnnxDestroyOfflineTts(tts);
return -1;
}
SherpaOnnxWriteWave(audio->samples, audio->n, audio->sample_rate, filename);
SherpaOnnxDestroyOfflineTtsGeneratedAudio(audio);
SherpaOnnxDestroyOfflineTts(tts); |
||
|
|
||
| return 0; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,8 @@ | |
| #include <utility> | ||
| #include <vector> | ||
|
|
||
| #include "nlohmann/json.hpp" | ||
|
|
||
| #if __OHOS__ | ||
| #include "rawfile/raw_file_manager.h" | ||
| #endif | ||
|
|
@@ -1286,6 +1288,22 @@ static sherpa_onnx::OfflineTtsConfig GetOfflineTtsConfig( | |
| tts_config.model.zipvoice.guidance_scale = | ||
| SHERPA_ONNX_OR(config->model.zipvoice.guidance_scale, 1.0f); | ||
|
|
||
| tts_config.model.pocket.lm_flow = | ||
| SHERPA_ONNX_OR(config->model.pocket.lm_flow, ""); | ||
| tts_config.model.pocket.lm_main = | ||
| SHERPA_ONNX_OR(config->model.pocket.lm_main, ""); | ||
| tts_config.model.pocket.encoder = | ||
| SHERPA_ONNX_OR(config->model.pocket.encoder, ""); | ||
| tts_config.model.pocket.decoder = | ||
| SHERPA_ONNX_OR(config->model.pocket.decoder, ""); | ||
| tts_config.model.pocket.text_conditioner = | ||
| SHERPA_ONNX_OR(config->model.pocket.text_conditioner, ""); | ||
| tts_config.model.pocket.vocab_json = | ||
| SHERPA_ONNX_OR(config->model.pocket.vocab_json, ""); | ||
| tts_config.model.pocket.token_scores_json = | ||
| SHERPA_ONNX_OR(config->model.pocket.token_scores_json, ""); | ||
|
|
||
| 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"); | ||
|
|
@@ -1359,6 +1377,52 @@ static const SherpaOnnxGeneratedAudio *SherpaOnnxOfflineTtsGenerateInternal( | |
| return ans; | ||
| } | ||
|
|
||
| static const SherpaOnnxGeneratedAudio *SherpaOnnxOfflineTtsGenerateInternal( | ||
| const SherpaOnnxOfflineTts *tts, const char *text, GenerationConfig *config, | ||
| std::function<int32_t(const float *, int32_t, float)> callback) { | ||
| sherpa_onnx::GenerationConfig cfg; | ||
| if (config->reference_audio_len > 0 && config->reference_audio) { | ||
| cfg.reference_audio.assign( | ||
| config->reference_audio, | ||
| config->reference_audio + config->reference_audio_len); | ||
| } | ||
| if (config->silence_scale > 0) cfg.silence_scale = config->silence_scale; | ||
| if (config->speed > 0) cfg.speed = config->speed; | ||
| cfg.sid = config->sid; | ||
| if (config->reference_sample_rate > 0) | ||
| cfg.reference_sample_rate = config->reference_sample_rate; | ||
| if (config->reference_text) | ||
| cfg.reference_text = config->reference_text; | ||
| if (config->num_steps > 0) cfg.num_steps = config->num_steps; | ||
| if (config->extra) { | ||
| try { | ||
| auto json = nlohmann::json::parse(config->extra); | ||
| for (auto &[k, v] : json.items()) { | ||
| cfg.extra.insert_or_assign(std::string(k), v.dump()); | ||
| } | ||
| } catch (const nlohmann::json::parse_error &e) { | ||
| SHERPA_ONNX_LOGE("Failed to parse extra JSON: %s", e.what()); | ||
| } | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| sherpa_onnx::GeneratedAudio audio = tts->impl->Generate(text, cfg, callback); | ||
|
|
||
| if (audio.samples.empty()) { | ||
| return nullptr; | ||
| } | ||
|
|
||
| SherpaOnnxGeneratedAudio *ans = new SherpaOnnxGeneratedAudio; | ||
|
|
||
| float *samples = new float[audio.samples.size()]; | ||
| std::copy(audio.samples.begin(), audio.samples.end(), samples); | ||
|
|
||
| ans->samples = samples; | ||
| ans->n = audio.samples.size(); | ||
| ans->sample_rate = audio.sample_rate; | ||
|
|
||
| return ans; | ||
| } | ||
|
Comment on lines
+1380
to
+1424
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add null check for The function dereferences 🛡️ Proposed fix static const SherpaOnnxGeneratedAudio *SherpaOnnxOfflineTtsGenerateInternal(
const SherpaOnnxOfflineTts *tts, const char *text, GenerationConfig *config,
std::function<int32_t(const float *, int32_t, float)> callback) {
+ if (!config) {
+ SHERPA_ONNX_LOGE("config is null");
+ return nullptr;
+ }
+
sherpa_onnx::GenerationConfig *cfg;
if (config->pvt) {🤖 Prompt for AI Agents |
||
|
|
||
| const SherpaOnnxGeneratedAudio *SherpaOnnxOfflineTtsGenerate( | ||
| const SherpaOnnxOfflineTts *tts, const char *text, int32_t sid, | ||
| float speed) { | ||
|
|
@@ -1444,6 +1508,18 @@ const SherpaOnnxGeneratedAudio *SherpaOnnxOfflineTtsGenerateWithZipvoice( | |
| return ans; | ||
| } | ||
|
|
||
| const SherpaOnnxGeneratedAudio *SherpaOnnxOfflineTtsGenerateWithConfig( | ||
| const SherpaOnnxOfflineTts *tts, const char *text, GenerationConfig *config, | ||
| SherpaOnnxGeneratedAudioProgressCallbackWithArg callback, void *arg) { | ||
| auto wrapper = [callback, arg](const float *samples, int32_t n, | ||
| float progress) { | ||
| if (!callback) return 1; | ||
| return callback(samples, n, progress, arg); | ||
| }; | ||
|
|
||
| return SherpaOnnxOfflineTtsGenerateInternal(tts, text, config, std::move(wrapper)); | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| void SherpaOnnxDestroyOfflineTtsGeneratedAudio( | ||
| const SherpaOnnxGeneratedAudio *p) { | ||
| if (p) { | ||
|
|
@@ -1513,14 +1589,21 @@ const SherpaOnnxGeneratedAudio *SherpaOnnxOfflineTtsGenerateWithZipvoice( | |
| const SherpaOnnxOfflineTts *tts, const char *text, const char *prompt_text, | ||
| const float *prompt_samples, int32_t n_prompt, int32_t prompt_sr, | ||
| float speed, int32_t num_steps) { | ||
| SHERPA_ONNX_LOGE("TTS is not enabled. Please rebuild sherpa-onnx"); | ||
| return nullptr; | ||
| } | ||
|
|
||
| const SherpaOnnxGeneratedAudio *SherpaOnnxOfflineTtsGenerateWithConfig( | ||
| const SherpaOnnxOfflineTts *tts, const char *text, GenerationConfig *config, | ||
| SherpaOnnxGeneratedAudioProgressCallbackWithArg callback, void *arg) { | ||
| SHERPA_ONNX_LOGE("TTS is not enabled. Please rebuild sherpa-onnx"); | ||
| return nullptr; | ||
| } | ||
|
|
||
| void SherpaOnnxDestroyOfflineTtsGeneratedAudio( | ||
| const SherpaOnnxGeneratedAudio *p) { | ||
| SHERPA_ONNX_LOGE("TTS is not enabled. Please rebuild sherpa-onnx"); | ||
| } | ||
|
|
||
| #endif // SHERPA_ONNX_ENABLE_TTS == 1 | ||
|
|
||
| int32_t SherpaOnnxWriteWave(const float *samples, int32_t n, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.