summaryrefslogtreecommitdiffstats
path: root/src/multimedia/audio
diff options
context:
space:
mode:
Diffstat (limited to 'src/multimedia/audio')
-rw-r--r--src/multimedia/audio/audio.pri50
-rw-r--r--src/multimedia/audio/qaudio.h88
-rw-r--r--src/multimedia/audio/qaudiobuffer.cpp511
-rw-r--r--src/multimedia/audio/qaudiobuffer.h230
-rw-r--r--src/multimedia/audio/qaudiobuffer_p.h96
-rw-r--r--src/multimedia/audio/qaudiobufferinput.cpp184
-rw-r--r--src/multimedia/audio/qaudiobufferinput.h48
-rw-r--r--src/multimedia/audio/qaudiobufferoutput.cpp78
-rw-r--r--src/multimedia/audio/qaudiobufferoutput.h37
-rw-r--r--src/multimedia/audio/qaudiobufferoutput_p.h42
-rw-r--r--src/multimedia/audio/qaudiodecoder.cpp345
-rw-r--r--src/multimedia/audio/qaudiodecoder.h85
-rw-r--r--src/multimedia/audio/qaudiodecoder_p.h40
-rw-r--r--src/multimedia/audio/qaudiodevice.cpp373
-rw-r--r--src/multimedia/audio/qaudiodevice.h84
-rw-r--r--src/multimedia/audio/qaudiodevice_p.h62
-rw-r--r--src/multimedia/audio/qaudiodevicefactory.cpp259
-rw-r--r--src/multimedia/audio/qaudiodevicefactory_p.h91
-rw-r--r--src/multimedia/audio/qaudiodeviceinfo.cpp476
-rw-r--r--src/multimedia/audio/qaudiodeviceinfo.h109
-rw-r--r--src/multimedia/audio/qaudioformat.cpp550
-rw-r--r--src/multimedia/audio/qaudioformat.h214
-rw-r--r--src/multimedia/audio/qaudiohelpers.cpp137
-rw-r--r--src/multimedia/audio/qaudiohelpers_p.h41
-rw-r--r--src/multimedia/audio/qaudioinput.cpp461
-rw-r--r--src/multimedia/audio/qaudioinput.h113
-rw-r--r--src/multimedia/audio/qaudiooutput.cpp490
-rw-r--r--src/multimedia/audio/qaudiooutput.h117
-rw-r--r--src/multimedia/audio/qaudioprobe.cpp205
-rw-r--r--src/multimedia/audio/qaudioprobe.h74
-rw-r--r--src/multimedia/audio/qaudiosink.cpp339
-rw-r--r--src/multimedia/audio/qaudiosink.h74
-rw-r--r--src/multimedia/audio/qaudiosource.cpp369
-rw-r--r--src/multimedia/audio/qaudiosource.h72
-rw-r--r--src/multimedia/audio/qaudiostatemachine.cpp150
-rw-r--r--src/multimedia/audio/qaudiostatemachine_p.h149
-rw-r--r--src/multimedia/audio/qaudiostatemachineutils_p.h96
-rw-r--r--src/multimedia/audio/qaudiosystem.cpp379
-rw-r--r--src/multimedia/audio/qaudiosystem.h138
-rw-r--r--src/multimedia/audio/qaudiosystem_p.h96
-rw-r--r--src/multimedia/audio/qaudiosystemplugin.cpp138
-rw-r--r--src/multimedia/audio/qaudiosystemplugin.h86
-rw-r--r--src/multimedia/audio/qaudiosystempluginext_p.h71
-rw-r--r--src/multimedia/audio/qsamplecache_p.cpp145
-rw-r--r--src/multimedia/audio/qsamplecache_p.h47
-rw-r--r--src/multimedia/audio/qsound.cpp244
-rw-r--r--src/multimedia/audio/qsound.h85
-rw-r--r--src/multimedia/audio/qsoundeffect.cpp574
-rw-r--r--src/multimedia/audio/qsoundeffect.h65
-rw-r--r--src/multimedia/audio/qsoundeffect_pulse_p.cpp1214
-rw-r--r--src/multimedia/audio/qsoundeffect_pulse_p.h188
-rw-r--r--src/multimedia/audio/qsoundeffect_qaudio_p.cpp457
-rw-r--r--src/multimedia/audio/qsoundeffect_qaudio_p.h151
-rw-r--r--src/multimedia/audio/qtaudio.cpp (renamed from src/multimedia/audio/qaudio.cpp)198
-rw-r--r--src/multimedia/audio/qtaudio.h18
-rw-r--r--src/multimedia/audio/qwavedecoder.cpp507
-rw-r--r--src/multimedia/audio/qwavedecoder.h115
-rw-r--r--src/multimedia/audio/qwavedecoder_p.cpp312
-rw-r--r--src/multimedia/audio/qwavedecoder_p.h133
59 files changed, 4539 insertions, 7761 deletions
diff --git a/src/multimedia/audio/audio.pri b/src/multimedia/audio/audio.pri
deleted file mode 100644
index 388124205..000000000
--- a/src/multimedia/audio/audio.pri
+++ /dev/null
@@ -1,50 +0,0 @@
-INCLUDEPATH += audio
-
-PUBLIC_HEADERS += \
- audio/qaudio.h \
- audio/qaudiobuffer.h \
- audio/qaudioformat.h \
- audio/qaudioinput.h \
- audio/qaudiooutput.h \
- audio/qaudiodeviceinfo.h \
- audio/qaudiosystemplugin.h \
- audio/qaudiosystem.h \
- audio/qsoundeffect.h \
- audio/qsound.h \
- audio/qaudioprobe.h \
- audio/qaudiodecoder.h
-
-PRIVATE_HEADERS += \
- audio/qaudiobuffer_p.h \
- audio/qaudiodevicefactory_p.h \
- audio/qwavedecoder_p.h \
- audio/qsamplecache_p.h \
- audio/qaudiohelpers_p.h \
- audio/qaudiosystempluginext_p.h
-
-SOURCES += \
- audio/qaudio.cpp \
- audio/qaudioformat.cpp \
- audio/qaudiodeviceinfo.cpp \
- audio/qaudiooutput.cpp \
- audio/qaudioinput.cpp \
- audio/qaudiosystemplugin.cpp \
- audio/qaudiosystem.cpp \
- audio/qaudiodevicefactory.cpp \
- audio/qsoundeffect.cpp \
- audio/qwavedecoder_p.cpp \
- audio/qsamplecache_p.cpp \
- audio/qsound.cpp \
- audio/qaudiobuffer.cpp \
- audio/qaudioprobe.cpp \
- audio/qaudiodecoder.cpp \
- audio/qaudiohelpers.cpp
-
-qtConfig(pulseaudio) {
- QMAKE_USE_FOR_PRIVATE += pulseaudio
- PRIVATE_HEADERS += audio/qsoundeffect_pulse_p.h
- SOURCES += audio/qsoundeffect_pulse_p.cpp
-} else {
- PRIVATE_HEADERS += audio/qsoundeffect_qaudio_p.h
- SOURCES += audio/qsoundeffect_qaudio_p.cpp
-}
diff --git a/src/multimedia/audio/qaudio.h b/src/multimedia/audio/qaudio.h
index 90a8c236f..5ae994b9f 100644
--- a/src/multimedia/audio/qaudio.h
+++ b/src/multimedia/audio/qaudio.h
@@ -1,48 +1,14 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIO_H
#define QAUDIO_H
+#if 0
+#pragma qt_class(QAudio)
+#endif
+
#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qmultimedia.h>
#include <QtCore/qmetatype.h>
@@ -52,25 +18,15 @@ QT_BEGIN_NAMESPACE
// Class forward declaration required for QDoc bug
class QString;
+
+#if defined(Q_QDOC)
+namespace QtAudio
+#else
namespace QAudio
+#endif
{
enum Error { NoError, OpenError, IOError, UnderrunError, FatalError };
- enum State { ActiveState, SuspendedState, StoppedState, IdleState, InterruptedState };
- enum Mode { AudioInput, AudioOutput };
-
- enum Role {
- UnknownRole,
- MusicRole,
- VideoRole,
- VoiceCommunicationRole,
- AlarmRole,
- NotificationRole,
- RingtoneRole,
- AccessibilityRole,
- SonificationRole,
- GameRole,
- CustomRole
- };
+ enum State { ActiveState, SuspendedState, StoppedState, IdleState };
enum VolumeScale {
LinearVolumeScale,
@@ -79,23 +35,19 @@ namespace QAudio
DecibelVolumeScale
};
- Q_MULTIMEDIA_EXPORT qreal convertVolume(qreal volume, VolumeScale from, VolumeScale to);
+ Q_MULTIMEDIA_EXPORT float convertVolume(float volume, VolumeScale from, VolumeScale to);
}
+#if !defined(Q_QDOC)
+namespace QtAudio = QAudio;
+#endif
+
#ifndef QT_NO_DEBUG_STREAM
-Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QAudio::Error error);
-Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QAudio::State state);
-Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QAudio::Mode mode);
-Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QAudio::Role role);
-Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QAudio::VolumeScale role);
+Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QtAudio::Error error);
+Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QtAudio::State state);
+Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QtAudio::VolumeScale role);
#endif
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QAudio::Error)
-Q_DECLARE_METATYPE(QAudio::State)
-Q_DECLARE_METATYPE(QAudio::Mode)
-Q_DECLARE_METATYPE(QAudio::Role)
-Q_DECLARE_METATYPE(QAudio::VolumeScale)
-
#endif // QAUDIO_H
diff --git a/src/multimedia/audio/qaudiobuffer.cpp b/src/multimedia/audio/qaudiobuffer.cpp
index 999f280b3..69adcc5b7 100644
--- a/src/multimedia/audio/qaudiobuffer.cpp
+++ b/src/multimedia/audio/qaudiobuffer.cpp
@@ -1,185 +1,27 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qaudiobuffer.h"
-#include "qaudiobuffer_p.h"
#include <QObject>
#include <QDebug>
QT_BEGIN_NAMESPACE
-
-static void qRegisterAudioBufferMetaTypes()
-{
- qRegisterMetaType<QAudioBuffer>();
-}
-
-Q_CONSTRUCTOR_FUNCTION(qRegisterAudioBufferMetaTypes)
-
-
class QAudioBufferPrivate : public QSharedData
{
public:
- QAudioBufferPrivate(QAbstractAudioBuffer *provider)
- : mProvider(provider)
- , mCount(1)
- {
- }
-
- ~QAudioBufferPrivate()
- {
- if (mProvider)
- mProvider->release();
- }
-
- void ref()
- {
- mCount.ref();
- }
-
- void deref()
- {
- if (!mCount.deref())
- delete this;
- }
-
- QAudioBufferPrivate *clone();
-
- static QAudioBufferPrivate *acquire(QAudioBufferPrivate *other)
- {
- if (!other)
- return nullptr;
-
- // Ref the other (if there are extant data() pointers, they will
- // also point here - it's a feature, not a bug, like QByteArray)
- other->ref();
- return other;
- }
-
- QAbstractAudioBuffer *mProvider;
- QAtomicInt mCount;
-};
-
-// Private class to go in .cpp file
-class QMemoryAudioBufferProvider : public QAbstractAudioBuffer {
-public:
- QMemoryAudioBufferProvider(const void *data, int frameCount, const QAudioFormat &format, qint64 startTime)
- : mStartTime(startTime)
- , mFrameCount(frameCount)
- , mFormat(format)
- {
- int numBytes = format.bytesForFrames(frameCount);
- if (numBytes > 0) {
- mBuffer = malloc(numBytes);
- if (!mBuffer) {
- // OOM, if that's likely
- mStartTime = -1;
- mFrameCount = 0;
- mFormat = QAudioFormat();
- } else {
- // Allocated, see if we have data to copy
- if (data) {
- memcpy(mBuffer, data, numBytes);
- } else {
- // We have to fill with the zero value..
- switch (format.sampleType()) {
- case QAudioFormat::SignedInt:
- // Signed int means 0x80, 0x8000 is zero
- // XXX this is not right for > 8 bits(0x8080 vs 0x8000)
- memset(mBuffer, 0x80, numBytes);
- break;
- default:
- memset(mBuffer, 0x0, numBytes);
- }
- }
- }
- } else
- mBuffer = nullptr;
- }
-
- ~QMemoryAudioBufferProvider()
+ QAudioBufferPrivate(const QAudioFormat &f, const QByteArray &d, qint64 start)
+ : format(f), data(d), startTime(start)
{
- if (mBuffer)
- free(mBuffer);
}
- void release() override {delete this;}
- QAudioFormat format() const override {return mFormat;}
- qint64 startTime() const override {return mStartTime;}
- int frameCount() const override {return mFrameCount;}
-
- void *constData() const override {return mBuffer;}
-
- void *writableData() override {return mBuffer;}
- QAbstractAudioBuffer *clone() const override
- {
- return new QMemoryAudioBufferProvider(mBuffer, mFrameCount, mFormat, mStartTime);
- }
-
- void *mBuffer;
- qint64 mStartTime;
- int mFrameCount;
- QAudioFormat mFormat;
+ QAudioFormat format;
+ QByteArray data;
+ qint64 startTime;
};
-QAudioBufferPrivate *QAudioBufferPrivate::clone()
-{
- // We want to create a single bufferprivate with a
- // single qaab
- // This should only be called when the count is > 1
- Q_ASSERT(mCount.loadRelaxed() > 1);
-
- if (mProvider) {
- QAbstractAudioBuffer *abuf = mProvider->clone();
-
- if (!abuf) {
- abuf = new QMemoryAudioBufferProvider(mProvider->constData(), mProvider->frameCount(), mProvider->format(), mProvider->startTime());
- }
-
- if (abuf) {
- return new QAudioBufferPrivate(abuf);
- }
- }
-
- return nullptr;
-}
+QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QAudioBufferPrivate);
/*!
\class QAbstractAudioBuffer
@@ -191,38 +33,29 @@ QAudioBufferPrivate *QAudioBufferPrivate::clone()
\inmodule QtMultimedia
\ingroup multimedia
\ingroup multimedia_audio
- \brief The QAudioBuffer class represents a collection of audio samples with a specific format and sample rate.
+ \brief The QAudioBuffer class represents a collection of audio samples with a specific format
+ and sample rate.
+
+ QAudioBuffer is used by the QAudioDecoder class to hand decoded audio data over to the
+ application. An audio buffer contains data in a certain QAudioFormat that can be queried using
+ format(). It is also tagged with timing and duration information.
+
+ To access the data stored inside the buffer, use the data() or constData() methods.
+
+ Audio buffers are explicitly shared, in most cases, you should call detach() before
+ modifying the data.
*/
-// ^ Mostly useful with probe or decoder
/*!
Create a new, empty, invalid buffer.
*/
-QAudioBuffer::QAudioBuffer()
- : d(nullptr)
-{
-}
+QAudioBuffer::QAudioBuffer() noexcept = default;
/*!
- \internal
- Create a new audio buffer from the supplied \a provider. This
- constructor is typically only used when handling certain hardware
- or media framework specific buffers, and generally isn't useful
- in application code.
+ Creates a new audio buffer from \a other. Audio buffers are explicitly shared,
+ you should call detach() on the buffer to make a copy that can then be modified.
*/
-QAudioBuffer::QAudioBuffer(QAbstractAudioBuffer *provider)
- : d(new QAudioBufferPrivate(provider))
-{
-}
-/*!
- Creates a new audio buffer from \a other. Generally
- this will have copy-on-write semantics - a copy will
- only be made when it has to be.
- */
-QAudioBuffer::QAudioBuffer(const QAudioBuffer &other)
-{
- d = QAudioBufferPrivate::acquire(other.d);
-}
+QAudioBuffer::QAudioBuffer(const QAudioBuffer &other) noexcept = default;
/*!
Creates a new audio buffer from the supplied \a data, in the
@@ -240,11 +73,9 @@ QAudioBuffer::QAudioBuffer(const QAudioBuffer &other)
*/
QAudioBuffer::QAudioBuffer(const QByteArray &data, const QAudioFormat &format, qint64 startTime)
{
- if (format.isValid()) {
- int frameCount = format.framesForBytes(data.size());
- d = new QAudioBufferPrivate(new QMemoryAudioBufferProvider(data.constData(), frameCount, format, startTime));
- } else
- d = nullptr;
+ if (!format.isValid() || !data.size())
+ return;
+ d = new QAudioBufferPrivate(format, data, startTime);
}
/*!
@@ -258,43 +89,55 @@ QAudioBuffer::QAudioBuffer(const QByteArray &data, const QAudioFormat &format, q
*/
QAudioBuffer::QAudioBuffer(int numFrames, const QAudioFormat &format, qint64 startTime)
{
- if (format.isValid())
- d = new QAudioBufferPrivate(new QMemoryAudioBufferProvider(nullptr, numFrames, format, startTime));
- else
- d = nullptr;
+ if (!format.isValid() || !numFrames)
+ return;
+
+ QByteArray data(format.bytesForFrames(numFrames), '\0');
+ d = new QAudioBufferPrivate(format, data, startTime);
}
/*!
+ \fn QAudioBuffer::QAudioBuffer(QAudioBuffer &&other)
+
+ Constructs a QAudioBuffer by moving from \a other.
+*/
+
+/*!
+ \fn void QAudioBuffer::swap(QAudioBuffer &other) noexcept
+
+ Swaps the audio buffer with \a other.
+*/
+
+/*!
+ \fn QAudioBuffer &QAudioBuffer::operator=(QAudioBuffer &&other)
+
+ Moves \a other into this QAudioBuffer.
+*/
+
+/*!
Assigns the \a other buffer to this.
*/
-QAudioBuffer &QAudioBuffer::operator =(const QAudioBuffer &other)
-{
- if (this->d != other.d) {
- if (d)
- d->deref();
- d = QAudioBufferPrivate::acquire(other.d);
- }
- return *this;
-}
+QAudioBuffer &QAudioBuffer::operator=(const QAudioBuffer &other) = default;
/*!
Destroys this audio buffer.
*/
-QAudioBuffer::~QAudioBuffer()
-{
- if (d)
- d->deref();
-}
+QAudioBuffer::~QAudioBuffer() = default;
+
+/*! \fn bool QAudioBuffer::isValid() const noexcept
-/*!
Returns true if this is a valid buffer. A valid buffer
has more than zero frames in it and a valid format.
*/
-bool QAudioBuffer::isValid() const
+
+/*!
+ Detaches this audio buffers from other copies that might share data with it.
+*/
+void QAudioBuffer::detach()
{
- if (!d || !d->mProvider)
- return false;
- return d->mProvider->format().isValid() && (d->mProvider->frameCount() > 0);
+ if (!d)
+ return;
+ d = new QAudioBufferPrivate(*d);
}
/*!
@@ -304,11 +147,11 @@ bool QAudioBuffer::isValid() const
the \l duration() or \l byteCount() are calculated
from the \l frameCount().
*/
-QAudioFormat QAudioBuffer::format() const
+QAudioFormat QAudioBuffer::format() const noexcept
{
- if (!isValid())
+ if (!d)
return QAudioFormat();
- return d->mProvider->format();
+ return d->format;
}
/*!
@@ -317,11 +160,11 @@ QAudioFormat QAudioBuffer::format() const
An audio frame is an interleaved set of one sample per channel
for the same instant in time.
*/
-int QAudioBuffer::frameCount() const
+qsizetype QAudioBuffer::frameCount() const noexcept
{
- if (!isValid())
+ if (!d)
return 0;
- return d->mProvider->frameCount();
+ return d->format.framesForBytes(d->data.size());
}
/*!
@@ -335,21 +178,17 @@ int QAudioBuffer::frameCount() const
\sa frameCount()
*/
-int QAudioBuffer::sampleCount() const
+qsizetype QAudioBuffer::sampleCount() const noexcept
{
- if (!isValid())
- return 0;
-
return frameCount() * format().channelCount();
}
/*!
Returns the size of this buffer, in bytes.
*/
-int QAudioBuffer::byteCount() const
+qsizetype QAudioBuffer::byteCount() const noexcept
{
- const QAudioFormat f(format());
- return format().bytesForFrames(frameCount());
+ return d ? d->data.size() : 0;
}
/*!
@@ -357,7 +196,7 @@ int QAudioBuffer::byteCount() const
This depends on the \l format(), and the \l frameCount().
*/
-qint64 QAudioBuffer::duration() const
+qint64 QAudioBuffer::duration() const noexcept
{
return format().durationForFrames(frameCount());
}
@@ -367,23 +206,23 @@ qint64 QAudioBuffer::duration() const
If this buffer is not part of a stream, this will return -1.
*/
-qint64 QAudioBuffer::startTime() const
+qint64 QAudioBuffer::startTime() const noexcept
{
- if (!isValid())
+ if (!d)
return -1;
- return d->mProvider->startTime();
+ return d->startTime;
}
/*!
+ \fn template <typename T> const T* QAudioBuffer::constData() const
+
Returns a pointer to this buffer's data. You can only read it.
This method is preferred over the const version of \l data() to
prevent unnecessary copying.
- There is also a templatized version of this constData() function that
- allows you to retrieve a specific type of read-only pointer to
- the data. Note that there is no checking done on the format of
- the audio buffer - this is simply a convenience function.
+ Note that there is no checking done on the format of the audio
+ buffer - this is simply a convenience function.
\code
// With a 16bit sample buffer:
@@ -391,22 +230,23 @@ qint64 QAudioBuffer::startTime() const
\endcode
*/
-const void* QAudioBuffer::constData() const
+
+const void *QAudioBuffer::constData() const noexcept
{
- if (!isValid())
+ if (!d)
return nullptr;
- return d->mProvider->constData();
+ return d->data.constData();
}
/*!
+ \fn template <typename T> const T* QAudioBuffer::data() const
+
Returns a pointer to this buffer's data. You can only read it.
You should use the \l constData() function rather than this
to prevent accidental deep copying.
- There is also a templatized version of this data() function that
- allows you to retrieve a specific type of read-only pointer to
- the data. Note that there is no checking done on the format of
+ Note that there is no checking done on the format of
the audio buffer - this is simply a convenience function.
\code
@@ -414,192 +254,73 @@ const void* QAudioBuffer::constData() const
const quint16 *data = buffer->data<quint16>();
\endcode
*/
-const void* QAudioBuffer::data() const
+
+const void *QAudioBuffer::data() const noexcept
{
- if (!isValid())
+ if (!d)
return nullptr;
- return d->mProvider->constData();
+ return d->data.constData();
}
-
-/*
- Template data/constData functions caused override problems with qdoc,
- so moved their docs into the non template versions.
-*/
-
/*!
+ \fn template <typename T> T* QAudioBuffer::data()
+
Returns a pointer to this buffer's data. You can modify the
data through the returned pointer.
- Since QAudioBuffers can share the actual sample data, calling
- this function will result in a deep copy being made if there
- are any other buffers using the sample. You should avoid calling
- this unless you really need to modify the data.
-
- This pointer will remain valid until the underlying storage is
- detached. In particular, if you obtain a pointer, and then
- copy this audio buffer, changing data through this pointer may
- change both buffer instances. Calling \l data() on either instance
- will again cause a deep copy to be made, which may invalidate
- the pointers returned from this function previously.
+ Since QAudioBuffer objects are explicitly shared, you should usually
+ call detach() before modifying the data through this function.
- There is also a templatized version of data() allows you to retrieve
- a specific type of pointer to the data. Note that there is no
- checking done on the format of the audio buffer - this is
- simply a convenience function.
+ Note that there is no checking done on the format of the audio
+ buffer - this is simply a convenience function.
\code
// With a 16bit sample buffer:
quint16 *data = buffer->data<quint16>(); // May cause deep copy
\endcode
*/
+
void *QAudioBuffer::data()
{
- if (!isValid())
+ if (!d)
return nullptr;
-
- if (d->mCount.loadRelaxed() != 1) {
- // Can't share a writable buffer
- // so we need to detach
- QAudioBufferPrivate *newd = d->clone();
-
- // This shouldn't happen
- if (!newd)
- return nullptr;
-
- d->deref();
- d = newd;
- }
-
- // We're (now) the only user of this qaab, so
- // see if it's writable directly
- void *buffer = d->mProvider->writableData();
- if (buffer) {
- return buffer;
- }
-
- // Wasn't writable, so turn it into a memory provider
- QAbstractAudioBuffer *memBuffer = new QMemoryAudioBufferProvider(constData(), frameCount(), format(), startTime());
-
- if (memBuffer) {
- d->mProvider->release();
- d->mCount.storeRelaxed(1);
- d->mProvider = memBuffer;
-
- return memBuffer->writableData();
- }
-
- return nullptr;
+ return d->data.data();
}
-// Template helper classes worth documenting
-
/*!
- \class QAudioBuffer::StereoFrameDefault
- \internal
-
- Just a trait class for the default value.
-*/
-
-/*!
- \class QAudioBuffer::StereoFrame
- \brief The StereoFrame class provides a simple wrapper for a stereo audio frame.
- \inmodule QtMultimedia
- \ingroup multimedia
- \ingroup multimedia_audio
-
- This templatized structure lets you treat a block of individual samples as an
- interleaved stereo stream frame. This is most useful when used with the templatized
- \l {QAudioBuffer::data()}{data()} functions of QAudioBuffer. Generally the data
- is accessed as a pointer, so no copying should occur.
-
- There are some predefined instantiations of this template for working with common
- stereo sample depths in a convenient way.
-
- This frame structure has \e left and \e right members for accessing individual channel data.
-
- For example:
- \code
- // Assuming 'buffer' is an unsigned 16 bit stereo buffer..
- QAudioBuffer::S16U *frames = buffer->data<QAudioBuffer::S16U>();
- for (int i=0; i < buffer->frameCount(); i++) {
- qSwap(frames[i].left, frames[i].right);
- }
- \endcode
+ \typedef QAudioBuffer::S16S
- \sa QAudioBuffer::S8U, QAudioBuffer::S8S, QAudioBuffer::S16S, QAudioBuffer::S16U, QAudioBuffer::S32F
+ This is a predefined specialization for a signed stereo 16 bit sample. Each
+ channel is a \e {signed short}.
*/
/*!
- \fn template <typename T> QAudioBuffer::StereoFrame<T>::StereoFrame()
+ \typedef QAudioBuffer::U8M
- Constructs a new frame with the "silent" value for this
- sample format (0 for signed formats and floats, 0x8* for unsigned formats).
+ This is a predefined specialization for an unsigned 8 bit mono sample.
*/
-
/*!
- \fn template <typename T> QAudioBuffer::StereoFrame<T>::StereoFrame(T leftSample, T rightSample)
-
- Constructs a new frame with the supplied \a leftSample and \a rightSample values.
-*/
-
+ \typedef QAudioBuffer::S16M
+ This is a predefined specialization for a signed 16 bit mono sample.
+i*/
/*!
- \fn template <typename T> QAudioBuffer::StereoFrame<T>::operator=(const StereoFrame &other)
-
- Assigns \a other to this frame.
- */
-
-
-/*!
- \fn template <typename T> QAudioBuffer::StereoFrame<T>::average() const
-
- Returns the arithmetic average of the left and right samples.
- */
-
-/*! \fn template <typename T> QAudioBuffer::StereoFrame<T>::clear()
-
- Sets the values of this frame to the "silent" value.
-*/
-
-/*!
- \variable QAudioBuffer::StereoFrame::left
- \brief the left sample
-*/
-
-/*!
- \variable QAudioBuffer::StereoFrame::right
- \brief the right sample
-*/
-
-/*!
- \typedef QAudioBuffer::S8U
-
- This is a predefined specialization for an unsigned stereo 8 bit sample. Each
- channel is an \e {unsigned char}.
+ \typedef QAudioBuffer::S32M
+ This is a predefined specialization for a signed 32 bit mono sample.
*/
/*!
- \typedef QAudioBuffer::S8S
-
- This is a predefined specialization for a signed stereo 8 bit sample. Each
- channel is a \e {signed char}.
+ \typedef QAudioBuffer::F32M
+ This is a predefined specialization for a 32 bit float mono sample.
*/
/*!
- \typedef QAudioBuffer::S16U
-
- This is a predefined specialization for an unsigned stereo 16 bit sample. Each
- channel is an \e {unsigned short}.
+ \typedef QAudioBuffer::U8S
+ This is a predifined specialization for an unsiged 8 bit stereo sample.
*/
/*!
- \typedef QAudioBuffer::S16S
-
- This is a predefined specialization for a signed stereo 16 bit sample. Each
- channel is a \e {signed short}.
+ \typedef QAudioBuffer::S32S
+ This is a predifined specialization for a siged 32 bit stereo sample.
*/
/*!
- \typedef QAudioBuffer::S32F
-
- This is a predefined specialization for an 32 bit float sample. Each
- channel is a \e float.
+ \typedef QAudioBuffer::F32S
+ This is a predifined specialization for a 32 bit float stereo sample.
*/
-
QT_END_NAMESPACE
diff --git a/src/multimedia/audio/qaudiobuffer.h b/src/multimedia/audio/qaudiobuffer.h
index bed387462..822619f31 100644
--- a/src/multimedia/audio/qaudiobuffer.h
+++ b/src/multimedia/audio/qaudiobuffer.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIOBUFFER_H
#define QAUDIOBUFFER_H
@@ -43,89 +7,149 @@
#include <QtCore/qshareddata.h>
#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qmultimedia.h>
-#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qtaudio.h>
#include <QtMultimedia/qaudioformat.h>
QT_BEGIN_NAMESPACE
-class QAbstractAudioBuffer;
-class QAudioBufferPrivate;
-class Q_MULTIMEDIA_EXPORT QAudioBuffer
+namespace QtPrivate {
+template <QAudioFormat::SampleFormat> struct AudioSampleFormatHelper
+{
+};
+
+template <> struct AudioSampleFormatHelper<QAudioFormat::UInt8>
+{
+ using value_type = unsigned char;
+ static constexpr value_type Default = 128;
+};
+
+template <> struct AudioSampleFormatHelper<QAudioFormat::Int16>
+{
+ using value_type = short;
+ static constexpr value_type Default = 0;
+};
+
+template <> struct AudioSampleFormatHelper<QAudioFormat::Int32>
{
+ using value_type = int;
+ static constexpr value_type Default = 0;
+};
+
+template <> struct AudioSampleFormatHelper<QAudioFormat::Float>
+{
+ using value_type = float;
+ static constexpr value_type Default = 0.;
+};
+
+}
+
+template <QAudioFormat::ChannelConfig config, QAudioFormat::SampleFormat format>
+struct QAudioFrame
+{
+private:
+ // popcount in qalgorithms.h is unfortunately not constexpr on MSVC.
+ // Use this here as a fallback
+ static constexpr int constexprPopcount(quint32 i)
+ {
+ i = i - ((i >> 1) & 0x55555555); // add pairs of bits
+ i = (i & 0x33333333) + ((i >> 2) & 0x33333333); // quads
+ i = (i + (i >> 4)) & 0x0F0F0F0F; // groups of 8
+ return (i * 0x01010101) >> 24; // horizontal sum of bytes
+ }
+ static constexpr int nChannels = constexprPopcount(config);
public:
- QAudioBuffer();
- QAudioBuffer(QAbstractAudioBuffer *provider);
- QAudioBuffer(const QAudioBuffer &other);
- QAudioBuffer(const QByteArray &data, const QAudioFormat &format, qint64 startTime = -1);
- QAudioBuffer(int numFrames, const QAudioFormat &format, qint64 startTime = -1); // Initialized to empty
+ using value_type = typename QtPrivate::AudioSampleFormatHelper<format>::value_type;
+ value_type channels[nChannels];
+ static constexpr int positionToIndex(QAudioFormat::AudioChannelPosition pos)
+ {
+ if (!(config & (1u << pos)))
+ return -1;
+
+ uint maskedChannels = config & ((1u << pos) - 1);
+ return qPopulationCount(maskedChannels);
+ }
- QAudioBuffer& operator=(const QAudioBuffer &other);
- ~QAudioBuffer();
+ value_type value(QAudioFormat::AudioChannelPosition pos) const {
+ int idx = positionToIndex(pos);
+ if (idx < 0)
+ return QtPrivate::AudioSampleFormatHelper<format>::Default;
+ return channels[idx];
+ }
+ void setValue(QAudioFormat::AudioChannelPosition pos, value_type val) {
+ int idx = positionToIndex(pos);
+ if (idx < 0)
+ return;
+ channels[idx] = val;
+ }
+ value_type operator[](QAudioFormat::AudioChannelPosition pos) const {
+ return value(pos);
+ }
+ constexpr void clear() {
+ for (int i = 0; i < nChannels; ++i)
+ channels[i] = QtPrivate::AudioSampleFormatHelper<format>::Default;
+ }
+};
+
+template <QAudioFormat::SampleFormat Format>
+using QAudioFrameMono = QAudioFrame<QAudioFormat::ChannelConfigMono, Format>;
+
+template <QAudioFormat::SampleFormat Format>
+using QAudioFrameStereo = QAudioFrame<QAudioFormat::ChannelConfigStereo, Format>;
+
+template <QAudioFormat::SampleFormat Format>
+using QAudioFrame2Dot1 = QAudioFrame<QAudioFormat::ChannelConfig2Dot1, Format>;
- bool isValid() const;
+template <QAudioFormat::SampleFormat Format>
+using QAudioFrameSurround5Dot1 = QAudioFrame<QAudioFormat::ChannelConfigSurround5Dot1, Format>;
- QAudioFormat format() const;
+template <QAudioFormat::SampleFormat Format>
+using QAudioFrameSurround7Dot1 = QAudioFrame<QAudioFormat::ChannelConfigSurround7Dot1, Format>;
- int frameCount() const;
- int sampleCount() const;
- int byteCount() const;
- qint64 duration() const;
- qint64 startTime() const;
+class QAudioBufferPrivate;
+QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QAudioBufferPrivate, Q_MULTIMEDIA_EXPORT)
- // Data modification
- // void clear();
- // Other ideas
- // operator *=
- // operator += (need to be careful about different formats)
+class Q_MULTIMEDIA_EXPORT QAudioBuffer
+{
+public:
+ QAudioBuffer() noexcept;
+ QAudioBuffer(const QAudioBuffer &other) noexcept;
+ QAudioBuffer(const QByteArray &data, const QAudioFormat &format, qint64 startTime = -1);
+ QAudioBuffer(int numFrames, const QAudioFormat &format, qint64 startTime = -1); // Initialized to empty
+ ~QAudioBuffer();
- // Data access
- const void* constData() const; // Does not detach, preferred
- const void* data() const; // Does not detach
- void *data(); // detaches
+ QAudioBuffer& operator=(const QAudioBuffer &other);
- // Structures for easier access to stereo data
- template <typename T> struct StereoFrameDefault { enum { Default = 0 }; };
+ QAudioBuffer(QAudioBuffer &&other) noexcept = default;
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QAudioBuffer)
+ void swap(QAudioBuffer &other) noexcept
+ { d.swap(other.d); }
- template <typename T> struct StereoFrame {
+ bool isValid() const noexcept { return d != nullptr; };
- StereoFrame()
- : left(T(StereoFrameDefault<T>::Default))
- , right(T(StereoFrameDefault<T>::Default))
- {
- }
+ void detach();
- StereoFrame(T leftSample, T rightSample)
- : left(leftSample)
- , right(rightSample)
- {
- }
+ QAudioFormat format() const noexcept;
- StereoFrame& operator=(const StereoFrame &other)
- {
- // Two straight assigns is probably
- // cheaper than a conditional check on
- // self assignment
- left = other.left;
- right = other.right;
- return *this;
- }
+ qsizetype frameCount() const noexcept;
+ qsizetype sampleCount() const noexcept;
+ qsizetype byteCount() const noexcept;
- T left;
- T right;
+ qint64 duration() const noexcept;
+ qint64 startTime() const noexcept;
- T average() const {return (left + right) / 2;}
- void clear() {left = right = T(StereoFrameDefault<T>::Default);}
- };
+ // Structures for easier access to data
+ typedef QAudioFrameMono<QAudioFormat::UInt8> U8M;
+ typedef QAudioFrameMono<QAudioFormat::Int16> S16M;
+ typedef QAudioFrameMono<QAudioFormat::Int32> S32M;
+ typedef QAudioFrameMono<QAudioFormat::Float> F32M;
- typedef StereoFrame<unsigned char> S8U;
- typedef StereoFrame<signed char> S8S;
- typedef StereoFrame<unsigned short> S16U;
- typedef StereoFrame<signed short> S16S;
- typedef StereoFrame<float> S32F;
+ typedef QAudioFrameStereo<QAudioFormat::UInt8> U8S;
+ typedef QAudioFrameStereo<QAudioFormat::Int16> S16S;
+ typedef QAudioFrameStereo<QAudioFormat::Int32> S32S;
+ typedef QAudioFrameStereo<QAudioFormat::Float> F32S;
template <typename T> const T* constData() const {
return static_cast<const T*>(constData());
@@ -137,12 +161,12 @@ public:
return static_cast<T*>(data());
}
private:
- QAudioBufferPrivate *d;
-};
-
-template <> struct QAudioBuffer::StereoFrameDefault<unsigned char> { enum { Default = 128 }; };
-template <> struct QAudioBuffer::StereoFrameDefault<unsigned short> { enum { Default = 32768 }; };
+ const void* constData() const noexcept;
+ const void* data() const noexcept;
+ void *data();
+ QExplicitlySharedDataPointer<QAudioBufferPrivate> d;
+};
QT_END_NAMESPACE
diff --git a/src/multimedia/audio/qaudiobuffer_p.h b/src/multimedia/audio/qaudiobuffer_p.h
deleted file mode 100644
index e770989f2..000000000
--- a/src/multimedia/audio/qaudiobuffer_p.h
+++ /dev/null
@@ -1,96 +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 QAUDIOBUFFER_P_H
-#define QAUDIOBUFFER_P_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 <qtmultimediaglobal.h>
-#include <qmultimedia.h>
-
-#include "qaudioformat.h"
-
-QT_BEGIN_NAMESPACE
-
-// Required for QDoc workaround
-class QString;
-
-class Q_MULTIMEDIA_EXPORT QAbstractAudioBuffer {
-public:
- virtual ~QAbstractAudioBuffer() {}
-
- // Lifetime management
- virtual void release() = 0;
-
- // Format related
- virtual QAudioFormat format() const = 0;
- virtual qint64 startTime() const = 0;
- virtual int frameCount() const = 0;
-
- // R/O Data
- virtual void *constData() const = 0;
-
- // For writable data we do this:
- // If we only have one reference to the provider,
- // call writableData(). If that does not return 0,
- // then we're finished. If it does return 0, then we call
- // writableClone() to get a new buffer and then release
- // the old clone if that succeeds. If it fails, we create
- // a memory clone from the constData and release the old buffer.
- // If writableClone() succeeds, we then call writableData() on it
- // and that should be good.
-
- virtual void *writableData() = 0;
- virtual QAbstractAudioBuffer *clone() const = 0;
-};
-
-
-QT_END_NAMESPACE
-
-#endif // QAUDIOBUFFER_P_H
diff --git a/src/multimedia/audio/qaudiobufferinput.cpp b/src/multimedia/audio/qaudiobufferinput.cpp
new file mode 100644
index 000000000..e43066f10
--- /dev/null
+++ b/src/multimedia/audio/qaudiobufferinput.cpp
@@ -0,0 +1,184 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qaudiobufferinput.h"
+#include "qplatformaudiobufferinput_p.h"
+#include "qmediainputencoderinterface_p.h"
+#include "qmediaframeinput_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAudioBufferInputPrivate : public QMediaFrameInputPrivate
+{
+public:
+ QAudioBufferInputPrivate(QAudioBufferInput *q) : q(q) { }
+
+ bool sendAudioBuffer(const QAudioBuffer &audioBuffer)
+ {
+ return sendMediaFrame(
+ [&]() { emit m_platfromAudioBufferInput->newAudioBuffer(audioBuffer); });
+ }
+
+ void initialize(const QAudioFormat &format = {})
+ {
+ m_platfromAudioBufferInput = std::make_unique<QPlatformAudioBufferInput>(format);
+ addUpdateSignal(m_platfromAudioBufferInput.get(),
+ &QPlatformAudioBufferInput::encoderUpdated);
+ }
+
+ void uninitialize()
+ {
+ m_platfromAudioBufferInput.reset();
+
+ if (captureSession())
+ captureSession()->setAudioBufferInput(nullptr);
+ }
+
+ QMediaCaptureSession *session() const { return m_captureSession; }
+
+ QPlatformAudioBufferInput *platfromAudioBufferInput() const
+ {
+ return m_platfromAudioBufferInput.get();
+ }
+
+private:
+ void updateCaptureSessionConnections(QMediaCaptureSession *prevSession,
+ QMediaCaptureSession *newSession) override
+ {
+ if (prevSession)
+ removeUpdateSignal(prevSession, &QMediaCaptureSession::audioOutputChanged);
+
+ if (newSession)
+ addUpdateSignal(newSession, &QMediaCaptureSession::audioOutputChanged);
+ }
+
+ bool checkIfCanSendMediaFrame() const override
+ {
+ if (auto encoderInterface = m_platfromAudioBufferInput->encoderInterface())
+ return encoderInterface->canPushFrame();
+
+ // Not implemented yet
+ // return captureSession()->audioOutput() != nullptr;
+ return false;
+ }
+
+ void emitReadyToSendMediaFrame() override { emit q->readyToSendAudioBuffer(); }
+
+private:
+ QAudioBufferInput *q = nullptr;
+ QMediaCaptureSession *m_captureSession = nullptr;
+ std::unique_ptr<QPlatformAudioBufferInput> m_platfromAudioBufferInput;
+};
+
+/*!
+ \class QAudioBufferInput
+ \inmodule QtMultimedia
+ \ingroup multimedia
+ \ingroup multimedia_audio
+ \since 6.8
+
+ \brief The QAudioBufferInput class is used for providing custom audio buffers
+ to \l QMediaRecorder through \l QMediaCaptureSession.
+
+ \sa QMediaRecorder, QMediaCaptureSession
+*/
+
+/*!
+ Constructs a new QAudioBufferInput object with \a parent.
+*/
+QAudioBufferInput::QAudioBufferInput(QObject *parent) : QAudioBufferInput({}, parent) { }
+
+/*!
+ Constructs a new QAudioBufferInput object with audio \a format and \a parent.
+
+ The specified \a format will work as a hint for the initialization of the matching
+ audio encoder upon invoking \l QMediaRecorder::record().
+ If the format is not specified or not valid, the audio encoder will be initialized
+ upon sending the first audio buffer.
+
+ We recommend specifying the format if you know in advance what kind of audio buffers
+ you're going to send.
+*/
+QAudioBufferInput::QAudioBufferInput(const QAudioFormat &format, QObject *parent)
+ : QObject(*new QAudioBufferInputPrivate(this), parent)
+{
+ Q_D(QAudioBufferInput);
+ d->initialize(format);
+}
+
+/*!
+ Destroys the object.
+ */
+QAudioBufferInput::~QAudioBufferInput()
+{
+ Q_D(QAudioBufferInput);
+ d->uninitialize();
+}
+
+/*!
+ Sends \l QAudioBuffer to \l QMediaRecorder through \l QMediaCaptureSession.
+
+ Returns \c true if the specified \a audioBuffer has been sent successfully
+ to the destination. Returns \c false, if the buffer hasn't been sent,
+ which can happen if the instance is not assigned to
+ \l QMediaCaptureSession, the session doesn't have a media recorder,
+ the media recorder is not started or its queue is full.
+ The \l readyToSendAudioBuffer() signal will be emitted as soon as
+ the destination is able to handle a new audio buffer.
+
+ Sending of an empty audio buffer is treated by \l QMediaRecorder
+ as an end of the input stream. QMediaRecorder stops the recording
+ automatically if \l QMediaRecorder::autoStop is \c true and
+ all the inputs have reported the end of the stream.
+*/
+bool QAudioBufferInput::sendAudioBuffer(const QAudioBuffer &audioBuffer)
+{
+ Q_D(QAudioBufferInput);
+ return d->sendAudioBuffer(audioBuffer);
+}
+
+/*!
+ Returns the audio format that was specified upon construction of the audio buffer input.
+*/
+QAudioFormat QAudioBufferInput::format() const
+{
+ Q_D(const QAudioBufferInput);
+ return d->platfromAudioBufferInput()->audioFormat();
+}
+
+/*!
+ Returns the capture session this audio buffer input is connected to, or
+ a \c nullptr if the audio buffer input is not connected to a capture session.
+
+ Use QMediaCaptureSession::setAudioBufferInput() to connect
+ the audio buffer input to a session.
+*/
+QMediaCaptureSession *QAudioBufferInput::captureSession() const
+{
+ Q_D(const QAudioBufferInput);
+ return d->captureSession();
+}
+
+void QAudioBufferInput::setCaptureSession(QMediaCaptureSession *captureSession)
+{
+ Q_D(QAudioBufferInput);
+ d->setCaptureSession(captureSession);
+}
+
+QPlatformAudioBufferInput *QAudioBufferInput::platformAudioBufferInput() const
+{
+ Q_D(const QAudioBufferInput);
+ return d->platfromAudioBufferInput();
+}
+
+/*!
+ \fn void QAudioBufferInput::readyToSendAudioBuffer()
+
+ Signals that a new audio buffer can be sent to the audio buffer input.
+ After receiving the signal, if you have audio date to be sent, invoke \l sendAudioBuffer
+ once or in a loop until it returns \c false.
+
+ \sa sendAudioBuffer()
+*/
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/audio/qaudiobufferinput.h b/src/multimedia/audio/qaudiobufferinput.h
new file mode 100644
index 000000000..f48db186a
--- /dev/null
+++ b/src/multimedia/audio/qaudiobufferinput.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAUDIOBUFFERINPUT_H
+#define QAUDIOBUFFERINPUT_H
+
+#include <QtMultimedia/qtmultimediaexports.h>
+#include <QtMultimedia/qaudiobuffer.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPlatformAudioBufferInput;
+class QAudioBufferInputPrivate;
+class QMediaCaptureSession;
+
+class Q_MULTIMEDIA_EXPORT QAudioBufferInput : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QAudioBufferInput(QObject *parent = nullptr);
+
+ explicit QAudioBufferInput(const QAudioFormat &format, QObject *parent = nullptr);
+
+ ~QAudioBufferInput() override;
+
+ bool sendAudioBuffer(const QAudioBuffer &audioBuffer);
+
+ QAudioFormat format() const;
+
+ QMediaCaptureSession *captureSession() const;
+
+Q_SIGNALS:
+ void readyToSendAudioBuffer();
+
+private:
+ void setCaptureSession(QMediaCaptureSession *captureSession);
+
+ QPlatformAudioBufferInput *platformAudioBufferInput() const;
+
+ friend class QMediaCaptureSession;
+ Q_DISABLE_COPY(QAudioBufferInput)
+ Q_DECLARE_PRIVATE(QAudioBufferInput)
+};
+
+QT_END_NAMESPACE
+
+#endif // QAUDIOBUFFERINPUT_H
diff --git a/src/multimedia/audio/qaudiobufferoutput.cpp b/src/multimedia/audio/qaudiobufferoutput.cpp
new file mode 100644
index 000000000..50389c49a
--- /dev/null
+++ b/src/multimedia/audio/qaudiobufferoutput.cpp
@@ -0,0 +1,78 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qaudiobufferoutput_p.h"
+#include "qmediaplayer.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAudioBufferOutput
+ \inmodule QtMultimedia
+ \ingroup multimedia
+ \ingroup multimedia_audio
+ \since 6.8
+
+ \brief The QAudioBufferOutput class is used for capturing audio data provided by \l QMediaPlayer.
+
+ QAudioBufferOutput can be set to QMediaPlayer in order to receive audio buffers
+ decoded by the media player. The received audio data can be used for any
+ processing or visualization.
+
+ \sa QMediaPlayer, QMediaPlayer::setAudioBufferOutput, QAudioBuffer
+*/
+
+/*!
+ Constructs a new QAudioBufferOutput object with \a parent.
+
+ The audio format of output audio buffers will depend on
+ the source media file and the inner audio decoder in \l QMediaPlayer.
+*/
+QAudioBufferOutput::QAudioBufferOutput(QObject *parent)
+ : QObject(*new QAudioBufferOutputPrivate, parent)
+{
+}
+
+/*!
+ Constructs a new QAudioBufferOutput object with audio \a format and \a parent.
+
+ If the specified \a format is valid, it will be the format of output
+ audio buffers. Otherwise, the format of output audio buffers
+ will depend on the source media file and the inner audio decoder in \l QMediaPlayer.
+*/
+QAudioBufferOutput::QAudioBufferOutput(const QAudioFormat &format, QObject *parent)
+ : QObject(*new QAudioBufferOutputPrivate(format), parent)
+{
+}
+
+/*!
+ Destroys the audio buffer output object.
+*/
+QAudioBufferOutput::~QAudioBufferOutput()
+{
+ Q_D(QAudioBufferOutput);
+
+ if (d->mediaPlayer)
+ d->mediaPlayer->setAudioBufferOutput(nullptr);
+}
+
+/*!
+ Gets the audio format specified in the constructor.
+
+ If the format is valid, it specifies the format of output oudio buffers.
+*/
+QAudioFormat QAudioBufferOutput::format() const
+{
+ Q_D(const QAudioBufferOutput);
+ return d->format;
+}
+
+/*!
+ \fn void QAudioBufferOutput::audioBufferReceived(const QAudioBuffer &buffer)
+
+ Signals that a new audio \a buffer has been received from \l QMediaPlayer.
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qaudiobufferoutput.cpp"
diff --git a/src/multimedia/audio/qaudiobufferoutput.h b/src/multimedia/audio/qaudiobufferoutput.h
new file mode 100644
index 000000000..2e4fab1a4
--- /dev/null
+++ b/src/multimedia/audio/qaudiobufferoutput.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAUDIOBUFFEROUTPUT_H
+#define QAUDIOBUFFEROUTPUT_H
+
+#include <QtMultimedia/qtmultimediaexports.h>
+#include <QtMultimedia/qaudiobuffer.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioBufferOutputPrivate;
+
+class Q_MULTIMEDIA_EXPORT QAudioBufferOutput : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QAudioBufferOutput(QObject *parent = nullptr);
+
+ explicit QAudioBufferOutput(const QAudioFormat &format, QObject *parent = nullptr);
+
+ ~QAudioBufferOutput() override;
+
+ QAudioFormat format() const;
+
+Q_SIGNALS:
+ void audioBufferReceived(const QAudioBuffer &buffer);
+
+private:
+ Q_DISABLE_COPY(QAudioBufferOutput)
+ Q_DECLARE_PRIVATE(QAudioBufferOutput)
+};
+
+QT_END_NAMESPACE
+
+#endif // QAUDIOBUFFEROUTPUT_H
diff --git a/src/multimedia/audio/qaudiobufferoutput_p.h b/src/multimedia/audio/qaudiobufferoutput_p.h
new file mode 100644
index 000000000..2f9c11bd1
--- /dev/null
+++ b/src/multimedia/audio/qaudiobufferoutput_p.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAUDIOBUFFEROUTPUT_P_H
+#define QAUDIOBUFFEROUTPUT_P_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/private/qobject_p.h>
+#include "qaudiobufferoutput.h"
+
+QT_BEGIN_NAMESPACE
+
+class QMediaPlayer;
+
+class QAudioBufferOutputPrivate : public QObjectPrivate
+{
+public:
+ QAudioBufferOutputPrivate(const QAudioFormat &format = {}) : format(std::move(format)) { }
+
+ static QMediaPlayer *exchangeMediaPlayer(QAudioBufferOutput &output, QMediaPlayer *player)
+ {
+ auto outputPrivate = static_cast<QAudioBufferOutputPrivate *>(output.d_func());
+ return std::exchange(outputPrivate->mediaPlayer, player);
+ }
+
+ QAudioFormat format;
+ QMediaPlayer *mediaPlayer = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QAUDIOBUFFEROUTPUT_P_H
diff --git a/src/multimedia/audio/qaudiodecoder.cpp b/src/multimedia/audio/qaudiodecoder.cpp
index 0286e9a85..f555f46ed 100644
--- a/src/multimedia/audio/qaudiodecoder.cpp
+++ b/src/multimedia/audio/qaudiodecoder.cpp
@@ -1,192 +1,104 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qaudiodecoder.h"
-#include "qmediaobject_p.h"
-#include <qmediaservice.h>
-#include "qaudiodecodercontrol.h"
-#include <private/qmediaserviceprovider_p.h>
+#include <private/qaudiodecoder_p.h>
+#include <private/qmultimediautils_p.h>
+#include <private/qplatformaudiodecoder_p.h>
+#include <private/qplatformmediaintegration_p.h>
#include <QtCore/qcoreevent.h>
-#include <QtCore/qmetaobject.h>
-#include <QtCore/qtimer.h>
#include <QtCore/qdebug.h>
+#include <QtCore/qmetaobject.h>
#include <QtCore/qpointer.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qurl.h>
QT_BEGIN_NAMESPACE
/*!
\class QAudioDecoder
- \brief The QAudioDecoder class allows decoding audio.
+ \brief The QAudioDecoder class implements decoding audio.
\inmodule QtMultimedia
\ingroup multimedia
\ingroup multimedia_audio
\preliminary
- The QAudioDecoder class is a high level class for decoding local
+ The QAudioDecoder class is a high level class for decoding
audio media files. It is similar to the QMediaPlayer class except
that audio is provided back through this API rather than routed
- directly to audio hardware, and playlists and network and streaming
- based media is not supported.
+ directly to audio hardware.
\sa QAudioBuffer
*/
-static void qRegisterAudioDecoderMetaTypes()
-{
- qRegisterMetaType<QAudioDecoder::State>("QAudioDecoder::State");
- qRegisterMetaType<QAudioDecoder::Error>("QAudioDecoder::Error");
-}
-
-Q_CONSTRUCTOR_FUNCTION(qRegisterAudioDecoderMetaTypes)
-
-class QAudioDecoderPrivate : public QMediaObjectPrivate
-{
- Q_DECLARE_NON_CONST_PUBLIC(QAudioDecoder)
-
-public:
- QAudioDecoderPrivate()
- : provider(nullptr)
- , control(nullptr)
- , state(QAudioDecoder::StoppedState)
- , error(QAudioDecoder::NoError)
- {}
-
- QMediaServiceProvider *provider;
- QAudioDecoderControl *control;
- QAudioDecoder::State state;
- QAudioDecoder::Error error;
- QString errorString;
-
- void _q_stateChanged(QAudioDecoder::State state);
- void _q_error(int error, const QString &errorString);
-};
-
-void QAudioDecoderPrivate::_q_stateChanged(QAudioDecoder::State ps)
+/*!
+ Construct an QAudioDecoder instance with \a parent.
+*/
+QAudioDecoder::QAudioDecoder(QObject *parent) : QObject{ *new QAudioDecoderPrivate, parent }
{
- Q_Q(QAudioDecoder);
+ QT6_ONLY(Q_UNUSED(unused))
- if (ps != state) {
- state = ps;
+ Q_D(QAudioDecoder);
- emit q->stateChanged(ps);
+ auto maybeDecoder = QPlatformMediaIntegration::instance()->createAudioDecoder(this);
+ if (maybeDecoder) {
+ d->decoder.reset(maybeDecoder.value());
+ } else {
+ qWarning() << "Failed to initialize QAudioDecoder" << maybeDecoder.error();
}
}
-void QAudioDecoderPrivate::_q_error(int error, const QString &errorString)
-{
- Q_Q(QAudioDecoder);
-
- this->error = QAudioDecoder::Error(error);
- this->errorString = errorString;
-
- emit q->error(this->error);
-}
+/*!
+ Destroys the audio decoder object.
+*/
+QAudioDecoder::~QAudioDecoder() = default;
/*!
- Construct an QAudioDecoder instance
- parented to \a parent.
+ Returns true is audio decoding is supported on this platform.
*/
-QAudioDecoder::QAudioDecoder(QObject *parent)
- : QMediaObject(*new QAudioDecoderPrivate,
- parent,
- QMediaServiceProvider::defaultServiceProvider()->requestService(Q_MEDIASERVICE_AUDIODECODER))
+bool QAudioDecoder::isSupported() const
{
- Q_D(QAudioDecoder);
+ Q_D(const QAudioDecoder);
- d->provider = QMediaServiceProvider::defaultServiceProvider();
- if (d->service) {
- d->control = qobject_cast<QAudioDecoderControl*>(d->service->requestControl(QAudioDecoderControl_iid));
- if (d->control != nullptr) {
- connect(d->control, SIGNAL(stateChanged(QAudioDecoder::State)), SLOT(_q_stateChanged(QAudioDecoder::State)));
- connect(d->control, SIGNAL(error(int,QString)), SLOT(_q_error(int,QString)));
-
- connect(d->control, SIGNAL(formatChanged(QAudioFormat)), SIGNAL(formatChanged(QAudioFormat)));
- connect(d->control, SIGNAL(sourceChanged()), SIGNAL(sourceChanged()));
- connect(d->control, SIGNAL(bufferReady()), this, SIGNAL(bufferReady()));
- connect(d->control ,SIGNAL(bufferAvailableChanged(bool)), this, SIGNAL(bufferAvailableChanged(bool)));
- connect(d->control ,SIGNAL(finished()), this, SIGNAL(finished()));
- connect(d->control ,SIGNAL(positionChanged(qint64)), this, SIGNAL(positionChanged(qint64)));
- connect(d->control ,SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64)));
- }
- }
- if (!d->control) {
- d->error = ServiceMissingError;
- d->errorString = tr("The QAudioDecoder object does not have a valid service");
- }
+ return bool(d->decoder);
}
-
/*!
- Destroys the audio decoder object.
+ \property QAudioDecoder::isDecoding
+ \brief \c true if the decoder is currently running and decoding audio data.
*/
-QAudioDecoder::~QAudioDecoder()
+bool QAudioDecoder::isDecoding() const
{
- Q_D(QAudioDecoder);
-
- if (d->service) {
- if (d->control)
- d->service->releaseControl(d->control);
-
- d->provider->releaseService(d->service);
- }
-}
+ Q_D(const QAudioDecoder);
-QAudioDecoder::State QAudioDecoder::state() const
-{
- return d_func()->state;
+ return d->decoder && d->decoder->isDecoding();
}
/*!
- Returns the current error state.
-*/
+ Returns the current error state of the QAudioDecoder.
+*/
QAudioDecoder::Error QAudioDecoder::error() const
{
- return d_func()->error;
+ Q_D(const QAudioDecoder);
+ return d->decoder ? d->decoder->error() : NotSupportedError;
}
+/*!
+ \property QAudioDecoder::error
+
+ Returns a human readable description of the current error, or
+ an empty string is there is no error.
+*/
QString QAudioDecoder::errorString() const
{
- return d_func()->errorString;
+ Q_D(const QAudioDecoder);
+ if (!d->decoder)
+ return tr("QAudioDecoder not supported.");
+ return d->decoder->errorString();
}
/*!
@@ -205,18 +117,12 @@ void QAudioDecoder::start()
{
Q_D(QAudioDecoder);
- if (d->control == nullptr) {
- QMetaObject::invokeMethod(this, "_q_error", Qt::QueuedConnection,
- Q_ARG(int, QAudioDecoder::ServiceMissingError),
- Q_ARG(QString, tr("The QAudioDecoder object does not have a valid service")));
+ if (!d->decoder)
return;
- }
// Reset error conditions
- d->error = NoError;
- d->errorString.clear();
-
- d->control->start();
+ d->decoder->clearError();
+ d->decoder->start();
}
/*!
@@ -226,8 +132,8 @@ void QAudioDecoder::stop()
{
Q_D(QAudioDecoder);
- if (d->control != nullptr)
- d->control->stop();
+ if (d->decoder)
+ d->decoder->stop();
}
/*!
@@ -235,12 +141,10 @@ void QAudioDecoder::stop()
If \l setSourceDevice was called, this will
be empty.
*/
-QString QAudioDecoder::sourceFilename() const
+QUrl QAudioDecoder::source() const
{
Q_D(const QAudioDecoder);
- if (d->control)
- return d->control->sourceFilename();
- return QString();
+ return d->unresolvedUrl;
}
/*!
@@ -252,24 +156,28 @@ QString QAudioDecoder::sourceFilename() const
You can only specify either a source filename or
a source QIODevice. Setting one will unset the other.
*/
-void QAudioDecoder::setSourceFilename(const QString &fileName)
+void QAudioDecoder::setSource(const QUrl &fileName)
{
Q_D(QAudioDecoder);
- if (d->control != nullptr)
- d_func()->control->setSourceFilename(fileName);
+ if (!d->decoder)
+ return;
+
+ d->decoder->clearError();
+ d->unresolvedUrl = fileName;
+ d->decoder->setSourceDevice(nullptr);
+ QUrl url = qMediaFromUserInput(fileName);
+ d->decoder->setSource(url);
}
/*!
Returns the current source QIODevice, if one was set.
- If \l setSourceFilename() was called, this will be 0.
+ If \l setSource() was called, this will be a nullptr.
*/
QIODevice *QAudioDecoder::sourceDevice() const
{
Q_D(const QAudioDecoder);
- if (d->control)
- return d->control->sourceDevice();
- return nullptr;
+ return d->decoder ? d->decoder->sourceDevice() : nullptr;
}
/*!
@@ -284,24 +192,24 @@ QIODevice *QAudioDecoder::sourceDevice() const
void QAudioDecoder::setSourceDevice(QIODevice *device)
{
Q_D(QAudioDecoder);
-
- if (d->control != nullptr)
- d_func()->control->setSourceDevice(device);
+ if (d->decoder) {
+ d->unresolvedUrl = QUrl{};
+ d->decoder->setSourceDevice(device);
+ }
}
/*!
- Returns the current audio format of the decoded stream.
+ Returns the audio format the decoder is set to.
- Any buffers returned should have this format.
+ \note This may be different than the format of the decoded
+ samples, if the audio format was set to an invalid one.
\sa setAudioFormat(), formatChanged()
*/
QAudioFormat QAudioDecoder::audioFormat() const
{
Q_D(const QAudioDecoder);
- if (d->control)
- return d->control->audioFormat();
- return QAudioFormat();
+ return d->decoder ? d->decoder->audioFormat() : QAudioFormat{};
}
/*!
@@ -319,45 +227,20 @@ QAudioFormat QAudioDecoder::audioFormat() const
If you wish to reset the decoded format to that of the original
audio file, you can specify an invalid \a format.
+
+ \warning Setting a desired audio format is not yet supported
+ on the Android backend. It does work with the default FFMPEG
+ backend.
*/
void QAudioDecoder::setAudioFormat(const QAudioFormat &format)
{
- Q_D(QAudioDecoder);
-
- if (state() != QAudioDecoder::StoppedState)
+ if (isDecoding())
return;
- if (d->control != nullptr)
- d_func()->control->setAudioFormat(format);
-}
-
-/*!
- \internal
-*/
-
-bool QAudioDecoder::bind(QObject *obj)
-{
- return QMediaObject::bind(obj);
-}
-
-/*!
- \internal
-*/
-
-void QAudioDecoder::unbind(QObject *obj)
-{
- QMediaObject::unbind(obj);
-}
+ Q_D(QAudioDecoder);
-/*!
- Returns the level of support an audio decoder has for a \a mimeType and a set of \a codecs.
-*/
-QMultimedia::SupportEstimate QAudioDecoder::hasSupport(const QString &mimeType,
- const QStringList& codecs)
-{
- return QMediaServiceProvider::defaultServiceProvider()->hasSupport(QByteArray(Q_MEDIASERVICE_AUDIODECODER),
- mimeType,
- codecs);
+ if (d->decoder)
+ d->decoder->setAudioFormat(format);
}
/*!
@@ -368,9 +251,7 @@ QMultimedia::SupportEstimate QAudioDecoder::hasSupport(const QString &mimeType,
bool QAudioDecoder::bufferAvailable() const
{
Q_D(const QAudioDecoder);
- if (d->control)
- return d->control->bufferAvailable();
- return false;
+ return d->decoder && d->decoder->bufferAvailable();
}
/*!
@@ -381,9 +262,7 @@ bool QAudioDecoder::bufferAvailable() const
qint64 QAudioDecoder::position() const
{
Q_D(const QAudioDecoder);
- if (d->control)
- return d->control->position();
- return -1;
+ return d->decoder ? d->decoder->position() : -1;
}
/*!
@@ -394,9 +273,7 @@ qint64 QAudioDecoder::position() const
qint64 QAudioDecoder::duration() const
{
Q_D(const QAudioDecoder);
- if (d->control)
- return d->control->duration();
- return -1;
+ return d->decoder ? d->decoder->duration() : -1;
}
/*!
@@ -412,26 +289,11 @@ qint64 QAudioDecoder::duration() const
QAudioBuffer QAudioDecoder::read() const
{
Q_D(const QAudioDecoder);
-
- if (d->control) {
- return d->control->read();
- } else {
- return QAudioBuffer();
- }
+ return d->decoder ? d->decoder->read() : QAudioBuffer{};
}
// Enums
/*!
- \enum QAudioDecoder::State
-
- Defines the current state of a media player.
-
- \value StoppedState The decoder is not decoding. Decoding will
- start at the start of the media.
- \value DecodingState The audio player is currently decoding media.
-*/
-
-/*!
\enum QAudioDecoder::Error
Defines a media player error condition.
@@ -440,12 +302,12 @@ QAudioBuffer QAudioDecoder::read() const
\value ResourceError A media resource couldn't be resolved.
\value FormatError The format of a media resource isn't supported.
\value AccessDeniedError There are not the appropriate permissions to play a media resource.
- \value ServiceMissingError A valid playback service was not found, playback cannot proceed.
+ \value NotSupportedError QAudioDecoder is not supported on this platform
*/
// Signals
/*!
- \fn QAudioDecoder::error(QAudioDecoder::Error error)
+ \fn void QAudioDecoder::error(QAudioDecoder::Error error)
Signals that an \a error condition has occurred.
@@ -453,17 +315,11 @@ QAudioBuffer QAudioDecoder::read() const
*/
/*!
- \fn void QAudioDecoder::stateChanged(State state)
-
- Signal the \a state of the decoder object has changed.
-*/
-
-/*!
\fn void QAudioDecoder::sourceChanged()
Signals that the current source of the decoder has changed.
- \sa sourceFilename(), sourceDevice()
+ \sa source(), sourceDevice()
*/
/*!
@@ -517,26 +373,9 @@ QAudioBuffer QAudioDecoder::read() const
\sa positionChanged()
*/
-
// Properties
/*!
- \property QAudioDecoder::state
- \brief the audio decoder's playback state.
-
- By default this property is QAudioDecoder::Stopped
-
- \sa start(), stop()
-*/
-
-/*!
- \property QAudioDecoder::error
- \brief a string describing the last error condition.
-
- \sa error()
-*/
-
-/*!
- \property QAudioDecoder::sourceFilename
+ \property QAudioDecoder::source
\brief the active filename being decoded by the decoder object.
*/
diff --git a/src/multimedia/audio/qaudiodecoder.h b/src/multimedia/audio/qaudiodecoder.h
index 4ba107946..9044b6617 100644
--- a/src/multimedia/audio/qaudiodecoder.h
+++ b/src/multimedia/audio/qaudiodecoder.h
@@ -1,89 +1,43 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIODECODER_H
#define QAUDIODECODER_H
-#include <QtMultimedia/qmediaobject.h>
+#include <QtCore/qobject.h>
#include <QtMultimedia/qmediaenumdebug.h>
-
#include <QtMultimedia/qaudiobuffer.h>
QT_BEGIN_NAMESPACE
class QAudioDecoderPrivate;
-class Q_MULTIMEDIA_EXPORT QAudioDecoder : public QMediaObject
+class Q_MULTIMEDIA_EXPORT QAudioDecoder : public QObject
{
Q_OBJECT
- Q_PROPERTY(QString sourceFilename READ sourceFilename WRITE setSourceFilename NOTIFY sourceChanged)
- Q_PROPERTY(State state READ state NOTIFY stateChanged)
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+ Q_PROPERTY(bool isDecoding READ isDecoding NOTIFY isDecodingChanged)
Q_PROPERTY(QString error READ errorString)
Q_PROPERTY(bool bufferAvailable READ bufferAvailable NOTIFY bufferAvailableChanged)
- Q_ENUMS(State)
- Q_ENUMS(Error)
-
public:
- enum State
- {
- StoppedState,
- DecodingState
- };
-
enum Error
{
NoError,
ResourceError,
FormatError,
AccessDeniedError,
- ServiceMissingError
+ NotSupportedError
};
+ Q_ENUM(Error)
explicit QAudioDecoder(QObject *parent = nullptr);
~QAudioDecoder();
- static QMultimedia::SupportEstimate hasSupport(const QString &mimeType, const QStringList& codecs = QStringList());
+ bool isSupported() const;
+ bool isDecoding() const;
- State state() const;
-
- QString sourceFilename() const;
- void setSourceFilename(const QString &fileName);
+ QUrl source() const;
+ void setSource(const QUrl &fileName);
QIODevice* sourceDevice() const;
void setSourceDevice(QIODevice *device);
@@ -108,8 +62,8 @@ Q_SIGNALS:
void bufferAvailableChanged(bool);
void bufferReady();
void finished();
+ void isDecodingChanged(bool);
- void stateChanged(QAudioDecoder::State newState);
void formatChanged(const QAudioFormat &format);
void error(QAudioDecoder::Error error);
@@ -119,23 +73,16 @@ Q_SIGNALS:
void positionChanged(qint64 position);
void durationChanged(qint64 duration);
-public:
- bool bind(QObject *) override;
- void unbind(QObject *) override;
-
private:
Q_DISABLE_COPY(QAudioDecoder)
Q_DECLARE_PRIVATE(QAudioDecoder)
- Q_PRIVATE_SLOT(d_func(), void _q_stateChanged(QAudioDecoder::State))
- Q_PRIVATE_SLOT(d_func(), void _q_error(int, const QString &))
+
+ // ### Qt7: remove unused member
+ QT6_ONLY(void *unused = nullptr;) // for ABI compatibility
};
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QAudioDecoder::State)
-Q_DECLARE_METATYPE(QAudioDecoder::Error)
-
-Q_MEDIA_ENUM_DEBUG(QAudioDecoder, State)
Q_MEDIA_ENUM_DEBUG(QAudioDecoder, Error)
#endif // QAUDIODECODER_H
diff --git a/src/multimedia/audio/qaudiodecoder_p.h b/src/multimedia/audio/qaudiodecoder_p.h
new file mode 100644
index 000000000..fa7311457
--- /dev/null
+++ b/src/multimedia/audio/qaudiodecoder_p.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAUDIODECODER_P_H
+#define QAUDIODECODER_P_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/private/qobject_p.h>
+#include <QtMultimedia/private/qplatformaudiodecoder_p.h>
+
+#include <memory.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioDecoder;
+
+class QAudioDecoderPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QAudioDecoder)
+
+public:
+ QAudioDecoderPrivate() = default;
+
+ QUrl unresolvedUrl;
+ std::unique_ptr<QPlatformAudioDecoder> decoder;
+};
+
+QT_END_NAMESPACE
+
+#endif // QAUDIODECODER_P_H
diff --git a/src/multimedia/audio/qaudiodevice.cpp b/src/multimedia/audio/qaudiodevice.cpp
new file mode 100644
index 000000000..4b1e182cb
--- /dev/null
+++ b/src/multimedia/audio/qaudiodevice.cpp
@@ -0,0 +1,373 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qaudiosystem_p.h"
+#include "qaudiodevice_p.h"
+#include <private/qplatformmediadevices_p.h>
+#include <private/qplatformmediaintegration_p.h>
+
+#include <QtCore/qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+QAudioDevicePrivate::~QAudioDevicePrivate() = default;
+
+QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QAudioDevicePrivate);
+
+/*!
+ \class QAudioDevice
+ \brief The QAudioDevice class provides an information about audio devices and their
+ functionality.
+ \inmodule QtMultimedia
+ \ingroup multimedia
+ \ingroup multimedia_audio
+
+ QAudioDevice describes an audio device available in the system, either for input or for
+ playback.
+
+ A QAudioDevice is used by Qt to construct
+ classes that communicate with the device -- such as
+ QAudioSource, and QAudioSink. It is also used to determine the
+ input or output device to use in a capture session or during media playback.
+
+ You can also query each device for the formats it supports. A
+ format in this context is a set consisting of a channel count, sample rate, and sample type. A
+ format is represented by the QAudioFormat class.
+
+ The values supported by the device for each of these parameters can be
+ fetched with minimumChannelCount(), maximumChannelCount(),
+ minimumSampleRate(), maximumSampleRate() and supportedSampleFormats(). The
+ combinations supported are dependent on the audio device capabilities. If
+ you need a specific format, you can check if the device supports it with
+ isFormatSupported(). For instance:
+
+ \snippet multimedia-snippets/audio.cpp Audio output setup
+
+ The set of available devices can be retrieved from the QMediaDevices class.
+
+ For instance:
+
+ \snippet multimedia-snippets/audio.cpp Dumping audio formats
+
+ In this code sample, we loop through all devices that are able to output
+ sound, i.e., play an audio stream in a supported format. For each device we
+ find, we simply print the deviceName().
+
+ \sa QAudioSink, QAudioSource, QAudioFormat
+*/
+
+/*!
+ \qmlvaluetype audioDevice
+ \inqmlmodule QtMultimedia
+ \since 6.2
+ //! \instantiates QAudioDevice
+ \brief Describes an audio device.
+ \ingroup multimedia_qml
+ \ingroup multimedia_audio_qml
+ \ingroup qmlvaluetypes
+
+ The audioDevice value type describes the properties of an audio device that
+ is connected to the system.
+
+ The list of audio input or output devices can be queried from the \l{MediaDevices}
+ type. To select a certain audio device for input or output set it as the device
+ on \l{AudioInput} or \l{AudioOutput}.
+
+ \qml
+ MediaPlayer {
+ audioOutput: AudioOutput {
+ device: mediaDevices.defaultAudioOutput
+ }
+ }
+ MediaDevices {
+ id: mediaDevices
+ }
+ \endqml
+*/
+
+/*!
+ Constructs a null QAudioDevice object.
+*/
+QAudioDevice::QAudioDevice() = default;
+
+/*!
+ Constructs a copy of \a other.
+*/
+QAudioDevice::QAudioDevice(const QAudioDevice &other) = default;
+
+/*!
+ \fn QAudioDevice::QAudioDevice(QAudioDevice &&other)
+
+ Move constructs from \a other.
+*/
+/*!
+ \fn void QAudioDevice::swap(QAudioDevice &other) noexcept
+
+ Swaps the audio device with the \a other.
+*/
+/*!
+ Destroy this audio device info.
+*/
+QAudioDevice::~QAudioDevice() = default;
+
+/*!
+ Sets the QAudioDevice object to be equal to \a other.
+*/
+QAudioDevice &QAudioDevice::operator=(const QAudioDevice &other) = default;
+
+/*!
+ \fn QAudioDevice& QAudioDevice::operator=(QAudioDevice &&other)
+
+ Moves \a other into this QAudioDevice object.
+*/
+
+/*!
+ Returns true if this QAudioDevice class represents the
+ same audio device as \a other.
+*/
+bool QAudioDevice::operator==(const QAudioDevice &other) const
+{
+ if (d == other.d)
+ return true;
+ if (!d || !other.d)
+ return false;
+ if (d->mode == other.d->mode && d->id == other.d->id && d->isDefault == other.d->isDefault)
+ return true;
+ return false;
+}
+
+/*!
+ Returns true if this QAudioDevice class represents a
+ different audio device than \a other
+*/
+bool QAudioDevice::operator!=(const QAudioDevice &other) const
+{
+ return !operator==(other);
+}
+
+/*!
+ Returns whether this QAudioDevice object holds a valid device definition.
+*/
+bool QAudioDevice::isNull() const
+{
+ return d == nullptr;
+}
+
+/*!
+ \qmlproperty string QtMultimedia::audioDevice::id
+
+ Holds an identifier for the audio device.
+
+ Device names vary depending on the platform/audio plugin being used.
+
+ They are a unique identifier for the audio device.
+*/
+
+/*!
+ \property QAudioDevice::id
+
+ Returns an identifier for the audio device.
+
+ Device names vary depending on the platform/audio plugin being used.
+
+ They are a unique identifier for the audio device.
+*/
+QByteArray QAudioDevice::id() const
+{
+ return isNull() ? QByteArray() : d->id;
+}
+
+/*!
+ \qmlproperty string QtMultimedia::audioDevice::description
+
+ Holds a human readable name of the audio device.
+
+ Use this string to present the device to the user.
+*/
+
+/*!
+ \property QAudioDevice::description
+
+ Returns a human readable name of the audio device.
+
+ Use this string to present the device to the user.
+*/
+QString QAudioDevice::description() const
+{
+ return isNull() ? QString() : d->description;
+}
+
+/*!
+ \qmlproperty bool QtMultimedia::audioDevice::isDefault
+
+ Is true if this is the default audio device.
+*/
+
+/*!
+ \property QAudioDevice::isDefault
+
+ Returns true if this is the default audio device.
+*/
+bool QAudioDevice::isDefault() const
+{
+ return d ? d->isDefault : false;
+}
+
+/*!
+ Returns true if the supplied \a settings are supported by the audio
+ device described by this QAudioDevice.
+*/
+bool QAudioDevice::isFormatSupported(const QAudioFormat &settings) const
+{
+ if (isNull())
+ return false;
+ if (settings.sampleRate() < d->minimumSampleRate
+ || settings.sampleRate() > d->maximumSampleRate)
+ return false;
+ if (settings.channelCount() < d->minimumChannelCount
+ || settings.channelCount() > d->maximumChannelCount)
+ return false;
+ if (!d->supportedSampleFormats.contains(settings.sampleFormat()))
+ return false;
+ return true;
+}
+
+/*!
+ Returns the default audio format settings for this device.
+
+ These settings are provided by the platform/audio plugin being used.
+
+ They are also dependent on the \l {QtAudio}::Mode being used.
+
+ A typical audio system would provide something like:
+ \list
+ \li Input settings: 48000Hz mono 16 bit.
+ \li Output settings: 48000Hz stereo 16 bit.
+ \endlist
+*/
+QAudioFormat QAudioDevice::preferredFormat() const
+{
+ return isNull() ? QAudioFormat() : d->preferredFormat;
+}
+
+/*!
+ Returns the minimum supported sample rate (in Hertz).
+*/
+int QAudioDevice::minimumSampleRate() const
+{
+ return isNull() ? 0 : d->minimumSampleRate;
+}
+
+/*!
+ Returns the maximum supported sample rate (in Hertz).
+*/
+int QAudioDevice::maximumSampleRate() const
+{
+ return isNull() ? 0 : d->maximumSampleRate;
+}
+
+/*!
+ Returns the minimum number of supported channel counts.
+
+ This is typically 1 for mono sound, or 2 for stereo sound.
+*/
+int QAudioDevice::minimumChannelCount() const
+{
+ return isNull() ? 0 : d->minimumChannelCount;
+}
+
+/*!
+ Returns the maximum number of supported channel counts.
+
+ This is typically 1 for mono sound, or 2 for stereo sound.
+*/
+int QAudioDevice::maximumChannelCount() const
+{
+ return isNull() ? 0 : d->maximumChannelCount;
+}
+
+/*!
+ Returns a list of supported sample types.
+*/
+QList<QAudioFormat::SampleFormat> QAudioDevice::supportedSampleFormats() const
+{
+ return isNull() ? QList<QAudioFormat::SampleFormat>() : d->supportedSampleFormats;
+}
+
+/*!
+ Returns the channel configuration of the device.
+*/
+QAudioFormat::ChannelConfig QAudioDevice::channelConfiguration() const
+{
+ return isNull() ? QAudioFormat::ChannelConfigUnknown : d->channelConfiguration;
+}
+
+/*!
+ \fn QAudioDevicePrivate QAudioDevice::handle() const
+ \internal
+*/
+/*!
+ \internal
+*/
+QAudioDevice::QAudioDevice(QAudioDevicePrivate *p) : d(p) { }
+
+/*!
+ \enum QAudioDevice::Mode
+
+ Describes the mode of this device.
+
+ \value Null
+ A null device.
+ \value Input
+ An input device.
+ \value Output
+ An output device.
+*/
+
+/*!
+ \qmlproperty enumeration QtMultimedia::audioDevice::mode
+
+ Holds whether this device is an input or output device.
+
+ The returned value can be one of the following:
+
+
+ \value audioDevice.Null A null device.
+ \value audioDevice.Input input device.
+ \value audioDevice.Output An output device.
+
+*/
+
+/*!
+ \property QAudioDevice::mode
+
+ Returns whether this device is an input or output device.
+*/
+QAudioDevice::Mode QAudioDevice::mode() const
+{
+ return d ? d->mode : Null;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, QAudioDevice::Mode mode)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ switch (mode) {
+ case QAudioDevice::Input:
+ dbg << "QAudioDevice::Input";
+ break;
+ case QAudioDevice::Output:
+ dbg << "QAudioDevice::Output";
+ break;
+ case QAudioDevice::Null:
+ dbg << "QAudioDevice::Null";
+ break;
+ }
+ return dbg;
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qaudiodevice.cpp"
diff --git a/src/multimedia/audio/qaudiodevice.h b/src/multimedia/audio/qaudiodevice.h
new file mode 100644
index 000000000..abd1b654c
--- /dev/null
+++ b/src/multimedia/audio/qaudiodevice.h
@@ -0,0 +1,84 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+#ifndef QAUDIODEVICEINFO_H
+#define QAUDIODEVICEINFO_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qlist.h>
+
+#include <QtMultimedia/qtmultimediaglobal.h>
+
+#include <QtMultimedia/qtaudio.h>
+#include <QtMultimedia/qaudioformat.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioDevicePrivate;
+QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QAudioDevicePrivate, Q_MULTIMEDIA_EXPORT)
+
+class Q_MULTIMEDIA_EXPORT QAudioDevice
+{
+ Q_GADGET
+ Q_PROPERTY(QByteArray id READ id CONSTANT)
+ Q_PROPERTY(QString description READ description CONSTANT)
+ Q_PROPERTY(bool isDefault READ isDefault CONSTANT)
+ Q_PROPERTY(Mode mode READ mode CONSTANT)
+public:
+ enum Mode {
+ Null,
+ Input,
+ Output
+ };
+ Q_ENUM(Mode)
+
+ QAudioDevice();
+ QAudioDevice(const QAudioDevice& other);
+ ~QAudioDevice();
+
+ QAudioDevice(QAudioDevice &&other) noexcept = default;
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QAudioDevice)
+ void swap(QAudioDevice &other) noexcept
+ { d.swap(other.d); }
+
+ QAudioDevice& operator=(const QAudioDevice& other);
+
+ bool operator==(const QAudioDevice &other) const;
+ bool operator!=(const QAudioDevice &other) const;
+
+ bool isNull() const;
+
+ QByteArray id() const;
+ QString description() const;
+
+ bool isDefault() const;
+ QAudioDevice::Mode mode() const;
+
+ bool isFormatSupported(const QAudioFormat &format) const;
+ QAudioFormat preferredFormat() const;
+
+ int minimumSampleRate() const;
+ int maximumSampleRate() const;
+ int minimumChannelCount() const;
+ int maximumChannelCount() const;
+ QList<QAudioFormat::SampleFormat> supportedSampleFormats() const;
+ QAudioFormat::ChannelConfig channelConfiguration() const;
+
+ const QAudioDevicePrivate *handle() const { return d.get(); }
+private:
+ friend class QAudioDevicePrivate;
+ QAudioDevice(QAudioDevicePrivate *p);
+ QExplicitlySharedDataPointer<QAudioDevicePrivate> d;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QAudioDevice::Mode mode);
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QAUDIODEVICEINFO_H
diff --git a/src/multimedia/audio/qaudiodevice_p.h b/src/multimedia/audio/qaudiodevice_p.h
new file mode 100644
index 000000000..c59856d72
--- /dev/null
+++ b/src/multimedia/audio/qaudiodevice_p.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+#ifndef QAUDIODEVICEINFO_P_H
+#define QAUDIODEVICEINFO_P_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 <QtMultimedia/qaudiodevice.h>
+#include <QtCore/private/qglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_MULTIMEDIA_EXPORT QAudioDevicePrivate : public QSharedData
+{
+public:
+ QAudioDevicePrivate(const QByteArray &i, QAudioDevice::Mode m)
+ : id(i),
+ mode(m)
+ {}
+ virtual ~QAudioDevicePrivate();
+ QByteArray id;
+ QAudioDevice::Mode mode = QAudioDevice::Output;
+ bool isDefault = false;
+
+ QAudioFormat preferredFormat;
+ QString description;
+ int minimumSampleRate = 0;
+ int maximumSampleRate = 0;
+ int minimumChannelCount = 0;
+ int maximumChannelCount = 0;
+ QList<QAudioFormat::SampleFormat> supportedSampleFormats;
+ QAudioFormat::ChannelConfig channelConfiguration = QAudioFormat::ChannelConfigUnknown;
+
+ bool operator == (const QAudioDevicePrivate &other) const
+ {
+ return id == other.id && mode == other.mode && isDefault == other.isDefault
+ && preferredFormat == other.preferredFormat && description == other.description
+ && minimumSampleRate == other.minimumSampleRate
+ && maximumSampleRate == other.maximumSampleRate
+ && minimumChannelCount == other.minimumChannelCount
+ && maximumChannelCount == other.maximumChannelCount
+ && supportedSampleFormats == other.supportedSampleFormats
+ && channelConfiguration == other.channelConfiguration;
+ }
+
+ QAudioDevice create() { return QAudioDevice(this); }
+};
+
+QT_END_NAMESPACE
+
+#endif // QAUDIODEVICEINFO_H
diff --git a/src/multimedia/audio/qaudiodevicefactory.cpp b/src/multimedia/audio/qaudiodevicefactory.cpp
deleted file mode 100644
index cf770c468..000000000
--- a/src/multimedia/audio/qaudiodevicefactory.cpp
+++ /dev/null
@@ -1,259 +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$
-**
-****************************************************************************/
-
-#include <QtCore/qdebug.h>
-
-#include "qaudiosystem.h"
-#include "qaudiosystemplugin.h"
-#include "qaudiosystempluginext_p.h"
-
-#include "qmediapluginloader_p.h"
-#include "qaudiodevicefactory_p.h"
-
-QT_BEGIN_NAMESPACE
-
-static QString defaultKey()
-{
- return QStringLiteral("default");
-}
-
-#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
-Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, audioLoader,
- (QAudioSystemFactoryInterface_iid, QLatin1String("audio"), Qt::CaseInsensitive))
-#endif
-
-class QNullDeviceInfo : public QAbstractAudioDeviceInfo
-{
-public:
- QAudioFormat preferredFormat() const override { qWarning()<<"using null deviceinfo, none available"; return QAudioFormat(); }
- bool isFormatSupported(const QAudioFormat& ) const override { return false; }
- QAudioFormat nearestFormat(const QAudioFormat& ) const { return QAudioFormat(); }
- QString deviceName() const override { return QString(); }
- QStringList supportedCodecs() override { return QStringList(); }
- QList<int> supportedSampleRates() override { return QList<int>(); }
- QList<int> supportedChannelCounts() override { return QList<int>(); }
- QList<int> supportedSampleSizes() override { return QList<int>(); }
- QList<QAudioFormat::Endian> supportedByteOrders() override { return QList<QAudioFormat::Endian>(); }
- QList<QAudioFormat::SampleType> supportedSampleTypes() override { return QList<QAudioFormat::SampleType>(); }
-};
-
-class QNullInputDevice : public QAbstractAudioInput
-{
-public:
- void start(QIODevice*) override { qWarning()<<"using null input device, none available";}
- QIODevice *start() override { qWarning()<<"using null input device, none available"; return nullptr; }
- void stop() override {}
- void reset() override {}
- void suspend() override {}
- void resume() override {}
- int bytesReady() const override { return 0; }
- int periodSize() const override { return 0; }
- void setBufferSize(int ) override {}
- int bufferSize() const override { return 0; }
- void setNotifyInterval(int ) override {}
- int notifyInterval() const override { return 0; }
- qint64 processedUSecs() const override { return 0; }
- qint64 elapsedUSecs() const override { return 0; }
- QAudio::Error error() const override { return QAudio::OpenError; }
- QAudio::State state() const override { return QAudio::StoppedState; }
- void setFormat(const QAudioFormat&) override {}
- QAudioFormat format() const override { return QAudioFormat(); }
- void setVolume(qreal) override {}
- qreal volume() const override {return 1.0f;}
-};
-
-class QNullOutputDevice : public QAbstractAudioOutput
-{
-public:
- void start(QIODevice*) override {qWarning()<<"using null output device, none available";}
- QIODevice *start() override { qWarning()<<"using null output device, none available"; return nullptr; }
- void stop() override {}
- void reset() override {}
- void suspend() override {}
- void resume() override {}
- int bytesFree() const override { return 0; }
- int periodSize() const override { return 0; }
- void setBufferSize(int ) override {}
- int bufferSize() const override { return 0; }
- void setNotifyInterval(int ) override {}
- int notifyInterval() const override { return 0; }
- qint64 processedUSecs() const override { return 0; }
- qint64 elapsedUSecs() const override { return 0; }
- QAudio::Error error() const override { return QAudio::OpenError; }
- QAudio::State state() const override { return QAudio::StoppedState; }
- void setFormat(const QAudioFormat&) override {}
- QAudioFormat format() const override { return QAudioFormat(); }
-};
-
-QList<QAudioDeviceInfo> QAudioDeviceFactory::availableDevices(QAudio::Mode mode)
-{
- QList<QAudioDeviceInfo> devices;
-#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
- QMediaPluginLoader* l = audioLoader();
- const auto keys = l->keys();
- for (const QString& key : keys) {
- QAudioSystemFactoryInterface* plugin = qobject_cast<QAudioSystemFactoryInterface*>(l->instance(key));
- if (plugin) {
- const auto availableDevices = plugin->availableDevices(mode);
- for (const QByteArray& handle : availableDevices)
- devices << QAudioDeviceInfo(key, handle, mode);
- }
- }
-#endif
-
- return devices;
-}
-
-QAudioDeviceInfo QAudioDeviceFactory::defaultDevice(QAudio::Mode mode)
-{
-#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
- QMediaPluginLoader* l = audioLoader();
-
- // Check if there is a default plugin.
- QAudioSystemFactoryInterface *plugin = qobject_cast<QAudioSystemFactoryInterface *>(l->instance(defaultKey()));
- if (plugin) {
- // Check if the plugin has the extension interface.
- QAudioSystemPluginExtension *pluginExt = qobject_cast<QAudioSystemPluginExtension *>(l->instance(defaultKey()));
- // Ask for the default device.
- if (pluginExt) {
- const QByteArray &device = pluginExt->defaultDevice(mode);
- if (!device.isEmpty())
- return QAudioDeviceInfo(defaultKey(), device, mode);
- }
-
- // If there were no default devices, e.g., if the plugin did not implement the extent-ion interface,
- // then just pick the first device that's available.
- const auto &devices = plugin->availableDevices(mode);
- if (!devices.isEmpty())
- return QAudioDeviceInfo(defaultKey(), devices.first(), mode);
- }
-
- // If no plugin is marked as default, check the other plugins.
- // Note: We're going to prioritize plugins that report a default device.
- const auto &keys = l->keys();
- QAudioDeviceInfo fallbackDevice;
- for (const auto &key : keys) {
- if (key == defaultKey())
- continue;
- QAudioSystemFactoryInterface* plugin = qobject_cast<QAudioSystemFactoryInterface*>(l->instance(key));
- if (plugin) {
- // Check if the plugin has the extent-ion interface.
- QAudioSystemPluginExtension *pluginExt = qobject_cast<QAudioSystemPluginExtension *>(l->instance(key));
- if (pluginExt) {
- const QByteArray &device = pluginExt->defaultDevice(mode);
- if (!device.isEmpty())
- return QAudioDeviceInfo(key, device, mode);
- } else if (fallbackDevice.isNull()) {
- const auto &devices = plugin->availableDevices(mode);
- if (!devices.isEmpty())
- fallbackDevice = QAudioDeviceInfo(key, devices.first(), mode);
- }
- }
- }
-
-#endif
-
- return QAudioDeviceInfo();
-}
-
-QAbstractAudioDeviceInfo* QAudioDeviceFactory::audioDeviceInfo(const QString &realm, const QByteArray &handle, QAudio::Mode mode)
-{
- QAbstractAudioDeviceInfo *rc = nullptr;
-
-#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
- QAudioSystemFactoryInterface* plugin =
- qobject_cast<QAudioSystemFactoryInterface*>(audioLoader()->instance(realm));
-
- if (plugin)
- rc = plugin->createDeviceInfo(handle, mode);
-#endif
-
- return rc == nullptr ? new QNullDeviceInfo() : rc;
-}
-
-QAbstractAudioInput* QAudioDeviceFactory::createDefaultInputDevice(QAudioFormat const &format)
-{
- return createInputDevice(defaultDevice(QAudio::AudioInput), format);
-}
-
-QAbstractAudioOutput* QAudioDeviceFactory::createDefaultOutputDevice(QAudioFormat const &format)
-{
- return createOutputDevice(defaultDevice(QAudio::AudioOutput), format);
-}
-
-QAbstractAudioInput* QAudioDeviceFactory::createInputDevice(QAudioDeviceInfo const& deviceInfo, QAudioFormat const &format)
-{
- if (deviceInfo.isNull())
- return new QNullInputDevice();
-
-#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
- QAudioSystemFactoryInterface* plugin =
- qobject_cast<QAudioSystemFactoryInterface*>(audioLoader()->instance(deviceInfo.realm()));
-
- if (plugin) {
- QAbstractAudioInput* p = plugin->createInput(deviceInfo.handle());
- if (p) p->setFormat(format);
- return p;
- }
-#endif
-
- return new QNullInputDevice();
-}
-
-QAbstractAudioOutput* QAudioDeviceFactory::createOutputDevice(QAudioDeviceInfo const& deviceInfo, QAudioFormat const &format)
-{
- if (deviceInfo.isNull())
- return new QNullOutputDevice();
-
-#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
- QAudioSystemFactoryInterface* plugin =
- qobject_cast<QAudioSystemFactoryInterface*>(audioLoader()->instance(deviceInfo.realm()));
-
- if (plugin) {
- QAbstractAudioOutput* p = plugin->createOutput(deviceInfo.handle());
- if (p) p->setFormat(format);
- return p;
- }
-#endif
-
- return new QNullOutputDevice();
-}
-
-QT_END_NAMESPACE
-
diff --git a/src/multimedia/audio/qaudiodevicefactory_p.h b/src/multimedia/audio/qaudiodevicefactory_p.h
deleted file mode 100644
index 238be46a7..000000000
--- a/src/multimedia/audio/qaudiodevicefactory_p.h
+++ /dev/null
@@ -1,91 +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 for the convenience
-// of other Qt classes. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#ifndef QAUDIODEVICEFACTORY_P_H
-#define QAUDIODEVICEFACTORY_P_H
-
-#include <QtCore/qbytearray.h>
-#include <QtCore/qlist.h>
-
-#include <qtmultimediaglobal.h>
-#include <qmultimedia.h>
-
-#include "qaudiodeviceinfo.h"
-
-QT_BEGIN_NAMESPACE
-
-
-class QAbstractAudioInput;
-class QAbstractAudioOutput;
-class QAbstractAudioDeviceInfo;
-
-class QAudioDeviceFactory
-{
-public:
- static QList<QAudioDeviceInfo> availableDevices(QAudio::Mode mode);
-
- static QAudioDeviceInfo defaultDevice(QAudio::Mode mode);
-
- static QAbstractAudioDeviceInfo* audioDeviceInfo(const QString &realm, const QByteArray &handle, QAudio::Mode mode);
-
- static QAbstractAudioInput* createDefaultInputDevice(QAudioFormat const &format);
- static QAbstractAudioOutput* createDefaultOutputDevice(QAudioFormat const &format);
-
- static QAbstractAudioInput* createInputDevice(QAudioDeviceInfo const &device, QAudioFormat const &format);
- static QAbstractAudioOutput* createOutputDevice(QAudioDeviceInfo const &device, QAudioFormat const &format);
-
- static QAbstractAudioInput* createNullInput();
- static QAbstractAudioOutput* createNullOutput();
-};
-
-QT_END_NAMESPACE
-
-#endif // QAUDIODEVICEFACTORY_P_H
-
diff --git a/src/multimedia/audio/qaudiodeviceinfo.cpp b/src/multimedia/audio/qaudiodeviceinfo.cpp
deleted file mode 100644
index 051ef8b3f..000000000
--- a/src/multimedia/audio/qaudiodeviceinfo.cpp
+++ /dev/null
@@ -1,476 +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$
-**
-****************************************************************************/
-
-#include "qaudiodevicefactory_p.h"
-#include "qaudiosystem.h"
-#include "qaudiodeviceinfo.h"
-
-#include <QtCore/qmap.h>
-
-QT_BEGIN_NAMESPACE
-
-static void qRegisterAudioDeviceInfoMetaTypes()
-{
- qRegisterMetaType<QAudioDeviceInfo>();
-}
-
-Q_CONSTRUCTOR_FUNCTION(qRegisterAudioDeviceInfoMetaTypes)
-
-class QAudioDeviceInfoPrivate : public QSharedData
-{
-public:
- QAudioDeviceInfoPrivate()
- : mode(QAudio::AudioOutput)
- , info(nullptr)
- {
- }
-
- QAudioDeviceInfoPrivate(const QString &r, const QByteArray &h, QAudio::Mode m):
- realm(r), handle(h), mode(m)
- {
- if (!handle.isEmpty())
- info = QAudioDeviceFactory::audioDeviceInfo(realm, handle, mode);
- else
- info = nullptr;
- }
-
- QAudioDeviceInfoPrivate(const QAudioDeviceInfoPrivate &other):
- QSharedData(other),
- realm(other.realm), handle(other.handle), mode(other.mode)
- {
- info = QAudioDeviceFactory::audioDeviceInfo(realm, handle, mode);
- }
-
- QAudioDeviceInfoPrivate& operator=(const QAudioDeviceInfoPrivate &other)
- {
- delete info;
-
- realm = other.realm;
- handle = other.handle;
- mode = other.mode;
- info = QAudioDeviceFactory::audioDeviceInfo(realm, handle, mode);
- return *this;
- }
-
- ~QAudioDeviceInfoPrivate()
- {
- delete info;
- }
-
- QString realm;
- QByteArray handle;
- QAudio::Mode mode;
- QAbstractAudioDeviceInfo* info;
-};
-
-
-/*!
- \class QAudioDeviceInfo
- \brief The QAudioDeviceInfo class provides an interface to query audio devices and their functionality.
- \inmodule QtMultimedia
- \ingroup multimedia
- \ingroup multimedia_audio
-
- QAudioDeviceInfo lets you query for audio devices--such as sound
- cards and USB headsets--that are currently available on the system.
- The audio devices available are dependent on the platform or audio plugins installed.
-
- A QAudioDeviceInfo is used by Qt to construct
- classes that communicate with the device--such as
- QAudioInput, and QAudioOutput.
-
- You can also query each device for the formats it supports. A
- format in this context is a set consisting of a specific byte
- order, channel, codec, frequency, sample rate, and sample type. A
- format is represented by the QAudioFormat class.
-
- The values supported by the device for each of these
- parameters can be fetched with
- supportedByteOrders(), supportedChannelCounts(), supportedCodecs(),
- supportedSampleRates(), supportedSampleSizes(), and
- supportedSampleTypes(). The combinations supported are dependent on the platform,
- audio plugins installed and the audio device capabilities. If you need a
- specific format, you can check if
- the device supports it with isFormatSupported(), or fetch a
- supported format that is as close as possible to the format with
- nearestFormat(). For instance:
-
- \snippet multimedia-snippets/audio.cpp Setting audio format
-
- The static
- functions defaultInputDevice(), defaultOutputDevice(), and
- availableDevices() let you get a list of all available
- devices. Devices are fetched according to the value of mode
- this is specified by the \l {QAudio}::Mode enum.
- The QAudioDeviceInfo returned are only valid for the \l {QAudio}::Mode.
-
- For instance:
-
- \snippet multimedia-snippets/audio.cpp Dumping audio formats
-
- In this code sample, we loop through all devices that are able to output
- sound, i.e., play an audio stream in a supported format. For each device we
- find, we simply print the deviceName().
-
- \sa QAudioOutput, QAudioInput
-*/
-
-/*!
- Constructs an empty QAudioDeviceInfo object.
-*/
-QAudioDeviceInfo::QAudioDeviceInfo():
- d(new QAudioDeviceInfoPrivate)
-{
-}
-
-/*!
- Constructs a copy of \a other.
-*/
-QAudioDeviceInfo::QAudioDeviceInfo(const QAudioDeviceInfo& other):
- d(other.d)
-{
-}
-
-/*!
- Destroy this audio device info.
-*/
-QAudioDeviceInfo::~QAudioDeviceInfo()
-{
-}
-
-/*!
- Sets the QAudioDeviceInfo object to be equal to \a other.
-*/
-QAudioDeviceInfo& QAudioDeviceInfo::operator=(const QAudioDeviceInfo &other)
-{
- d = other.d;
- return *this;
-}
-
-/*!
- Returns true if this QAudioDeviceInfo class represents the
- same audio device as \a other.
-*/
-bool QAudioDeviceInfo::operator ==(const QAudioDeviceInfo &other) const
-{
- if (d == other.d)
- return true;
- if (d->realm == other.d->realm
- && d->mode == other.d->mode
- && d->handle == other.d->handle
- && deviceName() == other.deviceName())
- return true;
- return false;
-}
-
-/*!
- Returns true if this QAudioDeviceInfo class represents a
- different audio device than \a other
-*/
-bool QAudioDeviceInfo::operator !=(const QAudioDeviceInfo &other) const
-{
- return !operator==(other);
-}
-
-/*!
- Returns whether this QAudioDeviceInfo object holds a valid device definition.
-*/
-bool QAudioDeviceInfo::isNull() const
-{
- return d->info == nullptr;
-}
-
-/*!
- Returns the human readable name of the audio device.
-
- Device names vary depending on the platform/audio plugin being used.
-
- They are a unique string identifier for the audio device.
-
- eg. default, Intel, U0x46d0x9a4
-*/
-QString QAudioDeviceInfo::deviceName() const
-{
- return isNull() ? QString() : d->info->deviceName();
-}
-
-/*!
- Returns true if the supplied \a settings are supported by the audio
- device described by this QAudioDeviceInfo.
-*/
-bool QAudioDeviceInfo::isFormatSupported(const QAudioFormat &settings) const
-{
- return isNull() ? false : d->info->isFormatSupported(settings);
-}
-
-/*!
- Returns the default audio format settings for this device.
-
- These settings are provided by the platform/audio plugin being used.
-
- They are also dependent on the \l {QAudio}::Mode being used.
-
- A typical audio system would provide something like:
- \list
- \li Input settings: 8000Hz mono 8 bit.
- \li Output settings: 44100Hz stereo 16 bit little endian.
- \endlist
-*/
-QAudioFormat QAudioDeviceInfo::preferredFormat() const
-{
- return isNull() ? QAudioFormat() : d->info->preferredFormat();
-}
-
-/*!
- Returns the closest QAudioFormat to the supplied \a settings that the system supports.
-
- These settings are provided by the platform/audio plugin being used.
-
- They are also dependent on the \l {QAudio}::Mode being used.
-*/
-QAudioFormat QAudioDeviceInfo::nearestFormat(const QAudioFormat &settings) const
-{
- if (isFormatSupported(settings))
- return settings;
-
- QAudioFormat nearest = settings;
-
- QList<QString> testCodecs = supportedCodecs();
- QList<int> testChannels = supportedChannelCounts();
- QList<QAudioFormat::Endian> testByteOrders = supportedByteOrders();
- QList<QAudioFormat::SampleType> testSampleTypes;
- QList<QAudioFormat::SampleType> sampleTypesAvailable = supportedSampleTypes();
- QMap<int,int> testSampleRates;
- QList<int> sampleRatesAvailable = supportedSampleRates();
- QMap<int,int> testSampleSizes;
- QList<int> sampleSizesAvailable = supportedSampleSizes();
-
- // Get sorted lists for checking
- if (testCodecs.contains(settings.codec())) {
- testCodecs.removeAll(settings.codec());
- testCodecs.insert(0, settings.codec());
- }
- testChannels.removeAll(settings.channelCount());
- testChannels.insert(0, settings.channelCount());
- testByteOrders.removeAll(settings.byteOrder());
- testByteOrders.insert(0, settings.byteOrder());
-
- if (sampleTypesAvailable.contains(settings.sampleType()))
- testSampleTypes.append(settings.sampleType());
- if (sampleTypesAvailable.contains(QAudioFormat::SignedInt))
- testSampleTypes.append(QAudioFormat::SignedInt);
- if (sampleTypesAvailable.contains(QAudioFormat::UnSignedInt))
- testSampleTypes.append(QAudioFormat::UnSignedInt);
- if (sampleTypesAvailable.contains(QAudioFormat::Float))
- testSampleTypes.append(QAudioFormat::Float);
-
- if (sampleSizesAvailable.contains(settings.sampleSize()))
- testSampleSizes.insert(0,settings.sampleSize());
- sampleSizesAvailable.removeAll(settings.sampleSize());
- for (int size : qAsConst(sampleSizesAvailable)) {
- int larger = (size > settings.sampleSize()) ? size : settings.sampleSize();
- int smaller = (size > settings.sampleSize()) ? settings.sampleSize() : size;
- bool isMultiple = ( 0 == (larger % smaller));
- int diff = larger - smaller;
- testSampleSizes.insert((isMultiple ? diff : diff+100000), size);
- }
- if (sampleRatesAvailable.contains(settings.sampleRate()))
- testSampleRates.insert(0,settings.sampleRate());
- sampleRatesAvailable.removeAll(settings.sampleRate());
- for (int sampleRate : qAsConst(sampleRatesAvailable)) {
- int larger = (sampleRate > settings.sampleRate()) ? sampleRate : settings.sampleRate();
- int smaller = (sampleRate > settings.sampleRate()) ? settings.sampleRate() : sampleRate;
- bool isMultiple = ( 0 == (larger % smaller));
- int diff = larger - smaller;
- testSampleRates.insert((isMultiple ? diff : diff+100000), sampleRate);
- }
-
- // Try to find nearest
- for (const QString &codec : qAsConst(testCodecs)) {
- nearest.setCodec(codec);
- for (QAudioFormat::Endian order : qAsConst(testByteOrders)) {
- nearest.setByteOrder(order);
- for (QAudioFormat::SampleType sample : qAsConst(testSampleTypes)) {
- nearest.setSampleType(sample);
- for (int sampleSize : qAsConst(testSampleSizes)) {
- nearest.setSampleSize(sampleSize);
- for (int channel : qAsConst(testChannels)) {
- nearest.setChannelCount(channel);
- for (int sampleRate : qAsConst(testSampleRates)) {
- nearest.setSampleRate(sampleRate);
- if (isFormatSupported(nearest))
- return nearest;
- }
- }
- }
- }
- }
- }
- //Fallback
- return preferredFormat();
-}
-
-/*!
- Returns a list of supported codecs.
-
- All platform and plugin implementations should provide support for:
-
- "audio/pcm" - Linear PCM
-
- For writing plugins to support additional codecs refer to:
-
- http://www.iana.org/assignments/media-types/audio/
-*/
-QStringList QAudioDeviceInfo::supportedCodecs() const
-{
- return isNull() ? QStringList() : d->info->supportedCodecs();
-}
-
-/*!
- Returns a list of supported sample rates (in Hertz).
-
-*/
-QList<int> QAudioDeviceInfo::supportedSampleRates() const
-{
- return isNull() ? QList<int>() : d->info->supportedSampleRates();
-}
-
-/*!
- Returns a list of supported channel counts.
-
- This is typically 1 for mono sound, or 2 for stereo sound.
-
-*/
-QList<int> QAudioDeviceInfo::supportedChannelCounts() const
-{
- return isNull() ? QList<int>() : d->info->supportedChannelCounts();
-}
-
-/*!
- Returns a list of supported sample sizes (in bits).
-
- Typically this will include 8 and 16 bit sample sizes.
-
-*/
-QList<int> QAudioDeviceInfo::supportedSampleSizes() const
-{
- return isNull() ? QList<int>() : d->info->supportedSampleSizes();
-}
-
-/*!
- Returns a list of supported byte orders.
-*/
-QList<QAudioFormat::Endian> QAudioDeviceInfo::supportedByteOrders() const
-{
- return isNull() ? QList<QAudioFormat::Endian>() : d->info->supportedByteOrders();
-}
-
-/*!
- Returns a list of supported sample types.
-*/
-QList<QAudioFormat::SampleType> QAudioDeviceInfo::supportedSampleTypes() const
-{
- return isNull() ? QList<QAudioFormat::SampleType>() : d->info->supportedSampleTypes();
-}
-
-/*!
- Returns the information for the default input audio device.
- All platform and audio plugin implementations provide a default audio device to use.
-*/
-QAudioDeviceInfo QAudioDeviceInfo::defaultInputDevice()
-{
- return QAudioDeviceFactory::defaultDevice(QAudio::AudioInput);
-}
-
-/*!
- Returns the information for the default output audio device.
- All platform and audio plugin implementations provide a default audio device to use.
-*/
-QAudioDeviceInfo QAudioDeviceInfo::defaultOutputDevice()
-{
- return QAudioDeviceFactory::defaultDevice(QAudio::AudioOutput);
-}
-
-/*!
- Returns a list of audio devices that support \a mode.
-*/
-QList<QAudioDeviceInfo> QAudioDeviceInfo::availableDevices(QAudio::Mode mode)
-{
- return QAudioDeviceFactory::availableDevices(mode);
-}
-
-
-/*!
- \internal
-*/
-QAudioDeviceInfo::QAudioDeviceInfo(const QString &realm, const QByteArray &handle, QAudio::Mode mode):
- d(new QAudioDeviceInfoPrivate(realm, handle, mode))
-{
-}
-
-/*!
- Returns the key that represents the audio plugin.
-
- \since 5.14
- \sa QAudioSystemPlugin
-*/
-QString QAudioDeviceInfo::realm() const
-{
- return d->realm;
-}
-
-/*!
- \internal
-*/
-QByteArray QAudioDeviceInfo::handle() const
-{
- return d->handle;
-}
-
-
-/*!
- \internal
-*/
-QAudio::Mode QAudioDeviceInfo::mode() const
-{
- return d->mode;
-}
-
-QT_END_NAMESPACE
-
diff --git a/src/multimedia/audio/qaudiodeviceinfo.h b/src/multimedia/audio/qaudiodeviceinfo.h
deleted file mode 100644
index 015c8bad7..000000000
--- a/src/multimedia/audio/qaudiodeviceinfo.h
+++ /dev/null
@@ -1,109 +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 QAUDIODEVICEINFO_H
-#define QAUDIODEVICEINFO_H
-
-#include <QtCore/qobject.h>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qlist.h>
-
-#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qmultimedia.h>
-
-#include <QtMultimedia/qaudio.h>
-#include <QtMultimedia/qaudioformat.h>
-
-QT_BEGIN_NAMESPACE
-
-
-class QAudioDeviceFactory;
-
-class QAudioDeviceInfoPrivate;
-class Q_MULTIMEDIA_EXPORT QAudioDeviceInfo
-{
- friend class QAudioDeviceFactory;
-
-public:
- QAudioDeviceInfo();
- QAudioDeviceInfo(const QAudioDeviceInfo& other);
- ~QAudioDeviceInfo();
-
- QAudioDeviceInfo& operator=(const QAudioDeviceInfo& other);
-
- bool operator==(const QAudioDeviceInfo &other) const;
- bool operator!=(const QAudioDeviceInfo &other) const;
-
- bool isNull() const;
-
- QString deviceName() const;
-
- bool isFormatSupported(const QAudioFormat &format) const;
- QAudioFormat preferredFormat() const;
- QAudioFormat nearestFormat(const QAudioFormat &format) const;
-
- QStringList supportedCodecs() const;
- QList<int> supportedSampleRates() const;
- QList<int> supportedChannelCounts() const;
- QList<int> supportedSampleSizes() const;
- QList<QAudioFormat::Endian> supportedByteOrders() const;
- QList<QAudioFormat::SampleType> supportedSampleTypes() const;
- QString realm() const;
-
- static QAudioDeviceInfo defaultInputDevice();
- static QAudioDeviceInfo defaultOutputDevice();
-
- static QList<QAudioDeviceInfo> availableDevices(QAudio::Mode mode);
-
-private:
- QAudioDeviceInfo(const QString &realm, const QByteArray &handle, QAudio::Mode mode);
- QByteArray handle() const;
- QAudio::Mode mode() const;
-
- QSharedDataPointer<QAudioDeviceInfoPrivate> d;
-};
-
-QT_END_NAMESPACE
-
-Q_DECLARE_METATYPE(QAudioDeviceInfo)
-
-#endif // QAUDIODEVICEINFO_H
diff --git a/src/multimedia/audio/qaudioformat.cpp b/src/multimedia/audio/qaudioformat.cpp
index be3561084..9f51759b5 100644
--- a/src/multimedia/audio/qaudioformat.cpp
+++ b/src/multimedia/audio/qaudioformat.cpp
@@ -1,99 +1,11 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QDebug>
#include <qaudioformat.h>
-
+#include <qalgorithms.h>
QT_BEGIN_NAMESPACE
-static void qRegisterAudioFormatMetaTypes()
-{
- qRegisterMetaType<QAudioFormat>();
- qRegisterMetaType<QAudioFormat::SampleType>();
- qRegisterMetaType<QAudioFormat::Endian>();
-}
-
-Q_CONSTRUCTOR_FUNCTION(qRegisterAudioFormatMetaTypes)
-
-class QAudioFormatPrivate : public QSharedData
-{
-public:
- QAudioFormatPrivate()
- {
- sampleRate = -1;
- channels = -1;
- sampleSize = -1;
- byteOrder = QAudioFormat::Endian(QSysInfo::ByteOrder);
- sampleType = QAudioFormat::Unknown;
- }
-
- QAudioFormatPrivate(const QAudioFormatPrivate &other):
- QSharedData(other),
- codec(other.codec),
- byteOrder(other.byteOrder),
- sampleType(other.sampleType),
- sampleRate(other.sampleRate),
- channels(other.channels),
- sampleSize(other.sampleSize)
- {
- }
-
- QAudioFormatPrivate& operator=(const QAudioFormatPrivate &other)
- {
- codec = other.codec;
- byteOrder = other.byteOrder;
- sampleType = other.sampleType;
- sampleRate = other.sampleRate;
- channels = other.channels;
- sampleSize = other.sampleSize;
-
- return *this;
- }
-
- QString codec;
- QAudioFormat::Endian byteOrder;
- QAudioFormat::SampleType sampleType;
- int sampleRate;
- int channels;
- int sampleSize;
-};
-
/*!
\class QAudioFormat
\brief The QAudioFormat class stores audio stream parameter information.
@@ -102,15 +14,12 @@ public:
\ingroup multimedia
\ingroup multimedia_audio
- An audio format specifies how data in an audio stream is arranged,
- i.e, how the stream is to be interpreted. The encoding itself is
- specified by the codec() used for the stream.
+ An audio format specifies how data in a raw audio stream is arranged. For
+ example, how the stream is to be interpreted.
- In addition to the encoding, QAudioFormat contains other
- parameters that further specify how the audio sample data is arranged.
- These are the frequency, the number of channels, the sample size,
- the sample type, and the byte order. The following table describes
- these in more detail.
+ QAudioFormat contains parameters that specify how the audio sample data
+ is arranged. These are the frequency, the number of channels, and the
+ sample format. The following table describes these in more detail.
\table
\header
@@ -122,241 +31,231 @@ public:
\row
\li Number of channels
\li The number of audio channels (typically one for mono
- or two for stereo)
- \row
- \li Sample size
- \li How much data is stored in each sample (typically 8
- or 16 bits)
+ or two for stereo). These are the amount of consecutive
+ samples that together form one frame in the stream
\row
- \li Sample type
- \li Numerical representation of sample (typically signed integer,
- unsigned integer or float)
- \row
- \li Byte order
- \li Byte ordering of sample (typically little endian, big endian)
+ \li Sample format
+ \li The format of the audio samples in the stream
\endtable
- This class is typically used in conjunction with QAudioInput or
- QAudioOutput to allow you to specify the parameters of the audio
+ This class is used in conjunction with QAudioSource or
+ QAudioSink to allow you to specify the parameters of the audio
stream being read or written, or with QAudioBuffer when dealing with
samples in memory.
You can obtain audio formats compatible with the audio device used
- through functions in QAudioDeviceInfo. This class also lets you
+ through functions in QAudioDevice. This class also lets you
query available parameter values for a device, so that you can set
- the parameters yourself. See the \l QAudioDeviceInfo class
+ the parameters yourself. See the \l QAudioDevice class
description for details. You need to know the format of the audio
streams you wish to play or record.
- In the common case of interleaved linear PCM data, the codec will
- be "audio/pcm", and the samples for all channels will be interleaved.
+ Samples for all channels will be interleaved.
One sample for each channel for the same instant in time is referred
to as a frame in Qt Multimedia (and other places).
*/
/*!
- Construct a new audio format.
+ \fn QAudioFormat::QAudioFormat()
+
+ Constructs a new audio format.
Values are initialized as follows:
\list
- \li sampleRate() = -1
- \li channelCount() = -1
- \li sampleSize() = -1
- \li byteOrder() = QAudioFormat::Endian(QSysInfo::ByteOrder)
- \li sampleType() = QAudioFormat::Unknown
- \c codec() = ""
+ \li sampleRate() = 0
+ \li channelCount() = 0
+ \li sampleFormat() = QAudioFormat::Unknown
\endlist
*/
-QAudioFormat::QAudioFormat():
- d(new QAudioFormatPrivate)
-{
-}
/*!
+ \fn QAudioFormat::QAudioFormat(const QAudioFormat &other)
+
Construct a new audio format using \a other.
*/
-QAudioFormat::QAudioFormat(const QAudioFormat &other):
- d(other.d)
-{
-}
/*!
+ \fn QAudioFormat::~QAudioFormat()
+
Destroy this audio format.
*/
-QAudioFormat::~QAudioFormat()
-{
-}
/*!
- Assigns \a other to this QAudioFormat implementation.
+ \fn bool QAudioFormat::operator==(const QAudioFormat &a, const QAudioFormat &b)
+
+ Returns \c true if audio format \a a is equal to \a b, otherwise returns \c false.
*/
-QAudioFormat& QAudioFormat::operator=(const QAudioFormat &other)
-{
- d = other.d;
- return *this;
-}
/*!
- Returns true if this QAudioFormat is equal to the \a other
- QAudioFormat; otherwise returns false.
+ \fn bool QAudioFormat::operator!=(const QAudioFormat &a, const QAudioFormat &b)
- All elements of QAudioFormat are used for the comparison.
+ Returns \c true if audio format \a a is not equal to \a b, otherwise returns \c false.
*/
-bool QAudioFormat::operator==(const QAudioFormat &other) const
-{
- return d->sampleRate == other.d->sampleRate &&
- d->channels == other.d->channels &&
- d->sampleSize == other.d->sampleSize &&
- d->byteOrder == other.d->byteOrder &&
- d->codec == other.d->codec &&
- d->sampleType == other.d->sampleType;
-}
/*!
- Returns true if this QAudioFormat is not equal to the \a other
- QAudioFormat; otherwise returns false.
+ \fn bool QAudioFormat::isValid() const
- All elements of QAudioFormat are used for the comparison.
+ Returns \c true if all of the parameters are valid.
*/
-bool QAudioFormat::operator!=(const QAudioFormat& other) const
-{
- return !(*this == other);
-}
/*!
- Returns true if all of the parameters are valid.
+ \fn void QAudioFormat::setSampleRate(int samplerate)
+
+ Sets the sample rate to \a samplerate in Hertz.
*/
-bool QAudioFormat::isValid() const
-{
- return d->sampleRate != -1 && d->channels != -1 && d->sampleSize != -1 &&
- d->sampleType != QAudioFormat::Unknown && !d->codec.isEmpty();
-}
/*!
- Sets the sample rate to \a samplerate Hertz.
+ \fn int QAudioFormat::sampleRate() const
+ Returns the current sample rate in Hertz.
*/
-void QAudioFormat::setSampleRate(int samplerate)
-{
- d->sampleRate = samplerate;
-}
/*!
- Returns the current sample rate in Hertz.
-
+ \enum QAudioFormat::AudioChannelPosition
+
+ Describes the possible audio channel positions. These follow the standard
+ definition used in the 22.2 surround sound configuration.
+
+ \value UnknownPosition Unknown position
+ \value FrontLeft
+ \value FrontRight
+ \value FrontCenter
+ \value LFE Low Frequency Effect channel (Subwoofer)
+ \value BackLeft
+ \value BackRight
+ \value FrontLeftOfCenter
+ \value FrontRightOfCenter
+ \value BackCenter
+ \value LFE2
+ \value SideLeft
+ \value SideRight
+ \value TopFrontLeft
+ \value TopFrontRight
+ \value TopFrontCenter
+ \value TopCenter
+ \value TopBackLeft
+ \value TopBackRight
+ \value TopSideLeft
+ \value TopSideRight
+ \value TopBackCenter
+ \value BottomFrontCenter
+ \value BottomFrontLeft
+ \value BottomFrontRight
*/
-int QAudioFormat::sampleRate() const
-{
- return d->sampleRate;
-}
-
/*!
- Sets the channel count to \a channels.
-
+ \variable QAudioFormat::NChannelPositions
+ \internal
*/
-void QAudioFormat::setChannelCount(int channels)
-{
- d->channels = channels;
-}
/*!
- Returns the current channel count value.
-
+ \enum QAudioFormat::ChannelConfig
+
+ This enum describes a standardized audio channel layout. The most common
+ configurations are Mono, Stereo, 2.1 (stereo plus low frequency), 5.1 surround,
+ and 7.1 surround configurations.
+
+ \value ChannelConfigUnknown The channel configuration is not known.
+ \value ChannelConfigMono The audio has one Center channel.
+ \value ChannelConfigStereo The audio has two channels, Left and Right.
+ \value ChannelConfig2Dot1 The audio has three channels, Left, Right and
+ LFE (low frequency effect).
+ \value ChannelConfig3Dot0 The audio has three channels, Left, Right, and
+ Center.
+ \value ChannelConfig3Dot1 The audio has four channels, Left, Right, Center,
+ and LFE (low frequency effect).
+ \value ChannelConfigSurround5Dot0 The audio has five channels, Left, Right,
+ Center, BackLeft, and BackRight.
+ \value ChannelConfigSurround5Dot1 The audio has 6 channels, Left, Right,
+ Center, LFE, BackLeft, and BackRight.
+ \value ChannelConfigSurround7Dot0 The audio has 7 channels, Left, Right,
+ Center, BackLeft, BackRight, SideLeft, and SideRight.
+ \value ChannelConfigSurround7Dot1 The audio has 8 channels, Left, Right,
+ Center, LFE, BackLeft, BackRight, SideLeft, and SideRight.
*/
-int QAudioFormat::channelCount() const
-{
- return d->channels;
-}
/*!
- Sets the sample size to the \a sampleSize specified, in bits.
+ Sets the channel configuration to \a config.
+
+ Sets the channel configuration of the audio format to one of the standard
+ audio channel configurations.
- This is typically 8 or 16, but some systems may support higher sample sizes.
+ \note that this will also modify the channel count.
*/
-void QAudioFormat::setSampleSize(int sampleSize)
+void QAudioFormat::setChannelConfig(ChannelConfig config) noexcept
{
- d->sampleSize = sampleSize;
+ m_channelConfig = config;
+ if (config != ChannelConfigUnknown)
+ m_channelCount = qPopulationCount(config);
}
/*!
- Returns the current sample size value, in bits.
-
- \sa bytesPerFrame()
+ Returns the position of a certain audio \a channel inside an audio frame
+ for the given format.
+ Returns -1 if the channel does not exist for this format or the channel
+ configuration is unknown.
*/
-int QAudioFormat::sampleSize() const
+int QAudioFormat::channelOffset(AudioChannelPosition channel) const noexcept
{
- return d->sampleSize;
+ if (!(m_channelConfig & (1u << channel)))
+ return -1;
+
+ uint maskedChannels = m_channelConfig & ((1u << channel) - 1);
+ return qPopulationCount(maskedChannels);
}
/*!
- Sets the codec to \a codec.
-
- The parameter to this function should be one of the types
- reported by the QAudioDeviceInfo::supportedCodecs() function
- for the audio device you are working with.
+ \fn void QAudioFormat::setChannelCount(int channels)
- \sa QAudioDeviceInfo::supportedCodecs()
+ Sets the channel count to \a channels. Setting this also sets the channel
+ config to ChannelConfigUnknown.
*/
-void QAudioFormat::setCodec(const QString &codec)
-{
- d->codec = codec;
-}
/*!
- Returns the current codec identifier.
+ \fn template <typename... Args> QAudioFormat::ChannelConfig QAudioFormat::channelConfig(Args... channels)
- \sa QAudioDeviceInfo::supportedCodecs()
+ Returns the current channel configuration for the given \a channels.
*/
-QString QAudioFormat::codec() const
-{
- return d->codec;
-}
-
/*!
- Sets the byteOrder to \a byteOrder.
+ \fn QAudioFormat::ChannelConfig QAudioFormat::channelConfig() const noexcept
+
+ Returns the current channel configuration.
*/
-void QAudioFormat::setByteOrder(QAudioFormat::Endian byteOrder)
-{
- d->byteOrder = byteOrder;
-}
/*!
- Returns the current byteOrder value.
+ \fn int QAudioFormat::channelCount() const
+
+ Returns the current channel count value.
+
*/
-QAudioFormat::Endian QAudioFormat::byteOrder() const
-{
- return d->byteOrder;
-}
/*!
- Sets the sampleType to \a sampleType.
+ \fn void QAudioFormat::setSampleFormat(SampleFormat format)
+
+ Sets the sample format to \a format.
+
+ \sa QAudioFormat::SampleFormat
*/
-void QAudioFormat::setSampleType(QAudioFormat::SampleType sampleType)
-{
- d->sampleType = sampleType;
-}
/*!
- Returns the current SampleType value.
+ \fn QAudioFormat::SampleFormat QAudioFormat::sampleFormat() const
+ Returns the current sample format.
+
+ \sa setSampleFormat()
*/
-QAudioFormat::SampleType QAudioFormat::sampleType() const
-{
- return d->sampleType;
-}
/*!
- Returns the number of bytes required for this audio format for \a duration microseconds.
+ Returns the number of bytes required for this audio format for \a microseconds.
Returns 0 if this format is not valid.
- Note that some rounding may occur if \a duration is not an exact fraction of the
+ Note that some rounding may occur if \a microseconds is not an exact fraction of the
sampleRate().
\sa durationForBytes()
*/
-qint32 QAudioFormat::bytesForDuration(qint64 duration) const
+qint32 QAudioFormat::bytesForDuration(qint64 microseconds) const
{
- return bytesPerFrame() * framesForDuration(duration);
+ return bytesPerFrame() * framesForDuration(microseconds);
}
/*!
@@ -371,6 +270,10 @@ qint32 QAudioFormat::bytesForDuration(qint64 duration) const
*/
qint64 QAudioFormat::durationForBytes(qint32 bytes) const
{
+ // avoid compiler warnings about unused variables. [[maybe_unused]] in the header
+ // gives compiler errors on older gcc versions
+ Q_UNUSED(reserved);
+
if (!isValid() || bytes <= 0)
return 0;
@@ -409,17 +312,17 @@ qint32 QAudioFormat::framesForBytes(qint32 byteCount) const
}
/*!
- Returns the number of frames required to represent \a duration microseconds in this format.
+ Returns the number of frames required to represent \a microseconds in this format.
- Note that some rounding may occur if \a duration is not an exact fraction of the
+ Note that some rounding may occur if \a microseconds is not an exact fraction of the
\l sampleRate().
*/
-qint32 QAudioFormat::framesForDuration(qint64 duration) const
+qint32 QAudioFormat::framesForDuration(qint64 microseconds) const
{
if (!isValid())
return 0;
- return qint32((duration * sampleRate()) / 1000000LL);
+ return qint32((microseconds * sampleRate()) / 1000000LL);
}
/*!
@@ -434,85 +337,142 @@ qint64 QAudioFormat::durationForFrames(qint32 frameCount) const
}
/*!
- Returns the number of bytes required to represent one frame (a sample in each channel) in this format.
+ \fn int QAudioFormat::bytesPerFrame() const
+
+ Returns the number of bytes required to represent one frame
+ (a sample in each channel) in this format.
Returns 0 if this format is invalid.
*/
-int QAudioFormat::bytesPerFrame() const
-{
- if (!isValid())
- return 0;
-
- return (sampleSize() * channelCount()) / 8;
-}
/*!
- \enum QAudioFormat::SampleType
+ \fn int QAudioFormat::bytesPerSample() const
+ Returns the number of bytes required to represent one sample in this format.
- \value Unknown Not Set
- \value SignedInt Samples are signed integers
- \value UnSignedInt Samples are unsigned intergers
- \value Float Samples are floats
+ Returns 0 if this format is invalid.
*/
/*!
- \enum QAudioFormat::Endian
-
- \value BigEndian Samples are big endian byte order
- \value LittleEndian Samples are little endian byte order
+ Normalizes the \a sample value to a number between -1 and 1.
+ The method depends on the QaudioFormat.
*/
+float QAudioFormat::normalizedSampleValue(const void *sample) const
+{
+ switch (m_sampleFormat) {
+ case UInt8:
+ return ((float)*reinterpret_cast<const quint8 *>(sample))
+ / (float)std::numeric_limits<qint8>::max()
+ - 1.;
+ case Int16:
+ return ((float)*reinterpret_cast<const qint16 *>(sample))
+ / (float)std::numeric_limits<qint16>::max();
+ case Int32:
+ return ((float)*reinterpret_cast<const qint32 *>(sample))
+ / (float)std::numeric_limits<qint32>::max();
+ case Float:
+ return *reinterpret_cast<const float *>(sample);
+ case Unknown:
+ case NSampleFormats:
+ break;
+ }
-#ifndef QT_NO_DEBUG_STREAM
-QDebug operator<<(QDebug dbg, QAudioFormat::Endian endian)
+ return 0.;
+}
+
+/*!
+ Returns a default channel configuration for \a channelCount.
+
+ Default configurations are defined for up to 8 channels, and correspond to
+ standard Mono, Stereo and Surround configurations. For higher channel counts,
+ this simply uses the first \a channelCount audio channels defined in
+ \l QAudioFormat::AudioChannelPosition.
+*/
+QAudioFormat::ChannelConfig QAudioFormat::defaultChannelConfigForChannelCount(int channelCount)
{
- QDebugStateSaver saver(dbg);
- dbg.nospace();
- switch (endian) {
- case QAudioFormat::BigEndian:
- dbg << "BigEndian";
- break;
- case QAudioFormat::LittleEndian:
- dbg << "LittleEndian";
- break;
+ QAudioFormat::ChannelConfig config;
+ switch (channelCount) {
+ case 0:
+ config = QAudioFormat::ChannelConfigUnknown;
+ break;
+ case 1:
+ config = QAudioFormat::ChannelConfigMono;
+ break;
+ case 2:
+ config = QAudioFormat::ChannelConfigStereo;
+ break;
+ case 3:
+ config = QAudioFormat::ChannelConfig2Dot1;
+ break;
+ case 4:
+ config = QAudioFormat::channelConfig(QAudioFormat::FrontLeft, QAudioFormat::FrontRight,
+ QAudioFormat::BackLeft, QAudioFormat::BackRight);
+ break;
+ case 5:
+ config = QAudioFormat::ChannelConfigSurround5Dot0;
+ break;
+ case 6:
+ config = QAudioFormat::ChannelConfigSurround5Dot1;
+ break;
+ case 7:
+ config = QAudioFormat::ChannelConfigSurround7Dot0;
+ break;
+ case 8:
+ config = QAudioFormat::ChannelConfigSurround7Dot1;
+ break;
+ default:
+ // give up, simply use the first n channels
+ config = QAudioFormat::ChannelConfig((1 << (channelCount + 1)) - 1);
}
- return dbg;
+ return config;
}
-QDebug operator<<(QDebug dbg, QAudioFormat::SampleType type)
+/*!
+ \enum QAudioFormat::SampleFormat
+
+ Qt will always expect and use samples in the endianness of the host platform.
+ When processing audio data from external sources yourself, ensure you convert
+ them to the correct endianness before writing them to a QAudioSink or
+ QAudioBuffer.
+
+ \value Unknown Not Set
+ \value UInt8 Samples are 8 bit unsigned integers
+ \value Int16 Samples are 16 bit signed integers
+ \value Int32 Samples are 32 bit signed integers
+ \value Float Samples are floats
+ \omitvalue NSampleFormats
+*/
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, QAudioFormat::SampleFormat type)
{
QDebugStateSaver saver(dbg);
dbg.nospace();
switch (type) {
- case QAudioFormat::SignedInt:
- dbg << "SignedInt";
- break;
- case QAudioFormat::UnSignedInt:
- dbg << "UnSignedInt";
- break;
- case QAudioFormat::Float:
- dbg << "Float";
- break;
- default:
- dbg << "Unknown";
- break;
+ case QAudioFormat::UInt8:
+ dbg << "UInt8";
+ break;
+ case QAudioFormat::Int16:
+ dbg << "Int16";
+ break;
+ case QAudioFormat::Int32:
+ dbg << "Int32";
+ break;
+ case QAudioFormat::Float:
+ dbg << "Float";
+ break;
+ default:
+ dbg << "Unknown";
+ break;
}
return dbg;
}
QDebug operator<<(QDebug dbg, const QAudioFormat &f)
{
- QDebugStateSaver saver(dbg);
- dbg.nospace();
- dbg << "QAudioFormat(" << f.sampleRate() << "Hz, "
- << f.sampleSize() << "bit, channelCount=" << f.channelCount()
- << ", sampleType=" << f.sampleType() << ", byteOrder=" << f.byteOrder()
- << ", codec=" << f.codec() << ')';
-
+ dbg << "QAudioFormat(" << f.sampleRate() << "Hz, " << f.channelCount() << "Channels, "
+ << f.sampleFormat() << "Format )";
return dbg;
}
#endif
-
-
QT_END_NAMESPACE
-
diff --git a/src/multimedia/audio/qaudioformat.h b/src/multimedia/audio/qaudioformat.h
index 97779ea2c..a04ab6e1a 100644
--- a/src/multimedia/audio/qaudioformat.h
+++ b/src/multimedia/audio/qaudioformat.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIOFORMAT_H
@@ -45,72 +9,152 @@
#include <QtCore/qshareddata.h>
#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qmultimedia.h>
QT_BEGIN_NAMESPACE
class QAudioFormatPrivate;
-class Q_MULTIMEDIA_EXPORT QAudioFormat
+namespace QtPrivate {
+template <typename... Args>
+constexpr int channelConfig(Args... values) {
+ return (0 | ... | (1u << values));
+}
+}
+
+class QAudioFormat
{
public:
- enum SampleType { Unknown, SignedInt, UnSignedInt, Float };
- enum Endian { BigEndian = QSysInfo::BigEndian, LittleEndian = QSysInfo::LittleEndian };
-
- QAudioFormat();
- QAudioFormat(const QAudioFormat &other);
- ~QAudioFormat();
-
- QAudioFormat& operator=(const QAudioFormat &other);
- bool operator==(const QAudioFormat &other) const;
- bool operator!=(const QAudioFormat &other) const;
-
- bool isValid() const;
-
- void setSampleRate(int sampleRate);
- int sampleRate() const;
-
- void setChannelCount(int channelCount);
- int channelCount() const;
-
- void setSampleSize(int sampleSize);
- int sampleSize() const;
-
- void setCodec(const QString &codec);
- QString codec() const;
-
- void setByteOrder(QAudioFormat::Endian byteOrder);
- QAudioFormat::Endian byteOrder() const;
-
- void setSampleType(QAudioFormat::SampleType sampleType);
- QAudioFormat::SampleType sampleType() const;
+ enum SampleFormat : quint16 {
+ Unknown,
+ UInt8,
+ Int16,
+ Int32,
+ Float,
+ NSampleFormats
+ };
+
+ // This matches the speaker positions of a 22.2 audio layout. Stereo, Surround 5.1 and Surround 7.1 are subsets of these
+ enum AudioChannelPosition {
+ UnknownPosition,
+ FrontLeft,
+ FrontRight,
+ FrontCenter,
+ LFE,
+ BackLeft,
+ BackRight,
+ FrontLeftOfCenter,
+ FrontRightOfCenter,
+ BackCenter,
+ SideLeft,
+ SideRight,
+ TopCenter,
+ TopFrontLeft,
+ TopFrontCenter,
+ TopFrontRight,
+ TopBackLeft,
+ TopBackCenter,
+ TopBackRight,
+ LFE2,
+ TopSideLeft,
+ TopSideRight,
+ BottomFrontCenter,
+ BottomFrontLeft,
+ BottomFrontRight
+ };
+ static constexpr int NChannelPositions = BottomFrontRight + 1;
+
+ enum ChannelConfig : quint32 {
+ ChannelConfigUnknown = 0,
+ ChannelConfigMono = QtPrivate::channelConfig(FrontCenter),
+ ChannelConfigStereo = QtPrivate::channelConfig(FrontLeft, FrontRight),
+ ChannelConfig2Dot1 = QtPrivate::channelConfig(FrontLeft, FrontRight, LFE),
+ ChannelConfig3Dot0 = QtPrivate::channelConfig(FrontLeft, FrontRight, FrontCenter),
+ ChannelConfig3Dot1 = QtPrivate::channelConfig(FrontLeft, FrontRight, FrontCenter, LFE),
+ ChannelConfigSurround5Dot0 = QtPrivate::channelConfig(FrontLeft, FrontRight, FrontCenter, BackLeft, BackRight),
+ ChannelConfigSurround5Dot1 = QtPrivate::channelConfig(FrontLeft, FrontRight, FrontCenter, LFE, BackLeft, BackRight),
+ ChannelConfigSurround7Dot0 = QtPrivate::channelConfig(FrontLeft, FrontRight, FrontCenter, BackLeft, BackRight, SideLeft, SideRight),
+ ChannelConfigSurround7Dot1 = QtPrivate::channelConfig(FrontLeft, FrontRight, FrontCenter, LFE, BackLeft, BackRight, SideLeft, SideRight),
+ };
+
+ template <typename... Args>
+ static constexpr ChannelConfig channelConfig(Args... channels)
+ {
+ return ChannelConfig(QtPrivate::channelConfig(channels...));
+ }
+
+ constexpr bool isValid() const noexcept
+ {
+ return m_sampleRate > 0 && m_channelCount > 0 && m_sampleFormat != Unknown;
+ }
+
+ constexpr void setSampleRate(int sampleRate) noexcept { m_sampleRate = sampleRate; }
+ constexpr int sampleRate() const noexcept { return m_sampleRate; }
+
+ Q_MULTIMEDIA_EXPORT void setChannelConfig(ChannelConfig config) noexcept;
+ constexpr ChannelConfig channelConfig() const noexcept { return m_channelConfig; }
+
+ constexpr void setChannelCount(int channelCount) noexcept { m_channelConfig = ChannelConfigUnknown; m_channelCount = channelCount; }
+ constexpr int channelCount() const noexcept { return m_channelCount; }
+
+ Q_MULTIMEDIA_EXPORT int channelOffset(AudioChannelPosition channel) const noexcept;
+
+ constexpr void setSampleFormat(SampleFormat f) noexcept { m_sampleFormat = f; }
+ constexpr SampleFormat sampleFormat() const noexcept { return m_sampleFormat; }
// Helper functions
- qint32 bytesForDuration(qint64 duration) const;
- qint64 durationForBytes(qint32 byteCount) const;
-
- qint32 bytesForFrames(qint32 frameCount) const;
- qint32 framesForBytes(qint32 byteCount) const;
-
- qint32 framesForDuration(qint64 duration) const;
- qint64 durationForFrames(qint32 frameCount) const;
-
- int bytesPerFrame() const;
+ Q_MULTIMEDIA_EXPORT qint32 bytesForDuration(qint64 microseconds) const;
+ Q_MULTIMEDIA_EXPORT qint64 durationForBytes(qint32 byteCount) const;
+
+ Q_MULTIMEDIA_EXPORT qint32 bytesForFrames(qint32 frameCount) const;
+ Q_MULTIMEDIA_EXPORT qint32 framesForBytes(qint32 byteCount) const;
+
+ Q_MULTIMEDIA_EXPORT qint32 framesForDuration(qint64 microseconds) const;
+ Q_MULTIMEDIA_EXPORT qint64 durationForFrames(qint32 frameCount) const;
+
+ constexpr int bytesPerFrame() const { return bytesPerSample()*channelCount(); }
+ constexpr int bytesPerSample() const noexcept
+ {
+ switch (m_sampleFormat) {
+ case Unknown:
+ case NSampleFormats: return 0;
+ case UInt8: return 1;
+ case Int16: return 2;
+ case Int32:
+ case Float: return 4;
+ }
+ return 0;
+ }
+
+ Q_MULTIMEDIA_EXPORT float normalizedSampleValue(const void *sample) const;
+
+ friend bool operator==(const QAudioFormat &a, const QAudioFormat &b)
+ {
+ return a.m_sampleRate == b.m_sampleRate &&
+ a.m_channelCount == b.m_channelCount &&
+ a.m_sampleFormat == b.m_sampleFormat;
+ }
+ friend bool operator!=(const QAudioFormat &a, const QAudioFormat &b)
+ {
+ return !(a == b);
+ }
+
+ static Q_MULTIMEDIA_EXPORT ChannelConfig defaultChannelConfigForChannelCount(int channelCount);
private:
- QSharedDataPointer<QAudioFormatPrivate> d;
+ SampleFormat m_sampleFormat = SampleFormat::Unknown;
+ short m_channelCount = 0;
+ ChannelConfig m_channelConfig = ChannelConfigUnknown;
+ int m_sampleRate = 0;
+ quint64 reserved = 0;
};
#ifndef QT_NO_DEBUG_STREAM
Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, const QAudioFormat &);
-Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, QAudioFormat::SampleType);
-Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, QAudioFormat::Endian);
+Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, QAudioFormat::SampleFormat);
#endif
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QAudioFormat)
-Q_DECLARE_METATYPE(QAudioFormat::SampleType)
-Q_DECLARE_METATYPE(QAudioFormat::Endian)
#endif // QAUDIOFORMAT_H
diff --git a/src/multimedia/audio/qaudiohelpers.cpp b/src/multimedia/audio/qaudiohelpers.cpp
index fae591477..c2d8681c6 100644
--- a/src/multimedia/audio/qaudiohelpers.cpp
+++ b/src/multimedia/audio/qaudiohelpers.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qaudiohelpers_p.h"
@@ -43,49 +7,6 @@
QT_BEGIN_NAMESPACE
-// Base implementation of 24 bits number.
-// Used to adjust 3 bytes values by a factor.
-// TODO: Uses little-endian only.
-class Int24
-{
-public:
- quint8 data[3];
- Int24(qint32 v) {
- data[0] = v & 0xFF;
- data[1] = (v & 0xFF00) >> 8;
- data[2] = (v & 0xFF0000) >> 16;
- }
- template<class T>
- T multiply(qreal factor, T v = 0) const {
- v |= data[0];
- v |= data[1] << 8;
- v |= data[2] << 16;
- v *= factor;
- return v;
- }
-};
-
-class qint24: public Int24
-{
-public:
- qint24(qint32 v): Int24(v) {}
- qint24 operator*(qreal factor) const {
- // Checks if it is a signed value.
- qint32 v = (data[2] & 0x80) ? 0xFF000000 : 0;
- return multiply(factor, v);
- }
-};
-
-class quint24: public Int24
-{
-public:
- quint24(quint32 v): Int24(v) {}
- quint24 operator*(qreal factor) const {
- return multiply<quint32>(factor);
- }
-};
-
-
namespace QAudioHelperInternal
{
@@ -102,20 +23,8 @@ template<class T> void adjustSamples(qreal factor, const void *src, void *dst, i
template<class T> struct signedVersion {};
template<> struct signedVersion<quint8>
{
- typedef qint8 TS;
- enum {offset = 0x80};
-};
-
-template<> struct signedVersion<quint16>
-{
- typedef qint16 TS;
- enum {offset = 0x8000};
-};
-
-template<> struct signedVersion<quint32>
-{
- typedef qint32 TS;
- enum {offset = 0x80000000};
+ using TS = qint8;
+ static constexpr int offset = 0x80;
};
template<class T> void adjustUnsignedSamples(qreal factor, const void *src, void *dst, int samples)
@@ -129,34 +38,24 @@ template<class T> void adjustUnsignedSamples(qreal factor, const void *src, void
void qMultiplySamples(qreal factor, const QAudioFormat &format, const void* src, void* dest, int len)
{
- int samplesCount = len / (format.sampleSize()/8);
+ const int samplesCount = len / qMax(1, format.bytesPerSample());
- switch ( format.sampleSize() ) {
- case 8:
- if (format.sampleType() == QAudioFormat::SignedInt)
- QAudioHelperInternal::adjustSamples<qint8>(factor,src,dest,samplesCount);
- else if (format.sampleType() == QAudioFormat::UnSignedInt)
- QAudioHelperInternal::adjustUnsignedSamples<quint8>(factor,src,dest,samplesCount);
+ switch (format.sampleFormat()) {
+ case QAudioFormat::Unknown:
+ case QAudioFormat::NSampleFormats:
+ return;
+ case QAudioFormat::UInt8:
+ QAudioHelperInternal::adjustUnsignedSamples<quint8>(factor,src,dest,samplesCount);
+ break;
+ case QAudioFormat::Int16:
+ QAudioHelperInternal::adjustSamples<qint16>(factor,src,dest,samplesCount);
break;
- case 16:
- if (format.sampleType() == QAudioFormat::SignedInt)
- QAudioHelperInternal::adjustSamples<qint16>(factor,src,dest,samplesCount);
- else if (format.sampleType() == QAudioFormat::UnSignedInt)
- QAudioHelperInternal::adjustUnsignedSamples<quint16>(factor,src,dest,samplesCount);
+ case QAudioFormat::Int32:
+ QAudioHelperInternal::adjustSamples<qint32>(factor,src,dest,samplesCount);
break;
- case 24:
- if (format.sampleType() == QAudioFormat::SignedInt)
- QAudioHelperInternal::adjustSamples<qint24>(factor,src,dest,samplesCount);
- else if (format.sampleType() == QAudioFormat::UnSignedInt)
- QAudioHelperInternal::adjustSamples<quint24>(factor,src,dest,samplesCount);
+ case QAudioFormat::Float:
+ QAudioHelperInternal::adjustSamples<float>(factor,src,dest,samplesCount);
break;
- default:
- if (format.sampleType() == QAudioFormat::SignedInt)
- QAudioHelperInternal::adjustSamples<qint32>(factor,src,dest,samplesCount);
- else if (format.sampleType() == QAudioFormat::UnSignedInt)
- QAudioHelperInternal::adjustUnsignedSamples<quint32>(factor,src,dest,samplesCount);
- else if (format.sampleType() == QAudioFormat::Float)
- QAudioHelperInternal::adjustSamples<float>(factor,src,dest,samplesCount);
}
}
}
diff --git a/src/multimedia/audio/qaudiohelpers_p.h b/src/multimedia/audio/qaudiohelpers_p.h
index c59e01cad..81e6c6382 100644
--- a/src/multimedia/audio/qaudiohelpers_p.h
+++ b/src/multimedia/audio/qaudiohelpers_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIOHELPERS_H
#define QAUDIOHELPERS_H
@@ -52,6 +16,7 @@
//
#include <qaudioformat.h>
+#include <private/qglobal_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/multimedia/audio/qaudioinput.cpp b/src/multimedia/audio/qaudioinput.cpp
index 872dce819..dc7d7335f 100644
--- a/src/multimedia/audio/qaudioinput.cpp
+++ b/src/multimedia/audio/qaudioinput.cpp
@@ -1,413 +1,192 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-
-#include "qaudio.h"
-#include "qaudiodeviceinfo.h"
-#include "qaudiosystem.h"
-#include "qaudioinput.h"
-
-#include "qaudiodevicefactory_p.h"
-
-QT_BEGIN_NAMESPACE
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-/*!
- \class QAudioInput
- \brief The QAudioInput class provides an interface for receiving audio data from an audio input device.
-
- \inmodule QtMultimedia
- \ingroup multimedia
- \ingroup multimedia_audio
-
- You can construct an audio input with the system's
- \l{QAudioDeviceInfo::defaultInputDevice()}{default audio input
- device}. It is also possible to create QAudioInput with a
- specific QAudioDeviceInfo. When you create the audio input, you
- should also send in the QAudioFormat to be used for the recording
- (see the QAudioFormat class description for details).
-
- To record to a file:
-
- QAudioInput lets you record audio with an audio input device. The
- default constructor of this class will use the systems default
- audio device, but you can also specify a QAudioDeviceInfo for a
- specific device. You also need to pass in the QAudioFormat in
- which you wish to record.
-
- Starting up the QAudioInput is simply a matter of calling start()
- with a QIODevice opened for writing. For instance, to record to a
- file, you can:
-
- \snippet multimedia-snippets/audio.cpp Audio input class members
-
- \snippet multimedia-snippets/audio.cpp Audio input setup
-
- This will start recording if the format specified is supported by
- the input device (you can check this with
- QAudioDeviceInfo::isFormatSupported(). In case there are any
- snags, use the error() function to check what went wrong. We stop
- recording in the \c stopRecording() slot.
-
- \snippet multimedia-snippets/audio.cpp Audio input stop recording
-
- At any point in time, QAudioInput will be in one of four states:
- active, suspended, stopped, or idle. These states are specified by
- the QAudio::State enum. You can request a state change directly through
- suspend(), resume(), stop(), reset(), and start(). The current
- state is reported by state(). QAudioOutput will also signal you
- when the state changes (stateChanged()).
+#include <qaudioinput.h>
+#include <qaudiodevice.h>
+#include <qmediadevices.h>
+#include <private/qplatformaudioinput_p.h>
+#include <private/qplatformmediaintegration_p.h>
- QAudioInput provides several ways of measuring the time that has
- passed since the start() of the recording. The \c processedUSecs()
- function returns the length of the stream in microseconds written,
- i.e., it leaves out the times the audio input was suspended or idle.
- The elapsedUSecs() function returns the time elapsed since start() was called regardless of
- which states the QAudioInput has been in.
-
- If an error should occur, you can fetch its reason with error().
- The possible error reasons are described by the QAudio::Error
- enum. The QAudioInput will enter the \l{QAudio::}{StoppedState} when
- an error is encountered. Connect to the stateChanged() signal to
- handle the error:
-
- \snippet multimedia-snippets/audio.cpp Audio input state changed
-
- \sa QAudioOutput, QAudioDeviceInfo
-*/
+#include <utility>
/*!
- Construct a new audio input and attach it to \a parent.
- The default audio input device is used with the output
- \a format parameters.
-*/
+ \qmltype AudioInput
+ \instantiates QAudioInput
+ \brief An audio input to be used for capturing audio in a capture session.
-QAudioInput::QAudioInput(const QAudioFormat &format, QObject *parent):
- QObject(parent)
-{
- d = QAudioDeviceFactory::createDefaultInputDevice(format);
- connect(d, SIGNAL(notify()), SIGNAL(notify()));
- connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State)));
-}
+ \inqmlmodule QtMultimedia
+ \ingroup multimedia_qml
+ \ingroup multimedia_audio_qml
-/*!
- Construct a new audio input and attach it to \a parent.
- The device referenced by \a audioDevice is used with the input
- \a format parameters.
-*/
+ \qml
+ CaptureSession {
+ id: playMusic
+ audioInput: AudioInput {
+ volume: slider.value
+ }
+ recorder: MediaRecorder { ... }
+ }
+ Slider {
+ id: slider
+ from: 0.
+ to: 1.
+ }
+ \endqml
-QAudioInput::QAudioInput(const QAudioDeviceInfo &audioDevice, const QAudioFormat &format, QObject *parent):
- QObject(parent)
-{
- d = QAudioDeviceFactory::createInputDevice(audioDevice, format);
- connect(d, SIGNAL(notify()), SIGNAL(notify()));
- connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State)));
-}
+ You can use AudioInput together with a QtMultiMedia::CaptureSession to capture audio from an
+ audio input device.
-/*!
- Destroy this audio input.
+ \sa Camera, AudioOutput
*/
-QAudioInput::~QAudioInput()
-{
- delete d;
-}
-
/*!
- Starts transferring audio data from the system's audio input to the \a device.
- The \a device must have been opened in the \l{QIODevice::WriteOnly}{WriteOnly},
- \l{QIODevice::Append}{Append} or \l{QIODevice::ReadWrite}{ReadWrite} modes.
-
- If the QAudioInput is able to successfully get audio data, state() returns
- either QAudio::ActiveState or QAudio::IdleState, error() returns QAudio::NoError
- and the stateChanged() signal is emitted.
-
- If a problem occurs during this process, error() returns QAudio::OpenError,
- state() returns QAudio::StoppedState and the stateChanged() signal is emitted.
-
- \sa QIODevice
-*/
-
-void QAudioInput::start(QIODevice* device)
-{
- d->start(device);
-}
-
-/*!
- Returns a pointer to the internal QIODevice being used to transfer data from
- the system's audio input. The device will already be open and
- \l{QIODevice::read()}{read()} can read data directly from it.
-
- \note The pointer will become invalid after the stream is stopped or
- if you start another stream.
-
- If the QAudioInput is able to access the system's audio device, state() returns
- QAudio::IdleState, error() returns QAudio::NoError
- and the stateChanged() signal is emitted.
-
- If a problem occurs during this process, error() returns QAudio::OpenError,
- state() returns QAudio::StoppedState and the stateChanged() signal is emitted.
+ \class QAudioInput
+ \brief Represents an input channel for audio.
+ \inmodule QtMultimedia
+ \ingroup multimedia
+ \ingroup multimedia_audio
- \sa QIODevice
+ This class represents an input channel that can be used together with
+ QMediaCaptureSession. It enables the selection of the physical input device
+ to be used, muting the channel, and changing the channel's volume.
*/
-QIODevice* QAudioInput::start()
+QAudioInput::QAudioInput(QObject *parent) : QAudioInput(QMediaDevices::defaultAudioInput(), parent)
{
- return d->start();
}
-/*!
- Returns the QAudioFormat being used.
-*/
-
-QAudioFormat QAudioInput::format() const
+QAudioInput::QAudioInput(const QAudioDevice &device, QObject *parent)
+ : QObject(parent)
{
- return d->format();
+ auto maybeAudioInput = QPlatformMediaIntegration::instance()->createAudioInput(this);
+ if (maybeAudioInput) {
+ d = maybeAudioInput.value();
+ d->device = device.mode() == QAudioDevice::Input ? device : QMediaDevices::defaultAudioInput();
+ d->setAudioDevice(d->device);
+ } else {
+ d = new QPlatformAudioInput(nullptr);
+ qWarning() << "Failed to initialize QAudioInput" << maybeAudioInput.error();
+ }
}
-/*!
- Stops the audio input, detaching from the system resource.
-
- Sets error() to QAudio::NoError, state() to QAudio::StoppedState and
- emit stateChanged() signal.
-*/
-
-void QAudioInput::stop()
+QAudioInput::~QAudioInput()
{
- d->stop();
+ setDisconnectFunction({});
+ delete d;
}
/*!
- Drops all audio data in the buffers, resets buffers to zero.
-*/
+ \qmlproperty real QtMultimedia::AudioInput::volume
-void QAudioInput::reset()
-{
- d->reset();
-}
+ The volume is scaled linearly, ranging from \c 0 (silence) to \c 1 (full volume).
+ \note values outside this range will be clamped.
-/*!
- Stops processing audio data, preserving buffered audio data.
+ By default the volume is \c 1.
- Sets error() to QAudio::NoError, state() to QAudio::SuspendedState and
- emit stateChanged() signal.
+ UI volume controls should usually be scaled non-linearly. For example,
+ using a logarithmic scale will produce linear changes in perceived loudness,
+ which is what a user would normally expect from a volume control.
+ \sa QtAudio::convertVolume()
*/
-
-void QAudioInput::suspend()
-{
- d->suspend();
-}
-
/*!
- Resumes processing audio data after a suspend().
+ \property QAudioInput::volume
- Sets error() to QAudio::NoError.
- Sets state() to QAudio::ActiveState if you previously called start(QIODevice*).
- Sets state() to QAudio::IdleState if you previously called start().
- emits stateChanged() signal.
+ The property returns the volume of the audio input.
*/
-
-void QAudioInput::resume()
+float QAudioInput::volume() const
{
- d->resume();
+ return d->volume;
}
-/*!
- Sets the audio buffer size to \a value bytes.
-
- Note: This function can be called anytime before start(), calls to this
- are ignored after start(). It should not be assumed that the buffer size
- set is the actual buffer size used, calling bufferSize() anytime after start()
- will return the actual buffer size being used.
-
-*/
-
-void QAudioInput::setBufferSize(int value)
+void QAudioInput::setVolume(float volume)
{
- d->setBufferSize(value);
+ volume = qBound(0., volume, 1.);
+ if (d->volume == volume)
+ return;
+ d->volume = volume;
+ d->setVolume(volume);
+ emit volumeChanged(volume);
}
/*!
- Returns the audio buffer size in bytes.
+ \qmlproperty bool QtMultimedia::AudioInput::muted
- If called before start(), returns platform default value.
- If called before start() but setBufferSize() was called prior, returns value set by setBufferSize().
- If called after start(), returns the actual buffer size being used. This may not be what was set previously
- by setBufferSize().
+ This property holds whether the audio input is muted.
+ Defaults to \c{false}.
*/
-int QAudioInput::bufferSize() const
-{
- return d->bufferSize();
-}
-
/*!
- Returns the amount of audio data available to read in bytes.
+ \property QAudioInput::muted
+ \brief The muted state of the current media.
- Note: returned value is only valid while in QAudio::ActiveState or QAudio::IdleState
- state, otherwise returns zero.
+ The value will be \c true if the input is muted; otherwise \c false.
*/
-
-int QAudioInput::bytesReady() const
+bool QAudioInput::isMuted() const
{
- /*
- -If not ActiveState|IdleState, return 0
- -return amount of audio data available to read
- */
- return d->bytesReady();
+ return d->muted;
}
-/*!
- Returns the period size in bytes.
-
- Note: This is the recommended read size in bytes.
-*/
-
-int QAudioInput::periodSize() const
+void QAudioInput::setMuted(bool muted)
{
- return d->periodSize();
+ if (d->muted == muted)
+ return;
+ d->muted = muted;
+ d->setMuted(muted);
+ emit mutedChanged(muted);
}
/*!
- Sets the interval for notify() signal to be emitted.
- This is based on the \a ms of audio data processed
- not on actual real-time.
- The minimum resolution of the timer is platform specific and values
- should be checked with notifyInterval() to confirm actual value
- being used.
-*/
+ \qmlproperty AudioDevice QtMultimedia::AudioInput::device
-void QAudioInput::setNotifyInterval(int ms)
-{
- d->setNotifyInterval(ms);
-}
+ This property describes the audio device connected to this input.
-/*!
- Returns the notify interval in milliseconds.
+ The device property represents the audio device this input is connected to.
+ This property can be used to select an output device from the
+ QtMultimedia::MediaDevices::audioInputs() list.
*/
-int QAudioInput::notifyInterval() const
-{
- return d->notifyInterval();
-}
-
/*!
- Sets the input volume to \a volume.
+ \property QAudioInput::device
+ \brief The audio device connected to this input.
- The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume). Values outside this
- range will be clamped.
+ The device property represents the audio device connected to this input.
+ This property can be used to select an input device from the
+ QMediaDevices::audioInputs() list.
- If the device does not support adjusting the input
- volume then \a volume will be ignored and the input
- volume will remain at 1.0.
-
- The default volume is \c 1.0.
-
- Note: Adjustments to the volume will change the volume of this audio stream, not the global volume.
+ You can select the system default audio input by setting this property to
+ a default constructed QAudioDevice object.
*/
-void QAudioInput::setVolume(qreal volume)
+QAudioDevice QAudioInput::device() const
{
- qreal v = qBound(qreal(0.0), volume, qreal(1.0));
- d->setVolume(v);
+ return d->device;
}
-/*!
- Returns the input volume.
-
- If the device does not support adjusting the input volume
- the returned value will be 1.0.
-*/
-qreal QAudioInput::volume() const
-{
- return d->volume();
-}
-
-/*!
- Returns the amount of audio data processed since start()
- was called in microseconds.
-*/
-
-qint64 QAudioInput::processedUSecs() const
+void QAudioInput::setDevice(const QAudioDevice &device)
{
- return d->processedUSecs();
+ auto dev = device;
+ if (dev.isNull())
+ dev = QMediaDevices::defaultAudioInput();
+ if (dev.mode() != QAudioDevice::Input)
+ return;
+ if (d->device == dev)
+ return;
+ d->device = dev;
+ d->setAudioDevice(dev);
+ emit deviceChanged();
}
/*!
- Returns the microseconds since start() was called, including time in Idle and
- Suspend states.
+ \internal
*/
-
-qint64 QAudioInput::elapsedUSecs() const
-{
- return d->elapsedUSecs();
-}
-
-/*!
- Returns the error state.
-*/
-
-QAudio::Error QAudioInput::error() const
+void QAudioInput::setDisconnectFunction(std::function<void()> disconnectFunction)
{
- return d->error();
+ if (d->disconnectFunction) {
+ auto df = d->disconnectFunction;
+ d->disconnectFunction = {};
+ df();
+ }
+ d->disconnectFunction = std::move(disconnectFunction);
}
-/*!
- Returns the state of audio processing.
-*/
-
-QAudio::State QAudioInput::state() const
-{
- return d->state();
-}
-
-/*!
- \fn QAudioInput::stateChanged(QAudio::State state)
- This signal is emitted when the device \a state has changed.
-*/
-
-/*!
- \fn QAudioInput::notify()
- This signal is emitted when x ms of audio data has been processed
- the interval set by setNotifyInterval(x).
-*/
-
-QT_END_NAMESPACE
-
#include "moc_qaudioinput.cpp"
-
diff --git a/src/multimedia/audio/qaudioinput.h b/src/multimedia/audio/qaudioinput.h
index b17fd4d79..6f4f6b099 100644
--- a/src/multimedia/audio/qaudioinput.h
+++ b/src/multimedia/audio/qaudioinput.h
@@ -1,109 +1,54 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QAUDIOINPUTDEVICE_H
+#define QAUDIOINPUTDEVICE_H
-#ifndef QAUDIOINPUT_H
-#define QAUDIOINPUT_H
-
-#include <QtCore/qiodevice.h>
-
+#include <QtCore/qobject.h>
#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qmultimedia.h>
-
-#include <QtMultimedia/qaudio.h>
-#include <QtMultimedia/qaudioformat.h>
-#include <QtMultimedia/qaudiodeviceinfo.h>
+#include <QtMultimedia/qtaudio.h>
+#include <functional>
QT_BEGIN_NAMESPACE
-
-
-class QAbstractAudioInput;
+class QAudioDevice;
+class QPlatformAudioInput;
class Q_MULTIMEDIA_EXPORT QAudioInput : public QObject
{
Q_OBJECT
+ Q_PROPERTY(QAudioDevice device READ device WRITE setDevice NOTIFY deviceChanged)
+ Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged)
+ Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
public:
- explicit QAudioInput(const QAudioFormat &format = QAudioFormat(), QObject *parent = nullptr);
- explicit QAudioInput(const QAudioDeviceInfo &audioDeviceInfo, const QAudioFormat &format = QAudioFormat(), QObject *parent = nullptr);
+ explicit QAudioInput(QObject *parent = nullptr);
+ explicit QAudioInput(const QAudioDevice &deviceInfo, QObject *parent = nullptr);
~QAudioInput();
- QAudioFormat format() const;
-
- void start(QIODevice *device);
- QIODevice* start();
-
- void stop();
- void reset();
- void suspend();
- void resume();
+ QAudioDevice device() const;
+ float volume() const;
+ bool isMuted() const;
- void setBufferSize(int bytes);
- int bufferSize() const;
-
- int bytesReady() const;
- int periodSize() const;
-
- void setNotifyInterval(int milliSeconds);
- int notifyInterval() const;
-
- void setVolume(qreal volume);
- qreal volume() const;
-
- qint64 processedUSecs() const;
- qint64 elapsedUSecs() const;
-
- QAudio::Error error() const;
- QAudio::State state() const;
+public Q_SLOTS:
+ void setDevice(const QAudioDevice &device);
+ void setVolume(float volume);
+ void setMuted(bool muted);
Q_SIGNALS:
- void stateChanged(QAudio::State state);
- void notify();
+ void deviceChanged();
+ void volumeChanged(float volume);
+ void mutedChanged(bool muted);
private:
+ QPlatformAudioInput *handle() const { return d; }
+ void setDisconnectFunction(std::function<void()> disconnectFunction);
+ friend class QMediaCaptureSession;
Q_DISABLE_COPY(QAudioInput)
-
- QAbstractAudioInput* d;
+ QPlatformAudioInput *d = nullptr;
};
QT_END_NAMESPACE
-#endif // QAUDIOINPUT_H
+#endif // QAUDIOINPUTDEVICE_H
diff --git a/src/multimedia/audio/qaudiooutput.cpp b/src/multimedia/audio/qaudiooutput.cpp
index e37da9bdf..3bb52a4d2 100644
--- a/src/multimedia/audio/qaudiooutput.cpp
+++ b/src/multimedia/audio/qaudiooutput.cpp
@@ -1,429 +1,209 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-
-#include "qaudio.h"
-#include "qaudiodeviceinfo.h"
-#include "qaudiosystem.h"
-#include "qaudiooutput.h"
-
-#include "qaudiodevicefactory_p.h"
-
-
-QT_BEGIN_NAMESPACE
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-/*!
- \class QAudioOutput
- \brief The QAudioOutput class provides an interface for sending audio data to an audio output device.
-
- \inmodule QtMultimedia
- \ingroup multimedia
- \ingroup multimedia_audio
-
- You can construct an audio output with the system's
- \l{QAudioDeviceInfo::defaultOutputDevice()}{default audio output
- device}. It is also possible to create QAudioOutput with a
- specific QAudioDeviceInfo. When you create the audio output, you
- should also send in the QAudioFormat to be used for the playback
- (see the QAudioFormat class description for details).
-
- To play a file:
-
- Starting to play an audio stream is simply a matter of calling
- start() with a QIODevice. QAudioOutput will then fetch the data it
- needs from the io device. So playing back an audio file is as
- simple as:
-
- \snippet multimedia-snippets/audio.cpp Audio output class members
-
- \snippet multimedia-snippets/audio.cpp Audio output setup
-
- The file will start playing assuming that the audio system and
- output device support it. If you run out of luck, check what's
- up with the error() function.
-
- After the file has finished playing, we need to stop the device:
-
- \snippet multimedia-snippets/audio.cpp Audio output state changed
-
- At any given time, the QAudioOutput will be in one of four states:
- active, suspended, stopped, or idle. These states are described
- by the QAudio::State enum.
- State changes are reported through the stateChanged() signal. You
- can use this signal to, for instance, update the GUI of the
- application; the mundane example here being changing the state of
- a \c { play/pause } button. You request a state change directly
- with suspend(), stop(), reset(), resume(), and start().
-
- While the stream is playing, you can set a notify interval in
- milliseconds with setNotifyInterval(). This interval specifies the
- time between two emissions of the notify() signal. This is
- relative to the position in the stream, i.e., if the QAudioOutput
- is in the SuspendedState or the IdleState, the notify() signal is
- not emitted. A typical use-case would be to update a
- \l{QSlider}{slider} that allows seeking in the stream.
- If you want the time since playback started regardless of which
- states the audio output has been in, elapsedUSecs() is the function for you.
-
- If an error occurs, you can fetch the \l{QAudio::Error}{error
- type} with the error() function. Please see the QAudio::Error enum
- for a description of the possible errors that are reported. When
- an error is encountered, the state changes to QAudio::StoppedState.
- You can check for errors by connecting to the stateChanged()
- signal:
-
- \snippet multimedia-snippets/audio.cpp Audio output state changed
-
- \sa QAudioInput, QAudioDeviceInfo
-*/
+#include <qaudiooutput.h>
+#include <qaudiodevice.h>
+#include <qmediadevices.h>
+#include <private/qplatformaudiooutput_p.h>
+#include <private/qplatformmediaintegration_p.h>
/*!
- Construct a new audio output and attach it to \a parent.
- The default audio output device is used with the output
- \a format parameters.
-*/
-QAudioOutput::QAudioOutput(const QAudioFormat &format, QObject *parent):
- QObject(parent)
-{
- d = QAudioDeviceFactory::createDefaultOutputDevice(format);
- connect(d, SIGNAL(notify()), SIGNAL(notify()));
- connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State)));
-}
+ \qmltype AudioOutput
+ \instantiates QAudioOutput
+ \brief An audio output to be used for playback or monitoring of a capture session.
-/*!
- Construct a new audio output and attach it to \a parent.
- The device referenced by \a audioDevice is used with the output
- \a format parameters.
-*/
-QAudioOutput::QAudioOutput(const QAudioDeviceInfo &audioDevice, const QAudioFormat &format, QObject *parent):
- QObject(parent)
-{
- d = QAudioDeviceFactory::createOutputDevice(audioDevice, format);
- connect(d, SIGNAL(notify()), SIGNAL(notify()));
- connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State)));
-}
-
-/*!
- Destroys this audio output.
+ \inqmlmodule QtMultimedia
+ \ingroup multimedia_qml
+ \ingroup multimedia_audio_qml
- This will release any system resources used and free any buffers.
-*/
-QAudioOutput::~QAudioOutput()
-{
- delete d;
-}
+ \qml
+ MediaPlayer {
+ id: playMusic
+ source: "music.wav"
+ audioOutput: AudioOutput {
+ volume: slider.value
+ }
+ }
+ Slider {
+ id: slider
+ from: 0.
+ to: 1.
+ }
+ \endqml
-/*!
- Returns the QAudioFormat being used.
+ You can use AudioOutput together with a QtMultiMedia::MediaPlayer to play audio content, or you
+ can use it in conjunction with a MultiMedia::CaptureSession to monitor the audio processed by the
+ capture session.
+ \sa VideoOutput, AudioInput
*/
-QAudioFormat QAudioOutput::format() const
-{
- return d->format();
-}
/*!
- Starts transferring audio data from the \a device to the system's audio output.
- The \a device must have been opened in the \l{QIODevice::ReadOnly}{ReadOnly} or
- \l{QIODevice::ReadWrite}{ReadWrite} modes.
-
- If the QAudioOutput is able to successfully output audio data, state() returns
- QAudio::ActiveState, error() returns QAudio::NoError
- and the stateChanged() signal is emitted.
-
- If a problem occurs during this process, error() returns QAudio::OpenError,
- state() returns QAudio::StoppedState and the stateChanged() signal is emitted.
+ \class QAudioOutput
+ \brief Represents an output channel for audio.
+ \inmodule QtMultimedia
+ \ingroup multimedia
+ \ingroup multimedia_audio
+ \since 6.0
- \sa QIODevice
+ This class represents an output channel that can be used together with
+ QMediaPlayer or QMediaCaptureSession. It enables the selection of the
+ physical output device to be used, muting the channel, and changing the
+ channel's volume.
*/
-void QAudioOutput::start(QIODevice* device)
+QAudioOutput::QAudioOutput(QObject *parent)
+ : QAudioOutput(QMediaDevices::defaultAudioOutput(), parent)
{
- d->start(device);
}
-/*!
- Returns a pointer to the internal QIODevice being used to transfer data to
- the system's audio output. The device will already be open and
- \l{QIODevice::write()}{write()} can write data directly to it.
-
- \note The pointer will become invalid after the stream is stopped or
- if you start another stream.
-
- If the QAudioOutput is able to access the system's audio device, state() returns
- QAudio::IdleState, error() returns QAudio::NoError
- and the stateChanged() signal is emitted.
-
- If a problem occurs during this process, error() returns QAudio::OpenError,
- state() returns QAudio::StoppedState and the stateChanged() signal is emitted.
-
- \sa QIODevice
-*/
-QIODevice* QAudioOutput::start()
+QAudioOutput::QAudioOutput(const QAudioDevice &device, QObject *parent)
+ : QObject(parent)
{
- return d->start();
+ auto maybeAudioOutput = QPlatformMediaIntegration::instance()->createAudioOutput(this);
+ if (maybeAudioOutput) {
+ d = maybeAudioOutput.value();
+ d->device = device.mode() == QAudioDevice::Output ? device : QMediaDevices::defaultAudioOutput();
+ d->setAudioDevice(d->device);
+ } else {
+ d = new QPlatformAudioOutput(nullptr);
+ qWarning() << "Failed to initialize QAudioOutput" << maybeAudioOutput.error();
+ }
}
-/*!
- Stops the audio output, detaching from the system resource.
-
- Sets error() to QAudio::NoError, state() to QAudio::StoppedState and
- emit stateChanged() signal.
-*/
-void QAudioOutput::stop()
+QAudioOutput::~QAudioOutput()
{
- d->stop();
+ setDisconnectFunction({});
+ delete d;
}
/*!
- Drops all audio data in the buffers, resets buffers to zero.
+ \qmlproperty real QtMultimedia::AudioOutput::volume
-*/
-void QAudioOutput::reset()
-{
- d->reset();
-}
+ This property holds the volume of the audio output.
-/*!
- Stops processing audio data, preserving buffered audio data.
+ The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume).
+ Values outside this range will be clamped: a value lower than 0.0 is set to
+ 0.0, a value higher than 1.0 will set to 1.0.
- Sets error() to QAudio::NoError, state() to QAudio::SuspendedState and
- emits stateChanged() signal.
-*/
-void QAudioOutput::suspend()
-{
- d->suspend();
-}
+ The default volume is \c{1.0}.
-/*!
- Resumes processing audio data after a suspend().
+ UI \l{volume controls} should usually be scaled non-linearly. For example,
+ using a logarithmic scale will produce linear changes in perceived \l{loudness},
+ which is what a user would normally expect from a volume control.
- Sets error() to QAudio::NoError.
- Sets state() to QAudio::ActiveState if you previously called start(QIODevice*).
- Sets state() to QAudio::IdleState if you previously called start().
- emits stateChanged() signal.
+ See \l {QtAudio::convertVolume()}{QtMultimedia.convertVolume()}
+ for more details.
*/
-void QAudioOutput::resume()
-{
- d->resume();
-}
/*!
- Returns the number of free bytes available in the audio buffer.
+ \property QAudioOutput::volume
+ \brief The current volume.
- \note The returned value is only valid while in QAudio::ActiveState or QAudio::IdleState
- state, otherwise returns zero.
-*/
-int QAudioOutput::bytesFree() const
-{
- return d->bytesFree();
-}
+ The volume is scaled linearly, ranging from \c 0 (silence) to \c 1
+ (full volume).
+ \note values outside this range will be clamped.
-/*!
- Returns the period size in bytes. This is the amount of data required each period
- to prevent buffer underrun, and to ensure uninterrupted playback.
+ By default the volume is \c 1.
- \note It is recommended to provide at least enough data for a full period with each
- write operation.
-*/
-int QAudioOutput::periodSize() const
-{
- return d->periodSize();
-}
-
-/*!
- Sets the audio buffer size to \a value in bytes.
+ UI volume controls should usually be scaled non-linearly. For example,
+ using a logarithmic scale will produce linear changes in perceived loudness,
+ which is what a user would normally expect from a volume control.
- \note This function can be called anytime before start(). Calls to this
- are ignored after start(). It should not be assumed that the buffer size
- set is the actual buffer size used - call bufferSize() anytime after start()
- to return the actual buffer size being used.
+ \sa QtAudio::convertVolume()
*/
-void QAudioOutput::setBufferSize(int value)
+float QAudioOutput::volume() const
{
- d->setBufferSize(value);
+ return d->volume;
}
-/*!
- Returns the audio buffer size in bytes.
-
- If called before start(), returns platform default value.
- If called before start() but setBufferSize() was called prior, returns value set by setBufferSize().
- If called after start(), returns the actual buffer size being used. This may not be what was set previously
- by setBufferSize().
-
-*/
-int QAudioOutput::bufferSize() const
+void QAudioOutput::setVolume(float volume)
{
- return d->bufferSize();
+ volume = qBound(0., volume, 1.);
+ if (d->volume == volume)
+ return;
+ d->volume = volume;
+ d->setVolume(volume);
+ emit volumeChanged(volume);
}
/*!
- Sets the interval for notify() signal to be emitted.
- This is based on the \a ms of audio data processed,
- not on wall clock time.
- The minimum resolution of the timer is platform specific and values
- should be checked with notifyInterval() to confirm the actual value
- being used.
-*/
-void QAudioOutput::setNotifyInterval(int ms)
-{
- d->setNotifyInterval(ms);
-}
+ \qmlproperty bool QtMultimedia::AudioOutput::muted
-/*!
- Returns the notify interval in milliseconds.
-*/
-int QAudioOutput::notifyInterval() const
-{
- return d->notifyInterval();
-}
+ This property holds whether the audio output is muted.
-/*!
- Returns the amount of audio data processed since start()
- was called (in microseconds).
+ Defaults to \c{false}.
*/
-qint64 QAudioOutput::processedUSecs() const
-{
- return d->processedUSecs();
-}
/*!
- Returns the microseconds since start() was called, including time in Idle and
- Suspend states.
-*/
-qint64 QAudioOutput::elapsedUSecs() const
-{
- return d->elapsedUSecs();
-}
+ \property QAudioOutput::muted
+ \brief The muted state of the current media.
-/*!
- Returns the error state.
+ The value will be \c true if the output is muted; otherwise \c{false}.
*/
-QAudio::Error QAudioOutput::error() const
+bool QAudioOutput::isMuted() const
{
- return d->error();
+ return d->muted;
}
-/*!
- Returns the state of audio processing.
-*/
-QAudio::State QAudioOutput::state() const
+void QAudioOutput::setMuted(bool muted)
{
- return d->state();
+ if (d->muted == muted)
+ return;
+ d->muted = muted;
+ d->setMuted(muted);
+ emit mutedChanged(muted);
}
/*!
- Sets the output volume to \a volume.
-
- The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume). Values outside this
- range will be clamped.
+ \qmlproperty AudioDevice QtMultimedia::AudioOutput::device
- The default volume is \c 1.0.
+ This property describes the audio device connected to this output.
- Note: Adjustments to the volume will change the volume of this audio stream, not the global volume.
-
- UI volume controls should usually be scaled nonlinearly. For example, using a logarithmic scale
- will produce linear changes in perceived loudness, which is what a user would normally expect
- from a volume control. See QAudio::convertVolume() for more details.
+ The device property represents the audio device this output is connected to.
+ This property can be used to select an output device from the
+ QtMultimedia::MediaDevices::audioOutputs() list.
*/
-void QAudioOutput::setVolume(qreal volume)
-{
- qreal v = qBound(qreal(0.0), volume, qreal(1.0));
- d->setVolume(v);
-}
/*!
- Returns the volume between 0.0 and 1.0 inclusive.
+ \property QAudioOutput::device
+ \brief The audio device connected to this output.
+
+ The device property represents the audio device this output is connected to.
+ This property can be used to select an output device from the
+ QMediaDevices::audioOutputs() list.
+ You can select the system default audio output by setting this property to
+ a default constructed QAudioDevice object.
*/
-qreal QAudioOutput::volume() const
+QAudioDevice QAudioOutput::device() const
{
- return d->volume();
+ return d->device;
}
-/*!
- Returns the audio category of this audio stream.
-
- Some platforms can group audio streams into categories
- and manage their volumes independently, or display them
- in a system mixer control. You can set this property to
- allow the platform to distinguish the purpose of your streams.
-
- \sa setCategory()
-*/
-QString QAudioOutput::category() const
+void QAudioOutput::setDevice(const QAudioDevice &device)
{
- return d->category();
+ auto dev = device;
+ if (dev.isNull())
+ dev = QMediaDevices::defaultAudioOutput();
+ if (dev.mode() != QAudioDevice::Output)
+ return;
+ if (d->device == dev)
+ return;
+ d->device = dev;
+ d->setAudioDevice(dev);
+ emit deviceChanged();
}
/*!
- Sets the audio category of this audio stream to \a category.
-
- Some platforms can group audio streams into categories
- and manage their volumes independently, or display them
- in a system mixer control. You can set this property to
- allow the platform to distinguish the purpose of your streams.
-
- Not all platforms support audio stream categorization. In this
- case, the function call will be ignored.
-
- Changing an audio output stream's category while it is opened
- will not take effect until it is reopened.
- \sa category()
+ \internal
*/
-void QAudioOutput::setCategory(const QString &category)
+void QAudioOutput::setDisconnectFunction(std::function<void()> disconnectFunction)
{
- d->setCategory(category);
+ if (d->disconnectFunction) {
+ auto df = d->disconnectFunction;
+ d->disconnectFunction = {};
+ df();
+ }
+ d->disconnectFunction = std::move(disconnectFunction);
}
-/*!
- \fn QAudioOutput::stateChanged(QAudio::State state)
- This signal is emitted when the device \a state has changed.
- This is the current state of the audio output.
-*/
-
-/*!
- \fn QAudioOutput::notify()
- This signal is emitted when a certain interval of milliseconds
- of audio data has been processed. The interval is set by
- setNotifyInterval().
-*/
-
-QT_END_NAMESPACE
-
#include "moc_qaudiooutput.cpp"
diff --git a/src/multimedia/audio/qaudiooutput.h b/src/multimedia/audio/qaudiooutput.h
index 5f466d8e1..643d19f94 100644
--- a/src/multimedia/audio/qaudiooutput.h
+++ b/src/multimedia/audio/qaudiooutput.h
@@ -1,112 +1,55 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QAUDIOOUTPUTDEVICE_H
+#define QAUDIOOUTPUTDEVICE_H
-#ifndef QAUDIOOUTPUT_H
-#define QAUDIOOUTPUT_H
-
-#include <QtCore/qiodevice.h>
-
+#include <QtCore/qobject.h>
#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qmultimedia.h>
-
-#include <QtMultimedia/qaudio.h>
-#include <QtMultimedia/qaudioformat.h>
-#include <QtMultimedia/qaudiodeviceinfo.h>
+#include <QtMultimedia/qtaudio.h>
+#include <functional>
QT_BEGIN_NAMESPACE
-
-
-class QAbstractAudioOutput;
+class QAudioDevice;
+class QPlatformAudioOutput;
class Q_MULTIMEDIA_EXPORT QAudioOutput : public QObject
{
Q_OBJECT
+ Q_PROPERTY(QAudioDevice device READ device WRITE setDevice NOTIFY deviceChanged)
+ Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged)
+ Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
public:
- explicit QAudioOutput(const QAudioFormat &format = QAudioFormat(), QObject *parent = nullptr);
- explicit QAudioOutput(const QAudioDeviceInfo &audioDeviceInfo, const QAudioFormat &format = QAudioFormat(), QObject *parent = nullptr);
+ explicit QAudioOutput(QObject *parent = nullptr);
+ explicit QAudioOutput(const QAudioDevice &device, QObject *parent = nullptr);
~QAudioOutput();
- QAudioFormat format() const;
-
- void start(QIODevice *device);
- QIODevice* start();
-
- void stop();
- void reset();
- void suspend();
- void resume();
+ QAudioDevice device() const;
+ float volume() const;
+ bool isMuted() const;
- void setBufferSize(int bytes);
- int bufferSize() const;
-
- int bytesFree() const;
- int periodSize() const;
-
- void setNotifyInterval(int milliSeconds);
- int notifyInterval() const;
-
- qint64 processedUSecs() const;
- qint64 elapsedUSecs() const;
-
- QAudio::Error error() const;
- QAudio::State state() const;
-
- void setVolume(qreal);
- qreal volume() const;
-
- QString category() const;
- void setCategory(const QString &category);
+public Q_SLOTS:
+ void setDevice(const QAudioDevice &device);
+ void setVolume(float volume);
+ void setMuted(bool muted);
Q_SIGNALS:
- void stateChanged(QAudio::State state);
- void notify();
+ void deviceChanged();
+ void volumeChanged(float volume);
+ void mutedChanged(bool muted);
private:
+ QPlatformAudioOutput *handle() const { return d; }
+ void setDisconnectFunction(std::function<void()> disconnectFunction);
+ friend class QMediaCaptureSession;
+ friend class QMediaPlayer;
Q_DISABLE_COPY(QAudioOutput)
-
- QAbstractAudioOutput* d;
+ QPlatformAudioOutput *d = nullptr;
};
QT_END_NAMESPACE
-#endif // QAUDIOOUTPUT_H
+#endif // QAUDIOOUTPUTDEVICE_H
diff --git a/src/multimedia/audio/qaudioprobe.cpp b/src/multimedia/audio/qaudioprobe.cpp
deleted file mode 100644
index 7fb57242f..000000000
--- a/src/multimedia/audio/qaudioprobe.cpp
+++ /dev/null
@@ -1,205 +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$
-**
-****************************************************************************/
-
-/*!
- \class QAudioProbe
- \inmodule QtMultimedia
-
- \ingroup multimedia
- \ingroup multimedia_audio
-
- \brief The QAudioProbe class allows you to monitor audio being played or recorded.
-
- \snippet multimedia-snippets/qaudioprobe.cpp desc
-
- \sa QVideoProbe, QMediaPlayer, QCamera
-*/
-
-#include "qaudioprobe.h"
-#include "qmediaaudioprobecontrol.h"
-#include "qmediaservice.h"
-#include "qmediarecorder.h"
-#include "qsharedpointer.h"
-#include "qpointer.h"
-
-QT_BEGIN_NAMESPACE
-
-class QAudioProbePrivate {
-public:
- QPointer<QMediaObject> source;
- QPointer<QMediaAudioProbeControl> probee;
-};
-
-/*!
- Creates a new QAudioProbe class with a \a parent. After setting the
- source to monitor with \l setSource(), the \l audioBufferProbed()
- signal will be emitted when audio buffers are flowing in the
- source media object.
- */
-QAudioProbe::QAudioProbe(QObject *parent)
- : QObject(parent)
- , d(new QAudioProbePrivate)
-{
-}
-
-/*!
- Destroys this probe and disconnects from any
- media object.
- */
-QAudioProbe::~QAudioProbe()
-{
- if (d->source) {
- // Disconnect
- if (d->probee) {
- disconnect(d->probee.data(), SIGNAL(audioBufferProbed(QAudioBuffer)), this, SIGNAL(audioBufferProbed(QAudioBuffer)));
- disconnect(d->probee.data(), SIGNAL(flush()), this, SIGNAL(flush()));
- }
- d->source.data()->service()->releaseControl(d->probee.data());
- }
-}
-
-/*!
- Sets the media object to monitor to \a source.
-
- If \a source is zero, this probe will be deactivated
- and this function wil return true.
-
- If the media object does not support monitoring
- audio, this function will return false.
-
- The previous object will no longer be monitored.
- Passing in the same object will be ignored, but
- monitoring will continue.
- */
-bool QAudioProbe::setSource(QMediaObject *source)
-{
- // Need to:
- // 1) disconnect from current source if necessary
- // 2) see if new one has the probe control
- // 3) connect if so
-
- // in case source was destroyed but probe control is still valid
- if (!d->source && d->probee) {
- disconnect(d->probee.data(), SIGNAL(audioBufferProbed(QAudioBuffer)), this, SIGNAL(audioBufferProbed(QAudioBuffer)));
- disconnect(d->probee.data(), SIGNAL(flush()), this, SIGNAL(flush()));
- d->probee.clear();
- }
-
- if (source != d->source.data()) {
- if (d->source) {
- Q_ASSERT(d->probee);
- disconnect(d->probee.data(), SIGNAL(audioBufferProbed(QAudioBuffer)), this, SIGNAL(audioBufferProbed(QAudioBuffer)));
- disconnect(d->probee.data(), SIGNAL(flush()), this, SIGNAL(flush()));
- d->source.data()->service()->releaseControl(d->probee.data());
- d->source.clear();
- d->probee.clear();
- }
-
- if (source) {
- QMediaService *service = source->service();
- if (service) {
- d->probee = service->requestControl<QMediaAudioProbeControl*>();
- }
-
- if (d->probee) {
- connect(d->probee.data(), SIGNAL(audioBufferProbed(QAudioBuffer)), this, SIGNAL(audioBufferProbed(QAudioBuffer)));
- connect(d->probee.data(), SIGNAL(flush()), this, SIGNAL(flush()));
- d->source = source;
- }
- }
- }
-
- return (!source || d->probee != nullptr);
-}
-
-/*!
- Starts monitoring the given \a mediaRecorder.
-
- Returns true on success.
-
- If there is no mediaObject associated with \a mediaRecorder, or if it is
- zero, this probe will be deactivated and this function wil return true.
-
- If the media recorder instance does not support monitoring
- audio, this function will return false.
-
- Any previously monitored objects will no longer be monitored.
- Passing in the same (valid) object will be ignored, but
- monitoring will continue.
- */
-bool QAudioProbe::setSource(QMediaRecorder *mediaRecorder)
-{
- QMediaObject *source = mediaRecorder ? mediaRecorder->mediaObject() : nullptr;
- bool result = setSource(source);
-
- if (!mediaRecorder)
- return true;
-
- if (mediaRecorder && !source)
- return false;
-
- return result;
-}
-
-/*!
- Returns true if this probe is monitoring something, or false otherwise.
-
- The source being monitored does not need to be active.
- */
-bool QAudioProbe::isActive() const
-{
- return d->probee != nullptr;
-}
-
-/*!
- \fn QAudioProbe::audioBufferProbed(const QAudioBuffer &buffer)
-
- This signal should be emitted when an audio \a buffer is processed in the
- media service.
-*/
-
-
-/*!
- \fn QAudioProbe::flush()
-
- This signal should be emitted when it is required to release all buffers.
- Application must release all outstanding references to audio buffers.
-*/
-
-QT_END_NAMESPACE
diff --git a/src/multimedia/audio/qaudioprobe.h b/src/multimedia/audio/qaudioprobe.h
deleted file mode 100644
index e0d5619d2..000000000
--- a/src/multimedia/audio/qaudioprobe.h
+++ /dev/null
@@ -1,74 +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 QAUDIOPROBE_H
-#define QAUDIOPROBE_H
-
-#include <QtCore/qobject.h>
-#include <QtMultimedia/qaudiobuffer.h>
-
-QT_BEGIN_NAMESPACE
-
-class QMediaObject;
-class QMediaRecorder;
-
-class QAudioProbePrivate;
-class Q_MULTIMEDIA_EXPORT QAudioProbe : public QObject
-{
- Q_OBJECT
-public:
- explicit QAudioProbe(QObject *parent = nullptr);
- ~QAudioProbe();
-
- bool setSource(QMediaObject *source);
- bool setSource(QMediaRecorder *source);
-
- bool isActive() const;
-
-Q_SIGNALS:
- void audioBufferProbed(const QAudioBuffer &buffer);
- void flush();
-
-private:
- QAudioProbePrivate *d;
-};
-
-QT_END_NAMESPACE
-
-#endif // QAUDIOPROBE_H
diff --git a/src/multimedia/audio/qaudiosink.cpp b/src/multimedia/audio/qaudiosink.cpp
new file mode 100644
index 000000000..12263d32a
--- /dev/null
+++ b/src/multimedia/audio/qaudiosink.cpp
@@ -0,0 +1,339 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+#include "qaudio.h"
+#include "qaudiodevice.h"
+#include "qaudiosystem_p.h"
+#include "qaudiosink.h"
+
+#include <private/qplatformmediadevices_p.h>
+#include <private/qplatformmediaintegration_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAudioSink
+ \brief The QAudioSink class provides an interface for sending audio data to
+ an audio output device.
+
+ \inmodule QtMultimedia
+ \ingroup multimedia
+ \ingroup multimedia_audio
+
+ You can construct an audio output with the system's
+ default audio output device. It is also possible to
+ create QAudioSink with a specific QAudioDevice. When
+ you create the audio output, you should also send in
+ the QAudioFormat to be used for the playback (see
+ the QAudioFormat class description for details).
+
+ To play a file:
+
+ Starting to play an audio stream is simply a matter of calling
+ start() with a QIODevice. QAudioSink will then fetch the data it
+ needs from the io device. So playing back an audio file is as
+ simple as:
+
+ \snippet multimedia-snippets/audio.cpp Audio output class members
+
+ \snippet multimedia-snippets/audio.cpp Audio output setup
+
+ The file will start playing assuming that the audio system and
+ output device support it. If you run out of luck, check what's
+ up with the error() function.
+
+ After the file has finished playing, we need to stop the device:
+
+ \snippet multimedia-snippets/audio.cpp Audio output stop
+
+ At any given time, the QAudioSink will be in one of four states:
+ active, suspended, stopped, or idle. These states are described
+ by the QtAudio::State enum.
+ State changes are reported through the stateChanged() signal. You
+ can use this signal to, for instance, update the GUI of the
+ application; the mundane example here being changing the state of
+ a \c { play/pause } button. You request a state change directly
+ with suspend(), stop(), reset(), resume(), and start().
+
+ If an error occurs, you can fetch the \l{QtAudio::Error}{error
+ type} with the error() function. Please see the QtAudio::Error enum
+ for a description of the possible errors that are reported. When
+ QtAudio::UnderrunError is encountered, the state changes to QtAudio::IdleState,
+ when another error is encountered, the state changes to QtAudio::StoppedState.
+ You can check for errors by connecting to the stateChanged()
+ signal:
+
+ \snippet multimedia-snippets/audio.cpp Audio output state changed
+
+ \sa QAudioSource, QAudioDevice
+*/
+
+/*!
+ Construct a new audio output and attach it to \a parent.
+ The default audio output device is used with the output
+ \a format parameters.
+*/
+QAudioSink::QAudioSink(const QAudioFormat &format, QObject *parent)
+ : QAudioSink({}, format, parent)
+{
+}
+
+/*!
+ Construct a new audio output and attach it to \a parent.
+ The device referenced by \a audioDevice is used with the output
+ \a format parameters.
+*/
+QAudioSink::QAudioSink(const QAudioDevice &audioDevice, const QAudioFormat &format, QObject *parent):
+ QObject(parent)
+{
+ d = QPlatformMediaIntegration::instance()->mediaDevices()->audioOutputDevice(format, audioDevice, parent);
+ if (d)
+ connect(d, &QPlatformAudioSink::stateChanged, this, [this](QAudio::State state) {
+ // if the signal has been emitted from another thread,
+ // the state may be already changed by main one
+ if (state == d->state())
+ emit stateChanged(state);
+ });
+ else
+ qWarning() << ("No audio device detected");
+}
+
+/*!
+ \fn bool QAudioSink::isNull() const
+
+ Returns \c true is the QAudioSink instance is \c null, otherwise returns
+ \c false.
+*/
+
+/*!
+ Destroys this audio output.
+
+ This will release any system resources used and free any buffers.
+*/
+QAudioSink::~QAudioSink()
+{
+ delete d;
+}
+
+/*!
+ Returns the QAudioFormat being used.
+
+*/
+QAudioFormat QAudioSink::format() const
+{
+ return d ? d->format() : QAudioFormat();
+}
+
+/*!
+ Starts transferring audio data from the \a device to the system's audio output.
+ The \a device must have been opened in the \l{QIODevice::ReadOnly}{ReadOnly} or
+ \l{QIODevice::ReadWrite}{ReadWrite} modes.
+
+ If the QAudioSink is able to successfully output audio data, state() returns
+ QtAudio::ActiveState, error() returns QtAudio::NoError
+ and the stateChanged() signal is emitted.
+
+ If a problem occurs during this process, error() returns QtAudio::OpenError,
+ state() returns QtAudio::StoppedState and the stateChanged() signal is emitted.
+
+ \sa QIODevice
+*/
+void QAudioSink::start(QIODevice* device)
+{
+ if (!d)
+ return;
+ d->elapsedTime.restart();
+ d->start(device);
+}
+
+/*!
+ Returns a pointer to the internal QIODevice being used to transfer data to
+ the system's audio output. The device will already be open and
+ \l{QIODevice::write()}{write()} can write data directly to it.
+
+ \note The pointer will become invalid after the stream is stopped or
+ if you start another stream.
+
+ If the QAudioSink is able to access the system's audio device, state() returns
+ QtAudio::IdleState, error() returns QtAudio::NoError
+ and the stateChanged() signal is emitted.
+
+ If a problem occurs during this process, error() returns QtAudio::OpenError,
+ state() returns QtAudio::StoppedState and the stateChanged() signal is emitted.
+
+ \sa QIODevice
+*/
+QIODevice* QAudioSink::start()
+{
+ if (!d)
+ return nullptr;
+ d->elapsedTime.restart();
+ return d->start();
+}
+
+/*!
+ Stops the audio output, detaching from the system resource.
+
+ Sets error() to QtAudio::NoError, state() to QtAudio::StoppedState and
+ emit stateChanged() signal.
+*/
+void QAudioSink::stop()
+{
+ if (d)
+ d->stop();
+}
+
+/*!
+ Drops all audio data in the buffers, resets buffers to zero.
+
+*/
+void QAudioSink::reset()
+{
+ if (d)
+ d->reset();
+}
+
+/*!
+ Stops processing audio data, preserving buffered audio data.
+
+ Sets error() to QtAudio::NoError, state() to QtAudio::SuspendedState and
+ emits stateChanged() signal.
+*/
+void QAudioSink::suspend()
+{
+ if (d)
+ d->suspend();
+}
+
+/*!
+ Resumes processing audio data after a suspend().
+
+ Sets state() to the state the sink had when suspend() was called, and sets
+ error() to QAudioError::NoError. This function does nothing if the audio sink's
+ state is not QtAudio::SuspendedState.
+*/
+void QAudioSink::resume()
+{
+ if (d)
+ d->resume();
+}
+
+/*!
+ Returns the number of free bytes available in the audio buffer.
+
+ \note The returned value is only valid while in QtAudio::ActiveState or QtAudio::IdleState
+ state, otherwise returns zero.
+*/
+qsizetype QAudioSink::bytesFree() const
+{
+ return d ? d->bytesFree() : 0;
+}
+
+/*!
+ Sets the audio buffer size to \a value in bytes.
+
+ \note This function can be called anytime before start(). Calls to this
+ are ignored after start(). It should not be assumed that the buffer size
+ set is the actual buffer size used - call bufferSize() anytime after start()
+ to return the actual buffer size being used.
+*/
+void QAudioSink::setBufferSize(qsizetype value)
+{
+ if (d)
+ d->setBufferSize(value);
+}
+
+/*!
+ Returns the audio buffer size in bytes.
+
+ If called before start(), returns platform default value.
+ If called before start() but setBufferSize() was called prior, returns value set by setBufferSize().
+ If called after start(), returns the actual buffer size being used. This may not be what was set previously
+ by setBufferSize().
+
+*/
+qsizetype QAudioSink::bufferSize() const
+{
+ return d ? d->bufferSize() : 0;
+}
+
+/*!
+ Returns the amount of audio data processed since start()
+ was called (in microseconds).
+*/
+qint64 QAudioSink::processedUSecs() const
+{
+ return d ? d->processedUSecs() : 0;
+}
+
+/*!
+ Returns the microseconds since start() was called, including time in Idle and
+ Suspend states.
+*/
+qint64 QAudioSink::elapsedUSecs() const
+{
+ return state() == QAudio::StoppedState ? 0 : d->elapsedTime.nsecsElapsed()/1000;
+}
+
+/*!
+ Returns the error state.
+*/
+QtAudio::Error QAudioSink::error() const
+{
+ return d ? d->error() : QAudio::OpenError;
+}
+
+/*!
+ Returns the state of audio processing.
+*/
+QtAudio::State QAudioSink::state() const
+{
+ return d ? d->state() : QAudio::StoppedState;
+}
+
+/*!
+ Sets the output volume to \a volume.
+
+ The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume).
+ Values outside this range will be clamped.
+
+ The default volume is \c 1.0.
+
+ \note Adjustments to the volume will change the volume of this audio stream,
+ not the global volume.
+
+ UI volume controls should usually be scaled non-linearly. For example, using
+ a logarithmic scale will produce linear changes in perceived loudness, which
+ is what a user would normally expect from a volume control. See
+ QtAudio::convertVolume() for more details.
+*/
+void QAudioSink::setVolume(qreal volume)
+{
+ if (!d)
+ return;
+ qreal v = qBound(qreal(0.0), volume, qreal(1.0));
+ d->setVolume(v);
+}
+
+/*!
+ Returns the volume between 0.0 and 1.0 inclusive.
+*/
+qreal QAudioSink::volume() const
+{
+ return d ? d->volume() : 1.0;
+}
+
+/*!
+ \fn QAudioSink::stateChanged(QtAudio::State state)
+ This signal is emitted when the device \a state has changed.
+ This is the current state of the audio output.
+
+ \note The QtAudio namespace was named QAudio up to and including Qt 6.6.
+ String-based connections to this signal have to use \c{QAudio::State} as
+ the parameter type: \c{connect(source, SIGNAL(stateChanged(QAudio::State)), ...);}
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qaudiosink.cpp"
diff --git a/src/multimedia/audio/qaudiosink.h b/src/multimedia/audio/qaudiosink.h
new file mode 100644
index 000000000..e071e6fbc
--- /dev/null
+++ b/src/multimedia/audio/qaudiosink.h
@@ -0,0 +1,74 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+#ifndef QAUDIOOUTPUT_H
+#define QAUDIOOUTPUT_H
+
+#include <QtCore/qiodevice.h>
+
+#include <QtMultimedia/qtmultimediaglobal.h>
+
+#include <QtMultimedia/qtaudio.h>
+#include <QtMultimedia/qaudioformat.h>
+#include <QtMultimedia/qaudiodevice.h>
+
+
+QT_BEGIN_NAMESPACE
+
+
+
+class QPlatformAudioSink;
+
+class Q_MULTIMEDIA_EXPORT QAudioSink : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit QAudioSink(const QAudioFormat &format = QAudioFormat(), QObject *parent = nullptr);
+ explicit QAudioSink(const QAudioDevice &audioDeviceInfo, const QAudioFormat &format = QAudioFormat(), QObject *parent = nullptr);
+ ~QAudioSink();
+
+ bool isNull() const { return !d; }
+
+ QAudioFormat format() const;
+
+ void start(QIODevice *device);
+ QIODevice* start();
+
+ void stop();
+ void reset();
+ void suspend();
+ void resume();
+
+ void setBufferSize(qsizetype bytes);
+ qsizetype bufferSize() const;
+
+ qsizetype bytesFree() const;
+
+ qint64 processedUSecs() const;
+ qint64 elapsedUSecs() const;
+
+ QtAudio::Error error() const;
+ QtAudio::State state() const;
+
+ void setVolume(qreal);
+ qreal volume() const;
+
+Q_SIGNALS:
+#if defined(Q_QDOC)
+ void stateChanged(QtAudio::State state);
+#else
+ // use QAudio here to keep string-based connections working
+ void stateChanged(QAudio::State state);
+#endif
+
+private:
+ Q_DISABLE_COPY(QAudioSink)
+
+ QPlatformAudioSink* d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QAUDIOOUTPUT_H
diff --git a/src/multimedia/audio/qaudiosource.cpp b/src/multimedia/audio/qaudiosource.cpp
new file mode 100644
index 000000000..1ed5e82bc
--- /dev/null
+++ b/src/multimedia/audio/qaudiosource.cpp
@@ -0,0 +1,369 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+#include "qaudio.h"
+#include "qaudiodevice.h"
+#include "qaudiosystem_p.h"
+#include "qaudiosource.h"
+
+#include <private/qplatformmediadevices_p.h>
+#include <private/qplatformmediaintegration_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAudioSource
+ \brief The QAudioSource class provides an interface for receiving audio data from an audio input device.
+
+ \inmodule QtMultimedia
+ \ingroup multimedia
+ \ingroup multimedia_audio
+
+ You can construct an audio input with the system's
+ default audio input device. It is also possible to
+ create QAudioSource with a specific QAudioDevice. When
+ you create the audio input, you should also send in the
+ QAudioFormat to be used for the recording (see the QAudioFormat
+ class description for details).
+
+ To record to a file:
+
+ QAudioSource lets you record audio with an audio input device. The
+ default constructor of this class will use the systems default
+ audio device, but you can also specify a QAudioDevice for a
+ specific device. You also need to pass in the QAudioFormat in
+ which you wish to record.
+
+ Starting up the QAudioSource is simply a matter of calling start()
+ with a QIODevice opened for writing. For instance, to record to a
+ file, you can:
+
+ \snippet multimedia-snippets/audio.cpp Audio input class members
+
+ \snippet multimedia-snippets/audio.cpp Audio input setup
+
+ This will start recording if the format specified is supported by
+ the input device (you can check this with
+ QAudioDevice::isFormatSupported(). In case there are any
+ snags, use the error() function to check what went wrong. We stop
+ recording in the \c stopRecording() slot.
+
+ \snippet multimedia-snippets/audio.cpp Audio input stop recording
+
+ At any point in time, QAudioSource will be in one of four states:
+ active, suspended, stopped, or idle. These states are specified by
+ the QtAudio::State enum. You can request a state change directly through
+ suspend(), resume(), stop(), reset(), and start(). The current
+ state is reported by state(). QAudioSink will also signal you
+ when the state changes (stateChanged()).
+
+ QAudioSource provides several ways of measuring the time that has
+ passed since the start() of the recording. The \c processedUSecs()
+ function returns the length of the stream in microseconds written,
+ i.e., it leaves out the times the audio input was suspended or idle.
+ The elapsedUSecs() function returns the time elapsed since start() was called regardless of
+ which states the QAudioSource has been in.
+
+ If an error should occur, you can fetch its reason with error().
+ The possible error reasons are described by the QtAudio::Error
+ enum. The QAudioSource will enter the \l{QtAudio::}{StoppedState} when
+ an error is encountered. Connect to the stateChanged() signal to
+ handle the error:
+
+ \snippet multimedia-snippets/audio.cpp Audio input state changed
+
+ \sa QAudioSink, QAudioDevice
+*/
+
+/*!
+ Construct a new audio input and attach it to \a parent.
+ The default audio input device is used with the output
+ \a format parameters.
+*/
+
+QAudioSource::QAudioSource(const QAudioFormat &format, QObject *parent)
+ : QAudioSource({}, format, parent)
+{
+}
+
+/*!
+ Construct a new audio input and attach it to \a parent.
+ The device referenced by \a audioDevice is used with the input
+ \a format parameters.
+*/
+
+QAudioSource::QAudioSource(const QAudioDevice &audioDevice, const QAudioFormat &format, QObject *parent):
+ QObject(parent)
+{
+ d = QPlatformMediaIntegration::instance()->mediaDevices()->audioInputDevice(format, audioDevice, parent);
+ if (d) {
+ connect(d, &QPlatformAudioSource::stateChanged, this, [this](QAudio::State state) {
+ // if the signal has been emitted from another thread,
+ // the state may be already changed by main one
+ if (state == d->state())
+ emit stateChanged(state);
+ });
+ }
+ else
+ qWarning() << ("No audio device detected");
+
+}
+
+/*!
+ \fn bool QAudioSource::isNull() const
+
+ Returns \c true if the audio source is \c null, otherwise returns \c false.
+*/
+
+/*!
+ Destroy this audio input.
+*/
+
+QAudioSource::~QAudioSource()
+{
+ delete d;
+}
+
+/*!
+ Starts transferring audio data from the system's audio input to the \a device.
+ The \a device must have been opened in the \l{QIODevice::WriteOnly}{WriteOnly},
+ \l{QIODevice::Append}{Append} or \l{QIODevice::ReadWrite}{ReadWrite} modes.
+
+ If the QAudioSource is able to successfully get audio data, state() returns
+ either QtAudio::ActiveState or QtAudio::IdleState, error() returns QtAudio::NoError
+ and the stateChanged() signal is emitted.
+
+ If a problem occurs during this process, error() returns QtAudio::OpenError,
+ state() returns QtAudio::StoppedState and the stateChanged() signal is emitted.
+
+ \sa QIODevice
+*/
+
+void QAudioSource::start(QIODevice* device)
+{
+ if (!d)
+ return;
+ d->elapsedTime.start();
+ d->start(device);
+}
+
+/*!
+ Returns a pointer to the internal QIODevice being used to transfer data from
+ the system's audio input. The device will already be open and
+ \l{QIODevice::read()}{read()} can read data directly from it.
+
+ \note The pointer will become invalid after the stream is stopped or
+ if you start another stream.
+
+ If the QAudioSource is able to access the system's audio device, state() returns
+ QtAudio::IdleState, error() returns QtAudio::NoError
+ and the stateChanged() signal is emitted.
+
+ If a problem occurs during this process, error() returns QtAudio::OpenError,
+ state() returns QtAudio::StoppedState and the stateChanged() signal is emitted.
+
+ \sa QIODevice
+*/
+
+QIODevice* QAudioSource::start()
+{
+ if (!d)
+ return nullptr;
+ d->elapsedTime.start();
+ return d->start();
+}
+
+/*!
+ Returns the QAudioFormat being used.
+*/
+
+QAudioFormat QAudioSource::format() const
+{
+ return d ? d->format() : QAudioFormat();
+}
+
+/*!
+ Stops the audio input, detaching from the system resource.
+
+ Sets error() to QtAudio::NoError, state() to QtAudio::StoppedState and
+ emit stateChanged() signal.
+*/
+
+void QAudioSource::stop()
+{
+ if (d)
+ d->stop();
+}
+
+/*!
+ Drops all audio data in the buffers, resets buffers to zero.
+*/
+
+void QAudioSource::reset()
+{
+ if (d)
+ d->reset();
+}
+
+/*!
+ Stops processing audio data, preserving buffered audio data.
+
+ Sets error() to QtAudio::NoError, state() to QtAudio::SuspendedState and
+ emit stateChanged() signal.
+*/
+
+void QAudioSource::suspend()
+{
+ if (d)
+ d->suspend();
+}
+
+/*!
+ Resumes processing audio data after a suspend().
+
+ Sets error() to QtAudio::NoError.
+ Sets state() to QtAudio::ActiveState if you previously called start(QIODevice*).
+ Sets state() to QtAudio::IdleState if you previously called start().
+ emits stateChanged() signal.
+*/
+
+void QAudioSource::resume()
+{
+ if (d)
+ d->resume();
+}
+
+/*!
+ Sets the audio buffer size to \a value bytes.
+
+ Note: This function can be called anytime before start(), calls to this
+ are ignored after start(). It should not be assumed that the buffer size
+ set is the actual buffer size used, calling bufferSize() anytime after start()
+ will return the actual buffer size being used.
+
+*/
+
+void QAudioSource::setBufferSize(qsizetype value)
+{
+ if (d)
+ d->setBufferSize(value);
+}
+
+/*!
+ Returns the audio buffer size in bytes.
+
+ If called before start(), returns platform default value.
+ If called before start() but setBufferSize() was called prior, returns value set by setBufferSize().
+ If called after start(), returns the actual buffer size being used. This may not be what was set previously
+ by setBufferSize().
+
+*/
+
+qsizetype QAudioSource::bufferSize() const
+{
+ return d ? d->bufferSize() : 0;
+}
+
+/*!
+ Returns the amount of audio data available to read in bytes.
+
+ Note: returned value is only valid while in QtAudio::ActiveState or QtAudio::IdleState
+ state, otherwise returns zero.
+*/
+
+qsizetype QAudioSource::bytesAvailable() const
+{
+ /*
+ -If not ActiveState|IdleState, return 0
+ -return amount of audio data available to read
+ */
+ return d ? d->bytesReady() : 0;
+}
+
+/*!
+ Sets the input volume to \a volume.
+
+ The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume). Values outside this
+ range will be clamped.
+
+ If the device does not support adjusting the input
+ volume then \a volume will be ignored and the input
+ volume will remain at 1.0.
+
+ The default volume is \c 1.0.
+
+ Note: Adjustments to the volume will change the volume of this audio stream, not the global volume.
+*/
+void QAudioSource::setVolume(qreal volume)
+{
+ if (!d)
+ return;
+ qreal v = qBound(qreal(0.0), volume, qreal(1.0));
+ d->setVolume(v);
+}
+
+/*!
+ Returns the input volume.
+
+ If the device does not support adjusting the input volume
+ the returned value will be 1.0.
+*/
+qreal QAudioSource::volume() const
+{
+ return d ? d->volume() : 1.0;
+}
+
+/*!
+ Returns the amount of audio data processed since start()
+ was called in microseconds.
+*/
+
+qint64 QAudioSource::processedUSecs() const
+{
+ return d ? d->processedUSecs() : 0;
+}
+
+/*!
+ Returns the microseconds since start() was called, including time in Idle and
+ Suspend states.
+*/
+
+#include <qdebug.h>
+
+qint64 QAudioSource::elapsedUSecs() const
+{
+ return state() == QAudio::StoppedState ? 0 : d->elapsedTime.nsecsElapsed()/1000;
+}
+
+/*!
+ Returns the error state.
+*/
+
+QtAudio::Error QAudioSource::error() const
+{
+ return d ? d->error() : QAudio::OpenError;
+}
+
+/*!
+ Returns the state of audio processing.
+*/
+
+QtAudio::State QAudioSource::state() const
+{
+ return d ? d->state() : QAudio::StoppedState;
+}
+
+/*!
+ \fn QAudioSource::stateChanged(QtAudio::State state)
+ This signal is emitted when the device \a state has changed.
+
+ \note The QtAudio namespace was named QAudio up to and including Qt 6.6.
+ String-based connections to this signal have to use \c{QAudio::State} as
+ the parameter type: \c{connect(source, SIGNAL(stateChanged(QAudio::State)), ...);}
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qaudiosource.cpp"
+
diff --git a/src/multimedia/audio/qaudiosource.h b/src/multimedia/audio/qaudiosource.h
new file mode 100644
index 000000000..f0ad0ceb5
--- /dev/null
+++ b/src/multimedia/audio/qaudiosource.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+#ifndef QAUDIOINPUT_H
+#define QAUDIOINPUT_H
+
+#include <QtCore/qiodevice.h>
+
+#include <QtMultimedia/qtmultimediaglobal.h>
+
+#include <QtMultimedia/qtaudio.h>
+#include <QtMultimedia/qaudioformat.h>
+#include <QtMultimedia/qaudiodevice.h>
+
+
+QT_BEGIN_NAMESPACE
+
+class QPlatformAudioSource;
+
+class Q_MULTIMEDIA_EXPORT QAudioSource : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit QAudioSource(const QAudioFormat &format = QAudioFormat(), QObject *parent = nullptr);
+ explicit QAudioSource(const QAudioDevice &audioDeviceInfo, const QAudioFormat &format = QAudioFormat(), QObject *parent = nullptr);
+ ~QAudioSource();
+
+ bool isNull() const { return !d; }
+
+ QAudioFormat format() const;
+
+ void start(QIODevice *device);
+ QIODevice* start();
+
+ void stop();
+ void reset();
+ void suspend();
+ void resume();
+
+ void setBufferSize(qsizetype bytes);
+ qsizetype bufferSize() const;
+
+ qsizetype bytesAvailable() const;
+
+ void setVolume(qreal volume);
+ qreal volume() const;
+
+ qint64 processedUSecs() const;
+ qint64 elapsedUSecs() const;
+
+ QtAudio::Error error() const;
+ QtAudio::State state() const;
+
+Q_SIGNALS:
+#if defined(Q_QDOC)
+ void stateChanged(QtAudio::State state);
+#else
+ // use QAudio here to keep string-based connections working
+ void stateChanged(QAudio::State state);
+#endif
+
+private:
+ Q_DISABLE_COPY(QAudioSource)
+
+ QPlatformAudioSource *d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QAUDIOINPUT_H
diff --git a/src/multimedia/audio/qaudiostatemachine.cpp b/src/multimedia/audio/qaudiostatemachine.cpp
new file mode 100644
index 000000000..2d42200e6
--- /dev/null
+++ b/src/multimedia/audio/qaudiostatemachine.cpp
@@ -0,0 +1,150 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qaudiostatemachine_p.h"
+#include "qaudiosystem_p.h"
+#include <qpointer.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+using Notifier = QAudioStateMachine::Notifier;
+using namespace AudioStateMachineUtils;
+
+QAudioStateMachine::QAudioStateMachine(QAudioStateChangeNotifier &notifier) : m_notifier(&notifier)
+{
+}
+
+QAudioStateMachine::~QAudioStateMachine() = default;
+
+QAudio::State QAudioStateMachine::state() const
+{
+ return toAudioState(m_state.load(std::memory_order_acquire));
+}
+
+QAudio::Error QAudioStateMachine::error() const
+{
+ return toAudioError(m_state.load(std::memory_order_acquire));
+}
+
+template <typename StatesChecker, typename NewState>
+Notifier QAudioStateMachine::changeState(const StatesChecker &checker, const NewState &newState)
+{
+ if constexpr (std::is_same_v<RawState, NewState>)
+ return changeState(checker, [newState](RawState) { return newState; });
+ else {
+ RawState prevState = m_state.load(std::memory_order_relaxed);
+ const auto exchanged = multipleCompareExchange(m_state, prevState, checker, newState);
+
+ if (Q_LIKELY(exchanged))
+ return { this, newState(prevState), prevState };
+
+ return {};
+ }
+}
+
+Notifier QAudioStateMachine::stop(QAudio::Error error, bool shouldDrain, bool forceUpdateError)
+{
+ auto statesChecker =
+ makeStatesChecker(QAudio::ActiveState, QAudio::IdleState, QAudio::SuspendedState,
+ forceUpdateError ? QAudio::StoppedState : QAudio::ActiveState);
+
+ const auto state = toRawState(QAudio::StoppedState, error);
+ auto getNewState = [&](RawState prevState) {
+ const bool shouldAddFlag = shouldDrain && toAudioState(prevState) == QAudio::ActiveState;
+ return shouldAddFlag ? addDrainingFlag(state) : state;
+ };
+
+ return changeState(statesChecker, getNewState);
+}
+
+Notifier QAudioStateMachine::start(bool active)
+{
+ return changeState(makeStatesChecker(QAudio::StoppedState),
+ toRawState(active ? QAudio::ActiveState : QAudio::IdleState));
+}
+
+bool QAudioStateMachine::isActiveOrIdle() const
+{
+ const auto state = this->state();
+ return state == QAudio::ActiveState || state == QAudio::IdleState;
+}
+
+bool QAudioStateMachine::onDrained()
+{
+ return changeState(isDrainingState, removeDrainingFlag);
+}
+
+bool QAudioStateMachine::isDraining() const
+{
+ return isDrainingState(m_state.load(std::memory_order_acquire));
+}
+
+std::pair<bool, bool> QAudioStateMachine::getDrainedAndStopped() const
+{
+ const auto state = m_state.load(std::memory_order_acquire);
+ return { !isDrainingState(state), toAudioState(state) == QAudio::StoppedState };
+}
+
+Notifier QAudioStateMachine::suspend()
+{
+ // Due to the current documentation, we set QAudio::NoError.
+ // TBD: leave the previous error should be more reasonable (IgnoreError)
+ const auto error = QAudio::NoError;
+ auto result = changeState(makeStatesChecker(QAudio::ActiveState, QAudio::IdleState),
+ toRawState(QAudio::SuspendedState, error));
+
+ if (result)
+ m_suspendedInState = result.prevAudioState();
+
+ return result;
+}
+
+Notifier QAudioStateMachine::resume()
+{
+ // Due to the current documentation, we set QAudio::NoError.
+ // TBD: leave the previous error should be more reasonable (IgnoreError)
+ const auto error = QAudio::NoError;
+ return changeState(makeStatesChecker(QAudio::SuspendedState),
+ toRawState(m_suspendedInState, error));
+}
+
+Notifier QAudioStateMachine::activateFromIdle()
+{
+ return changeState(makeStatesChecker(QAudio::IdleState), toRawState(QAudio::ActiveState));
+}
+
+Notifier QAudioStateMachine::updateActiveOrIdle(bool isActive, QAudio::Error error)
+{
+ const auto state = isActive ? QAudio::ActiveState : QAudio::IdleState;
+ return changeState(makeStatesChecker(QAudio::ActiveState, QAudio::IdleState),
+ toRawState(state, error));
+}
+
+Notifier QAudioStateMachine::setError(QAudio::Error error)
+{
+ auto fixState = [error](RawState prevState) { return setStateError(prevState, error); };
+ return changeState([](RawState) { return true; }, fixState);
+}
+
+Notifier QAudioStateMachine::forceSetState(QAudio::State state, QAudio::Error error)
+{
+ return changeState([](RawState) { return true; }, toRawState(state, error));
+}
+
+void QAudioStateMachine::reset(RawState state, RawState prevState)
+{
+ auto notifier = m_notifier;
+
+ const auto audioState = toAudioState(state);
+ const auto audioError = toAudioError(state);
+
+ if (toAudioState(prevState) != audioState && notifier)
+ emit notifier->stateChanged(audioState);
+
+ // check the notifier in case the object was deleted in
+ if (toAudioError(prevState) != audioError && notifier)
+ emit notifier->errorChanged(audioError);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/audio/qaudiostatemachine_p.h b/src/multimedia/audio/qaudiostatemachine_p.h
new file mode 100644
index 000000000..385453020
--- /dev/null
+++ b/src/multimedia/audio/qaudiostatemachine_p.h
@@ -0,0 +1,149 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAUDIOSTATEMACHINE_P_H
+#define QAUDIOSTATEMACHINE_P_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 "qaudiostatemachineutils_p.h"
+
+#include <qpointer.h>
+#include <atomic>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioStateChangeNotifier;
+
+/* QAudioStateMachine provides an opportunity to
+ * toggle QAudio::State with QAudio::Error in
+ * a thread-safe manner.
+ * The toggling functions return a notifier,
+ * which notifies about the change via
+ * QAudioStateChangeNotifier::stateChanged and errorChanged.
+ *
+ * The state machine is supposed to be used mostly in
+ * QAudioSink and QAudioSource implementations.
+ */
+class Q_MULTIMEDIA_EXPORT QAudioStateMachine
+{
+public:
+ using RawState = AudioStateMachineUtils::RawState;
+ class Notifier
+ {
+ public:
+ void reset()
+ {
+ if (auto stateMachine = std::exchange(m_stateMachine, nullptr))
+ stateMachine->reset(m_state, m_prevState);
+ }
+
+ ~Notifier() { reset(); }
+
+ Notifier(const Notifier &) = delete;
+ Notifier(Notifier &&other) noexcept
+ : m_stateMachine(std::exchange(other.m_stateMachine, nullptr)),
+ m_state(other.m_state),
+ m_prevState(other.m_prevState)
+ {
+ }
+
+ operator bool() const { return m_stateMachine != nullptr; }
+
+ QAudio::State prevAudioState() const { return AudioStateMachineUtils::toAudioState(m_prevState); }
+
+ QAudio::State audioState() const { return AudioStateMachineUtils::toAudioState(m_state); }
+
+ bool isDraining() const { return AudioStateMachineUtils::isDrainingState(m_state); }
+
+ bool isStateChanged() const { return prevAudioState() != audioState(); }
+
+ private:
+ Notifier(QAudioStateMachine *stateMachine = nullptr, RawState state = QAudio::StoppedState,
+ RawState prevState = QAudio::StoppedState)
+ : m_stateMachine(stateMachine), m_state(state), m_prevState(prevState)
+ {
+ }
+
+ private:
+ QAudioStateMachine *m_stateMachine;
+ RawState m_state;
+ const RawState m_prevState;
+
+ friend class QAudioStateMachine;
+ };
+
+ QAudioStateMachine(QAudioStateChangeNotifier &notifier);
+
+ ~QAudioStateMachine();
+
+ QAudio::State state() const;
+
+ QAudio::Error error() const;
+
+ bool isActiveOrIdle() const;
+
+ bool isDraining() const;
+
+ // atomicaly checks if the state is stopped and marked as drained
+ std::pair<bool, bool> getDrainedAndStopped() const;
+
+ // Stopped[draining] -> Stopped
+ bool onDrained();
+
+ // Active/Idle/Suspended -> Stopped
+ // or Active -> Stopped[draining] for shouldDrain = true
+ Notifier stop(QAudio::Error error = QAudio::NoError, bool shouldDrain = false,
+ bool forceUpdateError = false);
+
+ // Active/Idle/Suspended -> Stopped
+ Notifier stopOrUpdateError(QAudio::Error error = QAudio::NoError)
+ {
+ return stop(error, false, true);
+ }
+
+ // Stopped -> Active/Idle
+ Notifier start(bool isActive = true);
+
+ // Active/Idle -> Suspended + saves the exchanged state
+ Notifier suspend();
+
+ // Suspended -> saved state (Active/Idle)
+ Notifier resume();
+
+ // Idle -> Active
+ Notifier activateFromIdle();
+
+ // Active/Idle -> Active/Idle + updateError
+ Notifier updateActiveOrIdle(bool isActive, QAudio::Error error = QAudio::NoError);
+
+ // Any -> Any; better use more strict methods
+ Notifier forceSetState(QAudio::State state, QAudio::Error error = QAudio::NoError);
+
+ // force set the error
+ Notifier setError(QAudio::Error error);
+
+private:
+ template <typename StatesChecker, typename NewState>
+ Notifier changeState(const StatesChecker &statesChecker, const NewState &newState);
+
+ void reset(RawState state, RawState prevState);
+
+private:
+ QPointer<QAudioStateChangeNotifier> m_notifier;
+ std::atomic<RawState> m_state = QAudio::StoppedState;
+ QAudio::State m_suspendedInState = QAudio::SuspendedState;
+};
+
+QT_END_NAMESPACE
+
+#endif // QAUDIOSTATEMACHINE_P_H
diff --git a/src/multimedia/audio/qaudiostatemachineutils_p.h b/src/multimedia/audio/qaudiostatemachineutils_p.h
new file mode 100644
index 000000000..f80517f77
--- /dev/null
+++ b/src/multimedia/audio/qaudiostatemachineutils_p.h
@@ -0,0 +1,96 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAUDIOSTATEMACHINEUTILS_P_H
+#define QAUDIOSTATEMACHINEUTILS_P_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 "qaudio.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace AudioStateMachineUtils {
+
+using RawState = int;
+
+constexpr uint32_t AudioStateBitsCount = 8;
+constexpr RawState AudioStateMask = 0xFF;
+constexpr RawState AudioErrorMask = 0xFF00;
+constexpr RawState DrainingFlag = 0x10000;
+
+static_assert(!(AudioStateMask & DrainingFlag) && !(AudioStateMask & AudioErrorMask)
+ && !(AudioErrorMask & DrainingFlag),
+ "Invalid masks");
+
+constexpr bool isDrainingState(RawState state)
+{
+ return (state & DrainingFlag) != 0;
+}
+
+constexpr RawState addDrainingFlag(RawState state)
+{
+ return state | DrainingFlag;
+}
+
+constexpr RawState removeDrainingFlag(RawState state)
+{
+ return state & ~DrainingFlag;
+}
+
+constexpr QAudio::State toAudioState(RawState state)
+{
+ return QAudio::State(state & AudioStateMask);
+}
+
+constexpr QAudio::Error toAudioError(RawState state)
+{
+ return QAudio::Error((state & AudioErrorMask) >> AudioStateBitsCount);
+}
+
+constexpr RawState toRawState(QAudio::State state, QAudio::Error error = QAudio::NoError)
+{
+ return state | (error << AudioStateBitsCount);
+}
+
+constexpr RawState setStateError(RawState state, QAudio::Error error)
+{
+ return (error << AudioStateBitsCount) | (state & ~AudioErrorMask);
+}
+
+template <typename... States>
+constexpr auto makeStatesChecker(States... states)
+{
+ return [=](RawState state) {
+ state &= (AudioStateMask | DrainingFlag);
+ return (... || (state == states));
+ };
+}
+
+// ensures compareExchange (testAndSet) operation with opportunity
+// to check several states, can be considered as atomic
+template <typename T, typename Predicate, typename NewValueGetter>
+bool multipleCompareExchange(std::atomic<T> &target, T &prevValue, Predicate predicate,
+ NewValueGetter newValueGetter)
+{
+ while (predicate(prevValue))
+ if (target.compare_exchange_strong(prevValue, newValueGetter(prevValue),
+ std::memory_order_acq_rel))
+ return true;
+
+ return false;
+}
+} // namespace AudioStateMachineUtils
+
+QT_END_NAMESPACE
+
+#endif // QAUDIOSTATEMACHINEUTILS_P_H
diff --git a/src/multimedia/audio/qaudiosystem.cpp b/src/multimedia/audio/qaudiosystem.cpp
index b11993e55..355771f6b 100644
--- a/src/multimedia/audio/qaudiosystem.cpp
+++ b/src/multimedia/audio/qaudiosystem.cpp
@@ -1,376 +1,23 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qaudiosystem.h"
+#include "qaudiosystem_p.h"
-QT_BEGIN_NAMESPACE
-
-/*!
- \class QAbstractAudioDeviceInfo
- \brief The QAbstractAudioDeviceInfo class is a base class for audio backends.
-
- \ingroup multimedia
- \ingroup multimedia_audio
- \inmodule QtMultimedia
-
- This class implements the audio functionality for
- QAudioDeviceInfo, i.e., QAudioDeviceInfo keeps a
- QAbstractAudioDeviceInfo and routes function calls to it. For a
- description of the functionality that QAbstractAudioDeviceInfo
- implements, you can read the class and functions documentation of
- QAudioDeviceInfo.
-
- \sa QAudioDeviceInfo
- \sa QAbstractAudioOutput, QAbstractAudioInput
-*/
-
-/*!
- \fn virtual QAudioFormat QAbstractAudioDeviceInfo::preferredFormat() const
- Returns the recommended settings to use.
-*/
-
-/*!
- \fn virtual bool QAbstractAudioDeviceInfo::isFormatSupported(const QAudioFormat& format) const
- Returns true if \a format is available from audio device.
-*/
-
-/*!
- \fn virtual QString QAbstractAudioDeviceInfo::deviceName() const
- Returns the audio device name.
-*/
-
-/*!
- \fn virtual QStringList QAbstractAudioDeviceInfo::supportedCodecs()
- Returns the list of currently available codecs.
-*/
-
-/*!
- \fn virtual QList<int> QAbstractAudioDeviceInfo::supportedSampleRates()
- Returns the list of currently available sample rates.
-*/
-
-/*!
- \fn virtual QList<int> QAbstractAudioDeviceInfo::supportedChannelCounts()
- Returns the list of currently available channels.
-*/
-
-/*!
- \fn virtual QList<int> QAbstractAudioDeviceInfo::supportedSampleSizes()
- Returns the list of currently available sample sizes.
-*/
-
-/*!
- \fn virtual QList<QAudioFormat::Endian> QAbstractAudioDeviceInfo::supportedByteOrders()
- Returns the list of currently available byte orders.
-*/
-
-/*!
- \fn virtual QList<QAudioFormat::SampleType> QAbstractAudioDeviceInfo::supportedSampleTypes()
- Returns the list of currently available sample types.
-*/
-
-/*!
- \class QAbstractAudioOutput
- \brief The QAbstractAudioOutput class is a base class for audio backends.
-
- \ingroup multimedia
- \inmodule QtMultimedia
-
- QAbstractAudioOutput implements audio functionality for
- QAudioOutput, i.e., QAudioOutput routes function calls to
- QAbstractAudioOutput. For a description of the functionality that
- is implemented, see the QAudioOutput class and function
- descriptions.
-
- \sa QAudioOutput
-*/
-
-/*!
- \fn virtual void QAbstractAudioOutput::start(QIODevice* device)
- Uses the \a device as the QIODevice to transfer data.
-*/
-
-/*!
- \fn virtual QIODevice* QAbstractAudioOutput::start()
- Returns a pointer to the QIODevice being used to handle
- the data transfer. This QIODevice can be used to write() audio data directly.
-*/
-
-/*!
- \fn virtual void QAbstractAudioOutput::stop()
- Stops the audio output.
-*/
-
-/*!
- \fn virtual void QAbstractAudioOutput::reset()
- Drops all audio data in the buffers, resets buffers to zero.
-*/
-
-/*!
- \fn virtual void QAbstractAudioOutput::suspend()
- Stops processing audio data, preserving buffered audio data.
-*/
-
-/*!
- \fn virtual void QAbstractAudioOutput::resume()
- Resumes processing audio data after a suspend()
-*/
-
-/*!
- \fn virtual int QAbstractAudioOutput::bytesFree() const
- Returns the free space available in bytes in the audio buffer.
-*/
-
-/*!
- \fn virtual int QAbstractAudioOutput::periodSize() const
- Returns the period size in bytes.
-*/
-
-/*!
- \fn virtual void QAbstractAudioOutput::setBufferSize(int value)
- Sets the audio buffer size to \a value in bytes.
-*/
-
-/*!
- \fn virtual int QAbstractAudioOutput::bufferSize() const
- Returns the audio buffer size in bytes.
-*/
-
-/*!
- \fn virtual void QAbstractAudioOutput::setNotifyInterval(int ms)
- Sets the interval for notify() signal to be emitted. This is based on the \a ms
- of audio data processed not on actual real-time. The resolution of the timer
- is platform specific.
-*/
-
-/*!
- \fn virtual int QAbstractAudioOutput::notifyInterval() const
- Returns the notify interval in milliseconds.
-*/
-
-/*!
- \fn virtual qint64 QAbstractAudioOutput::processedUSecs() const
- Returns the amount of audio data processed since start() was called in milliseconds.
-*/
-
-/*!
- \fn virtual qint64 QAbstractAudioOutput::elapsedUSecs() const
- Returns the milliseconds since start() was called, including time in Idle and suspend states.
-*/
+#include <private/qplatformmediadevices_p.h>
-/*!
- \fn virtual QAudio::Error QAbstractAudioOutput::error() const
- Returns the error state.
-*/
-
-/*!
- \fn virtual QAudio::State QAbstractAudioOutput::state() const
- Returns the state of audio processing.
-*/
-
-/*!
- \fn virtual void QAbstractAudioOutput::setFormat(const QAudioFormat& fmt)
- Set the QAudioFormat to use to \a fmt.
- Setting the format is only allowable while in QAudio::StoppedState.
-*/
-
-/*!
- \fn virtual QAudioFormat QAbstractAudioOutput::format() const
- Returns the QAudioFormat being used.
-*/
-
-/*!
- \fn virtual void QAbstractAudioOutput::setVolume(qreal volume)
- Sets the volume.
- Where \a volume is between 0.0 and 1.0.
-*/
-
-/*!
- \fn virtual qreal QAbstractAudioOutput::volume() const
- Returns the volume in the range 0.0 and 1.0.
-*/
-
-/*!
- \fn QAbstractAudioOutput::errorChanged(QAudio::Error error)
- This signal is emitted when the \a error state has changed.
-*/
-
-/*!
- \fn QAbstractAudioOutput::stateChanged(QAudio::State state)
- This signal is emitted when the device \a state has changed.
-*/
-
-/*!
- \fn QAbstractAudioOutput::notify()
- This signal is emitted when x ms of audio data has been processed
- the interval set by setNotifyInterval(x).
-*/
-
-
-/*!
- \class QAbstractAudioInput
- \brief The QAbstractAudioInput class provides access for QAudioInput to access the audio
- device provided by the plugin.
-
- \ingroup multimedia
- \inmodule QtMultimedia
-
- QAudioDeviceInput keeps an instance of QAbstractAudioInput and
- routes calls to functions of the same name to QAbstractAudioInput.
- This means that it is QAbstractAudioInput that implements the
- audio functionality. For a description of the functionality, see
- the QAudioInput class description.
-
- \sa QAudioInput
-*/
-
-/*!
- \fn virtual void QAbstractAudioInput::start(QIODevice* device)
- Uses the \a device as the QIODevice to transfer data.
-*/
-
-/*!
- \fn virtual QIODevice* QAbstractAudioInput::start()
- Returns a pointer to the QIODevice being used to handle
- the data transfer. This QIODevice can be used to read() audio data directly.
-*/
-
-/*!
- \fn virtual void QAbstractAudioInput::stop()
- Stops the audio input.
-*/
-
-/*!
- \fn virtual void QAbstractAudioInput::reset()
- Drops all audio data in the buffers, resets buffers to zero.
-*/
-
-/*!
- \fn virtual void QAbstractAudioInput::suspend()
- Stops processing audio data, preserving buffered audio data.
-*/
-
-/*!
- \fn virtual void QAbstractAudioInput::resume()
- Resumes processing audio data after a suspend().
-*/
-
-/*!
- \fn virtual int QAbstractAudioInput::bytesReady() const
- Returns the amount of audio data available to read in bytes.
-*/
-
-/*!
- \fn virtual int QAbstractAudioInput::periodSize() const
- Returns the period size in bytes.
-*/
-
-/*!
- \fn virtual void QAbstractAudioInput::setBufferSize(int value)
- Sets the audio buffer size to \a value in milliseconds.
-*/
-
-/*!
- \fn virtual int QAbstractAudioInput::bufferSize() const
- Returns the audio buffer size in milliseconds.
-*/
-
-/*!
- \fn virtual void QAbstractAudioInput::setNotifyInterval(int ms)
- Sets the interval for notify() signal to be emitted. This is based
- on the \a ms of audio data processed not on actual real-time.
- The resolution of the timer is platform specific.
-*/
-
-/*!
- \fn virtual int QAbstractAudioInput::notifyInterval() const
- Returns the notify interval in milliseconds.
-*/
-
-/*!
- \fn virtual qint64 QAbstractAudioInput::processedUSecs() const
- Returns the amount of audio data processed since start() was called in milliseconds.
-*/
-
-/*!
- \fn virtual qint64 QAbstractAudioInput::elapsedUSecs() const
- Returns the milliseconds since start() was called, including time in Idle and suspend states.
-*/
-
-/*!
- \fn virtual QAudio::Error QAbstractAudioInput::error() const
- Returns the error state.
-*/
-
-/*!
- \fn virtual QAudio::State QAbstractAudioInput::state() const
- Returns the state of audio processing.
-*/
-
-/*!
- \fn virtual void QAbstractAudioInput::setFormat(const QAudioFormat& fmt)
- Set the QAudioFormat to use to \a fmt.
- Setting the format is only allowable while in QAudio::StoppedState.
-*/
-
-/*!
- \fn virtual QAudioFormat QAbstractAudioInput::format() const
- Returns the QAudioFormat being used
-*/
+QT_BEGIN_NAMESPACE
-/*!
- \fn QAbstractAudioInput::errorChanged(QAudio::Error error)
- This signal is emitted when the \a error state has changed.
-*/
+QAudioStateChangeNotifier::QAudioStateChangeNotifier(QObject *parent) : QObject(parent) { }
-/*!
- \fn QAbstractAudioInput::stateChanged(QAudio::State state)
- This signal is emitted when the device \a state has changed.
-*/
+QPlatformAudioSink::QPlatformAudioSink(QObject *parent) : QAudioStateChangeNotifier(parent) { }
-/*!
- \fn QAbstractAudioInput::notify()
- This signal is emitted when x ms of audio data has been processed
- the interval set by setNotifyInterval(x).
-*/
+qreal QPlatformAudioSink::volume() const
+{
+ return 1.0;
+}
+QPlatformAudioSource::QPlatformAudioSource(QObject *parent) : QAudioStateChangeNotifier(parent) { }
QT_END_NAMESPACE
-#include "moc_qaudiosystem.cpp"
+#include "moc_qaudiosystem_p.cpp"
diff --git a/src/multimedia/audio/qaudiosystem.h b/src/multimedia/audio/qaudiosystem.h
deleted file mode 100644
index c326460fe..000000000
--- a/src/multimedia/audio/qaudiosystem.h
+++ /dev/null
@@ -1,138 +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 QAUDIOSYSTEM_H
-#define QAUDIOSYSTEM_H
-
-#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qmultimedia.h>
-
-#include <QtMultimedia/qaudio.h>
-#include <QtMultimedia/qaudioformat.h>
-#include <QtMultimedia/qaudiodeviceinfo.h>
-
-QT_BEGIN_NAMESPACE
-
-class QIODevice;
-
-class Q_MULTIMEDIA_EXPORT QAbstractAudioDeviceInfo : public QObject
-{
- Q_OBJECT
-
-public:
- virtual QAudioFormat preferredFormat() const = 0;
- virtual bool isFormatSupported(const QAudioFormat &format) const = 0;
- virtual QString deviceName() const = 0;
- virtual QStringList supportedCodecs() = 0;
- virtual QList<int> supportedSampleRates() = 0;
- virtual QList<int> supportedChannelCounts() = 0;
- virtual QList<int> supportedSampleSizes() = 0;
- virtual QList<QAudioFormat::Endian> supportedByteOrders() = 0;
- virtual QList<QAudioFormat::SampleType> supportedSampleTypes() = 0;
-};
-
-class Q_MULTIMEDIA_EXPORT QAbstractAudioOutput : public QObject
-{
- Q_OBJECT
-
-public:
- virtual void start(QIODevice *device) = 0;
- virtual QIODevice* start() = 0;
- virtual void stop() = 0;
- virtual void reset() = 0;
- virtual void suspend() = 0;
- virtual void resume() = 0;
- virtual int bytesFree() const = 0;
- virtual int periodSize() const = 0;
- virtual void setBufferSize(int value) = 0;
- virtual int bufferSize() const = 0;
- virtual void setNotifyInterval(int milliSeconds) = 0;
- virtual int notifyInterval() const = 0;
- virtual qint64 processedUSecs() const = 0;
- virtual qint64 elapsedUSecs() const = 0;
- virtual QAudio::Error error() const = 0;
- virtual QAudio::State state() const = 0;
- virtual void setFormat(const QAudioFormat& fmt) = 0;
- virtual QAudioFormat format() const = 0;
- virtual void setVolume(qreal) {}
- virtual qreal volume() const { return 1.0; }
- virtual QString category() const { return QString(); }
- virtual void setCategory(const QString &) { }
-
-Q_SIGNALS:
- void errorChanged(QAudio::Error error);
- void stateChanged(QAudio::State state);
- void notify();
-};
-
-class Q_MULTIMEDIA_EXPORT QAbstractAudioInput : public QObject
-{
- Q_OBJECT
-
-public:
- virtual void start(QIODevice *device) = 0;
- virtual QIODevice* start() = 0;
- virtual void stop() = 0;
- virtual void reset() = 0;
- virtual void suspend() = 0;
- virtual void resume() = 0;
- virtual int bytesReady() const = 0;
- virtual int periodSize() const = 0;
- virtual void setBufferSize(int value) = 0;
- virtual int bufferSize() const = 0;
- virtual void setNotifyInterval(int milliSeconds) = 0;
- virtual int notifyInterval() const = 0;
- virtual qint64 processedUSecs() const = 0;
- virtual qint64 elapsedUSecs() const = 0;
- virtual QAudio::Error error() const = 0;
- virtual QAudio::State state() const = 0;
- virtual void setFormat(const QAudioFormat& fmt) = 0;
- virtual QAudioFormat format() const = 0;
- virtual void setVolume(qreal) = 0;
- virtual qreal volume() const = 0;
-
-Q_SIGNALS:
- void errorChanged(QAudio::Error error);
- void stateChanged(QAudio::State state);
- void notify();
-};
-
-QT_END_NAMESPACE
-
-#endif // QAUDIOSYSTEM_H
diff --git a/src/multimedia/audio/qaudiosystem_p.h b/src/multimedia/audio/qaudiosystem_p.h
new file mode 100644
index 000000000..4a0650e80
--- /dev/null
+++ b/src/multimedia/audio/qaudiosystem_p.h
@@ -0,0 +1,96 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAUDIOSYSTEM_H
+#define QAUDIOSYSTEM_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 <QtMultimedia/qtmultimediaglobal.h>
+
+#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qaudioformat.h>
+#include <QtMultimedia/qaudiodevice.h>
+
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/private/qglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+
+class Q_MULTIMEDIA_EXPORT QAudioStateChangeNotifier : public QObject
+{
+ Q_OBJECT
+public:
+ QAudioStateChangeNotifier(QObject *parent = nullptr);
+
+signals:
+ void errorChanged(QAudio::Error error);
+ void stateChanged(QAudio::State state);
+};
+
+class Q_MULTIMEDIA_EXPORT QPlatformAudioSink : public QAudioStateChangeNotifier
+{
+ Q_OBJECT
+
+public:
+ QPlatformAudioSink(QObject *parent);
+ virtual void start(QIODevice *device) = 0;
+ virtual QIODevice* start() = 0;
+ virtual void stop() = 0;
+ virtual void reset() = 0;
+ virtual void suspend() = 0;
+ virtual void resume() = 0;
+ virtual qsizetype bytesFree() const = 0;
+ virtual void setBufferSize(qsizetype value) = 0;
+ virtual qsizetype bufferSize() const = 0;
+ virtual qint64 processedUSecs() const = 0;
+ virtual QAudio::Error error() const = 0;
+ virtual QAudio::State state() const = 0;
+ virtual void setFormat(const QAudioFormat& fmt) = 0;
+ virtual QAudioFormat format() const = 0;
+ virtual void setVolume(qreal) {}
+ virtual qreal volume() const;
+
+ QElapsedTimer elapsedTime;
+};
+
+class Q_MULTIMEDIA_EXPORT QPlatformAudioSource : public QAudioStateChangeNotifier
+{
+ Q_OBJECT
+
+public:
+ QPlatformAudioSource(QObject *parent);
+ virtual void start(QIODevice *device) = 0;
+ virtual QIODevice* start() = 0;
+ virtual void stop() = 0;
+ virtual void reset() = 0;
+ virtual void suspend() = 0;
+ virtual void resume() = 0;
+ virtual qsizetype bytesReady() const = 0;
+ virtual void setBufferSize(qsizetype value) = 0;
+ virtual qsizetype bufferSize() const = 0;
+ virtual qint64 processedUSecs() const = 0;
+ virtual QAudio::Error error() const = 0;
+ virtual QAudio::State state() const = 0;
+ virtual void setFormat(const QAudioFormat& fmt) = 0;
+ virtual QAudioFormat format() const = 0;
+ virtual void setVolume(qreal) = 0;
+ virtual qreal volume() const = 0;
+
+ QElapsedTimer elapsedTime;
+};
+
+QT_END_NAMESPACE
+
+#endif // QAUDIOSYSTEM_H
diff --git a/src/multimedia/audio/qaudiosystemplugin.cpp b/src/multimedia/audio/qaudiosystemplugin.cpp
deleted file mode 100644
index cb5628477..000000000
--- a/src/multimedia/audio/qaudiosystemplugin.cpp
+++ /dev/null
@@ -1,138 +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$
-**
-****************************************************************************/
-
-
-#include "qaudiosystemplugin.h"
-#include "qaudiosystempluginext_p.h"
-
-QT_BEGIN_NAMESPACE
-
-QAudioSystemFactoryInterface::~QAudioSystemFactoryInterface()
-{
-}
-
-QAudioSystemPluginExtension::~QAudioSystemPluginExtension()
-{
-}
-
-/*!
- \class QAudioSystemPlugin
- \brief The QAudioSystemPlugin class provides an abstract base for audio plugins.
-
- \ingroup multimedia
- \ingroup multimedia_audio
- \inmodule QtMultimedia
-
- Writing a audio plugin is achieved by subclassing this base class,
- reimplementing the pure virtual functions availableDevices(),
- createInput(), createOutput() and createDeviceInfo() then exporting
- the class with the Q_PLUGIN_METADATA() macro.
-
- The json file containing the meta data should contain a list of keys
- matching the plugin. Add "default" to your list of keys available
- to override the default audio device to be provided by your plugin.
-
- \code
- { "Keys": [ "default" ] }
- \endcode
-
- Unit tests are available to help in debugging new plugins.
-
- \sa QAbstractAudioDeviceInfo, QAbstractAudioOutput, QAbstractAudioInput
-
- Qt comes with plugins for Windows (WinMM and WASAPI), Linux (ALSA and PulseAudio), \macos / iOS
- (CoreAudio), Android (OpenSL ES) and QNX.
-
- If no audio plugins are available, a fallback dummy backend will be used.
- This should print out warnings if this is the case when you try and use QAudioInput
- or QAudioOutput. To fix this problem, make sure the dependencies for the Qt plugins are
- installed on the system and reconfigure Qt (e.g. alsa-devel package on Linux), or create your
- own plugin with a default key to always override the dummy fallback. The easiest way to
- determine if you have only a dummy backend is to get a list of available audio devices.
-
- QAudioDeviceInfo::availableDevices(QAudio::AudioOutput).size() = 0 (dummy backend)
-*/
-
-/*!
- \fn QAudioSystemPlugin::QAudioSystemPlugin(QObject* parent)
-
- Constructs a new audio plugin with \a parent.
- This is invoked automatically by the Q_PLUGIN_METADATA() macro.
-*/
-
-QAudioSystemPlugin::QAudioSystemPlugin(QObject* parent) :
- QObject(parent)
-{}
-
-/*!
- \fn QAudioSystemPlugin::~QAudioSystemPlugin()
-
- Destroys the audio plugin.
- You never have to call this explicitly. Qt destroys a plugin automatically when it is no longer used.
-*/
-
-QAudioSystemPlugin::~QAudioSystemPlugin()
-{}
-
-/*!
- \fn QList<QByteArray> QAudioSystemPlugin::availableDevices(QAudio::Mode mode) const
- Returns a list of available audio devices for \a mode
-*/
-
-/*!
- \fn QAbstractAudioInput* QAudioSystemPlugin::createInput(const QByteArray& device)
- Returns a pointer to a QAbstractAudioInput created using \a device identifier
-*/
-
-/*!
- \fn QAbstractAudioOutput* QAudioSystemPlugin::createOutput(const QByteArray& device)
- Returns a pointer to a QAbstractAudioOutput created using \a device identifier
-
-*/
-
-/*!
- \fn QAbstractAudioDeviceInfo* QAudioSystemPlugin::createDeviceInfo(const QByteArray& device, QAudio::Mode mode)
- Returns a pointer to a QAbstractAudioDeviceInfo created using \a device and \a mode
-
-*/
-
-
-QT_END_NAMESPACE
-
-#include "moc_qaudiosystemplugin.cpp"
diff --git a/src/multimedia/audio/qaudiosystemplugin.h b/src/multimedia/audio/qaudiosystemplugin.h
deleted file mode 100644
index 18079e9d9..000000000
--- a/src/multimedia/audio/qaudiosystemplugin.h
+++ /dev/null
@@ -1,86 +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 QAUDIOSYSTEMPLUGIN_H
-#define QAUDIOSYSTEMPLUGIN_H
-
-#include <QtCore/qstring.h>
-#include <QtCore/qplugin.h>
-
-#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qmultimedia.h>
-
-#include <QtMultimedia/qaudioformat.h>
-#include <QtMultimedia/qaudiodeviceinfo.h>
-#include <QtMultimedia/qaudiosystem.h>
-
-QT_BEGIN_NAMESPACE
-
-struct Q_MULTIMEDIA_EXPORT QAudioSystemFactoryInterface
-{
- virtual QList<QByteArray> availableDevices(QAudio::Mode) const = 0;
- virtual QAbstractAudioInput* createInput(const QByteArray& device) = 0;
- virtual QAbstractAudioOutput* createOutput(const QByteArray& device) = 0;
- virtual QAbstractAudioDeviceInfo* createDeviceInfo(const QByteArray& device, QAudio::Mode mode) = 0;
- virtual ~QAudioSystemFactoryInterface();
-};
-
-#define QAudioSystemFactoryInterface_iid \
- "org.qt-project.qt.audiosystemfactory/5.0"
-Q_DECLARE_INTERFACE(QAudioSystemFactoryInterface, QAudioSystemFactoryInterface_iid)
-
-class Q_MULTIMEDIA_EXPORT QAudioSystemPlugin : public QObject, public QAudioSystemFactoryInterface
-{
- Q_OBJECT
- Q_INTERFACES(QAudioSystemFactoryInterface)
-
-public:
- explicit QAudioSystemPlugin(QObject *parent = nullptr);
- ~QAudioSystemPlugin();
-
- QList<QByteArray> availableDevices(QAudio::Mode) const override = 0;
- QAbstractAudioInput* createInput(const QByteArray& device) override = 0;
- QAbstractAudioOutput* createOutput(const QByteArray& device) override = 0;
- QAbstractAudioDeviceInfo* createDeviceInfo(const QByteArray& device, QAudio::Mode mode) override = 0;
-};
-
-QT_END_NAMESPACE
-
-#endif // QAUDIOSYSTEMPLUGIN_H
diff --git a/src/multimedia/audio/qaudiosystempluginext_p.h b/src/multimedia/audio/qaudiosystempluginext_p.h
deleted file mode 100644
index 6493b7f77..000000000
--- a/src/multimedia/audio/qaudiosystempluginext_p.h
+++ /dev/null
@@ -1,71 +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 QAUDIOSYSTEMPLUGINEXT_P_H
-#define QAUDIOSYSTEMPLUGINEXT_P_H
-
-#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qaudio.h>
-#include <QtCore/qplugin.h>
-
-QT_BEGIN_NAMESPACE
-
-//
-// 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.
-//
-
-struct Q_MULTIMEDIA_EXPORT QAudioSystemPluginExtension
-{
- virtual QByteArray defaultDevice(QAudio::Mode) const = 0;
- virtual ~QAudioSystemPluginExtension();
-};
-
-#define QAudioSystemPluginExtension_iid "org.qt-project.qt.audiosystempluginextension"
-Q_DECLARE_INTERFACE(QAudioSystemPluginExtension, QAudioSystemPluginExtension_iid)
-
-QT_END_NAMESPACE
-
-#endif // QAUDIOSYSTEMPLUGINEXT_P_H
diff --git a/src/multimedia/audio/qsamplecache_p.cpp b/src/multimedia/audio/qsamplecache_p.cpp
index b293946cc..805ab534e 100644
--- a/src/multimedia/audio/qsamplecache_p.cpp
+++ b/src/multimedia/audio/qsamplecache_p.cpp
@@ -1,51 +1,17 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsamplecache_p.h"
-#include "qwavedecoder_p.h"
+#include "qwavedecoder.h"
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <QtCore/QDebug>
-//#define QT_SAMPLECACHE_DEBUG
+#include <QtCore/qloggingcategory.h>
+
+Q_STATIC_LOGGING_CATEGORY(qLcSampleCache, "qt.multimedia.samplecache")
#include <mutex>
@@ -105,8 +71,6 @@ QSampleCache::QSampleCache(QObject *parent)
, m_loadingRefCount(0)
{
m_loadingThread.setObjectName(QLatin1String("QSampleCache::LoadingThread"));
- connect(&m_loadingThread, SIGNAL(finished()), this, SIGNAL(isLoadingChanged()));
- connect(&m_loadingThread, SIGNAL(started()), this, SIGNAL(isLoadingChanged()));
}
QNetworkAccessManager& QSampleCache::networkAccessManager()
@@ -166,22 +130,25 @@ QSample* QSampleCache::requestSample(const QUrl& url)
{
//lock and add first to make sure live loadingThread will not be killed during this function call
m_loadingMutex.lock();
+ const bool needsThreadStart = m_loadingRefCount == 0;
m_loadingRefCount++;
m_loadingMutex.unlock();
- if (!m_loadingThread.isRunning())
- m_loadingThread.start();
-
-#ifdef QT_SAMPLECACHE_DEBUG
- qDebug() << "QSampleCache: request sample [" << url << "]";
-#endif
+ qCDebug(qLcSampleCache) << "QSampleCache: request sample [" << url << "]";
std::unique_lock<QRecursiveMutex> locker(m_mutex);
QMap<QUrl, QSample*>::iterator it = m_samples.find(url);
QSample* sample;
if (it == m_samples.end()) {
+ if (needsThreadStart) {
+ // Previous thread might be finishing, need to wait for it. If not, this is a no-op.
+ m_loadingThread.wait();
+ m_loadingThread.start();
+ }
sample = new QSample(url, this);
m_samples.insert(url, sample);
+#if QT_CONFIG(thread)
sample->moveToThread(&m_loadingThread);
+#endif
} else {
sample = *it;
}
@@ -198,9 +165,7 @@ void QSampleCache::setCapacity(qint64 capacity)
const std::lock_guard<QRecursiveMutex> locker(m_mutex);
if (m_capacity == capacity)
return;
-#ifdef QT_SAMPLECACHE_DEBUG
- qDebug() << "QSampleCache: capacity changes from " << m_capacity << "to " << capacity;
-#endif
+ qCDebug(qLcSampleCache) << "QSampleCache: capacity changes from " << m_capacity << "to " << capacity;
if (m_capacity > 0 && capacity <= 0) { //memory management strategy changed
for (QMap<QUrl, QSample*>::iterator it = m_samples.begin(); it != m_samples.end();) {
QSample* sample = *it;
@@ -233,9 +198,7 @@ void QSampleCache::refresh(qint64 usageChange)
if (m_capacity <= 0 || m_usage <= m_capacity)
return;
-#ifdef QT_SAMPLECACHE_DEBUG
qint64 recoveredSize = 0;
-#endif
//free unused samples to keep usage under capacity limit.
for (QMap<QUrl, QSample*>::iterator it = m_samples.begin(); it != m_samples.end();) {
@@ -244,20 +207,16 @@ void QSampleCache::refresh(qint64 usageChange)
++it;
continue;
}
-#ifdef QT_SAMPLECACHE_DEBUG
recoveredSize += sample->m_soundData.size();
-#endif
unloadSample(sample);
it = m_samples.erase(it);
if (m_usage <= m_capacity)
return;
}
-#ifdef QT_SAMPLECACHE_DEBUG
- qDebug() << "QSampleCache: refresh(" << usageChange
+ qCDebug(qLcSampleCache) << "QSampleCache: refresh(" << usageChange
<< ") recovered size =" << recoveredSize
<< "new usage =" << m_usage;
-#endif
if (m_usage > m_capacity)
qWarning() << "QSampleCache: usage[" << m_usage << " out of limit[" << m_capacity << "]";
@@ -278,9 +237,7 @@ QSample::~QSample()
m_parent->removeUnreferencedSample(this);
QMutexLocker locker(&m_mutex);
-#ifdef QT_SAMPLECACHE_DEBUG
- qDebug() << "~QSample" << this << ": deleted [" << m_url << "]" << QThread::currentThread();
-#endif
+ qCDebug(qLcSampleCache) << "~QSample" << this << ": deleted [" << m_url << "]" << QThread::currentThread();
cleanup();
}
@@ -315,9 +272,7 @@ bool QSampleCache::notifyUnreferencedSample(QSample* sample)
void QSample::release()
{
QMutexLocker locker(&m_mutex);
-#ifdef QT_SAMPLECACHE_DEBUG
- qDebug() << "Sample:: release" << this << QThread::currentThread() << m_ref;
-#endif
+ qCDebug(qLcSampleCache) << "Sample:: release" << this << QThread::currentThread() << m_ref;
if (--m_ref == 0) {
locker.unlock();
m_parent->notifyUnreferencedSample(this);
@@ -328,10 +283,15 @@ void QSample::release()
// must be called locked.
void QSample::cleanup()
{
- if (m_waveDecoder)
+ qCDebug(qLcSampleCache) << "QSample: cleanup";
+ if (m_waveDecoder) {
+ m_waveDecoder->disconnect(this);
m_waveDecoder->deleteLater();
- if (m_stream)
+ }
+ if (m_stream) {
+ m_stream->disconnect(this);
m_stream->deleteLater();
+ }
m_waveDecoder = nullptr;
m_stream = nullptr;
@@ -346,14 +306,14 @@ void QSample::addRef()
// Called in loading thread
void QSample::readSample()
{
+#if QT_CONFIG(thread)
Q_ASSERT(QThread::currentThread()->objectName() == QLatin1String("QSampleCache::LoadingThread"));
- QMutexLocker m(&m_mutex);
-#ifdef QT_SAMPLECACHE_DEBUG
- qDebug() << "QSample: readSample";
#endif
+ QMutexLocker m(&m_mutex);
qint64 read = m_waveDecoder->read(m_soundData.data() + m_sampleReadLength,
qMin(m_waveDecoder->bytesAvailable(),
qint64(m_waveDecoder->size() - m_sampleReadLength)));
+ qCDebug(qLcSampleCache) << "QSample: readSample" << read;
if (read > 0)
m_sampleReadLength += read;
if (m_sampleReadLength < m_waveDecoder->size())
@@ -365,16 +325,17 @@ void QSample::readSample()
// Called in loading thread
void QSample::decoderReady()
{
+#if QT_CONFIG(thread)
Q_ASSERT(QThread::currentThread()->objectName() == QLatin1String("QSampleCache::LoadingThread"));
- QMutexLocker m(&m_mutex);
-#ifdef QT_SAMPLECACHE_DEBUG
- qDebug() << "QSample: decoder ready";
#endif
+ QMutexLocker m(&m_mutex);
+ qCDebug(qLcSampleCache) << "QSample: decoder ready";
m_parent->refresh(m_waveDecoder->size());
m_soundData.resize(m_waveDecoder->size());
m_sampleReadLength = 0;
qint64 read = m_waveDecoder->read(m_soundData.data(), m_waveDecoder->size());
+ qCDebug(qLcSampleCache) << " bytes read" << read;
if (read > 0)
m_sampleReadLength += read;
if (m_sampleReadLength >= m_waveDecoder->size())
@@ -392,26 +353,42 @@ QSample::State QSample::state() const
// Essentially a second ctor, doesn't need locks (?)
void QSample::load()
{
+#if QT_CONFIG(thread)
Q_ASSERT(QThread::currentThread()->objectName() == QLatin1String("QSampleCache::LoadingThread"));
-#ifdef QT_SAMPLECACHE_DEBUG
- qDebug() << "QSample: load [" << m_url << "]";
#endif
- m_stream = m_parent->networkAccessManager().get(QNetworkRequest(m_url));
- connect(m_stream, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), SLOT(decoderError()));
+ qCDebug(qLcSampleCache) << "QSample: load [" << m_url << "]";
+ QNetworkReply *reply = m_parent->networkAccessManager().get(QNetworkRequest(m_url));
+ m_stream = reply;
+ connect(reply, &QNetworkReply::errorOccurred, this, &QSample::loadingError);
m_waveDecoder = new QWaveDecoder(m_stream);
- connect(m_waveDecoder, SIGNAL(formatKnown()), SLOT(decoderReady()));
- connect(m_waveDecoder, SIGNAL(parsingError()), SLOT(decoderError()));
- connect(m_waveDecoder, SIGNAL(readyRead()), SLOT(readSample()));
+ connect(m_waveDecoder, &QWaveDecoder::formatKnown, this, &QSample::decoderReady);
+ connect(m_waveDecoder, &QWaveDecoder::parsingError, this, &QSample::decoderError);
+ connect(m_waveDecoder, &QIODevice::readyRead, this, &QSample::readSample);
+
+ m_waveDecoder->open(QIODevice::ReadOnly);
+}
+
+void QSample::loadingError(QNetworkReply::NetworkError errorCode)
+{
+#if QT_CONFIG(thread)
+ Q_ASSERT(QThread::currentThread()->objectName() == QLatin1String("QSampleCache::LoadingThread"));
+#endif
+ QMutexLocker m(&m_mutex);
+ qCDebug(qLcSampleCache) << "QSample: loading error" << errorCode;
+ cleanup();
+ m_state = QSample::Error;
+ qobject_cast<QSampleCache*>(m_parent)->loadingRelease();
+ emit error();
}
// Called in loading thread
void QSample::decoderError()
{
+#if QT_CONFIG(thread)
Q_ASSERT(QThread::currentThread()->objectName() == QLatin1String("QSampleCache::LoadingThread"));
- QMutexLocker m(&m_mutex);
-#ifdef QT_SAMPLECACHE_DEBUG
- qDebug() << "QSample: decoder error";
#endif
+ QMutexLocker m(&m_mutex);
+ qCDebug(qLcSampleCache) << "QSample: decoder error";
cleanup();
m_state = QSample::Error;
qobject_cast<QSampleCache*>(m_parent)->loadingRelease();
@@ -421,11 +398,11 @@ void QSample::decoderError()
// Called in loading thread from decoder when sample is done. Locked already.
void QSample::onReady()
{
+#if QT_CONFIG(thread)
Q_ASSERT(QThread::currentThread()->objectName() == QLatin1String("QSampleCache::LoadingThread"));
-#ifdef QT_SAMPLECACHE_DEBUG
- qDebug() << "QSample: load ready";
#endif
m_audioFormat = m_waveDecoder->audioFormat();
+ qCDebug(qLcSampleCache) << "QSample: load ready format:" << m_audioFormat;
cleanup();
m_state = QSample::Ready;
qobject_cast<QSampleCache*>(m_parent)->loadingRelease();
diff --git a/src/multimedia/audio/qsamplecache_p.h b/src/multimedia/audio/qsamplecache_p.h
index 4c2384743..3ba0c420c 100644
--- a/src/multimedia/audio/qsamplecache_p.h
+++ b/src/multimedia/audio/qsamplecache_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSAMPLECACHE_P_H
#define QSAMPLECACHE_P_H
@@ -58,7 +22,8 @@
#include <QtCore/qmap.h>
#include <QtCore/qset.h>
#include <qaudioformat.h>
-
+#include <qnetworkreply.h>
+#include <private/qglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -97,6 +62,7 @@ protected:
private Q_SLOTS:
void load();
+ void loadingError(QNetworkReply::NetworkError);
void decoderError();
void readSample();
void decoderReady();
@@ -136,9 +102,6 @@ public:
bool isLoading() const;
bool isCached(const QUrl& url) const;
-Q_SIGNALS:
- void isLoadingChanged();
-
private:
QMap<QUrl, QSample*> m_samples;
QSet<QSample*> m_staleSamples;
diff --git a/src/multimedia/audio/qsound.cpp b/src/multimedia/audio/qsound.cpp
deleted file mode 100644
index cbd336457..000000000
--- a/src/multimedia/audio/qsound.cpp
+++ /dev/null
@@ -1,244 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui module 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$
-**
-****************************************************************************/
-
-#include "qsound.h"
-#include "qsoundeffect.h"
-#include "qcoreapplication.h"
-
-
-/*!
- \class QSound
- \brief The QSound class provides a method to play .wav sound files.
- \inmodule QtMultimedia
- \ingroup multimedia
- \ingroup multimedia_audio
-
- Qt provides the most commonly required audio operation in GUI
- applications: asynchronously playing a sound file. This is most
- easily accomplished using the static play() function:
-
- \snippet multimedia-snippets/qsound.cpp 0
-
- Alternatively, create a QSound object from the sound file first
- and then call the play() slot:
-
- \snippet multimedia-snippets/qsound.cpp 1
-
- In both cases, the file may either be a local file or in a
- \l{The Qt Resource System}{resource}.
-
- Once created a QSound object can be queried for its fileName() and
- total number of loops() (i.e. the number of times the sound will
- play). The number of repetitions can be altered using the
- setLoops() function. While playing the sound, the loopsRemaining()
- function returns the remaining number of repetitions. Use the
- isFinished() function to determine whether the sound has finished
- playing.
-
- Sounds played using a QSound object may use more memory than the
- static play() function, but it may also play more immediately
- (depending on the underlying platform audio facilities).
-
- If you require finer control over playing sounds, consider the
- \l QSoundEffect or \l QAudioOutput classes.
-
- \sa QSoundEffect
-*/
-
-/*!
- \enum QSound::Loop
-
- \value Infinite Can be used as a parameter to \l setLoops() to loop infinitely.
-*/
-
-
-/*!
- Plays the sound stored in the file specified by the given \a filename.
-
- The file can either be a local file or in a \l{The Qt Resource System}{resource}.
-
- \sa stop(), loopsRemaining(), isFinished()
-*/
-void QSound::play(const QString &filename)
-{
- // Object destruction is generally handled via deleteOnComplete
- // Unexpected cases will be handled via parenting of QSound objects to qApp
- QSound *sound = new QSound(filename, qApp);
- sound->connect(sound->m_soundEffect, &QSoundEffect::playingChanged,
- sound, &QSound::deleteOnComplete);
- sound->play();
-}
-
-/*!
- Constructs a QSound object from the file specified by the given \a
- filename and with the given \a parent.
-
- The file can either be a local file or in a \l{The Qt Resource System}{resource}.
-
- \sa play()
-*/
-QSound::QSound(const QString &filename, QObject *parent)
- : QObject(parent)
-{
- m_soundEffect = new QSoundEffect(this);
- const bool isQrc = filename.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive);
- const QUrl url = isQrc ? QUrl(filename) : QUrl::fromLocalFile(filename);
- m_soundEffect->setSource(url);
-}
-
-/*!
- Destroys this sound object. If the sound is not finished playing,
- the stop() function is called before the sound object is
- destroyed.
-
- \sa stop(), isFinished()
-*/
-QSound::~QSound()
-{
- if (!isFinished())
- stop();
-}
-
-/*!
- Returns true if the sound has finished playing; otherwise returns false.
-*/
-bool QSound::isFinished() const
-{
- return !m_soundEffect->isPlaying();
-}
-
-/*!
- \overload
-
- Starts playing the sound specified by this QSound object.
-
- The function returns immediately. Depending on the platform audio
- facilities, other sounds may stop or be mixed with the new
- sound. The sound can be played again at any time, possibly mixing
- or replacing previous plays of the sound.
-
- \sa fileName()
-*/
-void QSound::play()
-{
- m_soundEffect->play();
-}
-
-/*!
- Returns the number of times the sound will play.
- Return value of \c QSound::Infinite indicates infinite number of loops
-
- \sa loopsRemaining(), setLoops()
-*/
-int QSound::loops() const
-{
- // retain old API value for infinite loops
- int loopCount = m_soundEffect->loopCount();
- if (loopCount == QSoundEffect::Infinite)
- loopCount = Infinite;
-
- return loopCount;
-}
-
-/*!
- Returns the remaining number of times the sound will loop (for all
- positive values this value decreases each time the sound is played).
- Return value of \c QSound::Infinite indicates infinite number of loops
-
- \sa loops(), isFinished()
-*/
-int QSound::loopsRemaining() const
-{
- // retain old API value for infinite loops
- int loopsRemaining = m_soundEffect->loopsRemaining();
- if (loopsRemaining == QSoundEffect::Infinite)
- loopsRemaining = Infinite;
-
- return loopsRemaining;
-}
-
-/*!
- \fn void QSound::setLoops(int number)
-
- Sets the sound to repeat the given \a number of times when it is
- played.
-
- Note that passing the value \c QSound::Infinite will cause the sound to loop
- indefinitely.
-
- \sa loops()
-*/
-void QSound::setLoops(int n)
-{
- if (n == Infinite)
- n = QSoundEffect::Infinite;
-
- m_soundEffect->setLoopCount(n);
-}
-
-/*!
- Returns the filename associated with this QSound object.
-
- \sa QSound()
-*/
-QString QSound::fileName() const
-{
- return m_soundEffect->source().toLocalFile();
-}
-
-/*!
- Stops the sound playing.
-
- \sa play()
-*/
-void QSound::stop()
-{
- m_soundEffect->stop();
-}
-
-/*!
- \internal
-*/
-void QSound::deleteOnComplete()
-{
- if (!m_soundEffect->isPlaying())
- deleteLater();
-}
-
-#include "moc_qsound.cpp"
diff --git a/src/multimedia/audio/qsound.h b/src/multimedia/audio/qsound.h
deleted file mode 100644
index 972847c37..000000000
--- a/src/multimedia/audio/qsound.h
+++ /dev/null
@@ -1,85 +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 QSOUND_H
-#define QSOUND_H
-
-#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtCore/qobject.h>
-
-QT_BEGIN_NAMESPACE
-
-class QSoundEffect;
-
-class Q_MULTIMEDIA_EXPORT QSound : public QObject
-{
- Q_OBJECT
-public:
- enum Loop
- {
- Infinite = -1
- };
-
- static void play(const QString &filename);
-
- explicit QSound(const QString &filename, QObject *parent = nullptr);
- ~QSound();
-
- int loops() const;
- int loopsRemaining() const;
- void setLoops(int);
- QString fileName() const;
-
- bool isFinished() const;
-
-public Q_SLOTS:
- void play();
- void stop();
-
-private Q_SLOTS:
- void deleteOnComplete();
-
-private:
- QSoundEffect *m_soundEffect = nullptr;
-};
-
-QT_END_NAMESPACE
-
-
-#endif // QSOUND_H
diff --git a/src/multimedia/audio/qsoundeffect.cpp b/src/multimedia/audio/qsoundeffect.cpp
index 09085dca9..c403648f9 100644
--- a/src/multimedia/audio/qsoundeffect.cpp
+++ b/src/multimedia/audio/qsoundeffect.cpp
@@ -1,53 +1,265 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtMultimedia/private/qtmultimediaglobal_p.h>
#include "qsoundeffect.h"
-
-#if QT_CONFIG(pulseaudio)
-#include "qsoundeffect_pulse_p.h"
-#else
-#include "qsoundeffect_qaudio_p.h"
-#endif
+#include "qsamplecache_p.h"
+#include "qaudiodevice.h"
+#include "qaudiosink.h"
+#include "qmediadevices.h"
+#include "qaudiobuffer.h"
+#include <QtCore/qloggingcategory.h>
+#include <private/qplatformmediadevices_p.h>
+#include <private/qplatformmediaintegration_p.h>
+#include <private/qplatformaudioresampler_p.h>
+
+Q_STATIC_LOGGING_CATEGORY(qLcSoundEffect, "qt.multimedia.soundeffect")
QT_BEGIN_NAMESPACE
+Q_GLOBAL_STATIC(QSampleCache, sampleCache)
+
+namespace
+{
+struct AudioSinkDeleter
+{
+ void operator ()(QAudioSink* sink) const
+ {
+ sink->stop();
+ // Investigate:should we just delete?
+ sink->deleteLater();
+ }
+};
+
+struct SampleDeleter
+{
+ void operator ()(QSample* sample) const
+ {
+ sample->release();
+ }
+};
+}
+
+class QSoundEffectPrivate : public QIODevice
+{
+public:
+ QSoundEffectPrivate(QSoundEffect *q, const QAudioDevice &audioDevice = QAudioDevice());
+ ~QSoundEffectPrivate() override = default;
+
+ qint64 readData(char *data, qint64 len) override;
+ qint64 writeData(const char *data, qint64 len) override;
+ qint64 size() const override {
+ if (m_sample->state() != QSample::Ready)
+ return 0;
+ return m_loopCount == QSoundEffect::Infinite ? 0 : m_loopCount * m_audioBuffer.byteCount();
+ }
+ qint64 bytesAvailable() const override {
+ if (m_sample->state() != QSample::Ready)
+ return 0;
+ if (m_loopCount == QSoundEffect::Infinite)
+ return std::numeric_limits<qint64>::max();
+ return m_runningCount * m_audioBuffer.byteCount() - m_offset;
+ }
+ bool isSequential() const override {
+ return m_loopCount == QSoundEffect::Infinite;
+ }
+ bool atEnd() const override {
+ return m_runningCount == 0;
+ }
+
+ 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;
+ std::unique_ptr<QAudioSink, AudioSinkDeleter> m_audioSink;
+ std::unique_ptr<QSample, SampleDeleter> m_sample;
+ QAudioBuffer m_audioBuffer;
+ bool m_muted = false;
+ float m_volume = 1.0;
+ bool m_sampleReady = false;
+ qint64 m_offset = 0;
+ QAudioDevice m_audioDevice;
+};
+
+QSoundEffectPrivate::QSoundEffectPrivate(QSoundEffect *q, const QAudioDevice &audioDevice)
+ : QIODevice(q)
+ , q_ptr(q)
+ , m_audioDevice(audioDevice)
+{
+ open(QIODevice::ReadOnly);
+
+ QPlatformMediaIntegration::instance()->mediaDevices()->prepareAudio();
+}
+
+void QSoundEffectPrivate::sampleReady()
+{
+ if (m_status == QSoundEffect::Error)
+ return;
+
+ qCDebug(qLcSoundEffect) << this << "sampleReady: sample size:" << m_sample->data().size();
+ disconnect(m_sample.get(), &QSample::error, this, &QSoundEffectPrivate::decoderError);
+ disconnect(m_sample.get(), &QSample::ready, this, &QSoundEffectPrivate::sampleReady);
+ if (!m_audioSink) {
+ const auto audioDevice =
+ m_audioDevice.isNull() ? QMediaDevices::defaultAudioOutput() : m_audioDevice;
+
+ if (audioDevice.isNull()) {
+ // We are likely on a virtual machine, for example in CI
+ qCCritical(qLcSoundEffect) << "Failed to play sound. No audio devices present.";
+ setStatus(QSoundEffect::Error);
+ return;
+ }
+
+ const auto &sampleFormat = m_sample->format();
+ const auto sampleChannelConfig =
+ sampleFormat.channelConfig() == QAudioFormat::ChannelConfigUnknown
+ ? QAudioFormat::defaultChannelConfigForChannelCount(sampleFormat.channelCount())
+ : sampleFormat.channelConfig();
+
+ if (sampleChannelConfig != audioDevice.channelConfiguration()
+ && audioDevice.channelConfiguration() != QAudioFormat::ChannelConfigUnknown) {
+ qCDebug(qLcSoundEffect) << "Create resampler for channels mapping: config"
+ << sampleFormat.channelConfig() << "=> config"
+ << audioDevice.channelConfiguration();
+ auto outputFormat = sampleFormat;
+ outputFormat.setChannelConfig(audioDevice.channelConfiguration());
+
+ const auto resampler = QPlatformMediaIntegration::instance()->createAudioResampler(
+ m_sample->format(), outputFormat);
+ if (resampler)
+ m_audioBuffer = resampler.value()->resample(m_sample->data().constData(),
+ m_sample->data().size());
+ else
+ qCDebug(qLcSoundEffect) << "Cannot create resampler for channels mapping";
+ }
+
+ if (!m_audioBuffer.isValid())
+ m_audioBuffer = QAudioBuffer(m_sample->data(), m_sample->format());
+
+ m_audioSink.reset(new QAudioSink(audioDevice, m_audioBuffer.format()));
+
+ connect(m_audioSink.get(), &QAudioSink::stateChanged, this, &QSoundEffectPrivate::stateChanged);
+ if (!m_muted)
+ m_audioSink->setVolume(m_volume);
+ else
+ m_audioSink->setVolume(0);
+ }
+ m_sampleReady = true;
+ setStatus(QSoundEffect::Ready);
+
+ if (m_playing && m_audioSink->state() == QAudio::StoppedState) {
+ qCDebug(qLcSoundEffect) << this << "starting playback on audiooutput";
+ m_audioSink->start(this);
+ }
+}
+
+void QSoundEffectPrivate::decoderError()
+{
+ qWarning("QSoundEffect(qaudio): Error decoding source %ls", qUtf16Printable(m_url.toString()));
+ disconnect(m_sample.get(), &QSample::ready, this, &QSoundEffectPrivate::sampleReady);
+ disconnect(m_sample.get(), &QSample::error, this, &QSoundEffectPrivate::decoderError);
+ m_playing = false;
+ setStatus(QSoundEffect::Error);
+}
+
+void QSoundEffectPrivate::stateChanged(QAudio::State state)
+{
+ qCDebug(qLcSoundEffect) << this << "stateChanged " << state;
+ if ((state == QAudio::IdleState && m_runningCount == 0) || state == QAudio::StoppedState)
+ q_ptr->stop();
+}
+
+qint64 QSoundEffectPrivate::readData(char *data, qint64 len)
+{
+ qCDebug(qLcSoundEffect) << this << "readData" << len << m_runningCount;
+ if (!len)
+ return 0;
+ if (m_sample->state() != QSample::Ready)
+ return 0;
+ if (m_runningCount == 0 || !m_playing)
+ return 0;
+
+ qint64 bytesWritten = 0;
+
+ const int sampleSize = m_audioBuffer.byteCount();
+ const char *sampleData = m_audioBuffer.constData<char>();
+
+ while (len && m_runningCount) {
+ int toWrite = qMin(sampleSize - m_offset, len);
+ memcpy(data, sampleData + m_offset, toWrite);
+ bytesWritten += toWrite;
+ data += toWrite;
+ len -= toWrite;
+ m_offset += toWrite;
+ if (m_offset >= sampleSize) {
+ if (m_runningCount > 0 && m_runningCount != QSoundEffect::Infinite)
+ setLoopsRemaining(m_runningCount - 1);
+ m_offset = 0;
+ }
+ }
+
+ return bytesWritten;
+}
+
+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;
+ qCDebug(qLcSoundEffect) << this << "setLoopsRemaining " << loopsRemaining;
+ m_runningCount = loopsRemaining;
+ emit q_ptr->loopsRemainingChanged();
+}
+
+void QSoundEffectPrivate::setStatus(QSoundEffect::Status status)
+{
+ qCDebug(qLcSoundEffect) << this << "setStatus" << status;
+ 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)
+{
+ qCDebug(qLcSoundEffect) << this << "setPlaying(" << playing << ")" << m_playing;
+ if (m_audioSink) {
+ m_audioSink->stop();
+ if (playing && !m_sampleReady)
+ return;
+ }
+
+ if (m_playing == playing)
+ return;
+ m_playing = playing;
+
+ if (m_audioSink && playing)
+ m_audioSink->start(this);
+
+ emit q_ptr->playingChanged();
+}
+
/*!
\class QSoundEffect
\brief The QSoundEffect class provides a way to play low latency sound effects.
@@ -94,8 +306,8 @@ QT_BEGIN_NAMESPACE
a generally lower latency way, and is suitable for "feedback" type sounds in
response to user actions (e.g. virtual keyboard sounds, positive or negative
feedback for popup dialogs, or game sounds). If low latency is not important,
- consider using the MediaPlayer or Audio types instead, since they support a wider
- variety of media formats and are less resource intensive.
+ consider using the MediaPlayer type instead, since it support a wider
+ variety of media formats and is less resource intensive.
Typically the sound effect should be reused, which allows all the
parsing and preparation to be done ahead of time, and only triggered
@@ -111,33 +323,20 @@ 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(QAudioDevice(), parent)
{
}
/*!
Creates a QSoundEffect with the given \a audioDevice and \a parent.
*/
-QSoundEffect::QSoundEffect(const QAudioDeviceInfo &audioDevice, QObject *parent)
+QSoundEffect::QSoundEffect(const QAudioDevice &audioDevice, QObject *parent)
: QObject(parent)
- , d(initPrivate(this, new QSoundEffectPrivate(audioDevice, this)))
+ , d(new QSoundEffectPrivate(this, audioDevice))
{
}
@@ -146,7 +345,10 @@ QSoundEffect::QSoundEffect(const QAudioDeviceInfo &audioDevice, QObject *parent)
*/
QSoundEffect::~QSoundEffect()
{
- d->release();
+ stop();
+ d->m_audioSink.reset();
+ d->m_sample.reset();
+ delete d;
}
/*!
@@ -156,7 +358,15 @@ QSoundEffect::~QSoundEffect()
*/
QStringList QSoundEffect::supportedMimeTypes()
{
- return QSoundEffectPrivate::supportedMimeTypes();
+ // Only return supported mime types if we have a audio device available
+ const QList<QAudioDevice> devices = QMediaDevices::audioOutputs();
+ if (devices.isEmpty())
+ return QStringList();
+
+ return QStringList() << QLatin1String("audio/x-wav")
+ << QLatin1String("audio/wav")
+ << QLatin1String("audio/wave")
+ << QLatin1String("audio/x-pn-wav");
}
/*!
@@ -178,16 +388,62 @@ 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)
+ qCDebug(qLcSoundEffect) << this << "setSource current=" << d->m_url << ", to=" << url;
+ if (d->m_url == url)
return;
- d->setSource(url);
+ 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;
+ }
+
+ if (d->m_sample) {
+ if (!d->m_sampleReady) {
+ disconnect(d->m_sample.get(), &QSample::error, d, &QSoundEffectPrivate::decoderError);
+ disconnect(d->m_sample.get(), &QSample::ready, d, &QSoundEffectPrivate::sampleReady);
+ }
+ d->m_sample.reset();
+ }
+
+ if (d->m_audioSink) {
+ disconnect(d->m_audioSink.get(), &QAudioSink::stateChanged, d, &QSoundEffectPrivate::stateChanged);
+ d->m_audioSink.reset();
+ }
+
+ d->setStatus(QSoundEffect::Loading);
+ d->m_sample.reset(sampleCache()->requestSample(url));
+ connect(d->m_sample.get(), &QSample::error, d, &QSoundEffectPrivate::decoderError);
+ connect(d->m_sample.get(), &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();
}
@@ -218,7 +474,7 @@ void QSoundEffect::setSource(const QUrl &url)
*/
int QSoundEffect::loopCount() const
{
- return d->loopCount();
+ return d->m_loopCount;
}
/*!
@@ -245,14 +501,35 @@ 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();
}
/*!
+ \property QSoundEffect::audioDevice
+
+ Returns the QAudioDevice instance.
+*/
+QAudioDevice QSoundEffect::audioDevice()
+{
+ return d->m_audioDevice;
+}
+
+void QSoundEffect::setAudioDevice(const QAudioDevice &device)
+{
+ if (d->m_audioDevice == device)
+ return;
+ // ### recreate the QAudioSink if needed
+ d->m_audioDevice = device;
+ emit audioDeviceChanged();
+}
+
+/*!
\qmlproperty int QtMultimedia::SoundEffect::loopsRemaining
This property contains the number of loops remaining before the sound effect
@@ -266,7 +543,7 @@ void QSoundEffect::setLoopCount(int loopCount)
*/
int QSoundEffect::loopsRemaining() const
{
- return d->loopsRemaining();
+ return d->m_runningCount;
}
@@ -280,9 +557,9 @@ int QSoundEffect::loopsRemaining() const
The default volume is \c 1.0.
- UI volume controls should usually be scaled nonlinearly. For example, using a logarithmic scale
+ UI volume controls should usually be scaled non-linearly. For example, using a logarithmic scale
will produce linear changes in perceived loudness, which is what a user would normally expect
- from a volume control. See \l {QtMultimedia::QtMultimedia::convertVolume()}{QtMultimedia.convertVolume()}
+ from a volume control. See \l {QtAudio::convertVolume()}{convertVolume()}
for more details.
*/
/*!
@@ -294,9 +571,12 @@ int QSoundEffect::loopsRemaining() const
/*!
Returns the current volume of this sound effect, from 0.0 (silent) to 1.0 (maximum volume).
*/
-qreal QSoundEffect::volume() const
+float QSoundEffect::volume() const
{
- return d->volume();
+ if (d->m_audioSink && !d->m_muted)
+ return d->m_audioSink->volume();
+
+ return d->m_volume;
}
/*!
@@ -307,17 +587,22 @@ qreal QSoundEffect::volume() const
The default volume is \c 1.0.
- UI volume controls should usually be scaled nonlinearly. For example, using a logarithmic scale
+ UI volume controls should usually be scaled non-linearly. For example, using a logarithmic scale
will produce linear changes in perceived loudness, which is what a user would normally expect
- from a volume control. See QAudio::convertVolume() for more details.
+ from a volume control. See QtAudio::convertVolume() for more details.
*/
-void QSoundEffect::setVolume(qreal volume)
+void QSoundEffect::setVolume(float volume)
{
- volume = qBound(qreal(0.0), volume, qreal(1.0));
- if (qFuzzyCompare(d->volume(), volume))
+ volume = qBound(0.0f, volume, 1.0f);
+ if (d->m_volume == volume)
return;
- d->setVolume(volume);
+ d->m_volume = volume;
+
+ if (d->m_audioSink && !d->m_muted)
+ d->m_audioSink->setVolume(volume);
+
+ emit volumeChanged();
}
/*!
@@ -335,7 +620,7 @@ void QSoundEffect::setVolume(qreal volume)
/*! Returns whether this sound effect is muted */
bool QSoundEffect::isMuted() const
{
- return d->isMuted();
+ return d->m_muted;
}
/*!
@@ -347,10 +632,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_audioSink)
+ d->m_audioSink->setVolume(0);
+ else if (!muted && d->m_audioSink && d->m_muted)
+ d->m_audioSink->setVolume(d->m_volume);
+
+ d->m_muted = muted;
+ emit mutedChanged();
}
/*!
@@ -365,7 +656,7 @@ void QSoundEffect::setMuted(bool muted)
*/
bool QSoundEffect::isLoaded() const
{
- return d->isLoaded();
+ return d->m_status == QSoundEffect::Ready;
}
/*!
@@ -386,7 +677,14 @@ bool QSoundEffect::isLoaded() const
*/
void QSoundEffect::play()
{
- d->play();
+ d->m_offset = 0;
+ d->setLoopsRemaining(d->m_loopCount);
+ qCDebug(qLcSoundEffect) << this << "play" << d->m_loopCount << d->m_runningCount;
+ if (d->m_status == QSoundEffect::Null || d->m_status == QSoundEffect::Error) {
+ d->setStatus(QSoundEffect::Null);
+ return;
+ }
+ d->setPlaying(true);
}
/*!
@@ -403,7 +701,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;
}
/*!
@@ -443,72 +741,9 @@ bool QSoundEffect::isPlaying() const
*/
QSoundEffect::Status QSoundEffect::status() const
{
- return d->status();
-}
-
-/*!
- \qmlproperty string QtMultimedia::SoundEffect::category
-
- This property contains the \e category of this sound effect.
-
- Some platforms can perform different audio routing
- for different categories, or may allow the user to
- set different volume levels for different categories.
-
- This setting will be ignored on platforms that do not
- support audio categories.
-*/
-/*!
- \property QSoundEffect::category
-
- This property contains the \e category of this sound effect.
-
- Some platforms can perform different audio routing
- for different categories, or may allow the user to
- set different volume levels for different categories.
-
- This setting will be ignored on platforms that do not
- support audio categories.
-*/
-/*!
- Returns the current \e category for this sound effect.
-
- Some platforms can perform different audio routing
- for different categories, or may allow the user to
- set different volume levels for different categories.
-
- This setting will be ignored on platforms that do not
- support audio categories.
-
- \sa setCategory()
-*/
-QString QSoundEffect::category() const
-{
- return d->category();
-}
-
-/*!
- Sets the \e category of this sound effect to \a category.
-
- Some platforms can perform different audio routing
- for different categories, or may allow the user to
- set different volume levels for different categories.
-
- This setting will be ignored on platforms that do not
- support audio categories.
-
- If this setting is changed while a sound effect is playing
- it will only take effect when the sound effect has stopped
- playing.
-
- \sa category()
- */
-void QSoundEffect::setCategory(const QString &category)
-{
- d->setCategory(category);
+ return d->m_status;
}
-
/*!
\qmlmethod QtMultimedia::SoundEffect::stop()
@@ -523,7 +758,12 @@ void QSoundEffect::setCategory(const QString &category)
*/
void QSoundEffect::stop()
{
- d->stop();
+ if (!d->m_playing)
+ return;
+ qCDebug(qLcSoundEffect) << "stop()";
+ d->m_offset = 0;
+
+ d->setPlaying(false);
}
/* Signals */
@@ -537,8 +777,6 @@ void QSoundEffect::stop()
\qmlsignal QtMultimedia::SoundEffect::sourceChanged()
The \c sourceChanged signal is emitted when the source has been changed.
-
- The corresponding handler is \c onSourceChanged.
*/
/*!
\fn void QSoundEffect::loadedChanged()
@@ -549,8 +787,6 @@ void QSoundEffect::stop()
\qmlsignal QtMultimedia::SoundEffect::loadedChanged()
The \c loadedChanged signal is emitted when the loading state has changed.
-
- The corresponding handler is \c onLoadedChanged.
*/
/*!
@@ -562,8 +798,6 @@ void QSoundEffect::stop()
\qmlsignal QtMultimedia::SoundEffect::loopCountChanged()
The \c loopCountChanged signal is emitted when the initial number of loops has changed.
-
- The corresponding handler is \c onLoopCountChanged.
*/
/*!
@@ -575,8 +809,6 @@ void QSoundEffect::stop()
\qmlsignal QtMultimedia::SoundEffect::loopsRemainingChanged()
The \c loopsRemainingChanged signal is emitted when the remaining number of loops has changed.
-
- The corresponding handler is \c onLoopsRemainingChanged.
*/
/*!
@@ -588,8 +820,6 @@ void QSoundEffect::stop()
\qmlsignal QtMultimedia::SoundEffect::volumeChanged()
The \c volumeChanged signal is emitted when the volume has changed.
-
- The corresponding handler is \c onVolumeChanged.
*/
/*!
@@ -601,8 +831,6 @@ void QSoundEffect::stop()
\qmlsignal QtMultimedia::SoundEffect::mutedChanged()
The \c mutedChanged signal is emitted when the mute state has changed.
-
- The corresponding handler is \c onMutedChanged.
*/
/*!
@@ -614,8 +842,6 @@ void QSoundEffect::stop()
\qmlsignal QtMultimedia::SoundEffect::playingChanged()
The \c playingChanged signal is emitted when the playing property has changed.
-
- The corresponding handler is \c onPlayingChanged.
*/
/*!
@@ -627,24 +853,8 @@ void QSoundEffect::stop()
\qmlsignal QtMultimedia::SoundEffect::statusChanged()
The \c statusChanged signal is emitted when the status property has changed.
-
- The corresponding handler is \c onStatusChanged.
*/
-/*!
- \fn void QSoundEffect::categoryChanged()
-
- The \c categoryChanged signal is emitted when the category property has changed.
-*/
-/*!
- \qmlsignal QtMultimedia::SoundEffect::categoryChanged()
-
- The \c categoryChanged signal is emitted when the category property has changed.
-
- The corresponding handler is \c onCategoryChanged.
-*/
-
-
QT_END_NAMESPACE
#include "moc_qsoundeffect.cpp"
diff --git a/src/multimedia/audio/qsoundeffect.h b/src/multimedia/audio/qsoundeffect.h
index 1185afe07..eeafc4c9f 100644
--- a/src/multimedia/audio/qsoundeffect.h
+++ b/src/multimedia/audio/qsoundeffect.h
@@ -1,46 +1,11 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSOUNDEFFECT_H
#define QSOUNDEFFECT_H
#include <QtMultimedia/qtmultimediaglobal.h>
+#include <QtMultimedia/qaudio.h>
#include <QtCore/qobject.h>
#include <QtCore/qurl.h>
#include <QtCore/qstringlist.h>
@@ -50,7 +15,7 @@ QT_BEGIN_NAMESPACE
class QSoundEffectPrivate;
-class QAudioDeviceInfo;
+class QAudioDevice;
class Q_MULTIMEDIA_EXPORT QSoundEffect : public QObject
{
@@ -59,19 +24,18 @@ class Q_MULTIMEDIA_EXPORT QSoundEffect : public QObject
Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(int loops READ loopCount WRITE setLoopCount NOTIFY loopCountChanged)
Q_PROPERTY(int loopsRemaining READ loopsRemaining NOTIFY loopsRemainingChanged)
- Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged)
+ Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged)
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
Q_PROPERTY(bool playing READ isPlaying NOTIFY playingChanged)
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
- Q_PROPERTY(QString category READ category WRITE setCategory NOTIFY categoryChanged)
- Q_ENUMS(Loop)
- Q_ENUMS(Status)
+ Q_PROPERTY(QAudioDevice audioDevice READ audioDevice WRITE setAudioDevice NOTIFY audioDeviceChanged)
public:
enum Loop
{
Infinite = -2
};
+ Q_ENUM(Loop)
enum Status
{
@@ -80,9 +44,10 @@ public:
Ready,
Error
};
+ Q_ENUM(Status)
explicit QSoundEffect(QObject *parent = nullptr);
- explicit QSoundEffect(const QAudioDeviceInfo &audioDevice, QObject *parent = nullptr);
+ explicit QSoundEffect(const QAudioDevice &audioDevice, QObject *parent = nullptr);
~QSoundEffect();
static QStringList supportedMimeTypes();
@@ -94,8 +59,11 @@ public:
int loopsRemaining() const;
void setLoopCount(int loopCount);
- qreal volume() const;
- void setVolume(qreal volume);
+ QAudioDevice audioDevice();
+ void setAudioDevice(const QAudioDevice &device);
+
+ float volume() const;
+ void setVolume(float volume);
bool isMuted() const;
void setMuted(bool muted);
@@ -105,9 +73,6 @@ public:
bool isPlaying() const;
Status status() const;
- QString category() const;
- void setCategory(const QString &category);
-
Q_SIGNALS:
void sourceChanged();
void loopCountChanged();
@@ -117,7 +82,7 @@ Q_SIGNALS:
void loadedChanged();
void playingChanged();
void statusChanged();
- void categoryChanged();
+ void audioDeviceChanged();
public Q_SLOTS:
void play();
diff --git a/src/multimedia/audio/qsoundeffect_pulse_p.cpp b/src/multimedia/audio/qsoundeffect_pulse_p.cpp
deleted file mode 100644
index 51cdde026..000000000
--- a/src/multimedia/audio/qsoundeffect_pulse_p.cpp
+++ /dev/null
@@ -1,1214 +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 <QtCore/qcoreapplication.h>
-#include <qaudioformat.h>
-#include <QTime>
-#include <QTimer>
-
-#include "qsoundeffect_pulse_p.h"
-
-#include <private/qaudiohelpers_p.h>
-#include <private/qmediaresourcepolicy_p.h>
-#include <private/qmediaresourceset_p.h>
-#include <QAudioDeviceInfo>
-#include <unistd.h>
-
-//#define QT_PA_DEBUG
-
-QT_BEGIN_NAMESPACE
-
-namespace
-{
-inline pa_sample_spec audioFormatToSampleSpec(const QAudioFormat &format)
-{
- pa_sample_spec spec;
-
- spec.rate = format.sampleRate();
- spec.channels = format.channelCount();
- spec.format = PA_SAMPLE_INVALID;
- const bool isBigEndian = (format.byteOrder() == QAudioFormat::BigEndian);
-
- if (format.sampleType() == QAudioFormat::UnSignedInt) {
- if (format.sampleSize() == 8)
- spec.format = PA_SAMPLE_U8;
- } else if (format.sampleType() == QAudioFormat::SignedInt) {
- if (format.sampleSize() == 16) {
- spec.format = isBigEndian ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
- } else if (format.sampleSize() == 24) {
- spec.format = isBigEndian ? PA_SAMPLE_S24BE : PA_SAMPLE_S24LE;
- } else if (format.sampleSize() == 32) {
- spec.format = isBigEndian ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
- }
- } else if (format.sampleType() == QAudioFormat::Float) {
- if (format.sampleSize() == 32)
- spec.format = isBigEndian ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE;
- }
-
- return spec;
-}
-
-class PulseDaemon : public QObject
-{
- Q_OBJECT
-public:
- PulseDaemon()
- {
- prepare();
- }
-
- ~PulseDaemon()
- {
- release();
- }
-
- inline void ref()
- {
- m_ref.ref();
- prepare();
- }
-
- inline void deref()
- {
- if (!m_ref.deref())
- release();
- }
-
- inline void lock()
- {
- if (m_mainLoop) {
- if (++m_lockCount == 1)
- pa_threaded_mainloop_lock(m_mainLoop);
- }
- }
-
- inline void unlock()
- {
- if (m_mainLoop) {
- if (--m_lockCount == 0)
- pa_threaded_mainloop_unlock(m_mainLoop);
- }
- }
-
- inline pa_context *context() const
- {
- return m_context;
- }
-
-Q_SIGNALS:
- void contextReady();
- void contextFailed();
-
-private Q_SLOTS:
- void onContextFailed()
- {
- release();
-
- // Try to reconnect later
- QTimer::singleShot(30000, this, &PulseDaemon::prepare);
-
- emit contextFailed();
- }
-
- void prepare()
- {
- if (m_prepared)
- return;
-
- m_context = nullptr;
- m_mainLoop = pa_threaded_mainloop_new();
- if (m_mainLoop == nullptr) {
- qWarning("PulseAudioService: unable to create pulseaudio mainloop");
- return;
- }
-
- if (pa_threaded_mainloop_start(m_mainLoop) != 0) {
- qWarning("PulseAudioService: unable to start pulseaudio mainloop");
- pa_threaded_mainloop_free(m_mainLoop);
- return;
- }
-
- m_mainLoopApi = pa_threaded_mainloop_get_api(m_mainLoop);
-
- lock();
- m_context = pa_context_new(m_mainLoopApi, QString(QLatin1String("QtPulseAudio:%1")).arg(::getpid()).toLatin1().constData());
-
- if (m_context == nullptr) {
- qWarning("PulseAudioService: Unable to create new pulseaudio context");
- unlock();
- pa_threaded_mainloop_free(m_mainLoop);
- m_mainLoop = nullptr;
- onContextFailed();
- return;
- }
-
- pa_context_set_state_callback(m_context, context_state_callback, this);
-
- const QByteArray srvStrEnv = qgetenv("QT_PULSE_SERVER_STRING");
- const char *srvStr = srvStrEnv.isNull() ? nullptr : srvStrEnv.constData();
- pa_context_flags_t flags = qEnvironmentVariableIsSet("QT_PULSE_NOAUTOSPAWN") ? PA_CONTEXT_NOAUTOSPAWN : (pa_context_flags_t)0;
-
- if (pa_context_connect(m_context, srvStr, flags, nullptr) < 0) {
- qWarning("PulseAudioService: pa_context_connect() failed");
- pa_context_unref(m_context);
- unlock();
- pa_threaded_mainloop_free(m_mainLoop);
- m_mainLoop = nullptr;
- m_context = nullptr;
- return;
- }
- unlock();
-
- m_prepared = true;
- }
-
-private:
- void release()
- {
- if (!m_prepared)
- return;
-
- if (m_context) {
- lock();
- pa_context_disconnect(m_context);
- unlock();
- }
-
- if (m_mainLoop) {
- pa_threaded_mainloop_stop(m_mainLoop);
- pa_threaded_mainloop_free(m_mainLoop);
- m_mainLoop = nullptr;
- }
-
- if (m_context) {
- pa_context_unref(m_context);
- m_context = nullptr;
- }
-
- m_prepared = false;
- }
-
- static void context_state_callback(pa_context *c, void *userdata)
- {
- PulseDaemon *self = reinterpret_cast<PulseDaemon*>(userdata);
- switch (pa_context_get_state(c)) {
- case PA_CONTEXT_CONNECTING:
- case PA_CONTEXT_AUTHORIZING:
- case PA_CONTEXT_SETTING_NAME:
- break;
- case PA_CONTEXT_READY:
- QMetaObject::invokeMethod(self, "contextReady", Qt::QueuedConnection);
- break;
- case PA_CONTEXT_FAILED:
- QMetaObject::invokeMethod(self, "onContextFailed", Qt::QueuedConnection);
- break;
- default:
- break;
- }
- }
-
- bool m_prepared = false;
- pa_context *m_context = nullptr;
- pa_threaded_mainloop *m_mainLoop = nullptr;
- pa_mainloop_api *m_mainLoopApi = nullptr;
- uint m_lockCount = 0;
- QAtomicInt m_ref;
-};
-
-}
-
-Q_GLOBAL_STATIC(PulseDaemon, pulseDaemon)
-Q_GLOBAL_STATIC(QSampleCache, sampleCache)
-
-namespace
-{
-class PulseDaemonLocker
-{
-public:
- PulseDaemonLocker()
- {
- pulseDaemon()->lock();
- }
-
- ~PulseDaemonLocker()
- {
- pulseDaemon()->unlock();
- }
-};
-}
-
-class QSoundEffectRef
-{
-public:
- QSoundEffectRef(QSoundEffectPrivate *target)
- : m_target(target)
- {
-#ifdef QT_PA_DEBUG
- qDebug() << "QSoundEffectRef(" << this << ") ctor";
-#endif
- }
-
- QSoundEffectRef *getRef()
- {
-#ifdef QT_PA_DEBUG
- qDebug() << "QSoundEffectRef(" << this << ") getRef";
-#endif
- QMutexLocker locker(&m_mutex);
- m_ref++;
- return this;
- }
-
- void release()
- {
-#ifdef QT_PA_DEBUG
- qDebug() << "QSoundEffectRef(" << this << ") Release";
-#endif
- m_mutex.lock();
- --m_ref;
- if (m_ref == 0) {
- m_mutex.unlock();
-#ifdef QT_PA_DEBUG
- qDebug() << "QSoundEffectRef(" << this << ") deleted";
-#endif
- delete this;
- return;
- }
- m_mutex.unlock();
- }
-
- QSoundEffectPrivate* soundEffect() const
- {
- QMutexLocker locker(&m_mutex);
- return m_target;
- }
-
- void notifyDeleted()
- {
-#ifdef QT_PA_DEBUG
- qDebug() << "QSoundEffectRef(" << this << ") notifyDeleted";
-#endif
- QMutexLocker locker(&m_mutex);
- m_target = nullptr;
- }
-
-private:
- int m_ref = 1;
- mutable QMutex m_mutex;
- QSoundEffectPrivate *m_target = nullptr;
-};
-
-QSoundEffectPrivate::QSoundEffectPrivate(QObject* parent):
- QObject(parent)
-{
- pulseDaemon()->ref();
-
- m_ref = new QSoundEffectRef(this);
- if (pulseDaemon()->context())
- pa_sample_spec_init(&m_pulseSpec);
-
- m_resources = QMediaResourcePolicy::createResourceSet<QMediaPlayerResourceSetInterface>();
- Q_ASSERT(m_resources);
- m_resourcesAvailable = m_resources->isAvailable();
- connect(m_resources, &QMediaPlayerResourceSetInterface::availabilityChanged,
- this, &QSoundEffectPrivate::handleAvailabilityChanged);
-}
-
-QSoundEffectPrivate::QSoundEffectPrivate(const QAudioDeviceInfo &audioDevice, QObject *parent)
- : QSoundEffectPrivate(parent)
-{
- m_sinkName = audioDevice.deviceName();
-}
-
-void QSoundEffectPrivate::handleAvailabilityChanged(bool available)
-{
- m_resourcesAvailable = available;
-#ifdef DEBUG_RESOURCE
- qDebug() << Q_FUNC_INFO << "Resource availability changed " << m_resourcesAvailable;
-#endif
- if (!m_resourcesAvailable)
- stop();
-}
-
-void QSoundEffectPrivate::release()
-{
-#ifdef QT_PA_DEBUG
- qDebug() << this << "release";
-#endif
- m_ref->notifyDeleted();
- unloadPulseStream();
- if (m_sample) {
- m_sample->release();
- m_sample = nullptr;
- }
-
- this->deleteLater();
-}
-
-QString QSoundEffectPrivate::category() const
-{
- return m_category;
-}
-
-void QSoundEffectPrivate::setCategory(const QString &category)
-{
- if (m_category != category) {
- m_category = category;
-
- PulseDaemonLocker locker;
-
- if (m_playing || m_playQueued) {
- // Currently playing, we need to disconnect when
- // playback stops
- m_reloadCategory = true;
- } else if (m_pulseStream) {
- // We have to disconnect and reconnect
- unloadPulseStream();
- createPulseStream();
- } else {
- // Well, next time we create the pulse stream
- // it should be set
- }
-
- emit categoryChanged();
- }
-}
-
-QSoundEffectPrivate::~QSoundEffectPrivate()
-{
- QMediaResourcePolicy::destroyResourceSet(m_resources);
- m_resources = nullptr;
- m_ref->release();
-
- pulseDaemon()->deref();
-}
-
-QStringList QSoundEffectPrivate::supportedMimeTypes()
-{
- QStringList supportedTypes;
- supportedTypes << QLatin1String("audio/x-wav") << QLatin1String("audio/vnd.wave") ;
- return supportedTypes;
-}
-
-QUrl QSoundEffectPrivate::source() const
-{
- return m_source;
-}
-
-void QSoundEffectPrivate::setSource(const QUrl &url)
-{
- Q_ASSERT(m_source != url);
-#ifdef QT_PA_DEBUG
- qDebug() << this << "setSource =" << url;
-#endif
-
- PulseDaemonLocker locker;
-
- // 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, &QSample::error, this, &QSoundEffectPrivate::decoderError);
- disconnect(m_sample, &QSample::ready, this, &QSoundEffectPrivate::sampleReady);
- }
- m_sample->release();
- m_sample = nullptr;
- }
-
- m_source = url;
- m_sampleReady = false;
-
- setLoopsRemaining(0);
- if (m_pulseStream && !pa_stream_is_corked(m_pulseStream)) {
- pa_stream_set_write_callback(m_pulseStream, nullptr, nullptr);
- pa_stream_set_underflow_callback(m_pulseStream, nullptr, nullptr);
- pa_operation *op = pa_stream_cork(m_pulseStream, 1, nullptr, nullptr);
- if (op)
- pa_operation_unref(op);
- else
- qWarning("QSoundEffect(pulseaudio): failed to cork stream");
- }
- setPlaying(false);
-
- if (url.isEmpty()) {
- setStatus(QSoundEffect::Null);
- return;
- }
-
- setStatus(QSoundEffect::Loading);
- m_sample = sampleCache()->requestSample(url);
- connect(m_sample, &QSample::error, this, &QSoundEffectPrivate::decoderError);
- connect(m_sample, &QSample::ready, this, &QSoundEffectPrivate::sampleReady);
- switch(m_sample->state()) {
- case QSample::Ready:
- sampleReady();
- break;
- case QSample::Error:
- decoderError();
- break;
- default:
- break;
- }
-}
-
-int QSoundEffectPrivate::loopCount() const
-{
- return m_loopCount;
-}
-
-int QSoundEffectPrivate::loopsRemaining() const
-{
- return m_runningCount;
-}
-
-void QSoundEffectPrivate::setLoopCount(int loopCount)
-{
- if (loopCount == 0)
- loopCount = 1;
- m_loopCount = loopCount;
- if (m_playing) {
- PulseDaemonLocker locker;
- setLoopsRemaining(loopCount);
- }
-}
-
-qreal QSoundEffectPrivate::volume() const
-{
- QMutexLocker locker(&m_volumeLock);
- return m_volume;
-}
-
-static void volume_stream_flush_callback(pa_stream *s, int success, void *userdata)
-{
- Q_UNUSED(s);
- QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef *>(userdata);
- QSoundEffectPrivate *self = ref->soundEffect();
- ref->release();
- if (!self)
- return;
-
- if (!success)
- qWarning("QSoundEffect(pulseaudio): failed to drain");
-
- QMetaObject::invokeMethod(self, "prepare", Qt::QueuedConnection);
-}
-
-void QSoundEffectPrivate::setVolume(qreal volume)
-{
- QMutexLocker locker(&m_volumeLock);
-
- if (qFuzzyCompare(m_volume, volume))
- return;
-
- m_volume = qBound(qreal(0), volume, qreal(1));
- locker.unlock();
- if (!m_playing && m_pulseStream)
- pa_stream_flush(m_pulseStream, volume_stream_flush_callback, m_ref->getRef());
- emit volumeChanged();
-}
-
-bool QSoundEffectPrivate::isMuted() const
-{
- QMutexLocker locker(&m_volumeLock);
- return m_muted;
-}
-
-void QSoundEffectPrivate::setMuted(bool muted)
-{
- m_volumeLock.lock();
- m_muted = muted;
- m_volumeLock.unlock();
-
- emit mutedChanged();
-}
-
-bool QSoundEffectPrivate::isLoaded() const
-{
- return m_status == QSoundEffect::Ready;
-}
-
-bool QSoundEffectPrivate::isPlaying() const
-{
- return m_playing;
-}
-
-QSoundEffect::Status QSoundEffectPrivate::status() const
-{
- return m_status;
-}
-
-void QSoundEffectPrivate::setPlaying(bool playing)
-{
-#ifdef QT_PA_DEBUG
- qDebug() << this << "setPlaying(" << playing << ")";
-#endif
- if (m_playing == playing)
- return;
- if (!playing)
- m_playQueued = false;
- m_playing = playing;
- emit playingChanged();
-}
-
-void QSoundEffectPrivate::setStatus(QSoundEffect::Status status)
-{
-#ifdef QT_PA_DEBUG
- qDebug() << this << "setStatus" << status;
-#endif
- if (m_status == status)
- return;
- bool oldLoaded = isLoaded();
- m_status = status;
- emit statusChanged();
- if (oldLoaded != isLoaded())
- emit loadedChanged();
-}
-
-void QSoundEffectPrivate::setLoopsRemaining(int loopsRemaining)
-{
-#ifdef QT_PA_DEBUG
- qDebug() << this << "setLoopsRemaining " << loopsRemaining;
-#endif
- if (m_runningCount == loopsRemaining)
- return;
- m_runningCount = loopsRemaining;
- emit loopsRemainingChanged();
-}
-
-void QSoundEffectPrivate::play()
-{
- if (!m_resourcesAvailable)
- return;
-
- playAvailable();
-}
-
-void QSoundEffectPrivate::playAvailable()
-{
-#ifdef QT_PA_DEBUG
- qDebug() << this << "play";
-#endif
- if (m_status == QSoundEffect::Null || m_status == QSoundEffect::Error || m_playQueued)
- return;
-
- PulseDaemonLocker locker;
-
- if (!m_pulseStream || m_status != QSoundEffect::Ready || m_stopping || m_emptying) {
-#ifdef QT_PA_DEBUG
- qDebug() << this << "play deferred";
-#endif
- m_playQueued = true;
- } else {
- if (m_playing) { //restart playing from the beginning
-#ifdef QT_PA_DEBUG
- qDebug() << this << "restart playing";
-#endif
- setLoopsRemaining(0);
- m_playQueued = true;
- Q_ASSERT(m_pulseStream);
- emptyStream(ReloadSampleWhenDone);
- return;
- }
- setLoopsRemaining(m_loopCount);
- playSample();
- }
-
- setPlaying(true);
-}
-
-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;
-
- PulseDaemonLocker locker;
-
- m_emptying = true;
- pa_stream_set_write_callback(m_pulseStream, nullptr, nullptr);
- pa_stream_set_underflow_callback(m_pulseStream, nullptr, nullptr);
- pa_operation *op = pa_stream_flush(m_pulseStream, flushCompleteCb, m_ref->getRef());
- if (op)
- pa_operation_unref(op);
- else
- qWarning("QSoundEffect(pulseaudio): failed to flush stream");
-}
-
-void QSoundEffectPrivate::emptyComplete(void *stream, bool reload)
-{
-#ifdef QT_PA_DEBUG
- qDebug() << this << "emptyComplete";
-#endif
-
- PulseDaemonLocker locker;
-
- m_emptying = false;
-
- if ((pa_stream *)stream == m_pulseStream) {
- pa_operation *op = pa_stream_cork(m_pulseStream, 1,
- reload ? stream_cork_callback : nullptr, m_ref->getRef());
- if (op)
- pa_operation_unref(op);
- else
- qWarning("QSoundEffect(pulseaudio): failed to cork stream");
- }
-}
-
-void QSoundEffectPrivate::sampleReady()
-{
- PulseDaemonLocker locker;
-
- // The slot might be called right after a new call to setSource().
- // In this case, the sample has been reset and the slot is being called for the previous sample.
- // Just ignore it.
- if (Q_UNLIKELY(!m_sample || m_sample->state() != QSample::Ready))
- return;
-
-#ifdef QT_PA_DEBUG
- qDebug() << this << "sampleReady";
-#endif
- disconnect(m_sample, &QSample::error, this, &QSoundEffectPrivate::decoderError);
- disconnect(m_sample, &QSample::ready, this, &QSoundEffectPrivate::sampleReady);
- pa_sample_spec newFormatSpec = audioFormatToSampleSpec(m_sample->format());
-
- if (m_pulseStream && !pa_sample_spec_equal(&m_pulseSpec, &newFormatSpec)) {
- unloadPulseStream();
- }
- m_pulseSpec = newFormatSpec;
-
- m_sampleReady = true;
- m_position = 0;
-
- if (m_name.isNull())
- m_name = QString(QLatin1String("QtPulseSample-%1-%2")).arg(::getpid()).arg(quintptr(this)).toUtf8();
-
- if (m_pulseStream && pa_stream_get_state(m_pulseStream) == PA_STREAM_READY) {
-#ifdef QT_PA_DEBUG
- qDebug() << this << "reuse existing pulsestream";
-#endif
- const pa_buffer_attr *bufferAttr = pa_stream_get_buffer_attr(m_pulseStream);
- if (bufferAttr->prebuf > uint32_t(m_sample->data().size())) {
- pa_buffer_attr newBufferAttr;
- newBufferAttr = *bufferAttr;
- newBufferAttr.prebuf = m_sample->data().size();
- pa_operation *op = pa_stream_set_buffer_attr(m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, m_ref->getRef());
- if (op)
- pa_operation_unref(op);
- else
- qWarning("QSoundEffect(pulseaudio): failed to adjust pre-buffer attribute");
- } else {
- streamReady();
- }
- } else if (!m_pulseStream) {
- if (!pulseDaemon()->context() || pa_context_get_state(pulseDaemon()->context()) != PA_CONTEXT_READY) {
- connect(pulseDaemon(), &PulseDaemon::contextReady,
- this, &QSoundEffectPrivate::contextReady);
- return;
- }
- createPulseStream();
- }
-}
-
-void QSoundEffectPrivate::decoderError()
-{
- qWarning("QSoundEffect(pulseaudio): Error decoding source %ls", qUtf16Printable(m_source.toString()));
- disconnect(m_sample, &QSample::error, this, &QSoundEffectPrivate::decoderError);
- bool playingDirty = false;
- if (m_playing) {
- m_playing = false;
- playingDirty = true;
- }
- setStatus(QSoundEffect::Error);
- if (playingDirty)
- emit playingChanged();
-}
-
-void QSoundEffectPrivate::unloadPulseStream()
-{
-#ifdef QT_PA_DEBUG
- qDebug() << this << "unloadPulseStream";
-#endif
- m_sinkInputId = -1;
- PulseDaemonLocker locker;
- if (m_pulseStream) {
- pa_stream_set_state_callback(m_pulseStream, nullptr, nullptr);
- pa_stream_set_write_callback(m_pulseStream, nullptr, nullptr);
- pa_stream_set_underflow_callback(m_pulseStream, nullptr, nullptr);
- pa_stream_disconnect(m_pulseStream);
- pa_stream_unref(m_pulseStream);
- disconnect(pulseDaemon(), &PulseDaemon::contextFailed,
- this, &QSoundEffectPrivate::contextFailed);
- m_pulseStream = nullptr;
- m_reloadCategory = false; // category will be reloaded when we connect anyway
- }
-}
-
-void QSoundEffectPrivate::prepare()
-{
- if (!m_pulseStream || !m_sampleReady)
- return;
- PulseDaemonLocker locker;
-
- if (pa_stream_get_state(m_pulseStream) != PA_STREAM_READY)
- return;
-
- pa_stream_set_write_callback(m_pulseStream, stream_write_callback, this);
- pa_stream_set_underflow_callback(m_pulseStream, stream_underrun_callback, this);
- m_stopping = false;
- size_t writeBytes = size_t(qMin(m_pulseBufferSize, m_sample->data().size()));
-#ifdef QT_PA_DEBUG
- qDebug() << this << "prepare(): writable size =" << pa_stream_writable_size(m_pulseStream)
- << "actual writeBytes =" << writeBytes
- << "m_playQueued =" << m_playQueued;
-#endif
- m_position = writeToStream(m_sample->data().data(), writeBytes);
-
- if (m_playQueued) {
- m_playQueued = false;
- setLoopsRemaining(m_loopCount);
- playSample();
- }
-}
-
-void QSoundEffectPrivate::uploadSample()
-{
- // Always called on PulseAudio thread
-
- if (m_runningCount == 0) {
-#ifdef QT_PA_DEBUG
- qDebug() << this << "uploadSample: return due to 0 m_runningCount";
-#endif
- return;
- }
-
- if (Q_UNLIKELY(!m_pulseStream
- || pa_stream_get_state(m_pulseStream) != PA_STREAM_READY
- || !m_sampleReady)) {
- return;
- }
-
-#ifdef QT_PA_DEBUG
- qDebug() << this << "uploadSample: m_runningCount =" << m_runningCount;
-#endif
- if (m_position == m_sample->data().size()) {
- m_position = 0;
- if (m_runningCount > 0)
- setLoopsRemaining(m_runningCount - 1);
- if (m_runningCount == 0) {
- return;
- }
- }
-
- int writableSize = int(pa_stream_writable_size(m_pulseStream));
- int firstPartLength = qMin(m_sample->data().size() - m_position, writableSize);
-
- 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)
- setLoopsRemaining(m_runningCount - 1);
- if (m_runningCount != 0 && firstPartLength < writableSize)
- {
- while (writtenBytes < writableSize) {
- int writeSize = qMin(writableSize - writtenBytes, m_sample->data().size());
- writtenBytes += writeToStream(m_sample->data().data(), writeSize);
-
- if (writeSize < m_sample->data().size()) {
- m_position = writeSize;
- break;
- }
- if (m_runningCount > 0)
- setLoopsRemaining(m_runningCount - 1);
- if (m_runningCount == 0)
- break;
- }
- }
- }
-#ifdef QT_PA_DEBUG
- qDebug() << this << "uploadSample: use direct write, writeable size =" << writableSize
- << "actual writtenBytes =" << writtenBytes;
-#endif
-}
-
-int QSoundEffectPrivate::writeToStream(const void *data, int size)
-{
- // Always called on PulseAudio thread
-
- if (size < 1)
- return 0;
-
- m_volumeLock.lock();
- 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 = nullptr;
- 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 = nullptr;
- }
-
- 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()
-{
- PulseDaemonLocker locker;
-
-#ifdef QT_PA_DEBUG
- qDebug() << this << "playSample";
-#endif
- Q_ASSERT(m_pulseStream);
- Q_ASSERT(pa_stream_get_state(m_pulseStream) == PA_STREAM_READY);
- pa_operation *o = pa_stream_cork(m_pulseStream, 0, nullptr, nullptr);
- if (o)
- pa_operation_unref(o);
-}
-
-void QSoundEffectPrivate::stop()
-{
-#ifdef QT_PA_DEBUG
- qDebug() << this << "stop";
-#endif
- if (!m_playing)
- return;
-
- PulseDaemonLocker locker;
-
- setPlaying(false);
-
- m_stopping = true;
- if (m_pulseStream) {
- emptyStream(ReloadSampleWhenDone);
- if (m_reloadCategory) {
- unloadPulseStream(); // upon play we reconnect anyway
- }
- }
- setLoopsRemaining(0);
- m_position = 0;
- m_playQueued = false;
- m_reloadCategory = false;
-}
-
-void QSoundEffectPrivate::underRun()
-{
- stop();
-}
-
-void QSoundEffectPrivate::streamReady()
-{
- PulseDaemonLocker locker;
-
- if (Q_UNLIKELY(!m_sample || m_sample->state() != QSample::Ready
- || !m_pulseStream || pa_stream_get_state(m_pulseStream) != PA_STREAM_READY)) {
- return;
- }
-
-#ifdef QT_PA_DEBUG
- qDebug() << this << "streamReady";
-#endif
-
- m_sinkInputId = pa_stream_get_index(m_pulseStream);
-#ifdef QT_PA_DEBUG
- const pa_buffer_attr *realBufAttr = pa_stream_get_buffer_attr(m_pulseStream);
- qDebug() << this << "m_sinkInputId =" << m_sinkInputId
- << "tlength =" << realBufAttr->tlength << "maxlength =" << realBufAttr->maxlength
- << "minreq = " << realBufAttr->minreq << "prebuf =" << realBufAttr->prebuf;
-#endif
- prepare();
- setStatus(QSoundEffect::Ready);
-}
-
-void QSoundEffectPrivate::createPulseStream()
-{
-#ifdef QT_PA_DEBUG
- qDebug() << this << "createPulseStream";
-#endif
-
- if (!pulseDaemon()->context())
- return;
-
- pa_proplist *propList = pa_proplist_new();
- if (!m_category.isNull())
- pa_proplist_sets(propList, PA_PROP_MEDIA_ROLE, m_category.toLatin1().constData());
- pa_stream *stream = pa_stream_new_with_proplist(pulseDaemon()->context(), m_name.constData(),
- &m_pulseSpec, nullptr, propList);
- pa_proplist_free(propList);
-
- connect(pulseDaemon(), &PulseDaemon::contextFailed,
- this, &QSoundEffectPrivate::contextFailed);
-
- if (stream == nullptr) {
- qWarning("QSoundEffect(pulseaudio): Failed to create stream");
- m_pulseStream = nullptr;
- setStatus(QSoundEffect::Error);
- setPlaying(false);
- return;
- }
- else {
- pa_stream_set_state_callback(stream, stream_state_callback, this);
- pa_stream_set_write_callback(stream, stream_write_callback, this);
- pa_stream_set_underflow_callback(stream, stream_underrun_callback, this);
- }
- m_pulseStream = stream;
-
- if (pa_stream_connect_playback(m_pulseStream,
- m_sinkName.isEmpty() ? nullptr : m_sinkName.toLatin1().constData(),
- nullptr, PA_STREAM_START_CORKED, nullptr, nullptr) < 0) {
- qWarning("QSoundEffect(pulseaudio): Failed to connect stream, error = %s",
- pa_strerror(pa_context_errno(pulseDaemon()->context())));
- }
-}
-
-void QSoundEffectPrivate::contextReady()
-{
- disconnect(pulseDaemon(), &PulseDaemon::contextReady,
- this, &QSoundEffectPrivate::contextReady);
- PulseDaemonLocker locker;
- createPulseStream();
-}
-
-void QSoundEffectPrivate::contextFailed()
-{
- unloadPulseStream();
- connect(pulseDaemon(), &PulseDaemon::contextReady,
- this, &QSoundEffectPrivate::contextReady);
-}
-
-void QSoundEffectPrivate::stream_write_callback(pa_stream *s, size_t length, void *userdata)
-{
- Q_UNUSED(length);
- Q_UNUSED(s);
-
- QSoundEffectPrivate *self = reinterpret_cast<QSoundEffectPrivate*>(userdata);
-#ifdef QT_PA_DEBUG
- qDebug() << self << "stream_write_callback";
-#endif
- self->uploadSample();
-}
-
-void QSoundEffectPrivate::stream_state_callback(pa_stream *s, void *userdata)
-{
- QSoundEffectPrivate *self = reinterpret_cast<QSoundEffectPrivate*>(userdata);
- switch (pa_stream_get_state(s)) {
- case PA_STREAM_READY:
- {
-#ifdef QT_PA_DEBUG
- qDebug() << self << "pulse stream ready";
-#endif
- if (Q_UNLIKELY(!self->m_sample || self->m_sample->state() != QSample::Ready))
- return;
-
- const pa_buffer_attr *bufferAttr = pa_stream_get_buffer_attr(self->m_pulseStream);
- self->m_pulseBufferSize = bufferAttr->tlength;
- if (bufferAttr->prebuf > uint32_t(self->m_sample->data().size())) {
- pa_buffer_attr newBufferAttr;
- newBufferAttr = *bufferAttr;
- newBufferAttr.prebuf = self->m_sample->data().size();
- pa_operation *op = pa_stream_set_buffer_attr(self->m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, self->m_ref->getRef());
- if (op)
- pa_operation_unref(op);
- else
- qWarning("QSoundEffect(pulseaudio): failed to adjust pre-buffer attribute");
- } else {
- QMetaObject::invokeMethod(self, "streamReady", Qt::QueuedConnection);
- }
- break;
- }
- case PA_STREAM_CREATING:
-#ifdef QT_PA_DEBUG
- qDebug() << self << "pulse stream creating";
-#endif
- break;
- case PA_STREAM_TERMINATED:
-#ifdef QT_PA_DEBUG
- qDebug() << self << "pulse stream terminated";
-#endif
- break;
-
- case PA_STREAM_FAILED:
- default:
- qWarning("QSoundEffect(pulseaudio): Error in pulse audio stream");
- break;
- }
-}
-
-void QSoundEffectPrivate::stream_adjust_prebuffer_callback(pa_stream *s, int success, void *userdata)
-{
-#ifdef QT_PA_DEBUG
- qDebug() << "stream_adjust_prebuffer_callback";
-#endif
- Q_UNUSED(s);
- QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef*>(userdata);
- QSoundEffectPrivate *self = ref->soundEffect();
- ref->release();
- if (!self)
- return;
-
- if (!success)
- qWarning("QSoundEffect(pulseaudio): failed to adjust pre-buffer attribute");
-#ifdef QT_PA_DEBUG
- qDebug() << self << "stream_adjust_prebuffer_callback";
-#endif
- QMetaObject::invokeMethod(self, "streamReady", Qt::QueuedConnection);
-}
-
-void QSoundEffectPrivate::stream_underrun_callback(pa_stream *s, void *userdata)
-{
- Q_UNUSED(s);
- QSoundEffectPrivate *self = reinterpret_cast<QSoundEffectPrivate*>(userdata);
-#ifdef QT_PA_DEBUG
- qDebug() << self << "stream_underrun_callback";
-#endif
- if (self->m_runningCount == 0 && !self->m_playQueued)
- QMetaObject::invokeMethod(self, "underRun", Qt::QueuedConnection);
-#ifdef QT_PA_DEBUG
- else
- qDebug() << "underun corked =" << pa_stream_is_corked(s);
-#endif
-}
-
-void QSoundEffectPrivate::stream_cork_callback(pa_stream *s, int success, void *userdata)
-{
-#ifdef QT_PA_DEBUG
- qDebug() << "stream_cork_callback";
-#endif
- Q_UNUSED(s);
- QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef*>(userdata);
- QSoundEffectPrivate *self = ref->soundEffect();
- ref->release();
- if (!self)
- return;
-
- if (!success)
- qWarning("QSoundEffect(pulseaudio): failed to stop");
-#ifdef QT_PA_DEBUG
- qDebug() << self << "stream_cork_callback";
-#endif
- QMetaObject::invokeMethod(self, "prepare", Qt::QueuedConnection);
-}
-
-void QSoundEffectPrivate::stream_flush_callback(pa_stream *s, int success, void *userdata)
-{
-#ifdef QT_PA_DEBUG
- qDebug() << "stream_flush_callback";
-#endif
- Q_UNUSED(s);
- QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef*>(userdata);
- QSoundEffectPrivate *self = ref->soundEffect();
- ref->release();
- if (!self)
- return;
-
- if (!success)
- qWarning("QSoundEffect(pulseaudio): failed 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() << "stream_flush_reload_callback";
-#endif
- Q_UNUSED(s);
- QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef*>(userdata);
- QSoundEffectPrivate *self = ref->soundEffect();
- ref->release();
- if (!self)
- return;
-
- if (!success)
- qWarning("QSoundEffect(pulseaudio): failed to drain");
-
- QMetaObject::invokeMethod(self, "emptyComplete", Qt::QueuedConnection, Q_ARG(void*, s), Q_ARG(bool, true));
-}
-
-void QSoundEffectPrivate::stream_write_done_callback(void *p)
-{
- Q_UNUSED(p);
-#ifdef QT_PA_DEBUG
- qDebug() << "stream_write_done_callback";
-#endif
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qsoundeffect_pulse_p.cpp"
-#include "qsoundeffect_pulse_p.moc"
diff --git a/src/multimedia/audio/qsoundeffect_pulse_p.h b/src/multimedia/audio/qsoundeffect_pulse_p.h
deleted file mode 100644
index e0073f0d5..000000000
--- a/src/multimedia/audio/qsoundeffect_pulse_p.h
+++ /dev/null
@@ -1,188 +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_PULSE_H
-#define QSOUNDEFFECT_PULSE_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 "qsoundeffect.h"
-
-#include <QtCore/qobject.h>
-#include <QtCore/qdatetime.h>
-#include <QtCore/qmutex.h>
-#include <qmediaplayer.h>
-#include <pulse/pulseaudio.h>
-#include "qsamplecache_p.h"
-
-#include <private/qmediaresourcepolicy_p.h>
-#include <private/qmediaresourceset_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QSoundEffectRef;
-
-class QSoundEffectPrivate : public QObject
-{
- 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 &category);
-
-public Q_SLOTS:
- void play();
- void stop();
-
-Q_SIGNALS:
- void loopsRemainingChanged();
- void volumeChanged();
- void mutedChanged();
- void loadedChanged();
- void playingChanged();
- void statusChanged();
- void categoryChanged();
-
-private Q_SLOTS:
- void decoderError();
- void sampleReady();
- void uploadSample();
- void contextReady();
- void contextFailed();
- void underRun();
- void prepare();
- void streamReady();
- void emptyComplete(void *stream, bool reload);
-
- void handleAvailabilityChanged(bool available);
-
-private:
- void playAvailable();
- void playSample();
-
- enum EmptyStreamOption {
- ReloadSampleWhenDone = 0x1
- };
- Q_DECLARE_FLAGS(EmptyStreamOptions, EmptyStreamOption)
- void emptyStream(EmptyStreamOptions options = EmptyStreamOptions());
-
- void createPulseStream();
- void unloadPulseStream();
-
- int writeToStream(const void *data, int size);
-
- void setPlaying(bool playing);
- void setStatus(QSoundEffect::Status status);
- void setLoopsRemaining(int loopsRemaining);
-
- static void stream_write_callback(pa_stream *s, size_t length, void *userdata);
- static void stream_state_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_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);
-
- pa_stream *m_pulseStream = nullptr;
- QString m_sinkName;
- int m_sinkInputId = -1;
- pa_sample_spec m_pulseSpec;
- int m_pulseBufferSize = 0;
-
- bool m_emptying = false;
- bool m_sampleReady = false;
- bool m_playing = false;
- QSoundEffect::Status m_status = QSoundEffect::Null;
- bool m_muted = false;
- bool m_playQueued = false;
- bool m_stopping = false;
- qreal m_volume = 1.0;
- int m_loopCount = 1;
- int m_runningCount = 0;
- QUrl m_source;
- QByteArray m_name;
- QString m_category;
- bool m_reloadCategory = false;
-
- QSample *m_sample = nullptr;
- int m_position = 0;
- QSoundEffectRef *m_ref = nullptr;
-
- bool m_resourcesAvailable = false;
-
- // Protects volume while PuseAudio is accessing it
- mutable QMutex m_volumeLock;
-
- QMediaPlayerResourceSetInterface *m_resources = nullptr;
-};
-
-QT_END_NAMESPACE
-
-#endif // QSOUNDEFFECT_PULSE_H
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
diff --git a/src/multimedia/audio/qaudio.cpp b/src/multimedia/audio/qtaudio.cpp
index 82613270a..fb14e5093 100644
--- a/src/multimedia/audio/qaudio.cpp
+++ b/src/multimedia/audio/qtaudio.cpp
@@ -1,44 +1,8 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include <qaudio.h>
+#include <qtaudio.h>
#include <qmath.h>
#include <QDebug>
@@ -46,28 +10,17 @@ QT_BEGIN_NAMESPACE
#define LOG100 4.60517018599
-static void qRegisterAudioMetaTypes()
-{
- qRegisterMetaType<QAudio::Error>();
- qRegisterMetaType<QAudio::State>();
- qRegisterMetaType<QAudio::Mode>();
- qRegisterMetaType<QAudio::Role>();
- qRegisterMetaType<QAudio::VolumeScale>();
-}
-
-Q_CONSTRUCTOR_FUNCTION(qRegisterAudioMetaTypes)
-
/*!
- \namespace QAudio
+ \namespace QtAudio
\ingroup multimedia-namespaces
- \brief The QAudio namespace contains enums used by the audio classes.
+ \brief The QtAudio namespace contains enums used by the audio classes.
\inmodule QtMultimedia
\ingroup multimedia
\ingroup multimedia_audio
*/
/*!
- \enum QAudio::Error
+ \enum QtAudio::Error
\value NoError No errors have occurred
\value OpenError An error occurred opening the audio device
@@ -77,7 +30,7 @@ Q_CONSTRUCTOR_FUNCTION(qRegisterAudioMetaTypes)
*/
/*!
- \enum QAudio::State
+ \enum QtAudio::State
\value ActiveState Audio data is being processed, this state is set after start() is called
and while audio data is available to be processed.
@@ -88,41 +41,10 @@ Q_CONSTRUCTOR_FUNCTION(qRegisterAudioMetaTypes)
\value StoppedState The audio device is closed, and is not processing any audio data
\value IdleState The QIODevice passed in has no data and audio system's buffer is empty, this state
is set after start() is called and while no audio data is available to be processed.
- \value InterruptedState This stream is in a suspended state because another higher priority stream currently
- has control of the audio device. Playback cannot resume until the higher priority
- stream relinquishes control of the audio device.
-*/
-
-/*!
- \enum QAudio::Mode
-
- \value AudioOutput audio output device
- \value AudioInput audio input device
-*/
-
-/*!
- \enum QAudio::Role
-
- This enum describes the role of an audio stream.
-
- \value UnknownRole The role is unknown or undefined
- \value MusicRole Music
- \value VideoRole Soundtrack from a movie or a video
- \value VoiceCommunicationRole Voice communications, such as telephony
- \value AlarmRole Alarm
- \value NotificationRole Notification, such as an incoming e-mail or a chat request
- \value RingtoneRole Ringtone
- \value AccessibilityRole For accessibility, such as with a screen reader
- \value SonificationRole Sonification, such as with user interface sounds
- \value GameRole Game audio
- \value CustomRole The role is specified by QMediaPlayer::customAudioRole()
-
- \since 5.6
- \sa QMediaPlayer::setAudioRole()
*/
/*!
- \enum QAudio::VolumeScale
+ \enum QtAudio::VolumeScale
This enum defines the different audio volume scales.
@@ -137,16 +59,17 @@ Q_CONSTRUCTOR_FUNCTION(qRegisterAudioMetaTypes)
\value DecibelVolumeScale Decibel (dB, amplitude) logarithmic scale. \c -200 is silence
and \c 0 is full volume.
- \since 5.8
- \sa QAudio::convertVolume()
+ \sa QtAudio::convertVolume()
*/
+#if defined(Q_QDOC)
+namespace QtAudio
+#else
namespace QAudio
+#endif
{
/*!
- \fn qreal QAudio::convertVolume(qreal volume, VolumeScale from, VolumeScale to)
-
Converts an audio \a volume \a from a volume scale \a to another, and returns the result.
Depending on the context, different scales are used to represent audio volume. All Qt Multimedia
@@ -164,31 +87,30 @@ namespace QAudio
\snippet multimedia-snippets/audio.cpp Volume conversion
- \since 5.8
- \sa VolumeScale, QMediaPlayer::setVolume(), QAudioOutput::setVolume(),
- QAudioInput::setVolume(), QSoundEffect::setVolume(), QMediaRecorder::setVolume()
+ \sa VolumeScale, QAudioSink::setVolume(), QAudioSource::setVolume(),
+ QSoundEffect::setVolume()
*/
-qreal convertVolume(qreal volume, VolumeScale from, VolumeScale to)
+float convertVolume(float volume, VolumeScale from, VolumeScale to)
{
switch (from) {
case LinearVolumeScale:
- volume = qMax(qreal(0), volume);
+ volume = qMax(float(0), volume);
switch (to) {
case LinearVolumeScale:
return volume;
case CubicVolumeScale:
- return qPow(volume, qreal(1 / 3.0));
+ return qPow(volume, float(1 / 3.0));
case LogarithmicVolumeScale:
return 1 - std::exp(-volume * LOG100);
case DecibelVolumeScale:
if (volume < 0.001)
- return qreal(-200);
+ return float(-200);
else
- return qreal(20.0) * std::log10(volume);
+ return float(20.0) * std::log10(volume);
}
break;
case CubicVolumeScale:
- volume = qMax(qreal(0), volume);
+ volume = qMax(float(0), volume);
switch (to) {
case LinearVolumeScale:
return volume * volume * volume;
@@ -198,13 +120,13 @@ qreal convertVolume(qreal volume, VolumeScale from, VolumeScale to)
return 1 - std::exp(-volume * volume * volume * LOG100);
case DecibelVolumeScale:
if (volume < 0.001)
- return qreal(-200);
+ return float(-200);
else
- return qreal(3.0 * 20.0) * std::log10(volume);
+ return float(3.0 * 20.0) * std::log10(volume);
}
break;
case LogarithmicVolumeScale:
- volume = qMax(qreal(0), volume);
+ volume = qMax(float(0), volume);
switch (to) {
case LinearVolumeScale:
if (volume > 0.99)
@@ -215,29 +137,29 @@ qreal convertVolume(qreal volume, VolumeScale from, VolumeScale to)
if (volume > 0.99)
return 1;
else
- return qPow(-std::log(1 - volume) / LOG100, qreal(1 / 3.0));
+ return qPow(-std::log(1 - volume) / LOG100, float(1 / 3.0));
case LogarithmicVolumeScale:
return volume;
case DecibelVolumeScale:
if (volume < 0.001)
- return qreal(-200);
+ return float(-200);
else if (volume > 0.99)
return 0;
else
- return qreal(20.0) * std::log10(-std::log(1 - volume) / LOG100);
+ return float(20.0) * std::log10(-std::log(1 - volume) / LOG100);
}
break;
case DecibelVolumeScale:
switch (to) {
case LinearVolumeScale:
- return qPow(qreal(10.0), volume / qreal(20.0));
+ return qPow(float(10.0), volume / float(20.0));
case CubicVolumeScale:
- return qPow(qreal(10.0), volume / qreal(3.0 * 20.0));
+ return qPow(float(10.0), volume / float(3.0 * 20.0));
case LogarithmicVolumeScale:
if (qFuzzyIsNull(volume))
return 1;
else
- return 1 - std::exp(-qPow(qreal(10.0), volume / qreal(20.0)) * LOG100);
+ return 1 - std::exp(-qPow(float(10.0), volume / float(20.0)) * LOG100);
case DecibelVolumeScale:
return volume;
}
@@ -291,66 +213,6 @@ QDebug operator<<(QDebug dbg, QAudio::State state)
case QAudio::IdleState:
dbg << "IdleState";
break;
- case QAudio::InterruptedState:
- dbg << "InterruptedState";
- break;
- }
- return dbg;
-}
-
-QDebug operator<<(QDebug dbg, QAudio::Mode mode)
-{
- QDebugStateSaver saver(dbg);
- dbg.nospace();
- switch (mode) {
- case QAudio::AudioInput:
- dbg << "AudioInput";
- break;
- case QAudio::AudioOutput:
- dbg << "AudioOutput";
- break;
- }
- return dbg;
-}
-
-QDebug operator<<(QDebug dbg, QAudio::Role role)
-{
- QDebugStateSaver saver(dbg);
- dbg.nospace();
- switch (role) {
- case QAudio::UnknownRole:
- dbg << "UnknownRole";
- break;
- case QAudio::AccessibilityRole:
- dbg << "AccessibilityRole";
- break;
- case QAudio::AlarmRole:
- dbg << "AlarmRole";
- break;
- case QAudio::GameRole:
- dbg << "GameRole";
- break;
- case QAudio::MusicRole:
- dbg << "MusicRole";
- break;
- case QAudio::NotificationRole:
- dbg << "NotificationRole";
- break;
- case QAudio::RingtoneRole:
- dbg << "RingtoneRole";
- break;
- case QAudio::SonificationRole:
- dbg << "SonificationRole";
- break;
- case QAudio::VideoRole:
- dbg << "VideoRole";
- break;
- case QAudio::VoiceCommunicationRole:
- dbg << "VoiceCommunicationRole";
- break;
- case QAudio::CustomRole:
- dbg << "CustomRole";
- break;
}
return dbg;
}
diff --git a/src/multimedia/audio/qtaudio.h b/src/multimedia/audio/qtaudio.h
new file mode 100644
index 000000000..6f6e0e668
--- /dev/null
+++ b/src/multimedia/audio/qtaudio.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+#ifndef QTAUDIO_H
+#define QTAUDIO_H
+
+#if 0
+#pragma qt_class(QtAudio)
+#endif
+
+#include <QtMultimedia/qaudio.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_END_NAMESPACE
+
+#endif // QTAUDIO_H
diff --git a/src/multimedia/audio/qwavedecoder.cpp b/src/multimedia/audio/qwavedecoder.cpp
new file mode 100644
index 000000000..452363ddc
--- /dev/null
+++ b/src/multimedia/audio/qwavedecoder.cpp
@@ -0,0 +1,507 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwavedecoder.h"
+
+#include <QtCore/qtimer.h>
+#include <QtCore/qendian.h>
+#include <limits.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+void bswap2(char *data, qsizetype count) noexcept
+{
+ for (qsizetype i = 0; i < count; ++i) {
+ qSwap(data[0], data[1]);
+ ++count;
+ data += 2;
+ }
+}
+
+void bswap4(char *data, qsizetype count) noexcept
+{
+ for (qsizetype i = 0; i < count; ++i) {
+ qSwap(data[0], data[3]);
+ qSwap(data[1], data[2]);
+ ++count;
+ data += 4;
+ }
+}
+
+}
+
+QWaveDecoder::QWaveDecoder(QIODevice *device, QObject *parent)
+ : QIODevice(parent),
+ device(device)
+{
+}
+
+QWaveDecoder::QWaveDecoder(QIODevice *device, const QAudioFormat &format, QObject *parent)
+ : QIODevice(parent),
+ device(device),
+ format(format)
+{
+}
+
+QWaveDecoder::~QWaveDecoder() = default;
+
+bool QWaveDecoder::open(QIODevice::OpenMode mode)
+{
+ bool canOpen = false;
+ if (mode & QIODevice::ReadOnly && mode & ~QIODevice::WriteOnly) {
+ canOpen = QIODevice::open(mode | QIODevice::Unbuffered);
+ if (canOpen && enoughDataAvailable())
+ handleData();
+ else
+ connect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData);
+ return canOpen;
+ }
+
+ if (mode & QIODevice::WriteOnly) {
+ if (format.sampleFormat() != QAudioFormat::Int16)
+ return false; // data format is not supported
+ canOpen = QIODevice::open(mode);
+ if (canOpen && writeHeader())
+ haveHeader = true;
+ return canOpen;
+ }
+ return QIODevice::open(mode);
+}
+
+void QWaveDecoder::close()
+{
+ if (isOpen() && (openMode() & QIODevice::WriteOnly)) {
+ Q_ASSERT(dataSize < INT_MAX);
+ if (!device->isOpen() || !writeDataLength())
+ qWarning() << "Failed to finalize wav file";
+ }
+ QIODevice::close();
+}
+
+bool QWaveDecoder::seek(qint64 pos)
+{
+ return device->seek(pos);
+}
+
+qint64 QWaveDecoder::pos() const
+{
+ return device->pos();
+}
+
+void QWaveDecoder::setIODevice(QIODevice * /* device */)
+{
+}
+
+QAudioFormat QWaveDecoder::audioFormat() const
+{
+ return format;
+}
+
+QIODevice* QWaveDecoder::getDevice()
+{
+ return device;
+}
+
+int QWaveDecoder::duration() const
+{
+ if (openMode() & QIODevice::WriteOnly)
+ return 0;
+ int bytesPerSec = format.bytesPerFrame() * format.sampleRate();
+ return bytesPerSec ? size() * 1000 / bytesPerSec : 0;
+}
+
+qint64 QWaveDecoder::size() const
+{
+ if (openMode() & QIODevice::ReadOnly) {
+ if (!haveFormat)
+ return 0;
+ if (bps == 24)
+ return dataSize*2/3;
+ return dataSize;
+ } else {
+ return device->size();
+ }
+}
+
+bool QWaveDecoder::isSequential() const
+{
+ return device->isSequential();
+}
+
+qint64 QWaveDecoder::bytesAvailable() const
+{
+ return haveFormat ? device->bytesAvailable() : 0;
+}
+
+qint64 QWaveDecoder::headerLength()
+{
+ return HeaderLength;
+}
+
+qint64 QWaveDecoder::readData(char *data, qint64 maxlen)
+{
+ const int bytesPerSample = format.bytesPerSample();
+ if (!haveFormat || bytesPerSample == 0)
+ return 0;
+
+ if (bps == 24) {
+ // 24 bit WAV, read in as 16 bit
+ qint64 l = 0;
+ while (l < maxlen - 1) {
+ char tmp[3];
+ device->read(tmp, 3);
+ if (byteSwap)
+ qSwap(tmp[0], tmp[2]);
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ data[0] = tmp[0];
+ data[1] = tmp[1];
+#else
+ data[0] = tmp[1];
+ data[1] = tmp[2];
+#endif
+ data += 2;
+ l += 2;
+ }
+ return l;
+ }
+
+ qint64 nSamples = maxlen / bytesPerSample;
+ maxlen = nSamples * bytesPerSample;
+ int read = device->read(data, maxlen);
+
+ if (!byteSwap || format.bytesPerFrame() == 1)
+ return read;
+
+ nSamples = read / bytesPerSample;
+ switch (bytesPerSample) {
+ case 2:
+ bswap2(data, nSamples);
+ break;
+ case 4:
+ bswap4(data, nSamples);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ return read;
+
+}
+
+qint64 QWaveDecoder::writeData(const char *data, qint64 len)
+{
+ if (!haveHeader)
+ return 0;
+ qint64 written = device->write(data, len);
+ dataSize += written;
+ return written;
+}
+
+bool QWaveDecoder::writeHeader()
+{
+ if (device->size() != 0)
+ return false;
+
+#ifndef Q_LITTLE_ENDIAN
+ // only implemented for LITTLE ENDIAN
+ return false;
+#endif
+
+ CombinedHeader header;
+
+ memset(&header, 0, HeaderLength);
+
+ // RIFF header
+ memcpy(header.riff.descriptor.id,"RIFF",4);
+ qToLittleEndian<quint32>(quint32(dataSize + HeaderLength - 8),
+ reinterpret_cast<unsigned char*>(&header.riff.descriptor.size));
+ memcpy(header.riff.type, "WAVE",4);
+
+ // WAVE header
+ memcpy(header.wave.descriptor.id,"fmt ",4);
+ qToLittleEndian<quint32>(quint32(16),
+ reinterpret_cast<unsigned char*>(&header.wave.descriptor.size));
+ qToLittleEndian<quint16>(quint16(1),
+ reinterpret_cast<unsigned char*>(&header.wave.audioFormat));
+ qToLittleEndian<quint16>(quint16(format.channelCount()),
+ reinterpret_cast<unsigned char*>(&header.wave.numChannels));
+ qToLittleEndian<quint32>(quint32(format.sampleRate()),
+ reinterpret_cast<unsigned char*>(&header.wave.sampleRate));
+ qToLittleEndian<quint32>(quint32(format.sampleRate() * format.bytesPerFrame()),
+ reinterpret_cast<unsigned char*>(&header.wave.byteRate));
+ qToLittleEndian<quint16>(quint16(format.channelCount() * format.bytesPerSample()),
+ reinterpret_cast<unsigned char*>(&header.wave.blockAlign));
+ qToLittleEndian<quint16>(quint16(format.bytesPerSample() * 8),
+ reinterpret_cast<unsigned char*>(&header.wave.bitsPerSample));
+
+ // DATA header
+ memcpy(header.data.descriptor.id,"data",4);
+ qToLittleEndian<quint32>(quint32(dataSize),
+ reinterpret_cast<unsigned char*>(&header.data.descriptor.size));
+
+ return device->write(reinterpret_cast<const char *>(&header), HeaderLength);
+}
+
+bool QWaveDecoder::writeDataLength()
+{
+#ifndef Q_LITTLE_ENDIAN
+ // only implemented for LITTLE ENDIAN
+ return false;
+#endif
+
+ if (isSequential())
+ return false;
+
+ // seek to RIFF header size, see header.riff.descriptor.size above
+ if (!device->seek(4)) {
+ qDebug() << "can't seek";
+ return false;
+ }
+
+ quint32 length = dataSize + HeaderLength - 8;
+ if (device->write(reinterpret_cast<const char *>(&length), 4) != 4)
+ return false;
+
+ // seek to DATA header size, see header.data.descriptor.size above
+ if (!device->seek(40))
+ return false;
+
+ return device->write(reinterpret_cast<const char *>(&dataSize), 4);
+}
+
+void QWaveDecoder::parsingFailed()
+{
+ Q_ASSERT(device);
+ disconnect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData);
+ emit parsingError();
+}
+
+void QWaveDecoder::handleData()
+{
+ if (openMode() == QIODevice::WriteOnly)
+ return;
+
+ // As a special "state", if we have junk to skip, we do
+ if (junkToSkip > 0) {
+ discardBytes(junkToSkip); // this also updates junkToSkip
+
+ // If we couldn't skip all the junk, return
+ if (junkToSkip > 0) {
+ // We might have run out
+ if (device->atEnd())
+ parsingFailed();
+ return;
+ }
+ }
+
+ if (state == QWaveDecoder::InitialState) {
+ if (device->bytesAvailable() < qint64(sizeof(RIFFHeader)))
+ return;
+
+ RIFFHeader riff;
+ device->read(reinterpret_cast<char *>(&riff), sizeof(RIFFHeader));
+
+ // RIFF = little endian RIFF, RIFX = big endian RIFF
+ if (((qstrncmp(riff.descriptor.id, "RIFF", 4) != 0) && (qstrncmp(riff.descriptor.id, "RIFX", 4) != 0))
+ || qstrncmp(riff.type, "WAVE", 4) != 0) {
+ parsingFailed();
+ return;
+ }
+
+ state = QWaveDecoder::WaitingForFormatState;
+ bigEndian = (qstrncmp(riff.descriptor.id, "RIFX", 4) == 0);
+ byteSwap = (bigEndian != (QSysInfo::ByteOrder == QSysInfo::BigEndian));
+ }
+
+ if (state == QWaveDecoder::WaitingForFormatState) {
+ if (findChunk("fmt ")) {
+ chunk descriptor;
+ peekChunk(&descriptor);
+
+ quint32 rawChunkSize = descriptor.size + sizeof(chunk);
+ if (device->bytesAvailable() < qint64(rawChunkSize))
+ return;
+
+ WAVEHeader wave;
+ device->read(reinterpret_cast<char *>(&wave), sizeof(WAVEHeader));
+
+ if (rawChunkSize > sizeof(WAVEHeader))
+ discardBytes(rawChunkSize - sizeof(WAVEHeader));
+
+ // Swizzle this
+ if (bigEndian) {
+ wave.audioFormat = qFromBigEndian<quint16>(wave.audioFormat);
+ } else {
+ wave.audioFormat = qFromLittleEndian<quint16>(wave.audioFormat);
+ }
+
+ if (wave.audioFormat != 0 && wave.audioFormat != 1) {
+ // 32bit wave files have format == 0xFFFE (WAVE_FORMAT_EXTENSIBLE).
+ // but don't support them at the moment.
+ parsingFailed();
+ return;
+ }
+
+ int rate;
+ int channels;
+ if (bigEndian) {
+ bps = qFromBigEndian<quint16>(wave.bitsPerSample);
+ rate = qFromBigEndian<quint32>(wave.sampleRate);
+ channels = qFromBigEndian<quint16>(wave.numChannels);
+ } else {
+ bps = qFromLittleEndian<quint16>(wave.bitsPerSample);
+ rate = qFromLittleEndian<quint32>(wave.sampleRate);
+ channels = qFromLittleEndian<quint16>(wave.numChannels);
+ }
+
+ QAudioFormat::SampleFormat fmt = QAudioFormat::Unknown;
+ switch(bps) {
+ case 8:
+ fmt = QAudioFormat::UInt8;
+ break;
+ case 16:
+ fmt = QAudioFormat::Int16;
+ break;
+ case 24:
+ fmt = QAudioFormat::Int16;
+ break;
+ case 32:
+ fmt = QAudioFormat::Int32;
+ break;
+ }
+ if (fmt == QAudioFormat::Unknown || rate == 0 || channels == 0) {
+ parsingFailed();
+ return;
+ }
+
+ format.setSampleFormat(fmt);
+ format.setSampleRate(rate);
+ format.setChannelCount(channels);
+
+ state = QWaveDecoder::WaitingForDataState;
+ }
+ }
+
+ if (state == QWaveDecoder::WaitingForDataState) {
+ if (findChunk("data")) {
+ disconnect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData);
+
+ chunk descriptor;
+ device->read(reinterpret_cast<char *>(&descriptor), sizeof(chunk));
+ if (bigEndian)
+ descriptor.size = qFromBigEndian<quint32>(descriptor.size);
+ else
+ descriptor.size = qFromLittleEndian<quint32>(descriptor.size);
+
+ dataSize = descriptor.size; //means the data size from the data header, not the actual file size
+ if (!dataSize)
+ dataSize = device->size() - headerLength();
+
+ haveFormat = true;
+ connect(device, &QIODevice::readyRead, this, &QIODevice::readyRead);
+ emit formatKnown();
+
+ return;
+ }
+ }
+
+ // If we hit the end without finding data, it's a parsing error
+ if (device->atEnd()) {
+ parsingFailed();
+ }
+}
+
+bool QWaveDecoder::enoughDataAvailable()
+{
+ chunk descriptor;
+ if (!peekChunk(&descriptor, false))
+ return false;
+
+ // This is only called for the RIFF/RIFX header, before bigEndian is set,
+ // so we have to manually swizzle
+ if (qstrncmp(descriptor.id, "RIFX", 4) == 0)
+ descriptor.size = qFromBigEndian<quint32>(descriptor.size);
+ if (qstrncmp(descriptor.id, "RIFF", 4) == 0)
+ descriptor.size = qFromLittleEndian<quint32>(descriptor.size);
+
+ if (device->bytesAvailable() < qint64(sizeof(chunk) + descriptor.size))
+ return false;
+
+ return true;
+}
+
+bool QWaveDecoder::findChunk(const char *chunkId)
+{
+ chunk descriptor;
+
+ do {
+ if (!peekChunk(&descriptor))
+ return false;
+
+ if (qstrncmp(descriptor.id, chunkId, 4) == 0)
+ return true;
+
+ // A program reading a RIFF file can skip over any chunk whose chunk
+ // ID it doesn't recognize; it simply skips the number of bytes specified
+ // by ckSize plus the pad byte, if present. See Multimedia Programming
+ // Interface and Data Specifications 1.0. IBM / Microsoft. August 1991. pp. 10-11.
+ const quint32 sizeWithPad = descriptor.size + (descriptor.size & 1);
+
+ // It's possible that bytes->available() is less than the chunk size
+ // if it's corrupt.
+ junkToSkip = qint64(sizeof(chunk) + sizeWithPad);
+
+ // Skip the current amount
+ if (junkToSkip > 0)
+ discardBytes(junkToSkip);
+
+ // If we still have stuff left, just exit and try again later
+ // since we can't call peekChunk
+ if (junkToSkip > 0)
+ return false;
+
+ } while (device->bytesAvailable() > 0);
+
+ return false;
+}
+
+bool QWaveDecoder::peekChunk(chunk *pChunk, bool handleEndianness)
+{
+ if (device->bytesAvailable() < qint64(sizeof(chunk)))
+ return false;
+
+ if (!device->peek(reinterpret_cast<char *>(pChunk), sizeof(chunk)))
+ return false;
+
+ if (handleEndianness) {
+ if (bigEndian)
+ pChunk->size = qFromBigEndian<quint32>(pChunk->size);
+ else
+ pChunk->size = qFromLittleEndian<quint32>(pChunk->size);
+ }
+ return true;
+}
+
+void QWaveDecoder::discardBytes(qint64 numBytes)
+{
+ // Discards a number of bytes
+ // If the iodevice doesn't have this many bytes in it,
+ // remember how much more junk we have to skip.
+ if (device->isSequential()) {
+ QByteArray r = device->read(qMin(numBytes, qint64(16384))); // uggh, wasted memory, limit to a max of 16k
+ if (r.size() < numBytes)
+ junkToSkip = numBytes - r.size();
+ else
+ junkToSkip = 0;
+ } else {
+ quint64 origPos = device->pos();
+ device->seek(device->pos() + numBytes);
+ junkToSkip = origPos + numBytes - device->pos();
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwavedecoder.cpp"
diff --git a/src/multimedia/audio/qwavedecoder.h b/src/multimedia/audio/qwavedecoder.h
new file mode 100644
index 000000000..a96e731db
--- /dev/null
+++ b/src/multimedia/audio/qwavedecoder.h
@@ -0,0 +1,115 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef WAVEDECODER_H
+#define WAVEDECODER_H
+
+#include <QtCore/qiodevice.h>
+#include <QtMultimedia/qaudioformat.h>
+
+
+QT_BEGIN_NAMESPACE
+
+
+
+class Q_MULTIMEDIA_EXPORT QWaveDecoder : public QIODevice
+{
+ Q_OBJECT
+
+public:
+ explicit QWaveDecoder(QIODevice *device, QObject *parent = nullptr);
+ explicit QWaveDecoder(QIODevice *device, const QAudioFormat &format,
+ QObject *parent = nullptr);
+ ~QWaveDecoder();
+
+ QAudioFormat audioFormat() const;
+ QIODevice* getDevice();
+ int duration() const;
+ static qint64 headerLength();
+
+ bool open(QIODevice::OpenMode mode) override;
+ void close() override;
+ bool seek(qint64 pos) override;
+ qint64 pos() const override;
+ void setIODevice(QIODevice *device);
+ qint64 size() const override;
+ bool isSequential() const override;
+ qint64 bytesAvailable() const override;
+
+Q_SIGNALS:
+ void formatKnown();
+ void parsingError();
+
+private Q_SLOTS:
+ void handleData();
+
+private:
+ qint64 readData(char *data, qint64 maxlen) override;
+ qint64 writeData(const char *data, qint64 len) override;
+
+ bool writeHeader();
+ bool writeDataLength();
+ bool enoughDataAvailable();
+ bool findChunk(const char *chunkId);
+ void discardBytes(qint64 numBytes);
+ void parsingFailed();
+
+ enum State {
+ InitialState,
+ WaitingForFormatState,
+ WaitingForDataState
+ };
+
+ struct chunk
+ {
+ char id[4]; // A four-character code that identifies the representation of the chunk data
+ // padded on the right with blank characters (ASCII 32)
+ quint32 size; // Does not include the size of the id or size fields or the pad byte at the end of payload
+ };
+
+ bool peekChunk(chunk* pChunk, bool handleEndianness = true);
+
+ struct RIFFHeader
+ {
+ chunk descriptor;
+ char type[4];
+ };
+ struct WAVEHeader
+ {
+ chunk descriptor;
+ quint16 audioFormat;
+ quint16 numChannels;
+ quint32 sampleRate;
+ quint32 byteRate;
+ quint16 blockAlign;
+ quint16 bitsPerSample;
+ };
+
+ struct DATAHeader
+ {
+ chunk descriptor;
+ };
+
+ struct CombinedHeader
+ {
+ RIFFHeader riff;
+ WAVEHeader wave;
+ DATAHeader data;
+ };
+ static const int HeaderLength = sizeof(CombinedHeader);
+
+ bool haveFormat = false;
+ bool haveHeader = false;
+ qint64 dataSize = 0;
+ QIODevice *device = nullptr;
+ QAudioFormat format;
+ State state = InitialState;
+ quint32 junkToSkip = 0;
+ bool bigEndian = false;
+ bool byteSwap = false;
+ int bps = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // WAVEDECODER_H
diff --git a/src/multimedia/audio/qwavedecoder_p.cpp b/src/multimedia/audio/qwavedecoder_p.cpp
deleted file mode 100644
index 7e10a0f10..000000000
--- a/src/multimedia/audio/qwavedecoder_p.cpp
+++ /dev/null
@@ -1,312 +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$
-**
-****************************************************************************/
-
-#include "qwavedecoder_p.h"
-
-#include <QtCore/qtimer.h>
-#include <QtCore/qendian.h>
-
-QT_BEGIN_NAMESPACE
-
-QWaveDecoder::QWaveDecoder(QIODevice *s, QObject *parent):
- QIODevice(parent),
- haveFormat(false),
- dataSize(0),
- source(s),
- state(QWaveDecoder::InitialState),
- junkToSkip(0),
- bigEndian(false)
-{
- open(QIODevice::ReadOnly | QIODevice::Unbuffered);
-
- if (enoughDataAvailable())
- QTimer::singleShot(0, this, SLOT(handleData()));
- else
- connect(source, SIGNAL(readyRead()), SLOT(handleData()));
-}
-
-QWaveDecoder::~QWaveDecoder()
-{
-}
-
-QAudioFormat QWaveDecoder::audioFormat() const
-{
- return format;
-}
-
-int QWaveDecoder::duration() const
-{
- return size() * 1000 / (format.sampleSize() / 8) / format.channelCount() / format.sampleRate();
-}
-
-qint64 QWaveDecoder::size() const
-{
- return haveFormat ? dataSize : 0;
-}
-
-bool QWaveDecoder::isSequential() const
-{
- return source->isSequential();
-}
-
-qint64 QWaveDecoder::bytesAvailable() const
-{
- return haveFormat ? source->bytesAvailable() : 0;
-}
-
-qint64 QWaveDecoder::readData(char *data, qint64 maxlen)
-{
- return haveFormat ? source->read(data, maxlen) : 0;
-}
-
-qint64 QWaveDecoder::writeData(const char *data, qint64 len)
-{
- Q_UNUSED(data);
- Q_UNUSED(len);
-
- return -1;
-}
-
-void QWaveDecoder::parsingFailed()
-{
- Q_ASSERT(source);
- source->disconnect(SIGNAL(readyRead()), this, SLOT(handleData()));
- emit parsingError();
-}
-
-void QWaveDecoder::handleData()
-{
- // As a special "state", if we have junk to skip, we do
- if (junkToSkip > 0) {
- discardBytes(junkToSkip); // this also updates junkToSkip
-
- // If we couldn't skip all the junk, return
- if (junkToSkip > 0) {
- // We might have run out
- if (source->atEnd())
- parsingFailed();
- return;
- }
- }
-
- if (state == QWaveDecoder::InitialState) {
- if (source->bytesAvailable() < qint64(sizeof(RIFFHeader)))
- return;
-
- RIFFHeader riff;
- source->read(reinterpret_cast<char *>(&riff), sizeof(RIFFHeader));
-
- // RIFF = little endian RIFF, RIFX = big endian RIFF
- if (((qstrncmp(riff.descriptor.id, "RIFF", 4) != 0) && (qstrncmp(riff.descriptor.id, "RIFX", 4) != 0))
- || qstrncmp(riff.type, "WAVE", 4) != 0) {
- parsingFailed();
- return;
- } else {
- state = QWaveDecoder::WaitingForFormatState;
- if (qstrncmp(riff.descriptor.id, "RIFX", 4) == 0)
- bigEndian = true;
- else
- bigEndian = false;
- }
- }
-
- if (state == QWaveDecoder::WaitingForFormatState) {
- if (findChunk("fmt ")) {
- chunk descriptor;
- peekChunk(&descriptor);
-
- quint32 rawChunkSize = descriptor.size + sizeof(chunk);
- if (source->bytesAvailable() < qint64(rawChunkSize))
- return;
-
- WAVEHeader wave;
- source->read(reinterpret_cast<char *>(&wave), sizeof(WAVEHeader));
-
- if (rawChunkSize > sizeof(WAVEHeader))
- discardBytes(rawChunkSize - sizeof(WAVEHeader));
-
- // Swizzle this
- if (bigEndian) {
- wave.audioFormat = qFromBigEndian<quint16>(wave.audioFormat);
- } else {
- wave.audioFormat = qFromLittleEndian<quint16>(wave.audioFormat);
- }
-
- if (wave.audioFormat != 0 && wave.audioFormat != 1) {
- // 32bit wave files have format == 0xFFFE (WAVE_FORMAT_EXTENSIBLE).
- // but don't support them at the moment.
- parsingFailed();
- return;
- } else {
- format.setCodec(QLatin1String("audio/pcm"));
-
- if (bigEndian) {
- int bps = qFromBigEndian<quint16>(wave.bitsPerSample);
-
- format.setSampleType(bps == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt);
- format.setByteOrder(QAudioFormat::BigEndian);
- format.setSampleRate(qFromBigEndian<quint32>(wave.sampleRate));
- format.setSampleSize(bps);
- format.setChannelCount(qFromBigEndian<quint16>(wave.numChannels));
- } else {
- int bps = qFromLittleEndian<quint16>(wave.bitsPerSample);
-
- format.setSampleType(bps == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt);
- format.setByteOrder(QAudioFormat::LittleEndian);
- format.setSampleRate(qFromLittleEndian<quint32>(wave.sampleRate));
- format.setSampleSize(bps);
- format.setChannelCount(qFromLittleEndian<quint16>(wave.numChannels));
- }
-
- state = QWaveDecoder::WaitingForDataState;
- }
- }
- }
-
- if (state == QWaveDecoder::WaitingForDataState) {
- if (findChunk("data")) {
- source->disconnect(SIGNAL(readyRead()), this, SLOT(handleData()));
-
- chunk descriptor;
- source->read(reinterpret_cast<char *>(&descriptor), sizeof(chunk));
- if (bigEndian)
- descriptor.size = qFromBigEndian<quint32>(descriptor.size);
- else
- descriptor.size = qFromLittleEndian<quint32>(descriptor.size);
-
- dataSize = descriptor.size;
-
- haveFormat = true;
- connect(source, SIGNAL(readyRead()), SIGNAL(readyRead()));
- emit formatKnown();
-
- return;
- }
- }
-
- // If we hit the end without finding data, it's a parsing error
- if (source->atEnd()) {
- parsingFailed();
- }
-}
-
-bool QWaveDecoder::enoughDataAvailable()
-{
- chunk descriptor;
- if (!peekChunk(&descriptor, false))
- return false;
-
- // This is only called for the RIFF/RIFX header, before bigEndian is set,
- // so we have to manually swizzle
- if (qstrncmp(descriptor.id, "RIFX", 4) == 0)
- descriptor.size = qFromBigEndian<quint32>(descriptor.size);
- if (qstrncmp(descriptor.id, "RIFF", 4) == 0)
- descriptor.size = qFromLittleEndian<quint32>(descriptor.size);
-
- if (source->bytesAvailable() < qint64(sizeof(chunk) + descriptor.size))
- return false;
-
- return true;
-}
-
-bool QWaveDecoder::findChunk(const char *chunkId)
-{
- chunk descriptor;
-
- do {
- if (!peekChunk(&descriptor))
- return false;
-
- if (qstrncmp(descriptor.id, chunkId, 4) == 0)
- return true;
-
- // It's possible that bytes->available() is less than the chunk size
- // if it's corrupt.
- junkToSkip = qint64(sizeof(chunk) + descriptor.size);
-
- // Skip the current amount
- if (junkToSkip > 0)
- discardBytes(junkToSkip);
-
- // If we still have stuff left, just exit and try again later
- // since we can't call peekChunk
- if (junkToSkip > 0)
- return false;
-
- } while (source->bytesAvailable() > 0);
-
- return false;
-}
-
-bool QWaveDecoder::peekChunk(chunk *pChunk, bool handleEndianness)
-{
- if (source->bytesAvailable() < qint64(sizeof(chunk)))
- return false;
-
- source->peek(reinterpret_cast<char *>(pChunk), sizeof(chunk));
- if (handleEndianness) {
- if (bigEndian)
- pChunk->size = qFromBigEndian<quint32>(pChunk->size);
- else
- pChunk->size = qFromLittleEndian<quint32>(pChunk->size);
- }
- return true;
-}
-
-void QWaveDecoder::discardBytes(qint64 numBytes)
-{
- // Discards a number of bytes
- // If the iodevice doesn't have this many bytes in it,
- // remember how much more junk we have to skip.
- if (source->isSequential()) {
- QByteArray r = source->read(qMin(numBytes, qint64(16384))); // uggh, wasted memory, limit to a max of 16k
- if (r.size() < numBytes)
- junkToSkip = numBytes - r.size();
- else
- junkToSkip = 0;
- } else {
- quint64 origPos = source->pos();
- source->seek(source->pos() + numBytes);
- junkToSkip = origPos + numBytes - source->pos();
- }
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qwavedecoder_p.cpp"
diff --git a/src/multimedia/audio/qwavedecoder_p.h b/src/multimedia/audio/qwavedecoder_p.h
deleted file mode 100644
index 3be0dbaa6..000000000
--- a/src/multimedia/audio/qwavedecoder_p.h
+++ /dev/null
@@ -1,133 +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 WAVEDECODER_H
-#define WAVEDECODER_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of other Qt classes. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/qiodevice.h>
-#include <qaudioformat.h>
-
-
-QT_BEGIN_NAMESPACE
-
-
-
-class QWaveDecoder : public QIODevice
-{
- Q_OBJECT
-
-public:
- explicit QWaveDecoder(QIODevice *source, QObject *parent = nullptr);
- ~QWaveDecoder();
-
- QAudioFormat audioFormat() const;
- int duration() const;
-
- qint64 size() const override;
- bool isSequential() const override;
- qint64 bytesAvailable() const override;
-
-Q_SIGNALS:
- void formatKnown();
- void parsingError();
-
-private Q_SLOTS:
- void handleData();
-
-private:
- qint64 readData(char *data, qint64 maxlen) override;
- qint64 writeData(const char *data, qint64 len) override;
-
- bool enoughDataAvailable();
- bool findChunk(const char *chunkId);
- void discardBytes(qint64 numBytes);
- void parsingFailed();
-
- enum State {
- InitialState,
- WaitingForFormatState,
- WaitingForDataState
- };
-
- struct chunk
- {
- char id[4];
- quint32 size;
- };
- bool peekChunk(chunk* pChunk, bool handleEndianness = true);
-
- struct RIFFHeader
- {
- chunk descriptor;
- char type[4];
- };
- struct WAVEHeader
- {
- chunk descriptor;
- quint16 audioFormat;
- quint16 numChannels;
- quint32 sampleRate;
- quint32 byteRate;
- quint16 blockAlign;
- quint16 bitsPerSample;
- };
-
- bool haveFormat;
- qint64 dataSize;
- QAudioFormat format;
- QIODevice *source;
- State state;
- quint32 junkToSkip;
- bool bigEndian;
-};
-
-QT_END_NAMESPACE
-
-#endif // WAVEDECODER_H