/**************************************************************************** ** ** 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 #import 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); // Set default category to Ambient (implies MixWithOthers). This makes sure audio stops playing // if the screen is locked or if the Silent switch is toggled. setCategory(CoreAudioSessionManager::Ambient, CoreAudioSessionManager::None); } 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 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 inputDevices; inputDevices << "default"; return inputDevices; } QList 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 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"