CoreAudio: Create an audio plugin supporting iOS and OS X
authorAndy Nichols <andy.nichols@digia.com>
Fri, 8 Mar 2013 14:18:36 +0000 (15:18 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Thu, 19 Sep 2013 12:36:28 +0000 (14:36 +0200)
This removes the Mac audio backend that was hardcoded into QtMultimedia
and adds a new audio plugin using the CoreAudio API.

Change-Id: Ib15291825f9452a3763e0eeb281d952deb0bad3d
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@digia.com>
Reviewed-by: Christian Stromme <christian.stromme@digia.com>
Reviewed-by: Yoann Lopes <yoann.lopes@digia.com>
22 files changed:
src/multimedia/audio/audio.pri
src/multimedia/audio/qaudiodevicefactory.cpp
src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp [deleted file]
src/multimedia/audio/qaudioinput_mac_p.cpp [deleted file]
src/multimedia/audio/qaudioinput_mac_p.h [deleted file]
src/multimedia/audio/qaudiooutput_mac_p.cpp [deleted file]
src/plugins/coreaudio/coreaudio.json [new file with mode: 0644]
src/plugins/coreaudio/coreaudio.pro [new file with mode: 0644]
src/plugins/coreaudio/coreaudiodeviceinfo.h [moved from src/multimedia/audio/qaudiodeviceinfo_mac_p.h with 79% similarity]
src/plugins/coreaudio/coreaudiodeviceinfo.mm [new file with mode: 0644]
src/plugins/coreaudio/coreaudioinput.h [new file with mode: 0644]
src/plugins/coreaudio/coreaudioinput.mm [new file with mode: 0644]
src/plugins/coreaudio/coreaudiooutput.h [moved from src/multimedia/audio/qaudiooutput_mac_p.h with 52% similarity]
src/plugins/coreaudio/coreaudiooutput.mm [new file with mode: 0644]
src/plugins/coreaudio/coreaudioplugin.h [new file with mode: 0644]
src/plugins/coreaudio/coreaudioplugin.mm [new file with mode: 0644]
src/plugins/coreaudio/coreaudiosessionmanager.h [new file with mode: 0644]
src/plugins/coreaudio/coreaudiosessionmanager.mm [new file with mode: 0644]
src/plugins/coreaudio/coreaudioutils.h [moved from src/multimedia/audio/qaudio_mac_p.h with 54% similarity]
src/plugins/coreaudio/coreaudioutils.mm [moved from src/multimedia/audio/qaudio_mac.cpp with 60% similarity]
src/plugins/plugins.pro
tests/auto/integration/qaudiooutput/tst_qaudiooutput.cpp

index f0706e3..f76d132 100644 (file)
@@ -39,20 +39,6 @@ SOURCES += \
            audio/qaudiodecoder.cpp \
            audio/qaudiohelpers.cpp
 
-mac:!ios {
-
-    PRIVATE_HEADERS +=  audio/qaudioinput_mac_p.h \
-                audio/qaudiooutput_mac_p.h \
-                audio/qaudiodeviceinfo_mac_p.h \
-                audio/qaudio_mac_p.h
-
-    SOURCES += audio/qaudiodeviceinfo_mac_p.cpp \
-               audio/qaudiooutput_mac_p.cpp \
-               audio/qaudioinput_mac_p.cpp \
-               audio/qaudio_mac.cpp
-    LIBS += -framework ApplicationServices -framework CoreAudio -framework AudioUnit -framework AudioToolbox
-}
-
 win32 {
     PRIVATE_HEADERS += audio/qaudioinput_win32_p.h audio/qaudiooutput_win32_p.h audio/qaudiodeviceinfo_win32_p.h
     SOURCES += audio/qaudiodeviceinfo_win32_p.cpp \
index 76f1227..e2d4ec3 100644 (file)
 #include "qaudiodeviceinfo_win32_p.h"
 #include "qaudiooutput_win32_p.h"
 #include "qaudioinput_win32_p.h"
-#elif defined(Q_OS_MAC) && !defined(Q_OS_IOS)
-#include "qaudiodeviceinfo_mac_p.h"
-#include "qaudiooutput_mac_p.h"
-#include "qaudioinput_mac_p.h"
 #elif defined(HAS_ALSA)
 #include "qaudiodeviceinfo_alsa_p.h"
 #include "qaudiooutput_alsa_p.h"
@@ -137,7 +133,7 @@ QList<QAudioDeviceInfo> QAudioDeviceFactory::availableDevices(QAudio::Mode mode)
 {
     QList<QAudioDeviceInfo> devices;
 #ifndef QT_NO_AUDIO_BACKEND
-#if (defined(Q_OS_WIN) || (defined(Q_OS_MAC) && !defined(Q_OS_IOS)) || defined(HAS_ALSA))
+#if (defined(Q_OS_WIN) || defined(HAS_ALSA))
     foreach (const QByteArray &handle, QAudioDeviceInfoInternal::availableDevices(mode))
         devices << QAudioDeviceInfo(QLatin1String("builtin"), handle, mode);
 #endif
@@ -170,7 +166,7 @@ QAudioDeviceInfo QAudioDeviceFactory::defaultInputDevice()
 #endif
 
 #ifndef QT_NO_AUDIO_BACKEND
-#if (defined(Q_OS_WIN) || (defined(Q_OS_MAC) && !defined(Q_OS_IOS)) || defined(HAS_ALSA))
+#if (defined(Q_OS_WIN) || defined(HAS_ALSA))
     return QAudioDeviceInfo(QLatin1String("builtin"), QAudioDeviceInfoInternal::defaultInputDevice(), QAudio::AudioInput);
 #endif
 #endif
@@ -190,7 +186,7 @@ QAudioDeviceInfo QAudioDeviceFactory::defaultOutputDevice()
 #endif
 
 #ifndef QT_NO_AUDIO_BACKEND
-#if (defined(Q_OS_WIN) || (defined(Q_OS_MAC) && !defined(Q_OS_IOS)) || defined(HAS_ALSA))
+#if (defined(Q_OS_WIN) || defined(HAS_ALSA))
     return QAudioDeviceInfo(QLatin1String("builtin"), QAudioDeviceInfoInternal::defaultOutputDevice(), QAudio::AudioOutput);
 #endif
 #endif
@@ -202,7 +198,7 @@ QAbstractAudioDeviceInfo* QAudioDeviceFactory::audioDeviceInfo(const QString &re
     QAbstractAudioDeviceInfo *rc = 0;
 
 #ifndef QT_NO_AUDIO_BACKEND
-#if (defined(Q_OS_WIN) || (defined(Q_OS_MAC) && !defined(Q_OS_IOS)) || defined(HAS_ALSA))
+#if (defined(Q_OS_WIN) || defined(HAS_ALSA))
     if (realm == QLatin1String("builtin"))
         return new QAudioDeviceInfoInternal(handle, mode);
 #endif
@@ -234,7 +230,7 @@ QAbstractAudioInput* QAudioDeviceFactory::createInputDevice(QAudioDeviceInfo con
     if (deviceInfo.isNull())
         return new QNullInputDevice();
 #ifndef QT_NO_AUDIO_BACKEND
-#if (defined(Q_OS_WIN) || (defined(Q_OS_MAC) && !defined(Q_OS_IOS))  || defined(HAS_ALSA))
+#if (defined(Q_OS_WIN) || defined(HAS_ALSA))
     if (deviceInfo.realm() == QLatin1String("builtin")) {
         QAbstractAudioInput* p = new QAudioInputPrivate(deviceInfo.handle());
         if (p) p->setFormat(format);
@@ -261,7 +257,7 @@ QAbstractAudioOutput* QAudioDeviceFactory::createOutputDevice(QAudioDeviceInfo c
     if (deviceInfo.isNull())
         return new QNullOutputDevice();
 #ifndef QT_NO_AUDIO_BACKEND
-#if (defined(Q_OS_WIN) || (defined(Q_OS_MAC) && !defined(Q_OS_IOS)) || defined(HAS_ALSA))
+#if (defined(Q_OS_WIN) || defined(HAS_ALSA))
     if (deviceInfo.realm() == QLatin1String("builtin")) {
         QAbstractAudioOutput* p = new QAudioOutputPrivate(deviceInfo.handle());
         if (p) p->setFormat(format);
diff --git a/src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp b/src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp
deleted file mode 100644 (file)
index 0afa655..0000000
+++ /dev/null
@@ -1,351 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia.  For licensing terms and
-** conditions see http://qt.digia.com/licensing.  For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights.  These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//
-//  W A R N I N G
-//  -------------
-//
-// This file is not part of the Qt API.  It exists for the convenience
-// of other Qt classes.  This header file may change from version to
-// version without notice, or even be removed.
-//
-// INTERNAL USE ONLY: Do NOT use for any other purpose.
-//
-
-#include <QtCore/qstringlist.h>
-#include <QtCore/qlist.h>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qdatastream.h>
-#include <QtCore/qdebug.h>
-
-#include <qaudiodeviceinfo.h>
-#include "qaudio_mac_p.h"
-#include "qaudiodeviceinfo_mac_p.h"
-
-
-
-QT_BEGIN_NAMESPACE
-
-// XXX: remove at some future date
-static inline QString cfStringToQString(CFStringRef str)
-{
-    CFIndex length = CFStringGetLength(str);
-    const UniChar *chars = CFStringGetCharactersPtr(str);
-    if (chars)
-        return QString(reinterpret_cast<const QChar *>(chars), length);
-
-    UniChar buffer[length];
-    CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
-    return QString(reinterpret_cast<const QChar *>(buffer), length);
-}
-
-QAudioDeviceInfoInternal::QAudioDeviceInfoInternal(QByteArray const& handle, QAudio::Mode)
-{
-    QDataStream ds(handle);
-    quint32 did, tm;
-
-    ds >> did >> tm >> name;
-    deviceId = AudioDeviceID(did);
-    mode = QAudio::Mode(tm);
-}
-
-bool QAudioDeviceInfoInternal::isFormatSupported(const QAudioFormat& format) const
-{
-    QAudioDeviceInfoInternal *self = const_cast<QAudioDeviceInfoInternal*>(this);
-
-    return format.isValid()
-            && format.codec() == QString::fromLatin1("audio/pcm")
-            && self->supportedSampleRates().contains(format.sampleRate())
-            && self->supportedChannelCounts().contains(format.channelCount())
-            && self->supportedSampleSizes().contains(format.sampleSize());
-}
-
-QAudioFormat QAudioDeviceInfoInternal::preferredFormat() const
-{
-    QAudioFormat    rc;
-
-    UInt32  propSize = 0;
-
-    if (AudioDeviceGetPropertyInfo(deviceId,
-                                    0,
-                                    mode == QAudio::AudioInput,
-                                    kAudioDevicePropertyStreams,
-                                    &propSize,
-                                    0) == noErr) {
-
-        const int sc = propSize / sizeof(AudioStreamID);
-
-        if (sc > 0) {
-            AudioStreamID*  streams = new AudioStreamID[sc];
-
-            if (AudioDeviceGetProperty(deviceId,
-                                        0,
-                                        mode == QAudio::AudioInput,
-                                        kAudioDevicePropertyStreams,
-                                        &propSize,
-                                        streams) == noErr) {
-
-                for (int i = 0; i < sc; ++i) {
-                    if (AudioStreamGetPropertyInfo(streams[i],
-                                                    0,
-                                                    kAudioStreamPropertyPhysicalFormat,
-                                                    &propSize,
-                                                    0) == noErr) {
-
-                        AudioStreamBasicDescription sf;
-
-                        if (AudioStreamGetProperty(streams[i],
-                                                    0,
-                                                    kAudioStreamPropertyPhysicalFormat,
-                                                    &propSize,
-                                                    &sf) == noErr) {
-                            rc = toQAudioFormat(sf);
-                            break;
-                        }
-                    }
-                }
-            }
-
-            delete streams;
-        }
-    }
-
-    return rc;
-}
-
-QString QAudioDeviceInfoInternal::deviceName() const
-{
-    return name;
-}
-
-QStringList QAudioDeviceInfoInternal::supportedCodecs()
-{
-    return QStringList() << QString::fromLatin1("audio/pcm");
-}
-
-QList<int> QAudioDeviceInfoInternal::supportedSampleRates()
-{
-    QSet<int>  rc;
-
-    // Add some common frequencies
-    rc << 8000 << 11025 << 22050 << 44100;
-
-    //
-    UInt32  propSize = 0;
-
-    if (AudioDeviceGetPropertyInfo(deviceId,
-                                    0,
-                                    mode == QAudio::AudioInput,
-                                    kAudioDevicePropertyAvailableNominalSampleRates,
-                                    &propSize,
-                                    0) == noErr) {
-
-        const int pc = propSize / sizeof(AudioValueRange);
-
-        if (pc > 0) {
-            AudioValueRange*    vr = new AudioValueRange[pc];
-
-            if (AudioDeviceGetProperty(deviceId,
-                                        0,
-                                        mode == QAudio::AudioInput,
-                                        kAudioDevicePropertyAvailableNominalSampleRates,
-                                        &propSize,
-                                        vr) == noErr) {
-
-                for (int i = 0; i < pc; ++i)
-                    rc << vr[i].mMaximum;
-            }
-
-            delete vr;
-        }
-    }
-
-    return rc.toList();
-}
-
-QList<int> QAudioDeviceInfoInternal::supportedChannelCounts()
-{
-    QList<int>  rc;
-
-    // Can mix down to 1 channel
-    rc << 1;
-
-    UInt32  propSize = 0;
-    int     channels = 0;
-
-    if (AudioDeviceGetPropertyInfo(deviceId, 
-                                    0,
-                                    mode == QAudio::AudioInput,
-                                    kAudioDevicePropertyStreamConfiguration,
-                                    &propSize, 
-                                    0) == noErr) {
-
-        AudioBufferList* audioBufferList = static_cast<AudioBufferList*>(malloc(propSize));
-
-        if (audioBufferList != 0) {
-            if (AudioDeviceGetProperty(deviceId, 
-                                        0,
-                                        mode == QAudio::AudioInput,
-                                        kAudioDevicePropertyStreamConfiguration,
-                                        &propSize,
-                                        audioBufferList) == noErr) {
-
-                for (int i = 0; i < int(audioBufferList->mNumberBuffers); ++i) {
-                    channels += audioBufferList->mBuffers[i].mNumberChannels;
-                    rc << channels;
-                }
-            }
-
-            free(audioBufferList);
-        }
-    }
-
-    return rc;
-}
-
-QList<int> QAudioDeviceInfoInternal::supportedSampleSizes()
-{
-    return QList<int>() << 8 << 16 << 24 << 32 << 64;
-}
-
-QList<QAudioFormat::Endian> QAudioDeviceInfoInternal::supportedByteOrders()
-{
-    return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian << QAudioFormat::BigEndian;
-}
-
-QList<QAudioFormat::SampleType> QAudioDeviceInfoInternal::supportedSampleTypes()
-{
-    return QList<QAudioFormat::SampleType>() << QAudioFormat::SignedInt << QAudioFormat::UnSignedInt << QAudioFormat::Float;
-}
-
-static QByteArray get_device_info(AudioDeviceID audioDevice, QAudio::Mode mode)
-{
-    UInt32      size;
-    QByteArray  device;
-    QDataStream ds(&device, QIODevice::WriteOnly);
-    AudioStreamBasicDescription     sf;
-    CFStringRef name;
-    Boolean     isInput = mode == QAudio::AudioInput;
-
-    // Id
-    ds << quint32(audioDevice);
-
-    // Mode
-    size = sizeof(AudioStreamBasicDescription);
-    if (AudioDeviceGetProperty(audioDevice, 0, isInput, kAudioDevicePropertyStreamFormat,
-                                &size, &sf) != noErr) {
-        return QByteArray();
-    }
-    ds << quint32(mode);
-
-    // Name
-    size = sizeof(CFStringRef);
-    if (AudioDeviceGetProperty(audioDevice, 0, isInput, kAudioObjectPropertyName,
-                                &size, &name) != noErr) {
-        qWarning() << "QAudioDeviceInfo: Unable to find device name";
-        return QByteArray();
-    }
-    ds << cfStringToQString(name);
-
-    CFRelease(name);
-
-    return device;
-}
-
-QByteArray QAudioDeviceInfoInternal::defaultInputDevice()
-{
-    AudioDeviceID   audioDevice;
-    UInt32          size = sizeof(audioDevice);
-
-    if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &size,
-                                    &audioDevice) != noErr) {
-        qWarning() << "QAudioDeviceInfo: Unable to find default input device";
-        return QByteArray();
-    }
-
-    return get_device_info(audioDevice, QAudio::AudioInput);
-}
-
-QByteArray QAudioDeviceInfoInternal::defaultOutputDevice()
-{
-    AudioDeviceID audioDevice;
-    UInt32        size = sizeof(audioDevice);
-
-    if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size,
-                                    &audioDevice) != noErr) {
-        qWarning() << "QAudioDeviceInfo: Unable to find default output device";
-        return QByteArray();
-    }
-
-    return get_device_info(audioDevice, QAudio::AudioOutput);
-}
-
-QList<QByteArray> QAudioDeviceInfoInternal::availableDevices(QAudio::Mode mode)
-{
-    QList<QByteArray>   devices;
-
-    UInt32  propSize = 0;
-
-    if (AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propSize, 0) == noErr) {
-
-        const int dc = propSize / sizeof(AudioDeviceID);
-
-        if (dc > 0) {
-            AudioDeviceID*  audioDevices = new AudioDeviceID[dc];
-
-            if (AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propSize, audioDevices) == noErr) {
-                for (int i = 0; i < dc; ++i) {
-                    QByteArray info = get_device_info(audioDevices[i], mode);
-                    if (!info.isNull())
-                        devices << info;
-                }
-            }
-
-            delete audioDevices;
-        }
-    }
-
-    return devices;
-}
-
-
-QT_END_NAMESPACE
-
diff --git a/src/multimedia/audio/qaudioinput_mac_p.cpp b/src/multimedia/audio/qaudioinput_mac_p.cpp
deleted file mode 100644 (file)
index ea5be5d..0000000
+++ /dev/null
@@ -1,1067 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia.  For licensing terms and
-** conditions see http://qt.digia.com/licensing.  For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights.  These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//
-//  W A R N I N G
-//  -------------
-//
-// This file is not part of the Qt API.  It exists for the convenience
-// of other Qt classes.  This header file may change from version to
-// version without notice, or even be removed.
-//
-// INTERNAL USE ONLY: Do NOT use for any other purpose.
-//
-
-#include <QtCore/qendian.h>
-#include <QtCore/qtimer.h>
-#include <QtCore/qdebug.h>
-
-#include <qaudioinput.h>
-
-#include "qaudio_mac_p.h"
-#include "qaudioinput_mac_p.h"
-#include "qaudiodeviceinfo_mac_p.h"
-#include "qaudiohelpers_p.h"
-
-QT_BEGIN_NAMESPACE
-
-
-namespace QtMultimediaInternal
-{
-
-static const int default_buffer_size = 4 * 1024;
-
-class QAudioBufferList
-{
-public:
-    QAudioBufferList(AudioStreamBasicDescription const& streamFormat):
-        owner(false),
-        sf(streamFormat)
-    {
-        const bool isInterleaved = (sf.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
-        const int numberOfBuffers = isInterleaved ? 1 : sf.mChannelsPerFrame;
-
-        dataSize = 0;
-
-        bfs = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) +
-                                                                (sizeof(AudioBuffer) * numberOfBuffers)));
-
-        bfs->mNumberBuffers = numberOfBuffers;
-        for (int i = 0; i < numberOfBuffers; ++i) {
-            bfs->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1;
-            bfs->mBuffers[i].mDataByteSize = 0;
-            bfs->mBuffers[i].mData = 0;
-        }
-    }
-
-    QAudioBufferList(AudioStreamBasicDescription const& streamFormat, char* buffer, int bufferSize):
-        owner(false),
-        sf(streamFormat),
-        bfs(0)
-    {
-        dataSize = bufferSize;
-
-        bfs = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer)));
-
-        bfs->mNumberBuffers = 1;
-        bfs->mBuffers[0].mNumberChannels = 1;
-        bfs->mBuffers[0].mDataByteSize = dataSize;
-        bfs->mBuffers[0].mData = buffer;
-    }
-
-    QAudioBufferList(AudioStreamBasicDescription const& streamFormat, int framesToBuffer):
-        owner(true),
-        sf(streamFormat),
-        bfs(0)
-    {
-        const bool isInterleaved = (sf.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
-        const int numberOfBuffers = isInterleaved ? 1 : sf.mChannelsPerFrame;
-
-        dataSize = framesToBuffer * sf.mBytesPerFrame;
-
-        bfs = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) +
-                                                                (sizeof(AudioBuffer) * numberOfBuffers)));
-        bfs->mNumberBuffers = numberOfBuffers;
-        for (int i = 0; i < numberOfBuffers; ++i) {
-            bfs->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1;
-            bfs->mBuffers[i].mDataByteSize = dataSize;
-            bfs->mBuffers[i].mData = malloc(dataSize);
-        }
-    }
-
-    ~QAudioBufferList()
-    {
-        if (owner) {
-            for (UInt32 i = 0; i < bfs->mNumberBuffers; ++i)
-                free(bfs->mBuffers[i].mData);
-        }
-
-        free(bfs);
-    }
-
-    AudioBufferList* audioBufferList() const
-    {
-        return bfs;
-    }
-
-    char* data(int buffer = 0) const
-    {
-        return static_cast<char*>(bfs->mBuffers[buffer].mData);
-    }
-
-    qint64 bufferSize(int buffer = 0) const
-    {
-        return bfs->mBuffers[buffer].mDataByteSize;
-    }
-
-    int frameCount(int buffer = 0) const
-    {
-        return bfs->mBuffers[buffer].mDataByteSize / sf.mBytesPerFrame;
-    }
-
-    int packetCount(int buffer = 0) const
-    {
-        return bfs->mBuffers[buffer].mDataByteSize / sf.mBytesPerPacket;
-    }
-
-    int packetSize() const
-    {
-        return sf.mBytesPerPacket;
-    }
-
-    void reset()
-    {
-        for (UInt32 i = 0; i < bfs->mNumberBuffers; ++i) {
-            bfs->mBuffers[i].mDataByteSize = dataSize;
-            bfs->mBuffers[i].mData = 0;
-        }
-    }
-
-private:
-    bool    owner;
-    int     dataSize;
-    AudioStreamBasicDescription sf;
-    AudioBufferList* bfs;
-};
-
-class QAudioPacketFeeder
-{
-public:
-    QAudioPacketFeeder(QAudioBufferList* abl):
-        audioBufferList(abl)
-    {
-        totalPackets = audioBufferList->packetCount();
-        position = 0;
-    }
-
-    bool feed(AudioBufferList& dst, UInt32& packetCount)
-    {
-        if (position == totalPackets) {
-            dst.mBuffers[0].mDataByteSize = 0;
-            packetCount = 0;
-            return false;
-        }
-
-        if (totalPackets - position < packetCount)
-            packetCount = totalPackets - position;
-
-        dst.mBuffers[0].mDataByteSize = packetCount * audioBufferList->packetSize();
-        dst.mBuffers[0].mData = audioBufferList->data() + (position * audioBufferList->packetSize());
-
-        position += packetCount;
-
-        return true;
-    }
-
-    bool empty() const
-    {
-        return position == totalPackets;
-    }
-
-private:
-    UInt32 totalPackets;
-    UInt32 position;
-    QAudioBufferList*   audioBufferList;
-};
-
-class QAudioInputBuffer : public QObject
-{
-    Q_OBJECT
-
-public:
-    QAudioInputBuffer(int bufferSize,
-                        int maxPeriodSize,
-                        AudioStreamBasicDescription const& inputFormat,
-                        AudioStreamBasicDescription const& outputFormat,
-                        QObject* parent):
-        QObject(parent),
-        m_deviceError(false),
-        m_audioConverter(0),
-        m_inputFormat(inputFormat),
-        m_outputFormat(outputFormat),
-        m_volume(qreal(1.0f))
-    {
-        m_maxPeriodSize = maxPeriodSize;
-        m_periodTime = m_maxPeriodSize / m_outputFormat.mBytesPerFrame * 1000 / m_outputFormat.mSampleRate;
-
-        m_buffer = new QAudioRingBuffer(bufferSize);
-
-        m_inputBufferList = new QAudioBufferList(m_inputFormat);
-
-        m_flushTimer = new QTimer(this);
-        connect(m_flushTimer, SIGNAL(timeout()), SLOT(flushBuffer()));
-
-        if (toQAudioFormat(inputFormat) != toQAudioFormat(outputFormat)) {
-            if (AudioConverterNew(&m_inputFormat, &m_outputFormat, &m_audioConverter) != noErr) {
-                qWarning() << "QAudioInput: Unable to create an Audio Converter";
-                m_audioConverter = 0;
-            }
-        }
-
-        m_qFormat = toQAudioFormat(inputFormat); // we adjust volume before conversion
-    }
-
-    ~QAudioInputBuffer()
-    {
-        delete m_buffer;
-    }
-
-    qreal volume() const
-    {
-        return m_volume;
-    }
-
-    void setVolume(qreal v)
-    {
-        m_volume = v;
-    }
-
-    qint64 renderFromDevice(AudioUnit audioUnit,
-                             AudioUnitRenderActionFlags* ioActionFlags,
-                             const AudioTimeStamp* inTimeStamp,
-                             UInt32 inBusNumber,
-                             UInt32 inNumberFrames)
-    {
-        const bool  pullMode = m_device == 0;
-
-        OSStatus    err;
-        qint64      framesRendered = 0;
-
-        m_inputBufferList->reset();
-        err = AudioUnitRender(audioUnit,
-                                ioActionFlags,
-                                inTimeStamp,
-                                inBusNumber,
-                                inNumberFrames,
-                                m_inputBufferList->audioBufferList());
-
-        // adjust volume, if necessary
-        if (!qFuzzyCompare(m_volume, qreal(1.0f))) {
-            QAudioHelperInternal::qMultiplySamples(m_volume,
-                                                   m_qFormat,
-                                                   m_inputBufferList->data(), /* input */
-                                                   m_inputBufferList->data(), /* output */
-                                                   m_inputBufferList->bufferSize());
-        }
-
-        if (m_audioConverter != 0) {
-            QAudioPacketFeeder  feeder(m_inputBufferList);
-
-            int     copied = 0;
-            const int available = m_buffer->free();
-
-            while (err == noErr && !feeder.empty()) {
-                QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied);
-
-                if (region.second == 0)
-                    break;
-
-                AudioBufferList     output;
-                output.mNumberBuffers = 1;
-                output.mBuffers[0].mNumberChannels = 1;
-                output.mBuffers[0].mDataByteSize = region.second;
-                output.mBuffers[0].mData = region.first;
-
-                UInt32  packetSize = region.second / m_outputFormat.mBytesPerPacket;
-                err = AudioConverterFillComplexBuffer(m_audioConverter,
-                                                      converterCallback,
-                                                      &feeder,
-                                                      &packetSize,
-                                                      &output,
-                                                      0);
-                region.second = output.mBuffers[0].mDataByteSize;
-                copied += region.second;
-
-                m_buffer->releaseWriteRegion(region);
-            }
-
-            framesRendered += copied / m_outputFormat.mBytesPerFrame;
-        }
-        else {
-            const int available = m_inputBufferList->bufferSize();
-            bool    wecan = true;
-            int     copied = 0;
-
-            while (wecan && copied < available) {
-                QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied);
-
-                if (region.second > 0) {
-                    memcpy(region.first, m_inputBufferList->data() + copied, region.second);
-                    copied += region.second;
-                }
-                else
-                    wecan = false;
-
-                m_buffer->releaseWriteRegion(region);
-            }
-
-            framesRendered = copied / m_outputFormat.mBytesPerFrame;
-        }
-
-        if (pullMode && framesRendered > 0)
-            emit readyRead();
-
-        return framesRendered;
-    }
-
-    qint64 readBytes(char* data, qint64 len)
-    {
-        bool    wecan = true;
-        qint64  bytesCopied = 0;
-
-        len -= len % m_maxPeriodSize;
-        while (wecan && bytesCopied < len) {
-            QAudioRingBuffer::Region region = m_buffer->acquireReadRegion(len - bytesCopied);
-
-            if (region.second > 0) {
-                memcpy(data + bytesCopied, region.first, region.second);
-                bytesCopied += region.second;
-            }
-            else
-                wecan = false;
-
-            m_buffer->releaseReadRegion(region);
-        }
-
-        return bytesCopied;
-    }
-
-    void setFlushDevice(QIODevice* device)
-    {
-        if (m_device != device)
-            m_device = device;
-    }
-
-    void startFlushTimer()
-    {
-        if (m_device != 0) {
-            // We use the period time for the timer, since that's
-            // around the buffer size (pre conversion >.>)
-            m_flushTimer->start(qMax(1, m_periodTime));
-        }
-    }
-
-    void stopFlushTimer()
-    {
-        m_flushTimer->stop();
-    }
-
-    void flush(bool all = false)
-    {
-        if (m_device == 0)
-            return;
-
-        const int used = m_buffer->used();
-        const int readSize = all ? used : used - (used % m_maxPeriodSize);
-
-        if (readSize > 0) {
-            bool    wecan = true;
-            int     flushed = 0;
-
-            while (!m_deviceError && wecan && flushed < readSize) {
-                QAudioRingBuffer::Region region = m_buffer->acquireReadRegion(readSize - flushed);
-
-                if (region.second > 0) {
-                    int bytesWritten = m_device->write(region.first, region.second);
-                    if (bytesWritten < 0) {
-                        stopFlushTimer();
-                        m_deviceError = true;
-                    }
-                    else {
-                        region.second = bytesWritten;
-                        flushed += bytesWritten;
-                        wecan = bytesWritten != 0;
-                    }
-                }
-                else
-                    wecan = false;
-
-                m_buffer->releaseReadRegion(region);
-            }
-        }
-    }
-
-    void reset()
-    {
-        m_buffer->reset();
-        m_deviceError = false;
-    }
-
-    int available() const
-    {
-        return m_buffer->free();
-    }
-
-    int used() const
-    {
-        return m_buffer->used();
-    }
-
-signals:
-    void readyRead();
-
-private slots:
-    void flushBuffer()
-    {
-        flush();
-    }
-
-private:
-    bool        m_deviceError;
-    int         m_maxPeriodSize;
-    int         m_periodTime;
-    QIODevice*  m_device;
-    QTimer*     m_flushTimer;
-    QAudioRingBuffer*   m_buffer;
-    QAudioBufferList*   m_inputBufferList;
-    AudioConverterRef   m_audioConverter;
-    AudioStreamBasicDescription m_inputFormat;
-    AudioStreamBasicDescription m_outputFormat;
-    QAudioFormat m_qFormat;
-    qreal     m_volume;
-
-    const static OSStatus as_empty = 'qtem';
-
-    // Converter callback
-    static OSStatus converterCallback(AudioConverterRef inAudioConverter,
-                                UInt32* ioNumberDataPackets,
-                                AudioBufferList* ioData,
-                                AudioStreamPacketDescription** outDataPacketDescription,
-                                void* inUserData)
-    {
-        Q_UNUSED(inAudioConverter);
-        Q_UNUSED(outDataPacketDescription);
-
-        QAudioPacketFeeder* feeder = static_cast<QAudioPacketFeeder*>(inUserData);
-
-        if (!feeder->feed(*ioData, *ioNumberDataPackets))
-            return as_empty;
-
-        return noErr;
-    }
-};
-
-
-class MacInputDevice : public QIODevice
-{
-    Q_OBJECT
-
-public:
-    MacInputDevice(QAudioInputBuffer* audioBuffer, QObject* parent):
-        QIODevice(parent),
-        m_audioBuffer(audioBuffer)
-    {
-        open(QIODevice::ReadOnly | QIODevice::Unbuffered);
-        connect(m_audioBuffer, SIGNAL(readyRead()), SIGNAL(readyRead()));
-    }
-
-    qint64 readData(char* data, qint64 len)
-    {
-        return m_audioBuffer->readBytes(data, len);
-    }
-
-    qint64 writeData(const char* data, qint64 len)
-    {
-        Q_UNUSED(data);
-        Q_UNUSED(len);
-
-        return 0;
-    }
-
-    bool isSequential() const
-    {
-        return true;
-    }
-
-private:
-    QAudioInputBuffer*   m_audioBuffer;
-};
-
-}
-
-
-QAudioInputPrivate::QAudioInputPrivate(const QByteArray& device)
-{
-    QDataStream ds(device);
-    quint32 did, mode;
-
-    ds >> did >> mode;
-
-    if (QAudio::Mode(mode) == QAudio::AudioOutput)
-        errorCode = QAudio::OpenError;
-    else {
-        audioDeviceInfo = new QAudioDeviceInfoInternal(device, QAudio::AudioInput);
-        isOpen = false;
-        audioDeviceId = AudioDeviceID(did);
-        audioUnit = 0;
-        startTime = 0;
-        totalFrames = 0;
-        audioBuffer = 0;
-        internalBufferSize = QtMultimediaInternal::default_buffer_size;
-        clockFrequency = AudioGetHostClockFrequency() / 1000;
-        errorCode = QAudio::NoError;
-        stateCode = QAudio::StoppedState;
-
-        m_volume = qreal(1.0f);
-
-        intervalTimer = new QTimer(this);
-        intervalTimer->setInterval(1000);
-        connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify()));
-    }
-}
-
-QAudioInputPrivate::~QAudioInputPrivate()
-{
-    close();
-    delete audioDeviceInfo;
-}
-
-bool QAudioInputPrivate::open()
-{
-    UInt32  size = 0;
-
-    if (isOpen)
-        return true;
-
-    ComponentDescription    cd;
-    cd.componentType = kAudioUnitType_Output;
-    cd.componentSubType = kAudioUnitSubType_HALOutput;
-    cd.componentManufacturer = kAudioUnitManufacturer_Apple;
-    cd.componentFlags = 0;
-    cd.componentFlagsMask = 0;
-
-    // Open
-    Component cp = FindNextComponent(NULL, &cd);
-    if (cp == 0) {
-        qWarning() << "QAudioInput: Failed to find HAL Output component";
-        return false;
-    }
-
-    if (OpenAComponent(cp, &audioUnit) != noErr) {
-        qWarning() << "QAudioInput: Unable to Open Output Component";
-        return false;
-    }
-
-    // Set mode
-    // switch to input mode
-    UInt32 enable = 1;
-    if (AudioUnitSetProperty(audioUnit,
-                               kAudioOutputUnitProperty_EnableIO,
-                               kAudioUnitScope_Input,
-                               1,
-                               &enable,
-                               sizeof(enable)) != noErr) {
-        qWarning() << "QAudioInput: Unable to switch to input mode (Enable Input)";
-        return false;
-    }
-
-    enable = 0;
-    if (AudioUnitSetProperty(audioUnit,
-                            kAudioOutputUnitProperty_EnableIO,
-                            kAudioUnitScope_Output,
-                            0,
-                            &enable,
-                            sizeof(enable)) != noErr) {
-        qWarning() << "QAudioInput: Unable to switch to input mode (Disable output)";
-        return false;
-    }
-
-    // register callback
-    AURenderCallbackStruct cb;
-    cb.inputProc = inputCallback;
-    cb.inputProcRefCon = this;
-
-    if (AudioUnitSetProperty(audioUnit,
-                               kAudioOutputUnitProperty_SetInputCallback,
-                               kAudioUnitScope_Global,
-                               0,
-                               &cb,
-                               sizeof(cb)) != noErr) {
-        qWarning() << "QAudioInput: Failed to set AudioUnit callback";
-        return false;
-    }
-
-    // Set Audio Device
-    if (AudioUnitSetProperty(audioUnit,
-                                kAudioOutputUnitProperty_CurrentDevice,
-                                kAudioUnitScope_Global,
-                                0,
-                                &audioDeviceId,
-                                sizeof(audioDeviceId)) != noErr) {
-        qWarning() << "QAudioInput: Unable to use configured device";
-        return false;
-    }
-
-    // Set format
-    // Wanted
-    streamFormat = toAudioStreamBasicDescription(audioFormat);
-
-    // Required on unit
-    if (audioFormat == audioDeviceInfo->preferredFormat()) {
-        deviceFormat = streamFormat;
-        AudioUnitSetProperty(audioUnit,
-                               kAudioUnitProperty_StreamFormat,
-                               kAudioUnitScope_Output,
-                               1,
-                               &deviceFormat,
-                               sizeof(deviceFormat));
-    }
-    else {
-        size = sizeof(deviceFormat);
-        if (AudioUnitGetProperty(audioUnit,
-                                    kAudioUnitProperty_StreamFormat,
-                                    kAudioUnitScope_Input,
-                                    1,
-                                    &deviceFormat,
-                                    &size) != noErr) {
-            qWarning() << "QAudioInput: Unable to retrieve device format";
-            return false;
-        }
-
-        if (AudioUnitSetProperty(audioUnit,
-                                   kAudioUnitProperty_StreamFormat,
-                                   kAudioUnitScope_Output,
-                                   1,
-                                   &deviceFormat,
-                                   sizeof(deviceFormat)) != noErr) {
-            qWarning() << "QAudioInput: Unable to set device format";
-            return false;
-        }
-    }
-
-    // Setup buffers
-    UInt32 numberOfFrames;
-    size = sizeof(UInt32);
-    if (AudioUnitGetProperty(audioUnit,
-                                kAudioDevicePropertyBufferFrameSize,
-                                kAudioUnitScope_Global,
-                                0,
-                                &numberOfFrames,
-                                &size) != noErr) {
-        qWarning() << "QAudioInput: Failed to get audio period size";
-        return false;
-    }
-
-    AudioValueRange bufferRange;
-    size = sizeof(AudioValueRange);
-
-    if (AudioUnitGetProperty(audioUnit,
-                             kAudioDevicePropertyBufferFrameSizeRange,
-                             kAudioUnitScope_Global,
-                             0,
-                             &bufferRange,
-                             &size) != noErr) {
-        qWarning() << "QAudioInput: Failed to get audio period size range";
-        return false;
-    }
-
-    // See if the requested buffer size is permissible
-    UInt32 frames = qBound((UInt32)bufferRange.mMinimum, internalBufferSize / streamFormat.mBytesPerFrame, (UInt32)bufferRange.mMaximum);
-
-    // Set it back
-    if (AudioUnitSetProperty(audioUnit,
-                             kAudioDevicePropertyBufferFrameSize,
-                             kAudioUnitScope_Global,
-                             0,
-                             &frames,
-                             sizeof(UInt32)) != noErr) {
-        qWarning() << "QAudioInput: Failed to set audio buffer size";
-        return false;
-    }
-
-    // Now allocate a few buffers to be safe.
-    periodSizeBytes = internalBufferSize = frames * streamFormat.mBytesPerFrame;
-
-    audioBuffer = new QtMultimediaInternal::QAudioInputBuffer(internalBufferSize * 4,
-                                        periodSizeBytes,
-                                        deviceFormat,
-                                        streamFormat,
-                                        this);
-
-    audioBuffer->setVolume(m_volume);
-    audioIO = new QtMultimediaInternal::MacInputDevice(audioBuffer, this);
-
-    // Init
-    if (AudioUnitInitialize(audioUnit) != noErr) {
-        qWarning() << "QAudioInput: Failed to initialize AudioUnit";
-        return false;
-    }
-
-    isOpen = true;
-
-    return isOpen;
-}
-
-void QAudioInputPrivate::close()
-{
-    if (audioUnit != 0) {
-        AudioOutputUnitStop(audioUnit);
-        AudioUnitUninitialize(audioUnit);
-        CloseComponent(audioUnit);
-    }
-
-    delete audioBuffer;
-}
-
-QAudioFormat QAudioInputPrivate::format() const
-{
-    return audioFormat;
-}
-
-void QAudioInputPrivate::setFormat(const QAudioFormat& fmt)
-{
-    if (stateCode == QAudio::StoppedState)
-        audioFormat = fmt;
-}
-
-void QAudioInputPrivate::start(QIODevice* device)
-{
-    QIODevice*  op = device;
-
-    if (!audioDeviceInfo->isFormatSupported(audioFormat) || !open()) {
-        stateCode = QAudio::StoppedState;
-        errorCode = QAudio::OpenError;
-        return;
-    }
-
-    reset();
-    audioBuffer->reset();
-    audioBuffer->setFlushDevice(op);
-
-    if (op == 0)
-        op = audioIO;
-
-    // Start
-    startTime = AudioGetCurrentHostTime();
-    totalFrames = 0;
-
-    stateCode = QAudio::IdleState;
-    errorCode = QAudio::NoError;
-    emit stateChanged(stateCode);
-
-    audioThreadStart();
-}
-
-QIODevice* QAudioInputPrivate::start()
-{
-    QIODevice*  op = 0;
-
-    if (!audioDeviceInfo->isFormatSupported(audioFormat) || !open()) {
-        stateCode = QAudio::StoppedState;
-        errorCode = QAudio::OpenError;
-        return audioIO;
-    }
-
-    reset();
-    audioBuffer->reset();
-    audioBuffer->setFlushDevice(op);
-
-    if (op == 0)
-        op = audioIO;
-
-    // Start
-    startTime = AudioGetCurrentHostTime();
-    totalFrames = 0;
-
-    stateCode = QAudio::IdleState;
-    errorCode = QAudio::NoError;
-    emit stateChanged(stateCode);
-
-    audioThreadStart();
-
-    return op;
-}
-
-void QAudioInputPrivate::stop()
-{
-    QMutexLocker    lock(&mutex);
-    if (stateCode != QAudio::StoppedState) {
-        audioThreadStop();
-        audioBuffer->flush(true);
-
-        errorCode = QAudio::NoError;
-        stateCode = QAudio::StoppedState;
-        QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
-    }
-}
-
-void QAudioInputPrivate::reset()
-{
-    QMutexLocker    lock(&mutex);
-    if (stateCode != QAudio::StoppedState) {
-        audioThreadStop();
-
-        errorCode = QAudio::NoError;
-        stateCode = QAudio::StoppedState;
-        audioBuffer->reset();
-        QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
-    }
-}
-
-void QAudioInputPrivate::suspend()
-{
-    QMutexLocker    lock(&mutex);
-    if (stateCode == QAudio::ActiveState || stateCode == QAudio::IdleState) {
-        audioThreadStop();
-
-        errorCode = QAudio::NoError;
-        stateCode = QAudio::SuspendedState;
-        QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
-    }
-}
-
-void QAudioInputPrivate::resume()
-{
-    QMutexLocker    lock(&mutex);
-    if (stateCode == QAudio::SuspendedState) {
-        audioThreadStart();
-
-        errorCode = QAudio::NoError;
-        stateCode = QAudio::ActiveState;
-        QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
-    }
-}
-
-int QAudioInputPrivate::bytesReady() const
-{
-    if (!audioBuffer)
-        return 0;
-    return audioBuffer->used();
-}
-
-int QAudioInputPrivate::periodSize() const
-{
-    return periodSizeBytes;
-}
-
-void QAudioInputPrivate::setBufferSize(int bs)
-{
-    internalBufferSize = bs;
-}
-
-int QAudioInputPrivate::bufferSize() const
-{
-    return internalBufferSize;
-}
-
-void QAudioInputPrivate::setNotifyInterval(int milliSeconds)
-{
-    if (intervalTimer->interval() == milliSeconds)
-        return;
-
-    if (milliSeconds <= 0)
-        milliSeconds = 0;
-
-    intervalTimer->setInterval(milliSeconds);
-}
-
-int QAudioInputPrivate::notifyInterval() const
-{
-    return intervalTimer->interval();
-}
-
-qint64 QAudioInputPrivate::processedUSecs() const
-{
-    return totalFrames * 1000000 / audioFormat.sampleRate();
-}
-
-qint64 QAudioInputPrivate::elapsedUSecs() const
-{
-    if (stateCode == QAudio::StoppedState)
-        return 0;
-
-    return (AudioGetCurrentHostTime() - startTime) / (clockFrequency / 1000);
-}
-
-QAudio::Error QAudioInputPrivate::error() const
-{
-    return errorCode;
-}
-
-QAudio::State QAudioInputPrivate::state() const
-{
-    return stateCode;
-}
-
-qreal QAudioInputPrivate::volume() const
-{
-    return m_volume;
-}
-
-void QAudioInputPrivate::setVolume(qreal volume)
-{
-    m_volume = volume;
-    if (audioBuffer)
-        audioBuffer->setVolume(m_volume);
-}
-
-
-void QAudioInputPrivate::audioThreadStop()
-{
-    stopTimers();
-    if (audioThreadState.testAndSetAcquire(Running, Stopped))
-        threadFinished.wait(&mutex);
-}
-
-void QAudioInputPrivate::audioThreadStart()
-{
-    startTimers();
-    audioThreadState.store(Running);
-    AudioOutputUnitStart(audioUnit);
-}
-
-void QAudioInputPrivate::audioDeviceStop()
-{
-    AudioOutputUnitStop(audioUnit);
-    audioThreadState.store(Stopped);
-    threadFinished.wakeOne();
-}
-
-void QAudioInputPrivate::audioDeviceActive()
-{
-    QMutexLocker    lock(&mutex);
-    if (stateCode == QAudio::IdleState) {
-        stateCode = QAudio::ActiveState;
-        QMetaObject::invokeMethod(this, "stateChanged",  Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
-    }
-}
-
-void QAudioInputPrivate::audioDeviceFull()
-{
-    QMutexLocker    lock(&mutex);
-    if (stateCode == QAudio::ActiveState) {
-        errorCode = QAudio::UnderrunError;
-        stateCode = QAudio::IdleState;
-        QMetaObject::invokeMethod(this, "stateChanged",  Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
-    }
-}
-
-void QAudioInputPrivate::audioDeviceError()
-{
-    QMutexLocker    lock(&mutex);
-    if (stateCode == QAudio::ActiveState) {
-        audioDeviceStop();
-
-        errorCode = QAudio::IOError;
-        stateCode = QAudio::StoppedState;
-        QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
-    }
-}
-
-void QAudioInputPrivate::startTimers()
-{
-    audioBuffer->startFlushTimer();
-    if (intervalTimer->interval() > 0)
-        intervalTimer->start();
-}
-
-void QAudioInputPrivate::stopTimers()
-{
-    audioBuffer->stopFlushTimer();
-    intervalTimer->stop();
-}
-
-void QAudioInputPrivate::deviceStopped()
-{
-    stopTimers();
-    emit stateChanged(stateCode);
-}
-
-// Input callback
-OSStatus QAudioInputPrivate::inputCallback(void* inRefCon,
-                                AudioUnitRenderActionFlags* ioActionFlags,
-                                const AudioTimeStamp* inTimeStamp,
-                                UInt32 inBusNumber,
-                                UInt32 inNumberFrames,
-                                AudioBufferList* ioData)
-{
-    Q_UNUSED(ioData);
-
-    QAudioInputPrivate* d = static_cast<QAudioInputPrivate*>(inRefCon);
-
-    const int threadState = d->audioThreadState.loadAcquire();
-    if (threadState == Stopped)
-        d->audioDeviceStop();
-    else {
-        qint64      framesWritten;
-
-        framesWritten = d->audioBuffer->renderFromDevice(d->audioUnit,
-                                                         ioActionFlags,
-                                                         inTimeStamp,
-                                                         inBusNumber,
-                                                         inNumberFrames);
-
-        if (framesWritten > 0) {
-            d->totalFrames += framesWritten;
-            d->audioDeviceActive();
-        } else if (framesWritten == 0)
-            d->audioDeviceFull();
-        else if (framesWritten < 0)
-            d->audioDeviceError();
-    }
-
-    return noErr;
-}
-
-
-QT_END_NAMESPACE
-
-#include "qaudioinput_mac_p.moc"
diff --git a/src/multimedia/audio/qaudioinput_mac_p.h b/src/multimedia/audio/qaudioinput_mac_p.h
deleted file mode 100644 (file)
index 7faaa69..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia.  For licensing terms and
-** conditions see http://qt.digia.com/licensing.  For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights.  These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//
-//  W A R N I N G
-//  -------------
-//
-// This file is not part of the Qt API.  It exists for the convenience
-// of other Qt classes.  This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-
-#ifndef QAUDIOINPUT_MAC_P_H
-#define QAUDIOINPUT_MAC_P_H
-
-#include <CoreServices/CoreServices.h>
-#include <CoreAudio/CoreAudio.h>
-#include <AudioUnit/AudioUnit.h>
-#include <AudioToolbox/AudioToolbox.h>
-
-#include <QtCore/qobject.h>
-#include <QtCore/qmutex.h>
-#include <QtCore/qwaitcondition.h>
-#include <QtCore/qatomic.h>
-
-#include <qaudio.h>
-#include <qaudioformat.h>
-#include <qaudiosystem.h>
-
-QT_BEGIN_NAMESPACE
-
-
-class QTimer;
-class QIODevice;
-class QAbstractAudioDeviceInfo;
-
-namespace QtMultimediaInternal
-{
-class QAudioInputBuffer;
-}
-
-class QAudioInputPrivate : public QAbstractAudioInput
-{
-    Q_OBJECT
-
-public:
-    bool            isOpen;
-    int             periodSizeBytes;
-    int             internalBufferSize;
-    qint64          totalFrames;
-    QAudioFormat    audioFormat;
-    QIODevice*      audioIO;
-    AudioUnit       audioUnit;
-    AudioDeviceID   audioDeviceId;
-    Float64         clockFrequency;
-    UInt64          startTime;
-    QAudio::Error   errorCode;
-    QAudio::State   stateCode;
-    QtMultimediaInternal::QAudioInputBuffer*   audioBuffer;
-    QMutex          mutex;
-    QWaitCondition  threadFinished;
-    QAtomicInt      audioThreadState;
-    QTimer*         intervalTimer;
-    AudioStreamBasicDescription streamFormat;
-    AudioStreamBasicDescription deviceFormat;
-    QAbstractAudioDeviceInfo *audioDeviceInfo;
-    qreal           m_volume;
-
-    QAudioInputPrivate(const QByteArray& device);
-    ~QAudioInputPrivate();
-
-    bool open();
-    void close();
-
-    QAudioFormat format() const;
-    void setFormat(const QAudioFormat& fmt);
-
-    QIODevice* start();
-    void start(QIODevice* device);
-    void stop();
-    void reset();
-    void suspend();
-    void resume();
-    void idle();
-
-    int bytesReady() const;
-    int periodSize() const;
-
-    void setBufferSize(int value);
-    int bufferSize() const;
-
-    void setNotifyInterval(int milliSeconds);
-    int notifyInterval() const;
-
-    qint64 processedUSecs() const;
-    qint64 elapsedUSecs() const;
-
-    QAudio::Error error() const;
-    QAudio::State state() const;
-
-    qreal volume() const;
-    void setVolume(qreal volume);
-
-    void audioThreadStart();
-    void audioThreadStop();
-
-    void audioDeviceStop();
-    void audioDeviceActive();
-    void audioDeviceFull();
-    void audioDeviceError();
-
-    void startTimers();
-    void stopTimers();
-
-private slots:
-    void deviceStopped();
-
-private:
-    enum { Running, Stopped };
-
-    // Input callback
-    static OSStatus inputCallback(void* inRefCon,
-                                    AudioUnitRenderActionFlags* ioActionFlags,
-                                    const AudioTimeStamp* inTimeStamp,
-                                    UInt32 inBusNumber,
-                                    UInt32 inNumberFrames,
-                                    AudioBufferList* ioData);
-};
-
-QT_END_NAMESPACE
-
-#endif // QAUDIOINPUT_MAC_P_H
diff --git a/src/multimedia/audio/qaudiooutput_mac_p.cpp b/src/multimedia/audio/qaudiooutput_mac_p.cpp
deleted file mode 100644 (file)
index 8ac4007..0000000
+++ /dev/null
@@ -1,759 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia.  For licensing terms and
-** conditions see http://qt.digia.com/licensing.  For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights.  These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//
-//  W A R N I N G
-//  -------------
-//
-// This file is not part of the Qt API.  It exists for the convenience
-// of other Qt classes.  This header file may change from version to
-// version without notice, or even be removed.
-//
-// INTERNAL USE ONLY: Do NOT use for any other purpose.
-//
-
-#include <CoreServices/CoreServices.h>
-#include <CoreAudio/CoreAudio.h>
-#include <AudioUnit/AudioUnit.h>
-#include <AudioToolbox/AudioToolbox.h>
-
-#include <QtCore/qendian.h>
-#include <QtCore/qbuffer.h>
-#include <QtCore/qtimer.h>
-#include <QtCore/qdebug.h>
-
-#include <qaudiooutput.h>
-
-#include "qaudio_mac_p.h"
-#include "qaudiooutput_mac_p.h"
-#include "qaudiodeviceinfo_mac_p.h"
-
-
-QT_BEGIN_NAMESPACE
-
-
-namespace QtMultimediaInternal
-{
-
-static const int default_buffer_size = 8 * 1024;
-
-
-class QAudioOutputBuffer : public QObject
-{
-    Q_OBJECT
-
-public:
-    QAudioOutputBuffer(int bufferSize, int maxPeriodSize, QAudioFormat const& audioFormat):
-        m_deviceError(false),
-        m_maxPeriodSize(maxPeriodSize),
-        m_device(0)
-    {
-        m_buffer = new QAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize)));
-        m_bytesPerFrame = (audioFormat.sampleSize() / 8) * audioFormat.channelCount();
-        m_periodTime = m_maxPeriodSize / m_bytesPerFrame * 1000 / audioFormat.sampleRate();
-
-        m_fillTimer = new QTimer(this);
-        connect(m_fillTimer, SIGNAL(timeout()), SLOT(fillBuffer()));
-    }
-
-    ~QAudioOutputBuffer()
-    {
-        delete m_buffer;
-    }
-
-    qint64 readFrames(char* data, qint64 maxFrames)
-    {
-        bool    wecan = true;
-        qint64  framesRead = 0;
-
-        while (wecan && framesRead < maxFrames) {
-            QAudioRingBuffer::Region region = m_buffer->acquireReadRegion((maxFrames - framesRead) * m_bytesPerFrame);
-
-            if (region.second > 0) {
-                // Ensure that we only read whole frames.
-                region.second -= region.second % m_bytesPerFrame;
-
-                if (region.second > 0) {
-                    memcpy(data + (framesRead * m_bytesPerFrame), region.first, region.second);
-                    framesRead += region.second / m_bytesPerFrame;
-                } else
-                    wecan = false; // If there is only a partial frame left we should exit.
-            }
-            else
-                wecan = false;
-
-            m_buffer->releaseReadRegion(region);
-        }
-
-        if (framesRead == 0 && m_deviceError)
-            framesRead = -1;
-
-        return framesRead;
-    }
-
-    qint64 writeBytes(const char* data, qint64 maxSize)
-    {
-        bool    wecan = true;
-        qint64  bytesWritten = 0;
-
-        maxSize -= maxSize % m_bytesPerFrame;
-        while (wecan && bytesWritten < maxSize) {
-            QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(maxSize - bytesWritten);
-
-            if (region.second > 0) {
-                memcpy(region.first, data + bytesWritten, region.second);
-                bytesWritten += region.second;
-            }
-            else
-                wecan = false;
-
-            m_buffer->releaseWriteRegion(region);
-        }
-
-        if (bytesWritten > 0)
-            emit readyRead();
-
-        return bytesWritten;
-    }
-
-    int available() const
-    {
-        return m_buffer->free();
-    }
-
-    void reset()
-    {
-        m_buffer->reset();
-        m_device = 0;
-        m_deviceError = false;
-    }
-
-    void setPrefetchDevice(QIODevice* device)
-    {
-        if (m_device != device) {
-            m_device = device;
-            if (m_device != 0)
-                fillBuffer();
-        }
-    }
-
-    void startFillTimer()
-    {
-        if (m_device != 0)
-            m_fillTimer->start(m_buffer->size() / 2 / m_maxPeriodSize * m_periodTime);
-    }
-
-    void stopFillTimer()
-    {
-        m_fillTimer->stop();
-    }
-
-signals:
-    void readyRead();
-
-private slots:
-    void fillBuffer()
-    {
-        const int free = m_buffer->free();
-        const int writeSize = free - (free % m_maxPeriodSize);
-
-        if (writeSize > 0) {
-            bool    wecan = true;
-            int     filled = 0;
-
-            while (!m_deviceError && wecan && filled < writeSize) {
-                QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(writeSize - filled);
-
-                if (region.second > 0) {
-                    region.second = m_device->read(region.first, region.second);
-                    if (region.second > 0)
-                        filled += region.second;
-                    else if (region.second == 0)
-                        wecan = false;
-                    else if (region.second < 0) {
-                        m_fillTimer->stop();
-                        region.second = 0;
-                        m_deviceError = true;
-                    }
-                }
-                else
-                    wecan = false;
-
-                m_buffer->releaseWriteRegion(region);
-            }
-
-            if (filled > 0)
-                emit readyRead();
-        }
-    }
-
-private:
-    bool        m_deviceError;
-    int         m_maxPeriodSize;
-    int         m_bytesPerFrame;
-    int         m_periodTime;
-    QIODevice*  m_device;
-    QTimer*     m_fillTimer;
-    QAudioRingBuffer*  m_buffer;
-};
-
-
-}
-
-class MacOutputDevice : public QIODevice
-{
-    Q_OBJECT
-
-public:
-    MacOutputDevice(QtMultimediaInternal::QAudioOutputBuffer* audioBuffer, QObject* parent):
-        QIODevice(parent),
-        m_audioBuffer(audioBuffer)
-    {
-        open(QIODevice::WriteOnly | QIODevice::Unbuffered);
-    }
-
-    qint64 readData(char* data, qint64 len)
-    {
-        Q_UNUSED(data);
-        Q_UNUSED(len);
-
-        return 0;
-    }
-
-    qint64 writeData(const char* data, qint64 len)
-    {
-        return m_audioBuffer->writeBytes(data, len);
-    }
-
-    bool isSequential() const
-    {
-        return true;
-    }
-
-private:
-    QtMultimediaInternal::QAudioOutputBuffer*    m_audioBuffer;
-};
-
-
-QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray& device)
-{
-    QDataStream ds(device);
-    quint32 did, mode;
-
-    ds >> did >> mode;
-
-    if (QAudio::Mode(mode) == QAudio::AudioInput)
-        errorCode = QAudio::OpenError;
-    else {
-        audioDeviceInfo = new QAudioDeviceInfoInternal(device, QAudio::AudioOutput);
-        isOpen = false;
-        audioDeviceId = AudioDeviceID(did);
-        audioUnit = 0;
-        audioIO = 0;
-        startTime = 0;
-        totalFrames = 0;
-        audioBuffer = 0;
-        internalBufferSize = QtMultimediaInternal::default_buffer_size;
-        clockFrequency = AudioGetHostClockFrequency() / 1000;
-        errorCode = QAudio::NoError;
-        stateCode = QAudio::StoppedState;
-        audioThreadState.store(Stopped);
-
-        cachedVolume = (qreal)1.;
-
-        intervalTimer = new QTimer(this);
-        intervalTimer->setInterval(1000);
-        connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify()));
-    }
-}
-
-QAudioOutputPrivate::~QAudioOutputPrivate()
-{
-    delete audioDeviceInfo;
-    close();
-}
-
-bool QAudioOutputPrivate::open()
-{
-    if (errorCode != QAudio::NoError)
-        return false;
-
-    if (isOpen)
-        return true;
-
-    ComponentDescription    cd;
-    cd.componentType = kAudioUnitType_Output;
-    cd.componentSubType = kAudioUnitSubType_HALOutput;
-    cd.componentManufacturer = kAudioUnitManufacturer_Apple;
-    cd.componentFlags = 0;
-    cd.componentFlagsMask = 0;
-
-    // Open
-    Component cp = FindNextComponent(NULL, &cd);
-    if (cp == 0) {
-        qWarning() << "QAudioOutput: Failed to find HAL Output component";
-        return false;
-    }
-
-    if (OpenAComponent(cp, &audioUnit) != noErr) {
-        qWarning() << "QAudioOutput: Unable to Open Output Component";
-        return false;
-    }
-
-    // register callback
-    AURenderCallbackStruct  cb;
-    cb.inputProc = renderCallback;
-    cb.inputProcRefCon = this;
-
-    if (AudioUnitSetProperty(audioUnit,
-                               kAudioUnitProperty_SetRenderCallback,
-                               kAudioUnitScope_Global,
-                               0,
-                               &cb,
-                               sizeof(cb)) != noErr) {
-        qWarning() << "QAudioOutput: Failed to set AudioUnit callback";
-        return false;
-    }
-
-    // Set Audio Device
-    if (AudioUnitSetProperty(audioUnit,
-                                kAudioOutputUnitProperty_CurrentDevice,
-                                kAudioUnitScope_Global,
-                                0,
-                                &audioDeviceId,
-                                sizeof(audioDeviceId)) != noErr) {
-        qWarning() << "QAudioOutput: Unable to use configured device";
-        return false;
-    }
-
-    // Set stream format
-    streamFormat = toAudioStreamBasicDescription(audioFormat);
-
-    UInt32 size = sizeof(streamFormat);
-    if (AudioUnitSetProperty(audioUnit,
-                                kAudioUnitProperty_StreamFormat,
-                                kAudioUnitScope_Input,
-                                0,
-                                &streamFormat,
-                                sizeof(streamFormat)) != noErr) {
-        qWarning() << "QAudioOutput: Unable to Set Stream information";
-        return false;
-    }
-
-    // Allocate buffer
-    UInt32 numberOfFrames = 0;
-    size = sizeof(UInt32);
-    if (AudioUnitGetProperty(audioUnit,
-                                kAudioDevicePropertyBufferFrameSize,
-                                kAudioUnitScope_Global,
-                                0,
-                                &numberOfFrames,
-                                &size) != noErr) {
-        qWarning() << "QAudioInput: Failed to get audio period size";
-        return false;
-    }
-
-    periodSizeBytes = numberOfFrames * streamFormat.mBytesPerFrame;
-    if (internalBufferSize < periodSizeBytes * 2)
-        internalBufferSize = periodSizeBytes * 2;
-    else
-        internalBufferSize -= internalBufferSize % streamFormat.mBytesPerFrame;
-
-    audioBuffer = new QtMultimediaInternal::QAudioOutputBuffer(internalBufferSize, periodSizeBytes, audioFormat);
-    connect(audioBuffer, SIGNAL(readyRead()), SLOT(inputReady()));  // Pull
-
-    audioIO = new MacOutputDevice(audioBuffer, this);
-
-    // Init
-    if (AudioUnitInitialize(audioUnit)) {
-        qWarning() << "QAudioOutput: Failed to initialize AudioUnit";
-        return false;
-    }
-
-    isOpen = true;
-
-    setVolume(cachedVolume);
-
-    return true;
-}
-
-void QAudioOutputPrivate::close()
-{
-    if (audioUnit != 0) {
-        AudioOutputUnitStop(audioUnit);
-        AudioUnitUninitialize(audioUnit);
-        CloseComponent(audioUnit);
-    }
-
-    delete audioBuffer;
-}
-
-QAudioFormat QAudioOutputPrivate::format() const
-{
-    return audioFormat;
-}
-
-void QAudioOutputPrivate::setFormat(const QAudioFormat& fmt)
-{
-    if (stateCode == QAudio::StoppedState)
-        audioFormat = fmt;
-}
-
-void QAudioOutputPrivate::start(QIODevice* device)
-{
-    QIODevice*  op = device;
-
-    if (!audioDeviceInfo->isFormatSupported(audioFormat) || !open()) {
-        stateCode = QAudio::StoppedState;
-        errorCode = QAudio::OpenError;
-    }
-
-    reset();
-    audioBuffer->reset();
-    audioBuffer->setPrefetchDevice(op);
-
-    if (op == 0) {
-        op = audioIO;
-        stateCode = QAudio::IdleState;
-    }
-    else
-        stateCode = QAudio::ActiveState;
-
-    // Start
-    errorCode = QAudio::NoError;
-    totalFrames = 0;
-    startTime = AudioGetCurrentHostTime();
-
-    if (stateCode == QAudio::ActiveState)
-        audioThreadStart();
-
-    emit stateChanged(stateCode);
-}
-
-QIODevice* QAudioOutputPrivate::start()
-{
-    if (!audioDeviceInfo->isFormatSupported(audioFormat) || !open()) {
-        stateCode = QAudio::StoppedState;
-        errorCode = QAudio::OpenError;
-        return audioIO;
-    }
-
-    reset();
-    audioBuffer->reset();
-    audioBuffer->setPrefetchDevice(0);
-
-    stateCode = QAudio::IdleState;
-
-    // Start
-    errorCode = QAudio::NoError;
-    totalFrames = 0;
-    startTime = AudioGetCurrentHostTime();
-
-    emit stateChanged(stateCode);
-
-    return audioIO;
-}
-
-void QAudioOutputPrivate::stop()
-{
-    QMutexLocker    lock(&mutex);
-    if (stateCode != QAudio::StoppedState) {
-        audioThreadDrain();
-
-        stateCode = QAudio::StoppedState;
-        errorCode = QAudio::NoError;
-        QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
-    }
-}
-
-void QAudioOutputPrivate::reset()
-{
-    QMutexLocker    lock(&mutex);
-    if (stateCode != QAudio::StoppedState) {
-        audioThreadStop();
-
-        stateCode = QAudio::StoppedState;
-        errorCode = QAudio::NoError;
-        QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
-    }
-}
-
-void QAudioOutputPrivate::suspend()
-{
-    QMutexLocker    lock(&mutex);
-    if (stateCode == QAudio::ActiveState || stateCode == QAudio::IdleState) {
-        audioThreadStop();
-
-        stateCode = QAudio::SuspendedState;
-        errorCode = QAudio::NoError;
-        QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
-    }
-}
-
-void QAudioOutputPrivate::resume()
-{
-    QMutexLocker    lock(&mutex);
-    if (stateCode == QAudio::SuspendedState) {
-        audioThreadStart();
-
-        stateCode = QAudio::ActiveState;
-        errorCode = QAudio::NoError;
-        QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
-    }
-}
-
-int QAudioOutputPrivate::bytesFree() const
-{
-    return audioBuffer->available();
-}
-
-int QAudioOutputPrivate::periodSize() const
-{
-    return periodSizeBytes;
-}
-
-void QAudioOutputPrivate::setBufferSize(int bs)
-{
-    if (stateCode == QAudio::StoppedState)
-        internalBufferSize = bs;
-}
-
-int QAudioOutputPrivate::bufferSize() const
-{
-    return internalBufferSize;
-}
-
-void QAudioOutputPrivate::setNotifyInterval(int milliSeconds)
-{
-    if (intervalTimer->interval() == milliSeconds)
-        return;
-
-    if (milliSeconds <= 0)
-        milliSeconds = 0;
-
-    intervalTimer->setInterval(milliSeconds);
-}
-
-int QAudioOutputPrivate::notifyInterval() const
-{
-    return intervalTimer->interval();
-}
-
-qint64 QAudioOutputPrivate::processedUSecs() const
-{
-    return totalFrames * 1000000 / audioFormat.sampleRate();
-}
-
-qint64 QAudioOutputPrivate::elapsedUSecs() const
-{
-    if (stateCode == QAudio::StoppedState)
-        return 0;
-
-    return (AudioGetCurrentHostTime() - startTime) / (clockFrequency / 1000);
-}
-
-QAudio::Error QAudioOutputPrivate::error() const
-{
-    return errorCode;
-}
-
-QAudio::State QAudioOutputPrivate::state() const
-{
-    return stateCode;
-}
-
-void QAudioOutputPrivate::audioThreadStart()
-{
-    startTimers();
-    audioThreadState.store(Running);
-    AudioOutputUnitStart(audioUnit);
-}
-
-void QAudioOutputPrivate::audioThreadStop()
-{
-    stopTimers();
-    if (audioThreadState.testAndSetAcquire(Running, Stopped))
-        threadFinished.wait(&mutex);
-}
-
-void QAudioOutputPrivate::audioThreadDrain()
-{
-    stopTimers();
-    if (audioThreadState.testAndSetAcquire(Running, Draining))
-        threadFinished.wait(&mutex);
-}
-
-void QAudioOutputPrivate::audioDeviceStop()
-{
-    AudioOutputUnitStop(audioUnit);
-    audioThreadState.store(Stopped);
-    threadFinished.wakeOne();
-}
-
-void QAudioOutputPrivate::audioDeviceIdle()
-{
-    QMutexLocker    lock(&mutex);
-    if (stateCode == QAudio::ActiveState) {
-        audioDeviceStop();
-
-        errorCode = QAudio::UnderrunError;
-        stateCode = QAudio::IdleState;
-        QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
-    }
-}
-
-void QAudioOutputPrivate::audioDeviceError()
-{
-    QMutexLocker    lock(&mutex);
-    if (stateCode == QAudio::ActiveState) {
-        audioDeviceStop();
-
-        errorCode = QAudio::IOError;
-        stateCode = QAudio::StoppedState;
-        QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
-    }
-}
-
-void QAudioOutputPrivate::startTimers()
-{
-    audioBuffer->startFillTimer();
-    if (intervalTimer->interval() > 0)
-        intervalTimer->start();
-}
-
-void QAudioOutputPrivate::stopTimers()
-{
-    audioBuffer->stopFillTimer();
-    intervalTimer->stop();
-}
-
-void QAudioOutputPrivate::setVolume(qreal v)
-{
-    const qreal normalizedVolume = qBound(qreal(0.0), v, qreal(1.0));
-    if (!isOpen) {
-        cachedVolume = normalizedVolume;
-        return;
-    }
-
-    if (AudioUnitSetParameter(audioUnit,
-                              kHALOutputParam_Volume,
-                              kAudioUnitScope_Global,
-                              0 /* bus */,
-                              (float)normalizedVolume,
-                              0) == noErr)
-        cachedVolume = normalizedVolume;
-}
-
-qreal QAudioOutputPrivate::volume() const
-{
-    return cachedVolume;
-}
-
-void QAudioOutputPrivate::deviceStopped()
-{
-    intervalTimer->stop();
-    emit stateChanged(stateCode);
-}
-
-void QAudioOutputPrivate::inputReady()
-{
-    QMutexLocker    lock(&mutex);
-    if (stateCode == QAudio::IdleState) {
-        audioThreadStart();
-
-        stateCode = QAudio::ActiveState;
-        errorCode = QAudio::NoError;
-
-        QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
-    }
-}
-
-
-OSStatus QAudioOutputPrivate::renderCallback(void* inRefCon,
-                                    AudioUnitRenderActionFlags* ioActionFlags,
-                                    const AudioTimeStamp* inTimeStamp,
-                                    UInt32 inBusNumber,
-                                    UInt32 inNumberFrames,
-                                    AudioBufferList* ioData)
-{
-    Q_UNUSED(ioActionFlags)
-    Q_UNUSED(inTimeStamp)
-    Q_UNUSED(inBusNumber)
-    Q_UNUSED(inNumberFrames)
-
-    QAudioOutputPrivate* d = static_cast<QAudioOutputPrivate*>(inRefCon);
-
-    const int threadState = d->audioThreadState.fetchAndAddAcquire(0);
-    if (threadState == Stopped) {
-        ioData->mBuffers[0].mDataByteSize = 0;
-        d->audioDeviceStop();
-    }
-    else {
-        const UInt32    bytesPerFrame = d->streamFormat.mBytesPerFrame;
-        qint64          framesRead;
-
-        framesRead = d->audioBuffer->readFrames((char*)ioData->mBuffers[0].mData,
-                                                 ioData->mBuffers[0].mDataByteSize / bytesPerFrame);
-
-        if (framesRead > 0) {
-            ioData->mBuffers[0].mDataByteSize = framesRead * bytesPerFrame;
-            d->totalFrames += framesRead;
-        }
-        else {
-            ioData->mBuffers[0].mDataByteSize = 0;
-            if (framesRead == 0) {
-                if (threadState == Draining)
-                    d->audioDeviceStop();
-                else
-                    d->audioDeviceIdle();
-            }
-            else
-                d->audioDeviceError();
-        }
-    }
-
-    return noErr;
-}
-
-
-QT_END_NAMESPACE
-
-#include "qaudiooutput_mac_p.moc"
diff --git a/src/plugins/coreaudio/coreaudio.json b/src/plugins/coreaudio/coreaudio.json
new file mode 100644 (file)
index 0000000..a31d521
--- /dev/null
@@ -0,0 +1,3 @@
+{
+    "Keys": ["default"]
+}
diff --git a/src/plugins/coreaudio/coreaudio.pro b/src/plugins/coreaudio/coreaudio.pro
new file mode 100644 (file)
index 0000000..1468514
--- /dev/null
@@ -0,0 +1,39 @@
+TARGET = qtaudio_coreaudio
+QT += multimedia-private
+
+PLUGIN_TYPE = audio
+PLUGIN_CLASS_NAME = CoreAudioPlugin
+
+load(qt_plugin)
+OTHER_FILES += \
+    coreaudio.json
+
+#DEFINES += QT_DEBUG_COREAUDIO
+
+HEADERS += \
+    coreaudiodeviceinfo.h \
+    coreaudioinput.h \
+    coreaudiooutput.h \
+    coreaudioplugin.h \
+    coreaudioutils.h
+
+OBJECTIVE_SOURCES += \
+    coreaudiodeviceinfo.mm \
+    coreaudioinput.mm \
+    coreaudiooutput.mm \
+    coreaudioplugin.mm \
+    coreaudioutils.mm
+
+ios {
+    HEADERS += coreaudiosessionmanager.h
+    OBJECTIVE_SOURCES += coreaudiosessionmanager.mm
+    LIBS += -framework AVFoundation
+} else {
+    LIBS += \
+        -framework ApplicationServices \
+        -framework AudioUnit
+}
+
+LIBS += \
+    -framework CoreAudio \
+    -framework AudioToolbox
similarity index 79%
rename from src/multimedia/audio/qaudiodeviceinfo_mac_p.h
rename to src/plugins/coreaudio/coreaudiodeviceinfo.h
index 2afb212..1a8bcec 100644 (file)
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/
-
-//
-//  W A R N I N G
-//  -------------
-//
-// This file is not part of the Qt API.  It exists for the convenience
-// of other Qt classes.  This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-
-#ifndef QDEVICEINFO_MAC_P_H
-#define QDEVICEINFO_MAC_P_H
-
-#include <CoreAudio/CoreAudio.h>
+#ifndef IOSAUDIODEVICEINFO_H
+#define IOSAUDIODEVICEINFO_H
 
 #include <qaudiosystem.h>
 
