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 <christian.stromme@qt.io>
This commit is contained in:
Yoann Lopes
2016-07-20 13:09:39 +02:00
parent 57b1dc867c
commit c7ae48c5fb
2 changed files with 45 additions and 10 deletions

View File

@@ -413,7 +413,13 @@ void QSoundEffectPrivate::setSource(const QUrl &url)
#ifdef QT_PA_DEBUG #ifdef QT_PA_DEBUG
qDebug() << this << "setSource =" << url; qDebug() << this << "setSource =" << url;
#endif #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(); stop();
if (m_sample) { if (m_sample) {
if (!m_sampleReady) { if (!m_sampleReady) {
disconnect(m_sample, SIGNAL(error()), this, SLOT(decoderError())); disconnect(m_sample, SIGNAL(error()), this, SLOT(decoderError()));
@@ -588,7 +594,7 @@ void QSoundEffectPrivate::playAvailable()
setLoopsRemaining(0); setLoopsRemaining(0);
m_playQueued = true; m_playQueued = true;
Q_ASSERT(m_pulseStream); Q_ASSERT(m_pulseStream);
emptyStream(); emptyStream(ReloadSampleWhenDone);
return; return;
} }
setLoopsRemaining(m_loopCount); setLoopsRemaining(m_loopCount);
@@ -598,18 +604,25 @@ void QSoundEffectPrivate::playAvailable()
setPlaying(true); setPlaying(true);
} }
void QSoundEffectPrivate::emptyStream() void QSoundEffectPrivate::emptyStream(EmptyStreamOptions options)
{ {
#ifdef QT_PA_DEBUG #ifdef QT_PA_DEBUG
qDebug() << this << "emptyStream"; qDebug() << this << "emptyStream";
#endif #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; m_emptying = true;
pa_stream_set_write_callback(m_pulseStream, 0, 0); pa_stream_set_write_callback(m_pulseStream, 0, 0);
pa_stream_set_underflow_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; PulseDaemonLocker locker;
#ifdef QT_PA_DEBUG #ifdef QT_PA_DEBUG
@@ -619,7 +632,7 @@ void QSoundEffectPrivate::emptyComplete(void *stream)
m_emptying = false; m_emptying = false;
if ((pa_stream *)stream == m_pulseStream) 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() void QSoundEffectPrivate::sampleReady()
@@ -851,7 +864,7 @@ void QSoundEffectPrivate::stop()
PulseDaemonLocker locker; PulseDaemonLocker locker;
m_stopping = true; m_stopping = true;
if (m_pulseStream) { if (m_pulseStream) {
emptyStream(); emptyStream(ReloadSampleWhenDone);
if (m_reloadCategory) { if (m_reloadCategory) {
unloadPulseStream(); // upon play we reconnect anyway unloadPulseStream(); // upon play we reconnect anyway
} }
@@ -1094,10 +1107,26 @@ void QSoundEffectPrivate::stream_flush_callback(pa_stream *s, int success, void
if (!success) if (!success)
qWarning("QSoundEffect(pulseaudio): faild to drain"); 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 #ifdef QT_PA_DEBUG
qDebug() << self << "stream_flush_callback"; qDebug() << "stream_flush_reload_callback";
#endif #endif
QMetaObject::invokeMethod(self, "emptyComplete", Qt::QueuedConnection, Q_ARG(void*, s)); Q_UNUSED(s);
QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef*>(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) void QSoundEffectPrivate::stream_write_done_callback(void *p)

View File

@@ -111,7 +111,7 @@ private Q_SLOTS:
void underRun(); void underRun();
void prepare(); void prepare();
void streamReady(); void streamReady();
void emptyComplete(void *stream); void emptyComplete(void *stream, bool reload);
void handleAvailabilityChanged(bool available); void handleAvailabilityChanged(bool available);
@@ -119,7 +119,12 @@ private:
void playAvailable(); void playAvailable();
void playSample(); void playSample();
void emptyStream(); enum EmptyStreamOption {
ReloadSampleWhenDone = 0x1
};
Q_DECLARE_FLAGS(EmptyStreamOptions, EmptyStreamOption)
void emptyStream(EmptyStreamOptions options = EmptyStreamOptions());
void createPulseStream(); void createPulseStream();
void unloadPulseStream(); void unloadPulseStream();
@@ -134,6 +139,7 @@ private:
static void stream_underrun_callback(pa_stream *s, void *userdata); 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_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_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_write_done_callback(void *p);
static void stream_adjust_prebuffer_callback(pa_stream *s, int success, void *userdata); 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); static void stream_reset_buffer_callback(pa_stream *s, int success, void *userdata);