~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/multimedia/audio/qaudiooutput_mac_p.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alessandro Ghersi
  • Date: 2009-11-02 18:30:08 UTC
  • mfrom: (1.2.2 upstream)
  • mto: (15.2.5 experimental)
  • mto: This revision was merged to the branch mainline in revision 88.
  • Revision ID: james.westby@ubuntu.com-20091102183008-b6a4gcs128mvfb3m
Tags: upstream-4.6.0~beta1
ImportĀ upstreamĀ versionĀ 4.6.0~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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