-QT_BEGIN_NAMESPACE
+#if defined(Q_OS_OSX)
+# include <CoreAudio/CoreAudio.h>
+#endif
 
+QT_BEGIN_NAMESPACE
 
-class QAudioDeviceInfoInternal : public QAbstractAudioDeviceInfo
+class CoreAudioDeviceInfo : public QAbstractAudioDeviceInfo
 {
-public:
-    AudioDeviceID   deviceId;
-    QString         name;
-    QAudio::Mode   mode;
+    Q_OBJECT
 
-    QAudioDeviceInfoInternal(QByteArray const& handle, QAudio::Mode mode);
+public:
+    CoreAudioDeviceInfo(const QByteArray &device, QAudio::Mode mode);
+    ~CoreAudioDeviceInfo() {}
 
-    bool isFormatSupported(const QAudioFormat& format) const;
     QAudioFormat preferredFormat() const;
-
+    bool isFormatSupported(const QAudioFormat &format) const;
     QString deviceName() const;
-
     QStringList supportedCodecs();
     QList<int> supportedSampleRates();
     QList<int> supportedChannelCounts();
@@ -86,8 +71,16 @@ public:
     static QByteArray defaultOutputDevice();
 
     static QList<QByteArray> availableDevices(QAudio::Mode mode);
+
+private:
+#if defined(Q_OS_OSX)
+    AudioDeviceID m_deviceId;
+#endif
+
+    QString m_device;
+    QAudio::Mode m_mode;
 };
 
 QT_END_NAMESPACE
 
