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>
482 lines
16 KiB
Plaintext
482 lines
16 KiB
Plaintext
/****************************************************************************
|
|
**
|
|
** 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"
|