Reduce radio VAD prefetch to 600 milliseconds
This commit is contained in:
1 parent
45eb36a8cd
commit
cfe73b7d28
4 files changed
+18
-17
No files matched your search
+1
-1
@@ -184,7 +184,7 @@
|
||||
512, фильтр-гистерезис + машина состояний (`src/radio/radio_vad.h`, чистый,
|
||||
юнит-тестируемый): старт при подтверждении 2 окон (~64мс) выше порога
|
||||
`radio_vad_threshold`, стоп при тишине `radio_vad_hangover_ms` (200мс) или чужом
|
||||
разговоре (busy = RX-кадр <200мс). Перед BEGIN сохраняется до 1с непереданного PCM;
|
||||
разговоре (busy = RX-кадр <200мс). Перед BEGIN сохраняется до 600мс непереданного PCM;
|
||||
prefetch отправляется одной задачей перед live без повторов и задержки на стороне отправителя.
|
||||
VAD использует отдельный компрессор с собственной историей усиления. Desktop `radio_audio_engine.cpp` и Android
|
||||
`RadioAudioEngine.kt` фидят PCM постоянно в VAD-режиме; JNI `radioVadMode()`.
|
||||
|
||||
@@ -49,7 +49,8 @@
|
||||
/* ── VAD авто-PTT (отдельный режим, ручной PTT не меняется) ── */
|
||||
#define RADIO_VAD_CONFIRM_WINDOWS 2 /* окон (32мс) выше порога для старта (~64мс) */
|
||||
#define RADIO_VAD_BUSY_TB 2000 /* 200мс: канал занят после последнего RX-кадра */
|
||||
#define RADIO_PREFETCH_FRAMES RADIO_AUDIO_SAMPLE_RATE /* 1с PCM на канал, только до начала передачи */
|
||||
#define RADIO_PREFETCH_MS 600 /* PCM только до начала передачи */
|
||||
#define RADIO_PREFETCH_FRAMES (RADIO_AUDIO_SAMPLE_RATE * RADIO_PREFETCH_MS / 1000)
|
||||
#define RADIO_PREFETCH_PACKETS (RADIO_PREFETCH_FRAMES / RADIO_AUDIO_FRAME_SAMPLES)
|
||||
#define RADIO_TX_PENDING_LIMIT 8
|
||||
|
||||
@@ -141,7 +142,7 @@ struct radio_audio_tx {
|
||||
struct UTUN_INSTANCE* inst;
|
||||
uint64_t group_id, generation, listen_generation, instance_generation, burst, created_tb;
|
||||
int frame_count;
|
||||
struct radio_audio_packet frames[]; /* один live-кадр либо до 50 кадров prefetch */
|
||||
struct radio_audio_packet frames[]; /* один live-кадр либо до 30 кадров prefetch */
|
||||
};
|
||||
|
||||
static void radio_audio_send(void* arg) {
|
||||
@@ -509,7 +510,7 @@ int radio_audio_capture_start(uint64_t group_id, int capture_channels, int vad_e
|
||||
"vad=%d threshold=%.2f hangover=%dms prefetch=%dms",
|
||||
RADIO_AUDIO_ID, (unsigned long long)group_id, capture_channels, opus_mode ? "AUDIO" : "VOIP", opus_bitrate_kbps,
|
||||
ac ? (audio_compressor_is_enabled(ac) ? "on" : "off") : "unavailable", g_vad_mode, g_vad_threshold,
|
||||
g_vad_hangover_ms, g_vad_mode ? 1000 : 0);
|
||||
g_vad_hangover_ms, g_vad_mode ? RADIO_PREFETCH_MS : 0);
|
||||
radio_tx_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ void radio_audio_talk_end(uint64_t group_id);
|
||||
/* Единственный TX-writer: очищенные interleaved отсчёты произвольной длины (кратно числу каналов).
|
||||
* VAD использует свою копию PCM и отдельный AGC с общими настройками TX, кроме radio_vad_compressor_max_gain_db.
|
||||
* Включённый AGC VAD накапливает 20мс; выключенный пропускает PCM сразу. TX не использует обработанную VAD-копию.
|
||||
* При включённом VAD до BEGIN хранится до 1с непереданного PCM и отправляется перед live.
|
||||
* При включённом VAD до BEGIN хранится до 600мс непереданного PCM и отправляется перед live.
|
||||
* Один автомат manual/VAD; хвост каждой серии дополняется тишиной до FIN. */
|
||||
int radio_audio_feed_pcm(uint64_t group_id, const int16_t* pcm, int count);
|
||||
|
||||
|
||||
@@ -122,11 +122,11 @@ static void test_vad_compressor(UTUN_INSTANCE* instance, int channels, int max_g
|
||||
check_vad_pcm(pcm.data(), idle + live, channels, max_gain);
|
||||
REQUIRE(*std::max_element(vad_input.begin(), vad_input.end()) > 240.0f / 32768.0f * 1.5f);
|
||||
uasync_poll(instance->ua, 0);
|
||||
REQUIRE(packets.size() == 54 && events.front() == 'B' && events.back() == 'F');
|
||||
REQUIRE(packets.size() == 34 && events.front() == 'B' && events.back() == 'F');
|
||||
auto* encoder = opus_codec_encoder_create(48000, channels); REQUIRE(encoder);
|
||||
opus_codec_encoder_bitrate_set(encoder, 32000);
|
||||
auto* tx = reference_compressor(channels, 6);
|
||||
for (int i = 0; i < 54; ++i) check_packet(encoder, pcm.data() + (idle - 48000 + i * 960) * channels, i, tx, channels);
|
||||
for (int i = 0; i < 34; ++i) check_packet(encoder, pcm.data() + (idle - 28800 + i * 960) * channels, i, tx, channels);
|
||||
audio_compressor_destroy(tx); opus_codec_encoder_destroy(encoder);
|
||||
// Разрыв и новый захват должны убрать и неполный блок 20мс, и историю усиления.
|
||||
feed_chunks(pcm.data(), 800);
|
||||
@@ -143,7 +143,7 @@ static void test_vad_compressor(UTUN_INSTANCE* instance, int channels, int max_g
|
||||
debug_set_category_level(DEBUG_CATEGORY_RADIO, DEBUG_LEVEL_NONE);
|
||||
}
|
||||
|
||||
/* Полная секунда кольца, граница VAD внутри кадра и live должны совпасть с непрерывным Opus-потоком. */
|
||||
/* Полные 600мс кольца, граница VAD внутри кадра и live должны совпасть с непрерывным Opus-потоком. */
|
||||
static void test_prefetch(UTUN_INSTANCE* instance, int channels, int compressor_enabled = 0) {
|
||||
debug_set_category_level(DEBUG_CATEGORY_RADIO, DEBUG_LEVEL_INFO);
|
||||
packets.clear(); events.clear(); probability = 0; radio_compressor_enabled = compressor_enabled;
|
||||
@@ -163,14 +163,14 @@ static void test_prefetch(UTUN_INSTANCE* instance, int channels, int compressor_
|
||||
REQUIRE(radio_audio_feed_pcm(7, pcm.data() + (trigger + 1) * channels, live * channels) == 0);
|
||||
int pending = 0;
|
||||
for (auto* task = instance->ua->posted_tasks_head; task; task = task->next) ++pending;
|
||||
REQUIRE(pending == 3); // BEGIN + одна задача на 50 кадров prefetch + один live-кадр.
|
||||
REQUIRE(pending == 3); // BEGIN + одна задача на 30 кадров prefetch + один live-кадр.
|
||||
radio_audio_capture_stop(); uasync_poll(instance->ua, 0);
|
||||
REQUIRE(packets.size() == 51 && events.size() == 53 && events.front() == 'B' && events.back() == 'F');
|
||||
REQUIRE(packets.size() == 31 && events.size() == 33 && events.front() == 'B' && events.back() == 'F');
|
||||
auto* encoder = opus_codec_encoder_create(48000, channels); REQUIRE(encoder);
|
||||
opus_codec_encoder_bitrate_set(encoder, 32000);
|
||||
auto* tx = compressor_enabled ? reference_compressor(channels, 15) : nullptr;
|
||||
const int first = trigger - 48000;
|
||||
for (int i = 0; i < 51; ++i) check_packet(encoder, pcm.data() + (first + i * 960) * channels, i, tx, channels);
|
||||
const int first = trigger - 28800;
|
||||
for (int i = 0; i < 31; ++i) check_packet(encoder, pcm.data() + (first + i * 960) * channels, i, tx, channels);
|
||||
audio_compressor_destroy(tx);
|
||||
opus_codec_encoder_destroy(encoder);
|
||||
radio_compressor_enabled = 0;
|
||||
@@ -208,7 +208,7 @@ static void test_prefetch(UTUN_INSTANCE* instance, int channels, int compressor_
|
||||
check_packet(encoder, tail.data(), 0);
|
||||
opus_codec_encoder_destroy(encoder);
|
||||
radio_audio_stop(); uasync_poll(instance->ua, 0);
|
||||
printf("voice TX: prefetch 1000ms, exact ring/live order, short history, discontinuity, no replay ch=%d PASS\n", channels);
|
||||
printf("voice TX: prefetch 600ms, exact ring/live order, short history, discontinuity, no replay ch=%d PASS\n", channels);
|
||||
debug_set_category_level(DEBUG_CATEGORY_RADIO, DEBUG_LEVEL_NONE);
|
||||
}
|
||||
|
||||
@@ -233,12 +233,12 @@ static void test_prefetch_deferred(UTUN_INSTANCE* instance) {
|
||||
packets.clear(); events.clear();
|
||||
REQUIRE(radio_audio_feed_pcm(7, pcm.data() + idle + confirm, 1) == 0 && radio_audio_transmitting());
|
||||
radio_audio_capture_stop(); uasync_poll(instance->ua, 0);
|
||||
REQUIRE(packets.size() == 51 && events.front() == 'B' && events.back() == 'F');
|
||||
REQUIRE(packets.size() == 31 && events.front() == 'B' && events.back() == 'F');
|
||||
auto* encoder = opus_codec_encoder_create(48000, 1); REQUIRE(encoder);
|
||||
opus_codec_encoder_bitrate_set(encoder, 32000);
|
||||
const int first = idle + confirm - 48000;
|
||||
for (int i = 0; i < 50; ++i) check_packet(encoder, pcm.data() + first + i * 960, i);
|
||||
std::array<int16_t, 960> tail{}; tail[0] = pcm.back(); check_packet(encoder, tail.data(), 50);
|
||||
const int first = idle + confirm - 28800;
|
||||
for (int i = 0; i < 30; ++i) check_packet(encoder, pcm.data() + first + i * 960, i);
|
||||
std::array<int16_t, 960> tail{}; tail[0] = pcm.back(); check_packet(encoder, tail.data(), 30);
|
||||
opus_codec_encoder_destroy(encoder);
|
||||
radio_audio_stop(); uasync_poll(instance->ua, 0);
|
||||
puts("voice TX: full media queue defers BEGIN and preserves complete prefetch PASS");
|
||||
|
||||
Reference in new issue
Block a user