-#endif  // QDEVICEINFO_MAC_P_H
+#endif
diff --git a/src/plugins/coreaudio/coreaudiodeviceinfo.mm b/src/plugins/coreaudio/coreaudiodeviceinfo.mm
new file mode 100644 (file)
index 0000000..77a9b83
--- /dev/null
@@ -0,0 +1,386 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "coreaudiodeviceinfo.h"
+#include "coreaudioutils.h"
+#if defined(Q_OS_IOS)
+# include "coreaudiosessionmanager.h"
+#endif
+
+#include <QtCore/QDebug>
+#include <QtCore/QSet>
+
+QT_BEGIN_NAMESPACE
+
+CoreAudioDeviceInfo::CoreAudioDeviceInfo(const QByteArray &device, QAudio::Mode mode)
+    : m_mode(mode)
+{
+#if defined(Q_OS_OSX)
+    quint32 deviceID;
+
+    QDataStream dataStream(device);
+    dataStream >> deviceID >> m_device;
+    m_deviceId = AudioDeviceID(deviceID);
+#else //iOS
+    m_device = device;
+#endif
+}
+
+
+QAudioFormat CoreAudioDeviceInfo::preferredFormat() const
+{
+    QAudioFormat format;
+
+#if defined(Q_OS_OSX)
+    UInt32  propSize = 0;
+    AudioObjectPropertyScope audioDevicePropertyScope = m_mode == QAudio::AudioInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
+    AudioObjectPropertyAddress audioDevicePropertyStreamsAddress = { kAudioDevicePropertyStreams,
+                                                                     audioDevicePropertyScope,
+                                                                     kAudioObjectPropertyElementMaster };
+
+    if (AudioObjectGetPropertyDataSize(m_deviceId, &audioDevicePropertyStreamsAddress, 0, NULL, &propSize) == noErr) {
+
+        const int sc = propSize / sizeof(AudioStreamID);
+
+        if (sc > 0) {
+            AudioStreamID*  streams = new AudioStreamID[sc];
+
+            if (AudioObjectGetPropertyData(m_deviceId, &audioDevicePropertyStreamsAddress, 0, NULL, &propSize, streams) == noErr) {
+
+                AudioObjectPropertyAddress audioDevicePhysicalFormatPropertyAddress = { kAudioStreamPropertyPhysicalFormat,
+                                                                                        kAudioObjectPropertyScopeGlobal,
+                                                                                        kAudioObjectPropertyElementMaster };
+
+                for (int i = 0; i < sc; ++i) {
+                    if (AudioObjectGetPropertyDataSize(streams[i], &audioDevicePhysicalFormatPropertyAddress, 0, NULL, &propSize) == noErr) {
+                        AudioStreamBasicDescription sf;
+
+                        if (AudioObjectGetPropertyData(streams[i], &audioDevicePhysicalFormatPropertyAddress, 0, NULL, &propSize, &sf) == noErr) {
+                            format = CoreAudioUtils::toQAudioFormat(sf);
+                            break;
+                        } else {
+                            qWarning() << "QAudioDeviceInfo: Unable to find perferedFormat for stream";
+                        }
+                    } else {
+                        qWarning() << "QAudioDeviceInfo: Unable to find size of perferedFormat for stream";
+                    }
+                }
+            }
+
+            delete streams;
+        }
+    }
+#else //iOS
+    format.setSampleSize(16);
+    if (m_mode == QAudio::AudioInput) {
+        format.setChannelCount(1);
+        format.setSampleRate(8000);
+    } else {
+        format.setChannelCount(2);
+        format.setSampleRate(44100);
+    }
+    format.setCodec(QString::fromLatin1("audio/pcm"));
+    format.setByteOrder(QAudioFormat::LittleEndian);
+    format.setSampleType(QAudioFormat::SignedInt);
+#endif
+
+    return format;
+}
+
+
+bool CoreAudioDeviceInfo::isFormatSupported(const QAudioFormat &format) const
+{
+    CoreAudioDeviceInfo *self = const_cast<CoreAudioDeviceInfo*>(this);
+
+    return format.isValid()
+            && format.codec() == QString::fromLatin1("audio/pcm")
+            && self->supportedSampleRates().contains(format.sampleRate())
+            && self->supportedChannelCounts().contains(format.channelCount())
+            && self->supportedSampleSizes().contains(format.sampleSize());
+}
+
+
+QString CoreAudioDeviceInfo::deviceName() const
+{
+    return m_device;
+}
+
+
+QStringList CoreAudioDeviceInfo::supportedCodecs()
+{
+    return QStringList() << QString::fromLatin1("audio/pcm");
+}
+
+
+QList<int> CoreAudioDeviceInfo::supportedSampleRates()
+{
+    QSet<int> sampleRates;
+
+#if defined(Q_OS_OSX)
+    UInt32  propSize = 0;
+    AudioObjectPropertyScope scope = m_mode == QAudio::AudioInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
+    AudioObjectPropertyAddress availableNominalSampleRatesAddress = { kAudioDevicePropertyAvailableNominalSampleRates,
+                                                                      scope,
+                                                                      kAudioObjectPropertyElementMaster };
+
+    if (AudioObjectGetPropertyDataSize(m_deviceId, &availableNominalSampleRatesAddress, 0, NULL, &propSize) == noErr) {
+        const int pc = propSize / sizeof(AudioValueRange);
+
+        if (pc > 0) {
+            AudioValueRange* vr = new AudioValueRange[pc];
+
+            if (AudioObjectGetPropertyData(m_deviceId, &availableNominalSampleRatesAddress, 0, NULL, &propSize, vr) == noErr) {
+                for (int i = 0; i < pc; ++i)
+                    sampleRates << vr[i].mMaximum;
+            }
+
+            delete vr;
+        }
+    }
+#else //iOS
+    //iOS doesn't have a way to query available sample rates
+    //instead we provide reasonable targets
+    //It may be necessary have CoreAudioSessionManger test combinations
+    //with available hardware
+    sampleRates << 8000 << 11025 << 22050 << 44100 << 48000;
+#endif
+    return sampleRates.toList();
+}
+
+
+QList<int> CoreAudioDeviceInfo::supportedChannelCounts()
+{
+    QSet<int> supportedChannels;
+
+#if defined(Q_OS_OSX)
+    UInt32 propSize = 0;
+    int channels = 0;
+    AudioObjectPropertyScope scope = m_mode == QAudio::AudioInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
+    AudioObjectPropertyAddress streamConfigurationPropertyAddress = { kAudioDevicePropertyStreamConfiguration,
+                                                                      scope,
+                                                                      kAudioObjectPropertyElementMaster };
+
+    if (AudioObjectGetPropertyDataSize(m_deviceId, &streamConfigurationPropertyAddress, 0, NULL, &propSize) == noErr) {
+        AudioBufferList* audioBufferList = static_cast<AudioBufferList*>(malloc(propSize));
+
+        if (audioBufferList != 0) {
+            if (AudioObjectGetPropertyData(m_deviceId, &streamConfigurationPropertyAddress, 0, NULL, &propSize, audioBufferList) == noErr) {
+                for (int i = 0; i < int(audioBufferList->mNumberBuffers); ++i) {
+                    channels += audioBufferList->mBuffers[i].mNumberChannels;
+                    supportedChannels << channels;
+                }
+            }
+
+            free(audioBufferList);
+        }
+    }
+#else //iOS
+    if (m_mode == QAudio::AudioInput) {
+        supportedChannels << CoreAudioSessionManager::instance().inputChannelCount();
+    } else if (m_mode == QAudio::AudioOutput) {
+        supportedChannels << CoreAudioSessionManager::instance().outputChannelCount();
+    }
+#endif
+
+    return supportedChannels.toList();
+}
+
+
+QList<int> CoreAudioDeviceInfo::supportedSampleSizes()
+{
+    return QList<int>() << 8 << 16 << 24 << 32 << 64;
+}
+
+
+QList<QAudioFormat::Endian> CoreAudioDeviceInfo::supportedByteOrders()
+{
+    return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian << QAudioFormat::BigEndian;
+}
+
+
+QList<QAudioFormat::SampleType> CoreAudioDeviceInfo::supportedSampleTypes()
+{
+    return QList<QAudioFormat::SampleType>() << QAudioFormat::SignedInt << QAudioFormat::UnSignedInt << QAudioFormat::Float;
+}
+
+#if defined(Q_OS_OSX)
+// XXX: remove at some future date
+static inline QString cfStringToQString(CFStringRef str)
+{
+    CFIndex length = CFStringGetLength(str);
+    const UniChar *chars = CFStringGetCharactersPtr(str);
+    if (chars)
+        return QString(reinterpret_cast<const QChar *>(chars), length);
+
+    UniChar buffer[length];
+    CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
+    return QString(reinterpret_cast<const QChar *>(buffer), length);
+}
+
+static QByteArray get_device_info(AudioDeviceID audioDevice, QAudio::Mode mode)
+{
+    UInt32      size;
+    QByteArray  device;
+    QDataStream ds(&device, QIODevice::WriteOnly);
+    AudioStreamBasicDescription     sf;
+    CFStringRef name;
+    Boolean     isInput = mode == QAudio::AudioInput;
+    AudioObjectPropertyScope audioPropertyScope = isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
+
+    // Id
+    ds << quint32(audioDevice);
+
+    // Mode //TODO: Why don't we use the Stream Format we ask for?
+    size = sizeof(AudioStreamBasicDescription);
+    AudioObjectPropertyAddress audioDeviceStreamFormatPropertyAddress = { kAudioDevicePropertyStreamFormat,
+                                                                    audioPropertyScope,
+                                                                    kAudioObjectPropertyElementMaster };
+
+    if (AudioObjectGetPropertyData(audioDevice, &audioDeviceStreamFormatPropertyAddress, 0, NULL, &size, &sf) != noErr) {
+        return QByteArray();
+    }
+
+    // Name
+    size = sizeof(CFStringRef);
+    AudioObjectPropertyAddress audioDeviceNamePropertyAddress = { kAudioObjectPropertyName,
+                                                                  audioPropertyScope,
+                                                                  kAudioObjectPropertyElementMaster };
+
+    if (AudioObjectGetPropertyData(audioDevice, &audioDeviceNamePropertyAddress, 0, NULL, &size, &name) != noErr) {
+        qWarning() << "QAudioDeviceInfo: Unable to find device name";
+        return QByteArray();
+    }
+    ds << cfStringToQString(name);
+
+    CFRelease(name);
+
+    return device;
+}
+#endif
+
+QByteArray CoreAudioDeviceInfo::defaultInputDevice()
+{
+#if defined(Q_OS_OSX)
+    AudioDeviceID audioDevice;
+    UInt32 size = sizeof(audioDevice);
+    AudioObjectPropertyAddress defaultInputDevicePropertyAddress = { kAudioHardwarePropertyDefaultInputDevice,
+                                                                     kAudioObjectPropertyScopeGlobal,
+                                                                     kAudioObjectPropertyElementMaster };
+
+    if (AudioObjectGetPropertyData(kAudioObjectSystemObject,
+                                   &defaultInputDevicePropertyAddress,
+                                   0, NULL, &size, &audioDevice) != noErr) {
+        qWarning() << "QAudioDeviceInfo: Unable to find default input device";
+        return QByteArray();
+    }
+
+    return get_device_info(audioDevice, QAudio::AudioInput);
+#else //iOS
+    return CoreAudioSessionManager::instance().inputDevices().first();
+#endif
+}
+
+QByteArray CoreAudioDeviceInfo::defaultOutputDevice()
+{
+#if defined(Q_OS_OSX)
+    AudioDeviceID audioDevice;
+    UInt32        size = sizeof(audioDevice);
+    AudioObjectPropertyAddress defaultOutputDevicePropertyAddress = { kAudioHardwarePropertyDefaultInputDevice,
+                                                                     kAudioObjectPropertyScopeGlobal,
+                                                                     kAudioObjectPropertyElementMaster };
+
+    if (AudioObjectGetPropertyData(kAudioObjectSystemObject,
+                                   &defaultOutputDevicePropertyAddress,
+                                   0, NULL, &size, &audioDevice) != noErr) {
+        qWarning() << "QAudioDeviceInfo: Unable to find default output device";
+        return QByteArray();
+    }
+
+    return get_device_info(audioDevice, QAudio::AudioOutput);
+#else //iOS
+    return CoreAudioSessionManager::instance().outputDevices().first();
+#endif
+}
+
+QList<QByteArray> CoreAudioDeviceInfo::availableDevices(QAudio::Mode mode)
+{
+    QList<QByteArray> devices;
+#if defined(Q_OS_OSX)
+    UInt32  propSize = 0;
+    AudioObjectPropertyAddress audioDevicesPropertyAddress = { kAudioHardwarePropertyDevices,
+                                                               kAudioObjectPropertyScopeGlobal,
+                                                               kAudioObjectPropertyElementMaster };
+
+    if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
+                                       &audioDevicesPropertyAddress,
+                                       0, NULL, &propSize) == noErr) {
+
+        const int dc = propSize / sizeof(AudioDeviceID);
+
+        if (dc > 0) {
+            AudioDeviceID*  audioDevices = new AudioDeviceID[dc];
+
+            if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &audioDevicesPropertyAddress, 0, NULL, &propSize, audioDevices) == noErr) {
+                for (int i = 0; i < dc; ++i) {
+                    QByteArray info = get_device_info(audioDevices[i], mode);
+                    if (!info.isNull())
+                        devices << info;
+                }
+            }
+
+            delete audioDevices;
+        }
+    }
+#else //iOS
+    CoreAudioSessionManager::instance().setActive(true);
+
+    if (mode == QAudio::AudioOutput)
+        return CoreAudioSessionManager::instance().outputDevices();
+    if (mode == QAudio::AudioInput)
+        return CoreAudioSessionManager::instance().inputDevices();
+#endif
+
+    return devices;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_coreaudiodeviceinfo.cpp"
diff --git a/src/plugins/coreaudio/coreaudioinput.h b/src/plugins/coreaudio/coreaudioinput.h
new file mode 100644 (file)
index 0000000..a54db77
--- /dev/null
@@ -0,0 +1,267 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef IOSAUDIOINPUT_H
+#define IOSAUDIOINPUT_H
+
+#include <qaudiosystem.h>
+#include <AudioUnit/AudioUnit.h>
+#include <CoreAudio/CoreAudioTypes.h>
+#include <AudioToolbox/AudioToolbox.h>
+
+#include <QtCore/QWaitCondition>
+#include <QtCore/QMutex>
+#include <QtCore/QTimer>
+
+QT_BEGIN_NAMESPACE
+
+class CoreAudioRingBuffer;
+class CoreAudioPacketFeeder;
+class CoreAudioInputBuffer;
+class CoreAudioInputDevice;
+
+class CoreAudioBufferList
+{
+public:
+    CoreAudioBufferList(AudioStreamBasicDescription const& streamFormat);
+    CoreAudioBufferList(AudioStreamBasicDescription const& streamFormat, char *buffer, int bufferSize);
+    CoreAudioBufferList(AudioStreamBasicDescription const& streamFormat, int framesToBuffer);
+
+    ~CoreAudioBufferList();
+
+    AudioBufferList* audioBufferList() const { return m_bufferList; }
+    char *data(int buffer = 0) const;
+    qint64 bufferSize(int buffer = 0) const;
+    int frameCount(int buffer = 0) const;
+    int packetCount(int buffer = 0) const;
+    int packetSize() const;
+    void reset();
+
+private:
+    bool m_owner;
+    int m_dataSize;
+    AudioStreamBasicDescription m_streamDescription;
+    AudioBufferList *m_bufferList;
+};
+
+class CoreAudioPacketFeeder
+{
+public:
+    CoreAudioPacketFeeder(CoreAudioBufferList *abl);
+
+    bool feed(AudioBufferList& dst, UInt32& packetCount);
+    bool empty() const;
+
+private:
+    UInt32 m_totalPackets;
+    UInt32 m_position;
+    CoreAudioBufferList *m_audioBufferList;
+};
+
+class CoreAudioInputBuffer : public QObject
+{
+    Q_OBJECT
+
+public:
+    CoreAudioInputBuffer(int bufferSize,
+                        int maxPeriodSize,
+                        AudioStreamBasicDescription const& inputFormat,
+                        AudioStreamBasicDescription const& outputFormat,
+                        QObject *parent);
+
+    ~CoreAudioInputBuffer();
+
+    qreal volume() const;
+    void setVolume(qreal v);
+
+    qint64 renderFromDevice(AudioUnit audioUnit,
+                             AudioUnitRenderActionFlags *ioActionFlags,
+                             const AudioTimeStamp *inTimeStamp,
+                             UInt32 inBusNumber,
+                             UInt32 inNumberFrames);
+
+    qint64 readBytes(char *data, qint64 len);
+
+    void setFlushDevice(QIODevice *device);
+
+    void startFlushTimer();
+    void stopFlushTimer();
+
+    void flush(bool all = false);
+    void reset();
+    int available() const;
+    int used() const;
+
+signals:
+    void readyRead();
+
+private slots:
+    void flushBuffer();
+
+private:
+    bool m_deviceError;
+    int m_maxPeriodSize;
+    int m_periodTime;
+    QIODevice *m_device;
+    QTimer *m_flushTimer;
+    CoreAudioRingBuffer *m_buffer;
+    CoreAudioBufferList *m_inputBufferList;
+    AudioConverterRef m_audioConverter;
+    AudioStreamBasicDescription m_inputFormat;
+    AudioStreamBasicDescription m_outputFormat;
+    QAudioFormat m_qFormat;
+    qreal m_volume;
+
+    const static OSStatus as_empty = 'qtem';
+
+    // Converter callback
+    static OSStatus converterCallback(AudioConverterRef inAudioConverter,
+                                UInt32 *ioNumberDataPackets,
+                                AudioBufferList *ioData,
+                                AudioStreamPacketDescription **outDataPacketDescription,
+                                void *inUserData);
+};
+
+class CoreAudioInputDevice : public QIODevice
+{
+    Q_OBJECT
+
+public:
+    CoreAudioInputDevice(CoreAudioInputBuffer *audioBuffer, QObject *parent);
+
+    qint64 readData(char *data, qint64 len);
+    qint64 writeData(const char *data, qint64 len);
+
+    bool isSequential() const { return true; }
+
+private:
+    CoreAudioInputBuffer *m_audioBuffer;
+};
+
+class CoreAudioInput : public QAbstractAudioInput
+{
+    Q_OBJECT
+
+public:
+    CoreAudioInput(const QByteArray &device);
+    ~CoreAudioInput();
+
+    void start(QIODevice *device);
+    QIODevice *start();
+    void stop();
+    void reset();
+    void suspend();
+    void resume();
+    int bytesReady() const;
+    int periodSize() const;
+    void setBufferSize(int value);
+    int bufferSize() const;
+    void setNotifyInterval(int milliSeconds);
+    int notifyInterval() const;
+    qint64 processedUSecs() const;
+    qint64 elapsedUSecs() const;
+    QAudio::Error error() const;
+    QAudio::State state() const;
+    void setFormat(const QAudioFormat &format);
+    QAudioFormat format() const;
+
+    void setVolume(qreal volume);
+    qreal volume() const;
+
+private slots:
+    void deviceStoppped();
+
+private:
+    enum {
+        Running,
+        Stopped
+    };
+
+    bool open();
+    void close();
+
+    void audioThreadStart();
+    void audioThreadStop();
+
+    void audioDeviceStop();
+    void audioDeviceActive();
+    void audioDeviceFull();
+    void audioDeviceError();
+
+    void startTimers();
+    void stopTimers();
+
+    // Input callback
+    static OSStatus inputCallback(void *inRefCon,
+                                    AudioUnitRenderActionFlags *ioActionFlags,
+                                    const AudioTimeStamp *inTimeStamp,
+                                    UInt32 inBusNumber,
+                                    UInt32 inNumberFrames,
+                                    AudioBufferList *ioData);
+
+    QByteArray m_device;
+    bool m_isOpen;
+    int m_periodSizeBytes;
+    int m_internalBufferSize;
+    qint64 m_totalFrames;
+    QAudioFormat m_audioFormat;
+    QIODevice *m_audioIO;
+    AudioUnit m_audioUnit;
+#if defined(Q_OS_OSX)
+    AudioDeviceID m_audioDeviceId;
+#endif
+    Float64 m_clockFrequency;
+    UInt64 m_startTime;
+    QAudio::Error m_errorCode;
+    QAudio::State m_stateCode;
+    CoreAudioInputBuffer *m_audioBuffer;
+    QMutex m_mutex;
+    QWaitCondition m_threadFinished;
+    QAtomicInt m_audioThreadState;
+    QTimer *m_intervalTimer;
+    AudioStreamBasicDescription m_streamFormat;
+    AudioStreamBasicDescription m_deviceFormat;
+    QAbstractAudioDeviceInfo *m_audioDeviceInfo;
+    qreal m_volume;
+};
+
+QT_END_NAMESPACE
+
+#endif // IOSAUDIOINPUT_H
diff --git a/src/plugins/coreaudio/coreaudioinput.mm b/src/plugins/coreaudio/coreaudioinput.mm
new file mode 100644 (file)
index 0000000..c41e1a5
--- /dev/null
@@ -0,0 +1,1018 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "coreaudioinput.h"
+#include "coreaudiosessionmanager.h"
+#include "coreaudiodeviceinfo.h"
+#include "coreaudioutils.h"
+
+#if defined(Q_OS_OSX)
+# include <CoreServices/CoreServices.h>
+#endif
+
+#include <QtMultimedia/private/qaudiohelpers_p.h>
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+static const int DEFAULT_BUFFER_SIZE = 4 * 1024;
+
+CoreAudioBufferList::CoreAudioBufferList(const AudioStreamBasicDescription &streamFormat)
+    : m_owner(false)
+    , m_streamDescription(streamFormat)
+{
+    const bool isInterleaved = (m_streamDescription.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
+    const int numberOfBuffers = isInterleaved ? 1 : m_streamDescription.mChannelsPerFrame;
+
+    m_dataSize = 0;
+
+    m_bufferList = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) +
+                                                            (sizeof(AudioBuffer) * numberOfBuffers)));
+
+    m_bufferList->mNumberBuffers = numberOfBuffers;
+    for (int i = 0; i < numberOfBuffers; ++i) {
+        m_bufferList->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1;
+        m_bufferList->mBuffers[i].mDataByteSize = 0;
+        m_bufferList->mBuffers[i].mData = 0;
+    }
+}
+
+CoreAudioBufferList::CoreAudioBufferList(const AudioStreamBasicDescription &streamFormat, char *buffer, int bufferSize)
+    : m_owner(false)
+    , m_streamDescription(streamFormat)
+    , m_bufferList(0)
+{
+    m_dataSize = bufferSize;
+
+    m_bufferList = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer)));
+
+    m_bufferList->mNumberBuffers = 1;
+    m_bufferList->mBuffers[0].mNumberChannels = 1;
+    m_bufferList->mBuffers[0].mDataByteSize = m_dataSize;
+    m_bufferList->mBuffers[0].mData = buffer;
+}
+
+CoreAudioBufferList::CoreAudioBufferList(const AudioStreamBasicDescription &streamFormat, int framesToBuffer)
+    : m_owner(true)
+    , m_streamDescription(streamFormat)
+    , m_bufferList(0)
+{
+    const bool isInterleaved = (m_streamDescription.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
+    const int numberOfBuffers = isInterleaved ? 1 : m_streamDescription.mChannelsPerFrame;
+
+    m_dataSize = framesToBuffer * m_streamDescription.mBytesPerFrame;
+
+    m_bufferList = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) +
+                                                            (sizeof(AudioBuffer) * numberOfBuffers)));
+    m_bufferList->mNumberBuffers = numberOfBuffers;
+    for (int i = 0; i < numberOfBuffers; ++i) {
+        m_bufferList->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1;
+        m_bufferList->mBuffers[i].mDataByteSize = m_dataSize;
+        m_bufferList->mBuffers[i].mData = malloc(m_dataSize);
+    }
+}
+
+CoreAudioBufferList::~CoreAudioBufferList()
+{
+    if (m_owner) {
+        for (UInt32 i = 0; i < m_bufferList->mNumberBuffers; ++i)
+            free(m_bufferList->mBuffers[i].mData);
+    }
+
+    free(m_bufferList);
+}
+
+char *CoreAudioBufferList::data(int buffer) const
+{
+    return static_cast<char*>(m_bufferList->mBuffers[buffer].mData);
+}
+
+qint64 CoreAudioBufferList::bufferSize(int buffer) const
+{
+    return m_bufferList->mBuffers[buffer].mDataByteSize;
+}
+
+int CoreAudioBufferList::frameCount(int buffer) const
+{
+    return m_bufferList->mBuffers[buffer].mDataByteSize / m_streamDescription.mBytesPerFrame;
+}
+
+int CoreAudioBufferList::packetCount(int buffer) const
+{
+    return m_bufferList->mBuffers[buffer].mDataByteSize / m_streamDescription.mBytesPerPacket;
+}
+
+int CoreAudioBufferList::packetSize() const
+{
+    return m_streamDescription.mBytesPerPacket;
+}
+
+void CoreAudioBufferList::reset()
+{
+    for (UInt32 i = 0; i < m_bufferList->mNumberBuffers; ++i) {
+        m_bufferList->mBuffers[i].mDataByteSize = m_dataSize;
+        m_bufferList->mBuffers[i].mData = 0;
+    }
+}
+
+CoreAudioPacketFeeder::CoreAudioPacketFeeder(CoreAudioBufferList *abl)
+    : m_audioBufferList(abl)
+{
+    m_totalPackets = m_audioBufferList->packetCount();
+    m_position = 0;
+}
+
+bool CoreAudioPacketFeeder::feed(AudioBufferList &dst, UInt32 &packetCount)
+{
+    if (m_position == m_totalPackets) {
+        dst.mBuffers[0].mDataByteSize = 0;
+        packetCount = 0;
+        return false;
+    }
+
+    if (m_totalPackets - m_position < packetCount)
+        packetCount = m_totalPackets - m_position;
+
+    dst.mBuffers[0].mDataByteSize = packetCount * m_audioBufferList->packetSize();
+    dst.mBuffers[0].mData = m_audioBufferList->data() + (m_position * m_audioBufferList->packetSize());
+
+    m_position += packetCount;
+
+    return true;
+}
+
+bool CoreAudioPacketFeeder::empty() const
+{
+    return m_position == m_totalPackets;
+}
+
+CoreAudioInputBuffer::CoreAudioInputBuffer(int bufferSize, int maxPeriodSize, const AudioStreamBasicDescription &inputFormat, const AudioStreamBasicDescription &outputFormat, QObject *parent)
+    : QObject(parent)
+    , m_deviceError(false)
+    , m_device(0)
+    , m_audioConverter(0)
+    , m_inputFormat(inputFormat)
+    , m_outputFormat(outputFormat)
+    , m_volume(qreal(1.0f))
+{
+    m_maxPeriodSize = maxPeriodSize;
+    m_periodTime = m_maxPeriodSize / m_outputFormat.mBytesPerFrame * 1000 / m_outputFormat.mSampleRate;
+
+    m_buffer = new CoreAudioRingBuffer(bufferSize);
+
+    m_inputBufferList = new CoreAudioBufferList(m_inputFormat);
+
+    m_flushTimer = new QTimer(this);
+    connect(m_flushTimer, SIGNAL(timeout()), SLOT(flushBuffer()));
+
+    if (CoreAudioUtils::toQAudioFormat(inputFormat) != CoreAudioUtils::toQAudioFormat(outputFormat)) {
+        if (AudioConverterNew(&m_inputFormat, &m_outputFormat, &m_audioConverter) != noErr) {
+            qWarning() << "QAudioInput: Unable to create an Audio Converter";
+            m_audioConverter = 0;
+        }
+    }
+
+    m_qFormat = CoreAudioUtils::toQAudioFormat(inputFormat); // we adjust volume before conversion
+}
+
+CoreAudioInputBuffer::~CoreAudioInputBuffer()
+{
+    delete m_buffer;
+}
+
+qreal CoreAudioInputBuffer::volume() const
+{
+    return m_volume;
+}
+
+void CoreAudioInputBuffer::setVolume(qreal v)
+{
+    m_volume = v;
+}
+
+qint64 CoreAudioInputBuffer::renderFromDevice(AudioUnit audioUnit, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames)
+{
+    const bool  pullMode = m_device == 0;
+
+    OSStatus    err;
+    qint64      framesRendered = 0;
+
+    m_inputBufferList->reset();
+    err = AudioUnitRender(audioUnit,
+                          ioActionFlags,
+                          inTimeStamp,
+                          inBusNumber,
+                          inNumberFrames,
+                          m_inputBufferList->audioBufferList());
+
+    // adjust volume, if necessary
+    if (!qFuzzyCompare(m_volume, qreal(1.0f))) {
+        QAudioHelperInternal::qMultiplySamples(m_volume,
+                                               m_qFormat,
+                                               m_inputBufferList->data(), /* input */
+                                               m_inputBufferList->data(), /* output */
+                                               m_inputBufferList->bufferSize());
+    }
+
+    if (m_audioConverter != 0) {
+        CoreAudioPacketFeeder  feeder(m_inputBufferList);
+
+        int     copied = 0;
+        const int available = m_buffer->free();
+
+        while (err == noErr && !feeder.empty()) {
+            CoreAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied);
+
+            if (region.second == 0)
+                break;
+
+            AudioBufferList     output;
+            output.mNumberBuffers = 1;
+            output.mBuffers[0].mNumberChannels = 1;
+            output.mBuffers[0].mDataByteSize = region.second;
+            output.mBuffers[0].mData = region.first;
+
+            UInt32  packetSize = region.second / m_outputFormat.mBytesPerPacket;
+            err = AudioConverterFillComplexBuffer(m_audioConverter,
+                                                  converterCallback,
+                                                  &feeder,
+                                                  &packetSize,
+                                                  &output,
+                                                  0);
+            region.second = output.mBuffers[0].mDataByteSize;
+            copied += region.second;
+
+            m_buffer->releaseWriteRegion(region);
+        }
+
+        framesRendered += copied / m_outputFormat.mBytesPerFrame;
+    }
+    else {
+        const int available = m_inputBufferList->bufferSize();
+        bool    wecan = true;
+        int     copied = 0;
+
+        while (wecan && copied < available) {
+            CoreAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied);
+
+            if (region.second > 0) {
+                memcpy(region.first, m_inputBufferList->data() + copied, region.second);
+                copied += region.second;
+            }
+            else
+                wecan = false;
+
+            m_buffer->releaseWriteRegion(region);
+        }
+
+        framesRendered = copied / m_outputFormat.mBytesPerFrame;
+    }
+
+    if (pullMode && framesRendered > 0)
+        emit readyRead();
+
+    return framesRendered;
+}
+
+qint64 CoreAudioInputBuffer::readBytes(char *data, qint64 len)
+{
+    bool    wecan = true;
+    qint64  bytesCopied = 0;
+
+    len -= len % m_maxPeriodSize;
+    while (wecan && bytesCopied < len) {
+        CoreAudioRingBuffer::Region region = m_buffer->acquireReadRegion(len - bytesCopied);
+
+        if (region.second > 0) {
+            memcpy(data + bytesCopied, region.first, region.second);
+            bytesCopied += region.second;
+        }
+        else
+            wecan = false;
+
+        m_buffer->releaseReadRegion(region);
+    }
+
+    return bytesCopied;
+}
+
+void CoreAudioInputBuffer::setFlushDevice(QIODevice *device)
+{
+    if (m_device != device)
+        m_device = device;
+}
+
+void CoreAudioInputBuffer::startFlushTimer()
+{
+    if (m_device != 0) {
+        // We use the period time for the timer, since that's
+        // around the buffer size (pre conversion >.>)
+        m_flushTimer->start(qMax(1, m_periodTime));
+    }
+}
+
+void CoreAudioInputBuffer::stopFlushTimer()
+{
+    m_flushTimer->stop();
+}
+
+void CoreAudioInputBuffer::flush(bool all)
+{
+    if (m_device == 0)
+        return;
+
+    const int used = m_buffer->used();
+    const int readSize = all ? used : used - (used % m_maxPeriodSize);
+
+    if (readSize > 0) {
+        bool    wecan = true;
+        int     flushed = 0;
+
+        while (!m_deviceError && wecan && flushed < readSize) {
+            CoreAudioRingBuffer::Region region = m_buffer->acquireReadRegion(readSize - flushed);
+
+            if (region.second > 0) {
+                int bytesWritten = m_device->write(region.first, region.second);
+                if (bytesWritten < 0) {
+                    stopFlushTimer();
+                    m_deviceError = true;
+                }
+                else {
+                    region.second = bytesWritten;
+                    flushed += bytesWritten;
+                    wecan = bytesWritten != 0;
+                }
+            }
+            else
+                wecan = false;
+
+            m_buffer->releaseReadRegion(region);
+        }
+    }
+}
+
+void CoreAudioInputBuffer::reset()
+{
+    m_buffer->reset();
+    m_deviceError = false;
+}
+
+int CoreAudioInputBuffer::available() const
+{
+    return m_buffer->free();
+}
+
+int CoreAudioInputBuffer::used() const
+{
+    return m_buffer->used();
+}
+
+void CoreAudioInputBuffer::flushBuffer()
+{
+    flush();
+}
+
+OSStatus CoreAudioInputBuffer::converterCallback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData)
+{
+    Q_UNUSED(inAudioConverter);
+    Q_UNUSED(outDataPacketDescription);
+
+    CoreAudioPacketFeeder* feeder = static_cast<CoreAudioPacketFeeder*>(inUserData);
+
+    if (!feeder->feed(*ioData, *ioNumberDataPackets))
+        return as_empty;
+
+    return noErr;
+}
+
+CoreAudioInputDevice::CoreAudioInputDevice(CoreAudioInputBuffer *audioBuffer, QObject *parent)
+    : QIODevice(parent)
+    , m_audioBuffer(audioBuffer)
+{
+    open(QIODevice::ReadOnly | QIODevice::Unbuffered);
+    connect(m_audioBuffer, SIGNAL(readyRead()), SIGNAL(readyRead()));
+}
+
+qint64 CoreAudioInputDevice::readData(char *data, qint64 len)
+{
+    return m_audioBuffer->readBytes(data, len);
+}
+
+qint64 CoreAudioInputDevice::writeData(const char *data, qint64 len)
+{
+    Q_UNUSED(data);
+    Q_UNUSED(len);
+
+    return 0;
+}
+
+CoreAudioInput::CoreAudioInput(const QByteArray &device)
+    : m_isOpen(false)
+    , m_internalBufferSize(DEFAULT_BUFFER_SIZE)
+    , m_totalFrames(0)
+    , m_audioUnit(0)
+    , m_clockFrequency(CoreAudioUtils::frequency() / 1000)
+    , m_startTime(0)
+    , m_errorCode(QAudio::NoError)
+    , m_stateCode(QAudio::StoppedState)
+    , m_audioBuffer(0)
+    , m_volume(1.0)
+{
+#if defined(Q_OS_OSX)
+    quint32 deviceId;
+    QDataStream dataStream(device);
+    dataStream >> deviceId >> m_device;
+    m_audioDeviceId = AudioDeviceID(deviceId);
+#else //iOS
+    m_device = device;
+#endif
+
+    m_audioDeviceInfo = new CoreAudioDeviceInfo(device, QAudio::AudioInput);
+
+    m_intervalTimer = new QTimer(this);
+    m_intervalTimer->setInterval(1000);
+    connect(m_intervalTimer, SIGNAL(timeout()), this, SIGNAL(notify()));
+}
+
+
+CoreAudioInput::~CoreAudioInput()
+{
+    close();
+    delete m_audioDeviceInfo;
+}
+
+bool CoreAudioInput::open()
+{
+    if (m_isOpen)
+        return true;
+
+#if defined(Q_OS_OSX)
+    UInt32  size = 0;
+
+    ComponentDescription componentDescription;
+    componentDescription.componentType = kAudioUnitType_Output;
+    componentDescription.componentSubType = kAudioUnitSubType_HALOutput;
+    componentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
+    componentDescription.componentFlags = 0;
+    componentDescription.componentFlagsMask = 0;
+
+    // Open
+    Component component = FindNextComponent(NULL, &componentDescription);
+    if (component == 0) {
+        qWarning() << "QAudioInput: Failed to find HAL Output component";
+        return false;
+    }
+
+    if (OpenAComponent(component, &m_audioUnit) != noErr) {
+        qWarning() << "QAudioInput: Unable to Open Output Component";
+        return false;
+    }
+#else //iOS
+    AudioComponentDescription componentDescription;
+    componentDescription.componentType = kAudioUnitType_Output;
+    componentDescription.componentSubType = kAudioUnitSubType_RemoteIO;
+    componentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
+    componentDescription.componentFlags = 0;
+    componentDescription.componentFlagsMask = 0;
+
+    AudioComponent component = AudioComponentFindNext(0, &componentDescription);
+    if (component == 0) {
+        qWarning() << "QAudioInput: Failed to find Output component";
+        return false;
+    }
+
+    if (AudioComponentInstanceNew(component, &m_audioUnit) != noErr) {
+        qWarning() << "QAudioInput: Unable to Open Output Component";
+        return false;
+    }
+#endif
+    // Set mode
+    // switch to input mode
+    UInt32 enable = 1;
+    if (AudioUnitSetProperty(m_audioUnit,
+                               kAudioOutputUnitProperty_EnableIO,
+                               kAudioUnitScope_Input,
+                               1,
+                               &enable,
+                               sizeof(enable)) != noErr) {
+        qWarning() << "QAudioInput: Unable to switch to input mode (Enable Input)";
+        return false;
+    }
+
+    enable = 0;
+    if (AudioUnitSetProperty(m_audioUnit,
+                            kAudioOutputUnitProperty_EnableIO,
+                            kAudioUnitScope_Output,
+                            0,
+                            &enable,
+                            sizeof(enable)) != noErr) {
+        qWarning() << "QAudioInput: Unable to switch to input mode (Disable output)";
+        return false;
+    }
+
+    // register callback
+    AURenderCallbackStruct callback;
+    callback.inputProc = inputCallback;
+    callback.inputProcRefCon = this;
+
+    if (AudioUnitSetProperty(m_audioUnit,
+                               kAudioOutputUnitProperty_SetInputCallback,
+                               kAudioUnitScope_Global,
+                               0,
+                               &callback,
+                               sizeof(callback)) != noErr) {
+        qWarning() << "QAudioInput: Failed to set AudioUnit callback";
+        return false;
+    }
+
+#if defined(Q_OS_OSX)
+    //Set Audio Device
+    if (AudioUnitSetProperty(m_audioUnit,
+                             kAudioOutputUnitProperty_CurrentDevice,
+                             kAudioUnitScope_Global,
+                             0,
+                             &m_audioDeviceId,
+                             sizeof(m_audioDeviceId)) != noErr) {
+        qWarning() << "QAudioInput: Unable to use configured device";
+        return false;
+    }
+#endif
+
+    //set format
+    m_streamFormat = CoreAudioUtils::toAudioStreamBasicDescription(m_audioFormat);
+
+#if defined(Q_OS_OSX)
+    if (m_audioFormat == m_audioDeviceInfo->preferredFormat()) {
+#endif
+
+    m_deviceFormat = m_streamFormat;
+    AudioUnitSetProperty(m_audioUnit,
+                         kAudioUnitProperty_StreamFormat,
+                         kAudioUnitScope_Output,
+                         1,
+                         &m_deviceFormat,
+                         sizeof(m_deviceFormat));
+#if defined(Q_OS_OSX)
+    } else {
+        size = sizeof(m_deviceFormat);
+        if (AudioUnitGetProperty(m_audioUnit,
+                                 kAudioUnitProperty_StreamFormat,
+                                 kAudioUnitScope_Input,
+                                 1,
+                                 &m_deviceFormat,
+                                 &size) != noErr) {
+            qWarning() << "QAudioInput: Unable to retrieve device format";
+            return false;
+        }
+
+        if (AudioUnitSetProperty(m_audioUnit,
+                                 kAudioUnitProperty_StreamFormat,
+                                 kAudioUnitScope_Output,
+                                 1,
+                                 &m_deviceFormat,
+                                 sizeof(m_deviceFormat)) != noErr) {
+            qWarning() << "QAudioInput: Unable to set device format";
+            return false;
+        }
+    }
+#endif
+
+    //setup buffers
+    UInt32 numberOfFrames;
+#if defined(Q_OS_OSX)
+    size = sizeof(UInt32);
+    if (AudioUnitGetProperty(m_audioUnit,
+                             kAudioDevicePropertyBufferFrameSize,
+                             kAudioUnitScope_Global,
+                             0,
+                             &numberOfFrames,
+                             &size) != noErr) {
+        qWarning() << "QAudioInput: Failed to get audio period size";
+        return false;
+    }
+    //BUG: numberOfFrames gets ignored after this point
+
+    AudioValueRange bufferRange;
+    size = sizeof(AudioValueRange);
+
+    if (AudioUnitGetProperty(m_audioUnit,
+                             kAudioDevicePropertyBufferFrameSizeRange,
+                             kAudioUnitScope_Global,
+                             0,
+                             &bufferRange,
+                             &size) != noErr) {
+        qWarning() << "QAudioInput: Failed to get audio period size range";
+        return false;
+    }
+
+    // See if the requested buffer size is permissible
+    numberOfFrames = qBound((UInt32)bufferRange.mMinimum, m_internalBufferSize / m_streamFormat.mBytesPerFrame, (UInt32)bufferRange.mMaximum);
+
+    // Set it back
+    if (AudioUnitSetProperty(m_audioUnit,
+                             kAudioDevicePropertyBufferFrameSize,
+                             kAudioUnitScope_Global,
+                             0,
+                             &numberOfFrames,
+                             sizeof(UInt32)) != noErr) {
+        qWarning() << "QAudioInput: Failed to set audio buffer size";
+        return false;
+    }
+#else //iOS
+    Float32 bufferSize = CoreAudioSessionManager::instance().currentIOBufferDuration();
+    bufferSize *= m_streamFormat.mSampleRate;
+    numberOfFrames = bufferSize;
+#endif
+
+    // Now allocate a few buffers to be safe.
+    m_periodSizeBytes = m_internalBufferSize = numberOfFrames * m_streamFormat.mBytesPerFrame;
+
+    m_audioBuffer = new CoreAudioInputBuffer(m_internalBufferSize * 4,
+                                        m_periodSizeBytes,
+                                        m_deviceFormat,
+                                        m_streamFormat,
+                                        this);
+
+    m_audioBuffer->setVolume(m_volume);
+    m_audioIO = new CoreAudioInputDevice(m_audioBuffer, this);
+
+    // Init
+    if (AudioUnitInitialize(m_audioUnit) != noErr) {
+        qWarning() << "QAudioInput: Failed to initialize AudioUnit";
+        return false;
+    }
+
+    m_isOpen = true;
+
+    return m_isOpen;
+
+}
+
+void CoreAudioInput::close()
+{
+    if (m_audioUnit != 0) {
+        AudioOutputUnitStop(m_audioUnit);
+        AudioUnitUninitialize(m_audioUnit);
+#if defined(Q_OS_OSX)
+        CloseComponent(m_audioUnit);
+#else //iOS
+        AudioComponentInstanceDispose(m_audioUnit);
+#endif
+
+    }
+
+    delete m_audioBuffer;
+}
+
+void CoreAudioInput::start(QIODevice *device)
+{
+    QIODevice* op = device;
+
+    if (!m_audioDeviceInfo->isFormatSupported(m_audioFormat) || !open()) {
+        m_stateCode = QAudio::StoppedState;
+        m_errorCode = QAudio::OpenError;
+        return;
+    }
+
+    reset();
+    m_audioBuffer->reset();
+    m_audioBuffer->setFlushDevice(op);
+
+    if (op == 0)
+        op = m_audioIO;
+
+    // Start
+    m_startTime = CoreAudioUtils::currentTime();
+    m_totalFrames = 0;
+
+    m_stateCode = QAudio::IdleState;
+    m_errorCode = QAudio::NoError;
+    emit stateChanged(m_stateCode);
+
+    audioThreadStart();
+}
+
+
+QIODevice *CoreAudioInput::start()
+{
+    QIODevice* op = 0;
+
+    if (!m_audioDeviceInfo->isFormatSupported(m_audioFormat) || !open()) {
+        m_stateCode = QAudio::StoppedState;
+        m_errorCode = QAudio::OpenError;
+        return m_audioIO;
+    }
+
+    reset();
+    m_audioBuffer->reset();
+    m_audioBuffer->setFlushDevice(op);
+
+    if (op == 0)
+        op = m_audioIO;
+
+    // Start
+    m_startTime = CoreAudioUtils::currentTime();
+    m_totalFrames = 0;
+
+    m_stateCode = QAudio::IdleState;
+    m_errorCode = QAudio::NoError;
+    emit stateChanged(m_stateCode);
+
+    audioThreadStart();
+
+    return op;
+}
+
+
+void CoreAudioInput::stop()
+{
+    QMutexLocker lock(&m_mutex);
+    if (m_stateCode != QAudio::StoppedState) {
+        audioThreadStop();
+        m_audioBuffer->flush(true);
+
+        m_errorCode = QAudio::NoError;
+        m_stateCode = QAudio::StoppedState;
+        QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode));
+    }
+}
+
+
+void CoreAudioInput::reset()
+{
+    QMutexLocker lock(&m_mutex);
+    if (m_stateCode != QAudio::StoppedState) {
+        audioThreadStop();
+
+        m_errorCode = QAudio::NoError;
+        m_stateCode = QAudio::StoppedState;
+        m_audioBuffer->reset();
+        QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode));
+    }
+}
+
+
+void CoreAudioInput::suspend()
+{
+    QMutexLocker lock(&m_mutex);
+    if (m_stateCode == QAudio::ActiveState || m_stateCode == QAudio::IdleState) {
+        audioThreadStop();
+
+        m_errorCode = QAudio::NoError;
+        m_stateCode = QAudio::SuspendedState;
+        QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode));
+    }
+}
+
+
+void CoreAudioInput::resume()
+{
+    QMutexLocker lock(&m_mutex);
+    if (m_stateCode == QAudio::SuspendedState) {
+        audioThreadStart();
+
+        m_errorCode = QAudio::NoError;
+        m_stateCode = QAudio::ActiveState;
+        QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode));
+    }
+}
+
+
+int CoreAudioInput::bytesReady() const
+{
+    if (!m_audioBuffer)
+        return 0;
+    return m_audioBuffer->used();
+}
+
+
+int CoreAudioInput::periodSize() const
+{
+    return m_periodSizeBytes;
+}
+
+
+void CoreAudioInput::setBufferSize(int value)
+{
+    m_internalBufferSize = value;
+}
+
+
+int CoreAudioInput::bufferSize() const
+{
+    return m_internalBufferSize;
+}
+
+
+void CoreAudioInput::setNotifyInterval(int milliSeconds)
+{
+    if (m_intervalTimer->interval() == milliSeconds)
+        return;
+
+    if (milliSeconds <= 0)
+        milliSeconds = 0;
+
+    m_intervalTimer->setInterval(milliSeconds);
+}
+
+
+int CoreAudioInput::notifyInterval() const
+{
+    return m_intervalTimer->interval();
+}
+
+
+qint64 CoreAudioInput::processedUSecs() const
+{
+    return m_totalFrames * 1000000 / m_audioFormat.sampleRate();
+}
+
+
+qint64 CoreAudioInput::elapsedUSecs() const
+{
+    if (m_stateCode == QAudio::StoppedState)
+        return 0;
+
+    return (CoreAudioUtils::currentTime() - m_startTime) / (m_clockFrequency / 1000);
+}
+
+
+QAudio::Error CoreAudioInput::error() const
+{
+    return m_errorCode;
+}
+
+
+QAudio::State CoreAudioInput::state() const
+{
+    return m_stateCode;
+}
+
+
+void CoreAudioInput::setFormat(const QAudioFormat &format)
+{
+    if (m_stateCode == QAudio::StoppedState)
+        m_audioFormat = format;
+}
+
+
+QAudioFormat CoreAudioInput::format() const
+{
+    return m_audioFormat;
+}
+
+
+void CoreAudioInput::setVolume(qreal volume)
+{
+    m_volume = volume;
+    if (m_audioBuffer)
+        m_audioBuffer->setVolume(m_volume);
+}
+
+
+qreal CoreAudioInput::volume() const
+{
+    return m_volume;
+}
+
+void CoreAudioInput::deviceStoppped()
+{
+    stopTimers();
+    emit stateChanged(m_stateCode);
+}
+
+void CoreAudioInput::audioThreadStart()
+{
+    startTimers();
+    m_audioThreadState.store(Running);
+    AudioOutputUnitStart(m_audioUnit);
+}
+
+void CoreAudioInput::audioThreadStop()
+{
+    stopTimers();
+    if (m_audioThreadState.testAndSetAcquire(Running, Stopped))
+        m_threadFinished.wait(&m_mutex);
+}
+
+void CoreAudioInput::audioDeviceStop()
+{
+    AudioOutputUnitStop(m_audioUnit);
+    m_audioThreadState.store(Stopped);
+    m_threadFinished.wakeOne();
+}
+
+void CoreAudioInput::audioDeviceActive()
+{
+    if (m_stateCode == QAudio::IdleState) {
+        QMutexLocker lock(&m_mutex);
+        m_stateCode = QAudio::ActiveState;
+        emit stateChanged(m_stateCode);
+    }
+}
+
+void CoreAudioInput::audioDeviceFull()
+{
+    if (m_stateCode == QAudio::ActiveState) {
+        QMutexLocker lock(&m_mutex);
+        m_errorCode = QAudio::UnderrunError;
+        m_stateCode = QAudio::IdleState;
+        emit stateChanged(m_stateCode);
+    }
+}
+
+void CoreAudioInput::audioDeviceError()
+{
+    if (m_stateCode == QAudio::ActiveState) {
+        QMutexLocker lock(&m_mutex);
+        audioDeviceStop();
+
+        m_errorCode = QAudio::IOError;
+        m_stateCode = QAudio::StoppedState;
+        QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
+    }
+}
+
+void CoreAudioInput::startTimers()
+{
+    m_audioBuffer->startFlushTimer();
+    if (m_intervalTimer->interval() > 0)
+        m_intervalTimer->start();
+}
+
+void CoreAudioInput::stopTimers()
+{
+    m_audioBuffer->stopFlushTimer();
+    m_intervalTimer->stop();
+}
+
+OSStatus CoreAudioInput::inputCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
+{
+    Q_UNUSED(ioData);
+
+    CoreAudioInput* d = static_cast<CoreAudioInput*>(inRefCon);
+
+    const int threadState = d->m_audioThreadState.loadAcquire();
+    if (threadState == Stopped)
+        d->audioDeviceStop();
+    else {
+        qint64 framesWritten;
+
+        framesWritten = d->m_audioBuffer->renderFromDevice(d->m_audioUnit,
+                                                         ioActionFlags,
+                                                         inTimeStamp,
+                                                         inBusNumber,
+                                                         inNumberFrames);
+
+        if (framesWritten > 0) {
+            d->m_totalFrames += framesWritten;
+            d->audioDeviceActive();
+        } else if (framesWritten == 0)
+            d->audioDeviceFull();
+        else if (framesWritten < 0)
+            d->audioDeviceError();
+    }
+
+    return noErr;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_coreaudioinput.cpp"
similarity index 52%
rename from src/multimedia/audio/qaudiooutput_mac_p.h
rename to src/plugins/coreaudio/coreaudiooutput.h
index 89e54fd..d4636e0 100644 (file)
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/
+#ifndef IOSAUDIOOUTPUT_H
+#define IOSAUDIOOUTPUT_H
 
-//
-//  W A R N I N G
-//  -------------
-//
-// This file is not part of the Qt API.  It exists for the convenience
-// of other Qt classes.  This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#ifndef QAUDIOOUTPUT_MAC_P_H
-#define QAUDIOOUTPUT_MAC_P_H
-
-#include <CoreServices/CoreServices.h>
-#include <CoreAudio/CoreAudio.h>
-#include <AudioUnit/AudioUnit.h>
-#include <AudioToolbox/AudioToolbox.h>
+#include <qaudiosystem.h>
 
-#include <QtCore/qobject.h>
-#include <QtCore/qmutex.h>
-#include <QtCore/qwaitcondition.h>
-#include <QtCore/qtimer.h>
-#include <QtCore/qatomic.h>
+#if defined(Q_OS_OSX)
+# include <CoreAudio/CoreAudio.h>
+#endif
+#include <AudioUnit/AudioUnit.h>
+#include <CoreAudio/CoreAudioTypes.h>
 
-#include <qaudio.h>
-#include <qaudioformat.h>
-#include <qaudiosystem.h>
+#include <QtCore/QWaitCondition>
+#include <QtCore/QMutex>
 
 QT_BEGIN_NAMESPACE
 
+class CoreAudioOutputBuffer;
+class QTimer;
+class CoreAudioDeviceInfo;
+class CoreAudioRingBuffer;
+
+class CoreAudioOutputBuffer : public QObject
+{
+    Q_OBJECT
+
+public:
+    CoreAudioOutputBuffer(int bufferSize, int maxPeriodSize, QAudioFormat const& audioFormat);
+    ~CoreAudioOutputBuffer();
+
+    qint64 readFrames(char *data, qint64 maxFrames);
+    qint64 writeBytes(const char *data, qint64 maxSize);
+
+    int available() const;
+    void reset();
+
+    void setPrefetchDevice(QIODevice *device);
+
+    void startFillTimer();
+    void stopFillTimer();
+
+signals:
+    void readyRead();
 
-class QIODevice;
-class QAbstractAudioDeviceInfo;
+private slots:
+    void fillBuffer();
+
+private:
+    bool m_deviceError;
+    int m_maxPeriodSize;
+    int m_bytesPerFrame;
+    int m_periodTime;
+    QIODevice *m_device;
+    QTimer *m_fillTimer;
+    CoreAudioRingBuffer *m_buffer;
+};
 
-namespace QtMultimediaInternal
+class CoreAudioOutputDevice : public QIODevice
 {
-class QAudioOutputBuffer;
-}
+public:
+    CoreAudioOutputDevice(CoreAudioOutputBuffer *audioBuffer, QObject *parent);
+
+    qint64 readData(char *data, qint64 len);
+    qint64 writeData(const char *data, qint64 len);
+
+    bool isSequential() const { return true; }
+
+private:
+    CoreAudioOutputBuffer *m_audioBuffer;
+};
 
