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>
This commit is contained in:
committed by
The Qt Project
parent
044e48d5a4
commit
b357c55f2d
481
src/plugins/coreaudio/coreaudiosessionmanager.mm
Normal file
481
src/plugins/coreaudio/coreaudiosessionmanager.mm
Normal file
@@ -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"
|
||||
Reference in New Issue
Block a user