blob: 6c86f07532e140d89377baf089c7b7a1e842eb32 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
** Contact: https://www.qt.io/licensing/
**
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.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];
}
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:
#ifndef Q_OS_TVOS
targetCategory = AVAudioSessionCategoryAudioProcessing;
#endif
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;
#ifndef Q_OS_TVOS
} else if (category == AVAudioSessionCategoryAudioProcessing) {
localCategory = AudioProcessing;
#endif
} 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;
}
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"