114 lines
5.1 KiB
C++
114 lines
5.1 KiB
C++
#include "addon.h" // Your header file for WhisperStreamWrapper
|
|
#include "whisper-stream.h" // Your header file for the WhisperStream class
|
|
|
|
// NOTE: The N-API wrapper handles errors by throwing JS exceptions, so this macro is not needed.
|
|
// #define CHECK_STATUS(env, status, msg) ...
|
|
|
|
// --- Implementation of the Wrapper ---
|
|
|
|
Napi::Object WhisperStreamWrapper::Init(Napi::Env env, Napi::Object exports) {
|
|
Napi::Function func = DefineClass(env, "WhisperStream", {
|
|
InstanceMethod("startModel", &WhisperStreamWrapper::startModel),
|
|
InstanceMethod("processChunk", &WhisperStreamWrapper::ProcessChunk),
|
|
InstanceMethod("freeModel", &WhisperStreamWrapper::freeModel),
|
|
});
|
|
|
|
exports.Set("WhisperStream", func);
|
|
return exports;
|
|
}
|
|
|
|
WhisperStreamWrapper::WhisperStreamWrapper(const Napi::CallbackInfo& info) : Napi::ObjectWrap<WhisperStreamWrapper>(info) {
|
|
}
|
|
|
|
Napi::Value WhisperStreamWrapper::startModel(const Napi::CallbackInfo& info) {
|
|
Napi::Env env = info.Env();
|
|
|
|
if (info.Length() < 1 || !info[0].IsObject()) {
|
|
Napi::TypeError::New(env, "Expected a configuration object").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
Napi::Object js_params = info[0].As<Napi::Object>();
|
|
StreamParams params;
|
|
|
|
if (js_params.Has("modelPath")) {
|
|
params.model = js_params.Get("modelPath").As<Napi::String>();
|
|
} else {
|
|
Napi::TypeError::New(env, "Missing required parameter 'model'").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (js_params.Has("language")) params.language = js_params.Get("language").As<Napi::String>();
|
|
if (js_params.Has("nThreads")) params.n_threads = js_params.Get("nThreads").As<Napi::Number>();
|
|
if (js_params.Has("stepMs")) params.step_ms = js_params.Get("stepMs").As<Napi::Number>();
|
|
if (js_params.Has("lengthMs")) params.length_ms = js_params.Get("lengthMs").As<Napi::Number>();
|
|
if (js_params.Has("keepMs")) params.keep_ms = js_params.Get("keepMs").As<Napi::Number>();
|
|
if (js_params.Has("maxTokens")) params.max_tokens = js_params.Get("maxTokens").As<Napi::Number>();
|
|
if (js_params.Has("audioCtx")) params.audio_ctx = js_params.Get("audioCtx").As<Napi::Number>();
|
|
if (js_params.Has("vadThold")) params.vad_thold = js_params.Get("vadThold").As<Napi::Number>();
|
|
if (js_params.Has("beamSize")) params.beam_size = js_params.Get("beamSize").As<Napi::Number>();
|
|
if (js_params.Has("freqThold")) params.freq_thold = js_params.Get("freqThold").As<Napi::Number>();
|
|
if (js_params.Has("translate")) params.translate = js_params.Get("translate").As<Napi::Boolean>();
|
|
if (js_params.Has("noFallback")) params.no_fallback = js_params.Get("noFallback").As<Napi::Boolean>();
|
|
if (js_params.Has("printSpecial")) params.print_special = js_params.Get("printSpecial").As<Napi::Boolean>();
|
|
if (js_params.Has("noContext")) params.no_context = js_params.Get("noContext").As<Napi::Boolean>();
|
|
if (js_params.Has("noTimestamps")) params.no_timestamps = js_params.Get("noTimestamps").As<Napi::Boolean>();
|
|
if (js_params.Has("tinydiarize")) params.tinydiarize = js_params.Get("tinydiarize").As<Napi::Boolean>();
|
|
if (js_params.Has("saveAudio")) params.save_audio = js_params.Get("saveAudio").As<Napi::Boolean>();
|
|
if (js_params.Has("useGpu")) params.use_gpu = js_params.Get("useGpu").As<Napi::Boolean>();
|
|
if (js_params.Has("flashAttn")) params.flash_attn = js_params.Get("flashAttn").As<Napi::Boolean>();
|
|
|
|
if (this->whisperStream_) {
|
|
delete this->whisperStream_;
|
|
}
|
|
|
|
try {
|
|
this->whisperStream_ = new WhisperStream(params);
|
|
this->whisperStream_->init();
|
|
} catch (const std::runtime_error& e) {
|
|
Napi::Error::New(env, e.what()).ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
return env.Undefined();
|
|
}
|
|
|
|
Napi::Value WhisperStreamWrapper::ProcessChunk(const Napi::CallbackInfo& info) {
|
|
Napi::Env env = info.Env();
|
|
|
|
if (!this->whisperStream_) {
|
|
Napi::Error::New(env, "Model not started. Call startModel() first.").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
if (info.Length() < 1 || !info[0].IsTypedArray() || info[0].As<Napi::TypedArray>().TypedArrayType() != napi_float32_array) {
|
|
Napi::TypeError::New(env, "Argument must be a Float32Array").ThrowAsJavaScriptException();
|
|
return env.Null();
|
|
}
|
|
|
|
Napi::Float32Array pcmf32_array = info[0].As<Napi::Float32Array>();
|
|
std::vector<float> pcmf32_new(pcmf32_array.Data(), pcmf32_array.Data() + pcmf32_array.ElementLength());
|
|
|
|
TranscriptionResult result = this->whisperStream_->process(pcmf32_new);
|
|
|
|
Napi::Object resultObj = Napi::Object::New(env);
|
|
resultObj.Set("text", Napi::String::New(env, result.text));
|
|
resultObj.Set("isFinal", Napi::Boolean::New(env, result.final));
|
|
|
|
return resultObj;
|
|
}
|
|
|
|
Napi::Value WhisperStreamWrapper::freeModel(const Napi::CallbackInfo& info) {
|
|
Napi::Env env = info.Env();
|
|
if (this->whisperStream_) {
|
|
delete this->whisperStream_;
|
|
this->whisperStream_ = nullptr;
|
|
}
|
|
return env.Undefined();
|
|
}
|
|
|
|
Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
|
|
return WhisperStreamWrapper::Init(env, exports);
|
|
}
|
|
|
|
NODE_API_MODULE(whisper, InitAll) |