@@ -13,7 +13,8 @@ namespace Libraries::AudioOut {
13
13
14
14
class SDLPortBackend : public PortBackend {
15
15
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) {
17
18
// We want the latency for delivering frames out to be as small as possible,
18
19
// so set the sample frames hint to the number of frames per buffer.
19
20
const auto samples_num_str = std::to_string (port.buffer_frames );
@@ -32,6 +33,7 @@ class SDLPortBackend : public PortBackend {
32
33
LOG_ERROR (Lib_AudioOut, " Failed to create SDL audio stream: {}" , SDL_GetError ());
33
34
return ;
34
35
}
36
+ queue_threshold = CalculateQueueThreshold ();
35
37
if (!SDL_ResumeAudioStreamDevice (stream)) {
36
38
LOG_ERROR (Lib_AudioOut, " Failed to resume SDL audio stream: {}" , SDL_GetError ());
37
39
SDL_DestroyAudioStream (stream);
@@ -52,6 +54,17 @@ class SDLPortBackend : public PortBackend {
52
54
if (!stream) {
53
55
return ;
54
56
}
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
+ }
55
68
if (!SDL_PutAudioStreamData (stream, ptr, static_cast <int >(buffer_size))) {
56
69
LOG_ERROR (Lib_AudioOut, " Failed to output to SDL audio stream: {}" , SDL_GetError ());
57
70
}
@@ -70,7 +83,21 @@ class SDLPortBackend : public PortBackend {
70
83
}
71
84
72
85
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;
73
99
u32 buffer_size;
100
+ u32 queue_threshold;
74
101
SDL_AudioStream* stream;
75
102
};
76
103
0 commit comments