diff options
-rw-r--r-- | src/multimedia/audio/qsoundeffect_pulse_p.cpp | 214 | ||||
-rw-r--r-- | src/multimedia/audio/qsoundeffect_pulse_p.h | 13 | ||||
-rw-r--r-- | src/plugins/pulseaudio/qaudioinput_pulse.cpp | 99 | ||||
-rw-r--r-- | src/plugins/pulseaudio/qaudioinput_pulse.h | 8 | ||||
-rw-r--r-- | src/plugins/pulseaudio/qaudiooutput_pulse.cpp | 84 | ||||
-rw-r--r-- | src/plugins/pulseaudio/qaudiooutput_pulse.h | 2 |
6 files changed, 113 insertions, 307 deletions
diff --git a/src/multimedia/audio/qsoundeffect_pulse_p.cpp b/src/multimedia/audio/qsoundeffect_pulse_p.cpp index ecc42cac5..43d4a6cb0 100644 --- a/src/multimedia/audio/qsoundeffect_pulse_p.cpp +++ b/src/multimedia/audio/qsoundeffect_pulse_p.cpp @@ -49,10 +49,7 @@ #include "qsoundeffect_pulse_p.h" -#if defined(Q_WS_MAEMO_6) || defined(NEMO_AUDIO) -#include <pulse/ext-stream-restore.h> -#endif - +#include <private/qaudiohelpers_p.h> #include <private/qmediaresourcepolicy_p.h> #include <private/qmediaresourceset_p.h> @@ -124,26 +121,9 @@ public: return m_context; } - inline pa_cvolume * calcVolume(pa_cvolume *dest, int soundEffectVolume) - { - pa_volume_t v = m_vol * soundEffectVolume / 100; - for (int i = 0; i < dest->channels; ++i) - dest->values[i] = v; - return dest; - } - - void updateStatus(const pa_cvolume& volume) - { - if (m_vol != pa_cvolume_max(&volume)) { - m_vol = pa_cvolume_max(&volume); - emit volumeChanged(); - } - } - Q_SIGNALS: void contextReady(); void contextFailed(); - void volumeChanged(); private Q_SLOTS: void onContextFailed() @@ -158,8 +138,6 @@ private Q_SLOTS: void prepare() { - m_vol = PA_VOLUME_NORM; - m_context = 0; m_mainLoop = pa_threaded_mainloop_new(); if (m_mainLoop == 0) { @@ -232,11 +210,6 @@ private: case PA_CONTEXT_SETTING_NAME: break; case PA_CONTEXT_READY: - #if defined(Q_WS_MAEMO_6) || defined(NEMO_AUDIO) - pa_ext_stream_restore_read(c, &stream_restore_info_callback, self); - pa_ext_stream_restore_set_subscribe_cb(c, &stream_restore_monitor_callback, self); - pa_ext_stream_restore_subscribe(c, 1, 0, self); - #endif QMetaObject::invokeMethod(self, "contextReady", Qt::QueuedConnection); break; case PA_CONTEXT_FAILED: @@ -247,37 +220,6 @@ private: } } -#if defined(Q_WS_MAEMO_6) || defined(NEMO_AUDIO) - - static void stream_restore_monitor_callback(pa_context *c, void *userdata) - { - PulseDaemon *self = reinterpret_cast<PulseDaemon*>(userdata); - pa_ext_stream_restore_read(c, &stream_restore_info_callback, self); - } - - static void stream_restore_info_callback(pa_context *c, - const pa_ext_stream_restore_info *info, - int eol, void *userdata) - { - Q_UNUSED(c) - - PulseDaemon *self = reinterpret_cast<PulseDaemon*>(userdata); - - if (!eol) { - if (QString(info->name).startsWith(QLatin1String("sink-input-by-media-role:x-maemo"))) { -#ifdef QT_PA_DEBUG - qDebug() << "x-maemo volume =(" << info->volume.values[0] * 100 / PA_VOLUME_NORM << "," - << info->volume.values[1] * 100 / PA_VOLUME_NORM << "), " - << "mute = " << info->mute; -#endif - self->updateStatus(info->volume); - } - } - } -#endif - - pa_volume_t m_vol; - bool m_prepared; pa_context *m_context; pa_threaded_mainloop *m_mainLoop; @@ -385,9 +327,6 @@ QSoundEffectPrivate::QSoundEffectPrivate(QObject* parent): m_sample(0), m_position(0), m_resourcesAvailable(false) -#if defined(Q_WS_MAEMO_6) || defined(NEMO_AUDIO) - , m_customVolume(false) -#endif { m_ref = new QSoundEffectRef(this); pa_sample_spec_init(&m_pulseSpec); @@ -538,60 +477,32 @@ void QSoundEffectPrivate::setLoopCount(int loopCount) qreal QSoundEffectPrivate::volume() const { + QReadLocker locker(&m_volumeLock); return m_volume; } void QSoundEffectPrivate::setVolume(qreal volume) { -#if defined(Q_WS_MAEMO_6) || defined(NEMO_AUDIO) - m_customVolume = true; -#endif - m_volume = volume; - emit volumeChanged(); - updateVolume(); -} + QWriteLocker locker(&m_volumeLock); -void QSoundEffectPrivate::updateVolume() -{ - if (m_sinkInputId < 0) + if (qFuzzyCompare(m_volume, volume)) return; -#if defined(Q_WS_MAEMO_6) || defined(NEMO_AUDIO) - if (!m_customVolume) - return; -#endif - PulseDaemonLocker locker; - pa_cvolume volume; - volume.channels = m_pulseSpec.channels; - if (pulseDaemon()->context()) - pa_operation_unref(pa_context_set_sink_input_volume(pulseDaemon()->context(), m_sinkInputId, pulseDaemon()->calcVolume(&volume, qRound(m_volume * 100)), setvolume_callback, m_ref->getRef())); - Q_ASSERT(pa_cvolume_valid(&volume)); -#ifdef QT_PA_DEBUG - qDebug() << this << "updateVolume =" << pa_cvolume_max(&volume); -#endif + + m_volume = qBound(qreal(0), volume, qreal(1)); + emit volumeChanged(); } bool QSoundEffectPrivate::isMuted() const { + QReadLocker locker(&m_volumeLock); return m_muted; } void QSoundEffectPrivate::setMuted(bool muted) { + QWriteLocker locker(&m_volumeLock); m_muted = muted; emit mutedChanged(); - updateMuted(); -} - -void QSoundEffectPrivate::updateMuted() -{ - if (m_sinkInputId < 0) - return; - PulseDaemonLocker locker; - if (pulseDaemon()->context()) - pa_operation_unref(pa_context_set_sink_input_mute(pulseDaemon()->context(), m_sinkInputId, m_muted, setmuted_callback, m_ref->getRef())); -#ifdef QT_PA_DEBUG - qDebug() << this << "updateMuted = " << m_muted; -#endif } bool QSoundEffectPrivate::isLoaded() const @@ -801,7 +712,6 @@ void QSoundEffectPrivate::unloadPulseStream() pa_stream_set_underflow_callback(m_pulseStream, 0, 0); pa_stream_disconnect(m_pulseStream); pa_stream_unref(m_pulseStream); - disconnect(pulseDaemon(), SIGNAL(volumeChanged()), this, SLOT(updateVolume())); disconnect(pulseDaemon(), SIGNAL(contextFailed()), this, SLOT(contextFailed())); m_pulseStream = 0; m_reloadCategory = false; // category will be reloaded when we connect anyway @@ -822,11 +732,8 @@ void QSoundEffectPrivate::prepare() << "actual writeBytes =" << writeBytes << "m_playQueued =" << m_playQueued; #endif - m_position = int(writeBytes); - if (pa_stream_write(m_pulseStream, reinterpret_cast<void *>(const_cast<char*>(m_sample->data().data())), writeBytes, - stream_write_done_callback, 0, PA_SEEK_RELATIVE) != 0) { - qWarning("QSoundEffect(pulseaudio): pa_stream_write, error = %s", pa_strerror(pa_context_errno(pulseDaemon()->context()))); - } + m_position = writeToStream(m_sample->data().data(), writeBytes); + if (m_playQueued) { m_playQueued = false; setLoopsRemaining(m_loopCount); @@ -854,15 +761,13 @@ void QSoundEffectPrivate::uploadSample() } } - int writtenBytes = 0; int writableSize = int(pa_stream_writable_size(m_pulseStream)); int firstPartLength = qMin(m_sample->data().size() - m_position, writableSize); - if (pa_stream_write(m_pulseStream, reinterpret_cast<void *>(const_cast<char*>(m_sample->data().data()) + m_position), - firstPartLength, stream_write_done_callback, 0, PA_SEEK_RELATIVE) != 0) { - qWarning("QSoundEffect(pulseaudio): pa_stream_write, error = %s", pa_strerror(pa_context_errno(pulseDaemon()->context()))); - } - writtenBytes = firstPartLength; - m_position += firstPartLength; + + int writtenBytes = writeToStream(m_sample->data().data() + m_position, + firstPartLength); + + m_position += writtenBytes; if (m_position == m_sample->data().size()) { m_position = 0; if (m_runningCount > 0) @@ -871,11 +776,8 @@ void QSoundEffectPrivate::uploadSample() { while (writtenBytes < writableSize) { int writeSize = qMin(writableSize - writtenBytes, m_sample->data().size()); - if (pa_stream_write(m_pulseStream, reinterpret_cast<void *>(const_cast<char*>(m_sample->data().data())), - writeSize, stream_write_done_callback, 0, PA_SEEK_RELATIVE) != 0) { - qWarning("QSoundEffect(pulseaudio): pa_stream_write, error = %s", pa_strerror(pa_context_errno(pulseDaemon()->context()))); - } - writtenBytes += writeSize; + writtenBytes += writeToStream(m_sample->data().data(), writeSize); + if (writeSize < m_sample->data().size()) { m_position = writeSize; break; @@ -893,6 +795,39 @@ void QSoundEffectPrivate::uploadSample() #endif } +int QSoundEffectPrivate::writeToStream(const void *data, int size) +{ + m_volumeLock.lockForRead(); + qreal volume = m_muted ? 0 : m_volume; + m_volumeLock.unlock(); + pa_free_cb_t writeDoneCb = stream_write_done_callback; + + if (volume < 1.0f) { + // Don't use PulseAudio volume, as it might affect all other streams of the same category + // or even affect the system volume if flat volumes are enabled + void *dest = NULL; + size_t nbytes = size; + if (pa_stream_begin_write(m_pulseStream, &dest, &nbytes) < 0) { + qWarning("QSoundEffect(pulseaudio): pa_stream_begin_write, error = %s", + pa_strerror(pa_context_errno(pulseDaemon()->context()))); + return 0; + } + + size = int(nbytes); + QAudioHelperInternal::qMultiplySamples(volume, m_sample->format(), data, dest, size); + data = dest; + writeDoneCb = NULL; + } + + if (pa_stream_write(m_pulseStream, data, size, writeDoneCb, 0, PA_SEEK_RELATIVE) < 0) { + qWarning("QSoundEffect(pulseaudio): pa_stream_write, error = %s", + pa_strerror(pa_context_errno(pulseDaemon()->context()))); + return 0; + } + + return size; +} + void QSoundEffectPrivate::playSample() { #ifdef QT_PA_DEBUG @@ -939,8 +874,6 @@ void QSoundEffectPrivate::streamReady() #endif PulseDaemonLocker locker; m_sinkInputId = pa_stream_get_index(m_pulseStream); - updateMuted(); - updateVolume(); #ifdef QT_PA_DEBUG const pa_buffer_attr *realBufAttr = pa_stream_get_buffer_attr(m_pulseStream); qDebug() << this << "m_sinkInputId =" << m_sinkInputId @@ -966,7 +899,6 @@ void QSoundEffectPrivate::createPulseStream() pa_stream *stream = pa_stream_new_with_proplist(pulseDaemon()->context(), m_name.constData(), &m_pulseSpec, 0, propList); pa_proplist_free(propList); - connect(pulseDaemon(), SIGNAL(volumeChanged()), this, SLOT(updateVolume())); connect(pulseDaemon(), SIGNAL(contextFailed()), this, SLOT(contextFailed())); if (stream == 0) { @@ -994,9 +926,7 @@ void QSoundEffectPrivate::createPulseStream() #else if (pa_stream_connect_playback(m_pulseStream, 0, 0, #endif - m_muted ? pa_stream_flags_t(PA_STREAM_START_MUTED | PA_STREAM_START_CORKED) - : pa_stream_flags_t(PA_STREAM_START_UNMUTED | PA_STREAM_START_CORKED), - 0, 0) < 0) { + PA_STREAM_START_CORKED, 0, 0) < 0) { qWarning("QSoundEffect(pulseaudio): Failed to connect stream, error = %s", pa_strerror(pa_context_errno(pulseDaemon()->context()))); } @@ -1115,46 +1045,6 @@ void QSoundEffectPrivate::stream_adjust_prebuffer_callback(pa_stream *s, int suc QMetaObject::invokeMethod(self, "streamReady", Qt::QueuedConnection); } -void QSoundEffectPrivate::setvolume_callback(pa_context *c, int success, void *userdata) -{ -#ifdef QT_PA_DEBUG - qDebug() << "setvolume_callback"; -#endif - Q_UNUSED(c); - Q_UNUSED(userdata); - QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef*>(userdata); - QSoundEffectPrivate *self = ref->soundEffect(); - ref->release(); - if (!self) - return; -#ifdef QT_PA_DEBUG - qDebug() << self << "setvolume_callback"; -#endif - if (!success) { - qWarning("QSoundEffect(pulseaudio): faild to set volume"); - } -} - -void QSoundEffectPrivate::setmuted_callback(pa_context *c, int success, void *userdata) -{ -#ifdef QT_PA_DEBUG - qDebug() << "setmuted_callback"; -#endif - Q_UNUSED(c); - Q_UNUSED(userdata); - QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef*>(userdata); - QSoundEffectPrivate *self = ref->soundEffect(); - ref->release(); - if (!self) - return; -#ifdef QT_PA_DEBUG - qDebug() << self << "setmuted_callback"; -#endif - if (!success) { - qWarning("QSoundEffect(pulseaudio): faild to set muted"); - } -} - void QSoundEffectPrivate::stream_underrun_callback(pa_stream *s, void *userdata) { Q_UNUSED(s); diff --git a/src/multimedia/audio/qsoundeffect_pulse_p.h b/src/multimedia/audio/qsoundeffect_pulse_p.h index 9b3564cc9..0f16b98a2 100644 --- a/src/multimedia/audio/qsoundeffect_pulse_p.h +++ b/src/multimedia/audio/qsoundeffect_pulse_p.h @@ -50,6 +50,7 @@ #include <QtCore/qobject.h> #include <QtCore/qdatetime.h> +#include <QtCore/qreadwritelock.h> #include <qmediaplayer.h> #include <pulse/pulseaudio.h> #include "qsamplecache_p.h" @@ -111,8 +112,6 @@ private Q_SLOTS: void prepare(); void streamReady(); void emptyComplete(void *stream); - void updateVolume(); - void updateMuted(); void handleAvailabilityChanged(bool available); @@ -124,6 +123,8 @@ private: void createPulseStream(); void unloadPulseStream(); + int writeToStream(const void *data, int size); + void setPlaying(bool playing); void setStatus(QSoundEffect::Status status); void setLoopsRemaining(int loopsRemaining); @@ -136,8 +137,6 @@ private: 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); - static void setvolume_callback(pa_context *c, int success, void *userdata); - static void setmuted_callback(pa_context *c, int success, void *userdata); pa_stream *m_pulseStream; int m_sinkInputId; @@ -165,11 +164,9 @@ private: bool m_resourcesAvailable; - QMediaPlayerResourceSetInterface *m_resources; + mutable QReadWriteLock m_volumeLock; -#if defined(Q_WS_MAEMO_6) || defined(NEMO_AUDIO) - bool m_customVolume; -#endif + QMediaPlayerResourceSetInterface *m_resources; }; QT_END_NAMESPACE diff --git a/src/plugins/pulseaudio/qaudioinput_pulse.cpp b/src/plugins/pulseaudio/qaudioinput_pulse.cpp index 77b438ff9..93d410598 100644 --- a/src/plugins/pulseaudio/qaudioinput_pulse.cpp +++ b/src/plugins/pulseaudio/qaudioinput_pulse.cpp @@ -34,6 +34,7 @@ #include <QtCore/qcoreapplication.h> #include <QtCore/qdebug.h> #include <QtCore/qmath.h> +#include <private/qaudiohelpers_p.h> #include "qaudioinput_pulse.h" #include "qaudiodeviceinfo_pulse.h" @@ -118,39 +119,12 @@ static void inputStreamSuccessCallback(pa_stream *stream, int success, void *use pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); } -void QPulseAudioInput::sourceInfoCallback(pa_context *context, const pa_source_info *i, int eol, void *userdata) -{ - Q_UNUSED(context); - Q_UNUSED(eol); - - Q_ASSERT(userdata); - if (i) { - QPulseAudioInput *that = reinterpret_cast<QPulseAudioInput*>(userdata); - that->m_volume = pa_sw_volume_to_linear(pa_cvolume_avg(&i->volume)); - } -} - -void QPulseAudioInput::inputVolumeCallback(pa_context *context, int success, void *userdata) -{ - Q_UNUSED(success); - - if (!success) - qWarning() << "QAudioInput: failed to set input volume"; - - QPulseAudioInput *that = reinterpret_cast<QPulseAudioInput*>(userdata); - - // Regardless of success or failure, we update the volume property - if (that->m_stream) - pa_context_get_source_info_by_index(context, pa_stream_get_device_index(that->m_stream), sourceInfoCallback, userdata); -} - QPulseAudioInput::QPulseAudioInput(const QByteArray &device) : m_totalTimeValue(0) , m_audioSource(0) , m_errorState(QAudio::NoError) , m_deviceState(QAudio::StoppedState) , m_volume(qreal(1.0f)) - , m_customVolumeRequired(false) , m_pullMode(true) , m_opened(false) , m_bytesAvailable(0) @@ -356,9 +330,6 @@ bool QPulseAudioInput::open() if (actualBufferAttr->tlength != (uint32_t)-1) m_bufferSize = actualBufferAttr->tlength; - if (m_customVolumeRequired) - setPulseVolume(); - pulseEngine->unlock(); connect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioInput::onPulseContextFailed); @@ -407,32 +378,6 @@ void QPulseAudioInput::close() m_opened = false; } -/* Call this with the stream opened and the mainloop locked */ -void QPulseAudioInput::setPulseVolume() -{ - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - Q_ASSERT(pulseEngine->context() != 0); - - pa_cvolume cvolume; - - if (qFuzzyCompare(m_volume, 0.0)) { - pa_cvolume_mute(&cvolume, m_spec.channels); - } else { - pa_cvolume_set(&cvolume, m_spec.channels, pa_sw_volume_from_linear(m_volume)); - } - - pa_operation *op = pa_context_set_source_volume_by_index(pulseEngine->context(), - pa_stream_get_device_index(m_stream), - &cvolume, - inputVolumeCallback, - this); - - if (op == NULL) - qWarning() << "QAudioInput: Failed to set volume"; - else - pa_operation_unref(op); -} - int QPulseAudioInput::checkBytesReady() { if (m_deviceState != QAudio::ActiveState && m_deviceState != QAudio::IdleState) { @@ -494,7 +439,9 @@ qint64 QPulseAudioInput::read(char *data, qint64 len) qint64 actualLength = 0; if (m_pullMode) { - actualLength = m_audioSource->write(static_cast<const char *>(audioBuffer), readLength); + QByteArray adjusted(readLength, Qt::Uninitialized); + applyVolume(audioBuffer, adjusted.data(), readLength); + actualLength = m_audioSource->write(adjusted); if (actualLength < qint64(readLength)) { pulseEngine->unlock(); @@ -506,7 +453,7 @@ qint64 QPulseAudioInput::read(char *data, qint64 len) } } else { actualLength = qMin(static_cast<int>(len - readBytes), static_cast<int>(readLength)); - memcpy(data + readBytes, audioBuffer, actualLength); + applyVolume(audioBuffer, data + readBytes, actualLength); } #ifdef DEBUG_PULSE @@ -517,7 +464,10 @@ qint64 QPulseAudioInput::read(char *data, qint64 len) #ifdef DEBUG_PULSE qDebug() << "QPulseAudioInput::read -- appending " << readLength - actualLength << " bytes of data to temp buffer"; #endif - m_tempBuffer.append(static_cast<const char *>(audioBuffer) + actualLength, readLength - actualLength); + int diff = readLength - actualLength; + int oldSize = m_tempBuffer.size(); + m_tempBuffer.resize(m_tempBuffer.size() + diff); + applyVolume(static_cast<const char *>(audioBuffer) + actualLength, m_tempBuffer.data() + oldSize, diff); QMetaObject::invokeMethod(this, "userFeed", Qt::QueuedConnection); } @@ -544,6 +494,14 @@ qint64 QPulseAudioInput::read(char *data, qint64 len) return readBytes; } +void QPulseAudioInput::applyVolume(const void *src, void *dest, int len) +{ + if (m_volume < 1.f) + QAudioHelperInternal::qMultiplySamples(m_volume, m_format, src, dest, len); + else + memcpy(dest, src, len); +} + void QPulseAudioInput::resume() { if (m_deviceState == QAudio::SuspendedState || m_deviceState == QAudio::IdleState) { @@ -567,30 +525,17 @@ void QPulseAudioInput::resume() void QPulseAudioInput::setVolume(qreal vol) { - if (vol >= 0.0 && vol <= 1.0) { - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pulseEngine->lock(); - m_customVolumeRequired = true; - if (!qFuzzyCompare(m_volume, vol)) { - m_volume = vol; - if (m_opened) { - setPulseVolume(); - } - } - pulseEngine->unlock(); - } + if (qFuzzyCompare(m_volume, vol)) + return; + + m_volume = qBound(qreal(0), vol, qreal(1)); } qreal QPulseAudioInput::volume() const { - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pulseEngine->lock(); - qreal vol = m_volume; - pulseEngine->unlock(); - return vol; + return m_volume; } - void QPulseAudioInput::setBufferSize(int value) { m_bufferSize = value; diff --git a/src/plugins/pulseaudio/qaudioinput_pulse.h b/src/plugins/pulseaudio/qaudioinput_pulse.h index 7b898c480..c130f8cac 100644 --- a/src/plugins/pulseaudio/qaudioinput_pulse.h +++ b/src/plugins/pulseaudio/qaudioinput_pulse.h @@ -100,8 +100,6 @@ public: QAudio::Error m_errorState; QAudio::State m_deviceState; qreal m_volume; - bool m_customVolumeRequired; - pa_cvolume m_chVolume; private slots: void userFeed(); @@ -112,13 +110,11 @@ private: void setState(QAudio::State state); void setError(QAudio::Error error); + void applyVolume(const void *src, void *dest, int len); + int checkBytesReady(); bool open(); void close(); - void setPulseVolume(); - - static void sourceInfoCallback(pa_context *c, const pa_source_info *i, int eol, void *userdata); - static void inputVolumeCallback(pa_context *context, int success, void *userdata); bool m_pullMode; bool m_opened; diff --git a/src/plugins/pulseaudio/qaudiooutput_pulse.cpp b/src/plugins/pulseaudio/qaudiooutput_pulse.cpp index c1d46fac4..a850c9b0e 100644 --- a/src/plugins/pulseaudio/qaudiooutput_pulse.cpp +++ b/src/plugins/pulseaudio/qaudiooutput_pulse.cpp @@ -34,6 +34,7 @@ #include <QtCore/qcoreapplication.h> #include <QtCore/qdebug.h> #include <QtCore/qmath.h> +#include <private/qaudiohelpers_p.h> #include "qaudiooutput_pulse.h" #include "qaudiodeviceinfo_pulse.h" @@ -162,7 +163,6 @@ QPulseAudioOutput::QPulseAudioOutput(const QByteArray &device) , m_audioBuffer(0) , m_resuming(false) , m_volume(1.0) - , m_customVolumeRequired(false) { connect(m_tickTimer, SIGNAL(timeout()), SLOT(userFeed())); } @@ -312,27 +312,6 @@ bool QPulseAudioOutput::open() pa_stream_set_overflow_callback(m_stream, outputStreamOverflowCallback, this); pa_stream_set_latency_update_callback(m_stream, outputStreamLatencyCallback, this); - pa_volume_t paVolume; - - /* streams without a custom volume set are expected to already have a - * sensible volume set by Pulse, so we don't set it explicitly. - * - * explicit setting also breaks volume handling on sailfish, where each - * stream's volume is set separately inside pulseaudio, with the - * exception of streams that already have a volume set (i.e. if we set - * it here, we'd ignore system volume). - */ - if (m_customVolumeRequired) { - if (qFuzzyCompare(m_volume, 0.0)) { - paVolume = PA_VOLUME_MUTED; - m_volume = 0.0; - } else { - paVolume = qFloor(m_volume * PA_VOLUME_NORM + 0.5); - } - - pa_cvolume_set(&m_chVolume, m_spec.channels, paVolume); - } - if (m_bufferSize <= 0 && m_category == LOW_LATENCY_CATEGORY_NAME) { m_bufferSize = bytesPerSecond * LowLatencyBufferSizeMs / qint64(1000); } @@ -344,7 +323,7 @@ bool QPulseAudioOutput::open() requestedBuffer.prebuf = (uint32_t)-1; requestedBuffer.tlength = m_bufferSize; - if (pa_stream_connect_playback(m_stream, m_device.data(), (m_bufferSize > 0) ? &requestedBuffer : NULL, (pa_stream_flags_t)0, m_customVolumeRequired ? &m_chVolume : NULL, NULL) < 0) { + if (pa_stream_connect_playback(m_stream, m_device.data(), (m_bufferSize > 0) ? &requestedBuffer : NULL, (pa_stream_flags_t)0, NULL, NULL) < 0) { qWarning() << "pa_stream_connect_playback() failed!"; pa_stream_unref(m_stream); m_stream = 0; @@ -491,8 +470,33 @@ qint64 QPulseAudioOutput::write(const char *data, qint64 len) QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); pulseEngine->lock(); + len = qMin(len, static_cast<qint64>(pa_stream_writable_size(m_stream))); - pa_stream_write(m_stream, data, len, 0, 0, PA_SEEK_RELATIVE); + + if (m_volume < 1.0f) { + // Don't use PulseAudio volume, as it might affect all other streams of the same category + // or even affect the system volume if flat volumes are enabled + void *dest = NULL; + size_t nbytes = len; + if (pa_stream_begin_write(m_stream, &dest, &nbytes) < 0) { + qWarning("QAudioOutput(pulseaudio): pa_stream_begin_write, error = %s", + pa_strerror(pa_context_errno(pulseEngine->context()))); + setError(QAudio::IOError); + return 0; + } + + len = int(nbytes); + QAudioHelperInternal::qMultiplySamples(m_volume, m_format, data, dest, len); + data = reinterpret_cast<char *>(dest); + } + + if (pa_stream_write(m_stream, data, len, NULL, 0, PA_SEEK_RELATIVE) < 0) { + qWarning("QAudioOutput(pulseaudio): pa_stream_write, error = %s", + pa_strerror(pa_context_errno(pulseEngine->context()))); + setError(QAudio::IOError); + return 0; + } + pulseEngine->unlock(); m_totalTimeValue += len; @@ -664,34 +668,10 @@ qint64 PulseOutputPrivate::writeData(const char *data, qint64 len) void QPulseAudioOutput::setVolume(qreal vol) { - if (vol >= 0.0 && vol <= 1.0) { - if (!qFuzzyCompare(m_volume, vol)) { - m_customVolumeRequired = true; - m_volume = vol; - if (m_opened) { - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pulseEngine->lock(); - pa_volume_t paVolume; - if (qFuzzyCompare(vol, 0.0)) { - pa_cvolume_mute(&m_chVolume, m_spec.channels); - m_volume = 0.0; - } else { - paVolume = qFloor(m_volume * PA_VOLUME_NORM + 0.5); - pa_cvolume_set(&m_chVolume, m_spec.channels, paVolume); - } - pa_operation *op = pa_context_set_sink_input_volume(pulseEngine->context(), - pa_stream_get_index(m_stream), - &m_chVolume, - NULL, - NULL); - if (op == NULL) - qWarning()<<"QAudioOutput: Failed to set volume"; - else - pa_operation_unref(op); - pulseEngine->unlock(); - } - } - } + if (qFuzzyCompare(m_volume, vol)) + return; + + m_volume = qBound(qreal(0), vol, qreal(1)); } qreal QPulseAudioOutput::volume() const diff --git a/src/plugins/pulseaudio/qaudiooutput_pulse.h b/src/plugins/pulseaudio/qaudiooutput_pulse.h index e24fd514b..a165da86a 100644 --- a/src/plugins/pulseaudio/qaudiooutput_pulse.h +++ b/src/plugins/pulseaudio/qaudiooutput_pulse.h @@ -135,8 +135,6 @@ private: QString m_category; qreal m_volume; - bool m_customVolumeRequired; - pa_cvolume m_chVolume; pa_sample_spec m_spec; }; |