qtmobility/src/multimedia/audio/qaudiooutput_mac_p.cpp
changeset 14 6fbed849b4f4
child 15 1f895d8a5b2b
equal deleted inserted replaced
11:06b8e2af4411 14:6fbed849b4f4
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 //
       
    43 //  W A R N I N G
       
    44 //  -------------
       
    45 //
       
    46 // This file is not part of the Qt API.  It exists for the convenience
       
    47 // of other Qt classes.  This header file may change from version to
       
    48 // version without notice, or even be removed.
       
    49 //
       
    50 // We mean it.
       
    51 //
       
    52 
       
    53 #include <CoreServices/CoreServices.h>
       
    54 #include <CoreAudio/CoreAudio.h>
       
    55 #include <AudioUnit/AudioUnit.h>
       
    56 #include <AudioToolbox/AudioToolbox.h>
       
    57 
       
    58 #include <QtCore/qendian.h>
       
    59 #include <QtCore/qbuffer.h>
       
    60 #include <QtCore/qtimer.h>
       
    61 #include <QtCore/qdebug.h>
       
    62 
       
    63 #include <qaudiodeviceinfo.h>
       
    64 #include <qaudiooutput.h>
       
    65 
       
    66 #include "qaudio_mac_p.h"
       
    67 #include "qaudiooutput_mac_p.h"
       
    68 
       
    69 
       
    70 QT_BEGIN_NAMESPACE
       
    71 
       
    72 
       
    73 namespace QtMultimediaInternal
       
    74 {
       
    75 
       
    76 static const int default_buffer_size = 8 * 1024;
       
    77 
       
    78 
       
    79 class QAudioOutputBuffer : public QObject
       
    80 {
       
    81     Q_OBJECT
       
    82 
       
    83 public:
       
    84     QAudioOutputBuffer(int bufferSize, int maxPeriodSize, QAudioFormat const& audioFormat):
       
    85         m_deviceError(false),
       
    86         m_maxPeriodSize(maxPeriodSize),
       
    87         m_device(0)
       
    88     {
       
    89         m_buffer = new QAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize)));
       
    90         m_bytesPerFrame = (audioFormat.sampleSize() / 8) * audioFormat.channels();
       
    91         m_periodTime = m_maxPeriodSize / m_bytesPerFrame * 1000 / audioFormat.frequency();
       
    92 
       
    93         m_fillTimer = new QTimer(this);
       
    94         connect(m_fillTimer, SIGNAL(timeout()), SLOT(fillBuffer()));
       
    95     }
       
    96 
       
    97     ~QAudioOutputBuffer()
       
    98     {
       
    99         delete m_buffer;
       
   100     }
       
   101 
       
   102     qint64 readFrames(char* data, qint64 maxFrames)
       
   103     {
       
   104         bool    wecan = true;
       
   105         qint64  framesRead = 0;
       
   106 
       
   107         while (wecan && framesRead < maxFrames) {
       
   108             QAudioRingBuffer::Region region = m_buffer->acquireReadRegion((maxFrames - framesRead) * m_bytesPerFrame);
       
   109 
       
   110             if (region.second > 0) {
       
   111                 region.second -= region.second % m_bytesPerFrame;
       
   112                 memcpy(data + (framesRead * m_bytesPerFrame), region.first, region.second);
       
   113                 framesRead += region.second / m_bytesPerFrame;
       
   114             }
       
   115             else
       
   116                 wecan = false;
       
   117 
       
   118             m_buffer->releaseReadRegion(region);
       
   119         }
       
   120 
       
   121         if (framesRead == 0 && m_deviceError)
       
   122             framesRead = -1;
       
   123 
       
   124         return framesRead;
       
   125     }
       
   126 
       
   127     qint64 writeBytes(const char* data, qint64 maxSize)
       
   128     {
       
   129         bool    wecan = true;
       
   130         qint64  bytesWritten = 0;
       
   131 
       
   132         maxSize -= maxSize % m_bytesPerFrame;
       
   133         while (wecan && bytesWritten < maxSize) {
       
   134             QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(maxSize - bytesWritten);
       
   135 
       
   136             if (region.second > 0) {
       
   137                 memcpy(region.first, data + bytesWritten, region.second);
       
   138                 bytesWritten += region.second;
       
   139             }
       
   140             else
       
   141                 wecan = false;
       
   142 
       
   143             m_buffer->releaseWriteRegion(region);
       
   144         }
       
   145 
       
   146         if (bytesWritten > 0)
       
   147             emit readyRead();
       
   148 
       
   149         return bytesWritten;
       
   150     }
       
   151 
       
   152     int available() const
       
   153     {
       
   154         return m_buffer->free();
       
   155     }
       
   156 
       
   157     void reset()
       
   158     {
       
   159         m_buffer->reset();
       
   160         m_deviceError = false;
       
   161     }
       
   162 
       
   163     void setPrefetchDevice(QIODevice* device)
       
   164     {
       
   165         if (m_device != device) {
       
   166             m_device = device;
       
   167             if (m_device != 0)
       
   168                 fillBuffer();
       
   169         }
       
   170     }
       
   171 
       
   172     void startFillTimer()
       
   173     {
       
   174         if (m_device != 0)
       
   175             m_fillTimer->start(m_buffer->size() / 2 / m_maxPeriodSize * m_periodTime);
       
   176     }
       
   177 
       
   178     void stopFillTimer()
       
   179     {
       
   180         m_fillTimer->stop();
       
   181     }
       
   182 
       
   183 signals:
       
   184     void readyRead();
       
   185 
       
   186 private slots:
       
   187     void fillBuffer()
       
   188     {
       
   189         const int free = m_buffer->free();
       
   190         const int writeSize = free - (free % m_maxPeriodSize);
       
   191 
       
   192         if (writeSize > 0) {
       
   193             bool    wecan = true;
       
   194             int     filled = 0;
       
   195 
       
   196             while (!m_deviceError && wecan && filled < writeSize) {
       
   197                 QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(writeSize - filled);
       
   198 
       
   199                 if (region.second > 0) {
       
   200                     region.second = m_device->read(region.first, region.second);
       
   201                     if (region.second > 0)
       
   202                         filled += region.second;
       
   203                     else if (region.second == 0)
       
   204                         wecan = false;
       
   205                     else if (region.second < 0) {
       
   206                         m_fillTimer->stop();
       
   207                         region.second = 0;
       
   208                         m_deviceError = true;
       
   209                     }
       
   210                 }
       
   211                 else
       
   212                     wecan = false;
       
   213 
       
   214                 m_buffer->releaseWriteRegion(region);
       
   215             }
       
   216 
       
   217             if (filled > 0)
       
   218                 emit readyRead();
       
   219         }
       
   220     }
       
   221 
       
   222 private:
       
   223     bool        m_deviceError;
       
   224     int         m_maxPeriodSize;
       
   225     int         m_bytesPerFrame;
       
   226     int         m_periodTime;
       
   227     QIODevice*  m_device;
       
   228     QTimer*     m_fillTimer;
       
   229     QAudioRingBuffer*  m_buffer;
       
   230 };
       
   231 
       
   232 
       
   233 }
       
   234 
       
   235 class MacOutputDevice : public QIODevice
       
   236 {
       
   237     Q_OBJECT
       
   238 
       
   239 public:
       
   240     MacOutputDevice(QtMultimediaInternal::QAudioOutputBuffer* audioBuffer, QObject* parent):
       
   241         QIODevice(parent),
       
   242         m_audioBuffer(audioBuffer)
       
   243     {
       
   244         open(QIODevice::WriteOnly | QIODevice::Unbuffered);
       
   245     }
       
   246 
       
   247     qint64 readData(char* data, qint64 len)
       
   248     {
       
   249         Q_UNUSED(data);
       
   250         Q_UNUSED(len);
       
   251 
       
   252         return 0;
       
   253     }
       
   254 
       
   255     qint64 writeData(const char* data, qint64 len)
       
   256     {
       
   257         return m_audioBuffer->writeBytes(data, len);
       
   258     }
       
   259 
       
   260     bool isSequential() const
       
   261     {
       
   262         return true;
       
   263     }
       
   264 
       
   265 private:
       
   266     QtMultimediaInternal::QAudioOutputBuffer*    m_audioBuffer;
       
   267 };
       
   268 
       
   269 
       
   270 QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray& device)
       
   271 {
       
   272     QDataStream ds(device);
       
   273     quint32 did, mode;
       
   274 
       
   275     ds >> did >> mode;
       
   276 
       
   277     if (QAudio::Mode(mode) == QAudio::AudioInput)
       
   278         errorCode = QAudio::OpenError;
       
   279     else {
       
   280         isOpen = false;
       
   281         audioDeviceId = AudioDeviceID(did);
       
   282         audioUnit = 0;
       
   283         audioIO = 0;
       
   284         startTime = 0;
       
   285         totalFrames = 0;
       
   286         audioBuffer = 0;
       
   287         internalBufferSize = QtMultimediaInternal::default_buffer_size;
       
   288         clockFrequency = AudioGetHostClockFrequency() / 1000;
       
   289         errorCode = QAudio::NoError;
       
   290         stateCode = QAudio::StoppedState;
       
   291         audioThreadState = Stopped;
       
   292 
       
   293         intervalTimer = new QTimer(this);
       
   294         intervalTimer->setInterval(1000);
       
   295         connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify()));
       
   296     }
       
   297 }
       
   298 
       
   299 QAudioOutputPrivate::~QAudioOutputPrivate()
       
   300 {
       
   301     close();
       
   302 }
       
   303 
       
   304 bool QAudioOutputPrivate::open()
       
   305 {
       
   306     if (errorCode != QAudio::NoError)
       
   307         return false;
       
   308 
       
   309     if (isOpen)
       
   310         return true;
       
   311 
       
   312     ComponentDescription    cd;
       
   313     cd.componentType = kAudioUnitType_Output;
       
   314     cd.componentSubType = kAudioUnitSubType_HALOutput;
       
   315     cd.componentManufacturer = kAudioUnitManufacturer_Apple;
       
   316     cd.componentFlags = 0;
       
   317     cd.componentFlagsMask = 0;
       
   318 
       
   319     // Open
       
   320     Component cp = FindNextComponent(NULL, &cd);
       
   321     if (cp == 0) {
       
   322         qWarning() << "QAudioOutput: Failed to find HAL Output component";
       
   323         return false;
       
   324     }
       
   325 
       
   326     if (OpenAComponent(cp, &audioUnit) != noErr) {
       
   327         qWarning() << "QAudioOutput: Unable to Open Output Component";
       
   328         return false;
       
   329     }
       
   330 
       
   331     // register callback
       
   332     AURenderCallbackStruct  cb;
       
   333     cb.inputProc = renderCallback;
       
   334     cb.inputProcRefCon = this;
       
   335 
       
   336     if (AudioUnitSetProperty(audioUnit,
       
   337                                kAudioUnitProperty_SetRenderCallback,
       
   338                                kAudioUnitScope_Global,
       
   339                                0,
       
   340                                &cb,
       
   341                                sizeof(cb)) != noErr) {
       
   342         qWarning() << "QAudioOutput: Failed to set AudioUnit callback";
       
   343         return false;
       
   344     }
       
   345 
       
   346     // Set Audio Device
       
   347     if (AudioUnitSetProperty(audioUnit,
       
   348                                 kAudioOutputUnitProperty_CurrentDevice,
       
   349                                 kAudioUnitScope_Global,
       
   350                                 0,
       
   351                                 &audioDeviceId,
       
   352                                 sizeof(audioDeviceId)) != noErr) {
       
   353         qWarning() << "QAudioOutput: Unable to use configured device";
       
   354         return false;
       
   355     }
       
   356 
       
   357     // Set stream format
       
   358     streamFormat = toAudioStreamBasicDescription(audioFormat);
       
   359 
       
   360     UInt32 size = sizeof(deviceFormat);
       
   361     if (AudioUnitGetProperty(audioUnit,
       
   362                                 kAudioUnitProperty_StreamFormat,
       
   363                                 kAudioUnitScope_Input,
       
   364                                 0,
       
   365                                 &deviceFormat,
       
   366                                 &size) != noErr) {
       
   367         qWarning() << "QAudioOutput: Unable to retrieve device format";
       
   368         return false;
       
   369     }
       
   370 
       
   371     if (AudioUnitSetProperty(audioUnit,
       
   372                                 kAudioUnitProperty_StreamFormat,
       
   373                                 kAudioUnitScope_Input,
       
   374                                 0,
       
   375                                 &streamFormat,
       
   376                                 sizeof(streamFormat)) != noErr) {
       
   377         qWarning() << "QAudioOutput: Unable to Set Stream information";
       
   378         return false;
       
   379     }
       
   380 
       
   381     // Allocate buffer
       
   382     UInt32 numberOfFrames = 0;
       
   383     size = sizeof(UInt32);
       
   384     if (AudioUnitGetProperty(audioUnit,
       
   385                                 kAudioDevicePropertyBufferFrameSize,
       
   386                                 kAudioUnitScope_Global,
       
   387                                 0,
       
   388                                 &numberOfFrames,
       
   389                                 &size) != noErr) {
       
   390         qWarning() << "QAudioInput: Failed to get audio period size";
       
   391         return false;
       
   392     }
       
   393 
       
   394     periodSizeBytes = (numberOfFrames * streamFormat.mSampleRate / deviceFormat.mSampleRate) * 
       
   395                         streamFormat.mBytesPerFrame;
       
   396     if (internalBufferSize < periodSizeBytes * 2)
       
   397         internalBufferSize = periodSizeBytes * 2;
       
   398     else
       
   399         internalBufferSize -= internalBufferSize % streamFormat.mBytesPerFrame;
       
   400 
       
   401     audioBuffer = new QtMultimediaInternal::QAudioOutputBuffer(internalBufferSize, periodSizeBytes, audioFormat);
       
   402     connect(audioBuffer, SIGNAL(readyRead()), SLOT(inputReady()));  // Pull
       
   403 
       
   404     audioIO = new MacOutputDevice(audioBuffer, this);
       
   405 
       
   406     // Init
       
   407     if (AudioUnitInitialize(audioUnit)) {
       
   408         qWarning() << "QAudioOutput: Failed to initialize AudioUnit";
       
   409         return false;
       
   410     }
       
   411 
       
   412     isOpen = true;
       
   413 
       
   414     return true;
       
   415 }
       
   416 
       
   417 void QAudioOutputPrivate::close()
       
   418 {
       
   419     if (audioUnit != 0) {
       
   420         AudioOutputUnitStop(audioUnit);
       
   421         AudioUnitUninitialize(audioUnit);
       
   422         CloseComponent(audioUnit);
       
   423     }
       
   424 
       
   425     delete audioBuffer;
       
   426 }
       
   427 
       
   428 QAudioFormat QAudioOutputPrivate::format() const
       
   429 {
       
   430     return audioFormat;
       
   431 }
       
   432 
       
   433 void QAudioOutputPrivate::setFormat(const QAudioFormat& fmt)
       
   434 {
       
   435     if (stateCode == QAudio::StoppedState)
       
   436         audioFormat = fmt;
       
   437 }
       
   438 
       
   439 void QAudioOutputPrivate::start(QIODevice* device)
       
   440 {
       
   441     QIODevice*  op = device;
       
   442 
       
   443     if (!audioFormat.isValid() || !open()) {
       
   444         stateCode = QAudio::StoppedState;
       
   445         errorCode = QAudio::OpenError;
       
   446     }
       
   447 
       
   448     reset();
       
   449     audioBuffer->reset();
       
   450     audioBuffer->setPrefetchDevice(op);
       
   451 
       
   452     if (op == 0) {
       
   453         op = audioIO;
       
   454         stateCode = QAudio::IdleState;
       
   455     }
       
   456     else
       
   457         stateCode = QAudio::ActiveState;
       
   458 
       
   459     // Start
       
   460     errorCode = QAudio::NoError;
       
   461     totalFrames = 0;
       
   462     startTime = AudioGetCurrentHostTime();
       
   463 
       
   464     if (stateCode == QAudio::ActiveState)
       
   465         audioThreadStart();
       
   466 
       
   467     emit stateChanged(stateCode);
       
   468 }
       
   469 
       
   470 QIODevice* QAudioOutputPrivate::start()
       
   471 {
       
   472     QIODevice*  op = 0;
       
   473 
       
   474     if (!audioFormat.isValid() || !open()) {
       
   475         stateCode = QAudio::StoppedState;
       
   476         errorCode = QAudio::OpenError;
       
   477         return audioIO;
       
   478     }
       
   479 
       
   480     reset();
       
   481     audioBuffer->reset();
       
   482     audioBuffer->setPrefetchDevice(op);
       
   483 
       
   484     if (op == 0) {
       
   485         op = audioIO;
       
   486         stateCode = QAudio::IdleState;
       
   487     }
       
   488     else
       
   489         stateCode = QAudio::ActiveState;
       
   490 
       
   491     // Start
       
   492     errorCode = QAudio::NoError;
       
   493     totalFrames = 0;
       
   494     startTime = AudioGetCurrentHostTime();
       
   495 
       
   496     if (stateCode == QAudio::ActiveState)
       
   497         audioThreadStart();
       
   498 
       
   499     emit stateChanged(stateCode);
       
   500 
       
   501     return op;
       
   502 }
       
   503 
       
   504 void QAudioOutputPrivate::stop()
       
   505 {
       
   506     QMutexLocker    lock(&mutex);
       
   507     if (stateCode != QAudio::StoppedState) {
       
   508         audioThreadDrain();
       
   509 
       
   510         stateCode = QAudio::StoppedState;
       
   511         errorCode = QAudio::NoError;
       
   512         QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
       
   513     }
       
   514 }
       
   515 
       
   516 void QAudioOutputPrivate::reset()
       
   517 {
       
   518     QMutexLocker    lock(&mutex);
       
   519     if (stateCode != QAudio::StoppedState) {
       
   520         audioThreadStop();
       
   521 
       
   522         stateCode = QAudio::StoppedState;
       
   523         errorCode = QAudio::NoError;
       
   524         QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
       
   525     }
       
   526 }
       
   527 
       
   528 void QAudioOutputPrivate::suspend()
       
   529 {
       
   530     QMutexLocker    lock(&mutex);
       
   531     if (stateCode == QAudio::ActiveState || stateCode == QAudio::IdleState) {
       
   532         audioThreadStop();
       
   533 
       
   534         stateCode = QAudio::SuspendedState;
       
   535         errorCode = QAudio::NoError;
       
   536         QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
       
   537     }
       
   538 }
       
   539 
       
   540 void QAudioOutputPrivate::resume()
       
   541 {
       
   542     QMutexLocker    lock(&mutex);
       
   543     if (stateCode == QAudio::SuspendedState) {
       
   544         audioThreadStart();
       
   545 
       
   546         stateCode = QAudio::ActiveState;
       
   547         errorCode = QAudio::NoError;
       
   548         QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
       
   549     }
       
   550 }
       
   551 
       
   552 int QAudioOutputPrivate::bytesFree() const
       
   553 {
       
   554     return audioBuffer->available();
       
   555 }
       
   556 
       
   557 int QAudioOutputPrivate::periodSize() const
       
   558 {
       
   559     return periodSizeBytes;
       
   560 }
       
   561 
       
   562 void QAudioOutputPrivate::setBufferSize(int bs)
       
   563 {
       
   564     if (stateCode == QAudio::StoppedState)
       
   565         internalBufferSize = bs;
       
   566 }
       
   567 
       
   568 int QAudioOutputPrivate::bufferSize() const
       
   569 {
       
   570     return internalBufferSize;
       
   571 }
       
   572 
       
   573 void QAudioOutputPrivate::setNotifyInterval(int milliSeconds)
       
   574 {
       
   575     if (intervalTimer->interval() == milliSeconds)
       
   576         return;
       
   577 
       
   578     if (milliSeconds <= 0)
       
   579         milliSeconds = 0;
       
   580 
       
   581     intervalTimer->setInterval(milliSeconds);
       
   582 }
       
   583 
       
   584 int QAudioOutputPrivate::notifyInterval() const
       
   585 {
       
   586     return intervalTimer->interval();
       
   587 }
       
   588 
       
   589 qint64 QAudioOutputPrivate::processedUSecs() const
       
   590 {
       
   591     return totalFrames * 1000000 / audioFormat.frequency();
       
   592 }
       
   593 
       
   594 qint64 QAudioOutputPrivate::elapsedUSecs() const
       
   595 {
       
   596     if (stateCode == QAudio::StoppedState)
       
   597         return 0;
       
   598 
       
   599     return (AudioGetCurrentHostTime() - startTime) / (clockFrequency / 1000);
       
   600 }
       
   601 
       
   602 QAudio::Error QAudioOutputPrivate::error() const
       
   603 {
       
   604     return errorCode;
       
   605 }
       
   606 
       
   607 QAudio::State QAudioOutputPrivate::state() const
       
   608 {
       
   609     return stateCode;
       
   610 }
       
   611 
       
   612 void QAudioOutputPrivate::audioThreadStart()
       
   613 {
       
   614     startTimers();
       
   615     audioThreadState = Running;
       
   616     AudioOutputUnitStart(audioUnit);
       
   617 }
       
   618 
       
   619 void QAudioOutputPrivate::audioThreadStop()
       
   620 {
       
   621     stopTimers();
       
   622     if (audioThreadState.testAndSetAcquire(Running, Stopped))
       
   623         threadFinished.wait(&mutex);
       
   624 }
       
   625 
       
   626 void QAudioOutputPrivate::audioThreadDrain()
       
   627 {
       
   628     stopTimers();
       
   629     if (audioThreadState.testAndSetAcquire(Running, Draining))
       
   630         threadFinished.wait(&mutex);
       
   631 }
       
   632 
       
   633 void QAudioOutputPrivate::audioDeviceStop()
       
   634 {
       
   635     AudioOutputUnitStop(audioUnit);
       
   636     audioThreadState = Stopped;
       
   637     threadFinished.wakeOne();
       
   638 }
       
   639 
       
   640 void QAudioOutputPrivate::audioDeviceIdle()
       
   641 {
       
   642     QMutexLocker    lock(&mutex);
       
   643     if (stateCode == QAudio::ActiveState) {
       
   644         audioDeviceStop();
       
   645 
       
   646         errorCode = QAudio::UnderrunError;
       
   647         stateCode = QAudio::IdleState;
       
   648         QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
       
   649     }
       
   650 }
       
   651 
       
   652 void QAudioOutputPrivate::audioDeviceError()
       
   653 {
       
   654     QMutexLocker    lock(&mutex);
       
   655     if (stateCode == QAudio::ActiveState) {
       
   656         audioDeviceStop();
       
   657 
       
   658         errorCode = QAudio::IOError;
       
   659         stateCode = QAudio::StoppedState;
       
   660         QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
       
   661     }
       
   662 }
       
   663 
       
   664 void QAudioOutputPrivate::startTimers()
       
   665 {
       
   666     audioBuffer->startFillTimer();
       
   667     if (intervalTimer->interval() > 0)
       
   668         intervalTimer->start();
       
   669 }
       
   670 
       
   671 void QAudioOutputPrivate::stopTimers()
       
   672 {
       
   673     audioBuffer->stopFillTimer();
       
   674     intervalTimer->stop();
       
   675 }
       
   676 
       
   677 
       
   678 void QAudioOutputPrivate::deviceStopped()
       
   679 {
       
   680     intervalTimer->stop();
       
   681     emit stateChanged(stateCode);
       
   682 }
       
   683 
       
   684 void QAudioOutputPrivate::inputReady()
       
   685 {
       
   686     QMutexLocker    lock(&mutex);
       
   687     if (stateCode == QAudio::IdleState) {
       
   688         audioThreadStart();
       
   689 
       
   690         stateCode = QAudio::ActiveState;
       
   691         errorCode = QAudio::NoError;
       
   692 
       
   693         QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
       
   694     }
       
   695 }
       
   696 
       
   697 
       
   698 OSStatus QAudioOutputPrivate::renderCallback(void* inRefCon,
       
   699                                     AudioUnitRenderActionFlags* ioActionFlags,
       
   700                                     const AudioTimeStamp* inTimeStamp,
       
   701                                     UInt32 inBusNumber,
       
   702                                     UInt32 inNumberFrames,
       
   703                                     AudioBufferList* ioData)
       
   704 {
       
   705     Q_UNUSED(ioActionFlags)
       
   706     Q_UNUSED(inTimeStamp)
       
   707     Q_UNUSED(inBusNumber)
       
   708     Q_UNUSED(inNumberFrames)
       
   709 
       
   710     QAudioOutputPrivate* d = static_cast<QAudioOutputPrivate*>(inRefCon);
       
   711 
       
   712     const int threadState = d->audioThreadState.fetchAndAddAcquire(0);
       
   713     if (threadState == Stopped) {
       
   714         ioData->mBuffers[0].mDataByteSize = 0;
       
   715         d->audioDeviceStop();
       
   716     }
       
   717     else {
       
   718         const UInt32    bytesPerFrame = d->streamFormat.mBytesPerFrame;
       
   719         qint64          framesRead;
       
   720 
       
   721         framesRead = d->audioBuffer->readFrames((char*)ioData->mBuffers[0].mData,
       
   722                                                  ioData->mBuffers[0].mDataByteSize / bytesPerFrame);
       
   723 
       
   724         if (framesRead > 0) {
       
   725             ioData->mBuffers[0].mDataByteSize = framesRead * bytesPerFrame;
       
   726             d->totalFrames += framesRead;
       
   727         }
       
   728         else {
       
   729             ioData->mBuffers[0].mDataByteSize = 0;
       
   730             if (framesRead == 0) {
       
   731                 if (threadState == Draining)
       
   732                     d->audioDeviceStop();
       
   733                 else
       
   734                     d->audioDeviceIdle();
       
   735             }
       
   736             else
       
   737                 d->audioDeviceError();
       
   738         }
       
   739     }
       
   740 
       
   741     return noErr;
       
   742 }
       
   743 
       
   744 
       
   745 QT_END_NAMESPACE
       
   746 
       
   747 #include "qaudiooutput_mac_p.moc"
       
   748