Skip to content

Commit 3ed5149

Browse files
committed
audio: Handle SDL audio queue stalls.
1 parent dc13b4c commit 3ed5149

File tree

1 file changed

+28
-1
lines changed

1 file changed

+28
-1
lines changed

src/core/libraries/audio/sdl_audio.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ namespace Libraries::AudioOut {
1313

1414
class SDLPortBackend : public PortBackend {
1515
public:
16-
explicit SDLPortBackend(const PortOut& port) : buffer_size(port.buffer_size) {
16+
explicit SDLPortBackend(const PortOut& port)
17+
: frame_size(port.frame_size), buffer_size(port.buffer_size) {
1718
// We want the latency for delivering frames out to be as small as possible,
1819
// so set the sample frames hint to the number of frames per buffer.
1920
const auto samples_num_str = std::to_string(port.buffer_frames);
@@ -32,6 +33,7 @@ class SDLPortBackend : public PortBackend {
3233
LOG_ERROR(Lib_AudioOut, "Failed to create SDL audio stream: {}", SDL_GetError());
3334
return;
3435
}
36+
queue_threshold = CalculateQueueThreshold();
3537
if (!SDL_ResumeAudioStreamDevice(stream)) {
3638
LOG_ERROR(Lib_AudioOut, "Failed to resume SDL audio stream: {}", SDL_GetError());
3739
SDL_DestroyAudioStream(stream);
@@ -52,6 +54,17 @@ class SDLPortBackend : public PortBackend {
5254
if (!stream) {
5355
return;
5456
}
57+
// AudioOut library manages timing, but we still need to guard against the SDL
58+
// audio queue stalling, which may happen during device changes, for example.
59+
// Otherwise, latency may grow over time unbounded.
60+
if (const auto queued = SDL_GetAudioStreamQueued(stream); queued >= queue_threshold) {
61+
LOG_WARNING(Lib_AudioOut,
62+
"SDL audio queue backed up ({} queued, {} threshold), clearing.", queued,
63+
queue_threshold);
64+
SDL_ClearAudioStream(stream);
65+
// Recalculate the threshold in case this happened because of a device change.
66+
queue_threshold = CalculateQueueThreshold();
67+
}
5568
if (!SDL_PutAudioStreamData(stream, ptr, static_cast<int>(buffer_size))) {
5669
LOG_ERROR(Lib_AudioOut, "Failed to output to SDL audio stream: {}", SDL_GetError());
5770
}
@@ -70,7 +83,21 @@ class SDLPortBackend : public PortBackend {
7083
}
7184

7285
private:
86+
[[nodiscard]] u32 CalculateQueueThreshold() const {
87+
SDL_AudioSpec discard;
88+
int sdl_buffer_frames;
89+
if (!SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(stream), &discard,
90+
&sdl_buffer_frames)) {
91+
LOG_WARNING(Lib_AudioOut, "Failed to get SDL audio stream buffer size: {}",
92+
SDL_GetError());
93+
sdl_buffer_frames = 0;
94+
}
95+
return std::max<u32>(buffer_size, sdl_buffer_frames * frame_size) * 4;
96+
}
97+
98+
u32 frame_size;
7399
u32 buffer_size;
100+
u32 queue_threshold;
74101
SDL_AudioStream* stream;
75102
};
76103

0 commit comments

Comments
 (0)