mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 22:06:59 +01:00
audio: keep blocking AudioOut writes on an even cadence (#822)
* audio: keep blocking AudioOut writes on an even cadence The blocking queue check only enforced a minimum cushion, so the guest could queue a burst of buffers and then be blocked for the whole cushion. Measured on PPSA03647, intervals between guest writes alternated between 0 ms and ~44 ms instead of one 5.33 ms buffer period, while the average rate stayed correct at 48000 frames/s. Mixers that run on a timer cannot track that cadence and the music turns slow and warbling. Keep the cushion as-is, but also hold writes to the buffer period, and skip that wait when the stream is running low so it cannot underrun. Intervals become ~5.4 ms with microsecond-level deviation and the menu music plays correctly. * audio: reuse output clock after SDL queue priming --------- Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
+21
-2
@@ -101,6 +101,7 @@ private:
|
|||||||
uint32_t freq = 0;
|
uint32_t freq = 0;
|
||||||
Format format = Format::Unknown;
|
Format format = Format::Unknown;
|
||||||
uint64_t last_output_time = 0;
|
uint64_t last_output_time = 0;
|
||||||
|
bool queue_primed = false;
|
||||||
int channels_num = 0;
|
int channels_num = 0;
|
||||||
int volume[12] = {};
|
int volume[12] = {};
|
||||||
|
|
||||||
@@ -360,20 +361,34 @@ bool Audio::QueueSdlAudio(PortOut* port, const void* data, bool blocking) {
|
|||||||
const auto prepared_size =
|
const auto prepared_size =
|
||||||
BytesPerSample(port->format) * output_channels * port->samples_num;
|
BytesPerSample(port->format) * output_channels * port->samples_num;
|
||||||
|
|
||||||
|
uint32_t min_queued_size = 0;
|
||||||
if (blocking) {
|
if (blocking) {
|
||||||
constexpr uint64_t target_latency_us = 40000;
|
constexpr uint64_t target_latency_us = 40000;
|
||||||
const auto buffer_us = port->freq != 0 ? (1000000ULL * port->samples_num) / port->freq : 0;
|
const auto buffer_us = port->freq != 0 ? (1000000ULL * port->samples_num) / port->freq : 0;
|
||||||
const auto buffers =
|
const auto buffers =
|
||||||
buffer_us != 0 ? static_cast<uint32_t>((target_latency_us + buffer_us - 1) / buffer_us)
|
buffer_us != 0 ? static_cast<uint32_t>((target_latency_us + buffer_us - 1) / buffer_us)
|
||||||
: 2u;
|
: 2u;
|
||||||
const auto min_queued_size = prepared_size * std::clamp(buffers, 2u, 16u);
|
min_queued_size = prepared_size * std::clamp(buffers, 2u, 16u);
|
||||||
const auto wait_start = LibKernel::KernelGetProcessTime();
|
const auto wait_start = LibKernel::KernelGetProcessTime();
|
||||||
while (SDL_GetAudioStreamQueued(port->stream) > static_cast<int>(min_queued_size)) {
|
auto queued = SDL_GetAudioStreamQueued(port->stream);
|
||||||
|
if (queued < static_cast<int>(prepared_size)) {
|
||||||
|
port->queue_primed = false;
|
||||||
|
}
|
||||||
|
while (queued > static_cast<int>(min_queued_size)) {
|
||||||
if (LibKernel::KernelGetProcessTime() - wait_start > 200000) {
|
if (LibKernel::KernelGetProcessTime() - wait_start > 200000) {
|
||||||
SDL_ClearAudioStream(port->stream);
|
SDL_ClearAudioStream(port->stream);
|
||||||
|
port->queue_primed = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Common::Thread::SleepMicro(1000);
|
Common::Thread::SleepMicro(1000);
|
||||||
|
queued = SDL_GetAudioStreamQueued(port->stream);
|
||||||
|
}
|
||||||
|
if (port->queue_primed) {
|
||||||
|
const auto next_time = port->last_output_time + buffer_us;
|
||||||
|
const auto now = LibKernel::KernelGetProcessTime();
|
||||||
|
if (next_time > now) {
|
||||||
|
Common::Thread::SleepMicro(next_time - now);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,6 +396,10 @@ bool Audio::QueueSdlAudio(PortOut* port, const void* data, bool blocking) {
|
|||||||
LOGF("AudioOut: SDL_PutAudioStreamData failed: %s\n", SDL_GetError());
|
LOGF("AudioOut: SDL_PutAudioStreamData failed: %s\n", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (blocking && !port->queue_primed &&
|
||||||
|
SDL_GetAudioStreamQueued(port->stream) >= static_cast<int>(min_queued_size)) {
|
||||||
|
port->queue_primed = true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user