Clarify API/docs by introducing the audio "frame", and add helpers.

The frame is the interleaved set of one sample for each channel.  Add
some docs and some methods that make working with samples a bit more
convenient.

Adjusted QAudioBuffer to use these helper functions and terminology.

Change-Id: I96db48e659561972d6de2aa19893d29f9a828cd3
Reviewed-by: Dmytro Poplavskiy <dmytro.poplavskiy@nokia.com>
This commit is contained in:
Michael Goddard
2012-07-10 14:26:58 +10:00
committed by Qt by Nokia
parent 68079a5e22
commit 4d13a15bae
7 changed files with 318 additions and 81 deletions

View File

@@ -106,18 +106,18 @@ public:
// Private class to go in .cpp file
class QMemoryAudioBufferProvider : public QAbstractAudioBuffer {
public:
QMemoryAudioBufferProvider(const void *data, int sampleCount, const QAudioFormat &format, qint64 startTime)
QMemoryAudioBufferProvider(const void *data, int frameCount, const QAudioFormat &format, qint64 startTime)
: mStartTime(startTime)
, mSampleCount(sampleCount)
, mFrameCount(frameCount)
, mFormat(format)
{
int numBytes = (sampleCount * format.sampleSize()) / 8;
int numBytes = format.bytesForFrames(frameCount);
if (numBytes > 0) {
mBuffer = malloc(numBytes);
if (!mBuffer) {
// OOM, if that's likely
mStartTime = -1;
mSampleCount = 0;
mFrameCount = 0;
mFormat = QAudioFormat();
} else {
// Allocated, see if we have data to copy
@@ -149,19 +149,19 @@ public:
void release() {delete this;}
QAudioFormat format() const {return mFormat;}
qint64 startTime() const {return mStartTime;}
int sampleCount() const {return mSampleCount;}
int frameCount() const {return mFrameCount;}
void *constData() const {return mBuffer;}
void *writableData() {return mBuffer;}
QAbstractAudioBuffer *clone() const
{
return new QMemoryAudioBufferProvider(mBuffer, mSampleCount, mFormat, mStartTime);
return new QMemoryAudioBufferProvider(mBuffer, mFrameCount, mFormat, mStartTime);
}
void *mBuffer;
qint64 mStartTime;
int mSampleCount;
int mFrameCount;
QAudioFormat mFormat;
};
@@ -176,7 +176,7 @@ QAudioBufferPrivate *QAudioBufferPrivate::clone()
QAbstractAudioBuffer *abuf = mProvider->clone();
if (!abuf) {
abuf = new QMemoryAudioBufferProvider(mProvider->constData(), mProvider->sampleCount(), mProvider->format(), mProvider->startTime());
abuf = new QMemoryAudioBufferProvider(mProvider->constData(), mProvider->frameCount(), mProvider->format(), mProvider->startTime());
}
if (abuf) {
@@ -236,7 +236,7 @@ QAudioBuffer::QAudioBuffer(const QAudioBuffer &other)
and sizes of the samples are interpreted from the \a data.
If the supplied \a data is not an integer multiple of the
calculated sample size, the excess data will not be used.
calculated frame size, the excess data will not be used.
This audio buffer will copy the contents of \a data.
@@ -247,25 +247,25 @@ QAudioBuffer::QAudioBuffer(const QAudioBuffer &other)
QAudioBuffer::QAudioBuffer(const QByteArray &data, const QAudioFormat &format, qint64 startTime)
{
if (format.isValid()) {
int sampleCount = (data.size() * 8) / format.sampleSize(); // truncate
d = new QAudioBufferPrivate(new QMemoryAudioBufferProvider(data.constData(), sampleCount, format, startTime));
int frameCount = format.framesForBytes(data.size());
d = new QAudioBufferPrivate(new QMemoryAudioBufferProvider(data.constData(), frameCount, format, startTime));
} else
d = 0;
}
/*!
Creates a new audio buffer with space for \a numSamples samples of
the given \a format. The samples will be initialized to the default
for the format.
Creates a new audio buffer with space for \a numFrames frames of
the given \a format. The individual samples will be initialized
to the default for the format.
\a startTime (in microseconds) indicates when this buffer
starts in the stream.
If this buffer is not part of a stream, set it to -1.
*/
QAudioBuffer::QAudioBuffer(int numSamples, const QAudioFormat &format, qint64 startTime)
QAudioBuffer::QAudioBuffer(int numFrames, const QAudioFormat &format, qint64 startTime)
{
if (format.isValid())
d = new QAudioBufferPrivate(new QMemoryAudioBufferProvider(0, numSamples, format, startTime));
d = new QAudioBufferPrivate(new QMemoryAudioBufferProvider(0, numFrames, format, startTime));
else
d = 0;
}
@@ -292,13 +292,13 @@ QAudioBuffer::~QAudioBuffer()
/*!
Returns true if this is a valid buffer. A valid buffer
has more than zero samples in it and a valid format.
has more than zero frames in it and a valid format.
*/
bool QAudioBuffer::isValid() const
{
if (!d || !d->mProvider)
return false;
return d->mProvider->format().isValid() && (d->mProvider->sampleCount() > 0);
return d->mProvider->format().isValid() && (d->mProvider->frameCount() > 0);
}
/*!
@@ -306,7 +306,7 @@ bool QAudioBuffer::isValid() const
Several properties of this format influence how
the \l duration() or \l byteCount() are calculated
from the \l sampleCount().
from the \l frameCount().
*/
QAudioFormat QAudioBuffer::format() const
{
@@ -315,6 +315,19 @@ QAudioFormat QAudioBuffer::format() const
return d->mProvider->format();
}
/*!
Returns the number of complete audio frames in this buffer.
An audio frame is an interleaved set of one sample per channel
for the same instant in time.
*/
int QAudioBuffer::frameCount() const
{
if (!isValid())
return 0;
return d->mProvider->frameCount();
}
/*!
Returns the number of samples in this buffer.
@@ -323,12 +336,15 @@ QAudioFormat QAudioBuffer::format() const
that a stereo buffer with 1000 samples in total will
have 500 left samples and 500 right samples (interleaved),
and this function will return 1000.
*/
\sa frameCount()
*/
int QAudioBuffer::sampleCount() const
{
if (!isValid())
return 0;
return d->mProvider->sampleCount();
return frameCount() * format().channelCount();
}
/*!
@@ -337,21 +353,17 @@ int QAudioBuffer::sampleCount() const
int QAudioBuffer::byteCount() const
{
const QAudioFormat f(format());
return (f.sampleSize() * sampleCount()) / 8; // sampleSize is in bits
return format().bytesForFrames(frameCount());
}
/*!
Returns the duration of audio in this buffer, in microseconds.
This depends on the /l format(), and the \l sampleCount().
This depends on the /l format(), and the \l frameCount().
*/
qint64 QAudioBuffer::duration() const
{
int divisor = format().sampleRate() * format().channelCount();
if (divisor > 0)
return (sampleCount() * 1000000LL) / divisor;
else
return 0;
return format().durationForFrames(frameCount());
}
/*!
@@ -471,7 +483,7 @@ void *QAudioBuffer::data()
}
// Wasn't writable, so turn it into a memory provider
QAbstractAudioBuffer *memBuffer = new QMemoryAudioBufferProvider(constData(), sampleCount(), format(), startTime());
QAbstractAudioBuffer *memBuffer = new QMemoryAudioBufferProvider(constData(), frameCount(), format(), startTime());
if (memBuffer) {
d->mProvider->release();
@@ -487,35 +499,35 @@ void *QAudioBuffer::data()
// Template helper classes worth documenting
/*!
\class QAudioBuffer::StereoSampleDefault
\class QAudioBuffer::StereoFrameDefault
\internal
Just a trait class for the default value.
*/
/*!
\class QAudioBuffer::StereoSample
\brief The StereoSample class provides a simple wrapper for a stereo audio sample.
\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. This is most useful when used with the templatized
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 structure has \e left and \e right members for accessing individual channel data.
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 *sample = buffer->data<QAudioBuffer::S16U>();
for (int i=0; i < buffer->sampleCount() / 2; i++) {
qSwap(sample[i].left, sample[i].right);
QAudioBuffer::S16U *frames = buffer->data<QAudioBuffer::S16U>();
for (int i=0; i < buffer->frameCount(); i++) {
qSwap(frames[i].left, frames[i].right);
}
\endcode
@@ -523,77 +535,77 @@ void *QAudioBuffer::data()
*/
/*!
\fn QAudioBuffer::StereoSample::StereoSample()
\fn QAudioBuffer::StereoFrame::StereoFrame()
Constructs a new sample with the "silent" value for this
Constructs a new frame with the "silent" value for this
sample format (0 for signed formats and floats, 0x8* for unsigned formats).
*/
/*!
\fn QAudioBuffer::StereoSample::StereoSample(T leftSample, T rightSample)
\fn QAudioBuffer::StereoFrame::StereoFrame(T leftSample, T rightSample)
Constructs a new sample with the supplied \a leftSample and \a rightSample values.
Constructs a new frame with the supplied \a leftSample and \a rightSample values.
*/
/*!
\fn QAudioBuffer::StereoSample::operator=(const StereoSample &other)
\fn QAudioBuffer::StereoFrame::operator=(const StereoFrame &other)
Assigns \a other to this sample.
Assigns \a other to this frame.
*/
/*!
\fn QAudioBuffer::StereoSample::average() const
\fn QAudioBuffer::StereoFrame::average() const
Returns the arithmetic average of the left and right samples.
*/
/*! \fn QAudioBuffer::StereoSample::clear()
/*! \fn QAudioBuffer::StereoFrame::clear()
Sets the values of this sample to the "silent" value.
Sets the values of this frame to the "silent" value.
*/
/*!
\variable QAudioBuffer::StereoSample::left
\variable QAudioBuffer::StereoFrame::left
\brief the left sample
*/
/*!
\variable QAudioBuffer::StereoSample::right
\variable QAudioBuffer::StereoFrame::right
\brief the right sample
*/
/*!
\typedef QAudioBuffer::S8U
\relates QAudioBuffer::StereoSample
\relates QAudioBuffer::StereoFrame
This is a predefined specialization for an unsigned stereo 8 bit sample. Each
channel is an \e {unsigned char}.
*/
/*!
\typedef QAudioBuffer::S8S
\relates QAudioBuffer::StereoSample
\relates QAudioBuffer::StereoFrame
This is a predefined specialization for a signed stereo 8 bit sample. Each
channel is a \e {signed char}.
*/
/*!
\typedef QAudioBuffer::S16U
\relates QAudioBuffer::StereoSample
\relates QAudioBuffer::StereoFrame
This is a predefined specialization for an unsigned stereo 16 bit sample. Each
channel is an \e {unsigned short}.
*/
/*!
\typedef QAudioBuffer::S16S
\relates QAudioBuffer::StereoSample
\relates QAudioBuffer::StereoFrame
This is a predefined specialization for a signed stereo 16 bit sample. Each
channel is a \e {signed short}.
*/
/*!
\typedef QAudioBuffer::S32F
\relates QAudioBuffer::StereoSample
\relates QAudioBuffer::StereoFrame
This is a predefined specialization for an 32 bit float sample. Each
channel is a \e float.

View File

@@ -65,7 +65,7 @@ public:
QAudioBuffer(QAbstractAudioBuffer *provider);
QAudioBuffer(const QAudioBuffer &other);
QAudioBuffer(const QByteArray &data, const QAudioFormat &format, qint64 startTime = -1);
QAudioBuffer(int numSamples, const QAudioFormat &format, qint64 startTime = -1); // Initialized to empty
QAudioBuffer(int numFrames, const QAudioFormat &format, qint64 startTime = -1); // Initialized to empty
QAudioBuffer& operator=(const QAudioBuffer &other);
@@ -75,6 +75,7 @@ public:
QAudioFormat format() const;
int frameCount() const;
int sampleCount() const;
int byteCount() const;
@@ -93,23 +94,23 @@ public:
void *data(); // detaches
// Structures for easier access to stereo data
template <typename T> struct StereoSampleDefault { enum { Default = 0 }; };
template <typename T> struct StereoFrameDefault { enum { Default = 0 }; };
template <typename T> struct StereoSample {
template <typename T> struct StereoFrame {
StereoSample()
: left(T(StereoSampleDefault<T>::Default))
, right(T(StereoSampleDefault<T>::Default))
StereoFrame()
: left(T(StereoFrameDefault<T>::Default))
, right(T(StereoFrameDefault<T>::Default))
{
}
StereoSample(T leftSample, T rightSample)
StereoFrame(T leftSample, T rightSample)
: left(leftSample)
, right(rightSample)
{
}
StereoSample& operator=(const StereoSample &other)
StereoFrame& operator=(const StereoFrame &other)
{
// Two straight assigns is probably
// cheaper than a conditional check on
@@ -123,14 +124,14 @@ public:
T right;
T average() const {return (left + right) / 2;}
void clear() {left = right = T(StereoSampleDefault<T>::Default);}
void clear() {left = right = T(StereoFrameDefault<T>::Default);}
};
typedef StereoSample<unsigned char> S8U;
typedef StereoSample<signed char> S8S;
typedef StereoSample<unsigned short> S16U;
typedef StereoSample<signed short> S16S;
typedef StereoSample<float> S32F;
typedef StereoFrame<unsigned char> S8U;
typedef StereoFrame<signed char> S8S;
typedef StereoFrame<unsigned short> S16U;
typedef StereoFrame<signed short> S16S;
typedef StereoFrame<float> S32F;
template <typename T> const T* constData() const {
return static_cast<const T*>(constData());
@@ -145,8 +146,8 @@ private:
QAudioBufferPrivate *d;
};
template <> struct QAudioBuffer::StereoSampleDefault<unsigned char> { enum { Default = 128 }; };
template <> struct QAudioBuffer::StereoSampleDefault<unsigned short> { enum { Default = 32768 }; };
template <> struct QAudioBuffer::StereoFrameDefault<unsigned char> { enum { Default = 128 }; };
template <> struct QAudioBuffer::StereoFrameDefault<unsigned short> { enum { Default = 32768 }; };
QT_END_NAMESPACE

View File

@@ -66,7 +66,7 @@ public:
// Format related
virtual QAudioFormat format() const = 0;
virtual qint64 startTime() const = 0;
virtual int sampleCount() const = 0;
virtual int frameCount() const = 0;
// R/O Data
virtual void *constData() const = 0;

View File

@@ -146,7 +146,8 @@ public:
This class is typically used in conjunction with QAudioInput or
QAudioOutput to allow you to specify the parameters of the audio
stream being read or written.
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
@@ -154,6 +155,11 @@ public:
the parameters yourself. See the \l QAudioDeviceInfo 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.
One sample for each channel for the same instant in time is referred
to as a frame in QtMultimedia (and other places).
*/
/*!
@@ -282,6 +288,8 @@ void QAudioFormat::setSampleSize(int sampleSize)
/*!
Returns the current sample size value, in bits.
\sa bytesPerFrame()
*/
int QAudioFormat::sampleSize() const
{
@@ -344,6 +352,108 @@ QAudioFormat::SampleType QAudioFormat::sampleType() const
return d->sampleType;
}
/*!
Returns the number of bytes required for this audio format for \a duration 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
sampleRate().
\sa durationForBytes()
*/
qint32 QAudioFormat::bytesForDuration(qint64 duration) const
{
return bytesPerFrame() * framesForDuration(duration);
}
/*!
Returns the number of microseconds represented by \a bytes in this format.
Returns 0 if this format is not valid.
Note that some rounding may occur if \a bytes is not an exact multiple
of the number of bytes per frame.
\sa bytesForDuration()
*/
qint64 QAudioFormat::durationForBytes(qint32 bytes) const
{
if (!isValid() || bytes <= 0)
return 0;
// We round the byte count to ensure whole frames
return qint64(1000000LL * (bytes / bytesPerFrame())) / sampleRate();
}
/*!
Returns the number of bytes required for \a frameCount frames of this format.
Returns 0 if this format is not valid.
\sa bytesForDuration()
*/
qint32 QAudioFormat::bytesForFrames(qint32 frameCount) const
{
return frameCount * bytesPerFrame();
}
/*!
Returns the number of frames represented by \a byteCount in this format.
Note that some rounding may occur if \a byteCount is not an exact multiple
of the number of bytes per frame.
Each frame has one sample per channel.
\sa framesForDuration()
*/
qint32 QAudioFormat::framesForBytes(qint32 byteCount) const
{
int size = bytesPerFrame();
if (size > 0)
return byteCount / size;
return 0;
}
/*!
Returns the number of frames required to represent \a duration microseconds in this format.
Note that some rounding may occur if \a duration is not an exact fraction of the
\l sampleRate().
*/
qint32 QAudioFormat::framesForDuration(qint64 duration) const
{
if (!isValid())
return 0;
return qint32((duration * sampleRate()) / 1000000LL);
}
/*!
Return the number of microseconds represented by \a frameCount frames in this format.
*/
qint64 QAudioFormat::durationForFrames(qint32 frameCount) const
{
if (!isValid() || frameCount <= 0)
return 0;
return (frameCount * 1000000LL) / sampleRate();
}
/*!
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

View File

@@ -91,6 +91,18 @@ public:
void setSampleType(QAudioFormat::SampleType sampleType);
QAudioFormat::SampleType sampleType() const;
// 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;
private:
QSharedDataPointer<QAudioFormatPrivate> d;
};

View File

@@ -76,11 +76,11 @@ tst_QAudioBuffer::tst_QAudioBuffer()
mFormat.setSampleSize(16);
mFormat.setSampleType(QAudioFormat::UnSignedInt);
mFormat.setSampleRate(10000);
mFormat.setCodec("audio/x-pcm"); // XXX this is not a good fit?
mFormat.setCodec("audio/pcm");
QByteArray b(4000, 0x80);
mNull = new QAudioBuffer;
mEmpty = new QAudioBuffer(1000, mFormat); // 1000 samples of 16 bits -> 2KB
mEmpty = new QAudioBuffer(500, mFormat); // 500 stereo frames of 16 bits -> 2KB
mFromArray = new QAudioBuffer(b, mFormat);
}
@@ -102,6 +102,7 @@ void tst_QAudioBuffer::ctors()
QCOMPARE(mNull->duration(), 0LL);
QCOMPARE(mNull->byteCount(), 0);
QCOMPARE(mNull->sampleCount(), 0);
QCOMPARE(mNull->frameCount(), 0);
QCOMPARE(mNull->startTime(), -1LL);
// Empty buffer
@@ -110,6 +111,7 @@ void tst_QAudioBuffer::ctors()
QVERIFY(mEmpty->data() != 0);
QVERIFY(((const QAudioBuffer*)mEmpty)->data() != 0);
QCOMPARE(mEmpty->sampleCount(), 1000);
QCOMPARE(mEmpty->frameCount(), 500);
QCOMPARE(mEmpty->duration(), 50000LL);
QCOMPARE(mEmpty->byteCount(), 2000);
QCOMPARE(mEmpty->startTime(), -1LL);
@@ -123,6 +125,7 @@ void tst_QAudioBuffer::ctors()
QCOMPARE(mFromArray->duration(), 100000LL);
QCOMPARE(mFromArray->byteCount(), 4000);
QCOMPARE(mFromArray->sampleCount(), 2000);
QCOMPARE(mFromArray->frameCount(), 1000);
QCOMPARE(mFromArray->startTime(), -1LL);
@@ -135,6 +138,7 @@ void tst_QAudioBuffer::ctors()
QCOMPARE(badFormat.duration(), 0LL);
QCOMPARE(badFormat.byteCount(), 0);
QCOMPARE(badFormat.sampleCount(), 0);
QCOMPARE(badFormat.frameCount(), 0);
QCOMPARE(badFormat.startTime(), -1LL);
QAudioBuffer badArray(QByteArray(), mFormat);
@@ -145,6 +149,7 @@ void tst_QAudioBuffer::ctors()
QCOMPARE(badArray.duration(), 0LL);
QCOMPARE(badArray.byteCount(), 0);
QCOMPARE(badArray.sampleCount(), 0);
QCOMPARE(badArray.frameCount(), 0);
QCOMPARE(badArray.startTime(), -1LL);
QAudioBuffer badBoth = QAudioBuffer(QByteArray(), QAudioFormat());
@@ -155,12 +160,13 @@ void tst_QAudioBuffer::ctors()
QCOMPARE(badBoth.duration(), 0LL);
QCOMPARE(badBoth.byteCount(), 0);
QCOMPARE(badBoth.sampleCount(), 0);
QCOMPARE(badBoth.frameCount(), 0);
QCOMPARE(badBoth.startTime(), -1LL);
}
void tst_QAudioBuffer::assign()
{
// Needs strong behaviour definition
// TODO Needs strong behaviour definition
}
void tst_QAudioBuffer::constData() const
@@ -245,7 +251,8 @@ void tst_QAudioBuffer::durations()
{
QFETCH(int, channelCount);
QFETCH(int, sampleSize);
QFETCH(int, sampleCount);
QFETCH(int, frameCount);
int sampleCount = frameCount * channelCount;
QFETCH(QAudioFormat::SampleType, sampleType);
QFETCH(int, sampleRate);
QFETCH(qint64, duration);
@@ -256,10 +263,11 @@ void tst_QAudioBuffer::durations()
f.setSampleType(sampleType);
f.setSampleSize(sampleSize);
f.setSampleRate(sampleRate);
f.setCodec("audio/x-pcm"); // XXX this is not a good fit?
f.setCodec("audio/pcm");
QAudioBuffer b(sampleCount, f);
QAudioBuffer b(frameCount, f);
QCOMPARE(b.frameCount(), frameCount);
QCOMPARE(b.sampleCount(), sampleCount);
QCOMPARE(b.duration(), duration);
QCOMPARE(b.byteCount(), byteCount);
@@ -269,7 +277,7 @@ void tst_QAudioBuffer::durations_data()
{
QTest::addColumn<int>("channelCount");
QTest::addColumn<int>("sampleSize");
QTest::addColumn<int>("sampleCount");
QTest::addColumn<int>("frameCount");
QTest::addColumn<QAudioFormat::SampleType>("sampleType");
QTest::addColumn<int>("sampleRate");
QTest::addColumn<qint64>("duration");
@@ -278,11 +286,11 @@ void tst_QAudioBuffer::durations_data()
QTest::newRow("M8_2000_8K") << 1 << 8 << 2000 << QAudioFormat::UnSignedInt << 8000 << 250000LL << 2000;
QTest::newRow("M8_1000_4K") << 1 << 8 << 1000 << QAudioFormat::UnSignedInt << 4000 << 250000LL << 1000;
QTest::newRow("S8_1000_8K") << 2 << 8 << 1000 << QAudioFormat::UnSignedInt << 8000 << 62500LL << 1000;
QTest::newRow("S8_1000_8K") << 2 << 8 << 500 << QAudioFormat::UnSignedInt << 8000 << 62500LL << 1000;
QTest::newRow("SF_1000_8K") << 2 << 32 << 1000 << QAudioFormat::Float << 8000 << 62500LL << 4000;
QTest::newRow("SF_1000_8K") << 2 << 32 << 500 << QAudioFormat::Float << 8000 << 62500LL << 4000;
QTest::newRow("4x128_1000_16K") << 4 << 128 << 1000 << QAudioFormat::SignedInt << 16000 << 15625LL << 16000;
QTest::newRow("4x128_1000_16K") << 4 << 128 << 250 << QAudioFormat::SignedInt << 16000 << 15625LL << 16000;
}
void tst_QAudioBuffer::stereoSample()

View File

@@ -67,6 +67,9 @@ private slots:
void checkSampleRate();
void checkChannelCount();
void checkSizes();
void checkSizes_data();
void debugOperator();
void debugOperator_data();
};
@@ -222,6 +225,97 @@ void tst_QAudioFormat::checkChannelCount()
QVERIFY(audioFormat.channelCount() == 5);
}
void tst_QAudioFormat::checkSizes()
{
QFETCH(QAudioFormat, format);
QFETCH(int, frameSize);
QFETCH(int, byteCount);
QFETCH(int, frameCount);
QFETCH(qint64, durationForByte);
QFETCH(int, byteForFrame);
QFETCH(qint64, durationForByteForDuration);
QFETCH(int, byteForDuration);
QFETCH(int, framesForDuration);
QCOMPARE(format.bytesPerFrame(), frameSize);
// Byte input
QCOMPARE(format.framesForBytes(byteCount), frameCount);
QCOMPARE(format.durationForBytes(byteCount), durationForByte);
// Framecount input
QCOMPARE(format.bytesForFrames(frameCount), byteForFrame);
QCOMPARE(format.durationForFrames(frameCount), durationForByte);
// Duration input
QCOMPARE(format.bytesForDuration(durationForByteForDuration), byteForDuration);
QCOMPARE(format.framesForDuration(durationForByte), frameCount);
QCOMPARE(format.framesForDuration(durationForByteForDuration), framesForDuration);
}
void tst_QAudioFormat::checkSizes_data()
{
QTest::addColumn<QAudioFormat>("format");
QTest::addColumn<int>("frameSize");
QTest::addColumn<int>("byteCount");
QTest::addColumn<qint64>("durationForByte");
QTest::addColumn<int>("frameCount"); // output of sampleCountforByte, input for byteForFrame
QTest::addColumn<int>("byteForFrame");
QTest::addColumn<qint64>("durationForByteForDuration"); // input for byteForDuration
QTest::addColumn<int>("byteForDuration");
QTest::addColumn<int>("framesForDuration");
QAudioFormat f;
QTest::newRow("invalid") << f << 0 << 0 << 0LL << 0 << 0 << 0LL << 0 << 0;
f.setByteOrder(QAudioFormat::LittleEndian);
f.setChannelCount(1);
f.setSampleRate(8000);
f.setCodec("audio/pcm");
f.setSampleSize(8);
f.setSampleType(QAudioFormat::SignedInt);
qint64 qrtr = 250000LL;
qint64 half = 500000LL;
qint64 one = 1000000LL;
qint64 two = 2000000LL;
// No rounding errors with mono 8 bit
QTest::newRow("1ch_8b_8k_signed_4000") << f << 1 << 4000 << half << 4000 << 4000 << half << 4000 << 4000;
QTest::newRow("1ch_8b_8k_signed_8000") << f << 1 << 8000 << one << 8000 << 8000 << one << 8000 << 8000;
QTest::newRow("1ch_8b_8k_signed_16000") << f << 1 << 16000 << two << 16000 << 16000 << two << 16000 << 16000;
// Mono 16bit
f.setSampleSize(16);
QTest::newRow("1ch_16b_8k_signed_8000") << f << 2 << 8000 << half << 4000 << 8000 << half << 8000 << 4000;
QTest::newRow("1ch_16b_8k_signed_16000") << f << 2 << 16000 << one << 8000 << 16000 << one << 16000 << 8000;
// Rounding errors
QTest::newRow("1ch_16b_8k_signed_8001") << f << 2 << 8001 << half << 4000 << 8000 << half << 8000 << 4000;
QTest::newRow("1ch_16b_8k_signed_8000_duration1") << f << 2 << 8000 << half << 4000 << 8000 << half + 1 << 8000 << 4000;
QTest::newRow("1ch_16b_8k_signed_8000_duration2") << f << 2 << 8000 << half << 4000 << 8000 << half + 124 << 8000 << 4000;
QTest::newRow("1ch_16b_8k_signed_8000_duration3") << f << 2 << 8000 << half << 4000 << 8000 << half + 125 << 8002 << 4001;
QTest::newRow("1ch_16b_8k_signed_8000_duration4") << f << 2 << 8000 << half << 4000 << 8000 << half + 126 << 8002 << 4001;
// Stereo 16 bit
f.setChannelCount(2);
QTest::newRow("2ch_16b_8k_signed_8000") << f << 4 << 8000 << qrtr << 2000 << 8000 << qrtr << 8000 << 2000;
QTest::newRow("2ch_16b_8k_signed_16000") << f << 4 << 16000 << half << 4000 << 16000 << half << 16000 << 4000;
// More rounding errors
// First rounding bytes
QTest::newRow("2ch_16b_8k_signed_8001") << f << 4 << 8001 << qrtr << 2000 << 8000 << qrtr << 8000 << 2000;
QTest::newRow("2ch_16b_8k_signed_8002") << f << 4 << 8002 << qrtr << 2000 << 8000 << qrtr << 8000 << 2000;
QTest::newRow("2ch_16b_8k_signed_8003") << f << 4 << 8003 << qrtr << 2000 << 8000 << qrtr << 8000 << 2000;
// Then rounding duration
// 8khz = 125us per frame
QTest::newRow("2ch_16b_8k_signed_8000_duration1") << f << 4 << 8000 << qrtr << 2000 << 8000 << qrtr + 1 << 8000 << 2000;
QTest::newRow("2ch_16b_8k_signed_8000_duration2") << f << 4 << 8000 << qrtr << 2000 << 8000 << qrtr + 124 << 8000 << 2000;
QTest::newRow("2ch_16b_8k_signed_8000_duration3") << f << 4 << 8000 << qrtr << 2000 << 8000 << qrtr + 125 << 8004 << 2001;
QTest::newRow("2ch_16b_8k_signed_8000_duration4") << f << 4 << 8000 << qrtr << 2000 << 8000 << qrtr + 126 << 8004 << 2001;
}
void tst_QAudioFormat::debugOperator_data()
{
QTest::addColumn<QAudioFormat>("format");