summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Nichols <andy.nichols@digia.com>2013-03-08 15:18:36 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-19 14:36:28 +0200
commitb357c55f2dfe44e5c2a2524b93478aecf668ca0a (patch)
tree6dec038966d3a45e708a3c4f8d8bb4df1e55bd46
parent044e48d5a4e0281efb1f6d5136c9a732e3119559 (diff)
CoreAudio: Create an audio plugin supporting iOS and OS X
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>
-rw-r--r--src/multimedia/audio/audio.pri14
-rw-r--r--src/multimedia/audio/qaudiodevicefactory.cpp16
-rw-r--r--src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp351
-rw-r--r--src/multimedia/audio/qaudioinput_mac_p.cpp1067
-rw-r--r--src/multimedia/audio/qaudioinput_mac_p.h173
-rw-r--r--src/multimedia/audio/qaudiooutput_mac_p.cpp759
-rw-r--r--src/plugins/coreaudio/coreaudio.json3
-rw-r--r--src/plugins/coreaudio/coreaudio.pro39
-rw-r--r--src/plugins/coreaudio/coreaudiodeviceinfo.h (renamed from src/multimedia/audio/qaudiodeviceinfo_mac_p.h)49
-rw-r--r--src/plugins/coreaudio/coreaudiodeviceinfo.mm386
-rw-r--r--src/plugins/coreaudio/coreaudioinput.h267
-rw-r--r--src/plugins/coreaudio/coreaudioinput.mm1018
-rw-r--r--src/plugins/coreaudio/coreaudiooutput.h (renamed from src/multimedia/audio/qaudiooutput_mac_p.h)206
-rw-r--r--src/plugins/coreaudio/coreaudiooutput.mm732
-rw-r--r--src/plugins/coreaudio/coreaudioplugin.h65
-rw-r--r--src/plugins/coreaudio/coreaudioplugin.mm80
-rw-r--r--src/plugins/coreaudio/coreaudiosessionmanager.h130
-rw-r--r--src/plugins/coreaudio/coreaudiosessionmanager.mm481
-rw-r--r--src/plugins/coreaudio/coreaudioutils.h (renamed from src/multimedia/audio/qaudio_mac_p.h)94
-rw-r--r--src/plugins/coreaudio/coreaudioutils.mm (renamed from src/multimedia/audio/qaudio_mac.cpp)98
-rw-r--r--src/plugins/plugins.pro2
-rwxr-xr-xtests/auto/integration/qaudiooutput/tst_qaudiooutput.cpp5
22 files changed, 3462 insertions, 2573 deletions
diff --git a/src/multimedia/audio/audio.pri b/src/multimedia/audio/audio.pri
index f0706e3aa..f76d13264 100644
--- a/src/multimedia/audio/audio.pri
+++ b/src/multimedia/audio/audio.pri
@@ -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 \
diff --git a/src/multimedia/audio/qaudiodevicefactory.cpp b/src/multimedia/audio/qaudiodevicefactory.cpp
index 76f122790..e2d4ec3e8 100644
--- a/src/multimedia/audio/qaudiodevicefactory.cpp
+++ b/src/multimedia/audio/qaudiodevicefactory.cpp
@@ -52,10 +52,6 @@
#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
index 0afa65593..000000000
--- a/src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp
+++ /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
index ea5be5d30..000000000
--- a/src/multimedia/audio/qaudioinput_mac_p.cpp
+++ /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
index 7faaa6980..000000000
--- a/src/multimedia/audio/qaudioinput_mac_p.h
+++ /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
index 8ac4007cd..000000000
--- a/src/multimedia/audio/qaudiooutput_mac_p.cpp
+++ /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
index 000000000..a31d52107
--- /dev/null
+++ b/src/plugins/coreaudio/coreaudio.json
@@ -0,0 +1,3 @@
+{
+ "Keys": ["default"]
+}
diff --git a/src/plugins/coreaudio/coreaudio.pro b/src/plugins/coreaudio/coreaudio.pro
new file mode 100644
index 000000000..146851493
--- /dev/null
+++ b/src/plugins/coreaudio/coreaudio.pro
@@ -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
diff --git a/src/multimedia/audio/qaudiodeviceinfo_mac_p.h b/src/plugins/coreaudio/coreaudiodeviceinfo.h
index 2afb21285..1a8bcec8f 100644
--- a/src/multimedia/audio/qaudiodeviceinfo_mac_p.h
+++ b/src/plugins/coreaudio/coreaudiodeviceinfo.h
@@ -38,43 +38,28 @@
** $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
index 000000000..77a9b835d
--- /dev/null
+++ b/src/plugins/coreaudio/coreaudiodeviceinfo.mm
@@ -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
index 000000000..a54db7773
--- /dev/null
+++ b/src/plugins/coreaudio/coreaudioinput.h
@@ -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
index 000000000..c41e1a51e
--- /dev/null
+++ b/src/plugins/coreaudio/coreaudioinput.mm
@@ -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"
diff --git a/src/multimedia/audio/qaudiooutput_mac_p.h b/src/plugins/coreaudio/coreaudiooutput.h
index 89e54fd16..d4636e052 100644
--- a/src/multimedia/audio/qaudiooutput_mac_p.h
+++ b/src/plugins/coreaudio/coreaudiooutput.h
@@ -38,110 +38,133 @@
** $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
index 000000000..bb0da57f3
--- /dev/null
+++ b/src/plugins/coreaudio/coreaudiooutput.mm
@@ -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
index 000000000..88b10c6e5
--- /dev/null
+++ b/src/plugins/coreaudio/coreaudioplugin.h
@@ -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
index 000000000..587815e6f
--- /dev/null
+++ b/src/plugins/coreaudio/coreaudioplugin.mm
@@ -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
index 000000000..61d8967b1
--- /dev/null
+++ b/src/plugins/coreaudio/coreaudiosessionmanager.h
@@ -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
index 000000000..4b3bdb7dc
--- /dev/null
+++ b/src/plugins/coreaudio/coreaudiosessionmanager.mm
@@ -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"
diff --git a/src/multimedia/audio/qaudio_mac_p.h b/src/plugins/coreaudio/coreaudioutils.h
index 5a22bbf44..8b7188c5d 100644
--- a/src/multimedia/audio/qaudio_mac_p.h
+++ b/src/plugins/coreaudio/coreaudioutils.h
@@ -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
@@ -39,80 +39,42 @@
**
****************************************************************************/
-//
-// 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
diff --git a/src/multimedia/audio/qaudio_mac.cpp b/src/plugins/coreaudio/coreaudioutils.mm
index 3084dfe03..d43303a1d 100644
--- a/src/multimedia/audio/qaudio_mac.cpp
+++ b/src/plugins/coreaudio/coreaudioutils.mm
@@ -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
@@ -39,13 +39,38 @@
**
****************************************************************************/
-
-#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
-
-
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index 742a4f7f0..5da1bb739 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -45,7 +45,7 @@ unix:!mac:!android {
}
mac:!simulator {
- SUBDIRS += audiocapture
+ SUBDIRS += audiocapture coreaudio
config_avfoundation: SUBDIRS += avfoundation
diff --git a/tests/auto/integration/qaudiooutput/tst_qaudiooutput.cpp b/tests/auto/integration/qaudiooutput/tst_qaudiooutput.cpp
index 3d2be8260..477727497 100755
--- a/tests/auto/integration/qaudiooutput/tst_qaudiooutput.cpp
+++ b/tests/auto/integration/qaudiooutput/tst_qaudiooutput.cpp
@@ -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),