From b3ed8b06e72953330b168e4b121f4e55979016b2 Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 15 Oct 2026 13:59:01 +0200 Subject: [PATCH] Back off PCM reserve probes after starvation and exclude output pauses --- tools/chatgui/src/voice_playback_buffer.h | 10 ++++++---- tools/chatgui/tests/test_voice_audio_io.cpp | 13 +++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/tools/chatgui/src/voice_playback_buffer.h b/tools/chatgui/src/voice_playback_buffer.h index 216be161..a827ee7f 100644 --- a/tools/chatgui/src/voice_playback_buffer.h +++ b/tools/chatgui/src/voice_playback_buffer.h @@ -9,21 +9,23 @@ class VoicePlaybackBuffer { public: unsigned blocks = 2; unsigned callbackFrames = 960; - void reset() { blocks = 2; callbackFrames = 960; stableSince = 0; lastGrowth = 0; } + void reset() { blocks = 2; callbackFrames = 960; stableSince = 0; lastGrowth = 0; lastSample = 0; probeUs = 10000000; } void sample(int64_t nowUs, unsigned frames, bool missing) { callbackFrames = frames; unsigned minimum = std::clamp((frames + 959) / 960, 1u, 4u); - if (!stableSince) stableSince = nowUs; + if (!stableSince || nowUs - lastSample > 1000000) stableSince = nowUs; + lastSample = nowUs; if (minimum > blocks) { blocks = minimum; stableSince = nowUs; } if (missing) { stableSince = nowUs; if (!lastGrowth || nowUs - lastGrowth >= 500000) { blocks = std::min(blocks + 1, 4u); lastGrowth = nowUs; + probeUs = std::min(probeUs * 2, int64_t(300000000)); } - } else if (blocks > minimum && nowUs - stableSince >= 10000000) { + } else if (blocks > minimum && nowUs - stableSince >= probeUs) { --blocks; stableSince = nowUs; } } private: - int64_t stableSince = 0, lastGrowth = 0; + int64_t stableSince = 0, lastGrowth = 0, lastSample = 0, probeUs = 10000000; }; diff --git a/tools/chatgui/tests/test_voice_audio_io.cpp b/tools/chatgui/tests/test_voice_audio_io.cpp index 63945fe6..00273f36 100644 --- a/tools/chatgui/tests/test_voice_audio_io.cpp +++ b/tools/chatgui/tests/test_voice_audio_io.cpp @@ -78,15 +78,20 @@ int main() { // Резерв уменьшается только при стабильной работе; крупный callback и starvation повышают минимум. VoicePlaybackBuffer buffering; buffering.sample(1, 512, false); - buffering.sample(10000000, 512, false); REQUIRE(buffering.blocks == 2); + for (int64_t t = 1000000; t <= 10000000; t += 1000000) buffering.sample(t, 512, false); + REQUIRE(buffering.blocks == 2); buffering.sample(10000001, 512, false); REQUIRE(buffering.blocks == 1); buffering.sample(10020001, 512, true); REQUIRE(buffering.blocks == 2); buffering.sample(10020002, 512, true); REQUIRE(buffering.blocks == 2); buffering.sample(10520001, 512, true); REQUIRE(buffering.blocks == 3); - buffering.sample(20520001, 2048, false); REQUIRE(buffering.blocks == 3); - buffering.sample(30520001, 512, false); REQUIRE(buffering.blocks == 2); - buffering.sample(40520001, 512, false); REQUIRE(buffering.blocks == 1); + for (int64_t t = 11520001; t <= 50520001; t += 1000000) buffering.sample(t, 2048, false); + REQUIRE(buffering.blocks == 3); + buffering.sample(50520002, 512, false); REQUIRE(buffering.blocks == 2); + for (int64_t t = 51520002; t <= 90520002; t += 1000000) buffering.sample(t, 512, false); + REQUIRE(buffering.blocks == 1); buffering.reset(); REQUIRE(buffering.blocks == 2); + buffering.sample(1, 512, false); + buffering.sample(20000001, 512, false); REQUIRE(buffering.blocks == 2); // Пауза не означает стабильный вывод. // Десять минут +/-300ppm дрейфа: связь с физическим временем сохраняется, шага >2мс нет. for (double drift : {-0.0003, 0.0003}) { VoiceCaptureClock clock;