Skip to content

Commit 2034108

Browse files
localai-botmudler
andauthored
feat(sherpa-onnx): add Kokoro TTS + multilingual Piper voices (#10309)
Wire the Kokoro model family into the sherpa-onnx backend (which only supported VITS/Piper before) and add gallery voices for Italian, English, Spanish, French and German plus a multilingual Kokoro model. - csrc/shim.{c,h}: kokoro_* config setters (model/voices/tokens/data_dir/ dict_dir/lexicon/lang/length_scale) mirroring the VITS path, with the matching frees in tts_config_free. - backend.go: loadTTS now detects a Kokoro model (a voices.bin beside the ONNX) and routes to configureKokoroTTS, otherwise configureVitsTTS. Kokoro picks up espeak-ng-data, the jieba dict and the per-language lexicons (only one English variant, to avoid tens of thousands of duplicate-word warnings at load); the language= option hints the lang. - backend_test.go: functional test for isKokoroModel detection. - gallery: 5 Piper VITS voices (it_IT-paola, en_US-amy, es_ES-davefx, fr_FR-siwis, de_DE-thorsten) + kokoro-multi-lang-v1.0, served through sherpa-onnx-tts.yaml with native streaming TTS. Verified by building the backend and synthesizing with a real Piper and Kokoro model (31/31 specs pass, including real-model synth smokes). Assisted-by: Claude:claude-opus-4-8 gofmt golangci-lint go-test Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
1 parent 7637f8c commit 2034108

5 files changed

Lines changed: 367 additions & 62 deletions

File tree

backend/go/sherpa-onnx/backend.go

Lines changed: 144 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ var (
6262
shimVadConfigSetDebug func(uintptr, int32)
6363
shimCreateVad func(uintptr, float32) uintptr
6464

65-
// TTS (offline, VITS) config
65+
// TTS (offline, VITS/Piper and Kokoro) config
6666
shimTtsConfigNew func() uintptr
6767
shimTtsConfigFree func(uintptr)
6868
shimTtsConfigSetVitsModel func(uintptr, string)
@@ -76,6 +76,14 @@ var (
7676
shimTtsConfigSetDebug func(uintptr, int32)
7777
shimTtsConfigSetProvider func(uintptr, string)
7878
shimTtsConfigSetMaxNumSentences func(uintptr, int32)
79+
shimTtsConfigSetKokoroModel func(uintptr, string)
80+
shimTtsConfigSetKokoroVoices func(uintptr, string)
81+
shimTtsConfigSetKokoroTokens func(uintptr, string)
82+
shimTtsConfigSetKokoroDataDir func(uintptr, string)
83+
shimTtsConfigSetKokoroDictDir func(uintptr, string)
84+
shimTtsConfigSetKokoroLexicon func(uintptr, string)
85+
shimTtsConfigSetKokoroLang func(uintptr, string)
86+
shimTtsConfigSetKokoroLengthScale func(uintptr, float32)
7987
shimCreateOfflineTts func(uintptr) uintptr
8088

8189
// Offline recognizer config
@@ -101,37 +109,37 @@ var (
101109
shimCreateOfflineRecognizer func(uintptr) uintptr
102110

103111
// Online recognizer config
104-
shimOnlineRecogConfigNew func() uintptr
105-
shimOnlineRecogConfigFree func(uintptr)
106-
shimOnlineRecogConfigSetTransducerEncoder func(uintptr, string)
107-
shimOnlineRecogConfigSetTransducerDecoder func(uintptr, string)
108-
shimOnlineRecogConfigSetTransducerJoiner func(uintptr, string)
109-
shimOnlineRecogConfigSetTokens func(uintptr, string)
110-
shimOnlineRecogConfigSetNumThreads func(uintptr, int32)
111-
shimOnlineRecogConfigSetDebug func(uintptr, int32)
112-
shimOnlineRecogConfigSetProvider func(uintptr, string)
113-
shimOnlineRecogConfigSetFeatSampleRate func(uintptr, int32)
114-
shimOnlineRecogConfigSetFeatFeatureDim func(uintptr, int32)
115-
shimOnlineRecogConfigSetDecodingMethod func(uintptr, string)
116-
shimOnlineRecogConfigSetEnableEndpoint func(uintptr, int32)
112+
shimOnlineRecogConfigNew func() uintptr
113+
shimOnlineRecogConfigFree func(uintptr)
114+
shimOnlineRecogConfigSetTransducerEncoder func(uintptr, string)
115+
shimOnlineRecogConfigSetTransducerDecoder func(uintptr, string)
116+
shimOnlineRecogConfigSetTransducerJoiner func(uintptr, string)
117+
shimOnlineRecogConfigSetTokens func(uintptr, string)
118+
shimOnlineRecogConfigSetNumThreads func(uintptr, int32)
119+
shimOnlineRecogConfigSetDebug func(uintptr, int32)
120+
shimOnlineRecogConfigSetProvider func(uintptr, string)
121+
shimOnlineRecogConfigSetFeatSampleRate func(uintptr, int32)
122+
shimOnlineRecogConfigSetFeatFeatureDim func(uintptr, int32)
123+
shimOnlineRecogConfigSetDecodingMethod func(uintptr, string)
124+
shimOnlineRecogConfigSetEnableEndpoint func(uintptr, int32)
117125
shimOnlineRecogConfigSetRule1MinTrailingSilence func(uintptr, float32)
118126
shimOnlineRecogConfigSetRule2MinTrailingSilence func(uintptr, float32)
119127
shimOnlineRecogConfigSetRule3MinUtteranceLength func(uintptr, float32)
120-
shimCreateOnlineRecognizer func(uintptr) uintptr
128+
shimCreateOnlineRecognizer func(uintptr) uintptr
121129

122130
// Result accessors. Pointer returns use unsafe.Pointer so Go's
123131
// vet checker doesn't flag them — the returned memory is C-owned,
124132
// not subject to Go GC motion.
125-
shimWaveSampleRate func(uintptr) int32
126-
shimWaveNumSamples func(uintptr) int32
127-
shimWaveSamples func(uintptr) unsafe.Pointer
128-
shimOfflineResultText func(uintptr) unsafe.Pointer
129-
shimOnlineResultText func(uintptr) unsafe.Pointer
130-
shimGeneratedAudioSampleRate func(uintptr) int32
131-
shimGeneratedAudioN func(uintptr) int32
132-
shimGeneratedAudioSamples func(uintptr) unsafe.Pointer
133-
shimSpeechSegmentStart func(uintptr) int32
134-
shimSpeechSegmentN func(uintptr) int32
133+
shimWaveSampleRate func(uintptr) int32
134+
shimWaveNumSamples func(uintptr) int32
135+
shimWaveSamples func(uintptr) unsafe.Pointer
136+
shimOfflineResultText func(uintptr) unsafe.Pointer
137+
shimOnlineResultText func(uintptr) unsafe.Pointer
138+
shimGeneratedAudioSampleRate func(uintptr) int32
139+
shimGeneratedAudioN func(uintptr) int32
140+
shimGeneratedAudioSamples func(uintptr) unsafe.Pointer
141+
shimSpeechSegmentStart func(uintptr) int32
142+
shimSpeechSegmentN func(uintptr) int32
135143

136144
// TTS streaming callback trampoline
137145
shimTtsGenerateWithCallback func(tts uintptr, text string, sid int32, speed float32, cb uintptr, ud uintptr) uintptr
@@ -161,25 +169,25 @@ var (
161169
// pointer returned by the shim or `unsafe.Pointer(&slice[0])` from Go.
162170
var (
163171
// VAD
164-
sherpaVadAcceptWaveform func(vad uintptr, samples unsafe.Pointer, n int32)
165-
sherpaVadReset func(vad uintptr)
166-
sherpaVadFlush func(vad uintptr)
167-
sherpaVadEmpty func(vad uintptr) int32
168-
sherpaVadFront func(vad uintptr) uintptr
169-
sherpaVadPop func(vad uintptr)
170-
sherpaDestroySpeechSegment func(seg uintptr)
172+
sherpaVadAcceptWaveform func(vad uintptr, samples unsafe.Pointer, n int32)
173+
sherpaVadReset func(vad uintptr)
174+
sherpaVadFlush func(vad uintptr)
175+
sherpaVadEmpty func(vad uintptr) int32
176+
sherpaVadFront func(vad uintptr) uintptr
177+
sherpaVadPop func(vad uintptr)
178+
sherpaDestroySpeechSegment func(seg uintptr)
171179

172180
// Wave IO
173181
sherpaReadWave func(filename string) uintptr
174182
sherpaFreeWave func(wave uintptr)
175183
sherpaWriteWave func(samples unsafe.Pointer, n int32, sampleRate int32, filename string) int32
176184

177185
// Offline ASR
178-
sherpaCreateOfflineStream func(rec uintptr) uintptr
179-
sherpaDestroyOfflineStream func(stream uintptr)
180-
sherpaAcceptWaveformOffline func(stream uintptr, sr int32, samples unsafe.Pointer, n int32)
181-
sherpaDecodeOfflineStream func(rec uintptr, stream uintptr)
182-
sherpaGetOfflineStreamResult func(stream uintptr) uintptr
186+
sherpaCreateOfflineStream func(rec uintptr) uintptr
187+
sherpaDestroyOfflineStream func(stream uintptr)
188+
sherpaAcceptWaveformOffline func(stream uintptr, sr int32, samples unsafe.Pointer, n int32)
189+
sherpaDecodeOfflineStream func(rec uintptr, stream uintptr)
190+
sherpaGetOfflineStreamResult func(stream uintptr) uintptr
183191
sherpaDestroyOfflineRecognizerResult func(result uintptr)
184192

185193
// Online ASR
@@ -195,21 +203,21 @@ var (
195203
sherpaOnlineStreamInputFinished func(stream uintptr)
196204

197205
// TTS
198-
sherpaOfflineTtsGenerate func(tts uintptr, text string, sid int32, speed float32) uintptr
206+
sherpaOfflineTtsGenerate func(tts uintptr, text string, sid int32, speed float32) uintptr
199207
sherpaDestroyOfflineTtsGeneratedAudio func(audio uintptr)
200-
sherpaOfflineTtsSampleRate func(tts uintptr) int32
208+
sherpaOfflineTtsSampleRate func(tts uintptr) int32
201209

202210
// Offline speaker diarization. Result handle owns the segment-array
203211
// pointer returned by ResultSortByStartTime; destroy the segment
204212
// array first, then the result, then (at backend Free()) the diarizer.
205-
sherpaDestroyOfflineSpeakerDiarization func(sd uintptr)
206-
sherpaOfflineSpeakerDiarizationGetSampleRate func(sd uintptr) int32
207-
sherpaOfflineSpeakerDiarizationProcess func(sd uintptr, samples unsafe.Pointer, n int32) uintptr
208-
sherpaOfflineSpeakerDiarizationResultGetNumSegments func(result uintptr) int32
209-
sherpaOfflineSpeakerDiarizationResultGetNumSpeakers func(result uintptr) int32
210-
sherpaOfflineSpeakerDiarizationResultSortByStartTime func(result uintptr) uintptr
211-
sherpaOfflineSpeakerDiarizationDestroySegment func(segs uintptr)
212-
sherpaDestroyOfflineSpeakerDiarizationResult func(result uintptr)
213+
sherpaDestroyOfflineSpeakerDiarization func(sd uintptr)
214+
sherpaOfflineSpeakerDiarizationGetSampleRate func(sd uintptr) int32
215+
sherpaOfflineSpeakerDiarizationProcess func(sd uintptr, samples unsafe.Pointer, n int32) uintptr
216+
sherpaOfflineSpeakerDiarizationResultGetNumSegments func(result uintptr) int32
217+
sherpaOfflineSpeakerDiarizationResultGetNumSpeakers func(result uintptr) int32
218+
sherpaOfflineSpeakerDiarizationResultSortByStartTime func(result uintptr) uintptr
219+
sherpaOfflineSpeakerDiarizationDestroySegment func(segs uintptr)
220+
sherpaDestroyOfflineSpeakerDiarizationResult func(result uintptr)
213221
)
214222

215223
var (
@@ -278,6 +286,14 @@ func loadSherpaLibsOnce() error {
278286
{&shimTtsConfigSetDebug, "sherpa_shim_tts_config_set_debug"},
279287
{&shimTtsConfigSetProvider, "sherpa_shim_tts_config_set_provider"},
280288
{&shimTtsConfigSetMaxNumSentences, "sherpa_shim_tts_config_set_max_num_sentences"},
289+
{&shimTtsConfigSetKokoroModel, "sherpa_shim_tts_config_set_kokoro_model"},
290+
{&shimTtsConfigSetKokoroVoices, "sherpa_shim_tts_config_set_kokoro_voices"},
291+
{&shimTtsConfigSetKokoroTokens, "sherpa_shim_tts_config_set_kokoro_tokens"},
292+
{&shimTtsConfigSetKokoroDataDir, "sherpa_shim_tts_config_set_kokoro_data_dir"},
293+
{&shimTtsConfigSetKokoroDictDir, "sherpa_shim_tts_config_set_kokoro_dict_dir"},
294+
{&shimTtsConfigSetKokoroLexicon, "sherpa_shim_tts_config_set_kokoro_lexicon"},
295+
{&shimTtsConfigSetKokoroLang, "sherpa_shim_tts_config_set_kokoro_lang"},
296+
{&shimTtsConfigSetKokoroLengthScale, "sherpa_shim_tts_config_set_kokoro_length_scale"},
281297
{&shimCreateOfflineTts, "sherpa_shim_create_offline_tts"},
282298

283299
{&shimOfflineRecogConfigNew, "sherpa_shim_offline_recog_config_new"},
@@ -688,6 +704,47 @@ func (s *SherpaBackend) loadTTS(opts *pb.ModelOptions) error {
688704
cfg := shimTtsConfigNew()
689705
defer shimTtsConfigFree(cfg)
690706

707+
// Kokoro models ship a voices style file alongside the ONNX, whereas
708+
// VITS/Piper voices do not. That presence is what tells the two model
709+
// families apart, since both arrive as a plain *.onnx in modelDir.
710+
if isKokoroModel(modelDir) {
711+
s.configureKokoroTTS(cfg, opts, modelFile, modelDir)
712+
} else {
713+
s.configureVitsTTS(cfg, opts, modelFile, modelDir)
714+
}
715+
716+
threads := int32(1)
717+
if opts.Threads != 0 {
718+
threads = opts.Threads
719+
}
720+
shimTtsConfigSetNumThreads(cfg, threads)
721+
shimTtsConfigSetDebug(cfg, 0)
722+
shimTtsConfigSetProvider(cfg, onnxProvider)
723+
shimTtsConfigSetMaxNumSentences(cfg, findOptionInt(opts, optionTtsMaxNumSentences, 1))
724+
725+
s.ttsSpeed = findOptionFloat(opts, optionTtsSpeed, 1.0)
726+
727+
tts := shimCreateOfflineTts(cfg)
728+
if tts == 0 {
729+
return fmt.Errorf("failed to create sherpa-onnx TTS engine from %s", modelFile)
730+
}
731+
s.tts = tts
732+
return nil
733+
}
734+
735+
// kokoroVoicesFile is the speaker-style bank that ships with Kokoro models and
736+
// is absent from VITS/Piper voices; its presence is how loadTTS tells them apart.
737+
const kokoroVoicesFile = "voices.bin"
738+
739+
// isKokoroModel reports whether modelDir holds a Kokoro model (a voices file
740+
// next to the ONNX) rather than a VITS/Piper single-speaker model.
741+
func isKokoroModel(modelDir string) bool {
742+
return fileExists(filepath.Join(modelDir, kokoroVoicesFile))
743+
}
744+
745+
// configureVitsTTS wires a VITS/Piper single-speaker model into cfg: the ONNX
746+
// plus the optional tokens, lexicon and espeak-ng-data found beside it.
747+
func (s *SherpaBackend) configureVitsTTS(cfg uintptr, opts *pb.ModelOptions, modelFile, modelDir string) {
691748
shimTtsConfigSetVitsModel(cfg, modelFile)
692749

693750
if tokensPath := filepath.Join(modelDir, "tokens.txt"); fileExists(tokensPath) {
@@ -703,24 +760,50 @@ func (s *SherpaBackend) loadTTS(opts *pb.ModelOptions) error {
703760
shimTtsConfigSetVitsNoiseScale(cfg, findOptionFloat(opts, optionTtsNoiseScale, 0.667))
704761
shimTtsConfigSetVitsNoiseScaleW(cfg, findOptionFloat(opts, optionTtsNoiseScaleW, 0.8))
705762
shimTtsConfigSetVitsLengthScale(cfg, findOptionFloat(opts, optionTtsLengthScale, 1.0))
763+
}
706764

707-
threads := int32(1)
708-
if opts.Threads != 0 {
709-
threads = opts.Threads
765+
// configureKokoroTTS wires a Kokoro model into cfg: the ONNX, its voices bank,
766+
// tokens, and the optional espeak-ng-data / jieba dict / lexicon assets the
767+
// multi-lingual packs ship. A language hint comes from the `language=` option.
768+
func (s *SherpaBackend) configureKokoroTTS(cfg uintptr, opts *pb.ModelOptions, modelFile, modelDir string) {
769+
shimTtsConfigSetKokoroModel(cfg, modelFile)
770+
shimTtsConfigSetKokoroVoices(cfg, filepath.Join(modelDir, kokoroVoicesFile))
771+
772+
if tokensPath := filepath.Join(modelDir, "tokens.txt"); fileExists(tokensPath) {
773+
shimTtsConfigSetKokoroTokens(cfg, tokensPath)
774+
}
775+
if dataDir := filepath.Join(modelDir, "espeak-ng-data"); dirExists(dataDir) {
776+
shimTtsConfigSetKokoroDataDir(cfg, dataDir)
777+
}
778+
if dictDir := filepath.Join(modelDir, "dict"); dirExists(dictDir) {
779+
shimTtsConfigSetKokoroDictDir(cfg, dictDir)
710780
}
711-
shimTtsConfigSetNumThreads(cfg, threads)
712-
shimTtsConfigSetDebug(cfg, 0)
713-
shimTtsConfigSetProvider(cfg, onnxProvider)
714-
shimTtsConfigSetMaxNumSentences(cfg, findOptionInt(opts, optionTtsMaxNumSentences, 1))
715781

716-
s.ttsSpeed = findOptionFloat(opts, optionTtsSpeed, 1.0)
782+
// Multi-lingual Kokoro ships per-language lexicons; the C API takes them as
783+
// a single comma-separated list. US and GB English overlap almost entirely,
784+
// so pass only one (US preferred) to avoid tens of thousands of "duplicated
785+
// word" warnings at load; non-English lexicons (e.g. zh) are additive.
786+
var lexicons []string
787+
addLexicon := func(name string) {
788+
if p := filepath.Join(modelDir, name); fileExists(p) {
789+
lexicons = append(lexicons, p)
790+
}
791+
}
792+
if fileExists(filepath.Join(modelDir, "lexicon-us-en.txt")) {
793+
addLexicon("lexicon-us-en.txt")
794+
} else {
795+
addLexicon("lexicon-gb-en.txt")
796+
}
797+
addLexicon("lexicon-zh.txt")
798+
addLexicon("lexicon.txt")
799+
if len(lexicons) > 0 {
800+
shimTtsConfigSetKokoroLexicon(cfg, strings.Join(lexicons, ","))
801+
}
717802

718-
tts := shimCreateOfflineTts(cfg)
719-
if tts == 0 {
720-
return fmt.Errorf("failed to create sherpa-onnx TTS engine from %s", modelFile)
803+
if lang := findOptionValue(opts, optionLanguage, ""); lang != "" {
804+
shimTtsConfigSetKokoroLang(cfg, lang)
721805
}
722-
s.tts = tts
723-
return nil
806+
shimTtsConfigSetKokoroLengthScale(cfg, findOptionFloat(opts, optionTtsLengthScale, 1.0))
724807
}
725808

726809
func fileExists(p string) bool {
@@ -1252,7 +1335,7 @@ type ttsStreamState struct {
12521335
var (
12531336
ttsStates sync.Map // uint64 → *ttsStreamState
12541337
ttsNextID atomic.Uint64
1255-
ttsCallbackPtr uintptr // purego.NewCallback return; registered in loadSherpaLibs
1338+
ttsCallbackPtr uintptr // purego.NewCallback return; registered in loadSherpaLibs
12561339
)
12571340

12581341
// ttsStreamCallback is invoked by sherpa-onnx for each PCM chunk VITS

backend/go/sherpa-onnx/backend_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,20 @@ var _ = Describe("Sherpa-ONNX", func() {
124124
Entry("empty", "", false),
125125
Entry("other", "other", false),
126126
)
127+
128+
It("isKokoroModel detects a voices file beside the ONNX", func() {
129+
dir, err := os.MkdirTemp("", "sherpa-kokoro-*")
130+
Expect(err).NotTo(HaveOccurred())
131+
defer func() { _ = os.RemoveAll(dir) }()
132+
133+
// A bare VITS/Piper directory (ONNX only) is not Kokoro.
134+
Expect(os.WriteFile(filepath.Join(dir, "model.onnx"), []byte("x"), 0o600)).To(Succeed())
135+
Expect(isKokoroModel(dir)).To(BeFalse())
136+
137+
// Adding the Kokoro voices bank flips detection on.
138+
Expect(os.WriteFile(filepath.Join(dir, kokoroVoicesFile), []byte("x"), 0o600)).To(Succeed())
139+
Expect(isKokoroModel(dir)).To(BeTrue())
140+
})
127141
})
128142

129143
Context("option parsing", func() {

backend/go/sherpa-onnx/csrc/shim.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ void sherpa_shim_tts_config_free(void *h) {
7979
free((char *)c->model.vits.tokens);
8080
free((char *)c->model.vits.lexicon);
8181
free((char *)c->model.vits.data_dir);
82+
free((char *)c->model.kokoro.model);
83+
free((char *)c->model.kokoro.voices);
84+
free((char *)c->model.kokoro.tokens);
85+
free((char *)c->model.kokoro.data_dir);
86+
free((char *)c->model.kokoro.dict_dir);
87+
free((char *)c->model.kokoro.lexicon);
88+
free((char *)c->model.kokoro.lang);
8289
free((char *)c->model.provider);
8390
free(c);
8491
}
@@ -117,6 +124,34 @@ void sherpa_shim_tts_config_set_max_num_sentences(void *h, int32_t v) {
117124
((SherpaOnnxOfflineTtsConfig *)h)->max_num_sentences = v;
118125
}
119126

127+
// Kokoro multi-speaker / multi-lingual TTS. Distinct ONNX + a voices style
128+
// file (voices.bin) instead of VITS' single-speaker graph; espeak-ng-data,
129+
// lexicon and a language hint are optional refinements.
130+
void sherpa_shim_tts_config_set_kokoro_model(void *h, const char *v) {
131+
shim_set_str(&((SherpaOnnxOfflineTtsConfig *)h)->model.kokoro.model, v);
132+
}
133+
void sherpa_shim_tts_config_set_kokoro_voices(void *h, const char *v) {
134+
shim_set_str(&((SherpaOnnxOfflineTtsConfig *)h)->model.kokoro.voices, v);
135+
}
136+
void sherpa_shim_tts_config_set_kokoro_tokens(void *h, const char *v) {
137+
shim_set_str(&((SherpaOnnxOfflineTtsConfig *)h)->model.kokoro.tokens, v);
138+
}
139+
void sherpa_shim_tts_config_set_kokoro_data_dir(void *h, const char *v) {
140+
shim_set_str(&((SherpaOnnxOfflineTtsConfig *)h)->model.kokoro.data_dir, v);
141+
}
142+
void sherpa_shim_tts_config_set_kokoro_dict_dir(void *h, const char *v) {
143+
shim_set_str(&((SherpaOnnxOfflineTtsConfig *)h)->model.kokoro.dict_dir, v);
144+
}
145+
void sherpa_shim_tts_config_set_kokoro_lexicon(void *h, const char *v) {
146+
shim_set_str(&((SherpaOnnxOfflineTtsConfig *)h)->model.kokoro.lexicon, v);
147+
}
148+
void sherpa_shim_tts_config_set_kokoro_lang(void *h, const char *v) {
149+
shim_set_str(&((SherpaOnnxOfflineTtsConfig *)h)->model.kokoro.lang, v);
150+
}
151+
void sherpa_shim_tts_config_set_kokoro_length_scale(void *h, float v) {
152+
((SherpaOnnxOfflineTtsConfig *)h)->model.kokoro.length_scale = v;
153+
}
154+
120155
void *sherpa_shim_create_offline_tts(void *h) {
121156
return (void *)SherpaOnnxCreateOfflineTts(
122157
(const SherpaOnnxOfflineTtsConfig *)h);

backend/go/sherpa-onnx/csrc/shim.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ void sherpa_shim_vad_config_set_provider(void *cfg, const char *v);
3737
void sherpa_shim_vad_config_set_debug(void *cfg, int32_t v);
3838
void *sherpa_shim_create_vad(void *cfg, float buffer_size_seconds);
3939

40-
// --- Offline TTS config (VITS path — the only TTS family the backend uses) ---
40+
// --- Offline TTS config (VITS/Piper and Kokoro model families) ---
4141
void *sherpa_shim_tts_config_new(void);
4242
void sherpa_shim_tts_config_free(void *cfg);
4343
void sherpa_shim_tts_config_set_vits_model(void *cfg, const char *v);
@@ -51,6 +51,14 @@ void sherpa_shim_tts_config_set_num_threads(void *cfg, int32_t v);
5151
void sherpa_shim_tts_config_set_debug(void *cfg, int32_t v);
5252
void sherpa_shim_tts_config_set_provider(void *cfg, const char *v);
5353
void sherpa_shim_tts_config_set_max_num_sentences(void *cfg, int32_t v);
54+
void sherpa_shim_tts_config_set_kokoro_model(void *cfg, const char *v);
55+
void sherpa_shim_tts_config_set_kokoro_voices(void *cfg, const char *v);
56+
void sherpa_shim_tts_config_set_kokoro_tokens(void *cfg, const char *v);
57+
void sherpa_shim_tts_config_set_kokoro_data_dir(void *cfg, const char *v);
58+
void sherpa_shim_tts_config_set_kokoro_dict_dir(void *cfg, const char *v);
59+
void sherpa_shim_tts_config_set_kokoro_lexicon(void *cfg, const char *v);
60+
void sherpa_shim_tts_config_set_kokoro_lang(void *cfg, const char *v);
61+
void sherpa_shim_tts_config_set_kokoro_length_scale(void *cfg, float v);
5462
void *sherpa_shim_create_offline_tts(void *cfg);
5563

5664
// --- Offline recognizer config (Whisper / Paraformer / SenseVoice / Omnilingual) ---

0 commit comments

Comments
 (0)