1/**************************************************************************** 2** 3** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). 4** Contact: https://www.qt.io/licensing/ 5** 6** This file is part of the Qt Toolkit. 7** 8** $QT_BEGIN_LICENSE:LGPL$ 9** Commercial License Usage 10** Licensees holding valid commercial Qt licenses may use this file in 11** accordance with the commercial license agreement provided with the 12** Software or, alternatively, in accordance with the terms contained in 13** a written agreement between you and The Qt Company. For licensing terms 14** and conditions see https://www.qt.io/terms-conditions. For further 15** information use the contact form at https://www.qt.io/contact-us. 16** 17** GNU Lesser General Public License Usage 18** Alternatively, this file may be used under the terms of the GNU Lesser 19** General Public License version 3 as published by the Free Software 20** Foundation and appearing in the file LICENSE.LGPL3 included in the 21** packaging of this file. Please review the following information to 22** ensure the GNU Lesser General Public License version 3 requirements 23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24** 25** GNU General Public License Usage 26** Alternatively, this file may be used under the terms of the GNU 27** General Public License version 2.0 or (at your option) the GNU General 28** Public license version 3 or any later version approved by the KDE Free 29** Qt Foundation. The licenses are as published by the Free Software 30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31** included in the packaging of this file. Please review the following 32** information to ensure the GNU General Public License requirements will 33** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34** https://www.gnu.org/licenses/gpl-3.0.html. 35** 36** $QT_END_LICENSE$ 37** 38****************************************************************************/ 39 40#include "coreaudiosessionmanager.h" 41#import <AVFoundation/AVAudioSession.h> 42#import <Foundation/Foundation.h> 43 44QT_BEGIN_NAMESPACE 45 46@interface CoreAudioSessionObserver : NSObject 47{ 48 CoreAudioSessionManager *m_sessionManager; 49 AVAudioSession *m_audioSession; 50} 51 52@property (readonly, getter=sessionManager) CoreAudioSessionManager *m_sessionManager; 53@property (readonly, getter=audioSession) AVAudioSession *m_audioSession; 54 55-(CoreAudioSessionObserver *)initWithAudioSessionManager:(CoreAudioSessionManager *)sessionManager; 56 57-(BOOL)activateAudio; 58-(BOOL)deactivateAudio; 59 60//Notification handlers 61-(void)audioSessionInterruption:(NSNotification *)notification; 62-(void)audioSessionRouteChange:(NSNotification *)notification; 63-(void)audioSessionMediaServicesWereReset:(NSNotification *)notification; 64 65@end //interface CoreAudioSessionObserver 66 67@implementation CoreAudioSessionObserver 68 69@synthesize m_sessionManager, m_audioSession; 70 71-(CoreAudioSessionObserver *)initWithAudioSessionManager:(CoreAudioSessionManager *)sessionManager 72{ 73 if (!(self = [super init])) 74 return nil; 75 76 self->m_sessionManager = sessionManager; 77 self->m_audioSession = [AVAudioSession sharedInstance]; 78 79 //Set up observers 80 [[NSNotificationCenter defaultCenter] addObserver:self 81 selector:@selector(audioSessionInterruption:) 82 name:AVAudioSessionInterruptionNotification 83 object:self->m_audioSession]; 84 [[NSNotificationCenter defaultCenter] addObserver:self 85 selector:@selector(audioSessionMediaServicesWereReset:) 86 name:AVAudioSessionMediaServicesWereResetNotification 87 object:self->m_audioSession]; 88 [[NSNotificationCenter defaultCenter] addObserver:self 89 selector:@selector(audioSessionRouteChange:) 90 name:AVAudioSessionRouteChangeNotification 91 object:self->m_audioSession]; 92 93 return self; 94} 95 96-(void)dealloc 97{ 98#ifdef QT_DEBUG_COREAUDIO 99 qDebug() << Q_FUNC_INFO; 100#endif 101 102 [[NSNotificationCenter defaultCenter] removeObserver:self 103 name:AVAudioSessionInterruptionNotification 104 object:self->m_audioSession]; 105 [[NSNotificationCenter defaultCenter] removeObserver:self 106 name:AVAudioSessionMediaServicesWereResetNotification 107 object:self->m_audioSession]; 108 [[NSNotificationCenter defaultCenter] removeObserver:self 109 name:AVAudioSessionRouteChangeNotification 110 object:self->m_audioSession]; 111 112 [super dealloc]; 113} 114 115-(BOOL)activateAudio 116{ 117 NSError *error = nil; 118 BOOL success = [self->m_audioSession setActive:YES error:&error]; 119 if (![self->m_audioSession setActive:YES error:&error]) { 120#ifdef QT_DEBUG_COREAUDIO 121 qDebug("audio session activation failed: %s", [[error localizedDescription] UTF8String]); 122 } else { 123 qDebug("audio session activated"); 124#endif 125 } 126 127 return success; 128} 129 130-(BOOL)deactivateAudio 131{ 132 NSError *error = nil; 133 BOOL success = [m_audioSession setActive:NO error:&error]; 134#ifdef QT_DEBUG_COREAUDIO 135 if (!success) { 136 qDebug("%s", [[error localizedDescription] UTF8String]); 137 } 138#endif 139 return success; 140} 141 142-(void)audioSessionInterruption:(NSNotification *)notification 143{ 144 NSNumber *type = [[notification userInfo] valueForKey:AVAudioSessionInterruptionTypeKey]; 145 if ([type intValue] == AVAudioSessionInterruptionTypeBegan) { 146#ifdef QT_DEBUG_COREAUDIO 147 qDebug("audioSession Interuption begain"); 148#endif 149 } else if ([type intValue] == AVAudioSessionInterruptionTypeEnded) { 150#ifdef QT_DEBUG_COREAUDIO 151 qDebug("audioSession Interuption ended"); 152#endif 153 NSNumber *option = [[notification userInfo] valueForKey:AVAudioSessionInterruptionOptionKey]; 154 if ([option intValue] == AVAudioSessionInterruptionOptionShouldResume) { 155#ifdef QT_DEBUG_COREAUDIO 156 qDebug("audioSession is active and immediately ready to be used."); 157#endif 158 } else { 159 [self activateAudio]; 160 } 161 } 162} 163 164-(void)audioSessionMediaServicesWereReset:(NSNotification *)notification 165{ 166 Q_UNUSED(notification) 167#ifdef QT_DEBUG_COREAUDIO 168 qDebug("audioSession Media Services were reset"); 169#endif 170 //Reactivate audio when this occurs 171 [self activateAudio]; 172} 173 174-(void)audioSessionRouteChange:(NSNotification *)notification 175{ 176 NSNumber *reason = [[notification userInfo] valueForKey:AVAudioSessionRouteChangeReasonKey]; 177 NSUInteger reasonEnum = [reason intValue]; 178 179 if (reasonEnum == AVAudioSessionRouteChangeReasonUnknown) { 180#ifdef QT_DEBUG_COREAUDIO 181 qDebug("audioSession route changed. reason: unknown"); 182#endif 183 } else if (reasonEnum == AVAudioSessionRouteChangeReasonNewDeviceAvailable) { 184#ifdef QT_DEBUG_COREAUDIO 185 qDebug("audioSession route changed. reason: new device available"); 186#endif 187 } else if (reasonEnum == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) { 188#ifdef QT_DEBUG_COREAUDIO 189 qDebug("audioSession route changed. reason: old device unavailable"); 190#endif 191 } else if (reasonEnum == AVAudioSessionRouteChangeReasonCategoryChange) { 192#ifdef QT_DEBUG_COREAUDIO 193 qDebug("audioSession route changed. reason: category changed"); 194#endif 195 } else if (reasonEnum == AVAudioSessionRouteChangeReasonOverride) { 196#ifdef QT_DEBUG_COREAUDIO 197 qDebug("audioSession route changed. reason: override"); 198#endif 199 } else if (reasonEnum == AVAudioSessionRouteChangeReasonWakeFromSleep) { 200#ifdef QT_DEBUG_COREAUDIO 201 qDebug("audioSession route changed. reason: woken from sleep"); 202#endif 203 } else if (reasonEnum == AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory) { 204#ifdef QT_DEBUG_COREAUDIO 205 qDebug("audioSession route changed. reason: no suitable route for category"); 206#endif 207 } 208 209} 210 211@end //implementation CoreAudioSessionObserver 212 213CoreAudioSessionManager::CoreAudioSessionManager() : 214 QObject(0) 215{ 216 m_sessionObserver = [[CoreAudioSessionObserver alloc] initWithAudioSessionManager:this]; 217} 218 219CoreAudioSessionManager::~CoreAudioSessionManager() 220{ 221#ifdef QT_DEBUG_COREAUDIO 222 qDebug() << Q_FUNC_INFO; 223#endif 224 [m_sessionObserver release]; 225} 226 227 228CoreAudioSessionManager &CoreAudioSessionManager::instance() 229{ 230 static CoreAudioSessionManager instance; 231 return instance; 232} 233 234bool CoreAudioSessionManager::setActive(bool active) 235{ 236 if (active) { 237 return [m_sessionObserver activateAudio]; 238 } else { 239 return [m_sessionObserver deactivateAudio]; 240 } 241} 242 243bool CoreAudioSessionManager::setCategory(CoreAudioSessionManager::AudioSessionCategorys category, CoreAudioSessionManager::AudioSessionCategoryOptions options) 244{ 245 NSString *targetCategory = nil; 246 247 switch (category) { 248 case CoreAudioSessionManager::Ambient: 249 targetCategory = AVAudioSessionCategoryAmbient; 250 break; 251 case CoreAudioSessionManager::SoloAmbient: 252 targetCategory = AVAudioSessionCategorySoloAmbient; 253 break; 254 case CoreAudioSessionManager::Playback: 255 targetCategory = AVAudioSessionCategoryPlayback; 256 break; 257 case CoreAudioSessionManager::Record: 258 targetCategory = AVAudioSessionCategoryRecord; 259 break; 260 case CoreAudioSessionManager::PlayAndRecord: 261 targetCategory = AVAudioSessionCategoryPlayAndRecord; 262 break; 263 case CoreAudioSessionManager::AudioProcessing: 264#ifndef Q_OS_TVOS 265 targetCategory = AVAudioSessionCategoryAudioProcessing; 266#endif 267 break; 268 case CoreAudioSessionManager::MultiRoute: 269 targetCategory = AVAudioSessionCategoryMultiRoute; 270 break; 271 } 272 273 if (targetCategory == nil) 274 return false; 275 276 return [[m_sessionObserver audioSession] setCategory:targetCategory 277 withOptions:(AVAudioSessionCategoryOptions)options 278 error:nil]; 279} 280 281bool CoreAudioSessionManager::setMode(CoreAudioSessionManager::AudioSessionModes mode) 282{ 283 NSString *targetMode = nil; 284 switch (mode) { 285 case CoreAudioSessionManager::Default: 286 targetMode = AVAudioSessionModeDefault; 287 break; 288 case CoreAudioSessionManager::VoiceChat: 289 targetMode = AVAudioSessionModeVoiceChat; 290 break; 291 case CoreAudioSessionManager::GameChat: 292 targetMode = AVAudioSessionModeGameChat; 293 break; 294 case CoreAudioSessionManager::VideoRecording: 295 targetMode = AVAudioSessionModeVideoRecording; 296 break; 297 case CoreAudioSessionManager::Measurement: 298 targetMode = AVAudioSessionModeMeasurement; 299 break; 300 case CoreAudioSessionManager::MoviePlayback: 301 targetMode = AVAudioSessionModeMoviePlayback; 302 break; 303 } 304 305 if (targetMode == nil) 306 return false; 307 308 return [[m_sessionObserver audioSession] setMode:targetMode error:nil]; 309 310} 311 312CoreAudioSessionManager::AudioSessionCategorys CoreAudioSessionManager::category() 313{ 314 NSString *category = [[m_sessionObserver audioSession] category]; 315 AudioSessionCategorys localCategory = Ambient; 316 317 if (category == AVAudioSessionCategoryAmbient) { 318 localCategory = Ambient; 319 } else if (category == AVAudioSessionCategorySoloAmbient) { 320 localCategory = SoloAmbient; 321 } else if (category == AVAudioSessionCategoryPlayback) { 322 localCategory = Playback; 323 } else if (category == AVAudioSessionCategoryRecord) { 324 localCategory = Record; 325 } else if (category == AVAudioSessionCategoryPlayAndRecord) { 326 localCategory = PlayAndRecord; 327#ifndef Q_OS_TVOS 328 } else if (category == AVAudioSessionCategoryAudioProcessing) { 329 localCategory = AudioProcessing; 330#endif 331 } else if (category == AVAudioSessionCategoryMultiRoute) { 332 localCategory = MultiRoute; 333 } 334 335 return localCategory; 336} 337 338CoreAudioSessionManager::AudioSessionModes CoreAudioSessionManager::mode() 339{ 340 NSString *mode = [[m_sessionObserver audioSession] mode]; 341 AudioSessionModes localMode = Default; 342 343 if (mode == AVAudioSessionModeDefault) { 344 localMode = Default; 345 } else if (mode == AVAudioSessionModeVoiceChat) { 346 localMode = VoiceChat; 347 } else if (mode == AVAudioSessionModeGameChat) { 348 localMode = GameChat; 349 } else if (mode == AVAudioSessionModeVideoRecording) { 350 localMode = VideoRecording; 351 } else if (mode == AVAudioSessionModeMeasurement) { 352 localMode = Measurement; 353 } else if (mode == AVAudioSessionModeMoviePlayback) { 354 localMode = MoviePlayback; 355 } 356 357 return localMode; 358} 359 360QList<QByteArray> CoreAudioSessionManager::inputDevices() 361{ 362 //TODO: Add support for USB input devices 363 //Right now the default behavior on iOS is to have only one input route 364 //at a time. 365 QList<QByteArray> inputDevices; 366 inputDevices << "default"; 367 return inputDevices; 368} 369 370QList<QByteArray> CoreAudioSessionManager::outputDevices() 371{ 372 //TODO: Add support for USB output devices 373 //Right now the default behavior on iOS is to have only one output route 374 //at a time. 375 QList<QByteArray> outputDevices; 376 outputDevices << "default"; 377 return outputDevices; 378} 379 380float CoreAudioSessionManager::currentIOBufferDuration() 381{ 382 return [[m_sessionObserver audioSession] IOBufferDuration]; 383} 384 385float CoreAudioSessionManager::preferredSampleRate() 386{ 387 return [[m_sessionObserver audioSession] preferredSampleRate]; 388} 389 390#ifdef QT_DEBUG_COREAUDIO 391QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategorys category) 392{ 393 QDebug output = dbg.nospace(); 394 switch (category) { 395 case CoreAudioSessionManager::Ambient: 396 output << "AudioSessionCategoryAmbient"; 397 break; 398 case CoreAudioSessionManager::SoloAmbient: 399 output << "AudioSessionCategorySoloAmbient"; 400 break; 401 case CoreAudioSessionManager::Playback: 402 output << "AudioSessionCategoryPlayback"; 403 break; 404 case CoreAudioSessionManager::Record: 405 output << "AudioSessionCategoryRecord"; 406 break; 407 case CoreAudioSessionManager::PlayAndRecord: 408 output << "AudioSessionCategoryPlayAndRecord"; 409 break; 410 case CoreAudioSessionManager::AudioProcessing: 411 output << "AudioSessionCategoryAudioProcessing"; 412 break; 413 case CoreAudioSessionManager::MultiRoute: 414 output << "AudioSessionCategoryMultiRoute"; 415 break; 416 } 417 return output; 418} 419 420QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategoryOptions option) 421{ 422 QDebug output = dbg.nospace(); 423 switch (option) { 424 case CoreAudioSessionManager::None: 425 output << "AudioSessionCategoryOptionNone"; 426 break; 427 case CoreAudioSessionManager::MixWithOthers: 428 output << "AudioSessionCategoryOptionMixWithOthers"; 429 break; 430 case CoreAudioSessionManager::DuckOthers: 431 output << "AudioSessionCategoryOptionDuckOthers"; 432 break; 433 case CoreAudioSessionManager::AllowBluetooth: 434 output << "AudioSessionCategoryOptionAllowBluetooth"; 435 break; 436 case CoreAudioSessionManager::DefaultToSpeaker: 437 output << "AudioSessionCategoryOptionDefaultToSpeaker"; 438 break; 439 } 440 return output; 441} 442 443QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionModes mode) 444{ 445 QDebug output = dbg.nospace(); 446 switch (mode) { 447 case CoreAudioSessionManager::Default: 448 output << "AudioSessionModeDefault"; 449 break; 450 case CoreAudioSessionManager::VoiceChat: 451 output << "AudioSessionModeVoiceChat"; 452 break; 453 case CoreAudioSessionManager::GameChat: 454 output << "AudioSessionModeGameChat"; 455 break; 456 case CoreAudioSessionManager::VideoRecording: 457 output << "AudioSessionModeVideoRecording"; 458 break; 459 case CoreAudioSessionManager::Measurement: 460 output << "AudioSessionModeMeasurement"; 461 break; 462 case CoreAudioSessionManager::MoviePlayback: 463 output << "AudioSessionModeMoviePlayback"; 464 break; 465 } 466 return output; 467} 468#endif 469 470QT_END_NAMESPACE 471 472#include "moc_coreaudiosessionmanager.cpp" 473