The IAudioClient implementation from both Windows and winepulse.drv never sets the event more than once per period, which is usually around 10 ms long. Some codecs produce audio samples shorter than 10 ms, which currently result into the SAR not being able to keep up with the audio client, because the SAR never requests more than a sample per event cycle.
This patch causes the event to be set each time the available buffer space is less then half full, and each time a sample is received, so that short samples do not lead to audio client underruns.
Of course it would be even better if the codecs didn't generate too short samples, because they uselessly require more CPU cycle, but we should handle them properly anyway.
This patch fixes audio stuttering problems in the logo videos of Borderlands 3, Deep Rock Galactic and probably other games.
Signed-off-by: Giovanni Mascellani gmascellani@codeweavers.com --- dlls/mf/sar.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/dlls/mf/sar.c b/dlls/mf/sar.c index eba822ae0fe..1916cbd50c9 100644 --- a/dlls/mf/sar.c +++ b/dlls/mf/sar.c @@ -1360,6 +1360,7 @@ static HRESULT WINAPI audio_renderer_stream_ProcessSample(IMFStreamSink *iface, if (renderer->state == STREAM_STATE_RUNNING) hr = stream_queue_sample(renderer, sample); renderer->flags &= ~SAR_SAMPLE_REQUESTED; + SetEvent(renderer->buffer_ready_event); LeaveCriticalSection(&renderer->cs);
return hr; @@ -1757,6 +1758,7 @@ static void audio_renderer_render(struct audio_renderer *renderer, IMFAsyncResul IMFMediaBuffer *buffer; BYTE *dst, *src; HRESULT hr; + BOOL retry_immediately = FALSE;
LIST_FOR_EACH_ENTRY_SAFE(obj, obj2, &renderer->queue, struct queued_object, entry) { @@ -1780,6 +1782,7 @@ static void audio_renderer_render(struct audio_renderer *renderer, IMFAsyncResul max_frames -= pad_frames; src_frames -= obj->u.sample.frame_offset; dst_frames = min(src_frames, max_frames); + retry_immediately = max_frames - dst_frames > dst_frames;
if (SUCCEEDED(hr = IAudioRenderClient_GetBuffer(renderer->audio_render_client, dst_frames, &dst))) { @@ -1814,6 +1817,8 @@ static void audio_renderer_render(struct audio_renderer *renderer, IMFAsyncResul renderer->flags |= SAR_SAMPLE_REQUESTED; }
+ if (retry_immediately) + SetEvent(renderer->buffer_ready_event); if (FAILED(hr = MFPutWaitingWorkItem(renderer->buffer_ready_event, 0, result, &renderer->buffer_ready_key))) WARN("Failed to submit wait item, hr %#x.\n", hr); }