-class QAudioOutputPrivate : public QAbstractAudioOutput
+
+class CoreAudioOutput : public QAbstractAudioOutput
 {
     Q_OBJECT
 
 public:
-    bool            isOpen;
-    int             internalBufferSize;
-    int             periodSizeBytes;
-    qint64          totalFrames;
-    QAudioFormat    audioFormat;
-    QIODevice*      audioIO;
-    AudioDeviceID   audioDeviceId;
-    AudioUnit       audioUnit;
-    Float64         clockFrequency;
-    UInt64          startTime;
-    AudioStreamBasicDescription deviceFormat;
-    AudioStreamBasicDescription streamFormat;
-    QtMultimediaInternal::QAudioOutputBuffer*   audioBuffer;
-    QAtomicInt      audioThreadState;
-    QWaitCondition  threadFinished;
-    QMutex          mutex;
-    QTimer*         intervalTimer;
-    QAbstractAudioDeviceInfo *audioDeviceInfo;
-    qreal           cachedVolume;
-
-    QAudio::Error    errorCode;
-    QAudio::State    stateCode;
-
-    QAudioOutputPrivate(const QByteArray& device);
-    ~QAudioOutputPrivate();
-
-    bool open();
-    void close();
-
-    QAudioFormat format() const;
-    void setFormat(const QAudioFormat& fmt);
+    CoreAudioOutput(const QByteArray &device);
+    ~CoreAudioOutput();
 
-    QIODevice* start();
-    void start(QIODevice* device);
+    void start(QIODevice *device);
+    QIODevice *start();
     void stop();
     void reset();
     void suspend();
     void resume();
-
     int bytesFree() const;
     int periodSize() const;
-
     void setBufferSize(int value);
     int bufferSize() const;
-
     void setNotifyInterval(int milliSeconds);
     int notifyInterval() const;
-
     qint64 processedUSecs() const;
     qint64 elapsedUSecs() const;
-
     QAudio::Error error() const;
     QAudio::State state() const;
+    void setFormat(const QAudioFormat &format);
+    QAudioFormat format() const;
+
+    void setVolume(qreal volume);
+    qreal volume() const;
 
+    void setCategory(const QString &category);
+    QString category() const;
+
+private slots:
+    void deviceStopped();
+    void inputReady();
+
+private:
+    enum {
+        Running,
+        Draining,
+        Stopped
+    };
+
+    static OSStatus renderCallback(void *inRefCon,
+                                    AudioUnitRenderActionFlags *ioActionFlags,
+                                    const AudioTimeStamp *inTimeStamp,
+                                    UInt32 inBusNumber,
+                                    UInt32 inNumberFrames,
+                                    AudioBufferList *ioData);
+
+    bool open();
+    void close();
     void audioThreadStart();
     void audioThreadStop();
     void audioThreadDrain();
-
     void audioDeviceStop();
     void audioDeviceIdle();
     void audioDeviceError();
@@ -149,24 +172,33 @@ public:
     void startTimers();
     void stopTimers();
 
-    void setVolume(qreal);
-    qreal volume() const;
+    QByteArray m_device;
 
-private slots:
-    void deviceStopped();
-    void inputReady();
-
-private:
-    enum { Running, Draining, Stopped };
-
-    static OSStatus renderCallback(void* inRefCon,
-                                    AudioUnitRenderActionFlags* ioActionFlags,
-                                    const AudioTimeStamp* inTimeStamp,
-                                    UInt32 inBusNumber,
-                                    UInt32 inNumberFrames,
-                                    AudioBufferList* ioData);
+    bool m_isOpen;
+    int m_internalBufferSize;
+    int m_periodSizeBytes;
+    qint64 m_totalFrames;
+    QAudioFormat m_audioFormat;
+    QIODevice *m_audioIO;
+#if defined(Q_OS_OSX)
+    AudioDeviceID m_audioDeviceId;
+#endif
+    AudioUnit m_audioUnit;
+    Float64 m_clockFrequency;
+    UInt64 m_startTime;
+    AudioStreamBasicDescription m_streamFormat;
+    CoreAudioOutputBuffer *m_audioBuffer;
+    QAtomicInt m_audioThreadState;
+    QWaitCondition m_threadFinished;
+    QMutex m_mutex;
+    QTimer *m_intervalTimer;
+    CoreAudioDeviceInfo *m_audioDeviceInfo;
+    qreal m_cachedVolume;
+
+    QAudio::Error m_errorCode;
+    QAudio::State m_stateCode;
 };
 
 QT_END_NAMESPACE
 
