mirror of
https://github.com/ggml-org/whisper.cpp.git
synced 2026-08-12 22:29:04 +04:00
ruby : add VAD speech segments API (#3931)
* Bump version * Add test for VAD segments API * Implement VAD segments API * Add signatures of VAD segments API * Stop to dependent on mutex_m * Check segment range for vad_segment_t0/t1 * Remove needless check * Remove dead codes * Fix index check for VAD segments * Remove needless line * Use NUM2DBL instead of RFLOAT_VALUE for safety * Fix document * Check type of samples * Arrange CMake cache notation for older version * Remove unnecessary line break * Check return value of whisper_vad_segments_from_samples * Fix type of VAD::Segment#start_time and #end_time * Prevent dangling pointer from Params to VAD::Params * Fix typos * Update README * Make test follow fix of impl * Use segments_from_samples_body in ruby_whisper_vad_detect * Run segments_from_samples without GVL * Remove unnecessary codes * [skip ci]Remove trailing whitespace Co-authored-by: Daniel Bevenius <daniel.bevenius@gmail.com> --------- Co-authored-by: Daniel Bevenius <daniel.bevenius@gmail.com>
This commit is contained in:
co-authored by
Daniel Bevenius
parent
080bbbe852
commit
97c56f1dc1
@@ -117,7 +117,7 @@ Whisper::Params.new(
|
||||
threshold: 1.0, # defaults to 0.5
|
||||
min_speech_duration_ms: 500, # defaults to 250
|
||||
min_silence_duration_ms: 200, # defaults to 100
|
||||
max_speech_duration_s: 30000, # default is FLT_MAX,
|
||||
max_speech_duration_s: 30000.0, # default is FLT_MAX,
|
||||
speech_pad_ms: 50, # defaults to 30
|
||||
samples_overlap: 0.5 # defaults to 0.1
|
||||
),
|
||||
@@ -176,7 +176,7 @@ See whisper.cpp's [README](https://github.com/ggml-org/whisper.cpp/blob/master/R
|
||||
Boolean options:
|
||||
|
||||
* `-DGGML_BLAS=1` -> `--enable-ggml-blas`
|
||||
* `-DWHISER_COREML=OFF` -> `--disable-whisper-coreml`
|
||||
* `-DWHISPER_COREML=OFF` -> `--disable-whisper-coreml`
|
||||
|
||||
Argument options:
|
||||
|
||||
@@ -184,7 +184,7 @@ Argument options:
|
||||
|
||||
Combination:
|
||||
|
||||
* `-DGGML_CUDA=1 -DCMAKE_CUDA_ARCHITECTURES="86"` -> `--enable-ggml-cuda --cmake_cuda-architectures="86"`
|
||||
* `-DGGML_CUDA=1 -DCMAKE_CUDA_ARCHITECTURES="86"` -> `--enable-ggml-cuda --cmake-cuda-architectures="86"`
|
||||
|
||||
For boolean options like `GGML_CUDA`, the README says `-DGGML_CUDA=1`. You need strip `-D`, prepend `--enable-` for `1` or `ON` (`--disable-` for `0` or `OFF`) and make it kebab-case: `--enable-ggml-cuda`.
|
||||
For options which require arguments like `CMAKE_CUDA_ARCHITECTURES`, the README says `-DCMAKE_CUDA_ARCHITECTURES="86"`. You need strip `-D`, prepend `--`, make it kebab-case, append `=` and append argument: `--cmake-cuda-architectures="86"`.
|
||||
@@ -478,13 +478,14 @@ Development
|
||||
% cd whisper.cpp/bindings/ruby
|
||||
% rake test
|
||||
|
||||
First call of `rake test` builds an extension and downloads a model for testing. After that, you add tests in `tests` directory and modify `ext/ruby_whisper.cpp`.
|
||||
First call of `rake test` builds an extension and downloads a model for testing. After that, you add tests in `test` directory and modify `ext/*.{h,c,cpp}`.
|
||||
|
||||
If something seems wrong on build, running `rake clean` solves some cases.
|
||||
|
||||
### Need help ###
|
||||
|
||||
* Windows support
|
||||
* Check of compilation with various hardware
|
||||
* Refinement of C/C++ code, especially memory management
|
||||
|
||||
License
|
||||
|
||||
@@ -105,7 +105,7 @@ class Options
|
||||
FileUtils.mkpath File.dirname(cache_path)
|
||||
File.open cache_path, "w" do |file|
|
||||
@options.reject {|name, (type, value)| value.nil?}.each do |name, (type, value)|
|
||||
line = "set(CACHE{%<name>s} TYPE %<type>s FORCE VALUE %<value>s)" % {
|
||||
line = 'set(%<name>s %<value>s CACHE %<type>s "" FORCE)' %{
|
||||
name:,
|
||||
type:,
|
||||
value: value == true ? "ON" : value == false ? "OFF" : escape_cmake(value)
|
||||
|
||||
@@ -133,7 +133,6 @@ void Init_whisper() {
|
||||
id_coreml_compiled_models = rb_intern("coreml_compiled_models");
|
||||
id_cache = rb_intern("cache");
|
||||
id_n_processors = rb_intern("n_processors");
|
||||
id_extended = rb_intern("extended");
|
||||
id_start_log_callback_thread = rb_intern("start_log_callback_thread");
|
||||
id_log_callback_thread = rb_intern("@log_callback_thread");
|
||||
id_alive_p = rb_intern("alive?");
|
||||
|
||||
@@ -110,6 +110,13 @@ typedef struct {
|
||||
int n_samples;
|
||||
} ruby_whisper_full_args;
|
||||
|
||||
typedef struct segments_from_samples_args {
|
||||
VALUE *context;
|
||||
VALUE *params;
|
||||
float *samples;
|
||||
int n_samples;
|
||||
} segments_from_samples_args;
|
||||
|
||||
typedef struct ruby_whisper_full_parallel_args {
|
||||
VALUE *context;
|
||||
VALUE *params;
|
||||
@@ -149,7 +156,6 @@ typedef struct {
|
||||
VALUE context;
|
||||
} ruby_whisper_parakeet_model;
|
||||
|
||||
extern ID id_extended;
|
||||
extern ID id_log_callback_thread;
|
||||
extern ID id_start_log_callback_thread;
|
||||
extern ID id_alive_p;
|
||||
|
||||
@@ -349,7 +349,11 @@ fill_samples(VALUE rb_args)
|
||||
|
||||
if (RB_TYPE_P(*args->src, T_ARRAY)) {
|
||||
for (int i = 0; i < args->n_samples; i++) {
|
||||
args->dest[i] = RFLOAT_VALUE(rb_ary_entry(*args->src, i));
|
||||
VALUE sample = rb_ary_entry(*args->src, i);
|
||||
if (!RB_FLOAT_TYPE_P(sample)) {
|
||||
sample = rb_to_float(sample);
|
||||
}
|
||||
args->dest[i] = RFLOAT_VALUE(sample);
|
||||
}
|
||||
} else {
|
||||
// TODO: use rb_block_call
|
||||
@@ -357,6 +361,9 @@ fill_samples(VALUE rb_args)
|
||||
for (int i = 0; i < args->n_samples; i++) {
|
||||
// TODO: check if iter is exhausted and raise ArgumentError appropriately
|
||||
VALUE sample = rb_funcall(iter, id_next, 0);
|
||||
if (!RB_FLOAT_TYPE_P(sample)) {
|
||||
sample = rb_to_float(sample);
|
||||
}
|
||||
args->dest[i] = RFLOAT_VALUE(sample);
|
||||
}
|
||||
}
|
||||
@@ -745,6 +752,45 @@ ruby_whisper_full_get_segment_no_speech_prob(VALUE self, VALUE i_segment)
|
||||
return DBL2NUM(no_speech_prob);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
ruby_whisper_full_n_vad_segments(VALUE self)
|
||||
{
|
||||
ruby_whisper *rw;
|
||||
GetContext(self, rw);
|
||||
|
||||
return INT2NUM(whisper_full_n_vad_segments(rw->context));
|
||||
}
|
||||
|
||||
static int
|
||||
ruby_whisper_full_check_vad_segment_index(const ruby_whisper *rw, const VALUE i_segment)
|
||||
{
|
||||
const int c_i_segment = NUM2INT(i_segment);
|
||||
if (c_i_segment < 0 || c_i_segment >= whisper_full_n_vad_segments(rw->context)) {
|
||||
rb_raise(rb_eIndexError, "segment index %d out of range", c_i_segment);
|
||||
}
|
||||
return c_i_segment;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
ruby_whisper_full_get_vad_segment_t0(VALUE self, VALUE i_segment)
|
||||
{
|
||||
ruby_whisper *rw;
|
||||
GetContext(self, rw);
|
||||
const int c_i_segment = ruby_whisper_full_check_vad_segment_index(rw, i_segment);
|
||||
|
||||
return LONG2NUM(whisper_full_get_vad_segment_t0(rw->context, c_i_segment));
|
||||
}
|
||||
|
||||
static VALUE
|
||||
ruby_whisper_full_get_vad_segment_t1(VALUE self, VALUE i_segment)
|
||||
{
|
||||
ruby_whisper *rw;
|
||||
GetContext(self, rw);
|
||||
const int c_i_segment = ruby_whisper_full_check_vad_segment_index(rw, i_segment);
|
||||
|
||||
return LONG2NUM(whisper_full_get_vad_segment_t1(rw->context, c_i_segment));
|
||||
}
|
||||
|
||||
// High level API
|
||||
|
||||
static VALUE
|
||||
@@ -830,6 +876,9 @@ init_ruby_whisper_context(VALUE *mWhisper)
|
||||
rb_define_method(cContext, "full_get_segment_speaker_turn_next", ruby_whisper_full_get_segment_speaker_turn_next, 1);
|
||||
rb_define_method(cContext, "full_get_segment_text", ruby_whisper_full_get_segment_text, 1);
|
||||
rb_define_method(cContext, "full_get_segment_no_speech_prob", ruby_whisper_full_get_segment_no_speech_prob, 1);
|
||||
rb_define_method(cContext, "full_n_vad_segments", ruby_whisper_full_n_vad_segments, 0);
|
||||
rb_define_method(cContext, "full_get_vad_segment_t0", ruby_whisper_full_get_vad_segment_t0, 1);
|
||||
rb_define_method(cContext, "full_get_vad_segment_t1", ruby_whisper_full_get_vad_segment_t1, 1);
|
||||
rb_define_method(cContext, "full", ruby_whisper_full, -1);
|
||||
rb_define_method(cContext, "full_parallel", ruby_whisper_full_parallel, -1);
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
rb_define_singleton_method(mod, "drain_logs", ruby_whisper_##log_queue##_s_drain_logs, 0); \
|
||||
rb_define_singleton_method(mod, "log_set", ruby_whisper_##log_queue##_s_log_set, 2); \
|
||||
rb_set_end_proc(ruby_whisper_##log_queue##_end_proc, Qnil); \
|
||||
rb_extend_object(mod, mLogSettable); \
|
||||
rb_funcall(mLogSettable, id_extended, 1, mod);
|
||||
rb_extend_object(mod, mLogSettable);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -446,7 +446,7 @@ ruby_whisper_params_allocate(VALUE klass)
|
||||
rwp->params.vad_model_path = ruby_strdup(rwp->params.vad_model_path);
|
||||
}
|
||||
rwp->diarize = false;
|
||||
rwp->vad_params = TypedData_Wrap_Struct(cVADParams, &ruby_whisper_vad_params_type, (void *)&rwp->params.vad_params);
|
||||
rwp->vad_params = rb_class_new_instance(0, NULL, cVADParams);
|
||||
rwp->new_segment_callback_container = ruby_whisper_callback_container_allocate();
|
||||
rwp->progress_callback_container = ruby_whisper_callback_container_allocate();
|
||||
rwp->encoder_begin_callback_container = ruby_whisper_callback_container_allocate();
|
||||
@@ -918,7 +918,7 @@ ruby_whisper_params_set_temperature(VALUE self, VALUE value)
|
||||
{
|
||||
ruby_whisper_params *rwp;
|
||||
TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp);
|
||||
rwp->params.temperature = RFLOAT_VALUE(value);
|
||||
rwp->params.temperature = NUM2DBL(value);
|
||||
return value;
|
||||
}
|
||||
/*
|
||||
@@ -943,7 +943,7 @@ ruby_whisper_params_set_max_initial_ts(VALUE self, VALUE value)
|
||||
{
|
||||
ruby_whisper_params *rwp;
|
||||
TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp);
|
||||
rwp->params.max_initial_ts = RFLOAT_VALUE(value);
|
||||
rwp->params.max_initial_ts = NUM2DBL(value);
|
||||
return value;
|
||||
}
|
||||
/*
|
||||
@@ -966,7 +966,7 @@ ruby_whisper_params_set_length_penalty(VALUE self, VALUE value)
|
||||
{
|
||||
ruby_whisper_params *rwp;
|
||||
TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp);
|
||||
rwp->params.length_penalty = RFLOAT_VALUE(value);
|
||||
rwp->params.length_penalty = NUM2DBL(value);
|
||||
return value;
|
||||
}
|
||||
/*
|
||||
@@ -989,7 +989,7 @@ ruby_whisper_params_set_temperature_inc(VALUE self, VALUE value)
|
||||
{
|
||||
ruby_whisper_params *rwp;
|
||||
TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp);
|
||||
rwp->params.temperature_inc = RFLOAT_VALUE(value);
|
||||
rwp->params.temperature_inc = NUM2DBL(value);
|
||||
return value;
|
||||
}
|
||||
/*
|
||||
@@ -1014,7 +1014,7 @@ ruby_whisper_params_set_entropy_thold(VALUE self, VALUE value)
|
||||
{
|
||||
ruby_whisper_params *rwp;
|
||||
TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp);
|
||||
rwp->params.entropy_thold = RFLOAT_VALUE(value);
|
||||
rwp->params.entropy_thold = NUM2DBL(value);
|
||||
return value;
|
||||
}
|
||||
/*
|
||||
@@ -1037,7 +1037,7 @@ ruby_whisper_params_set_logprob_thold(VALUE self, VALUE value)
|
||||
{
|
||||
ruby_whisper_params *rwp;
|
||||
TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp);
|
||||
rwp->params.logprob_thold = RFLOAT_VALUE(value);
|
||||
rwp->params.logprob_thold = NUM2DBL(value);
|
||||
return value;
|
||||
}
|
||||
/*
|
||||
@@ -1060,7 +1060,7 @@ ruby_whisper_params_set_no_speech_thold(VALUE self, VALUE value)
|
||||
{
|
||||
ruby_whisper_params *rwp;
|
||||
TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp);
|
||||
rwp->params.no_speech_thold = RFLOAT_VALUE(value);
|
||||
rwp->params.no_speech_thold = NUM2DBL(value);
|
||||
return value;
|
||||
}
|
||||
static VALUE
|
||||
@@ -1307,6 +1307,7 @@ static VALUE
|
||||
ruby_whisper_params_set_vad_params(VALUE self, VALUE value)
|
||||
{
|
||||
ruby_whisper_params *rwp;
|
||||
rb_check_typeddata(value, &ruby_whisper_vad_params_type);
|
||||
TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp);
|
||||
rwp->vad_params = value;
|
||||
return value;
|
||||
|
||||
@@ -15,28 +15,9 @@ extern ID id_call;
|
||||
extern ID id_to_path;
|
||||
extern ID transcribe_option_names[1];
|
||||
|
||||
extern void prepare_transcription(ruby_whisper_params * rwp, VALUE * self, int n_processors);
|
||||
extern VALUE full_body(VALUE rb_args);
|
||||
extern VALUE full_parallel_body(VALUE rb_args);
|
||||
|
||||
typedef struct{
|
||||
struct whisper_context *context;
|
||||
struct whisper_full_params *params;
|
||||
float *samples;
|
||||
size_t n_samples;
|
||||
int n_processors;
|
||||
int result;
|
||||
} transcribe_without_gvl_args;
|
||||
|
||||
static void*
|
||||
transcribe_without_gvl(void *rb_args)
|
||||
{
|
||||
transcribe_without_gvl_args *args = (transcribe_without_gvl_args *)rb_args;
|
||||
args->result = whisper_full_parallel(args->context, *args->params, args->samples, args->n_samples, args->n_processors);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* transcribe a single file
|
||||
* can emit to a block results
|
||||
|
||||
@@ -12,13 +12,6 @@ extern VALUE release_samples(VALUE parsed);
|
||||
|
||||
extern VALUE ruby_whisper_vad_segments_s_init(struct whisper_vad_segments *segments);
|
||||
|
||||
typedef struct segments_from_samples_args {
|
||||
VALUE *context;
|
||||
VALUE *params;
|
||||
float *samples;
|
||||
int n_samples;
|
||||
} segments_from_samples_args;
|
||||
|
||||
static size_t
|
||||
ruby_whisper_vad_context_memsize(const void *p)
|
||||
{
|
||||
@@ -77,7 +70,24 @@ ruby_whisper_vad_context_initialize(VALUE self, VALUE model_path)
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
typedef struct {
|
||||
struct whisper_vad_context *context;
|
||||
struct whisper_vad_params *params;
|
||||
float *samples;
|
||||
int n_samples;
|
||||
struct whisper_vad_segments *result;
|
||||
} segments_from_samples_without_gvl_args;
|
||||
|
||||
static void*
|
||||
segments_from_samples_without_gvl(void *rb_args)
|
||||
{
|
||||
segments_from_samples_without_gvl_args *args = (segments_from_samples_without_gvl_args *)rb_args;
|
||||
args->result = whisper_vad_segments_from_samples(args->context, *args->params, args->samples, args->n_samples);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VALUE
|
||||
segments_from_samples_body(VALUE rb_args)
|
||||
{
|
||||
segments_from_samples_args *args = (segments_from_samples_args *)rb_args;
|
||||
@@ -87,9 +97,19 @@ segments_from_samples_body(VALUE rb_args)
|
||||
GetVADContext(*args->context, rwvc);
|
||||
GetVADParams(*args->params, rwvp);
|
||||
|
||||
struct whisper_vad_segments *segments = whisper_vad_segments_from_samples(rwvc->context, rwvp->params, args->samples, args->n_samples);
|
||||
segments_from_samples_without_gvl_args segments_from_samples_without_gvl_args = {
|
||||
rwvc->context,
|
||||
&rwvp->params,
|
||||
args->samples,
|
||||
args->n_samples,
|
||||
NULL
|
||||
};
|
||||
rb_thread_call_without_gvl(segments_from_samples_without_gvl, (void *)&segments_from_samples_without_gvl_args, NULL, NULL);
|
||||
if (!segments_from_samples_without_gvl_args.result) {
|
||||
rb_raise(rb_eRuntimeError, "Failed to process audio");
|
||||
}
|
||||
|
||||
return ruby_whisper_vad_segments_s_init(segments);
|
||||
return ruby_whisper_vad_segments_s_init(segments_from_samples_without_gvl_args.result);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
||||
@@ -16,18 +16,16 @@ extern const rb_data_type_t ruby_whisper_vad_params_type;
|
||||
extern const rb_data_type_t ruby_whisper_vad_segments_type;
|
||||
|
||||
extern VALUE ruby_whisper_vad_segments_s_init(struct whisper_vad_segments *segments);
|
||||
extern VALUE segments_from_samples_body(VALUE rb_args);
|
||||
|
||||
VALUE
|
||||
ruby_whisper_vad_detect(VALUE self, VALUE file_path, VALUE params) {
|
||||
ruby_whisper_vad_context *rwvc;
|
||||
ruby_whisper_vad_params *rwvp;
|
||||
std::string cpp_file_path;
|
||||
std::vector<float> pcmf32;
|
||||
std::vector<std::vector<float>> pcmf32s;
|
||||
whisper_vad_segments *segments;
|
||||
|
||||
GetVADContext(self, rwvc);
|
||||
TypedData_Get_Struct(params, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp);
|
||||
|
||||
if (rb_respond_to(file_path, id_to_path)) {
|
||||
file_path = rb_funcall(file_path, id_to_path, 0);
|
||||
@@ -38,12 +36,13 @@ ruby_whisper_vad_detect(VALUE self, VALUE file_path, VALUE params) {
|
||||
rb_raise(rb_eRuntimeError, "Failed to open '%s' as WAV file\n", cpp_file_path.c_str());
|
||||
}
|
||||
|
||||
segments = whisper_vad_segments_from_samples(rwvc->context, rwvp->params, pcmf32.data(), pcmf32.size());
|
||||
if (segments == nullptr) {
|
||||
rb_raise(rb_eRuntimeError, "Failed to process audio\n");
|
||||
}
|
||||
|
||||
return ruby_whisper_vad_segments_s_init(segments);
|
||||
segments_from_samples_args args = {
|
||||
&self,
|
||||
¶ms,
|
||||
pcmf32.data(),
|
||||
(int)pcmf32.size(),
|
||||
};
|
||||
return segments_from_samples_body((VALUE)&args);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -31,7 +31,7 @@ static ID id_samples_overlap;
|
||||
|
||||
const rb_data_type_t ruby_whisper_vad_params_type = {
|
||||
"ruby_whisper_vad_params",
|
||||
{0, 0, ruby_whisper_vad_params_memsize,},
|
||||
{0, RUBY_DEFAULT_FREE, ruby_whisper_vad_params_memsize,},
|
||||
0, 0,
|
||||
0
|
||||
};
|
||||
@@ -56,7 +56,7 @@ ruby_whisper_vad_params_set_threshold(VALUE self, VALUE value)
|
||||
{
|
||||
ruby_whisper_vad_params *rwvp;
|
||||
TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp);
|
||||
rwvp->params.threshold = RFLOAT_VALUE(value);
|
||||
rwvp->params.threshold = NUM2DBL(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ ruby_whisper_vad_params_set_max_speech_duration_s(VALUE self, VALUE value)
|
||||
{
|
||||
ruby_whisper_vad_params *rwvp;
|
||||
TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp);
|
||||
rwvp->params.max_speech_duration_s = RFLOAT_VALUE(value);
|
||||
rwvp->params.max_speech_duration_s = NUM2DBL(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ ruby_whisper_vad_params_set_samples_overlap(VALUE self, VALUE value)
|
||||
{
|
||||
ruby_whisper_vad_params *rwvp;
|
||||
TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp);
|
||||
rwvp->params.samples_overlap = RFLOAT_VALUE(value);
|
||||
rwvp->params.samples_overlap = NUM2DBL(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ ruby_whisper_vad_segment_get_start_time(VALUE self)
|
||||
TypedData_Get_Struct(self, ruby_whisper_vad_segment, &ruby_whisper_vad_segment_type, rwvs);
|
||||
TypedData_Get_Struct(rwvs->segments, ruby_whisper_vad_segments, &ruby_whisper_vad_segments_type, rwvss);
|
||||
t0 = whisper_vad_segments_get_segment_t0(rwvss->segments, rwvs->index);
|
||||
return DBL2NUM(t0 * 10);
|
||||
return LONG2NUM(t0 * 10);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
@@ -81,7 +81,7 @@ ruby_whisper_vad_segment_get_end_time(VALUE self)
|
||||
TypedData_Get_Struct(self, ruby_whisper_vad_segment, &ruby_whisper_vad_segment_type, rwvs);
|
||||
TypedData_Get_Struct(rwvs->segments, ruby_whisper_vad_segments, &ruby_whisper_vad_segments_type, rwvss);
|
||||
t1 = whisper_vad_segments_get_segment_t1(rwvss->segments, rwvs->index);
|
||||
return DBL2NUM(t1 * 10);
|
||||
return LONG2NUM(t1 * 10);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
require "mutex_m"
|
||||
|
||||
module Whisper
|
||||
module LogSettable
|
||||
class << self
|
||||
def extended(base)
|
||||
base.extend Mutex_m
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def start_log_callback_thread
|
||||
@@ -32,5 +24,10 @@ module Whisper
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def synchronize(&block)
|
||||
@mutex ||= Thread::Mutex.new
|
||||
@mutex.synchronize &block
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -44,7 +44,6 @@ module Whisper
|
||||
return path if cache_path.exist?
|
||||
|
||||
headers = {}
|
||||
headers["if-modified-since"] = path.mtime.httpdate if path.exist?
|
||||
request @uri, headers
|
||||
path
|
||||
end
|
||||
@@ -54,17 +53,11 @@ module Whisper
|
||||
request = Net::HTTP::Get.new(uri, headers)
|
||||
http.request request do |response|
|
||||
case response
|
||||
when Net::HTTPNotModified
|
||||
# noop
|
||||
when Net::HTTPOK
|
||||
return if !response.key?("last-modified") && cache_path.exist?
|
||||
|
||||
download response
|
||||
when Net::HTTPRedirection
|
||||
request URI(response["location"]), headers
|
||||
else
|
||||
return if headers.key?("if-modified-since") # Use cache file
|
||||
|
||||
raise "#{response.code} #{response.message}\n#{response.body}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -131,6 +131,12 @@ module Whisper
|
||||
|
||||
def full_get_segment_no_speech_prob: (Integer) -> Float
|
||||
|
||||
def full_n_vad_segments: () -> Integer
|
||||
|
||||
def full_get_vad_segment_t0: (Integer) -> Integer
|
||||
|
||||
def full_get_vad_segment_t1: (Integer) -> Integer
|
||||
|
||||
# Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text
|
||||
# Not thread safe for same context
|
||||
# Uses the specified decoding strategy to obtain the text.
|
||||
|
||||
@@ -16,4 +16,13 @@ class TestVAD < TestBase
|
||||
assert_match(/ask not what your country can do for you[,.] ask what you can do for your country/i, text)
|
||||
end
|
||||
end
|
||||
|
||||
def test_vad_segments_api
|
||||
@whisper.transcribe TestBase::AUDIO, @params
|
||||
|
||||
assert_equal 4, @whisper.full_n_vad_segments
|
||||
@whisper.full_n_vad_segments.times do |i|
|
||||
assert @whisper.full_get_vad_segment_t0(i) < @whisper.full_get_vad_segment_t1(i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -44,8 +44,8 @@ class TestVADContext < TestBase
|
||||
assert_instance_of Enumerator, segments.each
|
||||
|
||||
segment = segments.each.first
|
||||
assert_instance_of Float, segment.start_time
|
||||
assert_instance_of Float, segment.end_time
|
||||
assert_instance_of Integer, segment.start_time
|
||||
assert_instance_of Integer, segment.end_time
|
||||
|
||||
segment => {start_time:, end_time:}
|
||||
assert_equal segment.start_time, start_time
|
||||
|
||||
@@ -3,7 +3,7 @@ require_relative "extsources"
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "whispercpp"
|
||||
s.authors = ["Georgi Gerganov", "Todd A. Fisher"]
|
||||
s.version = '1.3.7'
|
||||
s.version = '1.3.8'
|
||||
s.description = %q{High-performance inference of OpenAI's Whisper automatic speech recognition (ASR) model via Ruby}
|
||||
s.email = 'todd.fisher@gmail.com'
|
||||
s.extra_rdoc_files = ['LICENSE', 'README.md']
|
||||
|
||||
Reference in New Issue
Block a user