mirror of
https://github.com/ggerganov/whisper.cpp.git
synced 2025-04-20 17:28:51 +02:00
examples : support progress_callback API for addon.node (#2941)
* feat: progress supported * fix: missing params * style: Format the code to improve readability Unified code indentation ensures consistent coding style, enhancing code readability and maintainability. * feat: support prompt api --------- Co-authored-by: linxiaodong <calm.lin@wukongsch.com>
This commit is contained in:
parent
f28bf5d186
commit
1279f0d0bc
@ -18,6 +18,7 @@ const whisperParamsMock = {
|
|||||||
translate: true,
|
translate: true,
|
||||||
no_timestamps: false,
|
no_timestamps: false,
|
||||||
audio_ctx: 0,
|
audio_ctx: 0,
|
||||||
|
max_len: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("Run whisper.node", () => {
|
describe("Run whisper.node", () => {
|
||||||
|
@ -128,7 +128,67 @@ void whisper_print_segment_callback(struct whisper_context * ctx, struct whisper
|
|||||||
|
|
||||||
void cb_log_disable(enum ggml_log_level, const char *, void *) {}
|
void cb_log_disable(enum ggml_log_level, const char *, void *) {}
|
||||||
|
|
||||||
int run(whisper_params ¶ms, std::vector<std::vector<std::string>> &result) {
|
class ProgressWorker : public Napi::AsyncWorker {
|
||||||
|
public:
|
||||||
|
ProgressWorker(Napi::Function& callback, whisper_params params, Napi::Function progress_callback, Napi::Env env)
|
||||||
|
: Napi::AsyncWorker(callback), params(params), env(env) {
|
||||||
|
// Create thread-safe function
|
||||||
|
if (!progress_callback.IsEmpty()) {
|
||||||
|
tsfn = Napi::ThreadSafeFunction::New(
|
||||||
|
env,
|
||||||
|
progress_callback,
|
||||||
|
"Progress Callback",
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~ProgressWorker() {
|
||||||
|
if (tsfn) {
|
||||||
|
// Make sure to release the thread-safe function on destruction
|
||||||
|
tsfn.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Execute() override {
|
||||||
|
// Use custom run function with progress callback support
|
||||||
|
run_with_progress(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;
|
||||||
|
}
|
||||||
|
Callback().Call({Env().Null(), res});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress callback function - using thread-safe function
|
||||||
|
void OnProgress(int progress) {
|
||||||
|
if (tsfn) {
|
||||||
|
// Use thread-safe function to call JavaScript callback
|
||||||
|
auto callback = [progress](Napi::Env env, Napi::Function jsCallback) {
|
||||||
|
jsCallback.Call({Napi::Number::New(env, progress)});
|
||||||
|
};
|
||||||
|
|
||||||
|
tsfn.BlockingCall(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
whisper_params params;
|
||||||
|
std::vector<std::vector<std::string>> result;
|
||||||
|
Napi::Env env;
|
||||||
|
Napi::ThreadSafeFunction tsfn;
|
||||||
|
|
||||||
|
// Custom run function with progress callback support
|
||||||
|
int run_with_progress(whisper_params ¶ms, std::vector<std::vector<std::string>> &result) {
|
||||||
if (params.no_prints) {
|
if (params.no_prints) {
|
||||||
whisper_log_set(cb_log_disable, NULL);
|
whisper_log_set(cb_log_disable, NULL);
|
||||||
}
|
}
|
||||||
@ -144,7 +204,6 @@ int run(whisper_params ¶ms, std::vector<std::vector<std::string>> &result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// whisper init
|
// whisper init
|
||||||
|
|
||||||
struct whisper_context_params cparams = whisper_context_default_params();
|
struct whisper_context_params cparams = whisper_context_default_params();
|
||||||
cparams.use_gpu = params.use_gpu;
|
cparams.use_gpu = params.use_gpu;
|
||||||
cparams.flash_attn = params.flash_attn;
|
cparams.flash_attn = params.flash_attn;
|
||||||
@ -155,8 +214,7 @@ int run(whisper_params ¶ms, std::vector<std::vector<std::string>> &result) {
|
|||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if params.pcmf32 is provided, set params.fname_inp to "buffer"
|
// If params.pcmf32 provides, set params.fname_inp as "buffer"
|
||||||
// this is simpler than further modifications in the code
|
|
||||||
if (!params.pcmf32.empty()) {
|
if (!params.pcmf32.empty()) {
|
||||||
fprintf(stderr, "info: using audio buffer as input\n");
|
fprintf(stderr, "info: using audio buffer as input\n");
|
||||||
params.fname_inp.clear();
|
params.fname_inp.clear();
|
||||||
@ -170,7 +228,7 @@ int run(whisper_params ¶ms, std::vector<std::vector<std::string>> &result) {
|
|||||||
std::vector<float> pcmf32; // mono-channel F32 PCM
|
std::vector<float> pcmf32; // mono-channel F32 PCM
|
||||||
std::vector<std::vector<float>> pcmf32s; // stereo-channel F32 PCM
|
std::vector<std::vector<float>> pcmf32s; // stereo-channel F32 PCM
|
||||||
|
|
||||||
// read the input audio file if params.pcmf32 is not provided
|
// If params.pcmf32 is empty, read input audio file
|
||||||
if (params.pcmf32.empty()) {
|
if (params.pcmf32.empty()) {
|
||||||
if (!::read_audio_data(fname_inp, pcmf32, pcmf32s, params.diarize)) {
|
if (!::read_audio_data(fname_inp, pcmf32, pcmf32s, params.diarize)) {
|
||||||
fprintf(stderr, "error: failed to read audio file '%s'\n", fname_inp.c_str());
|
fprintf(stderr, "error: failed to read audio file '%s'\n", fname_inp.c_str());
|
||||||
@ -180,14 +238,14 @@ int run(whisper_params ¶ms, std::vector<std::vector<std::string>> &result) {
|
|||||||
pcmf32 = params.pcmf32;
|
pcmf32 = params.pcmf32;
|
||||||
}
|
}
|
||||||
|
|
||||||
// print system information
|
// Print system info
|
||||||
if (!params.no_prints) {
|
if (!params.no_prints) {
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
fprintf(stderr, "system_info: n_threads = %d / %d | %s\n",
|
fprintf(stderr, "system_info: n_threads = %d / %d | %s\n",
|
||||||
params.n_threads*params.n_processors, std::thread::hardware_concurrency(), whisper_print_system_info());
|
params.n_threads*params.n_processors, std::thread::hardware_concurrency(), whisper_print_system_info());
|
||||||
}
|
}
|
||||||
|
|
||||||
// print some info about the processing
|
// Print processing info
|
||||||
if (!params.no_prints) {
|
if (!params.no_prints) {
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
if (!whisper_is_multilingual(ctx)) {
|
if (!whisper_is_multilingual(ctx)) {
|
||||||
@ -208,7 +266,7 @@ int run(whisper_params ¶ms, std::vector<std::vector<std::string>> &result) {
|
|||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// run the inference
|
// Run inference
|
||||||
{
|
{
|
||||||
whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_GREEDY);
|
whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_GREEDY);
|
||||||
|
|
||||||
@ -241,17 +299,22 @@ int run(whisper_params ¶ms, std::vector<std::vector<std::string>> &result) {
|
|||||||
|
|
||||||
whisper_print_user_data user_data = { ¶ms, &pcmf32s };
|
whisper_print_user_data user_data = { ¶ms, &pcmf32s };
|
||||||
|
|
||||||
// this callback is called on each new segment
|
// This callback is called for each new segment
|
||||||
if (!wparams.print_realtime) {
|
if (!wparams.print_realtime) {
|
||||||
wparams.new_segment_callback = whisper_print_segment_callback;
|
wparams.new_segment_callback = whisper_print_segment_callback;
|
||||||
wparams.new_segment_callback_user_data = &user_data;
|
wparams.new_segment_callback_user_data = &user_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// example for abort mechanism
|
// Set progress callback
|
||||||
// in this example, we do not abort the processing, but we could if the flag is set to true
|
wparams.progress_callback = [](struct whisper_context * /*ctx*/, struct whisper_state * /*state*/, int progress, void * user_data) {
|
||||||
// the callback is called before every encoder run - if it returns false, the processing is aborted
|
ProgressWorker* worker = static_cast<ProgressWorker*>(user_data);
|
||||||
|
worker->OnProgress(progress);
|
||||||
|
};
|
||||||
|
wparams.progress_callback_user_data = this;
|
||||||
|
|
||||||
|
// Abort mechanism example
|
||||||
{
|
{
|
||||||
static bool is_aborted = false; // NOTE: this should be atomic to avoid data race
|
static bool is_aborted = false; // Note: this should be atomic to avoid data races
|
||||||
|
|
||||||
wparams.encoder_begin_callback = [](struct whisper_context * /*ctx*/, struct whisper_state * /*state*/, void * user_data) {
|
wparams.encoder_begin_callback = [](struct whisper_context * /*ctx*/, struct whisper_state * /*state*/, void * user_data) {
|
||||||
bool is_aborted = *(bool*)user_data;
|
bool is_aborted = *(bool*)user_data;
|
||||||
@ -284,36 +347,8 @@ int run(whisper_params ¶ms, std::vector<std::vector<std::string>> &result) {
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
Callback().Call({Env().Null(), res});
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
whisper_params params;
|
|
||||||
std::vector<std::vector<std::string>> result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Napi::Value whisper(const Napi::CallbackInfo& info) {
|
Napi::Value whisper(const Napi::CallbackInfo& info) {
|
||||||
Napi::Env env = info.Env();
|
Napi::Env env = info.Env();
|
||||||
if (info.Length() <= 0 || !info[0].IsObject()) {
|
if (info.Length() <= 0 || !info[0].IsObject()) {
|
||||||
@ -333,6 +368,23 @@ Napi::Value whisper(const Napi::CallbackInfo& info) {
|
|||||||
bool comma_in_time = whisper_params.Get("comma_in_time").As<Napi::Boolean>();
|
bool comma_in_time = whisper_params.Get("comma_in_time").As<Napi::Boolean>();
|
||||||
int32_t max_len = whisper_params.Get("max_len").As<Napi::Number>();
|
int32_t max_len = whisper_params.Get("max_len").As<Napi::Number>();
|
||||||
|
|
||||||
|
// support prompt
|
||||||
|
std::string prompt = "";
|
||||||
|
if (whisper_params.Has("prompt") && whisper_params.Get("prompt").IsString()) {
|
||||||
|
prompt = whisper_params.Get("prompt").As<Napi::String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add support for print_progress
|
||||||
|
bool print_progress = false;
|
||||||
|
if (whisper_params.Has("print_progress")) {
|
||||||
|
print_progress = whisper_params.Get("print_progress").As<Napi::Boolean>();
|
||||||
|
}
|
||||||
|
// Add support for progress_callback
|
||||||
|
Napi::Function progress_callback;
|
||||||
|
if (whisper_params.Has("progress_callback") && whisper_params.Get("progress_callback").IsFunction()) {
|
||||||
|
progress_callback = whisper_params.Get("progress_callback").As<Napi::Function>();
|
||||||
|
}
|
||||||
|
|
||||||
Napi::Value pcmf32Value = whisper_params.Get("pcmf32");
|
Napi::Value pcmf32Value = whisper_params.Get("pcmf32");
|
||||||
std::vector<float> pcmf32_vec;
|
std::vector<float> pcmf32_vec;
|
||||||
if (pcmf32Value.IsTypedArray()) {
|
if (pcmf32Value.IsTypedArray()) {
|
||||||
@ -355,9 +407,12 @@ Napi::Value whisper(const Napi::CallbackInfo& info) {
|
|||||||
params.pcmf32 = pcmf32_vec;
|
params.pcmf32 = pcmf32_vec;
|
||||||
params.comma_in_time = comma_in_time;
|
params.comma_in_time = comma_in_time;
|
||||||
params.max_len = max_len;
|
params.max_len = max_len;
|
||||||
|
params.print_progress = print_progress;
|
||||||
|
params.prompt = prompt;
|
||||||
|
|
||||||
Napi::Function callback = info[1].As<Napi::Function>();
|
Napi::Function callback = info[1].As<Napi::Function>();
|
||||||
Worker* worker = new Worker(callback, params);
|
// Create a new Worker class with progress callback support
|
||||||
|
ProgressWorker* worker = new ProgressWorker(callback, params, progress_callback, env);
|
||||||
worker->Queue();
|
worker->Queue();
|
||||||
return env.Undefined();
|
return env.Undefined();
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,9 @@ const whisperParams = {
|
|||||||
no_timestamps: false,
|
no_timestamps: false,
|
||||||
audio_ctx: 0,
|
audio_ctx: 0,
|
||||||
max_len: 0,
|
max_len: 0,
|
||||||
|
progress_callback: (progress) => {
|
||||||
|
console.log(`progress: ${progress}%`);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const arguments = process.argv.slice(2);
|
const arguments = process.argv.slice(2);
|
||||||
|
Loading…
Reference in New Issue
Block a user