-#endif
+#endif // IOSAUDIOOUTPUT_H
diff --git a/src/plugins/coreaudio/coreaudiooutput.mm b/src/plugins/coreaudio/coreaudiooutput.mm
new file mode 100644 (file)
index 0000000..bb0da57
--- /dev/null
@@ -0,0 +1,732 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "coreaudiooutput.h"
+#include "coreaudiosessionmanager.h"
+#include "coreaudiodeviceinfo.h"
+#include "coreaudioutils.h"
+
+#include <QtCore/QTimer>
+#include <QtCore/QDebug>
+
+#include <AudioUnit/AudioUnit.h>
+#include <AudioToolbox/AudioToolbox.h>
+#if defined(Q_OS_OSX)
+# include <CoreServices/CoreServices.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static const int DEFAULT_BUFFER_SIZE = 8 * 1024;
+
+CoreAudioOutputBuffer::CoreAudioOutputBuffer(int bufferSize, int maxPeriodSize, const QAudioFormat &audioFormat)
+    : m_deviceError(false)
+    , m_maxPeriodSize(maxPeriodSize)
+    , m_device(0)
+{
+    m_buffer = new CoreAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize)));
+    m_bytesPerFrame = (audioFormat.sampleSize() / 8) * audioFormat.channelCount();
+    m_periodTime = m_maxPeriodSize / m_bytesPerFrame * 1000 / audioFormat.sampleRate();
+
+    m_fillTimer = new QTimer(this);
+    connect(m_fillTimer, SIGNAL(timeout()), SLOT(fillBuffer()));
+}
+
+CoreAudioOutputBuffer::~CoreAudioOutputBuffer()
+{
+    delete m_buffer;
+}
+
+qint64 CoreAudioOutputBuffer::readFrames(char *data, qint64 maxFrames)
+{
+    bool    wecan = true;
+    qint64  framesRead = 0;
+
+    while (wecan && framesRead < maxFrames) {
+        CoreAudioRingBuffer::Region region = m_buffer->acquireReadRegion((maxFrames - framesRead) * m_bytesPerFrame);
+
+        if (region.second > 0) {
+            // Ensure that we only read whole frames.
+            region.second -= region.second % m_bytesPerFrame;
+
+            if (region.second > 0) {
+                memcpy(data + (framesRead * m_bytesPerFrame), region.first, region.second);
+                framesRead += region.second / m_bytesPerFrame;
+            } else
+                wecan = false; // If there is only a partial frame left we should exit.
+        }
+        else
+            wecan = false;
+
+        m_buffer->releaseReadRegion(region);
+    }
+
+    if (framesRead == 0 && m_deviceError)
+        framesRead = -1;
+
+    return framesRead;
+}
+
+qint64 CoreAudioOutputBuffer::writeBytes(const char *data, qint64 maxSize)
+{
+    bool    wecan = true;
+    qint64  bytesWritten = 0;
+
+    maxSize -= maxSize % m_bytesPerFrame;
+    while (wecan && bytesWritten < maxSize) {
+        CoreAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(maxSize - bytesWritten);
+
+        if (region.second > 0) {
+            memcpy(region.first, data + bytesWritten, region.second);
+            bytesWritten += region.second;
+        }
+        else
+            wecan = false;
+
+        m_buffer->releaseWriteRegion(region);
+    }
+
+    if (bytesWritten > 0)
+        emit readyRead();
+
+    return bytesWritten;
+}
+
+int CoreAudioOutputBuffer::available() const
+{
+    return m_buffer->free();
+}
+
+void CoreAudioOutputBuffer::reset()
+{
+    m_buffer->reset();
+    m_device = 0;
+    m_deviceError = false;
+}
+
+void CoreAudioOutputBuffer::setPrefetchDevice(QIODevice *device)
+{
+    if (m_device != device) {
+        m_device = device;
+        if (m_device != 0)
+            fillBuffer();
+    }
+}
+
+void CoreAudioOutputBuffer::startFillTimer()
+{
+    if (m_device != 0)
+        m_fillTimer->start(m_buffer->size() / 2 / m_maxPeriodSize * m_periodTime);
+}
+
+void CoreAudioOutputBuffer::stopFillTimer()
+{
+    m_fillTimer->stop();
+}
+
+void CoreAudioOutputBuffer::fillBuffer()
+{
+    const int free = m_buffer->free();
+    const int writeSize = free - (free % m_maxPeriodSize);
+
+    if (writeSize > 0) {
+        bool    wecan = true;
+        int     filled = 0;
+
+        while (!m_deviceError && wecan && filled < writeSize) {
+            CoreAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(writeSize - filled);
+
+            if (region.second > 0) {
+                region.second = m_device->read(region.first, region.second);
+                if (region.second > 0)
+                    filled += region.second;
+                else if (region.second == 0)
+                    wecan = false;
+                else if (region.second < 0) {
+                    m_fillTimer->stop();
+                    region.second = 0;
+                    m_deviceError = true;
+                }
+            }
+            else
+                wecan = false;
+
+            m_buffer->releaseWriteRegion(region);
+        }
+
+        if (filled > 0)
+            emit readyRead();
+    }
+}
+
+CoreAudioOutputDevice::CoreAudioOutputDevice(CoreAudioOutputBuffer *audioBuffer, QObject *parent)
+    : QIODevice(parent)
+    , m_audioBuffer(audioBuffer)
+{
+    open(QIODevice::WriteOnly | QIODevice::Unbuffered);
+}
+
+qint64 CoreAudioOutputDevice::readData(char *data, qint64 len)
+{
+    Q_UNUSED(data);
+    Q_UNUSED(len);
+
+    return 0;
+}
+
+qint64 CoreAudioOutputDevice::writeData(const char *data, qint64 len)
+{
+    return m_audioBuffer->writeBytes(data, len);
+}
+
+CoreAudioOutput::CoreAudioOutput(const QByteArray &device)
+    : m_isOpen(false)
+    , m_internalBufferSize(DEFAULT_BUFFER_SIZE)
+    , m_totalFrames(0)
+    , m_audioIO(0)
+    , m_audioUnit(0)
+    , m_startTime(0)
+    , m_audioBuffer(0)
+    , m_cachedVolume(1.0)
+    , m_errorCode(QAudio::NoError)
+    , m_stateCode(QAudio::StoppedState)
+{
+#if defined(Q_OS_OSX)
+    quint32 deviceID;
+    QDataStream dataStream(device);
+    dataStream >> deviceID >> m_device;
+    m_audioDeviceId = AudioDeviceID(deviceID);
+#else //iOS
+    m_device = device;
+#endif
+
+    m_clockFrequency = CoreAudioUtils::frequency() / 1000;
+    m_audioDeviceInfo = new CoreAudioDeviceInfo(device, QAudio::AudioOutput);
+    m_audioThreadState.store(Stopped);
+
+    m_intervalTimer = new QTimer(this);
+    m_intervalTimer->setInterval(1000);
+    connect(m_intervalTimer, SIGNAL(timeout()), this, SIGNAL(notify()));
+}
+
+CoreAudioOutput::~CoreAudioOutput()
+{
+    close();
+    delete m_audioDeviceInfo;
+}
+
+void CoreAudioOutput::start(QIODevice *device)
+{
+    QIODevice* op = device;
+
+    if (!m_audioDeviceInfo->isFormatSupported(m_audioFormat) || !open()) {
+        m_stateCode = QAudio::StoppedState;
+        m_errorCode = QAudio::OpenError;
+        return;
+    }
+
+    reset();
+    m_audioBuffer->reset();
+    m_audioBuffer->setPrefetchDevice(op);
+
+    if (op == 0) {
+        op = m_audioIO;
+        m_stateCode = QAudio::IdleState;
+    }
+    else
+        m_stateCode = QAudio::ActiveState;
+
+    // Start
+    m_errorCode = QAudio::NoError;
+    m_totalFrames = 0;
+    m_startTime = CoreAudioUtils::currentTime();
+
+    if (m_stateCode == QAudio::ActiveState)
+        audioThreadStart();
+
+    emit stateChanged(m_stateCode);
+}
+
+QIODevice *CoreAudioOutput::start()
+{
+    if (!m_audioDeviceInfo->isFormatSupported(m_audioFormat) || !open()) {
+        m_stateCode = QAudio::StoppedState;
+        m_errorCode = QAudio::OpenError;
+        return m_audioIO;
+    }
+
+    reset();
+    m_audioBuffer->reset();
+    m_audioBuffer->setPrefetchDevice(0);
+
+    m_stateCode = QAudio::IdleState;
+
+    // Start
+    m_errorCode = QAudio::NoError;
+    m_totalFrames = 0;
+    m_startTime = CoreAudioUtils::currentTime();
+
+    emit stateChanged(m_stateCode);
+
+    return m_audioIO;
+}
+
+void CoreAudioOutput::stop()
+{
+    QMutexLocker lock(&m_mutex);
+    if (m_stateCode != QAudio::StoppedState) {
+        audioThreadDrain();
+
+        m_stateCode = QAudio::StoppedState;
+        m_errorCode = QAudio::NoError;
+        emit stateChanged(m_stateCode);
+    }
+}
+
+void CoreAudioOutput::reset()
+{
+    QMutexLocker lock(&m_mutex);
+    if (m_stateCode != QAudio::StoppedState) {
+        audioThreadStop();
+
+        m_stateCode = QAudio::StoppedState;
+        m_errorCode = QAudio::NoError;
+        emit stateChanged(m_stateCode);
+    }
+}
+
+void CoreAudioOutput::suspend()
+{
+    QMutexLocker lock(&m_mutex);
+    if (m_stateCode == QAudio::ActiveState || m_stateCode == QAudio::IdleState) {
+        audioThreadStop();
+
+        m_stateCode = QAudio::SuspendedState;
+        m_errorCode = QAudio::NoError;
+        emit stateChanged(m_stateCode);
+    }
+}
+
+void CoreAudioOutput::resume()
+{
+    QMutexLocker lock(&m_mutex);
+    if (m_stateCode == QAudio::SuspendedState) {
+        audioThreadStart();
+
+        m_stateCode = QAudio::ActiveState;
+        m_errorCode = QAudio::NoError;
+        emit stateChanged(m_stateCode);
+    }
+}
+
+int CoreAudioOutput::bytesFree() const
+{
+    return m_audioBuffer->available();
+}
+
+int CoreAudioOutput::periodSize() const
+{
+    return m_periodSizeBytes;
+}
+
+void CoreAudioOutput::setBufferSize(int value)
+{
+    if (m_stateCode == QAudio::StoppedState)
+        m_internalBufferSize = value;
+}
+
+int CoreAudioOutput::bufferSize() const
+{
+    return m_internalBufferSize;
+}
+
+void CoreAudioOutput::setNotifyInterval(int milliSeconds)
+{
+    if (m_intervalTimer->interval() == milliSeconds)
+        return;
+
+    if (milliSeconds <= 0)
+        milliSeconds = 0;
+
+    m_intervalTimer->setInterval(milliSeconds);
+}
+
+int CoreAudioOutput::notifyInterval() const
+{
+    return m_intervalTimer->interval();
+}
+
+qint64 CoreAudioOutput::processedUSecs() const
+{
+    return m_totalFrames * 1000000 / m_audioFormat.sampleRate();
+}
+
+qint64 CoreAudioOutput::elapsedUSecs() const
+{
+    if (m_stateCode == QAudio::StoppedState)
+        return 0;
+
+    return (CoreAudioUtils::currentTime() - m_startTime) / (m_clockFrequency / 1000);
+}
+
+QAudio::Error CoreAudioOutput::error() const
+{
+    return m_errorCode;
+}
+
+QAudio::State CoreAudioOutput::state() const
+{
+    return m_stateCode;
+}
+
+void CoreAudioOutput::setFormat(const QAudioFormat &format)
+{
+    if (m_stateCode == QAudio::StoppedState)
+        m_audioFormat = format;
+}
+
+QAudioFormat CoreAudioOutput::format() const
+{
+    return m_audioFormat;
+}
+
+void CoreAudioOutput::setVolume(qreal volume)
+{
+    const qreal normalizedVolume = qBound(qreal(0.0), volume, qreal(1.0));
+    m_cachedVolume = normalizedVolume;
+    if (!m_isOpen) {
+        return;
+    }
+
+    //TODO: actually set the output volume here
+    //To set the output volume you need a handle to the mixer unit
+}
+
+qreal CoreAudioOutput::volume() const
+{
+    return m_cachedVolume;
+}
+
+void CoreAudioOutput::setCategory(const QString &category)
+{
+    Q_UNUSED(category);
+}
+
+QString CoreAudioOutput::category() const
+{
+    return QString();
+}
+
+void CoreAudioOutput::deviceStopped()
+{
+    m_intervalTimer->stop();
+    emit stateChanged(m_stateCode);
+}
+
+void CoreAudioOutput::inputReady()
+{
+    QMutexLocker lock(&m_mutex);
+    if (m_stateCode == QAudio::IdleState) {
+        audioThreadStart();
+
+        m_stateCode = QAudio::ActiveState;
+        m_errorCode = QAudio::NoError;
+
+        emit stateChanged(m_stateCode);
+    }
+}
+
+OSStatus CoreAudioOutput::renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
+{
+    Q_UNUSED(ioActionFlags)
+    Q_UNUSED(inTimeStamp)
+    Q_UNUSED(inBusNumber)
+    Q_UNUSED(inNumberFrames)
+
+    CoreAudioOutput* d = static_cast<CoreAudioOutput*>(inRefCon);
+
+    const int threadState = d->m_audioThreadState.fetchAndAddAcquire(0);
+    if (threadState == Stopped) {
+        ioData->mBuffers[0].mDataByteSize = 0;
+        d->audioDeviceStop();
+    }
+    else {
+        const UInt32 bytesPerFrame = d->m_streamFormat.mBytesPerFrame;
+        qint64 framesRead;
+
+        framesRead = d->m_audioBuffer->readFrames((char*)ioData->mBuffers[0].mData,
+                                                 ioData->mBuffers[0].mDataByteSize / bytesPerFrame);
+
+        if (framesRead > 0) {
+            ioData->mBuffers[0].mDataByteSize = framesRead * bytesPerFrame;
+            d->m_totalFrames += framesRead;
+        }
+        else {
+            ioData->mBuffers[0].mDataByteSize = 0;
+            if (framesRead == 0) {
+                if (threadState == Draining)
+                    d->audioDeviceStop();
+                else
+                    d->audioDeviceIdle();
+            }
+            else
+                d->audioDeviceError();
+        }
+    }
+
+    return noErr;
+}
+
+bool CoreAudioOutput::open()
+{
+    if (m_errorCode != QAudio::NoError)
+        return false;
+
+    if (m_isOpen)
+        return true;
+
+#if defined(Q_OS_OSX)
+    ComponentDescription componentDescription;
+    componentDescription.componentType = kAudioUnitType_Output;
+    componentDescription.componentSubType = kAudioUnitSubType_HALOutput;
+    componentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
+    componentDescription.componentFlags = 0;
+    componentDescription.componentFlagsMask = 0;
+
+    // Open
+    Component component = FindNextComponent(NULL, &componentDescription);
+    if (component == 0) {
+        qWarning() << "QAudioOutput: Failed to find HAL Output component";
+        return false;
+    }
+
+    if (OpenAComponent(component, &m_audioUnit) != noErr) {
+        qWarning() << "QAudioOutput: Unable to Open Output Component";
+        return false;
+    }
+#else //iOS
+
+    AudioComponentDescription componentDescription;
+    componentDescription.componentType = kAudioUnitType_Output;
+    componentDescription.componentSubType = kAudioUnitSubType_RemoteIO;
+    componentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
+    componentDescription.componentFlags = 0;
+    componentDescription.componentFlagsMask = 0;
+
+    AudioComponent component = AudioComponentFindNext(0, &componentDescription);
+    if (component == 0) {
+        qWarning() << "QAudioOutput: Failed to find Output component";
+        return false;
+    }
+
+    if (AudioComponentInstanceNew(component, &m_audioUnit) != noErr) {
+        qWarning() << "QAudioOutput: Unable to Open Output Component";
+        return false;
+    }
+#endif
+
+    // register callback
+    AURenderCallbackStruct callback;
+    callback.inputProc = renderCallback;
+    callback.inputProcRefCon = this;
+
+    if (AudioUnitSetProperty(m_audioUnit,
+                             kAudioUnitProperty_SetRenderCallback,
+                             kAudioUnitScope_Global,
+                             0,
+                             &callback,
+                             sizeof(callback)) != noErr) {
+        qWarning() << "QAudioOutput: Failed to set AudioUnit callback";
+        return false;
+    }
+
+#if defined(Q_OS_OSX)
+    //Set Audio Device
+    if (AudioUnitSetProperty(m_audioUnit,
+                             kAudioOutputUnitProperty_CurrentDevice,
+                             kAudioUnitScope_Global,
+                             0,
+                             &m_audioDeviceId,
+                             sizeof(m_audioDeviceId)) != noErr) {
+        qWarning() << "QAudioOutput: Unable to use configured device";
+        return false;
+    }
+#endif
+
+    // Set stream format
+    m_streamFormat = CoreAudioUtils::toAudioStreamBasicDescription(m_audioFormat);
+
+    UInt32 size = sizeof(m_streamFormat);
+    if (AudioUnitSetProperty(m_audioUnit,
+                                kAudioUnitProperty_StreamFormat,
+                                kAudioUnitScope_Input,
+                                0,
+                                &m_streamFormat,
+                                size) != noErr) {
+        qWarning() << "QAudioOutput: Unable to Set Stream information";
+        return false;
+    }
+
+    // Allocate buffer
+    UInt32 numberOfFrames = 0;
+#if defined(Q_OS_OSX)
+    size = sizeof(UInt32);
+    if (AudioUnitGetProperty(m_audioUnit,
+                             kAudioDevicePropertyBufferFrameSize,
+                             kAudioUnitScope_Global,
+                             0,
+                             &numberOfFrames,
+                             &size) != noErr) {
+        qWarning() << "QAudioInput: Failed to get audio period size";
+        return false;
+    }
+#else //iOS
+    Float32 bufferSize = CoreAudioSessionManager::instance().currentIOBufferDuration();
+    bufferSize *= m_streamFormat.mSampleRate;
+    numberOfFrames = bufferSize;
+#endif
+
+    m_periodSizeBytes = numberOfFrames * m_streamFormat.mBytesPerFrame;
+    if (m_internalBufferSize < m_periodSizeBytes * 2)
+        m_internalBufferSize = m_periodSizeBytes * 2;
+    else
+        m_internalBufferSize -= m_internalBufferSize % m_streamFormat.mBytesPerFrame;
+
+    m_audioBuffer = new CoreAudioOutputBuffer(m_internalBufferSize, m_periodSizeBytes, m_audioFormat);
+    connect(m_audioBuffer, SIGNAL(readyRead()), SLOT(inputReady())); //Pull
+
+    m_audioIO = new CoreAudioOutputDevice(m_audioBuffer, this);
+
+    //Init
+    if (AudioUnitInitialize(m_audioUnit)) {
+        qWarning() << "QAudioOutput: Failed to initialize AudioUnit";
+        return false;
+    }
+
+    m_isOpen = true;
+
+    setVolume(m_cachedVolume);
+
+    return true;
+}
+
+void CoreAudioOutput::close()
+{
+    if (m_audioUnit != 0) {
+        AudioOutputUnitStop(m_audioUnit);
+        AudioUnitUninitialize(m_audioUnit);
+#if defined(Q_OS_OSX)
+        CloseComponent(m_audioUnit);
+#else //iOS
+        AudioComponentInstanceDispose(m_audioUnit);
+#endif
+    }
+
+    delete m_audioBuffer;
+}
+
+void CoreAudioOutput::audioThreadStart()
+{
+    startTimers();
+    m_audioThreadState.store(Running);
+    AudioOutputUnitStart(m_audioUnit);
+}
+
+void CoreAudioOutput::audioThreadStop()
+{
+    stopTimers();
+    if (m_audioThreadState.testAndSetAcquire(Running, Stopped))
+        m_threadFinished.wait(&m_mutex);
+}
+
+void CoreAudioOutput::audioThreadDrain()
+{
+    stopTimers();
+    if (m_audioThreadState.testAndSetAcquire(Running, Draining))
+        m_threadFinished.wait(&m_mutex);
+}
+
+void CoreAudioOutput::audioDeviceStop()
+{
+    AudioOutputUnitStop(m_audioUnit);
+    m_audioThreadState.store(Stopped);
+    m_threadFinished.wakeOne();
+}
+
+void CoreAudioOutput::audioDeviceIdle()
+{
+    if (m_stateCode == QAudio::ActiveState) {
+        QMutexLocker lock(&m_mutex);
+        audioDeviceStop();
+
+        m_errorCode = QAudio::UnderrunError;
+        m_stateCode = QAudio::IdleState;
+        QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
+    }
+}
+
+void CoreAudioOutput::audioDeviceError()
+{
+    if (m_stateCode == QAudio::ActiveState) {
+        QMutexLocker lock(&m_mutex);
+        audioDeviceStop();
+
+        m_errorCode = QAudio::IOError;
+        m_stateCode = QAudio::StoppedState;
+        QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
+    }
+}
+
+void CoreAudioOutput::startTimers()
+{
+    m_audioBuffer->startFillTimer();
+    if (m_intervalTimer->interval() > 0)
+        m_intervalTimer->start();
+}
+
+void CoreAudioOutput::stopTimers()
+{
+    m_audioBuffer->stopFillTimer();
+    m_intervalTimer->stop();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_coreaudiooutput.cpp"
diff --git a/src/plugins/coreaudio/coreaudioplugin.h b/src/plugins/coreaudio/coreaudioplugin.h
new file mode 100644 (file)
index 0000000..88b10c6
--- /dev/null
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef IOSAUDIOPLUGIN_H
+#define IOSAUDIOPLUGIN_H
+
+#include <qaudiosystemplugin.h>
+
+QT_BEGIN_NAMESPACE
+
+class CoreAudioPlugin : public QAudioSystemPlugin
+{
+    Q_OBJECT
+    Q_PLUGIN_METADATA(IID "org.qt-project.qt.audiosystemfactory/5.0" FILE "coreaudio.json")
+
+public:
+    explicit CoreAudioPlugin(QObject *parent = 0);
+    ~CoreAudioPlugin() {}
+
+    QList<QByteArray> availableDevices(QAudio::Mode mode) const Q_DECL_OVERRIDE;
+    QAbstractAudioInput *createInput(const QByteArray &device) Q_DECL_OVERRIDE;
+    QAbstractAudioOutput *createOutput(const QByteArray &device) Q_DECL_OVERRIDE;
+    QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode) Q_DECL_OVERRIDE;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/coreaudio/coreaudioplugin.mm b/src/plugins/coreaudio/coreaudioplugin.mm
new file mode 100644 (file)
index 0000000..587815e
--- /dev/null
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "coreaudioplugin.h"
+
+#include "coreaudiodeviceinfo.h"
+#include "coreaudioinput.h"
+#include "coreaudiooutput.h"
+
+QT_BEGIN_NAMESPACE
+
+CoreAudioPlugin::CoreAudioPlugin(QObject *parent)
+    : QAudioSystemPlugin(parent)
+{
+}
+
+
+QList<QByteArray> CoreAudioPlugin::availableDevices(QAudio::Mode mode) const
+{
+    return CoreAudioDeviceInfo::availableDevices(mode);
+}
+
+
+QAbstractAudioInput *CoreAudioPlugin::createInput(const QByteArray &device)
+{
+    return new CoreAudioInput(device);
+}
+
+
+QAbstractAudioOutput *CoreAudioPlugin::createOutput(const QByteArray &device)
+{
+    return new CoreAudioOutput(device);
+}
+
+
+QAbstractAudioDeviceInfo *CoreAudioPlugin::createDeviceInfo(const QByteArray &device, QAudio::Mode mode)
+{
+    return new CoreAudioDeviceInfo(device, mode);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_coreaudioplugin.cpp"
diff --git a/src/plugins/coreaudio/coreaudiosessionmanager.h b/src/plugins/coreaudio/coreaudiosessionmanager.h
new file mode 100644 (file)
index 0000000..61d8967
--- /dev/null
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef IOSAUDIOSESSIONMANAGER_H
+#define IOSAUDIOSESSIONMANAGER_H
+
+#include <QObject>
+#ifdef QT_DEBUG_COREAUDIO
+# include <QtCore/QDebug>
+#endif
+
+@class CoreAudioSessionObserver;
+
+QT_BEGIN_NAMESPACE
+
+class CoreAudioSessionManager : public QObject
+{
+    Q_OBJECT
+public:
+    enum AudioSessionCategorys {
+        Ambient,
+        SoloAmbient,
+        Playback,
+        Record,
+        PlayAndRecord,
+        AudioProcessing,
+        MultiRoute
+    };
+    enum AudioSessionCategoryOptions {
+        None = 0,
+        MixWithOthers = 1,
+        DuckOthers = 2,
+        AllowBluetooth = 4,
+        DefaultToSpeaker = 8
+    };
+    enum AudioSessionModes {
+        Default,
+        VoiceChat,
+        GameChat,
+        VideoRecording,
+        Measurement,
+        MoviePlayback
+    };
+
+    static CoreAudioSessionManager& instance();
+
+    bool setActive(bool active);
+    bool setCategory(AudioSessionCategorys category, AudioSessionCategoryOptions options = None);
+    bool setMode(AudioSessionModes mode);
+
+    AudioSessionCategorys category();
+    AudioSessionModes mode();
+
+    QList<QByteArray> inputDevices();
+    QList<QByteArray> outputDevices();
+
+    int inputChannelCount();
+    int outputChannelCount();
+
+    float currentIOBufferDuration();
+    float preferredSampleRate();
+
+signals:
+    void activeChanged();
+    void categoryChanged();
+    void modeChanged();
+    void routeChanged();
+    void inputDevicesAvailableChanged();
+    void outputDevicesAvailableChanged();
+
+private:
+    CoreAudioSessionManager();
+    ~CoreAudioSessionManager();
+    CoreAudioSessionManager(CoreAudioSessionManager const &copy);
+    CoreAudioSessionManager& operator =(CoreAudioSessionManager const &copy);
+
+    CoreAudioSessionObserver *m_sessionObserver;
+};
+
+#ifdef QT_DEBUG_COREAUDIO
+QDebug operator <<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategorys category);
+QDebug operator <<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategoryOptions option);
+QDebug operator <<(QDebug dbg, CoreAudioSessionManager::AudioSessionModes mode);
+#endif
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(CoreAudioSessionManager::AudioSessionCategorys)
+Q_DECLARE_METATYPE(CoreAudioSessionManager::AudioSessionCategoryOptions)
+Q_DECLARE_METATYPE(CoreAudioSessionManager::AudioSessionModes)
+
+#endif // IOSAUDIOSESSIONMANAGER_H
diff --git a/src/plugins/coreaudio/coreaudiosessionmanager.mm b/src/plugins/coreaudio/coreaudiosessionmanager.mm
new file mode 100644 (file)
index 0000000..4b3bdb7
--- /dev/null
@@ -0,0 +1,481 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "coreaudiosessionmanager.h"
+
+#import <AVFoundation/AVAudioSession.h>
+#import <Foundation/Foundation.h>
+
+QT_BEGIN_NAMESPACE
+
+@interface CoreAudioSessionObserver : NSObject
+{
+    CoreAudioSessionManager *m_sessionManager;
+    AVAudioSession *m_audioSession;
+}
+
+@property (readonly, getter=sessionManager) CoreAudioSessionManager *m_sessionManager;
+@property (readonly, getter=audioSession) AVAudioSession *m_audioSession;
+
+-(CoreAudioSessionObserver *)initWithAudioSessionManager:(CoreAudioSessionManager *)sessionManager;
+
+-(BOOL)activateAudio;
+-(BOOL)deactivateAudio;
+
+//Notification handlers
+-(void)audioSessionInterruption:(NSNotification *)notification;
+-(void)audioSessionRouteChange:(NSNotification *)notification;
+-(void)audioSessionMediaServicesWereReset:(NSNotification *)notification;
+
+@end //interface CoreAudioSessionObserver
+
+@implementation CoreAudioSessionObserver
+
+@synthesize m_sessionManager, m_audioSession;
+
+-(CoreAudioSessionObserver *)initWithAudioSessionManager:(CoreAudioSessionManager *)sessionManager
+{
+        if (!(self = [super init]))
+            return nil;
+
+        self->m_sessionManager = sessionManager;
+        self->m_audioSession = [AVAudioSession sharedInstance];
+
+        //Set up observers
+        [[NSNotificationCenter defaultCenter] addObserver:self
+                                                 selector:@selector(audioSessionInterruption:)
+                                                     name:AVAudioSessionInterruptionNotification
+                                                   object:self->m_audioSession];
+        [[NSNotificationCenter defaultCenter] addObserver:self
+                                                 selector:@selector(audioSessionMediaServicesWereReset:)
+                                                     name:AVAudioSessionMediaServicesWereResetNotification
+                                                   object:self->m_audioSession];
+        [[NSNotificationCenter defaultCenter] addObserver:self
+                                                 selector:@selector(audioSessionRouteChange:)
+                                                     name:AVAudioSessionRouteChangeNotification
+                                                   object:self->m_audioSession];
+
+        return self;
+}
+
+-(void)dealloc
+{
+#ifdef QT_DEBUG_COREAUDIO
+    qDebug() << Q_FUNC_INFO;
+#endif
+    [[NSNotificationCenter defaultCenter] removeObserver:self
+                                                    name:AVAudioSessionInterruptionNotification
+                                                  object:self->m_audioSession];
+    [[NSNotificationCenter defaultCenter] removeObserver:self
+                                                    name:AVAudioSessionMediaServicesWereResetNotification
+                                                  object:self->m_audioSession];
+    [[NSNotificationCenter defaultCenter] removeObserver:self
+                                                    name:AVAudioSessionRouteChangeNotification
+                                                  object:self->m_audioSession];
+    [super dealloc];
+}
+
+-(BOOL)activateAudio
+{
+    NSError *error = nil;
+    BOOL success = [self->m_audioSession setActive:YES error:&error];
+    if (![self->m_audioSession setActive:YES error:&error]) {
+#ifdef QT_DEBUG_COREAUDIO
+        qDebug("audio session activation failed: %s", [[error localizedDescription] UTF8String]);
+    } else {
+        qDebug("audio session activated");
+#endif
+    }
+
+    return success;
+}
+
+-(BOOL)deactivateAudio
+{
+    NSError *error = nil;
+    BOOL success = [m_audioSession setActive:NO error:&error];
+#ifdef QT_DEBUG_COREAUDIO
+    if (!success) {
+        qDebug("%s", [[error localizedDescription] UTF8String]);
+    }
+#endif
+    return success;
+}
+
+-(void)audioSessionInterruption:(NSNotification *)notification
+{
+    NSNumber *type = [[notification userInfo] valueForKey:AVAudioSessionInterruptionTypeKey];
+    if ([type intValue] == AVAudioSessionInterruptionTypeBegan) {
+#ifdef QT_DEBUG_COREAUDIO
+        qDebug("audioSession Interuption begain");
+#endif
+    } else if ([type intValue] == AVAudioSessionInterruptionTypeEnded) {
+#ifdef QT_DEBUG_COREAUDIO
+        qDebug("audioSession Interuption ended");
+#endif
+        NSNumber *option = [[notification userInfo] valueForKey:AVAudioSessionInterruptionOptionKey];
+        if ([option intValue] == AVAudioSessionInterruptionOptionShouldResume) {
+#ifdef QT_DEBUG_COREAUDIO
+            qDebug("audioSession is active and immediately ready to be used.");
+#endif
+        } else {
+            [self activateAudio];
+        }
+    }
+}
+
+-(void)audioSessionMediaServicesWereReset:(NSNotification *)notification
+{
+    Q_UNUSED(notification)
+#ifdef QT_DEBUG_COREAUDIO
+    qDebug("audioSession Media Services were reset");
+#endif
+    //Reactivate audio when this occurs
+    [self activateAudio];
+}
+
+-(void)audioSessionRouteChange:(NSNotification *)notification
+{
+    NSNumber *reason = [[notification userInfo] valueForKey:AVAudioSessionRouteChangeReasonKey];
+    NSUInteger reasonEnum = [reason intValue];
+
+    if (reasonEnum == AVAudioSessionRouteChangeReasonUnknown) {
+#ifdef QT_DEBUG_COREAUDIO
+        qDebug("audioSession route changed. reason: unknown");
+#endif
+    } else if (reasonEnum == AVAudioSessionRouteChangeReasonNewDeviceAvailable) {
+#ifdef QT_DEBUG_COREAUDIO
+        qDebug("audioSession route changed. reason: new device available");
+#endif
+    } else if (reasonEnum == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
+#ifdef QT_DEBUG_COREAUDIO
+        qDebug("audioSession route changed. reason: old device unavailable");
+#endif
+    } else if (reasonEnum == AVAudioSessionRouteChangeReasonCategoryChange) {
+#ifdef QT_DEBUG_COREAUDIO
+        qDebug("audioSession route changed. reason: category changed");
+#endif
+    } else if (reasonEnum == AVAudioSessionRouteChangeReasonOverride) {
+#ifdef QT_DEBUG_COREAUDIO
+        qDebug("audioSession route changed. reason: override");
+#endif
+    } else if (reasonEnum == AVAudioSessionRouteChangeReasonWakeFromSleep) {
+#ifdef QT_DEBUG_COREAUDIO
+        qDebug("audioSession route changed. reason: woken from sleep");
+#endif
+    } else if (reasonEnum == AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory) {
+#ifdef QT_DEBUG_COREAUDIO
+        qDebug("audioSession route changed. reason: no suitable route for category");
+#endif
+    }
+
+}
+
+@end //implementation CoreAudioSessionObserver
+
+CoreAudioSessionManager::CoreAudioSessionManager() :
+    QObject(0)
+{
+    m_sessionObserver = [[CoreAudioSessionObserver alloc] initWithAudioSessionManager:this];
+    setActive(true);
+    setCategory(CoreAudioSessionManager::PlayAndRecord, CoreAudioSessionManager::MixWithOthers);
+}
+
+CoreAudioSessionManager::~CoreAudioSessionManager()
+{
+#ifdef QT_DEBUG_COREAUDIO
+    qDebug() << Q_FUNC_INFO;
+#endif
+    [m_sessionObserver release];
+}
+
+
+CoreAudioSessionManager &CoreAudioSessionManager::instance()
+{
+    static CoreAudioSessionManager instance;
+    return instance;
+}
+
+bool CoreAudioSessionManager::setActive(bool active)
+{
+    if (active) {
+        return [m_sessionObserver activateAudio];
+    } else {
+        return [m_sessionObserver deactivateAudio];
+    }
+}
+
+bool CoreAudioSessionManager::setCategory(CoreAudioSessionManager::AudioSessionCategorys category, CoreAudioSessionManager::AudioSessionCategoryOptions options)
+{
+    NSString *targetCategory = nil;
+
+    switch (category) {
+    case CoreAudioSessionManager::Ambient:
+        targetCategory = AVAudioSessionCategoryAmbient;
+        break;
+    case CoreAudioSessionManager::SoloAmbient:
+        targetCategory = AVAudioSessionCategorySoloAmbient;
+        break;
+    case CoreAudioSessionManager::Playback:
+        targetCategory = AVAudioSessionCategoryPlayback;
+        break;
+    case CoreAudioSessionManager::Record:
+        targetCategory = AVAudioSessionCategoryRecord;
+        break;
+    case CoreAudioSessionManager::PlayAndRecord:
+        targetCategory = AVAudioSessionCategoryPlayAndRecord;
+        break;
+    case CoreAudioSessionManager::AudioProcessing:
+        targetCategory = AVAudioSessionCategoryAudioProcessing;
+        break;
+    case CoreAudioSessionManager::MultiRoute:
+        targetCategory = AVAudioSessionCategoryMultiRoute;
+        break;
+    }
+
+    if (targetCategory == nil)
+        return false;
+
+    return [[m_sessionObserver audioSession] setCategory:targetCategory
+                                             withOptions:(AVAudioSessionCategoryOptions)options
+                                                   error:nil];
+}
+
+bool CoreAudioSessionManager::setMode(CoreAudioSessionManager::AudioSessionModes mode)
+{
+    NSString *targetMode = nil;
+    switch (mode) {
+    case CoreAudioSessionManager::Default:
+        targetMode = AVAudioSessionModeDefault;
+        break;
+    case CoreAudioSessionManager::VoiceChat:
+        targetMode = AVAudioSessionModeVoiceChat;
+        break;
+    case CoreAudioSessionManager::GameChat:
+        targetMode = AVAudioSessionModeGameChat;
+        break;
+    case CoreAudioSessionManager::VideoRecording:
+        targetMode = AVAudioSessionModeVideoRecording;
+        break;
+    case CoreAudioSessionManager::Measurement:
+        targetMode = AVAudioSessionModeMeasurement;
+        break;
+    case CoreAudioSessionManager::MoviePlayback:
+        targetMode = AVAudioSessionModeMoviePlayback;
+        break;
+    }
+
+    if (targetMode == nil)
+        return false;
+
+    return [[m_sessionObserver audioSession] setMode:targetMode error:nil];
+
+}
+
+CoreAudioSessionManager::AudioSessionCategorys CoreAudioSessionManager::category()
+{
+    NSString *category = [[m_sessionObserver audioSession] category];
+    AudioSessionCategorys localCategory = Ambient;
+
+    if (category == AVAudioSessionCategoryAmbient) {
+        localCategory = Ambient;
+    } else if (category == AVAudioSessionCategorySoloAmbient) {
+        localCategory = SoloAmbient;
+    } else if (category == AVAudioSessionCategoryPlayback) {
+        localCategory = Playback;
+    } else if (category == AVAudioSessionCategoryRecord) {
+        localCategory = Record;
+    } else if (category == AVAudioSessionCategoryPlayAndRecord) {
+        localCategory = PlayAndRecord;
+    } else if (category == AVAudioSessionCategoryAudioProcessing) {
+        localCategory = AudioProcessing;
+    } else if (category == AVAudioSessionCategoryMultiRoute) {
+        localCategory = MultiRoute;
+    }
+
+    return localCategory;
+}
+
+CoreAudioSessionManager::AudioSessionModes CoreAudioSessionManager::mode()
+{
+    NSString *mode = [[m_sessionObserver audioSession] mode];
+    AudioSessionModes localMode = Default;
+
+    if (mode == AVAudioSessionModeDefault) {
+        localMode = Default;
+    } else if (mode == AVAudioSessionModeVoiceChat) {
+        localMode = VoiceChat;
+    } else if (mode == AVAudioSessionModeGameChat) {
+        localMode = GameChat;
+    } else if (mode == AVAudioSessionModeVideoRecording) {
+        localMode = VideoRecording;
+    } else if (mode == AVAudioSessionModeMeasurement) {
+        localMode = Measurement;
+    } else if (mode == AVAudioSessionModeMoviePlayback) {
+        localMode = MoviePlayback;
+    }
+
+    return localMode;
+}
+
+QList<QByteArray> CoreAudioSessionManager::inputDevices()
+{
+    //TODO: Add support for USB input devices
+    //Right now the default behavior on iOS is to have only one input route
+    //at a time.
+    QList<QByteArray> inputDevices;
+    inputDevices << "default";
+    return inputDevices;
+}
+
+QList<QByteArray> CoreAudioSessionManager::outputDevices()
+{
+    //TODO: Add support for USB output devices
+    //Right now the default behavior on iOS is to have only one output route
+    //at a time.
+    QList<QByteArray> outputDevices;
+    outputDevices << "default";
+    return outputDevices;
+}
+
+int CoreAudioSessionManager::inputChannelCount()
+{
+    return [[m_sessionObserver audioSession] inputNumberOfChannels];
+}
+
+int CoreAudioSessionManager::outputChannelCount()
+{
+    return [[m_sessionObserver audioSession] outputNumberOfChannels];
+}
+
+float CoreAudioSessionManager::currentIOBufferDuration()
+{
+    return [[m_sessionObserver audioSession] IOBufferDuration];
+}
+
+float CoreAudioSessionManager::preferredSampleRate()
+{
+    return [[m_sessionObserver audioSession] preferredSampleRate];
+}
+
+#ifdef QT_DEBUG_COREAUDIO
+QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategorys category)
+{
+    QDebug output = dbg.nospace();
+    switch (category) {
+    case CoreAudioSessionManager::Ambient:
+        output << "AudioSessionCategoryAmbient";
+        break;
+    case CoreAudioSessionManager::SoloAmbient:
+        output << "AudioSessionCategorySoloAmbient";
+        break;
+    case CoreAudioSessionManager::Playback:
+        output << "AudioSessionCategoryPlayback";
+        break;
+    case CoreAudioSessionManager::Record:
+        output << "AudioSessionCategoryRecord";
+        break;
+    case CoreAudioSessionManager::PlayAndRecord:
+        output << "AudioSessionCategoryPlayAndRecord";
+        break;
+    case CoreAudioSessionManager::AudioProcessing:
+        output << "AudioSessionCategoryAudioProcessing";
+        break;
+    case CoreAudioSessionManager::MultiRoute:
+        output << "AudioSessionCategoryMultiRoute";
+        break;
+    }
+    return output;
+}
+
+QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategoryOptions option)
+{
+    QDebug output = dbg.nospace();
+    switch (option) {
+    case CoreAudioSessionManager::None:
+        output << "AudioSessionCategoryOptionNone";
+        break;
+    case CoreAudioSessionManager::MixWithOthers:
+        output << "AudioSessionCategoryOptionMixWithOthers";
+        break;
+    case CoreAudioSessionManager::DuckOthers:
+        output << "AudioSessionCategoryOptionDuckOthers";
+        break;
+    case CoreAudioSessionManager::AllowBluetooth:
+        output << "AudioSessionCategoryOptionAllowBluetooth";
+        break;
+    case CoreAudioSessionManager::DefaultToSpeaker:
+        output << "AudioSessionCategoryOptionDefaultToSpeaker";
+        break;
+    }
+    return output;
+}
+
+QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionModes mode)
+{
+    QDebug output = dbg.nospace();
+    switch (mode) {
+    case CoreAudioSessionManager::Default:
+        output << "AudioSessionModeDefault";
+        break;
+    case CoreAudioSessionManager::VoiceChat:
+        output << "AudioSessionModeVoiceChat";
+        break;
+    case CoreAudioSessionManager::GameChat:
+        output << "AudioSessionModeGameChat";
+        break;
+    case CoreAudioSessionManager::VideoRecording:
+        output << "AudioSessionModeVideoRecording";
+        break;
+    case CoreAudioSessionManager::Measurement:
+        output << "AudioSessionModeMeasurement";
+        break;
+    case CoreAudioSessionManager::MoviePlayback:
+        output << "AudioSessionModeMoviePlayback";
+        break;
+    }
+    return output;
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_coreaudiosessionmanager.cpp"
similarity index 54%
rename from src/multimedia/audio/qaudio_mac_p.h
rename to src/plugins/coreaudio/coreaudioutils.h
index 5a22bbf..8b7188c 100644 (file)
@@ -3,7 +3,7 @@
 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 ** Contact: http://www.qt-project.org/legal
 **
-** This file is part of the Qt Toolkit.
+** This file is part of the plugins of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
 ** Commercial License Usage
 **
 ****************************************************************************/
 
-//
-//  W A R N I N G
-//  -------------
-//
-// This file is not part of the Qt API.  It exists for the convenience
-// of other Qt classes.  This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
+#ifndef IOSAUDIOUTILS_H
+#define IOSAUDIOUTILS_H
 
+#include <CoreAudio/CoreAudioTypes.h>
 
-#ifndef QAUDIO_MAC_P_H
-#define QAUDIO_MAC_P_H
-
-#include <CoreAudio/CoreAudio.h>
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qatomic.h>
-
-#include <qaudioformat.h>
+#include <QtMultimedia/QAudioFormat>
+#include <QtCore/qglobal.h>
 
 QT_BEGIN_NAMESPACE
 
-extern QAudioFormat toQAudioFormat(const AudioStreamBasicDescription& streamFormat);
-extern AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat);
-
-class QAudioRingBuffer
+class CoreAudioUtils
 {
 public:
-    typedef QPair<char*, int> Region;
-
-    QAudioRingBuffer(int bufferSize);
-    ~QAudioRingBuffer();
-
-    Region acquireReadRegion(int size)
-    {
-        const int used = m_bufferUsed.fetchAndAddAcquire(0);
-
-        if (used > 0) {
-            const int readSize = qMin(size, qMin(m_bufferSize - m_readPos, used));
-
-            return readSize > 0 ? Region(m_buffer + m_readPos, readSize) : Region(0, 0);
-        }
-
-        return Region(0, 0);
-    }
-
-    void releaseReadRegion(Region const& region)
-    {
-        m_readPos = (m_readPos + region.second) % m_bufferSize;
+    static quint64 currentTime();
+    static double frequency();
+    static QAudioFormat toQAudioFormat(const AudioStreamBasicDescription& streamFormat);
+    static AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat);
 
-        m_bufferUsed.fetchAndAddRelease(-region.second);
-    }
-
-    Region acquireWriteRegion(int size)
-    {
-        const int free = m_bufferSize - m_bufferUsed.fetchAndAddAcquire(0);
-
-        if (free > 0) {
-            const int writeSize = qMin(size, qMin(m_bufferSize - m_writePos, free));
-
-            return writeSize > 0 ? Region(m_buffer + m_writePos, writeSize) : Region(0, 0);
-        }
+private:
+    static void initialize();
+    static double sFrequency;
+    static bool sIsInitialized;
+};
 
