Skip to content

Commit 6acf367

Browse files
authored
Add async TTS examples, LinearResampler API, and use node-cpal for microphone (#3623)
1 parent 58bb4b8 commit 6acf367

57 files changed

Lines changed: 1997 additions & 826 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/scripts/test-nodejs-addon-npm.sh

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ tar xf kitten-nano-en-v0_1-fp16.tar.bz2
130130
rm kitten-nano-en-v0_1-fp16.tar.bz2
131131

132132
node ./test_tts_non_streaming_kitten_en.js
133+
node ./test_tts_non_streaming_kitten_en_sync.js
133134

134135
rm -rf kitten-nano-en-v0_1-fp16
135136

@@ -253,9 +254,9 @@ if [[ $arch != "ia32" && $platform != "win32" ]]; then
253254
node ./test_asr_non_streaming_nemo_ctc.js
254255
rm -rf sherpa-onnx-nemo-fast-conformer-ctc-be-de-en-es-fr-hr-it-pl-ru-uk-20k
255256

256-
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
257-
tar xvf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
258-
rm sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
257+
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
258+
tar xvf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
259+
rm sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
259260

260261
node ./test_asr_non_streaming_sense_voice.js
261262

@@ -268,8 +269,12 @@ if [[ $arch != "ia32" && $platform != "win32" ]]; then
268269

269270
node ./test_asr_non_streaming_sense_voice_with_hr.js
270271

271-
rm -rf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17
272-
rm -rf dict replace.fst test-hr.wav lexicon.txt
272+
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx
273+
274+
node ./test_vad_asr_non_streaming_sense_voice_microphone.js
275+
276+
rm -rf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17
277+
rm -rf dict replace.fst test-hr.wav lexicon.txt silero_vad.onnx
273278

274279
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-paraformer-zh-2023-09-14.tar.bz2
275280
tar xvf sherpa-onnx-paraformer-zh-2023-09-14.tar.bz2
@@ -294,6 +299,7 @@ tar xf kokoro-multi-lang-v1_0.tar.bz2
294299
rm kokoro-multi-lang-v1_0.tar.bz2
295300

296301
node ./test_tts_non_streaming_kokoro_zh_en.js
302+
node ./test_tts_non_streaming_kokoro_zh_en_async.js
297303
ls -lh *.wav
298304
rm -rf kokoro-multi-lang-v1_0
299305

@@ -302,6 +308,7 @@ tar xf kokoro-en-v0_19.tar.bz2
302308
rm kokoro-en-v0_19.tar.bz2
303309

304310
node ./test_tts_non_streaming_kokoro_en.js
311+
node ./test_tts_non_streaming_kokoro_en_async.js
305312
ls -lh *.wav
306313
rm -rf kokoro-en-v0_19
307314

@@ -311,6 +318,7 @@ rm matcha-icefall-en_US-ljspeech.tar.bz2
311318
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/vocoder-models/vocos-22khz-univ.onnx
312319

313320
node ./test_tts_non_streaming_matcha_icefall_en.js
321+
node ./test_tts_non_streaming_matcha_icefall_en_async.js
314322
rm vocos-22khz-univ.onnx
315323
rm -rf matcha-icefall-en_US-ljspeech
316324

@@ -320,6 +328,7 @@ rm matcha-icefall-zh-baker.tar.bz2
320328
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/vocoder-models/vocos-22khz-univ.onnx
321329

322330
node ./test_tts_non_streaming_matcha_icefall_zh.js
331+
node ./test_tts_non_streaming_matcha_icefall_zh_async.js
323332
rm vocos-22khz-univ.onnx
324333
rm -rf matcha-icefall-zh-baker
325334
ls -lh *.wav
@@ -329,27 +338,31 @@ tar xf vits-piper-en_GB-cori-medium.tar.bz2
329338
rm vits-piper-en_GB-cori-medium.tar.bz2
330339

331340
node ./test_tts_non_streaming_vits_piper_en.js
341+
node ./test_tts_non_streaming_vits_piper_en_async.js
332342
rm -rf vits-piper-en_GB-cori-medium
333343

334344
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-coqui-de-css10.tar.bz2
335345
tar xvf vits-coqui-de-css10.tar.bz2
336346
rm vits-coqui-de-css10.tar.bz2
337347

338348
node ./test_tts_non_streaming_vits_coqui_de.js
349+
node ./test_tts_non_streaming_vits_coqui_de_async.js
339350
rm -rf vits-coqui-de-css10
340351

341352
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/sherpa-onnx-vits-zh-ll.tar.bz2
342353
tar xvf sherpa-onnx-vits-zh-ll.tar.bz2
343354
rm sherpa-onnx-vits-zh-ll.tar.bz2
344355

345356
node ./test_tts_non_streaming_vits_zh_ll.js
357+
node ./test_tts_non_streaming_vits_zh_ll_async.js
346358
rm -rf sherpa-onnx-vits-zh-ll
347359

348360
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-icefall-zh-aishell3.tar.bz2
349361
tar xvf vits-icefall-zh-aishell3.tar.bz2
350362
rm vits-icefall-zh-aishell3.tar.bz2
351363

352364
node ./test_tts_non_streaming_vits_zh_aishell3.js
365+
node ./test_tts_non_streaming_vits_zh_aishell3_async.js
353366
rm -rf vits-icefall-zh-aishell3
354367

355368
echo "----------keyword spotting----------"

harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/cpp/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ add_library(sherpa_onnx SHARED
5858
version.cc
5959
wave-reader.cc
6060
wave-writer.cc
61+
resampler.cc
6162
)
6263

6364
add_library(sherpa_onnx_c_api SHARED IMPORTED)
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
// scripts/node-addon-api/src/resampler.cc
2+
//
3+
// Copyright (c) 2026 Xiaomi Corporation
4+
5+
#include <sstream>
6+
7+
#include "macros.h" // NOLINT
8+
#include "napi.h" // NOLINT
9+
#include "sherpa-onnx/c-api/c-api.h"
10+
11+
// createLinearResampler(inputSampleRate, outputSampleRate)
12+
// Returns an External handle to a SherpaOnnxLinearResampler.
13+
static Napi::External<SherpaOnnxLinearResampler> CreateLinearResamplerWrapper(
14+
const Napi::CallbackInfo &info) {
15+
Napi::Env env = info.Env();
16+
17+
if (info.Length() != 2) {
18+
std::ostringstream os;
19+
os << "Expect 2 arguments (inputSampleRate, outputSampleRate). Given: "
20+
<< info.Length();
21+
22+
Napi::TypeError::New(env, os.str()).ThrowAsJavaScriptException();
23+
24+
return {};
25+
}
26+
27+
if (!info[0].IsNumber()) {
28+
Napi::TypeError::New(env, "Argument 0 should be a number (inputSampleRate)")
29+
.ThrowAsJavaScriptException();
30+
31+
return {};
32+
}
33+
34+
if (!info[1].IsNumber()) {
35+
Napi::TypeError::New(env,
36+
"Argument 1 should be a number (outputSampleRate)")
37+
.ThrowAsJavaScriptException();
38+
39+
return {};
40+
}
41+
42+
int32_t input_sample_rate = info[0].As<Napi::Number>().Int32Value();
43+
int32_t output_sample_rate = info[1].As<Napi::Number>().Int32Value();
44+
45+
if (input_sample_rate <= 0 || output_sample_rate <= 0) {
46+
Napi::TypeError::New(env,
47+
"inputSampleRate and outputSampleRate must be > 0")
48+
.ThrowAsJavaScriptException();
49+
50+
return {};
51+
}
52+
53+
const SherpaOnnxLinearResampler *p = SherpaOnnxCreateLinearResampler(
54+
input_sample_rate, output_sample_rate, 0, 0);
55+
56+
return Napi::External<SherpaOnnxLinearResampler>::New(
57+
env, const_cast<SherpaOnnxLinearResampler *>(p),
58+
[](Napi::Env env, SherpaOnnxLinearResampler *ptr) {
59+
SherpaOnnxDestroyLinearResampler(ptr);
60+
});
61+
}
62+
63+
// resampleLinear(resamplerHandle, samples, flush)
64+
// Returns a Float32Array of resampled samples.
65+
// flush should be 1 for the final chunk; 0 otherwise.
66+
static Napi::Float32Array ResampleLinearWrapper(
67+
const Napi::CallbackInfo &info) {
68+
Napi::Env env = info.Env();
69+
70+
if (info.Length() != 3) {
71+
std::ostringstream os;
72+
os << "Expect 3 arguments (resampler, samples, flush). Given: "
73+
<< info.Length();
74+
75+
Napi::TypeError::New(env, os.str()).ThrowAsJavaScriptException();
76+
77+
return {};
78+
}
79+
80+
if (!info[0].IsExternal()) {
81+
Napi::TypeError::New(env, "Argument 0 should be a resampler handle.")
82+
.ThrowAsJavaScriptException();
83+
84+
return {};
85+
}
86+
87+
if (!info[1].IsTypedArray() ||
88+
info[1].As<Napi::TypedArray>().TypedArrayType() != napi_float32_array) {
89+
Napi::TypeError::New(env, "Argument 1 should be a Float32Array.")
90+
.ThrowAsJavaScriptException();
91+
92+
return {};
93+
}
94+
95+
if (!info[2].IsNumber()) {
96+
Napi::TypeError::New(env, "Argument 2 should be a number (flush: 0 or 1).")
97+
.ThrowAsJavaScriptException();
98+
99+
return {};
100+
}
101+
102+
SherpaOnnxLinearResampler *p =
103+
info[0].As<Napi::External<SherpaOnnxLinearResampler>>().Data();
104+
105+
Napi::Float32Array samples = info[1].As<Napi::Float32Array>();
106+
int32_t input_dim = samples.ElementLength();
107+
int32_t flush = info[2].As<Napi::Number>().Int32Value();
108+
109+
const SherpaOnnxResampleOut *out =
110+
SherpaOnnxLinearResamplerResample(p, samples.Data(), input_dim, flush);
111+
112+
Napi::Float32Array result = Napi::Float32Array::New(env, out->n);
113+
std::copy(out->samples, out->samples + out->n, result.Data());
114+
115+
SherpaOnnxLinearResamplerResampleFree(out);
116+
117+
return result;
118+
}
119+
120+
// linearResamplerReset(resamplerHandle)
121+
static void LinearResamplerResetWrapper(const Napi::CallbackInfo &info) {
122+
Napi::Env env = info.Env();
123+
124+
if (info.Length() != 1) {
125+
std::ostringstream os;
126+
os << "Expect 1 argument (resampler). Given: " << info.Length();
127+
128+
Napi::TypeError::New(env, os.str()).ThrowAsJavaScriptException();
129+
130+
return;
131+
}
132+
133+
if (!info[0].IsExternal()) {
134+
Napi::TypeError::New(env, "Argument 0 should be a resampler handle.")
135+
.ThrowAsJavaScriptException();
136+
137+
return;
138+
}
139+
140+
SherpaOnnxLinearResampler *p =
141+
info[0].As<Napi::External<SherpaOnnxLinearResampler>>().Data();
142+
143+
SherpaOnnxLinearResamplerReset(p);
144+
}
145+
146+
// linearResamplerGetInputSampleRate(resamplerHandle)
147+
static Napi::Number LinearResamplerGetInputSampleRateWrapper(
148+
const Napi::CallbackInfo &info) {
149+
Napi::Env env = info.Env();
150+
151+
if (info.Length() != 1) {
152+
std::ostringstream os;
153+
os << "Expect 1 argument (resampler). Given: " << info.Length();
154+
155+
Napi::TypeError::New(env, os.str()).ThrowAsJavaScriptException();
156+
157+
return {};
158+
}
159+
160+
if (!info[0].IsExternal()) {
161+
Napi::TypeError::New(env, "Argument 0 should be a resampler handle.")
162+
.ThrowAsJavaScriptException();
163+
164+
return {};
165+
}
166+
167+
SherpaOnnxLinearResampler *p =
168+
info[0].As<Napi::External<SherpaOnnxLinearResampler>>().Data();
169+
170+
return Napi::Number::New(
171+
env, SherpaOnnxLinearResamplerResampleGetInputSampleRate(p));
172+
}
173+
174+
// linearResamplerGetOutputSampleRate(resamplerHandle)
175+
static Napi::Number LinearResamplerGetOutputSampleRateWrapper(
176+
const Napi::CallbackInfo &info) {
177+
Napi::Env env = info.Env();
178+
179+
if (info.Length() != 1) {
180+
std::ostringstream os;
181+
os << "Expect 1 argument (resampler). Given: " << info.Length();
182+
183+
Napi::TypeError::New(env, os.str()).ThrowAsJavaScriptException();
184+
185+
return {};
186+
}
187+
188+
if (!info[0].IsExternal()) {
189+
Napi::TypeError::New(env, "Argument 0 should be a resampler handle.")
190+
.ThrowAsJavaScriptException();
191+
192+
return {};
193+
}
194+
195+
SherpaOnnxLinearResampler *p =
196+
info[0].As<Napi::External<SherpaOnnxLinearResampler>>().Data();
197+
198+
return Napi::Number::New(
199+
env, SherpaOnnxLinearResamplerResampleGetOutputSampleRate(p));
200+
}
201+
202+
void InitResampler(Napi::Env env, Napi::Object exports) {
203+
exports.Set(Napi::String::New(env, "createLinearResampler"),
204+
Napi::Function::New(env, CreateLinearResamplerWrapper));
205+
206+
exports.Set(Napi::String::New(env, "resampleLinear"),
207+
Napi::Function::New(env, ResampleLinearWrapper));
208+
209+
exports.Set(Napi::String::New(env, "linearResamplerReset"),
210+
Napi::Function::New(env, LinearResamplerResetWrapper));
211+
212+
exports.Set(Napi::String::New(env, "linearResamplerGetInputSampleRate"),
213+
Napi::Function::New(env, LinearResamplerGetInputSampleRateWrapper));
214+
215+
exports.Set(
216+
Napi::String::New(env, "linearResamplerGetOutputSampleRate"),
217+
Napi::Function::New(env, LinearResamplerGetOutputSampleRateWrapper));
218+
}

harmony-os/SherpaOnnxHar/sherpa_onnx/src/main/cpp/sherpa-onnx-node-addon-api.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ void InitOnlineSpeechDenoiser(Napi::Env env, Napi::Object exports);
3535

3636
void InitVersion(Napi::Env env, Napi::Object exports);
3737

38+
void InitResampler(Napi::Env env, Napi::Object exports);
39+
3840
#if __OHOS__
3941
void InitUtils(Napi::Env env, Napi::Object exports);
4042
#endif
@@ -56,6 +58,7 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
5658
InitNonStreamingSpeechDenoiser(env, exports);
5759
InitOnlineSpeechDenoiser(env, exports);
5860
InitVersion(env, exports);
61+
InitResampler(env, exports);
5962

6063
#if __OHOS__
6164
InitUtils(env, exports);

0 commit comments

Comments
 (0)