Committed by
GitHub
Add more streaming ASR methods for node-addon-api (#860)
正在显示
6 个修改的文件
包含
398 行增加
和
13 行删除
| @@ -28,9 +28,13 @@ export LD_LIBRARY_PATH=$PWD/node_modules/sherpa-onnx-linux-arm64:$LD_LIBRARY_PAT | @@ -28,9 +28,13 @@ export LD_LIBRARY_PATH=$PWD/node_modules/sherpa-onnx-linux-arm64:$LD_LIBRARY_PAT | ||
| 28 | ``` | 28 | ``` |
| 29 | 29 | ||
| 30 | ## Streaming speech recognition with zipformer transducer | 30 | ## Streaming speech recognition with zipformer transducer |
| 31 | + | ||
| 31 | ```bash | 32 | ```bash |
| 32 | wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20.tar.bz2 | 33 | wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20.tar.bz2 |
| 33 | tar xvf sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20.tar.bz2 | 34 | tar xvf sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20.tar.bz2 |
| 34 | rm sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20.tar.bz2 | 35 | rm sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20.tar.bz2 |
| 35 | 36 | ||
| 37 | +node ./test_asr_streaming_transducer.js | ||
| 38 | + | ||
| 39 | +node ./test_asr_streaming_transducer_microphone.js | ||
| 36 | ``` | 40 | ``` |
| 1 | +// Copyright (c) 2023-2024 Xiaomi Corporation (authors: Fangjun Kuang) | ||
| 2 | +// | ||
| 3 | +const portAudio = require('naudiodon2'); | ||
| 4 | +// console.log(portAudio.getDevices()); | ||
| 5 | + | ||
| 6 | +const sherpa_onnx = require('sherpa-onnx-node'); | ||
| 7 | + | ||
| 8 | +function createOnlineRecognizer() { | ||
| 9 | + const config = { | ||
| 10 | + 'featConfig': { | ||
| 11 | + 'sampleRate': 16000, | ||
| 12 | + 'featureDim': 80, | ||
| 13 | + }, | ||
| 14 | + 'modelConfig': { | ||
| 15 | + 'transducer': { | ||
| 16 | + 'encoder': | ||
| 17 | + './sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20/encoder-epoch-99-avg-1.onnx', | ||
| 18 | + 'decoder': | ||
| 19 | + './sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20/decoder-epoch-99-avg-1.onnx', | ||
| 20 | + 'joiner': | ||
| 21 | + './sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20/joiner-epoch-99-avg-1.onnx', | ||
| 22 | + }, | ||
| 23 | + 'tokens': | ||
| 24 | + './sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20/tokens.txt', | ||
| 25 | + 'numThreads': 2, | ||
| 26 | + 'provider': 'cpu', | ||
| 27 | + 'debug': 1, | ||
| 28 | + 'modelType': 'zipformer', | ||
| 29 | + }, | ||
| 30 | + 'decodingMethod': 'greedy_search', | ||
| 31 | + 'maxActivePaths': 4, | ||
| 32 | + 'enableEndpoint': true, | ||
| 33 | + 'rule1MinTrailingSilence': 2.4, | ||
| 34 | + 'rule2MinTrailingSilence': 1.2, | ||
| 35 | + 'rule3MinUtteranceLength': 20 | ||
| 36 | + }; | ||
| 37 | + | ||
| 38 | + return new sherpa_onnx.OnlineRecognizer(config); | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +const recognizer = createOnlineRecognizer(); | ||
| 42 | +const stream = recognizer.createStream(); | ||
| 43 | + | ||
| 44 | +let lastText = ''; | ||
| 45 | +let segmentIndex = 0; | ||
| 46 | + | ||
| 47 | +const ai = new portAudio.AudioIO({ | ||
| 48 | + inOptions: { | ||
| 49 | + channelCount: 1, | ||
| 50 | + closeOnError: true, // Close the stream if an audio error is detected, if | ||
| 51 | + // set false then just log the error | ||
| 52 | + deviceId: -1, // Use -1 or omit the deviceId to select the default device | ||
| 53 | + sampleFormat: portAudio.SampleFormatFloat32, | ||
| 54 | + sampleRate: recognizer.config.featConfig.sampleRate | ||
| 55 | + } | ||
| 56 | +}); | ||
| 57 | + | ||
| 58 | +const display = new sherpa_onnx.Display(50); | ||
| 59 | + | ||
| 60 | +ai.on('data', data => { | ||
| 61 | + const samples = new Float32Array(data.buffer); | ||
| 62 | + | ||
| 63 | + stream.acceptWaveform(samples, recognizer.config.featConfig.sampleRate); | ||
| 64 | + | ||
| 65 | + while (recognizer.isReady(stream)) { | ||
| 66 | + recognizer.decode(stream); | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + const isEndpoint = recognizer.isEndpoint(stream); | ||
| 70 | + const text = recognizer.getResult(stream).text; | ||
| 71 | + | ||
| 72 | + if (text.length > 0 && lastText != text) { | ||
| 73 | + lastText = text; | ||
| 74 | + display.print(segmentIndex, lastText); | ||
| 75 | + } | ||
| 76 | + if (isEndpoint) { | ||
| 77 | + if (text.length > 0) { | ||
| 78 | + lastText = text; | ||
| 79 | + segmentIndex += 1; | ||
| 80 | + } | ||
| 81 | + recognizer.reset(stream) | ||
| 82 | + } | ||
| 83 | +}); | ||
| 84 | + | ||
| 85 | +ai.on('close', () => { | ||
| 86 | + console.log('Free resources'); | ||
| 87 | + stream.free(); | ||
| 88 | + recognizer.free(); | ||
| 89 | +}); | ||
| 90 | + | ||
| 91 | +ai.start(); | ||
| 92 | +console.log('Started! Please speak') |
| @@ -4,4 +4,5 @@ const streaming_asr = require('./streaming-asr.js'); | @@ -4,4 +4,5 @@ const streaming_asr = require('./streaming-asr.js'); | ||
| 4 | module.exports = { | 4 | module.exports = { |
| 5 | OnlineRecognizer: streaming_asr.OnlineRecognizer, | 5 | OnlineRecognizer: streaming_asr.OnlineRecognizer, |
| 6 | readWave: addon.readWave, | 6 | readWave: addon.readWave, |
| 7 | + Display: streaming_asr.Display, | ||
| 7 | } | 8 | } |
| 1 | const addon = require('./addon.js'); | 1 | const addon = require('./addon.js'); |
| 2 | 2 | ||
| 3 | +class Display { | ||
| 4 | + constructor(maxWordPerline) { | ||
| 5 | + this.handle = addon.createDisplay(maxWordPerline); | ||
| 6 | + } | ||
| 7 | + | ||
| 8 | + print(idx, text) { | ||
| 9 | + addon.print(this.handle, idx, text) | ||
| 10 | + } | ||
| 11 | +} | ||
| 12 | + | ||
| 3 | class OnlineStream { | 13 | class OnlineStream { |
| 4 | constructor(handle) { | 14 | constructor(handle) { |
| 5 | this.handle = handle; | 15 | this.handle = handle; |
| @@ -10,11 +20,16 @@ class OnlineStream { | @@ -10,11 +20,16 @@ class OnlineStream { | ||
| 10 | addon.acceptWaveformOnline( | 20 | addon.acceptWaveformOnline( |
| 11 | this.handle, {samples: samples, sampleRate: sampleRate}) | 21 | this.handle, {samples: samples, sampleRate: sampleRate}) |
| 12 | } | 22 | } |
| 23 | + | ||
| 24 | + inputFinished() { | ||
| 25 | + addon.inputFinished(this.handle) | ||
| 26 | + } | ||
| 13 | } | 27 | } |
| 14 | 28 | ||
| 15 | class OnlineRecognizer { | 29 | class OnlineRecognizer { |
| 16 | constructor(config) { | 30 | constructor(config) { |
| 17 | this.handle = addon.createOnlineRecognizer(config); | 31 | this.handle = addon.createOnlineRecognizer(config); |
| 32 | + this.config = config | ||
| 18 | } | 33 | } |
| 19 | 34 | ||
| 20 | createStream() { | 35 | createStream() { |
| @@ -30,6 +45,14 @@ class OnlineRecognizer { | @@ -30,6 +45,14 @@ class OnlineRecognizer { | ||
| 30 | addon.decodeOnlineStream(this.handle, stream.handle); | 45 | addon.decodeOnlineStream(this.handle, stream.handle); |
| 31 | } | 46 | } |
| 32 | 47 | ||
| 48 | + isEndpoint(stream) { | ||
| 49 | + return addon.isEndpoint(this.handle, stream.handle); | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + reset(stream) { | ||
| 53 | + addon.reset(this.handle, stream.handle); | ||
| 54 | + } | ||
| 55 | + | ||
| 33 | getResult(stream) { | 56 | getResult(stream) { |
| 34 | const jsonStr = | 57 | const jsonStr = |
| 35 | addon.getOnlineStreamResultAsJson(this.handle, stream.handle); | 58 | addon.getOnlineStreamResultAsJson(this.handle, stream.handle); |
| @@ -38,4 +61,7 @@ class OnlineRecognizer { | @@ -38,4 +61,7 @@ class OnlineRecognizer { | ||
| 38 | } | 61 | } |
| 39 | } | 62 | } |
| 40 | 63 | ||
| 41 | -module.exports = {OnlineRecognizer} | 64 | +module.exports = { |
| 65 | + OnlineRecognizer, | ||
| 66 | + Display | ||
| 67 | +} |
| @@ -166,6 +166,69 @@ static Napi::External<SherpaOnnxOnlineRecognizer> CreateOnlineRecognizerWrapper( | @@ -166,6 +166,69 @@ static Napi::External<SherpaOnnxOnlineRecognizer> CreateOnlineRecognizerWrapper( | ||
| 166 | memset(&c, 0, sizeof(c)); | 166 | memset(&c, 0, sizeof(c)); |
| 167 | c.feat_config = GetFeatureConfig(config); | 167 | c.feat_config = GetFeatureConfig(config); |
| 168 | c.model_config = GetOnlineModelConfig(config); | 168 | c.model_config = GetOnlineModelConfig(config); |
| 169 | + | ||
| 170 | + if (config.Has("decodingMethod") && config.Get("decodingMethod").IsString()) { | ||
| 171 | + Napi::String decoding_method = | ||
| 172 | + config.Get("decodingMethod").As<Napi::String>(); | ||
| 173 | + std::string s = decoding_method.Utf8Value(); | ||
| 174 | + char *p = new char[s.size() + 1]; | ||
| 175 | + std::copy(s.begin(), s.end(), p); | ||
| 176 | + p[s.size()] = 0; | ||
| 177 | + | ||
| 178 | + c.decoding_method = p; | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + if (config.Has("maxActivePaths") && config.Get("maxActivePaths").IsNumber()) { | ||
| 182 | + c.max_active_paths = | ||
| 183 | + config.Get("maxActivePaths").As<Napi::Number>().Int32Value(); | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + // enableEndpoint can be either a boolean or an integer | ||
| 187 | + if (config.Has("enableEndpoint") && | ||
| 188 | + (config.Get("enableEndpoint").IsNumber() || | ||
| 189 | + config.Get("enableEndpoint").IsBoolean())) { | ||
| 190 | + if (config.Get("enableEndpoint").IsNumber()) { | ||
| 191 | + c.enable_endpoint = | ||
| 192 | + config.Get("enableEndpoint").As<Napi::Number>().Int32Value(); | ||
| 193 | + } else { | ||
| 194 | + c.enable_endpoint = | ||
| 195 | + config.Get("enableEndpoint").As<Napi::Boolean>().Value(); | ||
| 196 | + } | ||
| 197 | + } | ||
| 198 | + | ||
| 199 | + if (config.Has("rule1MinTrailingSilence") && | ||
| 200 | + config.Get("rule1MinTrailingSilence").IsNumber()) { | ||
| 201 | + c.rule1_min_trailing_silence = | ||
| 202 | + config.Get("rule1MinTrailingSilence").As<Napi::Number>().FloatValue(); | ||
| 203 | + } | ||
| 204 | + | ||
| 205 | + if (config.Has("rule2MinTrailingSilence") && | ||
| 206 | + config.Get("rule2MinTrailingSilence").IsNumber()) { | ||
| 207 | + c.rule2_min_trailing_silence = | ||
| 208 | + config.Get("rule2MinTrailingSilence").As<Napi::Number>().FloatValue(); | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | + if (config.Has("rule3MinUtteranceLength") && | ||
| 212 | + config.Get("rule3MinUtteranceLength").IsNumber()) { | ||
| 213 | + c.rule3_min_utterance_length = | ||
| 214 | + config.Get("rule3MinUtteranceLength").As<Napi::Number>().FloatValue(); | ||
| 215 | + } | ||
| 216 | + | ||
| 217 | + if (config.Has("hotwordsFile") && config.Get("hotwordsFile").IsString()) { | ||
| 218 | + Napi::String hotwords_file = config.Get("hotwordsFile").As<Napi::String>(); | ||
| 219 | + std::string s = hotwords_file.Utf8Value(); | ||
| 220 | + char *p = new char[s.size() + 1]; | ||
| 221 | + std::copy(s.begin(), s.end(), p); | ||
| 222 | + p[s.size()] = 0; | ||
| 223 | + | ||
| 224 | + c.hotwords_file = p; | ||
| 225 | + } | ||
| 226 | + | ||
| 227 | + if (config.Has("hotwordsScore") && config.Get("hotwordsScore").IsNumber()) { | ||
| 228 | + c.hotwords_score = | ||
| 229 | + config.Get("hotwordsScore").As<Napi::Number>().FloatValue(); | ||
| 230 | + } | ||
| 231 | + | ||
| 169 | #if 0 | 232 | #if 0 |
| 170 | printf("encoder: %s\n", c.model_config.transducer.encoder | 233 | printf("encoder: %s\n", c.model_config.transducer.encoder |
| 171 | ? c.model_config.transducer.encoder | 234 | ? c.model_config.transducer.encoder |
| @@ -184,6 +247,15 @@ static Napi::External<SherpaOnnxOnlineRecognizer> CreateOnlineRecognizerWrapper( | @@ -184,6 +247,15 @@ static Napi::External<SherpaOnnxOnlineRecognizer> CreateOnlineRecognizerWrapper( | ||
| 184 | printf("debug: %d\n", c.model_config.debug); | 247 | printf("debug: %d\n", c.model_config.debug); |
| 185 | printf("model_type: %s\n", | 248 | printf("model_type: %s\n", |
| 186 | c.model_config.model_type ? c.model_config.model_type : "no"); | 249 | c.model_config.model_type ? c.model_config.model_type : "no"); |
| 250 | + | ||
| 251 | + printf("decoding_method: %s\n", c.decoding_method ? c.decoding_method : "no"); | ||
| 252 | + printf("max_active_paths: %d\n", c.max_active_paths); | ||
| 253 | + printf("enable_endpoint: %d\n", c.enable_endpoint); | ||
| 254 | + printf("rule1_min_trailing_silence: %.3f\n", c.rule1_min_trailing_silence); | ||
| 255 | + printf("rule2_min_trailing_silence: %.3f\n", c.rule2_min_trailing_silence); | ||
| 256 | + printf("rule3_min_utterance_length: %.3f\n", c.rule3_min_utterance_length); | ||
| 257 | + printf("hotwords_file: %s\n", c.hotwords_file ? c.hotwords_file : "no"); | ||
| 258 | + printf("hotwords_score: %.3f\n", c.hotwords_score); | ||
| 187 | #endif | 259 | #endif |
| 188 | 260 | ||
| 189 | SherpaOnnxOnlineRecognizer *recognizer = CreateOnlineRecognizer(&c); | 261 | SherpaOnnxOnlineRecognizer *recognizer = CreateOnlineRecognizer(&c); |
| @@ -212,6 +284,14 @@ static Napi::External<SherpaOnnxOnlineRecognizer> CreateOnlineRecognizerWrapper( | @@ -212,6 +284,14 @@ static Napi::External<SherpaOnnxOnlineRecognizer> CreateOnlineRecognizerWrapper( | ||
| 212 | delete[] c.model_config.model_type; | 284 | delete[] c.model_config.model_type; |
| 213 | } | 285 | } |
| 214 | 286 | ||
| 287 | + if (c.decoding_method) { | ||
| 288 | + delete[] c.decoding_method; | ||
| 289 | + } | ||
| 290 | + | ||
| 291 | + if (c.hotwords_file) { | ||
| 292 | + delete[] c.hotwords_file; | ||
| 293 | + } | ||
| 294 | + | ||
| 215 | if (!recognizer) { | 295 | if (!recognizer) { |
| 216 | Napi::TypeError::New(env, "Please check your config!") | 296 | Napi::TypeError::New(env, "Please check your config!") |
| 217 | .ThrowAsJavaScriptException(); | 297 | .ThrowAsJavaScriptException(); |
| @@ -270,7 +350,7 @@ static void AcceptWaveformWrapper(const Napi::CallbackInfo &info) { | @@ -270,7 +350,7 @@ static void AcceptWaveformWrapper(const Napi::CallbackInfo &info) { | ||
| 270 | } | 350 | } |
| 271 | 351 | ||
| 272 | if (!info[0].IsExternal()) { | 352 | if (!info[0].IsExternal()) { |
| 273 | - Napi::TypeError::New(env, "Argument 0 should be a online stream pointer.") | 353 | + Napi::TypeError::New(env, "Argument 0 should be an online stream pointer.") |
| 274 | .ThrowAsJavaScriptException(); | 354 | .ThrowAsJavaScriptException(); |
| 275 | 355 | ||
| 276 | return; | 356 | return; |
| @@ -337,15 +417,14 @@ static Napi::Boolean IsOnlineStreamReadyWrapper( | @@ -337,15 +417,14 @@ static Napi::Boolean IsOnlineStreamReadyWrapper( | ||
| 337 | 417 | ||
| 338 | if (!info[0].IsExternal()) { | 418 | if (!info[0].IsExternal()) { |
| 339 | Napi::TypeError::New(env, | 419 | Napi::TypeError::New(env, |
| 340 | - "Argument 0 should be a online recognizer pointer.") | 420 | + "Argument 0 should be an online recognizer pointer.") |
| 341 | .ThrowAsJavaScriptException(); | 421 | .ThrowAsJavaScriptException(); |
| 342 | 422 | ||
| 343 | return {}; | 423 | return {}; |
| 344 | } | 424 | } |
| 345 | 425 | ||
| 346 | if (!info[1].IsExternal()) { | 426 | if (!info[1].IsExternal()) { |
| 347 | - Napi::TypeError::New(env, | ||
| 348 | - "Argument 1 should be a online recognizer pointer.") | 427 | + Napi::TypeError::New(env, "Argument 1 should be an online stream pointer.") |
| 349 | .ThrowAsJavaScriptException(); | 428 | .ThrowAsJavaScriptException(); |
| 350 | 429 | ||
| 351 | return {}; | 430 | return {}; |
| @@ -375,15 +454,14 @@ static void DecodeOnlineStreamWrapper(const Napi::CallbackInfo &info) { | @@ -375,15 +454,14 @@ static void DecodeOnlineStreamWrapper(const Napi::CallbackInfo &info) { | ||
| 375 | 454 | ||
| 376 | if (!info[0].IsExternal()) { | 455 | if (!info[0].IsExternal()) { |
| 377 | Napi::TypeError::New(env, | 456 | Napi::TypeError::New(env, |
| 378 | - "Argument 0 should be a online recognizer pointer.") | 457 | + "Argument 0 should be an online recognizer pointer.") |
| 379 | .ThrowAsJavaScriptException(); | 458 | .ThrowAsJavaScriptException(); |
| 380 | 459 | ||
| 381 | return; | 460 | return; |
| 382 | } | 461 | } |
| 383 | 462 | ||
| 384 | if (!info[1].IsExternal()) { | 463 | if (!info[1].IsExternal()) { |
| 385 | - Napi::TypeError::New(env, | ||
| 386 | - "Argument 1 should be a online recognizer pointer.") | 464 | + Napi::TypeError::New(env, "Argument 1 should be an online stream pointer.") |
| 387 | .ThrowAsJavaScriptException(); | 465 | .ThrowAsJavaScriptException(); |
| 388 | 466 | ||
| 389 | return; | 467 | return; |
| @@ -412,15 +490,14 @@ static Napi::String GetOnlineStreamResultAsJsonWrapper( | @@ -412,15 +490,14 @@ static Napi::String GetOnlineStreamResultAsJsonWrapper( | ||
| 412 | 490 | ||
| 413 | if (!info[0].IsExternal()) { | 491 | if (!info[0].IsExternal()) { |
| 414 | Napi::TypeError::New(env, | 492 | Napi::TypeError::New(env, |
| 415 | - "Argument 0 should be a online recognizer pointer.") | 493 | + "Argument 0 should be an online recognizer pointer.") |
| 416 | .ThrowAsJavaScriptException(); | 494 | .ThrowAsJavaScriptException(); |
| 417 | 495 | ||
| 418 | return {}; | 496 | return {}; |
| 419 | } | 497 | } |
| 420 | 498 | ||
| 421 | if (!info[1].IsExternal()) { | 499 | if (!info[1].IsExternal()) { |
| 422 | - Napi::TypeError::New(env, | ||
| 423 | - "Argument 1 should be a online recognizer pointer.") | 500 | + Napi::TypeError::New(env, "Argument 1 should be an online stream pointer.") |
| 424 | .ThrowAsJavaScriptException(); | 501 | .ThrowAsJavaScriptException(); |
| 425 | 502 | ||
| 426 | return {}; | 503 | return {}; |
| @@ -440,6 +517,175 @@ static Napi::String GetOnlineStreamResultAsJsonWrapper( | @@ -440,6 +517,175 @@ static Napi::String GetOnlineStreamResultAsJsonWrapper( | ||
| 440 | return s; | 517 | return s; |
| 441 | } | 518 | } |
| 442 | 519 | ||
| 520 | +static void InputFinishedWrapper(const Napi::CallbackInfo &info) { | ||
| 521 | + Napi::Env env = info.Env(); | ||
| 522 | + | ||
| 523 | + if (info.Length() != 1) { | ||
| 524 | + std::ostringstream os; | ||
| 525 | + os << "Expect only 1 arguments. Given: " << info.Length(); | ||
| 526 | + | ||
| 527 | + Napi::TypeError::New(env, os.str()).ThrowAsJavaScriptException(); | ||
| 528 | + | ||
| 529 | + return; | ||
| 530 | + } | ||
| 531 | + | ||
| 532 | + if (!info[0].IsExternal()) { | ||
| 533 | + Napi::TypeError::New(env, "Argument 0 should be an online stream pointer.") | ||
| 534 | + .ThrowAsJavaScriptException(); | ||
| 535 | + | ||
| 536 | + return; | ||
| 537 | + } | ||
| 538 | + | ||
| 539 | + SherpaOnnxOnlineStream *stream = | ||
| 540 | + info[0].As<Napi::External<SherpaOnnxOnlineStream>>().Data(); | ||
| 541 | + | ||
| 542 | + InputFinished(stream); | ||
| 543 | +} | ||
| 544 | + | ||
| 545 | +static void ResetOnlineStreamWrapper(const Napi::CallbackInfo &info) { | ||
| 546 | + Napi::Env env = info.Env(); | ||
| 547 | + if (info.Length() != 2) { | ||
| 548 | + std::ostringstream os; | ||
| 549 | + os << "Expect only 2 arguments. Given: " << info.Length(); | ||
| 550 | + | ||
| 551 | + Napi::TypeError::New(env, os.str()).ThrowAsJavaScriptException(); | ||
| 552 | + | ||
| 553 | + return; | ||
| 554 | + } | ||
| 555 | + | ||
| 556 | + if (!info[0].IsExternal()) { | ||
| 557 | + Napi::TypeError::New(env, | ||
| 558 | + "Argument 0 should be an online recognizer pointer.") | ||
| 559 | + .ThrowAsJavaScriptException(); | ||
| 560 | + | ||
| 561 | + return; | ||
| 562 | + } | ||
| 563 | + | ||
| 564 | + if (!info[1].IsExternal()) { | ||
| 565 | + Napi::TypeError::New(env, "Argument 1 should be an online stream pointer.") | ||
| 566 | + .ThrowAsJavaScriptException(); | ||
| 567 | + | ||
| 568 | + return; | ||
| 569 | + } | ||
| 570 | + | ||
| 571 | + SherpaOnnxOnlineRecognizer *recognizer = | ||
| 572 | + info[0].As<Napi::External<SherpaOnnxOnlineRecognizer>>().Data(); | ||
| 573 | + | ||
| 574 | + SherpaOnnxOnlineStream *stream = | ||
| 575 | + info[1].As<Napi::External<SherpaOnnxOnlineStream>>().Data(); | ||
| 576 | + | ||
| 577 | + Reset(recognizer, stream); | ||
| 578 | +} | ||
| 579 | + | ||
| 580 | +static Napi::Boolean IsEndpointWrapper(const Napi::CallbackInfo &info) { | ||
| 581 | + Napi::Env env = info.Env(); | ||
| 582 | + if (info.Length() != 2) { | ||
| 583 | + std::ostringstream os; | ||
| 584 | + os << "Expect only 2 arguments. Given: " << info.Length(); | ||
| 585 | + | ||
| 586 | + Napi::TypeError::New(env, os.str()).ThrowAsJavaScriptException(); | ||
| 587 | + | ||
| 588 | + return {}; | ||
| 589 | + } | ||
| 590 | + | ||
| 591 | + if (!info[0].IsExternal()) { | ||
| 592 | + Napi::TypeError::New(env, | ||
| 593 | + "Argument 0 should be an online recognizer pointer.") | ||
| 594 | + .ThrowAsJavaScriptException(); | ||
| 595 | + | ||
| 596 | + return {}; | ||
| 597 | + } | ||
| 598 | + | ||
| 599 | + if (!info[1].IsExternal()) { | ||
| 600 | + Napi::TypeError::New(env, "Argument 1 should be an online stream pointer.") | ||
| 601 | + .ThrowAsJavaScriptException(); | ||
| 602 | + | ||
| 603 | + return {}; | ||
| 604 | + } | ||
| 605 | + | ||
| 606 | + SherpaOnnxOnlineRecognizer *recognizer = | ||
| 607 | + info[0].As<Napi::External<SherpaOnnxOnlineRecognizer>>().Data(); | ||
| 608 | + | ||
| 609 | + SherpaOnnxOnlineStream *stream = | ||
| 610 | + info[1].As<Napi::External<SherpaOnnxOnlineStream>>().Data(); | ||
| 611 | + | ||
| 612 | + int32_t is_endpoint = IsEndpoint(recognizer, stream); | ||
| 613 | + | ||
| 614 | + return Napi::Boolean::New(env, is_endpoint); | ||
| 615 | +} | ||
| 616 | + | ||
| 617 | +static Napi::External<SherpaOnnxDisplay> CreateDisplayWrapper( | ||
| 618 | + const Napi::CallbackInfo &info) { | ||
| 619 | + Napi::Env env = info.Env(); | ||
| 620 | + if (info.Length() != 1) { | ||
| 621 | + std::ostringstream os; | ||
| 622 | + os << "Expect only 1 argument. Given: " << info.Length(); | ||
| 623 | + | ||
| 624 | + Napi::TypeError::New(env, os.str()).ThrowAsJavaScriptException(); | ||
| 625 | + | ||
| 626 | + return {}; | ||
| 627 | + } | ||
| 628 | + | ||
| 629 | + if (!info[0].IsNumber()) { | ||
| 630 | + Napi::TypeError::New(env, "Expect a number as the argument") | ||
| 631 | + .ThrowAsJavaScriptException(); | ||
| 632 | + | ||
| 633 | + return {}; | ||
| 634 | + } | ||
| 635 | + int32_t max_word_per_line = info[0].As<Napi::Number>().Int32Value(); | ||
| 636 | + | ||
| 637 | + const SherpaOnnxDisplay *display = CreateDisplay(max_word_per_line); | ||
| 638 | + | ||
| 639 | + return Napi::External<SherpaOnnxDisplay>::New( | ||
| 640 | + env, const_cast<SherpaOnnxDisplay *>(display), | ||
| 641 | + [](Napi::Env env, SherpaOnnxDisplay *display) { | ||
| 642 | + DestroyDisplay(display); | ||
| 643 | + }); | ||
| 644 | +} | ||
| 645 | + | ||
| 646 | +static void PrintWrapper(const Napi::CallbackInfo &info) { | ||
| 647 | + Napi::Env env = info.Env(); | ||
| 648 | + | ||
| 649 | + if (info.Length() != 3) { | ||
| 650 | + std::ostringstream os; | ||
| 651 | + os << "Expect only 3 arguments. Given: " << info.Length(); | ||
| 652 | + | ||
| 653 | + Napi::TypeError::New(env, os.str()).ThrowAsJavaScriptException(); | ||
| 654 | + | ||
| 655 | + return; | ||
| 656 | + } | ||
| 657 | + | ||
| 658 | + if (!info[0].IsExternal()) { | ||
| 659 | + Napi::TypeError::New(env, "Argument 0 should be an online stream pointer.") | ||
| 660 | + .ThrowAsJavaScriptException(); | ||
| 661 | + | ||
| 662 | + return; | ||
| 663 | + } | ||
| 664 | + | ||
| 665 | + if (!info[1].IsNumber()) { | ||
| 666 | + Napi::TypeError::New(env, "Argument 1 should be a number.") | ||
| 667 | + .ThrowAsJavaScriptException(); | ||
| 668 | + | ||
| 669 | + return; | ||
| 670 | + } | ||
| 671 | + | ||
| 672 | + if (!info[2].IsString()) { | ||
| 673 | + Napi::TypeError::New(env, "Argument 2 should be a string.") | ||
| 674 | + .ThrowAsJavaScriptException(); | ||
| 675 | + | ||
| 676 | + return; | ||
| 677 | + } | ||
| 678 | + | ||
| 679 | + SherpaOnnxDisplay *display = | ||
| 680 | + info[0].As<Napi::External<SherpaOnnxDisplay>>().Data(); | ||
| 681 | + | ||
| 682 | + int32_t idx = info[1].As<Napi::Number>().Int32Value(); | ||
| 683 | + | ||
| 684 | + Napi::String text = info[2].As<Napi::String>(); | ||
| 685 | + std::string s = text.Utf8Value(); | ||
| 686 | + SherpaOnnxPrint(display, idx, s.c_str()); | ||
| 687 | +} | ||
| 688 | + | ||
| 443 | void InitStreamingAsr(Napi::Env env, Napi::Object exports) { | 689 | void InitStreamingAsr(Napi::Env env, Napi::Object exports) { |
| 444 | exports.Set(Napi::String::New(env, "createOnlineRecognizer"), | 690 | exports.Set(Napi::String::New(env, "createOnlineRecognizer"), |
| 445 | Napi::Function::New(env, CreateOnlineRecognizerWrapper)); | 691 | Napi::Function::New(env, CreateOnlineRecognizerWrapper)); |
| @@ -458,4 +704,19 @@ void InitStreamingAsr(Napi::Env env, Napi::Object exports) { | @@ -458,4 +704,19 @@ void InitStreamingAsr(Napi::Env env, Napi::Object exports) { | ||
| 458 | 704 | ||
| 459 | exports.Set(Napi::String::New(env, "getOnlineStreamResultAsJson"), | 705 | exports.Set(Napi::String::New(env, "getOnlineStreamResultAsJson"), |
| 460 | Napi::Function::New(env, GetOnlineStreamResultAsJsonWrapper)); | 706 | Napi::Function::New(env, GetOnlineStreamResultAsJsonWrapper)); |
| 707 | + | ||
| 708 | + exports.Set(Napi::String::New(env, "inputFinished"), | ||
| 709 | + Napi::Function::New(env, InputFinishedWrapper)); | ||
| 710 | + | ||
| 711 | + exports.Set(Napi::String::New(env, "reset"), | ||
| 712 | + Napi::Function::New(env, ResetOnlineStreamWrapper)); | ||
| 713 | + | ||
| 714 | + exports.Set(Napi::String::New(env, "isEndpoint"), | ||
| 715 | + Napi::Function::New(env, IsEndpointWrapper)); | ||
| 716 | + | ||
| 717 | + exports.Set(Napi::String::New(env, "createDisplay"), | ||
| 718 | + Napi::Function::New(env, CreateDisplayWrapper)); | ||
| 719 | + | ||
| 720 | + exports.Set(Napi::String::New(env, "print"), | ||
| 721 | + Napi::Function::New(env, PrintWrapper)); | ||
| 461 | } | 722 | } |
-
请 注册 或 登录 后发表评论