117 lines
2.8 KiB
C++
117 lines
2.8 KiB
C++
#include "Generator.h"
|
|
#include <qendian.h>
|
|
#include <qmath.h>
|
|
|
|
|
|
Generator::Generator(const QAudioFormat &format
|
|
, qint64 durationUs
|
|
, int sampleRate, bool right)
|
|
{
|
|
if (format.isValid())
|
|
GenerateData(format, durationUs, sampleRate, right);
|
|
}
|
|
|
|
void Generator::start()
|
|
{
|
|
open(QIODevice::ReadOnly);
|
|
}
|
|
|
|
void Generator::stop()
|
|
{
|
|
pos = 0;
|
|
close();
|
|
}
|
|
|
|
void Generator::GenerateData(const QAudioFormat &format, qint64 durationUs, int sampleRate, bool right)
|
|
{
|
|
const int channelBytes = format.sampleSize() / 8;
|
|
const int sampleBytes = format.channelCount() * channelBytes;
|
|
qint64 length = (format.sampleRate() * format.channelCount() * (format.sampleSize() / 8))
|
|
* durationUs / 1000000;
|
|
Q_ASSERT(length % sampleBytes == 0);
|
|
Q_UNUSED(sampleBytes) // suppress warning in release builds
|
|
|
|
buffer.resize(length);
|
|
unsigned char *ptr = reinterpret_cast<unsigned char *>(buffer.data());
|
|
int sampleIndex = 0;
|
|
|
|
while (length)
|
|
{
|
|
for (int i = 0; i < format.channelCount(); ++i)
|
|
{
|
|
qreal x = 0;
|
|
// Generate sine wave on first (left) or second (right) channel and silence on other.
|
|
if ( (i == 0 && !right) ||
|
|
(i == 1 && right))
|
|
{
|
|
x = qSin(2 * M_PI * sampleRate * sampleIndex / format.sampleRate()) * 0.1;
|
|
sampleIndex++;
|
|
}
|
|
if (format.sampleSize() == 8)
|
|
{
|
|
if (format.sampleType() == QAudioFormat::UnSignedInt)
|
|
{
|
|
const quint8 value = static_cast<quint8>((1.0 + x) / 2 * 255);
|
|
*reinterpret_cast<quint8 *>(ptr) = value;
|
|
}
|
|
else if (format.sampleType() == QAudioFormat::SignedInt)
|
|
{
|
|
const qint8 value = static_cast<qint8>(x * 127);
|
|
*reinterpret_cast<qint8 *>(ptr) = value;
|
|
}
|
|
}
|
|
else if (format.sampleSize() == 16)
|
|
{
|
|
if (format.sampleType() == QAudioFormat::UnSignedInt)
|
|
{
|
|
quint16 value = static_cast<quint16>((1.0 + x) / 2 * 65535);
|
|
if (format.byteOrder() == QAudioFormat::LittleEndian)
|
|
qToLittleEndian<quint16>(value, ptr);
|
|
else
|
|
qToBigEndian<quint16>(value, ptr);
|
|
}
|
|
else if (format.sampleType() == QAudioFormat::SignedInt)
|
|
{
|
|
qint16 value = static_cast<qint16>(x * 32767);
|
|
if (format.byteOrder() == QAudioFormat::LittleEndian)
|
|
qToLittleEndian<qint16>(value, ptr);
|
|
else
|
|
qToBigEndian<qint16>(value, ptr);
|
|
}
|
|
}
|
|
|
|
ptr += channelBytes;
|
|
length -= channelBytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
qint64 Generator::readData(char *data, qint64 len)
|
|
{
|
|
qint64 total = 0;
|
|
if (!buffer.isEmpty())
|
|
{
|
|
while (len - total > 0)
|
|
{
|
|
const qint64 chunk = qMin((buffer.size() - pos), len - total);
|
|
memcpy(data + total, buffer.constData() + pos, chunk);
|
|
pos = (pos + chunk) % buffer.size();
|
|
total += chunk;
|
|
}
|
|
}
|
|
return total;
|
|
}
|
|
|
|
qint64 Generator::writeData(const char *data, qint64 len)
|
|
{
|
|
Q_UNUSED(data);
|
|
Q_UNUSED(len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
qint64 Generator::bytesAvailable() const
|
|
{
|
|
return buffer.size() + QIODevice::bytesAvailable();
|
|
}
|