diff options
Diffstat (limited to 'src/multimedia/audio/qaudiobuffer.cpp')
-rw-r--r-- | src/multimedia/audio/qaudiobuffer.cpp | 511 |
1 files changed, 116 insertions, 395 deletions
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 |