Lucas Zanek commited on
Commit
099bfe3
·
unverified ·
1 Parent(s): a994b77

Nodejs Addon blocking main thread. Implemented Napi::AsyncWorker (#642)

Browse files

* 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 CHANGED
@@ -292,51 +292,64 @@ int run(whisper_params &params, std::vector<std::vector<std::string>> &result) {
292
  return 0;
293
  }
294
 
295
- Napi::Object whisper(const Napi::CallbackInfo& info) {
296
- Napi::Env env = info.Env();
297
- if (info.Length() <= 0 || !info[0].IsObject()) {
298
- Napi::TypeError::New(env, "object expected").ThrowAsJavaScriptException();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  }
300
- whisper_params params;
301
- std::vector<std::vector<std::string>> result;
302
 
303
- Napi::Object whisper_params = info[0].As<Napi::Object>();
304
- std::string language = whisper_params.Get("language").As<Napi::String>();
305
- std::string model = whisper_params.Get("model").As<Napi::String>();
306
- std::string input = whisper_params.Get("fname_inp").As<Napi::String>();
307
 
308
- params.language = language;
309
- params.model = model;
310
- params.fname_inp.emplace_back(input);
311
 
312
- // run model
313
- run(params, result);
314
 
315
- fprintf(stderr, "RESULT:\n");
316
- for (auto sentence:result) {
317
- fprintf(stderr, "t0: %s, t1: %s, content: %s \n",
318
- sentence[0].c_str(), sentence[1].c_str(), sentence[2].c_str());
319
- }
 
320
 
321
- Napi::Object res = Napi::Array::New(env, result.size());
322
- for (uint64_t i = 0; i < result.size(); ++i) {
323
- Napi::Object tmp = Napi::Array::New(env, 3);
324
- for (uint64_t j = 0; j < 3; ++j) {
325
- tmp[j] = Napi::String::New(env, result[i][j]);
326
- }
327
- res[i] = tmp;
328
- }
329
 
330
- return res;
 
 
 
331
  }
332
 
333
 
334
  Napi::Object Init(Napi::Env env, Napi::Object exports) {
335
- exports.Set(
336
- Napi::String::New(env, "whisper"),
337
- Napi::Function::New(env, whisper)
338
- );
339
- return exports;
340
  }
341
 
342
  NODE_API_MODULE(whisper, Init);
 
292
  return 0;
293
  }
294
 
295
+ class Worker : public Napi::AsyncWorker {
296
+ public:
297
+ Worker(Napi::Function& callback, whisper_params params)
298
+ : Napi::AsyncWorker(callback), params(params) {}
299
+
300
+ void Execute() override {
301
+ run(params, result);
302
+ }
303
+
304
+ void OnOK() override {
305
+ Napi::HandleScope scope(Env());
306
+ Napi::Object res = Napi::Array::New(Env(), result.size());
307
+ for (uint64_t i = 0; i < result.size(); ++i) {
308
+ Napi::Object tmp = Napi::Array::New(Env(), 3);
309
+ for (uint64_t j = 0; j < 3; ++j) {
310
+ tmp[j] = Napi::String::New(Env(), result[i][j]);
311
+ }
312
+ res[i] = tmp;
313
  }
314
+ Callback().Call({Env().Null(), res});
315
+ }
316
 
317
+ private:
318
+ whisper_params params;
319
+ std::vector<std::vector<std::string>> result;
320
+ };
321
 
 
 
 
322
 
 
 
323
 
324
+ Napi::Value whisper(const Napi::CallbackInfo& info) {
325
+ Napi::Env env = info.Env();
326
+ if (info.Length() <= 0 || !info[0].IsObject()) {
327
+ Napi::TypeError::New(env, "object expected").ThrowAsJavaScriptException();
328
+ }
329
+ whisper_params params;
330
 
331
+ Napi::Object whisper_params = info[0].As<Napi::Object>();
332
+ std::string language = whisper_params.Get("language").As<Napi::String>();
333
+ std::string model = whisper_params.Get("model").As<Napi::String>();
334
+ std::string input = whisper_params.Get("fname_inp").As<Napi::String>();
335
+
336
+ params.language = language;
337
+ params.model = model;
338
+ params.fname_inp.emplace_back(input);
339
 
340
+ Napi::Function callback = info[1].As<Napi::Function>();
341
+ Worker* worker = new Worker(callback, params);
342
+ worker->Queue();
343
+ return env.Undefined();
344
  }
345
 
346
 
347
  Napi::Object Init(Napi::Env env, Napi::Object exports) {
348
+ exports.Set(
349
+ Napi::String::New(env, "whisper"),
350
+ Napi::Function::New(env, whisper)
351
+ );
352
+ return exports;
353
  }
354
 
355
  NODE_API_MODULE(whisper, Init);
examples/addon.node/index.js CHANGED
@@ -1,27 +1,36 @@
1
- const path = require('path');
2
- const { whisper } = require(path.join(__dirname, '../../build/Release/whisper-addon'));
 
 
 
 
 
 
3
 
4
  const whisperParams = {
5
- language: 'en',
6
- model: path.join(__dirname, '../../models/ggml-base.en.bin'),
7
- fname_inp: '',
8
  };
9
 
10
  const arguments = process.argv.slice(2);
11
  const params = Object.fromEntries(
12
- arguments.reduce((pre, item) => {
13
- if (item.startsWith("--")) {
14
- return [...pre, item.slice(2).split("=")];
15
- }
16
- return pre;
17
- }, []),
18
  );
19
 
20
  for (const key in params) {
21
- if (whisperParams.hasOwnProperty(key)) {
22
- whisperParams[key] = params[key];
23
- }
24
  }
25
 
26
- console.log('whisperParams =', whisperParams);
27
- console.log(whisper(whisperParams));
 
 
 
 
1
+ const path = require("path");
2
+ const { whisper } = require(path.join(
3
+ __dirname,
4
+ "../../build/Release/whisper-addon"
5
+ ));
6
+ const { promisify } = require("util");
7
+
8
+ const whisperAsync = promisify(whisper);
9
 
10
  const whisperParams = {
11
+ language: "en",
12
+ model: path.join(__dirname, "../../models/ggml-base.en.bin"),
13
+ fname_inp: "../../samples/jfk.wav",
14
  };
15
 
16
  const arguments = process.argv.slice(2);
17
  const params = Object.fromEntries(
18
+ arguments.reduce((pre, item) => {
19
+ if (item.startsWith("--")) {
20
+ return [...pre, item.slice(2).split("=")];
21
+ }
22
+ return pre;
23
+ }, [])
24
  );
25
 
26
  for (const key in params) {
27
+ if (whisperParams.hasOwnProperty(key)) {
28
+ whisperParams[key] = params[key];
29
+ }
30
  }
31
 
32
+ console.log("whisperParams =", whisperParams);
33
+
34
+ whisperAsync(whisperParams).then((result) => {
35
+ console.log(`Result from whisper: ${result}`);
36
+ });