diff options
Diffstat (limited to 'examples/multimedia/audiooutput')
-rw-r--r-- | examples/multimedia/audiooutput/audiooutput.cpp | 334 | ||||
-rw-r--r-- | examples/multimedia/audiooutput/audiooutput.h | 122 | ||||
-rw-r--r-- | examples/multimedia/audiooutput/audiooutput.pro | 12 | ||||
-rwxr-xr-x | examples/multimedia/audiooutput/doc/images/audiooutput-example.png | bin | 0 -> 11966 bytes | |||
-rw-r--r-- | examples/multimedia/audiooutput/doc/src/audiooutput.qdoc | 43 | ||||
-rw-r--r-- | examples/multimedia/audiooutput/main.cpp | 54 |
6 files changed, 565 insertions, 0 deletions
diff --git a/examples/multimedia/audiooutput/audiooutput.cpp b/examples/multimedia/audiooutput/audiooutput.cpp new file mode 100644 index 000000000..4328e4e99 --- /dev/null +++ b/examples/multimedia/audiooutput/audiooutput.cpp @@ -0,0 +1,334 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QAudioDeviceInfo> +#include <QAudioOutput> +#include <QDebug> +#include <QVBoxLayout> +#include <qmath.h> +#include <qendian.h> + +#include "audiooutput.h" + +#define PUSH_MODE_LABEL "Enable push mode" +#define PULL_MODE_LABEL "Enable pull mode" +#define SUSPEND_LABEL "Suspend playback" +#define RESUME_LABEL "Resume playback" +#define VOLUME_LABEL "Volume:" + +const int DurationSeconds = 1; +const int ToneSampleRateHz = 600; +const int DataSampleRateHz = 44100; +const int BufferSize = 32768; + + +Generator::Generator(const QAudioFormat &format, + qint64 durationUs, + int sampleRate, + QObject *parent) + : QIODevice(parent) + , m_pos(0) +{ + generateData(format, durationUs, sampleRate); +} + +Generator::~Generator() +{ + +} + +void Generator::start() +{ + open(QIODevice::ReadOnly); +} + +void Generator::stop() +{ + m_pos = 0; + close(); +} + +void Generator::generateData(const QAudioFormat &format, qint64 durationUs, int sampleRate) +{ + const int channelBytes = format.sampleSize() / 8; + const int sampleBytes = format.channelCount() * channelBytes; + + qint64 length = (format.sampleRate() * format.channelCount() * (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 * sampleRate * qreal(sampleIndex % format.sampleRate()) / format.sampleRate()); + for (int i=0; i<format.channelCount(); ++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; + } +} + +qint64 Generator::readData(char *data, qint64 len) +{ + qint64 total = 0; + while (len - total > 0) { + const qint64 chunk = qMin((m_buffer.size() - m_pos), len - total); + memcpy(data + total, 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) +{ + Q_UNUSED(data); + Q_UNUSED(len); + + return 0; +} + +qint64 Generator::bytesAvailable() const +{ + return m_buffer.size() + QIODevice::bytesAvailable(); +} + +AudioTest::AudioTest() + : m_pullTimer(new QTimer(this)) + , m_modeButton(0) + , m_suspendResumeButton(0) + , m_deviceBox(0) + , m_device(QAudioDeviceInfo::defaultOutputDevice()) + , m_generator(0) + , m_audioOutput(0) + , m_output(0) + , m_buffer(BufferSize, 0) +{ + initializeWindow(); + initializeAudio(); +} + +void AudioTest::initializeWindow() +{ + QScopedPointer<QWidget> window(new QWidget); + QScopedPointer<QVBoxLayout> layout(new QVBoxLayout); + + 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(tr(PUSH_MODE_LABEL)); + connect(m_modeButton, SIGNAL(clicked()), SLOT(toggleMode())); + layout->addWidget(m_modeButton); + + m_suspendResumeButton = new QPushButton(this); + m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); + connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); + layout->addWidget(m_suspendResumeButton); + + QHBoxLayout *volumeBox = new QHBoxLayout; + m_volumeLabel = new QLabel; + m_volumeLabel->setText(tr(VOLUME_LABEL)); + m_volumeSlider = new QSlider(Qt::Horizontal); + m_volumeSlider->setMinimum(0); + m_volumeSlider->setMaximum(100); + m_volumeSlider->setSingleStep(10); + connect(m_volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int))); + volumeBox->addWidget(m_volumeLabel); + volumeBox->addWidget(m_volumeSlider); + layout->addLayout(volumeBox); + + window->setLayout(layout.data()); + layout.take(); // ownership transferred + + setCentralWidget(window.data()); + QWidget *const windowPtr = window.take(); // ownership transferred + windowPtr->show(); +} + +void AudioTest::initializeAudio() +{ + connect(m_pullTimer, SIGNAL(timeout()), SLOT(pullTimerExpired())); + + m_pullMode = true; + + m_format.setSampleRate(DataSampleRateHz); + m_format.setChannelCount(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(m_format)) { + qWarning() << "Default format not supported - trying to use nearest"; + m_format = info.nearestFormat(m_format); + } + + m_generator = new Generator(m_format, DurationSeconds*1000000, ToneSampleRateHz, this); + + createAudioOutput(); +} + +void AudioTest::createAudioOutput() +{ + delete m_audioOutput; + m_audioOutput = 0; + m_audioOutput = new QAudioOutput(m_device, 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); + m_volumeSlider->setValue(int(m_audioOutput->volume()*100.0f)); +} + +AudioTest::~AudioTest() +{ + +} + +void AudioTest::deviceChanged(int index) +{ + m_pullTimer->stop(); + m_generator->stop(); + m_audioOutput->stop(); + m_audioOutput->disconnect(this); + m_device = m_deviceBox->itemData(index).value<QAudioDeviceInfo>(); + createAudioOutput(); +} + +void AudioTest::volumeChanged(int value) +{ + if (m_audioOutput) + m_audioOutput->setVolume(qreal(value/100.0f)); +} + +void AudioTest::notified() +{ + qWarning() << "bytesFree = " << m_audioOutput->bytesFree() + << ", " << "elapsedUSecs = " << m_audioOutput->elapsedUSecs() + << ", " << "processedUSecs = " << m_audioOutput->processedUSecs(); +} + +void AudioTest::pullTimerExpired() +{ + 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::toggleMode() +{ + m_pullTimer->stop(); + m_audioOutput->stop(); + + if (m_pullMode) { + m_modeButton->setText(tr(PULL_MODE_LABEL)); + m_output = m_audioOutput->start(); + m_pullMode = false; + m_pullTimer->start(20); + } else { + m_modeButton->setText(tr(PUSH_MODE_LABEL)); + m_pullMode = true; + m_audioOutput->start(m_generator); + } + + m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); +} + +void AudioTest::toggleSuspendResume() +{ + if (m_audioOutput->state() == QAudio::SuspendedState) { + qWarning() << "status: Suspended, resume()"; + m_audioOutput->resume(); + m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); + } else if (m_audioOutput->state() == QAudio::ActiveState) { + qWarning() << "status: Active, suspend()"; + m_audioOutput->suspend(); + m_suspendResumeButton->setText(tr(RESUME_LABEL)); + } else if (m_audioOutput->state() == QAudio::StoppedState) { + qWarning() << "status: Stopped, resume()"; + m_audioOutput->resume(); + m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); + } else if (m_audioOutput->state() == QAudio::IdleState) { + qWarning() << "status: IdleState"; + } +} + +void AudioTest::stateChanged(QAudio::State state) +{ + qWarning() << "state = " << state; +} diff --git a/examples/multimedia/audiooutput/audiooutput.h b/examples/multimedia/audiooutput/audiooutput.h new file mode 100644 index 000000000..52c29cc79 --- /dev/null +++ b/examples/multimedia/audiooutput/audiooutput.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AUDIOOUTPUT_H +#define AUDIOOUTPUT_H + +#include <math.h> + +#include <QAudioOutput> +#include <QByteArray> +#include <QComboBox> +#include <QIODevice> +#include <QLabel> +#include <QMainWindow> +#include <QObject> +#include <QPushButton> +#include <QSlider> +#include <QTimer> + +class Generator : public QIODevice +{ + Q_OBJECT + +public: + Generator(const QAudioFormat &format, qint64 durationUs, int sampleRate, QObject *parent); + ~Generator(); + + void start(); + void stop(); + + qint64 readData(char *data, qint64 maxlen); + qint64 writeData(const char *data, qint64 len); + qint64 bytesAvailable() const; + +private: + void generateData(const QAudioFormat &format, qint64 durationUs, int sampleRate); + +private: + qint64 m_pos; + QByteArray m_buffer; +}; + +class AudioTest : public QMainWindow +{ + Q_OBJECT + +public: + AudioTest(); + ~AudioTest(); + +private: + void initializeWindow(); + void initializeAudio(); + void createAudioOutput(); + +private: + QTimer *m_pullTimer; + + // Owned by layout + QPushButton *m_modeButton; + QPushButton *m_suspendResumeButton; + QComboBox *m_deviceBox; + QLabel *m_volumeLabel; + QSlider *m_volumeSlider; + + QAudioDeviceInfo m_device; + Generator *m_generator; + QAudioOutput *m_audioOutput; + QIODevice *m_output; // not owned + QAudioFormat m_format; + + bool m_pullMode; + QByteArray m_buffer; + +private slots: + void notified(); + void pullTimerExpired(); + void toggleMode(); + void toggleSuspendResume(); + void stateChanged(QAudio::State state); + void deviceChanged(int index); + void volumeChanged(int); +}; + +#endif // AUDIOOUTPUT_H diff --git a/examples/multimedia/audiooutput/audiooutput.pro b/examples/multimedia/audiooutput/audiooutput.pro new file mode 100644 index 000000000..c843f010f --- /dev/null +++ b/examples/multimedia/audiooutput/audiooutput.pro @@ -0,0 +1,12 @@ +TEMPLATE = app +TARGET = audiooutput + +QT += multimedia widgets + +HEADERS = audiooutput.h + +SOURCES = audiooutput.cpp \ + main.cpp + +target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audiooutput +INSTALLS += target diff --git a/examples/multimedia/audiooutput/doc/images/audiooutput-example.png b/examples/multimedia/audiooutput/doc/images/audiooutput-example.png Binary files differnew file mode 100755 index 000000000..5588fbb57 --- /dev/null +++ b/examples/multimedia/audiooutput/doc/images/audiooutput-example.png diff --git a/examples/multimedia/audiooutput/doc/src/audiooutput.qdoc b/examples/multimedia/audiooutput/doc/src/audiooutput.qdoc new file mode 100644 index 000000000..c29fcd81d --- /dev/null +++ b/examples/multimedia/audiooutput/doc/src/audiooutput.qdoc @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example audiooutput + \title Audio Output Example + \ingroup audio_examples + \brief The Audio Output Example show the use of the QAudioOutput API. + + The example demonstrates the basic use cases of the QAudioOutput class. + + \image audiooutput-example.png + + This example provides a tone generator to supply continuous audio playback. + The first button allows pause and resume of the playback, and the second + button allows toggling between push and pull modes of operation. +*/ + + diff --git a/examples/multimedia/audiooutput/main.cpp b/examples/multimedia/audiooutput/main.cpp new file mode 100644 index 000000000..097cabb7b --- /dev/null +++ b/examples/multimedia/audiooutput/main.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> + +#include "audiooutput.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + app.setApplicationName("Audio Output Test"); + + AudioTest audio; + audio.show(); + + return app.exec(); +} |