summaryrefslogtreecommitdiffstats
path: root/src/multimedia/audio
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2021-01-06 08:42:38 +0100
committerLars Knoll <lars.knoll@qt.io>2021-01-22 07:00:33 +0000
commitc9964f051abadbc0f2228c60484a595873059e01 (patch)
tree51e9811f06d1785fae05e5cd523b2868b2c9e6f3 /src/multimedia/audio
parentb898e5038b29a236578dc7cd1588bdbc0d40eac1 (diff)
Clean up the QSoundEffect implementation
Change-Id: I2d1bb41575d0516492e7e9225fb022fbd62cdb0a Reviewed-by: Doris Verria <doris.verria@qt.io> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/multimedia/audio')
-rw-r--r--src/multimedia/audio/audio.pri4
-rw-r--r--src/multimedia/audio/qsoundeffect.cpp382
-rw-r--r--src/multimedia/audio/qsoundeffect_qaudio_p.cpp457
-rw-r--r--src/multimedia/audio/qsoundeffect_qaudio_p.h151
4 files changed, 344 insertions, 650 deletions
diff --git a/src/multimedia/audio/audio.pri b/src/multimedia/audio/audio.pri
index 352c157bd..aeb204481 100644
--- a/src/multimedia/audio/audio.pri
+++ b/src/multimedia/audio/audio.pri
@@ -18,7 +18,6 @@ PRIVATE_HEADERS += \
audio/qsamplecache_p.h \
audio/qaudiohelpers_p.h \
audio/qaudiosystem_p.h \
- audio/qsoundeffect_qaudio_p.h
SOURCES += \
audio/qaudio.cpp \
@@ -34,8 +33,7 @@ SOURCES += \
audio/qaudiobuffer.cpp \
audio/qaudioprobe.cpp \
audio/qaudiodecoder.cpp \
- audio/qaudiohelpers.cpp \
- audio/qsoundeffect_qaudio_p.cpp
+ audio/qaudiohelpers.cpp
android: include(opensles/opensles.pri)
win32: include(windows/windows.pri)
diff --git a/src/multimedia/audio/qsoundeffect.cpp b/src/multimedia/audio/qsoundeffect.cpp
index cf4a28028..97be1e0a8 100644
--- a/src/multimedia/audio/qsoundeffect.cpp
+++ b/src/multimedia/audio/qsoundeffect.cpp
@@ -39,10 +39,223 @@
#include <QtMultimedia/private/qtmultimediaglobal_p.h>
#include "qsoundeffect.h"
-#include "qsoundeffect_qaudio_p.h"
+#include "qsamplecache_p.h"
+#include "qaudiodeviceinfo.h"
+#include "qaudiooutput.h"
QT_BEGIN_NAMESPACE
+Q_GLOBAL_STATIC(QSampleCache, sampleCache)
+
+class QSoundEffectPrivate : public QIODevice
+{
+public:
+ QSoundEffectPrivate(QSoundEffect *q, const QAudioDeviceInfo &audioDevice = QAudioDeviceInfo());
+ ~QSoundEffectPrivate() {}
+
+ qint64 readData(char *data, qint64 len) override;
+ qint64 writeData(const char *data, qint64 len) override;
+
+ void setLoopsRemaining(int loopsRemaining);
+ void setStatus(QSoundEffect::Status status);
+ void setPlaying(bool playing);
+
+public Q_SLOTS:
+ void sampleReady();
+ void decoderError();
+ void stateChanged(QAudio::State);
+
+public:
+ QSoundEffect *q_ptr;
+ QUrl m_url;
+ int m_loopCount = 1;
+ int m_runningCount = 0;
+ bool m_playing = false;
+ QSoundEffect::Status m_status = QSoundEffect::Null;
+ QAudioOutput *m_audioOutput = nullptr;
+ QSample *m_sample = nullptr;
+ bool m_muted = false;
+ qreal m_volume = 1.0;
+ bool m_sampleReady = false;
+ qint64 m_offset = 0;
+ QString m_category;
+ QAudioDeviceInfo m_audioDevice;
+};
+
+QSoundEffectPrivate::QSoundEffectPrivate(QSoundEffect *q, const QAudioDeviceInfo &audioDevice)
+ : QIODevice(q)
+ , q_ptr(q)
+ , m_audioDevice(audioDevice)
+{
+ m_category = QLatin1String("game");
+ open(QIODevice::ReadOnly);
+}
+
+void QSoundEffectPrivate::sampleReady()
+{
+ if (m_status == QSoundEffect::Error)
+ return;
+
+#ifdef QT_QAUDIO_DEBUG
+ qDebug() << this << "sampleReady "<<m_playing;
+#endif
+ disconnect(m_sample, &QSample::error, this, &QSoundEffectPrivate::decoderError);
+ disconnect(m_sample, &QSample::ready, this, &QSoundEffectPrivate::sampleReady);
+ if (!m_audioOutput) {
+ if (m_audioDevice.isNull())
+ m_audioOutput = new QAudioOutput(m_sample->format());
+ else
+ m_audioOutput = new QAudioOutput(m_audioDevice, m_sample->format());
+ connect(m_audioOutput, &QAudioOutput::stateChanged, this, &QSoundEffectPrivate::stateChanged);
+ if (!m_muted)
+ m_audioOutput->setVolume(m_volume);
+ else
+ m_audioOutput->setVolume(0);
+ }
+ m_sampleReady = true;
+ setStatus(QSoundEffect::Ready);
+
+ if (m_playing && m_audioOutput->state() == QAudio::StoppedState)
+ m_audioOutput->start(this);
+}
+
+void QSoundEffectPrivate::decoderError()
+{
+ qWarning("QSoundEffect(qaudio): Error decoding source %ls", qUtf16Printable(m_url.toString()));
+ disconnect(m_sample, &QSample::ready, this, &QSoundEffectPrivate::sampleReady);
+ disconnect(m_sample, &QSample::error, this, &QSoundEffectPrivate::decoderError);
+ m_playing = false;
+ setStatus(QSoundEffect::Error);
+}
+
+void QSoundEffectPrivate::stateChanged(QAudio::State state)
+{
+#ifdef QT_QAUDIO_DEBUG
+ qDebug() << this << "stateChanged " << state;
+#endif
+ if ((state == QAudio::IdleState && m_runningCount == 0)
+ || (state == QAudio::StoppedState && m_audioOutput->error() != QAudio::NoError))
+ emit q_ptr->stop();
+}
+
+qint64 QSoundEffectPrivate::readData(char *data, qint64 len)
+{
+ if ((m_runningCount > 0 || m_runningCount == QSoundEffect::Infinite) && m_playing) {
+
+ if (m_sample->state() != QSample::Ready)
+ return 0;
+
+ qint64 bytesWritten = 0;
+
+ const int periodSize = m_audioOutput->periodSize();
+ const int sampleSize = m_sample->data().size();
+ const char* sampleData = m_sample->data().constData();
+
+ // Some systems can have large buffers we only need a max of three
+ int periodsFree = qMin(3, (int)(m_audioOutput->bytesFree()/periodSize));
+ int dataOffset = 0;
+
+#ifdef QT_QAUDIO_DEBUG
+ qDebug() << "bytesFree=" << m_audioOutput->bytesFree() << ", can fit " << periodsFree << " periodSize() chunks";
+#endif
+
+ while ((periodsFree > 0) && (bytesWritten + periodSize
+ <= len)) {
+
+ if (sampleSize - m_offset >= periodSize) {
+ // We can fit a whole period of data
+ memcpy(data + dataOffset, sampleData + m_offset, periodSize);
+ m_offset += periodSize;
+ dataOffset += periodSize;
+ bytesWritten += periodSize;
+#ifdef QT_QAUDIO_DEBUG
+ qDebug() << "WHOLE PERIOD: bytesWritten=" << bytesWritten << ", offset=" << m_offset
+ << ", filesize=" << sampleSize;
+#endif
+ } else {
+ // We are at end of sound, first write what is left of current sound
+ memcpy(data + dataOffset, sampleData + m_offset, sampleSize - m_offset);
+ bytesWritten += sampleSize - m_offset;
+ int wrapLen = periodSize - (sampleSize - m_offset);
+ if (wrapLen > sampleSize)
+ wrapLen = sampleSize;
+#ifdef QT_QAUDIO_DEBUG
+ qDebug() << "END OF SOUND: bytesWritten=" << bytesWritten << ", offset=" << m_offset
+ << ", part1=" << (sampleSize-m_offset);
+#endif
+ dataOffset += (sampleSize - m_offset);
+ m_offset = 0;
+
+ if (m_runningCount > 0 && m_runningCount != QSoundEffect::Infinite)
+ setLoopsRemaining(m_runningCount-1);
+
+ if (m_runningCount > 0 || m_runningCount == QSoundEffect::Infinite) {
+ // There are still more loops of this sound to play, append the start of sound to make up full period
+ memcpy(data + dataOffset, sampleData + m_offset, wrapLen);
+ m_offset += wrapLen;
+ dataOffset += wrapLen;
+ bytesWritten += wrapLen;
+#ifdef QT_QAUDIO_DEBUG
+ qDebug() << "APPEND START FOR FULL PERIOD: bytesWritten=" << bytesWritten << ", offset=" << m_offset
+ << ", part2=" << wrapLen;
+ qDebug() << "part1 + part2 should be a period " << periodSize;
+#endif
+ }
+ }
+ if (m_runningCount == 0)
+ break;
+
+ periodsFree--;
+ }
+ return bytesWritten;
+ }
+
+ return 0;
+}
+
+qint64 QSoundEffectPrivate::writeData(const char *data, qint64 len)
+{
+ Q_UNUSED(data);
+ Q_UNUSED(len);
+ return 0;
+}
+
+void QSoundEffectPrivate::setLoopsRemaining(int loopsRemaining)
+{
+ if (m_runningCount == loopsRemaining)
+ return;
+#ifdef QT_QAUDIO_DEBUG
+ qDebug() << this << "setLoopsRemaining " << loopsRemaining;
+#endif
+ m_runningCount = loopsRemaining;
+ emit q_ptr->loopsRemainingChanged();
+}
+
+void QSoundEffectPrivate::setStatus(QSoundEffect::Status status)
+{
+#ifdef QT_QAUDIO_DEBUG
+ qDebug() << this << "setStatus" << status;
+#endif
+ if (m_status == status)
+ return;
+ bool oldLoaded = q_ptr->isLoaded();
+ m_status = status;
+ emit q_ptr->statusChanged();
+ if (oldLoaded != q_ptr->isLoaded())
+ emit q_ptr->loadedChanged();
+}
+
+void QSoundEffectPrivate::setPlaying(bool playing)
+{
+#ifdef QT_QAUDIO_DEBUG
+ qDebug() << this << "setPlaying(" << playing << ")";
+#endif
+ if (m_playing == playing)
+ return;
+ m_playing = playing;
+ emit q_ptr->playingChanged();
+}
+
/*!
\class QSoundEffect
\brief The QSoundEffect class provides a way to play low latency sound effects.
@@ -106,24 +319,11 @@ QT_BEGIN_NAMESPACE
sound effects.
*/
-static QSoundEffectPrivate *initPrivate(QSoundEffect *self, QSoundEffectPrivate *d)
-{
- QObject::connect(d, &QSoundEffectPrivate::loopsRemainingChanged, self, &QSoundEffect::loopsRemainingChanged);
- QObject::connect(d, &QSoundEffectPrivate::volumeChanged, self, &QSoundEffect::volumeChanged);
- QObject::connect(d, &QSoundEffectPrivate::mutedChanged, self, &QSoundEffect::mutedChanged);
- QObject::connect(d, &QSoundEffectPrivate::loadedChanged, self, &QSoundEffect::loadedChanged);
- QObject::connect(d, &QSoundEffectPrivate::playingChanged, self, &QSoundEffect::playingChanged);
- QObject::connect(d, &QSoundEffectPrivate::statusChanged, self, &QSoundEffect::statusChanged);
- QObject::connect(d, &QSoundEffectPrivate::categoryChanged, self, &QSoundEffect::categoryChanged);
-
- return d;
-}
/*!
Creates a QSoundEffect with the given \a parent.
*/
QSoundEffect::QSoundEffect(QObject *parent)
- : QObject(parent)
- , d(initPrivate(this, new QSoundEffectPrivate(this)))
+ : QSoundEffect(QAudioDeviceInfo(), parent)
{
}
@@ -132,7 +332,7 @@ QSoundEffect::QSoundEffect(QObject *parent)
*/
QSoundEffect::QSoundEffect(const QAudioDeviceInfo &audioDevice, QObject *parent)
: QObject(parent)
- , d(initPrivate(this, new QSoundEffectPrivate(audioDevice, this)))
+ , d(new QSoundEffectPrivate(this, audioDevice))
{
}
@@ -141,7 +341,13 @@ QSoundEffect::QSoundEffect(const QAudioDeviceInfo &audioDevice, QObject *parent)
*/
QSoundEffect::~QSoundEffect()
{
- d->release();
+ stop();
+ if (d->m_audioOutput) {
+ d->m_audioOutput->stop();
+ d->m_audioOutput->deleteLater();
+ d->m_sample->release();
+ }
+ delete d;
}
/*!
@@ -151,7 +357,15 @@ QSoundEffect::~QSoundEffect()
*/
QStringList QSoundEffect::supportedMimeTypes()
{
- return QSoundEffectPrivate::supportedMimeTypes();
+ // Only return supported mime types if we have a audio device available
+ const QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
+ if (devices.size() <= 0)
+ return QStringList();
+
+ return QStringList() << QLatin1String("audio/x-wav")
+ << QLatin1String("audio/wav")
+ << QLatin1String("audio/wave")
+ << QLatin1String("audio/x-pn-wav");
}
/*!
@@ -173,16 +387,67 @@ QStringList QSoundEffect::supportedMimeTypes()
/*! Returns the URL of the current source to play */
QUrl QSoundEffect::source() const
{
- return d->source();
+ return d->m_url;
}
/*! Set the current URL to play to \a url. */
void QSoundEffect::setSource(const QUrl &url)
{
- if (d->source() == url)
+ if (d->m_url == url)
+ return;
+
+#ifdef QT_QAUDIO_DEBUG
+ qDebug() << this << "setSource current=" << d->m_url << ", to=" << url;
+#endif
+ Q_ASSERT(d->m_url != url);
+
+ stop();
+
+ d->m_url = url;
+
+ d->m_sampleReady = false;
+
+ if (url.isEmpty()) {
+ d->setStatus(QSoundEffect::Null);
+ return;
+ }
+
+ if (!url.isValid()) {
+ d->setStatus(QSoundEffect::Error);
return;
+ }
- d->setSource(url);
+ if (d->m_sample) {
+ if (!d->m_sampleReady) {
+ QObject::disconnect(d->m_sample, &QSample::error, d, &QSoundEffectPrivate::decoderError);
+ QObject::disconnect(d->m_sample, &QSample::ready, d, &QSoundEffectPrivate::sampleReady);
+ }
+ d->m_sample->release();
+ d->m_sample = nullptr;
+ }
+
+ if (d->m_audioOutput) {
+ QObject::disconnect(d->m_audioOutput, &QAudioOutput::stateChanged, d, &QSoundEffectPrivate::stateChanged);
+ d->m_audioOutput->stop();
+ d->m_audioOutput->deleteLater();
+ d->m_audioOutput = nullptr;
+ }
+
+ d->setStatus(QSoundEffect::Loading);
+ d->m_sample = sampleCache()->requestSample(url);
+ QObject::connect(d->m_sample, &QSample::error, d, &QSoundEffectPrivate::decoderError);
+ QObject::connect(d->m_sample, &QSample::ready, d, &QSoundEffectPrivate::sampleReady);
+
+ switch (d->m_sample->state()) {
+ case QSample::Ready:
+ d->sampleReady();
+ break;
+ case QSample::Error:
+ d->decoderError();
+ break;
+ default:
+ break;
+ }
emit sourceChanged();
}
@@ -213,7 +478,7 @@ void QSoundEffect::setSource(const QUrl &url)
*/
int QSoundEffect::loopCount() const
{
- return d->loopCount();
+ return d->m_loopCount;
}
/*!
@@ -240,10 +505,12 @@ void QSoundEffect::setLoopCount(int loopCount)
}
if (loopCount == 0)
loopCount = 1;
- if (d->loopCount() == loopCount)
+ if (d->m_loopCount == loopCount)
return;
- d->setLoopCount(loopCount);
+ d->m_loopCount = loopCount;
+ if (d->m_playing)
+ d->setLoopsRemaining(loopCount);
emit loopCountChanged();
}
@@ -261,7 +528,7 @@ void QSoundEffect::setLoopCount(int loopCount)
*/
int QSoundEffect::loopsRemaining() const
{
- return d->loopsRemaining();
+ return d->m_runningCount;
}
@@ -291,7 +558,10 @@ int QSoundEffect::loopsRemaining() const
*/
qreal QSoundEffect::volume() const
{
- return d->volume();
+ if (d->m_audioOutput && !d->m_muted)
+ return d->m_audioOutput->volume();
+
+ return d->m_volume;
}
/*!
@@ -309,10 +579,15 @@ qreal QSoundEffect::volume() const
void QSoundEffect::setVolume(qreal volume)
{
volume = qBound(qreal(0.0), volume, qreal(1.0));
- if (qFuzzyCompare(d->volume(), volume))
+ if (d->m_volume == volume)
return;
- d->setVolume(volume);
+ d->m_volume = volume;
+
+ if (d->m_audioOutput && !d->m_muted)
+ d->m_audioOutput->setVolume(volume);
+
+ emit volumeChanged();
}
/*!
@@ -330,7 +605,7 @@ void QSoundEffect::setVolume(qreal volume)
/*! Returns whether this sound effect is muted */
bool QSoundEffect::isMuted() const
{
- return d->isMuted();
+ return d->m_muted;
}
/*!
@@ -342,10 +617,16 @@ bool QSoundEffect::isMuted() const
*/
void QSoundEffect::setMuted(bool muted)
{
- if (d->isMuted() == muted)
+ if (d->m_muted == muted)
return;
- d->setMuted(muted);
+ if (muted && d->m_audioOutput)
+ d->m_audioOutput->setVolume(0);
+ else if (!muted && d->m_audioOutput && d->m_muted)
+ d->m_audioOutput->setVolume(d->m_volume);
+
+ d->m_muted = muted;
+ emit mutedChanged();
}
/*!
@@ -360,7 +641,7 @@ void QSoundEffect::setMuted(bool muted)
*/
bool QSoundEffect::isLoaded() const
{
- return d->isLoaded();
+ return d->m_status == QSoundEffect::Ready;
}
/*!
@@ -381,7 +662,18 @@ bool QSoundEffect::isLoaded() const
*/
void QSoundEffect::play()
{
- d->play();
+ d->m_offset = 0;
+ d->setLoopsRemaining(d->m_loopCount);
+#ifdef QT_QAUDIO_DEBUG
+ qDebug() << this << "play";
+#endif
+ if (d->m_status == QSoundEffect::Null || d->m_status == QSoundEffect::Error) {
+ d->setStatus(QSoundEffect::Null);
+ return;
+ }
+ d->setPlaying(true);
+ if (d->m_audioOutput && d->m_audioOutput->state() == QAudio::StoppedState && d->m_sampleReady)
+ d->m_audioOutput->start(d);
}
/*!
@@ -398,7 +690,7 @@ void QSoundEffect::play()
/*! Returns true if the sound effect is currently playing, or false otherwise */
bool QSoundEffect::isPlaying() const
{
- return d->isPlaying();
+ return d->m_playing;
}
/*!
@@ -438,7 +730,7 @@ bool QSoundEffect::isPlaying() const
*/
QSoundEffect::Status QSoundEffect::status() const
{
- return d->status();
+ return d->m_status;
}
/*!
@@ -479,7 +771,7 @@ QSoundEffect::Status QSoundEffect::status() const
*/
QString QSoundEffect::category() const
{
- return d->category();
+ return d->m_category;
}
/*!
@@ -500,7 +792,10 @@ QString QSoundEffect::category() const
*/
void QSoundEffect::setCategory(const QString &category)
{
- d->setCategory(category);
+ if (d->m_category != category && !d->m_playing) {
+ d->m_category = category;
+ emit categoryChanged();
+ }
}
@@ -518,7 +813,17 @@ void QSoundEffect::setCategory(const QString &category)
*/
void QSoundEffect::stop()
{
- d->stop();
+ if (!d->m_playing)
+ return;
+#ifdef QT_QAUDIO_DEBUG
+ qDebug() << "stop()";
+#endif
+ d->m_offset = 0;
+
+ d->setPlaying(false);
+
+ if (d->m_audioOutput)
+ d->m_audioOutput->stop();
}
/* Signals */
@@ -639,7 +944,6 @@ void QSoundEffect::stop()
The corresponding handler is \c onCategoryChanged.
*/
-
QT_END_NAMESPACE
#include "moc_qsoundeffect.cpp"
diff --git a/src/multimedia/audio/qsoundeffect_qaudio_p.cpp b/src/multimedia/audio/qsoundeffect_qaudio_p.cpp
deleted file mode 100644
index 90d195d3f..000000000
--- a/src/multimedia/audio/qsoundeffect_qaudio_p.cpp
+++ /dev/null
@@ -1,457 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// INTERNAL USE ONLY: Do NOT use for any other purpose.
-//
-
-#include "qsoundeffect_qaudio_p.h"
-
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qiodevice.h>
-
-//#include <QDebug>
-//#define QT_QAUDIO_DEBUG 1
-
-QT_BEGIN_NAMESPACE
-
-Q_GLOBAL_STATIC(QSampleCache, sampleCache)
-
-QSoundEffectPrivate::QSoundEffectPrivate(QObject *parent):
- QObject(parent),
- d(new PrivateSoundSource(this))
-{
-}
-
-QSoundEffectPrivate::QSoundEffectPrivate(const QAudioDeviceInfo &audioDevice, QObject *parent)
- : QObject(parent)
- , d(new PrivateSoundSource(this, audioDevice))
-{
-}
-
-QSoundEffectPrivate::~QSoundEffectPrivate()
-{
-}
-
-void QSoundEffectPrivate::release()
-{
- stop();
- if (d->m_audioOutput) {
- d->m_audioOutput->stop();
- d->m_audioOutput->deleteLater();
- d->m_sample->release();
- }
- delete d;
- this->deleteLater();
-}
-
-QStringList QSoundEffectPrivate::supportedMimeTypes()
-{
- // Only return supported mime types if we have a audio device available
- const QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
- if (devices.size() <= 0)
- return QStringList();
-
- return QStringList() << QLatin1String("audio/x-wav")
- << QLatin1String("audio/wav")
- << QLatin1String("audio/wave")
- << QLatin1String("audio/x-pn-wav");
-}
-
-QUrl QSoundEffectPrivate::source() const
-{
- return d->m_url;
-}
-
-void QSoundEffectPrivate::setSource(const QUrl &url)
-{
-#ifdef QT_QAUDIO_DEBUG
- qDebug() << this << "setSource current=" << d->m_url << ", to=" << url;
-#endif
- Q_ASSERT(d->m_url != url);
-
- stop();
-
- d->m_url = url;
-
- d->m_sampleReady = false;
-
- if (url.isEmpty()) {
- setStatus(QSoundEffect::Null);
- return;
- }
-
- if (!url.isValid()) {
- setStatus(QSoundEffect::Error);
- return;
- }
-
- if (d->m_sample) {
- if (!d->m_sampleReady) {
- disconnect(d->m_sample, &QSample::error, d, &PrivateSoundSource::decoderError);
- disconnect(d->m_sample, &QSample::ready, d, &PrivateSoundSource::sampleReady);
- }
- d->m_sample->release();
- d->m_sample = nullptr;
- }
-
- if (d->m_audioOutput) {
- disconnect(d->m_audioOutput, &QAudioOutput::stateChanged, d, &PrivateSoundSource::stateChanged);
- d->m_audioOutput->stop();
- d->m_audioOutput->deleteLater();
- d->m_audioOutput = nullptr;
- }
-
- setStatus(QSoundEffect::Loading);
- d->m_sample = sampleCache()->requestSample(url);
- connect(d->m_sample, &QSample::error, d, &PrivateSoundSource::decoderError);
- connect(d->m_sample, &QSample::ready, d, &PrivateSoundSource::sampleReady);
-
- switch (d->m_sample->state()) {
- case QSample::Ready:
- d->sampleReady();
- break;
- case QSample::Error:
- d->decoderError();
- break;
- default:
- break;
- }
-}
-
-int QSoundEffectPrivate::loopCount() const
-{
- return d->m_loopCount;
-}
-
-int QSoundEffectPrivate::loopsRemaining() const
-{
- return d->m_runningCount;
-}
-
-void QSoundEffectPrivate::setLoopCount(int loopCount)
-{
-#ifdef QT_QAUDIO_DEBUG
- qDebug() << "setLoopCount " << loopCount;
-#endif
- if (loopCount == 0)
- loopCount = 1;
- d->m_loopCount = loopCount;
- if (d->m_playing)
- setLoopsRemaining(loopCount);
-}
-
-qreal QSoundEffectPrivate::volume() const
-{
- if (d->m_audioOutput && !d->m_muted)
- return d->m_audioOutput->volume();
-
- return d->m_volume;
-}
-
-void QSoundEffectPrivate::setVolume(qreal volume)
-{
- d->m_volume = volume;
-
- if (d->m_audioOutput && !d->m_muted)
- d->m_audioOutput->setVolume(volume);
-
- emit volumeChanged();
-}
-
-bool QSoundEffectPrivate::isMuted() const
-{
- return d->m_muted;
-}
-
-void QSoundEffectPrivate::setMuted(bool muted)
-{
- if (muted && d->m_audioOutput)
- d->m_audioOutput->setVolume(0);
- else if (!muted && d->m_audioOutput && d->m_muted)
- d->m_audioOutput->setVolume(d->m_volume);
-
- d->m_muted = muted;
- emit mutedChanged();
-}
-
-bool QSoundEffectPrivate::isLoaded() const
-{
- return d->m_status == QSoundEffect::Ready;
-}
-
-
-bool QSoundEffectPrivate::isPlaying() const
-{
- return d->m_playing;
-}
-
-QSoundEffect::Status QSoundEffectPrivate::status() const
-{
- return d->m_status;
-}
-
-void QSoundEffectPrivate::play()
-{
- d->m_offset = 0;
- setLoopsRemaining(d->m_loopCount);
-#ifdef QT_QAUDIO_DEBUG
- qDebug() << this << "play";
-#endif
- if (d->m_status == QSoundEffect::Null || d->m_status == QSoundEffect::Error) {
- setStatus(QSoundEffect::Null);
- return;
- }
- setPlaying(true);
- if (d->m_audioOutput && d->m_audioOutput->state() == QAudio::StoppedState && d->m_sampleReady)
- d->m_audioOutput->start(d);
-}
-
-void QSoundEffectPrivate::stop()
-{
- if (!d->m_playing)
- return;
-#ifdef QT_QAUDIO_DEBUG
- qDebug() << "stop()";
-#endif
- d->m_offset = 0;
-
- setPlaying(false);
-
- if (d->m_audioOutput)
- d->m_audioOutput->stop();
-}
-
-void QSoundEffectPrivate::setStatus(QSoundEffect::Status status)
-{
-#ifdef QT_QAUDIO_DEBUG
- qDebug() << this << "setStatus" << status;
-#endif
- if (d->m_status == status)
- return;
- bool oldLoaded = isLoaded();
- d->m_status = status;
- emit statusChanged();
- if (oldLoaded != isLoaded())
- emit loadedChanged();
-}
-
-void QSoundEffectPrivate::setPlaying(bool playing)
-{
-#ifdef QT_QAUDIO_DEBUG
- qDebug() << this << "setPlaying(" << playing << ")";
-#endif
- if (d->m_playing == playing)
- return;
- d->m_playing = playing;
- emit playingChanged();
-}
-
-void QSoundEffectPrivate::setLoopsRemaining(int loopsRemaining)
-{
- if (d->m_runningCount == loopsRemaining)
- return;
-#ifdef QT_QAUDIO_DEBUG
- qDebug() << this << "setLoopsRemaining " << loopsRemaining;
-#endif
- d->m_runningCount = loopsRemaining;
- emit loopsRemainingChanged();
-}
-
-/* Categories are ignored */
-QString QSoundEffectPrivate::category() const
-{
- return d->m_category;
-}
-
-void QSoundEffectPrivate::setCategory(const QString &category)
-{
- if (d->m_category != category && !d->m_playing) {
- d->m_category = category;
- emit categoryChanged();
- }
-}
-
-PrivateSoundSource::PrivateSoundSource(QSoundEffectPrivate *s, const QAudioDeviceInfo &audioDevice)
- : QIODevice(s)
- , m_audioDevice(audioDevice)
-{
- soundeffect = s;
- m_category = QLatin1String("game");
- open(QIODevice::ReadOnly);
-}
-
-void PrivateSoundSource::sampleReady()
-{
- if (m_status == QSoundEffect::Error)
- return;
-
-#ifdef QT_QAUDIO_DEBUG
- qDebug() << this << "sampleReady "<<m_playing;
-#endif
- disconnect(m_sample, &QSample::error, this, &PrivateSoundSource::decoderError);
- disconnect(m_sample, &QSample::ready, this, &PrivateSoundSource::sampleReady);
- if (!m_audioOutput) {
- if (m_audioDevice.isNull())
- m_audioOutput = new QAudioOutput(m_sample->format());
- else
- m_audioOutput = new QAudioOutput(m_audioDevice, m_sample->format());
- connect(m_audioOutput, &QAudioOutput::stateChanged, this, &PrivateSoundSource::stateChanged);
- if (!m_muted)
- m_audioOutput->setVolume(m_volume);
- else
- m_audioOutput->setVolume(0);
- }
- m_sampleReady = true;
- soundeffect->setStatus(QSoundEffect::Ready);
-
- if (m_playing && m_audioOutput->state() == QAudio::StoppedState)
- m_audioOutput->start(this);
-}
-
-void PrivateSoundSource::decoderError()
-{
- qWarning("QSoundEffect(qaudio): Error decoding source %ls", qUtf16Printable(m_url.toString()));
- disconnect(m_sample, &QSample::ready, this, &PrivateSoundSource::sampleReady);
- disconnect(m_sample, &QSample::error, this, &PrivateSoundSource::decoderError);
- m_playing = false;
- soundeffect->setStatus(QSoundEffect::Error);
-}
-
-void PrivateSoundSource::stateChanged(QAudio::State state)
-{
-#ifdef QT_QAUDIO_DEBUG
- qDebug() << this << "stateChanged " << state;
-#endif
- if ((state == QAudio::IdleState && m_runningCount == 0)
- || (state == QAudio::StoppedState && m_audioOutput->error() != QAudio::NoError))
- emit soundeffect->stop();
-}
-
-qint64 PrivateSoundSource::readData(char *data, qint64 len)
-{
- if ((m_runningCount > 0 || m_runningCount == QSoundEffect::Infinite) && m_playing) {
-
- if (m_sample->state() != QSample::Ready)
- return 0;
-
- qint64 bytesWritten = 0;
-
- const int periodSize = m_audioOutput->periodSize();
- const int sampleSize = m_sample->data().size();
- const char* sampleData = m_sample->data().constData();
-
- // Some systems can have large buffers we only need a max of three
- int periodsFree = qMin(3, (int)(m_audioOutput->bytesFree()/periodSize));
- int dataOffset = 0;
-
-#ifdef QT_QAUDIO_DEBUG
- qDebug() << "bytesFree=" << m_audioOutput->bytesFree() << ", can fit " << periodsFree << " periodSize() chunks";
-#endif
-
- while ((periodsFree > 0) && (bytesWritten + periodSize <= len)) {
-
- if (sampleSize - m_offset >= periodSize) {
- // We can fit a whole period of data
- memcpy(data + dataOffset, sampleData + m_offset, periodSize);
- m_offset += periodSize;
- dataOffset += periodSize;
- bytesWritten += periodSize;
-#ifdef QT_QAUDIO_DEBUG
- qDebug() << "WHOLE PERIOD: bytesWritten=" << bytesWritten << ", offset=" << m_offset
- << ", filesize=" << sampleSize;
-#endif
- } else {
- // We are at end of sound, first write what is left of current sound
- memcpy(data + dataOffset, sampleData + m_offset, sampleSize - m_offset);
- bytesWritten += sampleSize - m_offset;
- int wrapLen = periodSize - (sampleSize - m_offset);
- if (wrapLen > sampleSize)
- wrapLen = sampleSize;
-#ifdef QT_QAUDIO_DEBUG
- qDebug() << "END OF SOUND: bytesWritten=" << bytesWritten << ", offset=" << m_offset
- << ", part1=" << (sampleSize-m_offset);
-#endif
- dataOffset += (sampleSize - m_offset);
- m_offset = 0;
-
- if (m_runningCount > 0 && m_runningCount != QSoundEffect::Infinite)
- soundeffect->setLoopsRemaining(m_runningCount-1);
-
- if (m_runningCount > 0 || m_runningCount == QSoundEffect::Infinite) {
- // There are still more loops of this sound to play, append the start of sound to make up full period
- memcpy(data + dataOffset, sampleData + m_offset, wrapLen);
- m_offset += wrapLen;
- dataOffset += wrapLen;
- bytesWritten += wrapLen;
-#ifdef QT_QAUDIO_DEBUG
- qDebug() << "APPEND START FOR FULL PERIOD: bytesWritten=" << bytesWritten << ", offset=" << m_offset
- << ", part2=" << wrapLen;
- qDebug() << "part1 + part2 should be a period " << periodSize;
-#endif
- }
- }
- if (m_runningCount == 0)
- break;
-
- periodsFree--;
- }
- return bytesWritten;
- }
-
- return 0;
-}
-
-qint64 PrivateSoundSource::writeData(const char *data, qint64 len)
-{
- Q_UNUSED(data);
- Q_UNUSED(len);
- return 0;
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qsoundeffect_qaudio_p.cpp"
diff --git a/src/multimedia/audio/qsoundeffect_qaudio_p.h b/src/multimedia/audio/qsoundeffect_qaudio_p.h
deleted file mode 100644
index a3a48f60d..000000000
--- a/src/multimedia/audio/qsoundeffect_qaudio_p.h
+++ /dev/null
@@ -1,151 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSOUNDEFFECT_QAUDIO_H
-#define QSOUNDEFFECT_QAUDIO_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/qobject.h>
-#include <QtCore/qurl.h>
-#include "qaudiooutput.h"
-#include "qsamplecache_p.h"
-#include "qsoundeffect.h"
-
-QT_BEGIN_NAMESPACE
-
-class QSoundEffectPrivate;
-
-class PrivateSoundSource : public QIODevice
-{
- friend class QSoundEffectPrivate;
- Q_OBJECT
-public:
- PrivateSoundSource(QSoundEffectPrivate *s, const QAudioDeviceInfo &audioDevice = QAudioDeviceInfo());
- ~PrivateSoundSource() {}
-
- qint64 readData(char *data, qint64 len) override;
- qint64 writeData(const char *data, qint64 len) override;
-
-private Q_SLOTS:
- void sampleReady();
- void decoderError();
- void stateChanged(QAudio::State);
-
-private:
- QUrl m_url;
- int m_loopCount = 1;
- int m_runningCount = 0;
- bool m_playing = false;
- QSoundEffect::Status m_status = QSoundEffect::Null;
- QAudioOutput *m_audioOutput = nullptr;
- QSample *m_sample = nullptr;
- bool m_muted = false;
- qreal m_volume = 1.0;
- bool m_sampleReady = false;
- qint64 m_offset = 0;
- QString m_category;
- QAudioDeviceInfo m_audioDevice;
- QSoundEffectPrivate *soundeffect = nullptr;
-};
-
-
-class QSoundEffectPrivate : public QObject
-{
- friend class PrivateSoundSource;
- Q_OBJECT
-public:
-
- explicit QSoundEffectPrivate(QObject *parent);
- explicit QSoundEffectPrivate(const QAudioDeviceInfo &audioDevice, QObject *parent);
- ~QSoundEffectPrivate();
-
- static QStringList supportedMimeTypes();
-
- QUrl source() const;
- void setSource(const QUrl &url);
- int loopCount() const;
- int loopsRemaining() const;
- void setLoopCount(int loopCount);
- qreal volume() const;
- void setVolume(qreal volume);
- bool isMuted() const;
- void setMuted(bool muted);
- bool isLoaded() const;
- bool isPlaying() const;
- QSoundEffect::Status status() const;
-
- void release();
-
- QString category() const;
- void setCategory(const QString &);
-
-public Q_SLOTS:
- void play();
- void stop();
-
-Q_SIGNALS:
- void loopsRemainingChanged();
- void volumeChanged();
- void mutedChanged();
- void loadedChanged();
- void playingChanged();
- void statusChanged();
- void categoryChanged();
-
-private:
- void setStatus(QSoundEffect::Status status);
- void setPlaying(bool playing);
- void setLoopsRemaining(int loopsRemaining);
-
- PrivateSoundSource *d = nullptr;
-};
-
-QT_END_NAMESPACE
-
-#endif // QSOUNDEFFECT_QAUDIO_H