-        return Region(0, 0);
-    }
+class CoreAudioRingBuffer
+{
+public:
+    typedef QPair<char*, int> Region;
 
-    void releaseWriteRegion(Region const& region)
-    {
-        m_writePos = (m_writePos + region.second) % m_bufferSize;
+    CoreAudioRingBuffer(int bufferSize);
+    ~CoreAudioRingBuffer();
 
-        m_bufferUsed.fetchAndAddRelease(region.second);
-    }
+    Region acquireReadRegion(int size);
+    void releaseReadRegion(Region const& region);
+    Region acquireWriteRegion(int size);
+    void releaseWriteRegion(Region const& region);
 
     int used() const;
     int free() const;
@@ -130,6 +92,4 @@ private:
 
 QT_END_NAMESPACE
 
-#endif  // QAUDIO_MAC_P_H
-
-
+#endif // IOSAUDIOUTILS_H
similarity index 60%
rename from src/multimedia/audio/qaudio_mac.cpp
rename to src/plugins/coreaudio/coreaudioutils.mm
index 3084dfe..d43303a 100644 (file)
@@ -3,7 +3,7 @@
 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 ** Contact: http://www.qt-project.org/legal
 **
-** This file is part of the Qt Toolkit.
+** This file is part of the plugins of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
 ** Commercial License Usage
 **
 ****************************************************************************/
 
-
-#include "qaudio_mac_p.h"
+#include "coreaudioutils.h"
+#include <mach/mach_time.h>
 
 QT_BEGIN_NAMESPACE
 
-// Conversion
-QAudioFormat toQAudioFormat(AudioStreamBasicDescription const& sf)
+double CoreAudioUtils::sFrequency = 0.0;
+bool CoreAudioUtils::sIsInitialized = false;
+
+void CoreAudioUtils::initialize()
+{
+    struct mach_timebase_info timeBaseInfo;
+    mach_timebase_info(&timeBaseInfo);
+    sFrequency = static_cast<double>(timeBaseInfo.denom) / static_cast<double>(timeBaseInfo.numer);
+    sFrequency *= 1000000000.0;
+
+    sIsInitialized = true;
+}
+
+
+quint64 CoreAudioUtils::currentTime()
+{
+    return mach_absolute_time();
+}
+
+double CoreAudioUtils::frequency()
+{
+    if (!sIsInitialized)
+        initialize();
+    return sFrequency;
+}
+
+QAudioFormat CoreAudioUtils::toQAudioFormat(AudioStreamBasicDescription const& sf)
 {
     QAudioFormat    audioFormat;
 
@@ -64,7 +89,7 @@ QAudioFormat toQAudioFormat(AudioStreamBasicDescription const& sf)
     return audioFormat;
 }
 
-AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat)
+AudioStreamBasicDescription CoreAudioUtils::toAudioStreamBasicDescription(QAudioFormat const& audioFormat)
 {
     AudioStreamBasicDescription sf;
 
@@ -91,34 +116,81 @@ AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& au
 }
 
 // QAudioRingBuffer
