1
+ #include " addon.h" // Your header file for WhisperStreamWrapper
2
+ #include " whisper-stream.h" // Your header file for the WhisperStream class
3
+
4
+ // NOTE: The N-API wrapper handles errors by throwing JS exceptions, so this macro is not needed.
5
+ // #define CHECK_STATUS(env, status, msg) ...
6
+
7
+ // --- Implementation of the Wrapper ---
8
+
9
+ Napi::Object WhisperStreamWrapper::Init (Napi::Env env, Napi::Object exports) {
10
+ Napi::Function func = DefineClass (env, " WhisperStream" , {
11
+ InstanceMethod (" startModel" , &WhisperStreamWrapper::startModel),
12
+ InstanceMethod (" processChunk" , &WhisperStreamWrapper::ProcessChunk),
13
+ InstanceMethod (" freeModel" , &WhisperStreamWrapper::freeModel),
14
+ });
15
+
16
+ exports.Set (" WhisperStream" , func);
17
+ return exports;
18
+ }
19
+
20
+ WhisperStreamWrapper::WhisperStreamWrapper (const Napi::CallbackInfo& info) : Napi::ObjectWrap<WhisperStreamWrapper>(info) {
21
+ }
22
+
23
+ Napi::Value WhisperStreamWrapper::startModel (const Napi::CallbackInfo& info) {
24
+ Napi::Env env = info.Env ();
25
+
26
+ if (info.Length () < 1 || !info[0 ].IsObject ()) {
27
+ Napi::TypeError::New (env, " Expected a configuration object" ).ThrowAsJavaScriptException ();
28
+ return env.Null ();
29
+ }
30
+
31
+ Napi::Object js_params = info[0 ].As <Napi::Object>();
32
+ StreamParams params;
33
+
34
+ if (js_params.Has (" modelPath" )) {
35
+ params.model = js_params.Get (" modelPath" ).As <Napi::String>();
36
+ } else {
37
+ Napi::TypeError::New (env, " Missing required parameter 'model'" ).ThrowAsJavaScriptException ();
38
+ return env.Null ();
39
+ }
40
+
41
+ if (js_params.Has (" language" )) params.language = js_params.Get (" language" ).As <Napi::String>();
42
+ if (js_params.Has (" nThreads" )) params.n_threads = js_params.Get (" nThreads" ).As <Napi::Number>();
43
+ if (js_params.Has (" stepMs" )) params.step_ms = js_params.Get (" stepMs" ).As <Napi::Number>();
44
+ if (js_params.Has (" lengthMs" )) params.length_ms = js_params.Get (" lengthMs" ).As <Napi::Number>();
45
+ if (js_params.Has (" keepMs" )) params.keep_ms = js_params.Get (" keepMs" ).As <Napi::Number>();
46
+ if (js_params.Has (" maxTokens" )) params.max_tokens = js_params.Get (" maxTokens" ).As <Napi::Number>();
47
+ if (js_params.Has (" audioCtx" )) params.audio_ctx = js_params.Get (" audioCtx" ).As <Napi::Number>();
48
+ if (js_params.Has (" vadThold" )) params.vad_thold = js_params.Get (" vadThold" ).As <Napi::Number>();
49
+ if (js_params.Has (" beamSize" )) params.beam_size = js_params.Get (" beamSize" ).As <Napi::Number>();
50
+ if (js_params.Has (" freqThold" )) params.freq_thold = js_params.Get (" freqThold" ).As <Napi::Number>();
51
+ if (js_params.Has (" translate" )) params.translate = js_params.Get (" translate" ).As <Napi::Boolean>();
52
+ if (js_params.Has (" noFallback" )) params.no_fallback = js_params.Get (" noFallback" ).As <Napi::Boolean>();
53
+ if (js_params.Has (" printSpecial" )) params.print_special = js_params.Get (" printSpecial" ).As <Napi::Boolean>();
54
+ if (js_params.Has (" noContext" )) params.no_context = js_params.Get (" noContext" ).As <Napi::Boolean>();
55
+ if (js_params.Has (" noTimestamps" )) params.no_timestamps = js_params.Get (" noTimestamps" ).As <Napi::Boolean>();
56
+ if (js_params.Has (" tinydiarize" )) params.tinydiarize = js_params.Get (" tinydiarize" ).As <Napi::Boolean>();
57
+ if (js_params.Has (" saveAudio" )) params.save_audio = js_params.Get (" saveAudio" ).As <Napi::Boolean>();
58
+ if (js_params.Has (" useGpu" )) params.use_gpu = js_params.Get (" useGpu" ).As <Napi::Boolean>();
59
+ if (js_params.Has (" flashAttn" )) params.flash_attn = js_params.Get (" flashAttn" ).As <Napi::Boolean>();
60
+
61
+ if (this ->whisperStream_ ) {
62
+ delete this ->whisperStream_ ;
63
+ }
64
+
65
+ try {
66
+ this ->whisperStream_ = new WhisperStream (params);
67
+ this ->whisperStream_ ->init ();
68
+ } catch (const std::runtime_error& e) {
69
+ Napi::Error::New (env, e.what ()).ThrowAsJavaScriptException ();
70
+ return env.Null ();
71
+ }
72
+
73
+ return env.Undefined ();
74
+ }
75
+
76
+ Napi::Value WhisperStreamWrapper::ProcessChunk (const Napi::CallbackInfo& info) {
77
+ Napi::Env env = info.Env ();
78
+
79
+ if (!this ->whisperStream_ ) {
80
+ Napi::Error::New (env, " Model not started. Call startModel() first." ).ThrowAsJavaScriptException ();
81
+ return env.Null ();
82
+ }
83
+
84
+ if (info.Length () < 1 || !info[0 ].IsTypedArray () || info[0 ].As <Napi::TypedArray>().TypedArrayType () != napi_float32_array) {
85
+ Napi::TypeError::New (env, " Argument must be a Float32Array" ).ThrowAsJavaScriptException ();
86
+ return env.Null ();
87
+ }
88
+
89
+ Napi::Float32Array pcmf32_array = info[0 ].As <Napi::Float32Array>();
90
+ std::vector<float > pcmf32_new (pcmf32_array.Data (), pcmf32_array.Data () + pcmf32_array.ElementLength ());
91
+
92
+ TranscriptionResult result = this ->whisperStream_ ->process (pcmf32_new);
93
+
94
+ Napi::Object resultObj = Napi::Object::New (env);
95
+ resultObj.Set (" text" , Napi::String::New (env, result.text ));
96
+ resultObj.Set (" isFinal" , Napi::Boolean::New (env, result.final ));
97
+
98
+ return resultObj;
99
+ }
100
+
101
+ Napi::Value WhisperStreamWrapper::freeModel (const Napi::CallbackInfo& info) {
102
+ Napi::Env env = info.Env ();
103
+ if (this ->whisperStream_ ) {
104
+ delete this ->whisperStream_ ;
105
+ this ->whisperStream_ = nullptr ;
106
+ }
107
+ return env.Undefined ();
108
+ }
109
+
110
+ Napi::Object InitAll (Napi::Env env, Napi::Object exports) {
111
+ return WhisperStreamWrapper::Init (env, exports);
112
+ }
113
+
114
+ NODE_API_MODULE (whisper, InitAll)
0 commit comments