]> git.djapps.eu Git - pkg/ggml/sources/whisper.cpp/commitdiff
Nodejs Addon blocking main thread. Implemented Napi::AsyncWorker (#642)
authorLucas Zanek <redacted>
Wed, 22 Mar 2023 20:19:22 +0000 (17:19 -0300)
committerGitHub <redacted>
Wed, 22 Mar 2023 20:19:22 +0000 (22:19 +0200)
* 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
examples/addon.node/index.js

index 825232736bd6de8281ba4c06cb676ca0ee46c971..0fa4a8ca3bc3ffcc239fd7a75afb809fef13c775 100644 (file)
@@ -292,51 +292,64 @@ int run(whisper_params &params, std::vector<std::vector<std::string>> &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<std::vector<std::string>> result;
+    Callback().Call({Env().Null(), res});
+  }
 
-    Napi::Object whisper_params = info[0].As<Napi::Object>();
-    std::string language = whisper_params.Get("language").As<Napi::String>();
-    std::string model = whisper_params.Get("model").As<Napi::String>();
-    std::string input = whisper_params.Get("fname_inp").As<Napi::String>();
+ private:
+  whisper_params params;
+  std::vector<std::vector<std::string>> 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<Napi::Object>();
+  std::string language = whisper_params.Get("language").As<Napi::String>();
+  std::string model = whisper_params.Get("model").As<Napi::String>();
+  std::string input = whisper_params.Get("fname_inp").As<Napi::String>();
+
+  params.language = language;
+  params.model = model;
+  params.fname_inp.emplace_back(input);
 
-    return res;
+  Napi::Function callback = info[1].As<Napi::Function>();
+  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);
index c9038faea4334315aa4d4d9fff3e13ab977b5071..d511cdc2b673ef3e3e191eba6c7df3340caad11a 100644 (file)
@@ -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}`);
+});