From 21165580a16cea8019fbe31d3c5156053ba6b784 Mon Sep 17 00:00:00 2001 From: Lucas Zanek <57494138+LucasZNK@users.noreply.github.com> Date: Wed, 22 Mar 2023 17:19:22 -0300 Subject: [PATCH] Nodejs Addon blocking main thread. Implemented Napi::AsyncWorker (#642) * fixed blocking code on node addon * modify the example to run async * format * added logic to see the whisper output * added logic to see the whisper output * removed extra function for more clean example --- examples/addon.node/addon.cpp | 81 ++++++++++++++++++++--------------- examples/addon.node/index.js | 41 +++++++++++------- 2 files changed, 72 insertions(+), 50 deletions(-) diff --git a/examples/addon.node/addon.cpp b/examples/addon.node/addon.cpp index 825232736bd..0fa4a8ca3bc 100644 --- a/examples/addon.node/addon.cpp +++ b/examples/addon.node/addon.cpp @@ -292,51 +292,64 @@ int run(whisper_params ¶ms, std::vector> &result) { return 0; } -Napi::Object whisper(const Napi::CallbackInfo& info) { - Napi::Env env = info.Env(); - if (info.Length() <= 0 || !info[0].IsObject()) { - Napi::TypeError::New(env, "object expected").ThrowAsJavaScriptException(); +class Worker : public Napi::AsyncWorker { + public: + Worker(Napi::Function& callback, whisper_params params) + : Napi::AsyncWorker(callback), params(params) {} + + void Execute() override { + run(params, result); + } + + void OnOK() override { + Napi::HandleScope scope(Env()); + Napi::Object res = Napi::Array::New(Env(), result.size()); + for (uint64_t i = 0; i < result.size(); ++i) { + Napi::Object tmp = Napi::Array::New(Env(), 3); + for (uint64_t j = 0; j < 3; ++j) { + tmp[j] = Napi::String::New(Env(), result[i][j]); + } + res[i] = tmp; } - whisper_params params; - std::vector> result; + Callback().Call({Env().Null(), res}); + } - Napi::Object whisper_params = info[0].As(); - std::string language = whisper_params.Get("language").As(); - std::string model = whisper_params.Get("model").As(); - std::string input = whisper_params.Get("fname_inp").As(); + private: + whisper_params params; + std::vector> result; +}; - params.language = language; - params.model = model; - params.fname_inp.emplace_back(input); - // run model - run(params, result); - fprintf(stderr, "RESULT:\n"); - for (auto sentence:result) { - fprintf(stderr, "t0: %s, t1: %s, content: %s \n", - sentence[0].c_str(), sentence[1].c_str(), sentence[2].c_str()); - } +Napi::Value whisper(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + if (info.Length() <= 0 || !info[0].IsObject()) { + Napi::TypeError::New(env, "object expected").ThrowAsJavaScriptException(); + } + whisper_params params; - Napi::Object res = Napi::Array::New(env, result.size()); - for (uint64_t i = 0; i < result.size(); ++i) { - Napi::Object tmp = Napi::Array::New(env, 3); - for (uint64_t j = 0; j < 3; ++j) { - tmp[j] = Napi::String::New(env, result[i][j]); - } - res[i] = tmp; - } + Napi::Object whisper_params = info[0].As(); + std::string language = whisper_params.Get("language").As(); + std::string model = whisper_params.Get("model").As(); + std::string input = whisper_params.Get("fname_inp").As(); + + params.language = language; + params.model = model; + params.fname_inp.emplace_back(input); - return res; + Napi::Function callback = info[1].As(); + Worker* worker = new Worker(callback, params); + worker->Queue(); + return env.Undefined(); } Napi::Object Init(Napi::Env env, Napi::Object exports) { - exports.Set( - Napi::String::New(env, "whisper"), - Napi::Function::New(env, whisper) - ); - return exports; + exports.Set( + Napi::String::New(env, "whisper"), + Napi::Function::New(env, whisper) + ); + return exports; } NODE_API_MODULE(whisper, Init); diff --git a/examples/addon.node/index.js b/examples/addon.node/index.js index c9038faea43..d511cdc2b67 100644 --- a/examples/addon.node/index.js +++ b/examples/addon.node/index.js @@ -1,27 +1,36 @@ -const path = require('path'); -const { whisper } = require(path.join(__dirname, '../../build/Release/whisper-addon')); +const path = require("path"); +const { whisper } = require(path.join( + __dirname, + "../../build/Release/whisper-addon" +)); +const { promisify } = require("util"); + +const whisperAsync = promisify(whisper); const whisperParams = { - language: 'en', - model: path.join(__dirname, '../../models/ggml-base.en.bin'), - fname_inp: '', + language: "en", + model: path.join(__dirname, "../../models/ggml-base.en.bin"), + fname_inp: "../../samples/jfk.wav", }; const arguments = process.argv.slice(2); const params = Object.fromEntries( - arguments.reduce((pre, item) => { - if (item.startsWith("--")) { - return [...pre, item.slice(2).split("=")]; - } - return pre; - }, []), + arguments.reduce((pre, item) => { + if (item.startsWith("--")) { + return [...pre, item.slice(2).split("=")]; + } + return pre; + }, []) ); for (const key in params) { - if (whisperParams.hasOwnProperty(key)) { - whisperParams[key] = params[key]; - } + if (whisperParams.hasOwnProperty(key)) { + whisperParams[key] = params[key]; + } } -console.log('whisperParams =', whisperParams); -console.log(whisper(whisperParams)); +console.log("whisperParams =", whisperParams); + +whisperAsync(whisperParams).then((result) => { + console.log(`Result from whisper: ${result}`); +});