From 481161574ecf7b534577318882ee8ca557c2f62e Mon Sep 17 00:00:00 2001 From: phantom10111 Date: Sun, 7 Apr 2024 20:28:51 +0200 Subject: [PATCH] PulseAudio: Report actual audio playback position PulseAudio driver used to report position of last frame written to audio stream instead of actual playback position. This kind of worked, since on average the write position doesn't diverge too much from actual elaped time, but it can result in note stuttering and in general isn't very accurate. Change the implementation to use actual playback position from PulseAudio stream. Remove some compatibity code with old PulseAudio, since version 0.9.10 was released in 2008. Also, as an optimization to avoid unnecessary memory copying, use pa_stream_begin_write() to mix audio directly into PulseAudio memory. --- src/arch/Sound/RageSoundDriver_PulseAudio.cpp | 47 +++++++++++-------- src/arch/Sound/RageSoundDriver_PulseAudio.h | 4 +- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/arch/Sound/RageSoundDriver_PulseAudio.cpp b/src/arch/Sound/RageSoundDriver_PulseAudio.cpp index 4ebedd0043..32a1b02bae 100644 --- a/src/arch/Sound/RageSoundDriver_PulseAudio.cpp +++ b/src/arch/Sound/RageSoundDriver_PulseAudio.cpp @@ -23,13 +23,13 @@ REGISTER_SOUND_DRIVER_CLASS2( Pulse, PulseAudio ); /* Constructor */ RageSoundDriver_PulseAudio::RageSoundDriver_PulseAudio() : RageSoundDriver(), -m_LastPosition(0), m_SampleRate(0), m_Error(nullptr), +m_LastPosition(0), m_Error(nullptr), m_Sem("Pulseaudio Synchronization Semaphore"), m_PulseMainLoop(nullptr), m_PulseCtx(nullptr), m_PulseStream(nullptr) { - m_SampleRate = PREFSMAN->m_iSoundPreferredSampleRate; - if( m_SampleRate == 0 ) - m_SampleRate = 44100; + m_ss.rate = PREFSMAN->m_iSoundPreferredSampleRate; + if( m_ss.rate == 0 ) + m_ss.rate = 44100; } RageSoundDriver_PulseAudio::~RageSoundDriver_PulseAudio() @@ -231,8 +231,11 @@ void RageSoundDriver_PulseAudio::m_InitStream(void) /* connect the stream for playback */ LOG->Trace("Pulse: pa_stream_connect_playback()..."); + const int flags = PA_STREAM_INTERPOLATE_TIMING + | PA_STREAM_NOT_MONOTONIC + | PA_STREAM_AUTO_TIMING_UPDATE; error = pa_stream_connect_playback(m_PulseStream, nullptr, &attr, - PA_STREAM_AUTO_TIMING_UPDATE, nullptr, nullptr); + static_cast(flags), nullptr, nullptr); if(error < 0) { if(asprintf(&m_Error, "pa_stream_connect_playback(): %s", @@ -244,7 +247,7 @@ void RageSoundDriver_PulseAudio::m_InitStream(void) return; } - m_SampleRate = ss.rate; + m_ss = ss; } void RageSoundDriver_PulseAudio::CtxStateCb(pa_context *c) @@ -305,30 +308,34 @@ void RageSoundDriver_PulseAudio::StreamStateCb(pa_stream *s) std::int64_t RageSoundDriver_PulseAudio::GetPosition() const { - return m_LastPosition; + pa_usec_t usec; + if(pa_stream_get_time(m_PulseStream, &usec) < 0) + { + RageException::Throw("Pulse: pa_stream_get_time()"); + } + + std::size_t length = pa_usec_to_bytes(usec, &m_ss); + return length / (sizeof(std::int16_t) * 2); /* we use 16-bit frames and 2 channels */ } -/* - * XXX: Something here is slow and causes arrows to stutter in gameplay. - * This needs to be looked into (and for some reason the ALSA driver is - * useless on my laptop). - Colby - */ void RageSoundDriver_PulseAudio::StreamWriteCb(pa_stream *s, std::size_t length) { -#if PA_API_VERSION <= 11 - /* We have to multiply the requested length by 2 on 0.9.10 - * maybe the requested length is given in frames instead of bytes */ - length *= 2; -#endif + void* buf; + if(pa_stream_begin_write(m_PulseStream, &buf, &length) < 0) + { + RageException::Throw("Pulse: pa_stream_begin_write()"); + } + const std::size_t nbframes = length / sizeof(std::int16_t); /* we use 16-bit frames */ - std::vector buf(nbframes); std::int64_t pos1 = m_LastPosition; std::int64_t pos2 = pos1 + nbframes/2; /* Mix() position in stereo frames */ - this->Mix( buf.data(), pos2-pos1, pos1, pos2); - if(pa_stream_write(m_PulseStream, buf.data(), length, nullptr, 0, PA_SEEK_RELATIVE) < 0) + this->Mix( reinterpret_cast(buf), pos2-pos1, pos1, pos2); + + if(pa_stream_write(m_PulseStream, buf, length, nullptr, 0, PA_SEEK_RELATIVE) < 0) { RageException::Throw("Pulse: pa_stream_write()"); } + m_LastPosition = pos2; } diff --git a/src/arch/Sound/RageSoundDriver_PulseAudio.h b/src/arch/Sound/RageSoundDriver_PulseAudio.h index af7952db98..c58258ead9 100644 --- a/src/arch/Sound/RageSoundDriver_PulseAudio.h +++ b/src/arch/Sound/RageSoundDriver_PulseAudio.h @@ -19,11 +19,11 @@ public: RString Init(); inline std::int64_t GetPosition() const; - inline int GetSampleRate() const { return m_SampleRate; }; + inline int GetSampleRate() const { return m_ss.rate; }; protected: std::int64_t m_LastPosition; - int m_SampleRate; + pa_sample_spec m_ss; char *m_Error; void m_InitStream();