diff --git a/tools/chatgui/src/audio_buffer_tuning.h b/tools/chatgui/src/audio_buffer_tuning.h index 71c63554..2a3ce5f2 100644 --- a/tools/chatgui/src/audio_buffer_tuning.h +++ b/tools/chatgui/src/audio_buffer_tuning.h @@ -9,7 +9,7 @@ * Состояние живёт до смены устройства/сессии; begin() сохраняет подобранный размер при reopen. */ struct AudioBufferTuning { enum Result { Unchanged, Increase, Limit }; - unsigned requestedMs = 0, actualMs = 0, periods = 1; + unsigned requestedMs = 0, baseMs = 0, actualMs = 0, periods = 1; int64_t settleUntil = 0, changeAfter = 0, firstFaultAt = -1; unsigned faultPolls = 0; @@ -24,7 +24,7 @@ struct AudioBufferTuning { faultPolls = std::min(faultPolls + 1, 2u); if (faultPolls < 2 || now < changeAfter) return Unchanged; firstFaultAt = -1; faultPolls = 0; changeAfter = now + 5000; - unsigned current = std::max(actualMs, requestedMs), maximum = 100 / periods; + unsigned current = std::max({actualMs, requestedMs, baseMs}), maximum = 100 / periods; if (!current || current >= maximum) return Limit; requestedMs = std::min(current + 5, maximum); return Increase; diff --git a/tools/chatgui/src/audio_device.cpp b/tools/chatgui/src/audio_device.cpp index 96d8a8dc..d124f362 100644 --- a/tools/chatgui/src/audio_device.cpp +++ b/tools/chatgui/src/audio_device.cpp @@ -48,6 +48,8 @@ bool AudioDevice::init(ma_context* context, ma_device_config config, const char* m_bufferKeyValid = true; m_bufferBackend = context->backend; m_bufferType = config.deviceType; m_bufferSession = session; m_bufferSelected = selected != nullptr; if (selected) m_bufferDeviceId = *selected; + m_bufferTuning.baseMs = config.periodSizeInFrames && config.sampleRate + ? unsigned((uint64_t(config.periodSizeInFrames) * 1000 + config.sampleRate - 1) / config.sampleRate) : config.periodSizeInMilliseconds; if (m_bufferTuning.requestedMs) { config.periodSizeInFrames = 0; config.periodSizeInMilliseconds = m_bufferTuning.requestedMs; } diff --git a/tools/chatgui/tests/test_audio_timing.c b/tools/chatgui/tests/test_audio_timing.c index f6adf0e5..0bb05c39 100644 --- a/tools/chatgui/tests/test_audio_timing.c +++ b/tools/chatgui/tests/test_audio_timing.c @@ -6,6 +6,15 @@ static int64_t times[64]; static unsigned counts[64], seen; static int capture_missing; +static unsigned overruns, underruns; +static void notification(const ma_device_notification* event) { + if (event->type == ma_device_notification_type_capture_overrun) overruns++; + if (event->type == ma_device_notification_type_playback_underrun) underruns++; +} +static int fake_hole(const void* stream, const void** data, size_t* bytes) { + (void)stream; *data = NULL; *bytes = 64 * 2 * sizeof(float); return 0; +} +static int fake_drop(void* stream) { (void)stream; return 0; } static int fake_latency(const void* stream, uint64_t* latency, int* negative) { (void)stream; *latency = 100000; *negative = 0; return 0; } @@ -52,6 +61,16 @@ int main(void) { after = time_us(); assert(seen == 1 && times[0] >= now - 100000 + 2500 && times[0] <= after - 100000 + 2500); assert(!audio_stream_capture_missing(&device)); // Hint действует только внутри dispatch. + device.onNotification = notification; + ma_device__set_state(&device, ma_device_state_started); + context.pulse.pa_stream_peek = (ma_proc)fake_hole; context.pulse.pa_stream_drop = (ma_proc)fake_drop; + seen = 0; + ma_device_on_read__pulse(device.pulse.pStreamCapture, 64 * 2 * sizeof(float), &device); + assert(overruns == 1 && underruns == 0 && seen == 4); // Один hole, четыре converter callbacks. + audio_diag_pulse_overflow(device.pulse.pStreamCapture, &device); + assert(overruns == 2 && underruns == 0); + audio_diag_pulse_underflow(NULL, &device); + assert(overruns == 2 && underruns == 1); ma_data_converter_uninit(&device.capture.converter, NULL); - puts("audio timing: capture read index, converter offsets, explicit hole PASS"); + puts("audio timing: capture read index, converter offsets, hole/overrun/underrun notifications PASS"); }