-QAudioRingBuffer::QAudioRingBuffer(int bufferSize):
+CoreAudioRingBuffer::CoreAudioRingBuffer(int bufferSize):
     m_bufferSize(bufferSize)
 {
     m_buffer = new char[m_bufferSize];
     reset();
 }
 
-QAudioRingBuffer::~QAudioRingBuffer()
+CoreAudioRingBuffer::~CoreAudioRingBuffer()
 {
     delete m_buffer;
 }
 
-int QAudioRingBuffer::used() const
+CoreAudioRingBuffer::Region CoreAudioRingBuffer::acquireReadRegion(int size)
+{
+    const int used = m_bufferUsed.fetchAndAddAcquire(0);
+
+    if (used > 0) {
+        const int readSize = qMin(size, qMin(m_bufferSize - m_readPos, used));
+
+        return readSize > 0 ? Region(m_buffer + m_readPos, readSize) : Region(0, 0);
+    }
+
+    return Region(0, 0);
+}
+
+void CoreAudioRingBuffer::releaseReadRegion(const CoreAudioRingBuffer::Region &region)
+{
+    m_readPos = (m_readPos + region.second) % m_bufferSize;
+
+    m_bufferUsed.fetchAndAddRelease(-region.second);
+}
+
+CoreAudioRingBuffer::Region CoreAudioRingBuffer::acquireWriteRegion(int size)
+{
+    const int free = m_bufferSize - m_bufferUsed.fetchAndAddAcquire(0);
+
+    Region output;
+
+    if (free > 0) {
+        const int writeSize = qMin(size, qMin(m_bufferSize - m_writePos, free));
+        output =  writeSize > 0 ? Region(m_buffer + m_writePos, writeSize) : Region(0, 0);
+    } else {
+        output = Region(0, 0);
+    }
+#ifdef QT_DEBUG_COREAUDIO
+    qDebug("acquireWriteRegion(%d) free: %d returning Region(%p, %d)", size, free, output.first, output.second);
+#endif
+    return output;
+}
+void CoreAudioRingBuffer::releaseWriteRegion(const CoreAudioRingBuffer::Region &region)
+{
+    m_writePos = (m_writePos + region.second) % m_bufferSize;
+
+    m_bufferUsed.fetchAndAddRelease(region.second);
+#ifdef QT_DEBUG_COREAUDIO
+    qDebug("releaseWriteRegion(%p,%d): m_writePos:%d", region.first, region.second, m_writePos);
+#endif
+}
+
+int CoreAudioRingBuffer::used() const
 {
     return m_bufferUsed.load();
 }
 
