#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(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(); StreamParams params; if (js_params.Has("modelPath")) { params.model = js_params.Get("modelPath").As(); } 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(); if (js_params.Has("nThreads")) params.n_threads = js_params.Get("nThreads").As(); if (js_params.Has("stepMs")) params.step_ms = js_params.Get("stepMs").As(); if (js_params.Has("lengthMs")) params.length_ms = js_params.Get("lengthMs").As(); if (js_params.Has("keepMs")) params.keep_ms = js_params.Get("keepMs").As(); if (js_params.Has("maxTokens")) params.max_tokens = js_params.Get("maxTokens").As(); if (js_params.Has("audioCtx")) params.audio_ctx = js_params.Get("audioCtx").As(); if (js_params.Has("vadThold")) params.vad_thold = js_params.Get("vadThold").As(); if (js_params.Has("beamSize")) params.beam_size = js_params.Get("beamSize").As(); if (js_params.Has("freqThold")) params.freq_thold = js_params.Get("freqThold").As(); if (js_params.Has("translate")) params.translate = js_params.Get("translate").As(); if (js_params.Has("noFallback")) params.no_fallback = js_params.Get("noFallback").As(); if (js_params.Has("printSpecial")) params.print_special = js_params.Get("printSpecial").As(); if (js_params.Has("noContext")) params.no_context = js_params.Get("noContext").As(); if (js_params.Has("noTimestamps")) params.no_timestamps = js_params.Get("noTimestamps").As(); if (js_params.Has("tinydiarize")) params.tinydiarize = js_params.Get("tinydiarize").As(); if (js_params.Has("saveAudio")) params.save_audio = js_params.Get("saveAudio").As(); if (js_params.Has("useGpu")) params.use_gpu = js_params.Get("useGpu").As(); if (js_params.Has("flashAttn")) params.flash_attn = js_params.Get("flashAttn").As(); 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().TypedArrayType() != napi_float32_array) { Napi::TypeError::New(env, "Argument must be a Float32Array").ThrowAsJavaScriptException(); return env.Null(); } Napi::Float32Array pcmf32_array = info[0].As(); std::vector 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)