// // IBTAudioController.m // XFFruit // // Created by Xummer on 4/17/15. // Copyright (c) 2015 Xummer. All rights reserved. // #import "IBTAudioController.h" @import AVFoundation; @import AudioToolbox; @interface IBTAudioController () @end @implementation IBTAudioController static IBTAudioController *sharedInstance = nil; + (IBTAudioController *)sharedController { @synchronized(self) { if (nil == sharedInstance) { sharedInstance = [[self alloc] init]; } } return sharedInstance; } + (void)unload { if (sharedInstance) { sharedInstance = nil; } } - (id)init{ self = [super init]; if (self) { NSNotificationCenter *notiCenter = [NSNotificationCenter defaultCenter]; [notiCenter addObserver: self selector: @selector(proximityStateDidChange:) name: UIDeviceProximityStateDidChangeNotification object: nil]; } return self; } - (void)dealloc { // AVAudioSession *mySession = [AVAudioSession sharedInstance]; // [mySession setDelegate:nil]; NSNotificationCenter *notiCenter = [NSNotificationCenter defaultCenter]; [notiCenter removeObserver: self name: AVAudioSessionInterruptionNotification object: nil]; [notiCenter removeObserver: self name: AVAudioSessionRouteChangeNotification object: nil]; [self deactivate]; [notiCenter removeObserver: self name: UIDeviceProximityStateDidChangeNotification object: nil]; } //the callback define for audio route change void propChangeListenerCallback ( void *inUserData, AudioSessionPropertyID inPropertyID, UInt32 inPropertyValueSize, const void *inPropertyValue ); - (void)setup { AVAudioSession *mySession = [AVAudioSession sharedInstance]; // Assign the Playback category to the audio session. // OSStatus err; NSNotificationCenter *notiCenter = [NSNotificationCenter defaultCenter]; [notiCenter addObserver: self selector: @selector(handleInterruption:) name: AVAudioSessionInterruptionNotification object: mySession]; [notiCenter addObserver: self selector: @selector(handleRouteChange:) name: AVAudioSessionRouteChangeNotification object: nil]; // // listen for audio route changes // err = AudioSessionAddPropertyListener( // kAudioSessionProperty_AudioRouteChange, // propChangeListenerCallback, // self); // if(kAudioSessionNoError != err){ // NSLog(@"unable to register property listener for AudioRouteChange."); // return NO; // } // // // listen for audio input availability changes // err = AudioSessionAddPropertyListener( // kAudioSessionProperty_AudioInputAvailable, // propChangeListenerCallback, // self); // if(kAudioSessionNoError != err){ // NSLog(@"unable to register property listener for AudioInputAvailable."); // return NO; // } // if(![self activate]){ // NSLog(@"unable to activate AudioSession."); // return NO; // } } - (BOOL)setAVAudioSessionCategory:(NSString*)category { AVAudioSession *mySession = [AVAudioSession sharedInstance]; // Assign the Playback category to the audio session. NSError *audioSessionError = nil; [mySession setCategory: category error: &audioSessionError]; if (audioSessionError != nil) { NSLog (@"Error setting audio session category."); return NO; } else { return YES; } } - (BOOL)switchToPlaybackMode { return [self setAVAudioSessionCategory:AVAudioSessionCategoryPlayback]; } - (BOOL)switchToPlayAndRecordMode { return [self setAVAudioSessionCategory:AVAudioSessionCategoryPlayAndRecord]; } - (void)reset { } - (BOOL)activate { UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback; AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory), &sessionCategory); UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker; AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof (audioRouteOverride), &audioRouteOverride); AVAudioSession *audioSession = [AVAudioSession sharedInstance]; //默认情况下扬声器播放 [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil]; NSError *audioSessionError = nil; [audioSession setActive:YES error:&audioSessionError]; if (audioSessionError != nil) { NSLog (@"Error activating audio session during initial setup."); return NO; } else { return YES; } } - (BOOL)deactivate { AVAudioSession *mySession = [AVAudioSession sharedInstance]; NSError *audioSessionError = nil; // Deactivate the audio session [mySession setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&audioSessionError]; if (audioSessionError != nil) { NSLog (@"Error activating audio session during initial setup."); return NO; } else { return YES; } } #pragma mark - #pragma mark - - (void)handleInterruption:(NSNotification*)notification{ NSInteger reason = 0; NSString* reasonStr= @""; if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) { //Posted when an audio interruption occurs. reason = [[[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue]; if (reason == AVAudioSessionInterruptionTypeBegan) { // Audio has stopped, already inactive // Change state of UI, etc., to reflect non-playing state // if(soundSessionIO_.isProcessingSound)[soundSessionIO_ stopSoundProcessing:nil]; } if (reason == AVAudioSessionInterruptionTypeEnded) { // Make session active // Update user interface // AVAudioSessionInterruptionOptionShouldResume option reasonStr = @"AVAudioSessionInterruptionTypeEnded"; NSNumber* seccondReason = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey] ; switch ([seccondReason integerValue]) { case AVAudioSessionInterruptionOptionShouldResume: // Indicates that the audio session is active and immediately ready to be used. Your app can resume the audio operation that was interrupted. break; default: break; } } if ([notification.name isEqualToString:@"AVAudioSessionDidBeginInterruptionNotification"]) { // if (soundSessionIO_.isProcessingSound) { // // } // Posted after an interruption in your audio session occurs. // This notification is posted on the main thread of your app. There is no userInfo dictionary. } if ([notification.name isEqualToString:@"AVAudioSessionDidEndInterruptionNotification"]) { // Posted after an interruption in your audio session ends. // This notification is posted on the main thread of your app. There is no userInfo dictionary. } if ([notification.name isEqualToString:@"AVAudioSessionInputDidBecomeAvailableNotification"]) { // Posted when an input to the audio session becomes available. // This notification is posted on the main thread of your app. There is no userInfo dictionary. } if ([notification.name isEqualToString:@"AVAudioSessionInputDidBecomeUnavailableNotification"]) { // Posted when an input to the audio session becomes unavailable. // This notification is posted on the main thread of your app. There is no userInfo dictionary. } }; NSLog(@"handleInterruption: %@ reason %@",[notification name],reasonStr); } #pragma mark - #pragma mark - - (void)handleRouteChange:(NSNotification*)notification{ AVAudioSession *session = [ AVAudioSession sharedInstance ]; NSString* seccReason = @""; NSInteger reason = [[[notification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; // AVAudioSessionRouteDescription* prevRoute = [[notification userInfo] objectForKey:AVAudioSessionRouteChangePreviousRouteKey]; switch (reason) { case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: seccReason = @"The route changed because no suitable route is now available for the specified category."; break; case AVAudioSessionRouteChangeReasonWakeFromSleep: seccReason = @"The route changed when the device woke up from sleep."; break; case AVAudioSessionRouteChangeReasonOverride: seccReason = @"The output route was overridden by the app."; break; case AVAudioSessionRouteChangeReasonCategoryChange: seccReason = @"The category of the session object changed."; UInt32 newCategory; UInt32 size = sizeof(newCategory); if (AudioSessionGetProperty(kAudioSessionProperty_AudioCategory, &size, &newCategory) == noErr && newCategory != kAudioSessionCategory_PlayAndRecord && newCategory != kAudioSessionCategory_RecordAudio) { } NSLog(@"Audio category %u %d %d %d %d %d %d\n", (unsigned int)newCategory, kAudioSessionCategory_AmbientSound, kAudioSessionCategory_SoloAmbientSound, kAudioSessionCategory_MediaPlayback, kAudioSessionCategory_RecordAudio, kAudioSessionCategory_PlayAndRecord , kAudioSessionCategory_AudioProcessing); break; case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: seccReason = @"The previous audio output path is no longer available."; break; case AVAudioSessionRouteChangeReasonNewDeviceAvailable: seccReason = @"A preferred new audio output path is now available."; break; case AVAudioSessionRouteChangeReasonUnknown: default: seccReason = @"The reason for the change is unknown."; break; } AVAudioSessionPortDescription *input = [[session.currentRoute.inputs count]?session.currentRoute.inputs:nil objectAtIndex:0]; if (input.portType == AVAudioSessionPortHeadsetMic) { } } #pragma mark - #pragma mark UIDeviceProximityStateDidChangeNotification - (void)proximityStateDidChange:(NSNotification *)notification; { UIDevice *device = [notification object]; if ([device proximityState]){ [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; } else{ [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; } } #pragma mark - #pragma mark Audio Route Change Listener // Audio session callback function for responding to audio route changes. If playing back audio and // the user unplugs a headset or headphones, or removes the device from a dock connector for hardware // that supports audio playback, this callback detects that and stops playback. // // Refer to AudioSessionPropertyListener in Audio Session Services Reference. void propChangeListenerCallback ( void *inUserData, AudioSessionPropertyID inPropertyID, UInt32 inPropertyValueSize, const void *inPropertyValue ) { // AudioController *controller = (AudioController*) inUserData; // Ensure that this callback was invoked because of an audio route change if (inPropertyID == kAudioSessionProperty_AudioRouteChange) { CFDictionaryRef routeChangeDictionary = inPropertyValue; CFNumberRef routeChangeReasonRef = CFDictionaryGetValue(routeChangeDictionary, CFSTR(kAudioSession_AudioRouteChangeKey_Reason)); SInt32 routeChangeReason; CFNumberGetValue(routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason); if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) { NSLog(@"kAudioSessionRouteChangeReason_OldDeviceUnavailable.\n"); //occurs when headset is unplugged } else if(routeChangeReason == kAudioSessionRouteChangeReason_NewDeviceAvailable) { //occurs when headset is plugged NSLog(@"kAudioSessionRouteChangeReason_NewDeviceAvailable.\n"); } else if (routeChangeReason == kAudioSessionRouteChangeReason_NoSuitableRouteForCategory) { NSLog(@"kAudioSessionRouteChangeReason_NoSuitableRouteForCategory.\n"); } else if(routeChangeReason == kAudioSessionRouteChangeReason_CategoryChange) { NSLog(@"kAudioSessionRouteChangeReason_CategoryChange.\n"); UInt32 newCategory; UInt32 size = sizeof(newCategory); if (AudioSessionGetProperty(kAudioSessionProperty_AudioCategory, &size, &newCategory) == noErr && newCategory != kAudioSessionCategory_PlayAndRecord && newCategory != kAudioSessionCategory_RecordAudio) { } NSLog(@"Audio category %u %d %d %d %d %d %d\n", (unsigned int)newCategory, kAudioSessionCategory_AmbientSound, kAudioSessionCategory_SoloAmbientSound, kAudioSessionCategory_MediaPlayback, kAudioSessionCategory_RecordAudio, kAudioSessionCategory_PlayAndRecord , kAudioSessionCategory_AudioProcessing); } } else if(inPropertyID == kAudioSessionProperty_AudioInputAvailable) { //may resume the audio record if the input become available } } @end