radio: add activation controls, live VAD meters and signal diagnostics
This commit is contained in:
1 parent
fddbba217d
commit
7f3247bfcc
23 files changed
+818
-182
No files matched your search
@@ -26,6 +26,7 @@ static std::vector<char> events;
|
||||
static std::vector<std::vector<uint8_t>> packets;
|
||||
static float probability;
|
||||
static int vad_windows;
|
||||
static int vad_result;
|
||||
static int radio_compressor_enabled;
|
||||
static int auto_hangover_ms;
|
||||
static bool observe_vad;
|
||||
@@ -40,7 +41,7 @@ extern "C" void __wrap_silero_vad_reset(silero_vad_t*) {}
|
||||
extern "C" int __wrap_silero_vad_process(silero_vad_t*, const float* samples, float* result) {
|
||||
if (stall_vad) { vad_stalled = true; while (stall_vad) std::this_thread::sleep_for(std::chrono::milliseconds(1)); }
|
||||
if (observe_vad) vad_input.insert(vad_input.end(), samples, samples + SILERO_VAD_WINDOW_SAMPLES);
|
||||
++vad_windows; *result = probability; return 0;
|
||||
++vad_windows; *result = probability; return vad_result;
|
||||
}
|
||||
extern "C" int __wrap_radio_talk_begin(UTUN_INSTANCE*, uint64_t) { events.push_back('B'); return 0; }
|
||||
extern "C" int __wrap_radio_talk_end(UTUN_INSTANCE*, uint64_t) { events.push_back('F'); return 0; }
|
||||
@@ -97,6 +98,8 @@ static void check_vad_pcm(const int16_t* pcm, int frames, int channels, int max_
|
||||
expected.resize(expected.size() / 512 * 512);
|
||||
REQUIRE(vad_input == expected);
|
||||
REQUIRE(audio_compressor_gain_db(ac) <= max_gain + 0.001f);
|
||||
auto status = radio_audio_get_input_status();
|
||||
REQUIRE(status.vad_compressor_enabled && std::abs(status.vad_gain_db - audio_compressor_gain_db(ac)) < 0.0001f);
|
||||
audio_compressor_destroy(ac);
|
||||
}
|
||||
|
||||
@@ -275,6 +278,7 @@ static void test_vad_diagnostics(UTUN_INSTANCE* instance) {
|
||||
REQUIRE(!radio_audio_transmitting()); uasync_poll(instance->ua, 0);
|
||||
REQUIRE(!packets.empty());
|
||||
radio_audio_on_frame(instance, 7, 0x1234, 1, 0, 0, packets.front().data(), packets.front().size(), nullptr);
|
||||
REQUIRE(radio_audio_get_input_status().blocked && !radio_audio_get_input_status().vad_valid);
|
||||
probability = 0.9f;
|
||||
for (int i = 0; i < 2; ++i) REQUIRE(radio_audio_feed_pcm(7, pcm.data(), pcm.size()) == 0);
|
||||
REQUIRE(!radio_audio_transmitting());
|
||||
@@ -289,6 +293,16 @@ static void test_vad_diagnostics(UTUN_INSTANCE* instance) {
|
||||
for (const char* text : {"VAD config", "reason=confirming", "reason=confirm_reset", "reason=confirmed",
|
||||
"reason=silence_timeout", "reason=rx_busy", "rx_node=0000000000001234", "manual=1",
|
||||
"BEGIN dispatched", "capture discontinuity", "VAD capture stop", "model_rms="}) REQUIRE(contains(text));
|
||||
// Клиппинг должен быть явно виден в диагностике ветки VAD, отдельно от компрессора передачи.
|
||||
radio_compressor_enabled = 1; probability = 0; test_time = 100000;
|
||||
REQUIRE(radio_audio_start(7, 0) == 0 && radio_audio_capture_start(7, 1, RADIO_AUTO_VAD) == 0);
|
||||
pcm.fill(0);
|
||||
for (int i = 0; i < 2; ++i) REQUIRE(radio_audio_feed_pcm(7, pcm.data(), pcm.size()) == 0);
|
||||
REQUIRE(contains("dc=0.000000 zeros=512/512"));
|
||||
test_time += 10001; pcm.fill(30000);
|
||||
for (int i = 0; i < 2; ++i) REQUIRE(radio_audio_feed_pcm(7, pcm.data(), pcm.size()) == 0);
|
||||
REQUIRE(contains("VAD compressor clipping") && contains("input_peak="));
|
||||
radio_audio_stop(); uasync_poll(instance->ua, 0); test_time = 0; radio_compressor_enabled = 0;
|
||||
debug_set_log_hook(nullptr);
|
||||
debug_set_category_level(DEBUG_CATEGORY_VAD, DEBUG_LEVEL_NONE);
|
||||
debug_set_category_level(DEBUG_CATEGORY_RADIO, DEBUG_LEVEL_NONE);
|
||||
@@ -432,6 +446,36 @@ static void test_level_detector(UTUN_INSTANCE* instance, int channels, int compr
|
||||
channels, compressor);
|
||||
}
|
||||
|
||||
/* UI получает именно результат Silero; устаревшее значение не выглядит свежей тишиной. */
|
||||
static void test_vad_input_status(UTUN_INSTANCE* instance) {
|
||||
radio_compressor_enabled = 0; test_time = 100000;
|
||||
REQUIRE(radio_audio_start(7, 0) == 0 && radio_audio_capture_start(7, 1, RADIO_AUTO_VAD) == 0);
|
||||
REQUIRE(!radio_audio_get_input_status().vad_valid);
|
||||
std::array<int16_t, 1536> pcm{};
|
||||
auto feed = [&] { REQUIRE(radio_audio_feed_pcm(7, pcm.data(), pcm.size()) == 0); };
|
||||
probability = 0.25f; feed();
|
||||
auto status = radio_audio_get_input_status();
|
||||
REQUIRE(status.valid && status.vad_valid && status.vad_probability == probability);
|
||||
REQUIRE(!status.vad_compressor_enabled && status.vad_gain_db == 0);
|
||||
test_time += 2000; REQUIRE(radio_audio_get_input_status().vad_valid);
|
||||
test_time += 1; REQUIRE(!radio_audio_get_input_status().vad_valid);
|
||||
probability = 0; feed();
|
||||
status = radio_audio_get_input_status(); REQUIRE(status.vad_valid && status.vad_probability == 0);
|
||||
radio_audio_capture_discontinuity(7); REQUIRE(!radio_audio_get_input_status().vad_valid);
|
||||
probability = 0.4f; feed(); REQUIRE(radio_audio_get_input_status().vad_valid);
|
||||
vad_result = -1; feed(); REQUIRE(!radio_audio_get_input_status().vad_valid);
|
||||
vad_result = 0; feed();
|
||||
status = radio_audio_get_input_status(); REQUIRE(status.vad_valid && status.vad_probability == probability);
|
||||
radio_audio_capture_stop(); REQUIRE(!radio_audio_get_input_status().vad_valid);
|
||||
for (int mode : {RADIO_AUTO_OFF, RADIO_AUTO_LEVEL, RADIO_AUTO_VAD}) {
|
||||
REQUIRE(radio_audio_capture_start(7, 1, mode) == 0 && !radio_audio_get_input_status().vad_valid);
|
||||
feed(); REQUIRE(!!radio_audio_get_input_status().vad_valid == (mode == RADIO_AUTO_VAD));
|
||||
radio_audio_capture_stop();
|
||||
}
|
||||
radio_audio_stop(); uasync_poll(instance->ua, 0); test_time = 0;
|
||||
puts("voice TX: VAD probability snapshot, zero, freshness, inference failure, discontinuity, stop and mode switch PASS");
|
||||
}
|
||||
|
||||
int main() {
|
||||
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
|
||||
UTUN_INSTANCE instance{}; instance.ua = uasync_create(); REQUIRE(instance.ua);
|
||||
@@ -513,6 +557,7 @@ int main() {
|
||||
}
|
||||
test_prefetch_deferred(&instance);
|
||||
test_vad_diagnostics(&instance);
|
||||
test_vad_input_status(&instance);
|
||||
test_vad_receive_guard(&instance);
|
||||
test_vad_receive_guard(&instance, RADIO_AUTO_LEVEL);
|
||||
for (int channels : {1, 2}) for (int gain : {6, 30}) test_vad_compressor(&instance, channels, gain);
|
||||
|
||||
Reference in new issue
Block a user