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:
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user