From c7ae48c5fb5005efae05f2cabdeee7117f6f72fa Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Wed, 20 Jul 2016 13:09:39 +0200 Subject: PulseAudio: flush stream before loading a new source in a sound effect When loading a new QSoundEffect source, the data in the stream must be flushed to avoid that the old source plays right before the new one. Task-number: QTBUG-48982 Change-Id: Iff14884edb2fb4851f93e67ff8191b77ebb16359 Reviewed-by: Christian Stromme --- src/multimedia/audio/qsoundeffect_pulse_p.cpp | 45 ++++++++++++++++++++++----- src/multimedia/audio/qsoundeffect_pulse_p.h | 10 ++++-- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/multimedia/audio/qsoundeffect_pulse_p.cpp b/src/multimedia/audio/qsoundeffect_pulse_p.cpp index 43d4a6cb0..48f3333c2 100644 --- a/src/multimedia/audio/qsoundeffect_pulse_p.cpp +++ b/src/multimedia/audio/qsoundeffect_pulse_p.cpp @@ -413,7 +413,13 @@ void QSoundEffectPrivate::setSource(const QUrl &url) #ifdef QT_PA_DEBUG qDebug() << this << "setSource =" << url; #endif + + // Make sure the stream is empty before loading a new source (otherwise whatever is there will + // be played before the new source) + emptyStream(); + stop(); + if (m_sample) { if (!m_sampleReady) { disconnect(m_sample, SIGNAL(error()), this, SLOT(decoderError())); @@ -588,7 +594,7 @@ void QSoundEffectPrivate::playAvailable() setLoopsRemaining(0); m_playQueued = true; Q_ASSERT(m_pulseStream); - emptyStream(); + emptyStream(ReloadSampleWhenDone); return; } setLoopsRemaining(m_loopCount); @@ -598,18 +604,25 @@ void QSoundEffectPrivate::playAvailable() setPlaying(true); } -void QSoundEffectPrivate::emptyStream() +void QSoundEffectPrivate::emptyStream(EmptyStreamOptions options) { #ifdef QT_PA_DEBUG qDebug() << this << "emptyStream"; #endif + if (!m_pulseStream || m_emptying) + return; + + const bool reloadSample = options.testFlag(ReloadSampleWhenDone); + pa_stream_success_cb_t flushCompleteCb = reloadSample ? stream_flush_reload_callback + : stream_flush_callback; + m_emptying = true; pa_stream_set_write_callback(m_pulseStream, 0, 0); pa_stream_set_underflow_callback(m_pulseStream, 0, 0); - pa_operation_unref(pa_stream_flush(m_pulseStream, stream_flush_callback, m_ref->getRef())); + pa_operation_unref(pa_stream_flush(m_pulseStream, flushCompleteCb, m_ref->getRef())); } -void QSoundEffectPrivate::emptyComplete(void *stream) +void QSoundEffectPrivate::emptyComplete(void *stream, bool reload) { PulseDaemonLocker locker; #ifdef QT_PA_DEBUG @@ -619,7 +632,7 @@ void QSoundEffectPrivate::emptyComplete(void *stream) m_emptying = false; if ((pa_stream *)stream == m_pulseStream) - pa_operation_unref(pa_stream_cork(m_pulseStream, 1, stream_cork_callback, m_ref->getRef())); + pa_operation_unref(pa_stream_cork(m_pulseStream, 1, reload ? stream_cork_callback : 0, m_ref->getRef())); } void QSoundEffectPrivate::sampleReady() @@ -851,7 +864,7 @@ void QSoundEffectPrivate::stop() PulseDaemonLocker locker; m_stopping = true; if (m_pulseStream) { - emptyStream(); + emptyStream(ReloadSampleWhenDone); if (m_reloadCategory) { unloadPulseStream(); // upon play we reconnect anyway } @@ -1094,10 +1107,26 @@ void QSoundEffectPrivate::stream_flush_callback(pa_stream *s, int success, void if (!success) qWarning("QSoundEffect(pulseaudio): faild to drain"); + + QMetaObject::invokeMethod(self, "emptyComplete", Qt::QueuedConnection, Q_ARG(void*, s), Q_ARG(bool, false)); +} + +void QSoundEffectPrivate::stream_flush_reload_callback(pa_stream *s, int success, void *userdata) +{ #ifdef QT_PA_DEBUG - qDebug() << self << "stream_flush_callback"; + qDebug() << "stream_flush_reload_callback"; #endif - QMetaObject::invokeMethod(self, "emptyComplete", Qt::QueuedConnection, Q_ARG(void*, s)); + Q_UNUSED(s); + QSoundEffectRef *ref = reinterpret_cast(userdata); + QSoundEffectPrivate *self = ref->soundEffect(); + ref->release(); + if (!self) + return; + + if (!success) + qWarning("QSoundEffect(pulseaudio): faild to drain"); + + QMetaObject::invokeMethod(self, "emptyComplete", Qt::QueuedConnection, Q_ARG(void*, s), Q_ARG(bool, true)); } void QSoundEffectPrivate::stream_write_done_callback(void *p) diff --git a/src/multimedia/audio/qsoundeffect_pulse_p.h b/src/multimedia/audio/qsoundeffect_pulse_p.h index 0f16b98a2..2eb20695a 100644 --- a/src/multimedia/audio/qsoundeffect_pulse_p.h +++ b/src/multimedia/audio/qsoundeffect_pulse_p.h @@ -111,7 +111,7 @@ private Q_SLOTS: void underRun(); void prepare(); void streamReady(); - void emptyComplete(void *stream); + void emptyComplete(void *stream, bool reload); void handleAvailabilityChanged(bool available); @@ -119,7 +119,12 @@ private: void playAvailable(); void playSample(); - void emptyStream(); + enum EmptyStreamOption { + ReloadSampleWhenDone = 0x1 + }; + Q_DECLARE_FLAGS(EmptyStreamOptions, EmptyStreamOption) + void emptyStream(EmptyStreamOptions options = EmptyStreamOptions()); + void createPulseStream(); void unloadPulseStream(); @@ -134,6 +139,7 @@ private: static void stream_underrun_callback(pa_stream *s, void *userdata); static void stream_cork_callback(pa_stream *s, int success, void *userdata); static void stream_flush_callback(pa_stream *s, int success, void *userdata); + static void stream_flush_reload_callback(pa_stream *s, int success, void *userdata); static void stream_write_done_callback(void *p); static void stream_adjust_prebuffer_callback(pa_stream *s, int success, void *userdata); static void stream_reset_buffer_callback(pa_stream *s, int success, void *userdata); -- cgit v1.2.3