examples/multimedia/audiooutput/audiooutput.cpp
changeset 7 f7bc934e204c
parent 3 41300fa6a67c
child 30 5dc02b23752f
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
     1 /****************************************************************************
     1 /****************************************************************************
     2 **
     2 **
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
     4 ** All rights reserved.
     4 ** All rights reserved.
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
     6 **
     6 **
     7 ** This file is part of the examples of the Qt Toolkit.
     7 ** This file is part of the examples of the Qt Toolkit.
     8 **
     8 **
    42 #include <QDebug>
    42 #include <QDebug>
    43 #include <QVBoxLayout>
    43 #include <QVBoxLayout>
    44 
    44 
    45 #include <QAudioOutput>
    45 #include <QAudioOutput>
    46 #include <QAudioDeviceInfo>
    46 #include <QAudioDeviceInfo>
       
    47 #include <QtCore/qmath.h>
       
    48 #include <QtCore/qendian.h>
    47 #include "audiooutput.h"
    49 #include "audiooutput.h"
    48 
    50 
    49 #ifndef M_PI
    51 const QString AudioTest::PushModeLabel(tr("Enable push mode"));
    50 #define M_PI 3.14159265358979323846
    52 const QString AudioTest::PullModeLabel(tr("Enable pull mode"));
    51 #endif
    53 const QString AudioTest::SuspendLabel(tr("Suspend playback"));
    52 
    54 const QString AudioTest::ResumeLabel(tr("Resume playback"));
    53 #define SECONDS     1
    55 
    54 #define FREQ        600
    56 const int DurationSeconds = 1;
    55 #define SYSTEM_FREQ 44100
    57 const int ToneFrequencyHz = 600;
    56 
    58 const int DataFrequencyHz = 44100;
    57 Generator::Generator(QObject *parent)
    59 const int BufferSize      = 32768;
    58     :QIODevice( parent )
    60 
    59 {
    61 
    60     finished = false;
    62 Generator::Generator(const QAudioFormat &format,
    61     buffer = new char[SECONDS*SYSTEM_FREQ*4+1000];
    63                      qint64 durationUs,
    62     t=buffer;
    64                      int frequency,
    63     len=fillData(t,FREQ,SECONDS); /* mono FREQHz sine */
    65                      QObject *parent)
    64     pos   = 0;
    66     :   QIODevice(parent)
    65     total = len;
    67     ,   m_pos(0)
       
    68 {
       
    69     generateData(format, durationUs, frequency);
    66 }
    70 }
    67 
    71 
    68 Generator::~Generator()
    72 Generator::~Generator()
    69 {
    73 {
    70     delete [] buffer;
    74 
    71 }
    75 }
    72 
    76 
    73 void Generator::start()
    77 void Generator::start()
    74 {
    78 {
    75     open(QIODevice::ReadOnly);
    79     open(QIODevice::ReadOnly);
    76 }
    80 }
    77 
    81 
    78 void Generator::stop()
    82 void Generator::stop()
    79 {
    83 {
       
    84     m_pos = 0;
    80     close();
    85     close();
    81 }
    86 }
    82 
    87 
    83 int Generator::putShort(char *t, unsigned int value)
    88 void Generator::generateData(const QAudioFormat &format, qint64 durationUs, int frequency)
    84 {
    89 {
    85     *(unsigned char *)(t++)=value&255;
    90     const int channelBytes = format.sampleSize() / 8;
    86     *(unsigned char *)(t)=(value/256)&255;
    91     const int sampleBytes = format.channels() * channelBytes;
    87     return 2;
    92 
    88 }
    93     qint64 length = (format.frequency() * format.channels() * (format.sampleSize() / 8))
    89 
    94                         * durationUs / 100000;
    90 int Generator::fillData(char *start, int frequency, int seconds)
    95 
    91 {
    96     Q_ASSERT(length % sampleBytes == 0);
    92     int i, len=0;
    97     Q_UNUSED(sampleBytes) // suppress warning in release builds
    93     int value;
    98 
    94     for(i=0; i<seconds*SYSTEM_FREQ; i++) {
    99     m_buffer.resize(length);
    95         value=(int)(32767.0*sin(2.0*M_PI*((double)(i))*(double)(frequency)/SYSTEM_FREQ));
   100     unsigned char *ptr = reinterpret_cast<unsigned char *>(m_buffer.data());
    96         putShort(start, value);
   101     int sampleIndex = 0;
    97         start += 4;
   102 
    98         len+=2;
   103     while (length) {
    99     }
   104         const qreal x = qSin(2 * M_PI * frequency * qreal(sampleIndex % format.frequency()) / format.frequency());
   100     return len;
   105         for (int i=0; i<format.channels(); ++i) {
   101 }
   106             if (format.sampleSize() == 8 && format.sampleType() == QAudioFormat::UnSignedInt) {
   102 
   107                 const quint8 value = static_cast<quint8>((1.0 + x) / 2 * 255);
   103 qint64 Generator::readData(char *data, qint64 maxlen)
   108                 *reinterpret_cast<quint8*>(ptr) = value;
   104 {
   109             } else if (format.sampleSize() == 8 && format.sampleType() == QAudioFormat::SignedInt) {
   105     int len = maxlen;
   110                 const qint8 value = static_cast<qint8>(x * 127);
   106     if(len > 16384)
   111                 *reinterpret_cast<quint8*>(ptr) = value;
   107         len = 16384;
   112             } else if (format.sampleSize() == 16 && format.sampleType() == QAudioFormat::UnSignedInt) {
   108 
   113                 quint16 value = static_cast<quint16>((1.0 + x) / 2 * 65535);
   109     if(len < (SECONDS*SYSTEM_FREQ*2)-pos) {
   114                 if (format.byteOrder() == QAudioFormat::LittleEndian)
   110         // Normal
   115                     qToLittleEndian<quint16>(value, ptr);
   111         memcpy(data,t+pos,len);
   116                 else
   112         pos+=len;
   117                     qToBigEndian<quint16>(value, ptr);
   113         return len;
   118             } else if (format.sampleSize() == 16 && format.sampleType() == QAudioFormat::SignedInt) {
   114     } else {
   119                 qint16 value = static_cast<qint16>(x * 32767);
   115         // Whats left and reset to start
   120                 if (format.byteOrder() == QAudioFormat::LittleEndian)
   116         qint64 left = (SECONDS*SYSTEM_FREQ*2)-pos;
   121                     qToLittleEndian<qint16>(value, ptr);
   117         memcpy(data,t+pos,left);
   122                 else
   118         pos=0;
   123                     qToBigEndian<qint16>(value, ptr);
   119         return left;
   124             }
   120     }
   125 
       
   126             ptr += channelBytes;
       
   127             length -= channelBytes;
       
   128         }
       
   129         ++sampleIndex;
       
   130     }
       
   131 }
       
   132 
       
   133 qint64 Generator::readData(char *data, qint64 len)
       
   134 {
       
   135     qint64 total = 0;
       
   136     while (len - total) {
       
   137         const qint64 chunk = qMin((m_buffer.size() - m_pos), len - total);
       
   138         memcpy(data, m_buffer.constData() + m_pos, chunk);
       
   139         m_pos = (m_pos + chunk) % m_buffer.size();
       
   140         total += chunk;
       
   141     }
       
   142     return total;
   121 }
   143 }
   122 
   144 
   123 qint64 Generator::writeData(const char *data, qint64 len)
   145 qint64 Generator::writeData(const char *data, qint64 len)
   124 {
   146 {
   125     Q_UNUSED(data);
   147     Q_UNUSED(data);
   126     Q_UNUSED(len);
   148     Q_UNUSED(len);
   127 
   149 
   128     return 0;
   150     return 0;
   129 }
   151 }
   130 
   152 
       
   153 qint64 Generator::bytesAvailable() const
       
   154 {
       
   155     return m_buffer.size() + QIODevice::bytesAvailable();
       
   156 }
       
   157 
   131 AudioTest::AudioTest()
   158 AudioTest::AudioTest()
   132 {
   159     :   m_pullTimer(new QTimer(this))
   133     QWidget *window = new QWidget;
   160     ,   m_modeButton(0)
   134     QVBoxLayout* layout = new QVBoxLayout;
   161     ,   m_suspendResumeButton(0)
   135 
   162     ,   m_deviceBox(0)
   136     deviceBox = new QComboBox(this);
   163     ,   m_device(QAudioDeviceInfo::defaultOutputDevice())
       
   164     ,   m_generator(0)
       
   165     ,   m_audioOutput(0)
       
   166     ,   m_output(0)
       
   167     ,   m_buffer(BufferSize, 0)
       
   168 {
       
   169     initializeWindow();
       
   170     initializeAudio();
       
   171 }
       
   172 
       
   173 void AudioTest::initializeWindow()
       
   174 {
       
   175     QScopedPointer<QWidget> window(new QWidget);
       
   176     QScopedPointer<QVBoxLayout> layout(new QVBoxLayout);
       
   177 
       
   178     m_deviceBox = new QComboBox(this);
   137     foreach (const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
   179     foreach (const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
   138         deviceBox->addItem(deviceInfo.deviceName(), qVariantFromValue(deviceInfo));
   180         m_deviceBox->addItem(deviceInfo.deviceName(), qVariantFromValue(deviceInfo));
   139     connect(deviceBox,SIGNAL(activated(int)),SLOT(deviceChanged(int)));
   181     connect(m_deviceBox,SIGNAL(activated(int)),SLOT(deviceChanged(int)));
   140     layout->addWidget(deviceBox);
   182     layout->addWidget(m_deviceBox);
   141 
   183 
   142     button = new QPushButton(this);
   184     m_modeButton = new QPushButton(this);
   143     button->setText(tr("Click for Push Mode"));
   185     m_modeButton->setText(PushModeLabel);
   144     connect(button,SIGNAL(clicked()),SLOT(toggle()));
   186     connect(m_modeButton, SIGNAL(clicked()), SLOT(toggleMode()));
   145     layout->addWidget(button);
   187     layout->addWidget(m_modeButton);
   146 
   188 
   147     button2 = new QPushButton(this);
   189     m_suspendResumeButton = new QPushButton(this);
   148     button2->setText(tr("Click To Suspend"));
   190     m_suspendResumeButton->setText(SuspendLabel);
   149     connect(button2,SIGNAL(clicked()),SLOT(togglePlay()));
   191     connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume()));
   150     layout->addWidget(button2);
   192     layout->addWidget(m_suspendResumeButton);
   151 
   193 
   152     window->setLayout(layout);
   194     window->setLayout(layout.data());
   153     setCentralWidget(window);
   195     layout.take(); // ownership transferred
   154     window->show();
   196 
   155 
   197     setCentralWidget(window.data());
   156     buffer = new char[BUFFER_SIZE];
   198     QWidget *const windowPtr = window.take(); // ownership transferred
   157 
   199     windowPtr->show();
   158     gen = new Generator(this);
   200 }
   159 
   201 
   160     pullMode = true;
   202 void AudioTest::initializeAudio()
   161 
   203 {
   162     timer = new QTimer(this);
   204     connect(m_pullTimer, SIGNAL(timeout()), SLOT(pullTimerExpired()));
   163     connect(timer,SIGNAL(timeout()),SLOT(writeMore()));
   205 
   164 
   206     m_pullMode = true;
   165     gen->start();
   207 
   166 
   208     m_format.setFrequency(DataFrequencyHz);
   167     settings.setFrequency(SYSTEM_FREQ);
   209     m_format.setChannels(1);
   168     settings.setChannels(1);
   210     m_format.setSampleSize(16);
   169     settings.setSampleSize(16);
   211     m_format.setCodec("audio/pcm");
   170     settings.setCodec("audio/pcm");
   212     m_format.setByteOrder(QAudioFormat::LittleEndian);
   171     settings.setByteOrder(QAudioFormat::LittleEndian);
   213     m_format.setSampleType(QAudioFormat::SignedInt);
   172     settings.setSampleType(QAudioFormat::SignedInt);
       
   173 
   214 
   174     QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
   215     QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
   175     if (!info.isFormatSupported(settings)) {
   216     if (!info.isFormatSupported(m_format)) {
   176         qWarning()<<"default format not supported try to use nearest";
   217         qWarning() << "Default format not supported - trying to use nearest";
   177         settings = info.nearestFormat(settings);
   218         m_format = info.nearestFormat(m_format);
   178     }
   219     }
   179 
   220 
   180     if(settings.sampleSize() != 16) {
   221     m_generator = new Generator(m_format, DurationSeconds*1000000, ToneFrequencyHz, this);
   181         qWarning()<<"audio device doesn't support 16 bit samples, example cannot run";
   222 
   182         return;
   223     createAudioOutput();
   183     }
   224 }
   184 
   225 
   185     audioOutput = new QAudioOutput(settings,this);
   226 void AudioTest::createAudioOutput()
   186     connect(audioOutput,SIGNAL(notify()),SLOT(status()));
   227 {
   187     connect(audioOutput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State)));
   228     delete m_audioOutput;
   188 
   229     m_audioOutput = 0;
   189     audioOutput->start(gen);
   230     m_audioOutput = new QAudioOutput(m_device, m_format, this);
       
   231     connect(m_audioOutput, SIGNAL(notify()), SLOT(notified()));
       
   232     connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
       
   233     m_generator->start();
       
   234     m_audioOutput->start(m_generator);
   190 }
   235 }
   191 
   236 
   192 AudioTest::~AudioTest()
   237 AudioTest::~AudioTest()
   193 {
   238 {
   194     delete [] buffer;
   239 
   195 }
   240 }
   196 
   241 
   197 void AudioTest::deviceChanged(int idx)
   242 void AudioTest::deviceChanged(int index)
   198 {
   243 {
   199     timer->stop();
   244     m_pullTimer->stop();
   200     gen->stop();
   245     m_generator->stop();
   201     audioOutput->stop();
   246     m_audioOutput->stop();
   202     audioOutput->disconnect(this);
   247     m_audioOutput->disconnect(this);
   203     delete audioOutput;
   248     m_device = m_deviceBox->itemData(index).value<QAudioDeviceInfo>();
   204 
   249     createAudioOutput();
   205     device = deviceBox->itemData(idx).value<QAudioDeviceInfo>();
   250 }
   206     audioOutput = new QAudioOutput(device,settings,this);
   251 
   207     connect(audioOutput,SIGNAL(notify()),SLOT(status()));
   252 void AudioTest::notified()
   208     connect(audioOutput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State)));
   253 {
   209     gen->start();
   254     qWarning() << "bytesFree = " << m_audioOutput->bytesFree()
   210     audioOutput->start(gen);
   255                << ", " << "elapsedUSecs = " << m_audioOutput->elapsedUSecs()
   211 }
   256                << ", " << "processedUSecs = " << m_audioOutput->processedUSecs();
   212 
   257 }
   213 void AudioTest::status()
   258 
   214 {
   259 void AudioTest::pullTimerExpired()
   215     qWarning()<<"byteFree = "<<audioOutput->bytesFree()<<" bytes, elapsedUSecs = "<<audioOutput->elapsedUSecs()<<", processedUSecs = "<<audioOutput->processedUSecs();
   260 {
   216 }
   261     if (m_audioOutput && m_audioOutput->state() != QAudio::StoppedState) {
   217 
   262         int chunks = m_audioOutput->bytesFree()/m_audioOutput->periodSize();
   218 void AudioTest::writeMore()
   263         while (chunks) {
   219 {
   264            const qint64 len = m_generator->read(m_buffer.data(), m_audioOutput->periodSize());
   220     if(!audioOutput)
   265            if (len)
   221         return;
   266                m_output->write(m_buffer.data(), len);
   222 
   267            if (len != m_audioOutput->periodSize())
   223     if(audioOutput->state() == QAudio::StoppedState)
   268                break;
   224         return;
   269            --chunks;
   225 
   270         }
   226     int    l;
   271     }
   227     int    out;
   272 }
   228 
   273 
   229     int chunks = audioOutput->bytesFree()/audioOutput->periodSize();
   274 void AudioTest::toggleMode()
   230     while(chunks) {
   275 {
   231        l = gen->read(buffer,audioOutput->periodSize());
   276     m_pullTimer->stop();
   232        if(l > 0)
   277     m_audioOutput->stop();
   233            out = output->write(buffer,l);
   278 
   234        if(l != audioOutput->periodSize())
   279     if (m_pullMode) {
   235 	   break;
   280         m_modeButton->setText(PullModeLabel);
   236        chunks--;
   281         m_output = m_audioOutput->start();
   237     }
   282         m_pullMode = false;
   238 }
   283         m_pullTimer->start(20);
   239 
       
   240 void AudioTest::toggle()
       
   241 {
       
   242     // Change between pull and push modes
       
   243 
       
   244     timer->stop();
       
   245     audioOutput->stop();
       
   246 
       
   247     if (pullMode) {
       
   248         button->setText("Click for Pull Mode");
       
   249         output = audioOutput->start();
       
   250         pullMode = false;
       
   251         timer->start(20);
       
   252     } else {
   284     } else {
   253         button->setText("Click for Push Mode");
   285         m_modeButton->setText(PushModeLabel);
   254         pullMode = true;
   286         m_pullMode = true;
   255         audioOutput->start(gen);
   287         m_audioOutput->start(m_generator);
   256     }
   288     }
   257 }
   289 
   258 
   290     m_suspendResumeButton->setText(SuspendLabel);
   259 void AudioTest::togglePlay()
   291 }
   260 {
   292 
   261     // toggle suspend/resume
   293 void AudioTest::toggleSuspendResume()
   262     if(audioOutput->state() == QAudio::SuspendedState) {
   294 {
   263         qWarning()<<"status: Suspended, resume()";
   295     if (m_audioOutput->state() == QAudio::SuspendedState) {
   264         audioOutput->resume();
   296         qWarning() << "status: Suspended, resume()";
   265         button2->setText("Click To Suspend");
   297         m_audioOutput->resume();
   266     } else if (audioOutput->state() == QAudio::ActiveState) {
   298         m_suspendResumeButton->setText(SuspendLabel);
   267         qWarning()<<"status: Active, suspend()";
   299     } else if (m_audioOutput->state() == QAudio::ActiveState) {
   268         audioOutput->suspend();
   300         qWarning() << "status: Active, suspend()";
   269         button2->setText("Click To Resume");
   301         m_audioOutput->suspend();
   270     } else if (audioOutput->state() == QAudio::StoppedState) {
   302         m_suspendResumeButton->setText(ResumeLabel);
   271         qWarning()<<"status: Stopped, resume()";
   303     } else if (m_audioOutput->state() == QAudio::StoppedState) {
   272         audioOutput->resume();
   304         qWarning() << "status: Stopped, resume()";
   273         button2->setText("Click To Suspend");
   305         m_audioOutput->resume();
   274     } else if (audioOutput->state() == QAudio::IdleState) {
   306         m_suspendResumeButton->setText(SuspendLabel);
   275         qWarning()<<"status: IdleState";
   307     } else if (m_audioOutput->state() == QAudio::IdleState) {
   276     }
   308         qWarning() << "status: IdleState";
   277 }
   309     }
   278 
   310 }
   279 void AudioTest::state(QAudio::State state)
   311 
   280 {
   312 void AudioTest::stateChanged(QAudio::State state)
   281     qWarning()<<" state="<<state;
   313 {
   282 }
   314     qWarning() << "state = " << state;
       
   315 }