diff options
author | Gareth Stockwell <ext-gareth.stockwell@nokia.com> | 2010-02-16 12:55:23 (GMT) |
---|---|---|
committer | Gareth Stockwell <ext-gareth.stockwell@nokia.com> | 2010-02-16 17:57:39 (GMT) |
commit | 53dc6607cf669b977ff40de995bd9b1b8e395b78 (patch) | |
tree | fb83b582460bb91d7a3da9805b6a03a151175cdc /examples/multimedia | |
parent | b03306503cc7de9513aa282b55bda40cea00e128 (diff) | |
download | Qt-53dc6607cf669b977ff40de995bd9b1b8e395b78.zip Qt-53dc6607cf669b977ff40de995bd9b1b8e395b78.tar.gz Qt-53dc6607cf669b977ff40de995bd9b1b8e395b78.tar.bz2 |
Made data generation in audio output example work for all formats
The previous code only worked correctly for mono PCM16 formats. This
meant that:
a) If the device did not support mono, but did support stereo
PCM16, the tone would be played at the wrong pitch.
b) If the device did not support little-endian PCM16 at all
(admittedly unlikely), the example would not run.
Reviewed-by: trustme
Diffstat (limited to 'examples/multimedia')
-rw-r--r-- | examples/multimedia/audiooutput/audiooutput.cpp | 128 | ||||
-rw-r--r-- | examples/multimedia/audiooutput/audiooutput.h | 19 |
2 files changed, 75 insertions, 72 deletions
diff --git a/examples/multimedia/audiooutput/audiooutput.cpp b/examples/multimedia/audiooutput/audiooutput.cpp index d4bb294..d459e50e 100644 --- a/examples/multimedia/audiooutput/audiooutput.cpp +++ b/examples/multimedia/audiooutput/audiooutput.cpp @@ -44,30 +44,27 @@ #include <QAudioOutput> #include <QAudioDeviceInfo> +#include <QtCore/qmath.h> +#include <QtCore/qendian.h> #include "audiooutput.h" -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - #define SECONDS 1 #define FREQ 600 #define SYSTEM_FREQ 44100 -Generator::Generator(QObject *parent) - :QIODevice( parent ) +Generator::Generator(const QAudioFormat &format, + qint64 durationUs, + int frequency, + QObject *parent) + : QIODevice(parent) + , pos(0) { - finished = false; - buffer = new char[SECONDS*SYSTEM_FREQ*4+1000]; - t=buffer; - len=fillData(t,FREQ,SECONDS); /* mono FREQHz sine */ - pos = 0; - total = len; + generateData(format, durationUs, frequency); } Generator::~Generator() { - delete [] buffer; + } void Generator::start() @@ -75,54 +72,67 @@ void Generator::start() open(QIODevice::ReadOnly); } -qint64 Generator::bytesAvailable() const -{ - return (SECONDS*SYSTEM_FREQ*2)-pos + QIODevice::bytesAvailable(); -} - void Generator::stop() { + pos = 0; close(); } -int Generator::putShort(char *t, unsigned int value) +void Generator::generateData(const QAudioFormat &format, qint64 durationUs, int frequency) { - *(unsigned char *)(t++)=value&255; - *(unsigned char *)(t)=(value/256)&255; - return 2; -} - -int Generator::fillData(char *start, int frequency, int seconds) -{ - int i, len=0; - int value; - for(i=0; i<seconds*SYSTEM_FREQ; i++) { - value=(int)(32767.0*sin(2.0*M_PI*((double)(i))*(double)(frequency)/SYSTEM_FREQ)); - putShort(start, value); - start += 4; - len+=2; + const int channelBytes = format.sampleSize() / 8; + const int sampleBytes = format.channels() * channelBytes; + + qint64 length = (format.frequency() * format.channels() * (format.sampleSize() / 8)) + * durationUs / 100000; + + 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) { + const qreal x = qSin(2 * M_PI * frequency * qreal(sampleIndex % format.frequency()) / format.frequency()); + for (int i=0; i<format.channels(); ++i) { + if (format.sampleSize() == 8 && format.sampleType() == QAudioFormat::UnSignedInt) { + const quint8 value = static_cast<quint8>((1.0 + x) / 2 * 255); + *reinterpret_cast<quint8*>(ptr) = value; + } else if (format.sampleSize() == 8 && format.sampleType() == QAudioFormat::SignedInt) { + const qint8 value = static_cast<qint8>(x * 127); + *reinterpret_cast<quint8*>(ptr) = value; + } else if (format.sampleSize() == 16 && 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.sampleSize() == 16 && 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; + } + ++sampleIndex; } - return len; } -qint64 Generator::readData(char *data, qint64 maxlen) +qint64 Generator::readData(char *data, qint64 len) { - int len = maxlen; - if (len > 16384) - len = 16384; - - if (len < (SECONDS*SYSTEM_FREQ*2)-pos) { - // Normal - memcpy(data,t+pos,len); - pos+=len; - return len; - } else { - // Whats left and reset to start - qint64 left = (SECONDS*SYSTEM_FREQ*2)-pos; - memcpy(data,t+pos,left); - pos=0; - return left; + qint64 total = 0; + while (len - total) { + const qint64 chunk = qMin((buffer.size() - pos), len - total); + memcpy(data, buffer.constData() + pos, chunk); + pos = (pos + chunk) % buffer.size(); + total += chunk; } + return total; } qint64 Generator::writeData(const char *data, qint64 len) @@ -133,6 +143,11 @@ qint64 Generator::writeData(const char *data, qint64 len) return 0; } +qint64 Generator::bytesAvailable() const +{ + return buffer.size() + QIODevice::bytesAvailable(); +} + AudioTest::AudioTest() { QWidget *window = new QWidget; @@ -160,15 +175,11 @@ AudioTest::AudioTest() buffer = new char[BUFFER_SIZE]; - gen = new Generator(this); - pullMode = true; timer = new QTimer(this); connect(timer,SIGNAL(timeout()),SLOT(writeMore())); - gen->start(); - settings.setFrequency(SYSTEM_FREQ); settings.setChannels(1); settings.setSampleSize(16); @@ -182,13 +193,8 @@ AudioTest::AudioTest() settings = info.nearestFormat(settings); } - if(settings.sampleSize() != 16) { - qWarning()<<"audio device doesn't support 16 bit samples, example cannot run"; - button->setDisabled(true); - button2->setDisabled(true); - audioOutput = 0; - return; - } + gen = new Generator(settings, SECONDS*1000000, FREQ, this); + gen->start(); audioOutput = new QAudioOutput(settings,this); connect(audioOutput,SIGNAL(notify()),SLOT(status())); diff --git a/examples/multimedia/audiooutput/audiooutput.h b/examples/multimedia/audiooutput/audiooutput.h index eba6446..2c62d84 100644 --- a/examples/multimedia/audiooutput/audiooutput.h +++ b/examples/multimedia/audiooutput/audiooutput.h @@ -49,6 +49,7 @@ #include <QTimer> #include <QPushButton> #include <QComboBox> +#include <QByteArray> #include <QAudioOutput> @@ -56,27 +57,22 @@ class Generator : public QIODevice { Q_OBJECT public: - Generator(QObject *parent); + Generator(const QAudioFormat &format, qint64 durationUs, int frequency, QObject *parent); ~Generator(); void start(); void stop(); - char *t; - int len; - int pos; - int total; - char *buffer; - bool finished; - int chunk_size; - qint64 readData(char *data, qint64 maxlen); qint64 writeData(const char *data, qint64 len); qint64 bytesAvailable() const; private: - int putShort(char *t, unsigned int value); - int fillData(char *start, int frequency, int seconds); + void generateData(const QAudioFormat &format, qint64 durationUs, int frequency); + +private: + qint64 pos; + QByteArray buffer; }; class AudioTest : public QMainWindow @@ -86,6 +82,7 @@ public: AudioTest(); ~AudioTest(); +private: QAudioDeviceInfo device; Generator* gen; QAudioOutput* audioOutput; |