Skip to content

Commit 71cf9c9

Browse files
authored
Add CXX API for supertonic TTS (#3280)
This pull request integrates Supertonic Text-to-Speech (TTS) functionality into the sherpa-onnx library, expanding its TTS capabilities. It provides both C and C++ APIs for Supertonic, along with example programs to guide users on how to utilize this new feature for generating speech from text.
1 parent 9b2fc65 commit 71cf9c9

10 files changed

Lines changed: 311 additions & 1 deletion

File tree

.github/workflows/c-api.yaml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,43 @@ jobs:
170170
name: pocket-tts-wavs-${{ matrix.os }}
171171
path: ./generated-pocket-en.wav
172172

173+
- name: Test SupertonicTTS
174+
shell: bash
175+
run: |
176+
name=supertonic-tts-en-c-api
177+
gcc -o $name ./c-api-examples/$name.c \
178+
-I ./build/install/include \
179+
-L ./build/install/lib/ \
180+
-l sherpa-onnx-c-api \
181+
-l onnxruntime
182+
183+
ls -lh $name
184+
185+
if [[ ${{ matrix.os }} == ubuntu-latest || ${{ matrix.os }} == ubuntu-22.04-arm ]]; then
186+
ldd ./$name
187+
echo "----"
188+
readelf -d ./$name
189+
fi
190+
191+
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/sherpa-onnx-supertonic-tts-int8-2026-03-06.tar.bz2
192+
tar xf sherpa-onnx-supertonic-tts-int8-2026-03-06.tar.bz2
193+
rm sherpa-onnx-supertonic-tts-int8-2026-03-06.tar.bz2
194+
195+
export LD_LIBRARY_PATH=$PWD/build/install/lib:$LD_LIBRARY_PATH
196+
export DYLD_LIBRARY_PATH=$PWD/build/install/lib:$DYLD_LIBRARY_PATH
197+
198+
./$name
199+
200+
rm $name
201+
rm -rf sherpa-onnx-supertonic-tts-int8-2026-03-06
202+
203+
ls -lh ./generated-supertonic-en-c.wav
204+
205+
- uses: actions/upload-artifact@v4
206+
with:
207+
name: supertonic-tts-wavs-${{ matrix.os }}
208+
path: ./generated-supertonic-en-c.wav
209+
173210
- name: Test FunASR Nano
174211
shell: bash
175212
run: |

.github/workflows/cxx-api.yaml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,47 @@ jobs:
187187
name: pocket-tts-wavs-${{ matrix.os }}
188188
path: ./generated-pocket-en-cxx.wav
189189

190+
- name: Test SupertonicTTS
191+
shell: bash
192+
run: |
193+
name=supertonic-tts-en-cxx-api
194+
g++ -std=c++17 -o $name ./cxx-api-examples/$name.cc \
195+
-I ./build/install/include \
196+
-L ./build/install/lib/ \
197+
-l sherpa-onnx-cxx-api \
198+
-l sherpa-onnx-c-api \
199+
-l onnxruntime
200+
201+
ls -lh $name
202+
203+
if [[ ${{ matrix.os }} == ubuntu-latest || ${{ matrix.os }} == ubuntu-22.04-arm ]]; then
204+
ls -lh ./$name
205+
ldd ./$name
206+
echo "----"
207+
readelf -d ./$name
208+
fi
209+
210+
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/sherpa-onnx-supertonic-tts-int8-2026-03-06.tar.bz2
211+
tar xf sherpa-onnx-supertonic-tts-int8-2026-03-06.tar.bz2
212+
rm sherpa-onnx-supertonic-tts-int8-2026-03-06.tar.bz2
213+
214+
echo "---"
215+
216+
export LD_LIBRARY_PATH=$PWD/build/install/lib:$LD_LIBRARY_PATH
217+
export DYLD_LIBRARY_PATH=$PWD/build/install/lib:$DYLD_LIBRARY_PATH
218+
219+
./$name
220+
221+
rm -rf sherpa-onnx-supertonic-tts-int8-2026-03-06
222+
rm -v ./$name
223+
224+
ls -lh ./generated-supertonic-en-cxx.wav
225+
226+
- uses: actions/upload-artifact@v4
227+
with:
228+
name: supertonic-tts-wavs-${{ matrix.os }}
229+
path: ./generated-supertonic-en-cxx.wav
230+
190231
- name: Test FunASR Nano
191232
shell: bash
192233
run: |

c-api-examples/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ if(SHERPA_ONNX_ENABLE_TTS)
3131

3232
add_executable(pocket-tts-en-c-api pocket-tts-en-c-api.c)
3333
target_link_libraries(pocket-tts-en-c-api sherpa-onnx-c-api)
34+
35+
add_executable(supertonic-tts-en-c-api supertonic-tts-en-c-api.c)
36+
target_link_libraries(supertonic-tts-en-c-api sherpa-onnx-c-api)
3437
endif()
3538

3639
if(SHERPA_ONNX_ENABLE_SPEAKER_DIARIZATION)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// c-api-examples/supertonic-tts-en-c-api.c
2+
//
3+
// Copyright (c) 2026 zengyw
4+
5+
// This file shows how to use sherpa-onnx C API
6+
// for English TTS with Supertonic.
7+
//
8+
// clang-format off
9+
/*
10+
Usage
11+
12+
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/sherpa-onnx-supertonic-tts-int8-2026-03-06.tar.bz2
13+
tar xf sherpa-onnx-supertonic-tts-int8-2026-03-06.tar.bz2
14+
rm sherpa-onnx-supertonic-tts-int8-2026-03-06.tar.bz2
15+
16+
./supertonic-tts-en-c-api
17+
18+
*/
19+
// clang-format on
20+
21+
#include <stdio.h>
22+
#include <stdlib.h>
23+
#include <string.h>
24+
25+
#include "sherpa-onnx/c-api/c-api.h"
26+
27+
static int32_t ProgressCallback(const float* samples, int32_t num_samples,
28+
float progress, void* arg) {
29+
fprintf(stderr, "Progress: %.3f%%\n", progress * 100);
30+
// return 1 to continue generating
31+
// return 0 to stop generating
32+
return 1;
33+
}
34+
35+
int32_t main(int32_t argc, char* argv[]) {
36+
SherpaOnnxOfflineTtsConfig config;
37+
memset(&config, 0, sizeof(config));
38+
config.model.supertonic.duration_predictor =
39+
"./sherpa-onnx-supertonic-tts-int8-2026-03-06/"
40+
"duration_predictor.int8.onnx";
41+
config.model.supertonic.text_encoder =
42+
"./sherpa-onnx-supertonic-tts-int8-2026-03-06/text_encoder.int8.onnx";
43+
config.model.supertonic.vector_estimator =
44+
"./sherpa-onnx-supertonic-tts-int8-2026-03-06/vector_estimator.int8.onnx";
45+
config.model.supertonic.vocoder =
46+
"./sherpa-onnx-supertonic-tts-int8-2026-03-06/vocoder.int8.onnx";
47+
config.model.supertonic.tts_json =
48+
"./sherpa-onnx-supertonic-tts-int8-2026-03-06/tts.json";
49+
config.model.supertonic.unicode_indexer =
50+
"./sherpa-onnx-supertonic-tts-int8-2026-03-06/unicode_indexer.bin";
51+
config.model.supertonic.voice_style =
52+
"./sherpa-onnx-supertonic-tts-int8-2026-03-06/voice.bin";
53+
54+
config.model.num_threads = 2;
55+
56+
// If you don't want to see debug messages, please set it to 0
57+
config.model.debug = 1;
58+
59+
const char* filename = "./generated-supertonic-en-c.wav";
60+
const char* text =
61+
"Today as always, men fall into two groups: slaves and free men. Whoever "
62+
"does not have two-thirds of his day for himself, is a slave, whatever "
63+
"he may be: a statesman, a businessman, an official, or a scholar.";
64+
65+
const SherpaOnnxOfflineTts* tts = SherpaOnnxCreateOfflineTts(&config);
66+
if (!tts) {
67+
fprintf(stderr, "Error create Offline TTS\n");
68+
return -1;
69+
}
70+
71+
SherpaOnnxGenerationConfig cfg = {0};
72+
cfg.sid = 6;
73+
cfg.num_steps = 5;
74+
cfg.speed = 1.25f; // larger -> faster
75+
cfg.extra = "{\"lang\": \"en\"}";
76+
77+
const SherpaOnnxGeneratedAudio* audio =
78+
SherpaOnnxOfflineTtsGenerateWithConfig(tts, text, &cfg, ProgressCallback,
79+
NULL);
80+
81+
fprintf(stderr, "Input text is: %s\n", text);
82+
83+
if (audio) {
84+
SherpaOnnxWriteWave(audio->samples, audio->n, audio->sample_rate, filename);
85+
fprintf(stderr, "Saved to: %s\n", filename);
86+
SherpaOnnxDestroyOfflineTtsGeneratedAudio(audio);
87+
}
88+
89+
SherpaOnnxDestroyOfflineTts(tts);
90+
91+
return 0;
92+
}

cxx-api-examples/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,4 +191,7 @@ if(SHERPA_ONNX_ENABLE_TTS)
191191

192192
add_executable(kokoro-tts-zh-en-cxx-api ./kokoro-tts-zh-en-cxx-api.cc)
193193
target_link_libraries(kokoro-tts-zh-en-cxx-api sherpa-onnx-cxx-api)
194+
195+
add_executable(supertonic-tts-en-cxx-api ./supertonic-tts-en-cxx-api.cc)
196+
target_link_libraries(supertonic-tts-en-cxx-api sherpa-onnx-cxx-api)
194197
endif()
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// cxx-api-examples/supertonic-tts-en-cxx-api.cc
2+
//
3+
// Copyright (c) 2026 zengyw
4+
5+
// This file shows how to use sherpa-onnx CXX API
6+
// for English TTS with Supertonic.
7+
//
8+
// clang-format off
9+
/*
10+
Usage
11+
12+
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/sherpa-onnx-supertonic-tts-int8-2026-03-06.tar.bz2
13+
tar xf sherpa-onnx-supertonic-tts-int8-2026-03-06.tar.bz2
14+
rm sherpa-onnx-supertonic-tts-int8-2026-03-06.tar.bz2
15+
16+
./supertonic-tts-en-cxx-api
17+
18+
*/
19+
// clang-format on
20+
21+
#include <cstdint>
22+
#include <cstdio>
23+
#include <string>
24+
25+
#include "sherpa-onnx/c-api/cxx-api.h"
26+
27+
static int32_t ProgressCallback(const float *samples, int32_t num_samples,
28+
float progress, void *arg) {
29+
fprintf(stderr, "Progress: %.3f%%\n", progress * 100);
30+
// return 1 to continue generating
31+
// return 0 to stop generating
32+
return 1;
33+
}
34+
35+
int32_t main(int32_t argc, char *argv[]) {
36+
using namespace sherpa_onnx::cxx; // NOLINT
37+
OfflineTtsConfig config;
38+
39+
config.model.supertonic.duration_predictor =
40+
"./sherpa-onnx-supertonic-tts-int8-2026-03-06/"
41+
"duration_predictor.int8.onnx";
42+
config.model.supertonic.text_encoder =
43+
"./sherpa-onnx-supertonic-tts-int8-2026-03-06/text_encoder.int8.onnx";
44+
config.model.supertonic.vector_estimator =
45+
"./sherpa-onnx-supertonic-tts-int8-2026-03-06/vector_estimator.int8.onnx";
46+
config.model.supertonic.vocoder =
47+
"./sherpa-onnx-supertonic-tts-int8-2026-03-06/vocoder.int8.onnx";
48+
config.model.supertonic.tts_json =
49+
"./sherpa-onnx-supertonic-tts-int8-2026-03-06/tts.json";
50+
config.model.supertonic.unicode_indexer =
51+
"./sherpa-onnx-supertonic-tts-int8-2026-03-06/unicode_indexer.bin";
52+
config.model.supertonic.voice_style =
53+
"./sherpa-onnx-supertonic-tts-int8-2026-03-06/voice.bin";
54+
55+
config.model.num_threads = 2;
56+
57+
// If you don't want to see debug messages, please set it to 0
58+
config.model.debug = 1;
59+
60+
std::string filename = "./generated-supertonic-en-cxx.wav";
61+
std::string text =
62+
"Today as always, men fall into two groups: slaves and free men. Whoever "
63+
"does not have two-thirds of his day for himself, is a slave, whatever "
64+
"he may be: a statesman, a businessman, an official, or a scholar.";
65+
66+
auto tts = OfflineTts::Create(config);
67+
68+
GenerationConfig gen_config;
69+
gen_config.sid = 6;
70+
gen_config.num_steps = 5;
71+
gen_config.speed = 1.25; // larger -> faster
72+
gen_config.extra["lang"] = "en";
73+
74+
// Use GenerationConfig for Supertonic.
75+
GeneratedAudio audio = tts.Generate(text, gen_config, ProgressCallback);
76+
77+
WriteWave(filename, {audio.samples, audio.sample_rate});
78+
79+
fprintf(stderr, "Input text is: %s\n", text.c_str());
80+
fprintf(stderr, "Saved to: %s\n", filename.c_str());
81+
82+
return 0;
83+
}

sherpa-onnx/c-api/c-api.cc

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,22 @@ static sherpa_onnx::OfflineTtsConfig GetOfflineTtsConfig(
14291429
tts_config.model.pocket.voice_embedding_cache_capacity = 50;
14301430
}
14311431

1432+
// supertonic
1433+
tts_config.model.supertonic.duration_predictor =
1434+
SHERPA_ONNX_OR(config->model.supertonic.duration_predictor, "");
1435+
tts_config.model.supertonic.text_encoder =
1436+
SHERPA_ONNX_OR(config->model.supertonic.text_encoder, "");
1437+
tts_config.model.supertonic.vector_estimator =
1438+
SHERPA_ONNX_OR(config->model.supertonic.vector_estimator, "");
1439+
tts_config.model.supertonic.vocoder =
1440+
SHERPA_ONNX_OR(config->model.supertonic.vocoder, "");
1441+
tts_config.model.supertonic.tts_json =
1442+
SHERPA_ONNX_OR(config->model.supertonic.tts_json, "");
1443+
tts_config.model.supertonic.unicode_indexer =
1444+
SHERPA_ONNX_OR(config->model.supertonic.unicode_indexer, "");
1445+
tts_config.model.supertonic.voice_style =
1446+
SHERPA_ONNX_OR(config->model.supertonic.voice_style, "");
1447+
14321448
tts_config.model.num_threads = SHERPA_ONNX_OR(config->model.num_threads, 1);
14331449
tts_config.model.debug = config->model.debug;
14341450
tts_config.model.provider = SHERPA_ONNX_OR(config->model.provider, "cpu");
@@ -1532,7 +1548,8 @@ static const SherpaOnnxGeneratedAudio *SherpaOnnxOfflineTtsGenerateInternal(
15321548
try {
15331549
auto json = nlohmann::json::parse(config->extra);
15341550
for (auto &[k, v] : json.items()) {
1535-
cfg.extra.insert_or_assign(std::string(k), v.dump());
1551+
std::string val = v.is_string() ? v.get<std::string>() : v.dump();
1552+
cfg.extra.insert_or_assign(std::string(k), std::move(val));
15361553
}
15371554
} catch (const nlohmann::json::parse_error &e) {
15381555
SHERPA_ONNX_LOGE("Failed to parse extra JSON: '%s'", e.what());

sherpa-onnx/c-api/c-api.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,16 @@ SHERPA_ONNX_API typedef struct SherpaOnnxOfflineTtsPocketModelConfig {
11481148
int32_t voice_embedding_cache_capacity;
11491149
} SherpaOnnxOfflineTtsPocketModelConfig;
11501150

1151+
SHERPA_ONNX_API typedef struct SherpaOnnxOfflineTtsSupertonicModelConfig {
1152+
const char *duration_predictor;
1153+
const char *text_encoder;
1154+
const char *vector_estimator;
1155+
const char *vocoder;
1156+
const char *tts_json;
1157+
const char *unicode_indexer;
1158+
const char *voice_style;
1159+
} SherpaOnnxOfflineTtsSupertonicModelConfig;
1160+
11511161
SHERPA_ONNX_API typedef struct SherpaOnnxOfflineTtsModelConfig {
11521162
SherpaOnnxOfflineTtsVitsModelConfig vits;
11531163
int32_t num_threads;
@@ -1158,6 +1168,7 @@ SHERPA_ONNX_API typedef struct SherpaOnnxOfflineTtsModelConfig {
11581168
SherpaOnnxOfflineTtsKittenModelConfig kitten;
11591169
SherpaOnnxOfflineTtsZipvoiceModelConfig zipvoice;
11601170
SherpaOnnxOfflineTtsPocketModelConfig pocket;
1171+
SherpaOnnxOfflineTtsSupertonicModelConfig supertonic;
11611172
} SherpaOnnxOfflineTtsModelConfig;
11621173

11631174
SHERPA_ONNX_API typedef struct SherpaOnnxOfflineTtsConfig {

sherpa-onnx/c-api/cxx-api.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,18 @@ OfflineTts OfflineTts::Create(const OfflineTtsConfig &config) {
472472
c.model.pocket.voice_embedding_cache_capacity =
473473
config.model.pocket.voice_embedding_cache_capacity;
474474

475+
c.model.supertonic.duration_predictor =
476+
config.model.supertonic.duration_predictor.c_str();
477+
c.model.supertonic.text_encoder =
478+
config.model.supertonic.text_encoder.c_str();
479+
c.model.supertonic.vector_estimator =
480+
config.model.supertonic.vector_estimator.c_str();
481+
c.model.supertonic.vocoder = config.model.supertonic.vocoder.c_str();
482+
c.model.supertonic.tts_json = config.model.supertonic.tts_json.c_str();
483+
c.model.supertonic.unicode_indexer =
484+
config.model.supertonic.unicode_indexer.c_str();
485+
c.model.supertonic.voice_style = config.model.supertonic.voice_style.c_str();
486+
475487
c.model.num_threads = config.model.num_threads;
476488
c.model.debug = config.model.debug;
477489
c.model.provider = config.model.provider.c_str();

sherpa-onnx/c-api/cxx-api.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,13 +481,24 @@ struct OfflineTtsPocketModelConfig {
481481
int32_t voice_embedding_cache_capacity = 50;
482482
};
483483

484+
struct OfflineTtsSupertonicModelConfig {
485+
std::string duration_predictor;
486+
std::string text_encoder;
487+
std::string vector_estimator;
488+
std::string vocoder;
489+
std::string tts_json;
490+
std::string unicode_indexer;
491+
std::string voice_style;
492+
};
493+
484494
struct OfflineTtsModelConfig {
485495
OfflineTtsVitsModelConfig vits;
486496
OfflineTtsMatchaModelConfig matcha;
487497
OfflineTtsKokoroModelConfig kokoro;
488498
OfflineTtsKittenModelConfig kitten;
489499
OfflineTtsZipvoiceModelConfig zipvoice;
490500
OfflineTtsPocketModelConfig pocket;
501+
OfflineTtsSupertonicModelConfig supertonic;
491502

492503
int32_t num_threads = 1;
493504
bool debug = false;

0 commit comments

Comments
 (0)