You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

196 lines
6.4 KiB

/* Тест Silero VAD-обёртки (lib/silero_vad) поверх ONNX Runtime.
*
* Проверяет:
* - загрузку модели и жизненный цикл (create/destroy/reset);
* - обработку ошибок (пустой/несуществующий путь, NULL-аргументы);
* - что на тишине вероятность речи близка к нулю (реальный инференс, не мусор);
* - что путь pcm16 даёт тот же результат, что и float (конвертация /32768);
* - что вероятность всегда лежит в [0;1].
*
* Модель ищется в argv[1], по умолчанию "../lib/silero_vad.onnx" (запуск из tests/).
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "../lib/silero_vad.h"
#include "../lib/debug_config.h"
#define PI 3.14159265358979323846
static int g_failures = 0;
#define CHECK(cond, ...) do { \
if (!(cond)) { \
printf(" FAIL: " __VA_ARGS__); printf("\n"); \
g_failures++; \
} \
} while (0)
/* Детерминированный «гласный»: модулированный гармонический комплекс (f0 ~120 Гц). */
static void gen_vowel(float* out, int n, int offset) {
int i;
for (i = 0; i < n; i++) {
double t = (double)(offset + i) / 16000.0;
double f0 = 120.0 + 20.0 * sin(2.0 * PI * 3.0 * t);
double env = 0.5 + 0.5 * sin(2.0 * PI * 2.0 * t);
double s = sin(2.0 * PI * f0 * t)
+ 0.5 * sin(2.0 * PI * 2.0 * f0 * t)
+ 0.3 * sin(2.0 * PI * 3.0 * f0 * t)
+ 0.2 * sin(2.0 * PI * 4.0 * f0 * t);
out[i] = (float)(s * env * 0.25);
}
}
static int test_lifecycle_and_errors(const char* model_path) {
silero_vad_t* vad;
printf("[silero_vad] lifecycle + error paths\n");
vad = silero_vad_create(NULL);
CHECK(vad == NULL, "create(NULL) should fail");
if (vad) silero_vad_destroy(vad);
vad = silero_vad_create("");
CHECK(vad == NULL, "create(\"\") should fail");
vad = silero_vad_create("/nonexistent/silero_vad.onnx");
CHECK(vad == NULL, "create(bad path) should fail");
vad = silero_vad_create(model_path);
CHECK(vad != NULL, "create(%s) should succeed", model_path);
if (!vad) return 1;
silero_vad_reset(vad);
silero_vad_reset(vad); /* повторный reset безопасен */
silero_vad_destroy(vad);
silero_vad_destroy(NULL); /* destroy(NULL) безопасен */
return 0;
}
static int test_silence(const char* model_path) {
silero_vad_t* vad;
float zeros[SILERO_VAD_WINDOW_SAMPLES];
float prob = -1.0f;
float max_prob = 0.0f;
int i, rc;
printf("[silero_vad] silence -> prob ~ 0\n");
memset(zeros, 0, sizeof(zeros));
vad = silero_vad_create(model_path);
CHECK(vad != NULL, "create failed");
if (!vad) return 1;
for (i = 0; i < 32; i++) {
rc = silero_vad_process(vad, zeros, &prob);
CHECK(rc == 0, "process(%d) rc=%d", i, rc);
CHECK(prob >= 0.0f && prob <= 1.0f, "prob out of range: %f", prob);
if (prob > max_prob) max_prob = prob;
}
printf(" silence max_prob=%.4f\n", max_prob);
CHECK(max_prob < 0.2f, "silence probability too high: %f", max_prob);
silero_vad_destroy(vad);
return 0;
}
static int test_pcm16_matches_float(const char* model_path) {
silero_vad_t* vad;
int16_t pcm[SILERO_VAD_WINDOW_SAMPLES];
float samples[SILERO_VAD_WINDOW_SAMPLES];
float prob_f = -1.0f, prob_i = -1.0f;
int rc;
printf("[silero_vad] pcm16 path == float path\n");
vad = silero_vad_create(model_path);
CHECK(vad != NULL, "create failed");
if (!vad) return 1;
gen_vowel(samples, SILERO_VAD_WINDOW_SAMPLES, 0);
for (int i = 0; i < SILERO_VAD_WINDOW_SAMPLES; i++) {
pcm[i] = (int16_t)(samples[i] * 32768.0f);
}
rc = silero_vad_process(vad, samples, &prob_f);
CHECK(rc == 0, "process(float) rc=%d", rc);
silero_vad_reset(vad); /* одинаковое состояние перед вторым прогоном */
rc = silero_vad_process_pcm16(vad, pcm, &prob_i);
CHECK(rc == 0, "process_pcm16 rc=%d", rc);
printf(" float prob=%.5f pcm16 prob=%.5f\n", prob_f, prob_i);
CHECK(fabsf(prob_f - prob_i) < 1e-4f, "pcm16 != float: %f vs %f", prob_i, prob_f);
silero_vad_destroy(vad);
return 0;
}
static int test_default_model(void) {
silero_vad_t* vad;
float zeros[SILERO_VAD_WINDOW_SAMPLES];
float prob = -1.0f;
int rc;
printf("[silero_vad] create_default (embedded model)\n");
memset(zeros, 0, sizeof(zeros));
vad = silero_vad_create_default();
CHECK(vad != NULL, "create_default failed");
if (!vad) return 1;
rc = silero_vad_process(vad, zeros, &prob);
CHECK(rc == 0, "process rc=%d", rc);
CHECK(prob >= 0.0f && prob <= 1.0f, "prob out of range: %f", prob);
CHECK(prob < 0.2f, "silence prob too high: %f", prob);
printf(" embedded silence prob=%.4f\n", prob);
silero_vad_destroy(vad);
return 0;
}
static int test_null_args(const char* model_path) {
silero_vad_t* vad;
float samples[SILERO_VAD_WINDOW_SAMPLES];
float prob;
printf("[silero_vad] NULL-arg guards\n");
memset(samples, 0, sizeof(samples));
vad = silero_vad_create(model_path);
CHECK(vad != NULL, "create failed");
if (!vad) return 1;
CHECK(silero_vad_process(NULL, samples, &prob) != 0, "process(NULL vad) should fail");
CHECK(silero_vad_process(vad, NULL, &prob) != 0, "process(NULL samples) should fail");
CHECK(silero_vad_process(vad, samples, NULL) != 0, "process(NULL prob) should fail");
CHECK(silero_vad_process_pcm16(vad, NULL, &prob) != 0, "process_pcm16(NULL) should fail");
silero_vad_destroy(vad);
return 0;
}
int main(int argc, char** argv) {
const char* model_path = (argc > 1) ? argv[1] : "../lib/silero_vad.onnx";
debug_config_init();
debug_set_level(DEBUG_LEVEL_INFO);
debug_set_category_level(DEBUG_CATEGORY_VAD, DEBUG_LEVEL_INFO);
printf("Silero VAD test, model: %s\n", model_path);
test_lifecycle_and_errors(model_path);
test_silence(model_path);
test_pcm16_matches_float(model_path);
test_null_args(model_path);
test_default_model();
if (g_failures == 0) {
printf("TEST PASSED\n");
return 0;
}
printf("TEST FAILED (%d failures)\n", g_failures);
return 1;
}