From d1fe7520282c212dfdb156fef37555c4ab006787 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Tue, 27 Nov 2018 23:43:31 -0800 Subject: [PATCH] Fix busy looping in libsoundio audio renderer when no audio is playing --- .../audio/renderers/soundioaudiorenderer.cpp | 20 +++++++++++++------ .../audio/renderers/soundioaudiorenderer.h | 2 ++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/streaming/audio/renderers/soundioaudiorenderer.cpp b/app/streaming/audio/renderers/soundioaudiorenderer.cpp index 243445f6..bf3339ee 100644 --- a/app/streaming/audio/renderers/soundioaudiorenderer.cpp +++ b/app/streaming/audio/renderers/soundioaudiorenderer.cpp @@ -4,6 +4,13 @@ #include +// This determines the size of the buffers we'll +// get from CoreAudio. Since GFE sends us packets +// in 5 ms chunks, we'll give them to the OS in +// buffers of the same size. It is also the minimum +// size that we will write when called to fill a buffer. +const double SoundIoAudioRenderer::k_RawSampleLengthSec = 0.005; + SoundIoAudioRenderer::SoundIoAudioRenderer() : m_OpusChannelCount(0), m_SoundIo(nullptr), @@ -152,17 +159,12 @@ bool SoundIoAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATI m_OutputStream->format = SoundIoFormatS16NE; m_OutputStream->sample_rate = opusConfig->sampleRate; + m_OutputStream->software_latency = k_RawSampleLengthSec; m_OutputStream->name = "Moonlight"; m_OutputStream->userdata = this; m_OutputStream->error_callback = sioErrorCallback; m_OutputStream->write_callback = sioWriteCallback; - // This determines the size of the buffers we'll - // get from CoreAudio. Since GFE sends us packets - // in 5 ms chunks, we'll give them to the OS in - // buffers of the same size. - m_OutputStream->software_latency = 0.005; - SoundIoChannelLayout bestLayout = m_Device->current_layout; for (int i = 0; i < m_Device->layout_count; i++) { if (scoreChannelLayout(&bestLayout, opusConfig) < @@ -331,6 +333,12 @@ void SoundIoAudioRenderer::sioWriteCallback(SoundIoOutStream* stream, int frameC // Clamp framesLeft to frameCountMax framesLeft = qMin(framesLeft, frameCountMax); + // Ensure we always write at least a buffer, even if it's silence, to avoid + // busy looping when no audio data is available while libsoundio tries to keep + // us from starving the output device. + frameCountMin = qMax(frameCountMin, (int)(stream->sample_rate * k_RawSampleLengthSec)); + frameCountMin = qMin(frameCountMin, frameCountMax); + // Place an upper-bound on audio stream latency to // avoid accumulating packets in queue-based backends // like WASAPI. This bound was set by testing on several diff --git a/app/streaming/audio/renderers/soundioaudiorenderer.h b/app/streaming/audio/renderers/soundioaudiorenderer.h index 218afb06..d9c8ef99 100644 --- a/app/streaming/audio/renderers/soundioaudiorenderer.h +++ b/app/streaming/audio/renderers/soundioaudiorenderer.h @@ -33,4 +33,6 @@ private: struct SoundIoRingBuffer* m_RingBuffer; struct SoundIoChannelLayout m_EffectiveLayout; bool m_Errored; + + static const double k_RawSampleLengthSec; };