src/multimedia/audio/qaudiooutput_mac_p.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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 QtMultimedia module of the Qt Toolkit.
       
     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 <QtMultimedia/qaudiodeviceinfo.h>
       
    64 #include <QtMultimedia/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
       
    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(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     QAudioOutputBuffer*    m_audioBuffer;
       
   267 };
       
   268 
       
   269 
       
   270 QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray& device, const QAudioFormat& format):
       
   271     audioFormat(format)
       
   272 {
       
   273     QDataStream ds(device);
       
   274     quint32 did, mode;
       
   275 
       
   276     ds >> did >> mode;
       
   277 
       
   278     if (QAudio::Mode(mode) == QAudio::AudioInput)
       
   279         errorCode = QAudio::OpenError;
       
   280     else {
       
   281         isOpen = false;
       
   282         audioDeviceId = AudioDeviceID(did);
       
   283         audioUnit = 0;
       
   284         audioIO = 0;
       
   285         startTime = 0;
       
   286         totalFrames = 0;
       
   287         audioBuffer = 0;
       
   288         internalBufferSize = default_buffer_size;
       
   289         clockFrequency = AudioGetHostClockFrequency() / 1000;
       
   290         errorCode = QAudio::NoError;
       
   291         stateCode = QAudio::StopState;
       
   292         audioThreadState = Stopped;
       
   293 
       
   294         intervalTimer = new QTimer(this);
       
   295         intervalTimer->setInterval(1000);
       
   296         connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify()));
       
   297     }
       
   298 }
       
   299 
       
   300 QAudioOutputPrivate::~QAudioOutputPrivate()
       
   301 {
       
   302     close();
       
   303 }
       
   304 
       
   305 bool QAudioOutputPrivate::open()
       
   306 {
       
   307     if (errorCode != QAudio::NoError)
       
   308         return false;
       
   309 
       
   310     if (isOpen)
       
   311         return true;
       
   312 
       
   313     ComponentDescription    cd;
       
   314     cd.componentType = kAudioUnitType_Output;
       
   315     cd.componentSubType = kAudioUnitSubType_HALOutput;
       
   316     cd.componentManufacturer = kAudioUnitManufacturer_Apple;
       
   317     cd.componentFlags = 0;
       
   318     cd.componentFlagsMask = 0;
       
   319 
       
   320     // Open
       
   321     Component cp = FindNextComponent(NULL, &cd);
       
   322     if (cp == 0) {
       
   323         qWarning() << "QAudioOutput: Failed to find HAL Output component";
       
   324         return false;
       
   325     }
       
   326 
       
   327     if (OpenAComponent(cp, &audioUnit) != noErr) {
       
   328         qWarning() << "QAudioOutput: Unable to Open Output Component";
       
   329         return false;
       
   330     }
       
   331 
       
   332     // register callback
       
   333     AURenderCallbackStruct  cb;
       
   334     cb.inputProc = renderCallback;
       
   335     cb.inputProcRefCon = this;
       
   336 
       
   337     if (AudioUnitSetProperty(audioUnit,
       
   338                                kAudioUnitProperty_SetRenderCallback,
       
   339                                kAudioUnitScope_Global,
       
   340                                0,
       
   341                                &cb,
       
   342                                sizeof(cb)) != noErr) {
       
   343         qWarning() << "QAudioOutput: Failed to set AudioUnit callback";
       
   344         return false;
       
   345     }
       
   346 
       
   347     // Set Audio Device
       
   348     if (AudioUnitSetProperty(audioUnit,
       
   349                                 kAudioOutputUnitProperty_CurrentDevice,
       
   350                                 kAudioUnitScope_Global,
       
   351                                 0,
       
   352                                 &audioDeviceId,
       
   353                                 sizeof(audioDeviceId)) != noErr) {
       
   354         qWarning() << "QAudioOutput: Unable to use configured device";
       
   355         return false;
       
   356     }
       
   357 
       
   358     // Set stream format
       
   359     streamFormat = toAudioStreamBasicDescription(audioFormat);
       
   360 
       
   361     UInt32 size = sizeof(deviceFormat);
       
   362     if (AudioUnitGetProperty(audioUnit,
       
   363                                 kAudioUnitProperty_StreamFormat,
       
   364                                 kAudioUnitScope_Input,
       
   365                                 0,
       
   366                                 &deviceFormat,
       
   367                                 &size) != noErr) {
       
   368         qWarning() << "QAudioOutput: Unable to retrieve device format";
       
   369         return false;
       
   370     }
       
   371 
       
   372     if (AudioUnitSetProperty(audioUnit,
       
   373                                 kAudioUnitProperty_StreamFormat,
       
   374                                 kAudioUnitScope_Input,
       
   375                                 0,
       
   376                                 &streamFormat,
       
   377                                 sizeof(streamFormat)) != noErr) {
       
   378         qWarning() << "QAudioOutput: Unable to Set Stream information";
       
   379         return false;
       
   380     }
       
   381 
       
   382     // Allocate buffer
       
   383     UInt32 numberOfFrames = 0;
       
   384     size = sizeof(UInt32);
       
   385     if (AudioUnitGetProperty(audioUnit,
       
   386                                 kAudioDevicePropertyBufferFrameSize,
       
   387                                 kAudioUnitScope_Global,
       
   388                                 0,
       
   389                                 &numberOfFrames,
       
   390                                 &size) != noErr) {
       
   391         qWarning() << "QAudioInput: Failed to get audio period size";
       
   392         return false;
       
   393     }
       
   394 
       
   395     periodSizeBytes = (numberOfFrames * streamFormat.mSampleRate / deviceFormat.mSampleRate) * 
       
   396                         streamFormat.mBytesPerFrame;
       
   397     if (internalBufferSize < periodSizeBytes * 2)
       
   398         internalBufferSize = periodSizeBytes * 2;
       
   399     else
       
   400         internalBufferSize -= internalBufferSize % streamFormat.mBytesPerFrame;
       
   401 
       
   402     audioBuffer = new QAudioOutputBuffer(internalBufferSize, periodSizeBytes, audioFormat);
       
   403     connect(audioBuffer, SIGNAL(readyRead()), SLOT(inputReady()));  // Pull
       
   404 
       
   405     audioIO = new MacOutputDevice(audioBuffer, this);
       
   406 
       
   407     // Init
       
   408     if (AudioUnitInitialize(audioUnit)) {
       
   409         qWarning() << "QAudioOutput: Failed to initialize AudioUnit";
       
   410         return false;
       
   411     }
       
   412 
       
   413     isOpen = true;
       
   414 
       
   415     return true;
       
   416 }
       
   417 
       
   418 void QAudioOutputPrivate::close()
       
   419 {
       
   420     if (audioUnit != 0) {
       
   421         AudioOutputUnitStop(audioUnit);
       
   422         AudioUnitUninitialize(audioUnit);
       
   423         CloseComponent(audioUnit);
       
   424     }
       
   425 
       
   426     delete audioBuffer;
       
   427 }
       
   428 
       
   429 QAudioFormat QAudioOutputPrivate::format() const
       
   430 {
       
   431     return audioFormat;
       
   432 }
       
   433 
       
   434 QIODevice* QAudioOutputPrivate::start(QIODevice* device)
       
   435 {
       
   436     QIODevice*  op = device;
       
   437 
       
   438     if (!open()) {
       
   439         stateCode = QAudio::StopState;
       
   440         errorCode = QAudio::OpenError;
       
   441         return audioIO;
       
   442     }
       
   443 
       
   444     reset();
       
   445     audioBuffer->reset();
       
   446     audioBuffer->setPrefetchDevice(op);
       
   447 
       
   448     if (op == 0) {
       
   449         op = audioIO;
       
   450         stateCode = QAudio::IdleState;
       
   451     }
       
   452     else
       
   453         stateCode = QAudio::ActiveState;
       
   454 
       
   455     // Start
       
   456     errorCode = QAudio::NoError;
       
   457     totalFrames = 0;
       
   458     startTime = AudioGetCurrentHostTime();
       
   459 
       
   460     if (stateCode == QAudio::ActiveState)
       
   461         audioThreadStart();
       
   462 
       
   463     emit stateChanged(stateCode);
       
   464 
       
   465     return op;
       
   466 }
       
   467 
       
   468 void QAudioOutputPrivate::stop()
       
   469 {
       
   470     QMutexLocker    lock(&mutex);
       
   471     if (stateCode != QAudio::StopState) {
       
   472         audioThreadDrain();
       
   473 
       
   474         stateCode = QAudio::StopState;
       
   475         errorCode = QAudio::NoError;
       
   476         QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
       
   477     }
       
   478 }
       
   479 
       
   480 void QAudioOutputPrivate::reset()
       
   481 {
       
   482     QMutexLocker    lock(&mutex);
       
   483     if (stateCode != QAudio::StopState) {
       
   484         audioThreadStop();
       
   485 
       
   486         stateCode = QAudio::StopState;
       
   487         errorCode = QAudio::NoError;
       
   488         QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
       
   489     }
       
   490 }
       
   491 
       
   492 void QAudioOutputPrivate::suspend()
       
   493 {
       
   494     QMutexLocker    lock(&mutex);
       
   495     if (stateCode == QAudio::ActiveState || stateCode == QAudio::IdleState) {
       
   496         audioThreadStop();
       
   497 
       
   498         stateCode = QAudio::SuspendState;
       
   499         errorCode = QAudio::NoError;
       
   500         QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
       
   501     }
       
   502 }
       
   503 
       
   504 void QAudioOutputPrivate::resume()
       
   505 {
       
   506     QMutexLocker    lock(&mutex);
       
   507     if (stateCode == QAudio::SuspendState) {
       
   508         audioThreadStart();
       
   509 
       
   510         stateCode = QAudio::ActiveState;
       
   511         errorCode = QAudio::NoError;
       
   512         QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
       
   513     }
       
   514 }
       
   515 
       
   516 int QAudioOutputPrivate::bytesFree() const
       
   517 {
       
   518     return audioBuffer->available();
       
   519 }
       
   520 
       
   521 int QAudioOutputPrivate::periodSize() const
       
   522 {
       
   523     return periodSizeBytes;
       
   524 }
       
   525 
       
   526 void QAudioOutputPrivate::setBufferSize(int bs)
       
   527 {
       
   528     if (stateCode == QAudio::StopState)
       
   529         internalBufferSize = bs;
       
   530 }
       
   531 
       
   532 int QAudioOutputPrivate::bufferSize() const
       
   533 {
       
   534     return internalBufferSize;
       
   535 }
       
   536 
       
   537 void QAudioOutputPrivate::setNotifyInterval(int milliSeconds)
       
   538 {
       
   539     intervalTimer->setInterval(milliSeconds);
       
   540 }
       
   541 
       
   542 int QAudioOutputPrivate::notifyInterval() const
       
   543 {
       
   544     return intervalTimer->interval();
       
   545 }
       
   546 
       
   547 qint64 QAudioOutputPrivate::totalTime() const
       
   548 {
       
   549     return totalFrames * 1000000 / audioFormat.frequency();
       
   550 }
       
   551 
       
   552 qint64 QAudioOutputPrivate::clock() const
       
   553 {
       
   554     if (stateCode == QAudio::StopState)
       
   555         return 0;
       
   556 
       
   557     return (AudioGetCurrentHostTime() - startTime) / (clockFrequency / 1000);
       
   558 }
       
   559 
       
   560 QAudio::Error QAudioOutputPrivate::error() const
       
   561 {
       
   562     return errorCode;
       
   563 }
       
   564 
       
   565 QAudio::State QAudioOutputPrivate::state() const
       
   566 {
       
   567     return stateCode;
       
   568 }
       
   569 
       
   570 void QAudioOutputPrivate::audioThreadStart()
       
   571 {
       
   572     startTimers();
       
   573     audioThreadState = Running;
       
   574     AudioOutputUnitStart(audioUnit);
       
   575 }
       
   576 
       
   577 void QAudioOutputPrivate::audioThreadStop()
       
   578 {
       
   579     stopTimers();
       
   580     if (audioThreadState.testAndSetAcquire(Running, Stopped))
       
   581         threadFinished.wait(&mutex);
       
   582 }
       
   583 
       
   584 void QAudioOutputPrivate::audioThreadDrain()
       
   585 {
       
   586     stopTimers();
       
   587     if (audioThreadState.testAndSetAcquire(Running, Draining))
       
   588         threadFinished.wait(&mutex);
       
   589 }
       
   590 
       
   591 void QAudioOutputPrivate::audioDeviceStop()
       
   592 {
       
   593     AudioOutputUnitStop(audioUnit);
       
   594     audioThreadState = Stopped;
       
   595     threadFinished.wakeOne();
       
   596 }
       
   597 
       
   598 void QAudioOutputPrivate::audioDeviceIdle()
       
   599 {
       
   600     QMutexLocker    lock(&mutex);
       
   601     if (stateCode == QAudio::ActiveState) {
       
   602         audioDeviceStop();
       
   603 
       
   604         errorCode = QAudio::UnderrunError;
       
   605         stateCode = QAudio::IdleState;
       
   606         QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
       
   607     }
       
   608 }
       
   609 
       
   610 void QAudioOutputPrivate::audioDeviceError()
       
   611 {
       
   612     QMutexLocker    lock(&mutex);
       
   613     if (stateCode == QAudio::ActiveState) {
       
   614         audioDeviceStop();
       
   615 
       
   616         errorCode = QAudio::IOError;
       
   617         stateCode = QAudio::StopState;
       
   618         QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
       
   619     }
       
   620 }
       
   621 
       
   622 void QAudioOutputPrivate::startTimers()
       
   623 {
       
   624     audioBuffer->startFillTimer();
       
   625     intervalTimer->start();
       
   626 }
       
   627 
       
   628 void QAudioOutputPrivate::stopTimers()
       
   629 {
       
   630     audioBuffer->stopFillTimer();
       
   631     intervalTimer->stop();
       
   632 }
       
   633 
       
   634 
       
   635 void QAudioOutputPrivate::deviceStopped()
       
   636 {
       
   637     intervalTimer->stop();
       
   638     emit stateChanged(stateCode);
       
   639 }
       
   640 
       
   641 void QAudioOutputPrivate::inputReady()
       
   642 {
       
   643     QMutexLocker    lock(&mutex);
       
   644     if (stateCode == QAudio::IdleState) {
       
   645         audioThreadStart();
       
   646 
       
   647         stateCode = QAudio::ActiveState;
       
   648         errorCode = QAudio::NoError;
       
   649 
       
   650         QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
       
   651     }
       
   652 }
       
   653 
       
   654 
       
   655 OSStatus QAudioOutputPrivate::renderCallback(void* inRefCon,
       
   656                                     AudioUnitRenderActionFlags* ioActionFlags,
       
   657                                     const AudioTimeStamp* inTimeStamp,
       
   658                                     UInt32 inBusNumber,
       
   659                                     UInt32 inNumberFrames,
       
   660                                     AudioBufferList* ioData)
       
   661 {
       
   662     Q_UNUSED(ioActionFlags)
       
   663     Q_UNUSED(inTimeStamp)
       
   664     Q_UNUSED(inBusNumber)
       
   665     Q_UNUSED(inNumberFrames)
       
   666 
       
   667     QAudioOutputPrivate* d = static_cast<QAudioOutputPrivate*>(inRefCon);
       
   668 
       
   669     const int threadState = d->audioThreadState.fetchAndAddAcquire(0);
       
   670     if (threadState == Stopped) {
       
   671         ioData->mBuffers[0].mDataByteSize = 0;
       
   672         d->audioDeviceStop();
       
   673     }
       
   674     else {
       
   675         const UInt32    bytesPerFrame = d->streamFormat.mBytesPerFrame;
       
   676         qint64          framesRead;
       
   677 
       
   678         framesRead = d->audioBuffer->readFrames((char*)ioData->mBuffers[0].mData,
       
   679                                                  ioData->mBuffers[0].mDataByteSize / bytesPerFrame);
       
   680 
       
   681         if (framesRead > 0) {
       
   682             ioData->mBuffers[0].mDataByteSize = framesRead * bytesPerFrame;
       
   683             d->totalFrames += framesRead;
       
   684         }
       
   685         else {
       
   686             ioData->mBuffers[0].mDataByteSize = 0;
       
   687             if (framesRead == 0) {
       
   688                 if (threadState == Draining)
       
   689                     d->audioDeviceStop();
       
   690                 else
       
   691                     d->audioDeviceIdle();
       
   692             }
       
   693             else
       
   694                 d->audioDeviceError();
       
   695         }
       
   696     }
       
   697 
       
   698     return noErr;
       
   699 }
       
   700 
       
   701 
       
   702 QT_END_NAMESPACE
       
   703 
       
   704 #include "qaudiooutput_mac_p.moc"
       
   705