Browse Source

Keep adaptive requests above baseline and test backend fault events

master
evgeny 1 day ago
parent
commit
b463fc3309
  1. 4
      tools/chatgui/src/audio_buffer_tuning.h
  2. 2
      tools/chatgui/src/audio_device.cpp
  3. 21
      tools/chatgui/tests/test_audio_timing.c

4
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;

2
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;
}

21
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");
}

Loading…
Cancel
Save