diff --git a/lib/silero_vad.c b/lib/silero_vad.c index 1a906100..b7c1e180 100644 --- a/lib/silero_vad.c +++ b/lib/silero_vad.c @@ -13,6 +13,9 @@ /* Число float-элементов рекуррентного состояния модели: 2 * 1 * 128 */ #define SILERO_VAD_STATE_COUNT 256 +/* ONNX-вход: 64 отсчёта предыдущего окна + 512 новых отсчётов @16кГц. */ +#define SILERO_VAD_CONTEXT_SAMPLES 64 +#define SILERO_VAD_INPUT_SAMPLES (SILERO_VAD_CONTEXT_SAMPLES + SILERO_VAD_WINDOW_SAMPLES) struct silero_vad { const OrtApi* ort; @@ -20,6 +23,7 @@ struct silero_vad { OrtSession* session; OrtMemoryInfo* mem_info; float state[SILERO_VAD_STATE_COUNT]; /* рекуррентное состояние между вызовами */ + float context[SILERO_VAD_CONTEXT_SAMPLES]; /* звуковой контекст предыдущего окна */ uint8_t* model_data; /* байты .onnx, загруженные в память (для file-версии) */ size_t model_len; }; @@ -124,7 +128,8 @@ static silero_vad_t* silero_vad_create_from_bytes(const uint8_t* data, size_t le ort->ReleaseSessionOptions(opts); - DEBUG_INFO(DEBUG_CATEGORY_VAD, "silero_vad_create(%s): ok bytes=%zu", label, len); + DEBUG_INFO(DEBUG_CATEGORY_VAD, "silero_vad_create(%s): ok bytes=%zu window=%d context=%d input=%d", label, len, + SILERO_VAD_WINDOW_SAMPLES, SILERO_VAD_CONTEXT_SAMPLES, SILERO_VAD_INPUT_SAMPLES); return vad; fail: @@ -168,7 +173,8 @@ void silero_vad_destroy(silero_vad_t* vad) { void silero_vad_reset(silero_vad_t* vad) { if (!vad) return; memset(vad->state, 0, sizeof(vad->state)); - DEBUG_DEBUG(DEBUG_CATEGORY_VAD, "silero_vad_reset"); + memset(vad->context, 0, sizeof(vad->context)); + DEBUG_DEBUG(DEBUG_CATEGORY_VAD, "silero_vad_reset: recurrent state and audio context cleared"); } int silero_vad_process(silero_vad_t* vad, const float* samples, float* prob) { @@ -183,12 +189,13 @@ int silero_vad_process(silero_vad_t* vad, const float* samples, float* prob) { const char* output_names[2]; const OrtValue* inputs[3]; OrtValue* outputs[2]; - int64_t shape_input[2] = {1, SILERO_VAD_WINDOW_SAMPLES}; + int64_t shape_input[2] = {1, SILERO_VAD_INPUT_SAMPLES}; int64_t shape_state[3] = {2, 1, 128}; int64_t shape_prob[2] = {1, 1}; int64_t sr_value = SILERO_VAD_SAMPLE_RATE; float prob_local = 0.0f; float next_state[SILERO_VAD_STATE_COUNT]; + float input[SILERO_VAD_INPUT_SAMPLES]; int rc = -1; if (!vad || !samples || !prob) { @@ -196,9 +203,11 @@ int silero_vad_process(silero_vad_t* vad, const float* samples, float* prob) { return -1; } ort = vad->ort; + memcpy(input, vad->context, sizeof(vad->context)); + memcpy(input + SILERO_VAD_CONTEXT_SAMPLES, samples, SILERO_VAD_WINDOW_SAMPLES * sizeof(float)); - status = ort->CreateTensorWithDataAsOrtValue(vad->mem_info, (void*)samples, - SILERO_VAD_WINDOW_SAMPLES * sizeof(float), shape_input, 2, + status = ort->CreateTensorWithDataAsOrtValue(vad->mem_info, input, + sizeof(input), shape_input, 2, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT, &in_input); if (status) { vad_report_ort_error(ort, status, "input tensor"); return -1; } @@ -241,6 +250,7 @@ int silero_vad_process(silero_vad_t* vad, const float* samples, float* prob) { } memcpy(vad->state, next_state, sizeof(next_state)); + memcpy(vad->context, input + SILERO_VAD_INPUT_SAMPLES - SILERO_VAD_CONTEXT_SAMPLES, sizeof(vad->context)); *prob = prob_local; rc = 0; diff --git a/lib/silero_vad.h b/lib/silero_vad.h index 4bffba79..1e374d48 100644 --- a/lib/silero_vad.h +++ b/lib/silero_vad.h @@ -4,7 +4,8 @@ * Запускает официальную стриминговую модель silero_vad.onnx (v5, ~2.3 МБ), * которая по окну аудио возвращает вероятность наличия речи [0..1]. * Рекуррентное состояние (GRU) хранится внутри объекта и переносится между - * вызовами, поэтому детектор работает в потоковом (real-time) режиме. + * вызовами. Обёртка также хранит 64 последних отсчёта аудио и добавляет их + * перед новым окном: ONNX получает 576 отсчётов, публичный API принимает 512. * * Требования к входному аудио: * - частота дискретизации 16 кГц (модель поддерживает только 16k); @@ -54,7 +55,7 @@ silero_vad_t* silero_vad_create_default(void); /* Освободить детектор. NULL безопасен. */ void silero_vad_destroy(silero_vad_t* vad); -/* Сбросить рекуррентное состояние (начало нового аудиопотока / после длительной тишины). */ +/* Сбросить рекуррентное состояние и звуковой контекст (начало нового потока / разрыв захвата). */ void silero_vad_reset(silero_vad_t* vad); /**