-int QAudioRingBuffer::free() const
+int CoreAudioRingBuffer::free() const
 {
     return m_bufferSize - m_bufferUsed.load();
 }
 
-int QAudioRingBuffer::size() const
+int CoreAudioRingBuffer::size() const
 {
     return m_bufferSize;
 }
 
-void QAudioRingBuffer::reset()
+void CoreAudioRingBuffer::reset()
 {
     m_readPos = 0;
     m_writePos = 0;
@@ -126,5 +198,3 @@ void QAudioRingBuffer::reset()
 }
 
 QT_END_NAMESPACE
-
-
index 742a4f7..5da1bb7 100644 (file)
@@ -45,7 +45,7 @@ unix:!mac:!android {
 }
 
 mac:!simulator {
-    SUBDIRS += audiocapture
+    SUBDIRS += audiocapture coreaudio
 
     config_avfoundation: SUBDIRS += avfoundation
 
index 3d2be82..4777274 100755 (executable)
@@ -829,8 +829,9 @@ void tst_QAudioOutput::pushSuspendResume()
 
     audioOutput.resume();
 
-    // Give backends running in separate threads a chance to resume.
-    QTest::qWait(100);
+    // Give backends running in separate threads a chance to resume
+    // but not too much or the rest of the file may be processed
+    QTest::qWait(20);
 
     // Check that QAudioOutput immediately transitions to ActiveState
     QVERIFY2((stateSignal.count() == 1),