diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/multimedia/audioinput/audioinput.cpp | 309 | ||||
-rw-r--r-- | examples/multimedia/audioinput/audioinput.h | 60 | ||||
-rw-r--r-- | examples/multimedia/audiooutput/audiooutput.cpp | 345 | ||||
-rw-r--r-- | examples/multimedia/audiooutput/audiooutput.h | 68 |
4 files changed, 443 insertions, 339 deletions
diff --git a/examples/multimedia/audioinput/audioinput.cpp b/examples/multimedia/audioinput/audioinput.cpp index fbf4dc4..8a97640 100644 --- a/examples/multimedia/audioinput/audioinput.cpp +++ b/examples/multimedia/audioinput/audioinput.cpp @@ -48,16 +48,47 @@ #include <QAudioDeviceInfo> #include <QAudioInput> + +#include <QtCore/qendian.h> + #include "audioinput.h" -#define BUFFER_SIZE 4096 +const QString InputTest::PushModeLabel(tr("Enable push mode")); +const QString InputTest::PullModeLabel(tr("Enable pull mode")); +const QString InputTest::SuspendLabel(tr("Suspend recording")); +const QString InputTest::ResumeLabel(tr("Resume recording")); -AudioInfo::AudioInfo(QObject *parent, QAudioInput *device) - :QIODevice(parent) -{ - input = device; +const int BufferSize = 4096; + +AudioInfo::AudioInfo(const QAudioFormat &format, QObject *parent) + : QIODevice(parent) + , m_format(format) + , m_maxAmplitude(0) + , m_level(0.0) - m_maxValue = 0; +{ + switch (m_format.sampleSize()) { + case 8: + switch (m_format.sampleType()) { + case QAudioFormat::UnSignedInt: + m_maxAmplitude = 255; + break; + case QAudioFormat::SignedInt: + m_maxAmplitude = 127; + break; + } + break; + case 16: + switch (m_format.sampleType()) { + case QAudioFormat::UnSignedInt: + m_maxAmplitude = 65535; + break; + case QAudioFormat::SignedInt: + m_maxAmplitude = 32767; + break; + } + break; + } } AudioInfo::~AudioInfo() @@ -84,48 +115,56 @@ qint64 AudioInfo::readData(char *data, qint64 maxlen) qint64 AudioInfo::writeData(const char *data, qint64 len) { - int samples = len/2; // 2 bytes per sample - int maxAmp = 32768; // max for S16 samples - bool clipping = false; - - m_maxValue = 0; - - qint16 *s = (qint16*)data; - - // sample format is S16LE, only! - - for (int i = 0; i < samples; ++i) { - qint16 sample = *s; - s++; - if (abs(sample) > m_maxValue) m_maxValue = abs(sample); + if (m_maxAmplitude) { + Q_ASSERT(m_format.sampleSize() % 8 == 0); + const int channelBytes = m_format.sampleSize() / 8; + const int sampleBytes = m_format.channels() * channelBytes; + Q_ASSERT(len % sampleBytes == 0); + const int numSamples = len / sampleBytes; + + quint16 maxValue = 0; + const unsigned char *ptr = reinterpret_cast<const unsigned char *>(data); + + for (int i = 0; i < numSamples; ++i) { + for(int j = 0; j < m_format.channels(); ++j) { + quint16 value = 0; + + if (m_format.sampleSize() == 8 && m_format.sampleType() == QAudioFormat::UnSignedInt) { + value = *reinterpret_cast<const quint8*>(ptr); + } else if (m_format.sampleSize() == 8 && m_format.sampleType() == QAudioFormat::SignedInt) { + value = qAbs(*reinterpret_cast<const qint8*>(ptr)); + } else if (m_format.sampleSize() == 16 && m_format.sampleType() == QAudioFormat::UnSignedInt) { + if (m_format.byteOrder() == QAudioFormat::LittleEndian) + value = qFromLittleEndian<quint16>(ptr); + else + value = qFromBigEndian<quint16>(ptr); + } else if (m_format.sampleSize() == 16 && m_format.sampleType() == QAudioFormat::SignedInt) { + if (m_format.byteOrder() == QAudioFormat::LittleEndian) + value = qAbs(qFromLittleEndian<qint16>(ptr)); + else + value = qAbs(qFromBigEndian<qint16>(ptr)); + } + + maxValue = qMax(value, maxValue); + ptr += channelBytes; + } + } + + maxValue = qMin(maxValue, m_maxAmplitude); + m_level = qreal(maxValue) / m_maxAmplitude; } - // check for clipping - if (m_maxValue >= (maxAmp - 1)) - clipping = true; - - float value = ((float)m_maxValue/(float)maxAmp); - if (clipping) - m_maxValue = 100; - else - m_maxValue = (int)(value*100); emit update(); - return len; } -int AudioInfo::LinearMax() -{ - return m_maxValue; -} - RenderArea::RenderArea(QWidget *parent) : QWidget(parent) { setBackgroundRole(QPalette::Base); setAutoFillBackground(true); - level = 0; + m_level = 0; setMinimumHeight(30); setMinimumWidth(200); } @@ -139,12 +178,12 @@ void RenderArea::paintEvent(QPaintEvent * /* event */) painter.viewport().top()+10, painter.viewport().right()-20, painter.viewport().bottom()-20)); - if (level == 0) + if (m_level == 0.0) return; painter.setPen(Qt::red); - int pos = ((painter.viewport().right()-20)-(painter.viewport().left()+11))*level/100; + int pos = ((painter.viewport().right()-20)-(painter.viewport().left()+11))*m_level; for (int i = 0; i < 10; ++i) { int x1 = painter.viewport().left()+11; int y1 = painter.viewport().top()+10+i; @@ -157,156 +196,174 @@ void RenderArea::paintEvent(QPaintEvent * /* event */) } } -void RenderArea::setLevel(int value) +void RenderArea::setLevel(qreal value) { - level = value; + m_level = value; repaint(); } InputTest::InputTest() + : m_canvas(0) + , m_modeButton(0) + , m_suspendResumeButton(0) + , m_deviceBox(0) + , m_audioInfo(0) + , m_audioInput(0) + , m_input(0) + , m_pullMode(false) + , m_buffer(BufferSize, 0) { - QWidget *window = new QWidget; - QVBoxLayout* layout = new QVBoxLayout; + initializeWindow(); + initializeAudio(); +} + +InputTest::~InputTest() {} + +void InputTest::initializeWindow() +{ + QScopedPointer<QWidget> window(new QWidget); + QScopedPointer<QVBoxLayout> layout(new QVBoxLayout); - canvas = new RenderArea; - layout->addWidget(canvas); + m_canvas = new RenderArea(this); + layout->addWidget(m_canvas); - deviceBox = new QComboBox(this); + m_deviceBox = new QComboBox(this); QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for(int i = 0; i < devices.size(); ++i) - deviceBox->addItem(devices.at(i).deviceName(), qVariantFromValue(devices.at(i))); + m_deviceBox->addItem(devices.at(i).deviceName(), qVariantFromValue(devices.at(i))); - connect(deviceBox, SIGNAL(activated(int)), SLOT(deviceChanged(int))); - layout->addWidget(deviceBox); + connect(m_deviceBox, SIGNAL(activated(int)), SLOT(deviceChanged(int))); + layout->addWidget(m_deviceBox); - button = new QPushButton(this); - button->setText(tr("Click for Push Mode")); - connect(button, SIGNAL(clicked()), SLOT(toggleMode())); - layout->addWidget(button); + m_modeButton = new QPushButton(this); + m_modeButton->setText(PushModeLabel); + connect(m_modeButton, SIGNAL(clicked()), SLOT(toggleMode())); + layout->addWidget(m_modeButton); - button2 = new QPushButton(this); - button2->setText(tr("Click To Suspend")); - connect(button2, SIGNAL(clicked()), SLOT(toggleSuspend())); - layout->addWidget(button2); + m_suspendResumeButton = new QPushButton(this); + m_suspendResumeButton->setText(SuspendLabel); + connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspend())); + layout->addWidget(m_suspendResumeButton); - window->setLayout(layout); - setCentralWidget(window); - window->show(); + window->setLayout(layout.data()); + layout.take(); // ownership transferred - buffer = new char[BUFFER_SIZE]; + setCentralWidget(window.data()); + QWidget *const windowPtr = window.take(); // ownership transferred + windowPtr->show(); +} - pullMode = true; +void InputTest::initializeAudio() +{ + m_pullMode = true; - format.setFrequency(8000); - format.setChannels(1); - format.setSampleSize(16); - format.setSampleType(QAudioFormat::SignedInt); - format.setByteOrder(QAudioFormat::LittleEndian); - format.setCodec("audio/pcm"); + m_format.setFrequency(8000); + m_format.setChannels(1); + m_format.setSampleSize(16); + m_format.setSampleType(QAudioFormat::SignedInt); + m_format.setByteOrder(QAudioFormat::LittleEndian); + m_format.setCodec("audio/pcm"); QAudioDeviceInfo info(QAudioDeviceInfo::defaultInputDevice()); - if (!info.isFormatSupported(format)) { - qWarning()<<"default format not supported try to use nearest"; - format = info.nearestFormat(format); + if (!info.isFormatSupported(m_format)) { + qWarning() << "Default format not supported - trying to use nearest"; + m_format = info.nearestFormat(m_format); } - if(format.sampleSize() != 16) { - qWarning()<<"audio device doesn't support 16 bit samples, example cannot run"; - audioInput = 0; - button->setDisabled(true); - button2->setDisabled(true); - return; - } + m_audioInfo = new AudioInfo(m_format, this); + connect(m_audioInfo, SIGNAL(update()), SLOT(refreshDisplay())); - audioInput = new QAudioInput(format,this); - connect(audioInput, SIGNAL(notify()), SLOT(status())); - connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(state(QAudio::State))); - audioinfo = new AudioInfo(this,audioInput); - connect(audioinfo, SIGNAL(update()), SLOT(refreshDisplay())); - audioinfo->start(); - audioInput->start(audioinfo); + createAudioInput(); } -InputTest::~InputTest() {} +void InputTest::createAudioInput() +{ -void InputTest::status() + m_audioInput = new QAudioInput(m_device, m_format, this); + connect(m_audioInput, SIGNAL(notify()), SLOT(notified())); + connect(m_audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); + m_audioInfo->start(); + m_audioInput->start(m_audioInfo); +} + +void InputTest::notified() { - qWarning()<<"bytesReady = "<<audioInput->bytesReady()<<" bytes, elapsedUSecs = "<<audioInput->elapsedUSecs()<<", processedUSecs = "<<audioInput->processedUSecs(); + qWarning() << "bytesReady = " << m_audioInput->bytesReady() + << ", " << "elapsedUSecs = " <<m_audioInput->elapsedUSecs() + << ", " << "processedUSecs = "<<m_audioInput->processedUSecs(); } void InputTest::readMore() { - if(!audioInput) + if(!m_audioInput) return; - qint64 len = audioInput->bytesReady(); + qint64 len = m_audioInput->bytesReady(); if(len > 4096) len = 4096; - qint64 l = input->read(buffer,len); + qint64 l = m_input->read(m_buffer.data(), len); if(l > 0) { - audioinfo->write(buffer,l); + m_audioInfo->write(m_buffer.constData(), l); } } void InputTest::toggleMode() { // Change bewteen pull and push modes - audioInput->stop(); + m_audioInput->stop(); - if (pullMode) { - button->setText(tr("Click for Pull Mode")); - input = audioInput->start(); - connect(input, SIGNAL(readyRead()), SLOT(readMore())); - pullMode = false; + if (m_pullMode) { + m_modeButton->setText(PullModeLabel); + m_input = m_audioInput->start(); + connect(m_input, SIGNAL(readyRead()), SLOT(readMore())); + m_pullMode = false; } else { - button->setText(tr("Click for Push Mode")); - pullMode = true; - audioInput->start(audioinfo); + m_modeButton->setText(PushModeLabel); + m_pullMode = true; + m_audioInput->start(m_audioInfo); } + + m_suspendResumeButton->setText(SuspendLabel); } void InputTest::toggleSuspend() { // toggle suspend/resume - if(audioInput->state() == QAudio::SuspendedState) { + if(m_audioInput->state() == QAudio::SuspendedState) { qWarning() << "status: Suspended, resume()"; - audioInput->resume(); - button2->setText("Click To Suspend"); - } else if (audioInput->state() == QAudio::ActiveState) { + m_audioInput->resume(); + m_suspendResumeButton->setText(SuspendLabel); + } else if (m_audioInput->state() == QAudio::ActiveState) { qWarning() << "status: Active, suspend()"; - audioInput->suspend(); - button2->setText("Click To Resume"); - } else if (audioInput->state() == QAudio::StoppedState) { + m_audioInput->suspend(); + m_suspendResumeButton->setText(ResumeLabel); + } else if (m_audioInput->state() == QAudio::StoppedState) { qWarning() << "status: Stopped, resume()"; - audioInput->resume(); - button2->setText("Click To Suspend"); - } else if (audioInput->state() == QAudio::IdleState) { + m_audioInput->resume(); + m_suspendResumeButton->setText(SuspendLabel); + } else if (m_audioInput->state() == QAudio::IdleState) { qWarning() << "status: IdleState"; } } -void InputTest::state(QAudio::State state) +void InputTest::stateChanged(QAudio::State state) { - qWarning() << " state=" << state; + qWarning() << "state = " << state; } void InputTest::refreshDisplay() { - canvas->setLevel(audioinfo->LinearMax()); - canvas->repaint(); + m_canvas->setLevel(m_audioInfo->level()); + m_canvas->repaint(); } -void InputTest::deviceChanged(int idx) +void InputTest::deviceChanged(int index) { - audioinfo->stop(); - audioInput->stop(); - audioInput->disconnect(this); - delete audioInput; - - device = deviceBox->itemData(idx).value<QAudioDeviceInfo>(); - audioInput = new QAudioInput(device, format, this); - connect(audioInput, SIGNAL(notify()), SLOT(status())); - connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(state(QAudio::State))); - audioinfo->start(); - audioInput->start(audioinfo); + m_audioInfo->stop(); + m_audioInput->stop(); + m_audioInput->disconnect(this); + delete m_audioInput; + + m_device = m_deviceBox->itemData(index).value<QAudioDeviceInfo>(); + createAudioInput(); } diff --git a/examples/multimedia/audioinput/audioinput.h b/examples/multimedia/audioinput/audioinput.h index abe2be1..f87c682 100644 --- a/examples/multimedia/audioinput/audioinput.h +++ b/examples/multimedia/audioinput/audioinput.h @@ -45,6 +45,7 @@ #include <QMainWindow> #include <QPushButton> #include <QComboBox> +#include <QByteArray> #include <qaudioinput.h> @@ -52,21 +53,21 @@ class AudioInfo : public QIODevice { Q_OBJECT public: - AudioInfo(QObject *parent, QAudioInput *device); + AudioInfo(const QAudioFormat &format, QObject *parent); ~AudioInfo(); void start(); void stop(); - int LinearMax(); + qreal level() const { return m_level; } qint64 readData(char *data, qint64 maxlen); qint64 writeData(const char *data, qint64 len); - QAudioInput *input; - private: - int m_maxValue; + const QAudioFormat m_format; + quint16 m_maxAmplitude; + qreal m_level; // 0.0 <= m_level <= 1.0 signals: void update(); @@ -80,14 +81,14 @@ class RenderArea : public QWidget public: RenderArea(QWidget *parent = 0); - void setLevel(int value); + void setLevel(qreal value); protected: void paintEvent(QPaintEvent *event); private: - int level; - QPixmap pixmap; + qreal m_level; + QPixmap m_pixmap; }; class InputTest : public QMainWindow @@ -97,29 +98,38 @@ public: InputTest(); ~InputTest(); +private: + void initializeWindow(); + void initializeAudio(); + void createAudioInput(); + private slots: void refreshDisplay(); - void status(); + void notified(); void readMore(); void toggleMode(); void toggleSuspend(); - void state(QAudio::State s); - void deviceChanged(int idx); + void stateChanged(QAudio::State state); + void deviceChanged(int index); private: - AudioInfo *audioinfo; - QAudioDeviceInfo device; - QAudioFormat format; - QAudioInput *audioInput; - QIODevice *input; - RenderArea *canvas; - - bool pullMode; - - QPushButton *button; - QPushButton *button2; - QComboBox *deviceBox; - - char *buffer; + // Owned by layout + RenderArea *m_canvas; + QPushButton *m_modeButton; + QPushButton *m_suspendResumeButton; + QComboBox *m_deviceBox; + + AudioInfo *m_audioInfo; + QAudioDeviceInfo m_device; + QAudioFormat m_format; + QAudioInput *m_audioInput; + QIODevice *m_input; + bool m_pullMode; + QByteArray m_buffer; + + static const QString PushModeLabel; + static const QString PullModeLabel; + static const QString SuspendLabel; + static const QString ResumeLabel; }; diff --git a/examples/multimedia/audiooutput/audiooutput.cpp b/examples/multimedia/audiooutput/audiooutput.cpp index b44accd..4866d36 100644 --- a/examples/multimedia/audiooutput/audiooutput.cpp +++ b/examples/multimedia/audiooutput/audiooutput.cpp @@ -44,30 +44,34 @@ #include <QAudioOutput> #include <QAudioDeviceInfo> +#include <QtCore/qmath.h> +#include <QtCore/qendian.h> #include "audiooutput.h" -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif +const QString AudioTest::PushModeLabel(tr("Enable push mode")); +const QString AudioTest::PullModeLabel(tr("Enable pull mode")); +const QString AudioTest::SuspendLabel(tr("Suspend playback")); +const QString AudioTest::ResumeLabel(tr("Resume playback")); -#define SECONDS 1 -#define FREQ 600 -#define SYSTEM_FREQ 44100 +const int DurationSeconds = 1; +const int ToneFrequencyHz = 600; +const int DataFrequencyHz = 44100; +const int BufferSize = 32768; -Generator::Generator(QObject *parent) - :QIODevice( parent ) + +Generator::Generator(const QAudioFormat &format, + qint64 durationUs, + int frequency, + QObject *parent) + : QIODevice(parent) + , m_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() @@ -77,47 +81,65 @@ void Generator::start() void Generator::stop() { + m_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 + + m_buffer.resize(length); + unsigned char *ptr = reinterpret_cast<unsigned char *>(m_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((m_buffer.size() - m_pos), len - total); + memcpy(data, m_buffer.constData() + m_pos, chunk); + m_pos = (m_pos + chunk) % m_buffer.size(); + total += chunk; } + return total; } qint64 Generator::writeData(const char *data, qint64 len) @@ -128,158 +150,165 @@ qint64 Generator::writeData(const char *data, qint64 len) return 0; } -AudioTest::AudioTest() +qint64 Generator::bytesAvailable() const { - QWidget *window = new QWidget; - QVBoxLayout* layout = new QVBoxLayout; - - deviceBox = new QComboBox(this); - foreach (const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) - deviceBox->addItem(deviceInfo.deviceName(), qVariantFromValue(deviceInfo)); - connect(deviceBox,SIGNAL(activated(int)),SLOT(deviceChanged(int))); - layout->addWidget(deviceBox); - - button = new QPushButton(this); - button->setText(tr("Click for Push Mode")); - connect(button,SIGNAL(clicked()),SLOT(toggle())); - layout->addWidget(button); - - button2 = new QPushButton(this); - button2->setText(tr("Click To Suspend")); - connect(button2,SIGNAL(clicked()),SLOT(togglePlay())); - layout->addWidget(button2); - - window->setLayout(layout); - setCentralWidget(window); - window->show(); + return m_buffer.size() + QIODevice::bytesAvailable(); +} - buffer = new char[BUFFER_SIZE]; +AudioTest::AudioTest() + : m_pullTimer(new QTimer(this)) + , m_modeButton(0) + , m_suspendResumeButton(0) + , m_deviceBox(0) + , m_generator(0) + , m_audioOutput(0) + , m_output(0) + , m_buffer(BufferSize, 0) +{ + initializeWindow(); + initializeAudio(); +} - gen = new Generator(this); +void AudioTest::initializeWindow() +{ + QScopedPointer<QWidget> window(new QWidget); + QScopedPointer<QVBoxLayout> layout(new QVBoxLayout); - pullMode = true; + m_deviceBox = new QComboBox(this); + foreach (const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) + m_deviceBox->addItem(deviceInfo.deviceName(), qVariantFromValue(deviceInfo)); + connect(m_deviceBox,SIGNAL(activated(int)),SLOT(deviceChanged(int))); + layout->addWidget(m_deviceBox); + + m_modeButton = new QPushButton(this); + m_modeButton->setText(PushModeLabel); + connect(m_modeButton, SIGNAL(clicked()), SLOT(toggleMode())); + layout->addWidget(m_modeButton); + + m_suspendResumeButton = new QPushButton(this); + m_suspendResumeButton->setText(SuspendLabel); + connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); + layout->addWidget(m_suspendResumeButton); + + window->setLayout(layout.data()); + layout.take(); // ownership transferred + + setCentralWidget(window.data()); + QWidget *const windowPtr = window.take(); // ownership transferred + windowPtr->show(); +} - timer = new QTimer(this); - connect(timer,SIGNAL(timeout()),SLOT(writeMore())); +void AudioTest::initializeAudio() +{ + connect(m_pullTimer, SIGNAL(timeout()), SLOT(pullTimerExpired())); - gen->start(); + m_pullMode = true; - settings.setFrequency(SYSTEM_FREQ); - settings.setChannels(1); - settings.setSampleSize(16); - settings.setCodec("audio/pcm"); - settings.setByteOrder(QAudioFormat::LittleEndian); - settings.setSampleType(QAudioFormat::SignedInt); + m_format.setFrequency(DataFrequencyHz); + m_format.setChannels(1); + m_format.setSampleSize(16); + m_format.setCodec("audio/pcm"); + m_format.setByteOrder(QAudioFormat::LittleEndian); + m_format.setSampleType(QAudioFormat::SignedInt); QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); - if (!info.isFormatSupported(settings)) { - qWarning()<<"default format not supported try to use nearest"; - settings = info.nearestFormat(settings); + if (!info.isFormatSupported(m_format)) { + qWarning() << "Default format not supported - trying to use nearest"; + m_format = info.nearestFormat(m_format); } - 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; - } + m_generator = new Generator(m_format, DurationSeconds*1000000, ToneFrequencyHz, this); - audioOutput = new QAudioOutput(settings,this); - connect(audioOutput,SIGNAL(notify()),SLOT(status())); - connect(audioOutput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State))); + createAudioOutput(); +} - audioOutput->start(gen); +void AudioTest::createAudioOutput() +{ + delete m_audioOutput; + m_audioOutput = 0; + m_audioOutput = new QAudioOutput(m_format, this); + connect(m_audioOutput, SIGNAL(notify()), SLOT(notified())); + connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); + m_generator->start(); + m_audioOutput->start(m_generator); } AudioTest::~AudioTest() { - delete [] buffer; + } -void AudioTest::deviceChanged(int idx) +void AudioTest::deviceChanged(int index) { - timer->stop(); - gen->stop(); - audioOutput->stop(); - audioOutput->disconnect(this); - delete audioOutput; - - device = deviceBox->itemData(idx).value<QAudioDeviceInfo>(); - audioOutput = new QAudioOutput(device,settings,this); - connect(audioOutput,SIGNAL(notify()),SLOT(status())); - connect(audioOutput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State))); - gen->start(); - audioOutput->start(gen); + m_pullTimer->stop(); + m_generator->stop(); + m_audioOutput->stop(); + m_audioOutput->disconnect(this); + m_device = m_deviceBox->itemData(index).value<QAudioDeviceInfo>(); + createAudioOutput(); } -void AudioTest::status() +void AudioTest::notified() { - qWarning() << "byteFree = " << audioOutput->bytesFree() << " bytes, elapsedUSecs = " << audioOutput->elapsedUSecs() << ", processedUSecs = " << audioOutput->processedUSecs(); + qWarning() << "bytesFree = " << m_audioOutput->bytesFree() + << ", " << "elapsedUSecs = " << m_audioOutput->elapsedUSecs() + << ", " << "processedUSecs = " << m_audioOutput->processedUSecs(); } -void AudioTest::writeMore() +void AudioTest::pullTimerExpired() { - if (!audioOutput) - return; - - if (audioOutput->state() == QAudio::StoppedState) - return; - - int l; - int out; - - int chunks = audioOutput->bytesFree()/audioOutput->periodSize(); - while(chunks) { - l = gen->read(buffer,audioOutput->periodSize()); - if (l > 0) - out = output->write(buffer,l); - if (l != audioOutput->periodSize()) - break; - chunks--; + if (m_audioOutput && m_audioOutput->state() != QAudio::StoppedState) { + int chunks = m_audioOutput->bytesFree()/m_audioOutput->periodSize(); + while (chunks) { + const qint64 len = m_generator->read(m_buffer.data(), m_audioOutput->periodSize()); + if (len) + m_output->write(m_buffer.data(), len); + if (len != m_audioOutput->periodSize()) + break; + --chunks; + } } } -void AudioTest::toggle() +void AudioTest::toggleMode() { - // Change between pull and push modes - - timer->stop(); - audioOutput->stop(); - - if (pullMode) { - button->setText("Click for Pull Mode"); - output = audioOutput->start(); - pullMode = false; - timer->start(20); + m_pullTimer->stop(); + m_audioOutput->stop(); + + if (m_pullMode) { + m_modeButton->setText(PullModeLabel); + m_output = m_audioOutput->start(); + m_pullMode = false; + m_pullTimer->start(20); } else { - button->setText("Click for Push Mode"); - pullMode = true; - audioOutput->start(gen); + m_modeButton->setText(PushModeLabel); + m_pullMode = true; + m_audioOutput->start(m_generator); } + + m_suspendResumeButton->setText(SuspendLabel); } -void AudioTest::togglePlay() +void AudioTest::toggleSuspendResume() { - // toggle suspend/resume - if (audioOutput->state() == QAudio::SuspendedState) { + if (m_audioOutput->state() == QAudio::SuspendedState) { qWarning() << "status: Suspended, resume()"; - audioOutput->resume(); - button2->setText("Click To Suspend"); - } else if (audioOutput->state() == QAudio::ActiveState) { + m_audioOutput->resume(); + m_suspendResumeButton->setText(SuspendLabel); + } else if (m_audioOutput->state() == QAudio::ActiveState) { qWarning() << "status: Active, suspend()"; - audioOutput->suspend(); - button2->setText("Click To Resume"); - } else if (audioOutput->state() == QAudio::StoppedState) { + m_audioOutput->suspend(); + m_suspendResumeButton->setText(ResumeLabel); + } else if (m_audioOutput->state() == QAudio::StoppedState) { qWarning() << "status: Stopped, resume()"; - audioOutput->resume(); - button2->setText("Click To Suspend"); - } else if (audioOutput->state() == QAudio::IdleState) { + m_audioOutput->resume(); + m_suspendResumeButton->setText(SuspendLabel); + } else if (m_audioOutput->state() == QAudio::IdleState) { qWarning() << "status: IdleState"; } } -void AudioTest::state(QAudio::State state) +void AudioTest::stateChanged(QAudio::State state) { - qWarning() << " state=" << state; + qWarning() << "state = " << state; } diff --git a/examples/multimedia/audiooutput/audiooutput.h b/examples/multimedia/audiooutput/audiooutput.h index 372cf78..889482b 100644 --- a/examples/multimedia/audiooutput/audiooutput.h +++ b/examples/multimedia/audiooutput/audiooutput.h @@ -41,14 +41,13 @@ #include <math.h> -#define BUFFER_SIZE 32768 - #include <QObject> #include <QMainWindow> #include <QIODevice> #include <QTimer> #include <QPushButton> #include <QComboBox> +#include <QByteArray> #include <QAudioOutput> @@ -56,26 +55,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 m_pos; + QByteArray m_buffer; }; class AudioTest : public QMainWindow @@ -85,26 +80,39 @@ public: AudioTest(); ~AudioTest(); - QAudioDeviceInfo device; - Generator* gen; - QAudioOutput* audioOutput; - QIODevice* output; - QTimer* timer; - QAudioFormat settings; +private: + void initializeWindow(); + void initializeAudio(); + void createAudioOutput(); + +private: + QTimer* m_pullTimer; + + // Owned by layout + QPushButton* m_modeButton; + QPushButton* m_suspendResumeButton; + QComboBox* m_deviceBox; + + QAudioDeviceInfo m_device; + Generator* m_generator; + QAudioOutput* m_audioOutput; + QIODevice* m_output; // not owned + QAudioFormat m_format; - bool pullMode; - char* buffer; + bool m_pullMode; + QByteArray m_buffer; - QPushButton* button; - QPushButton* button2; - QComboBox* deviceBox; + static const QString PushModeLabel; + static const QString PullModeLabel; + static const QString SuspendLabel; + static const QString ResumeLabel; private slots: - void status(); - void writeMore(); - void toggle(); - void togglePlay(); - void state(QAudio::State s); - void deviceChanged(int idx); + void notified(); + void pullTimerExpired(); + void toggleMode(); + void toggleSuspendResume(); + void stateChanged(QAudio::State state); + void deviceChanged(int index); }; |