#import "AppAudioManager.h" #import "AudioRouter.h" #import "SoundBoard.h" #define DEFAULT_CATEGORY AVAudioSessionCategorySoloAmbient #define RECORDING_CATEGORY AVAudioSessionCategoryPlayAndRecord AppAudioManager *sharedAppAudioManager; @interface AppAudioManager () { enum AudioProfile _audioProfile; BOOL isSpeakerphoneActive; } @property (retain) SoundPlayer *soundPlayer; @end @implementation AppAudioManager + (AppAudioManager *)sharedInstance { @synchronized(self) { if (nil == sharedAppAudioManager) { sharedAppAudioManager = [AppAudioManager new]; sharedAppAudioManager.soundPlayer = [SoundPlayer new]; [[sharedAppAudioManager soundPlayer] setDelegate:sharedAppAudioManager]; } } return sharedAppAudioManager; } #pragma mark AudioState Management - (void)setAudioProfile:(enum AudioProfile)profile { [self updateAudioRouter]; _audioProfile = profile; } - (void)updateAudioRouter { if (isSpeakerphoneActive) { [AudioRouter routeAllAudioToExternalSpeaker]; } else { switch (_audioProfile) { case AudioProfile_Default: [AudioRouter routeAllAudioToInteralSpeaker]; break; case AudioProfile_ExternalSpeaker: [AudioRouter routeAllAudioToExternalSpeaker]; break; default: DDLogError(@"Unhandled AudioProfile"); } } } - (void)overrideAudioProfile { isSpeakerphoneActive = YES; [self updateAudioRouter]; } - (void)resetOverride { isSpeakerphoneActive = NO; [self updateAudioRouter]; } - (enum AudioProfile)getCurrentAudioProfile { return (isSpeakerphoneActive) ? AudioProfile_ExternalSpeaker : _audioProfile; } #pragma mark AudioControl; - (void)respondToProgressChange:(enum CallProgressType)progressType forLocallyInitiatedCall:(BOOL)initiatedLocally { switch (progressType) { case CallProgressType_Connecting: [sharedAppAudioManager setAudioEnabled:YES]; case CallProgressType_Ringing: (initiatedLocally) ? [self handleOutboundRing] : [self handleInboundRing]; break; case CallProgressType_Terminated: break; case CallProgressType_Securing: [self handleSecuring]; break; case CallProgressType_Talking: [self handleCallEstablished]; break; } } - (void)respondToTerminationType:(enum CallTerminationType)terminationType { if (terminationType == CallTerminationType_ResponderIsBusy) { [_soundPlayer playSound:[SoundBoard instanceOfBusySound]]; } else if ([self shouldErrorSoundBePlayedForCallTerminationType:terminationType]) { [_soundPlayer playSound:[SoundBoard instanceOfErrorAlert]]; } else { [_soundPlayer playSound:[SoundBoard instanceOfAlert]]; } } - (BOOL)shouldErrorSoundBePlayedForCallTerminationType:(enum CallTerminationType)type { [_soundPlayer stopAllAudio]; if (type == CallTerminationType_RejectedLocal || type == CallTerminationType_RejectedRemote || type == CallTerminationType_HangupLocal || type == CallTerminationType_HangupRemote || type == CallTerminationType_RecipientUnavailable) { return NO; } return YES; } - (void)handleInboundRing { [_soundPlayer playSound:[SoundBoard instanceOfInboundRingtone]]; } - (void)handleOutboundRing { [self setAudioProfile:AudioProfile_Default]; [_soundPlayer playSound:[SoundBoard instanceOfOutboundRingtone]]; } - (void)handleSecuring { [_soundPlayer stopAllAudio]; [self setAudioProfile:AudioProfile_Default]; [_soundPlayer playSound:[SoundBoard instanceOfHandshakeSound]]; } - (void)handleCallEstablished { [_soundPlayer stopAllAudio]; [self setAudioProfile:AudioProfile_Default]; [_soundPlayer playSound:[SoundBoard instanceOfCompletedSound]]; } - (BOOL)toggleSpeakerPhone { isSpeakerphoneActive = !isSpeakerphoneActive; [self updateAudioRouter]; return isSpeakerphoneActive; } #pragma mark Audio Control - (void)cancellAllAudio { [_soundPlayer stopAllAudio]; } - (BOOL)requestRecordingPrivilege { return [self changeAudioSessionCategoryTo:RECORDING_CATEGORY]; } - (BOOL)releaseRecordingPrivilege { return [self changeAudioSessionCategoryTo:DEFAULT_CATEGORY]; } - (void)requestRequiredPermissionsIfNeededWithCompletion:(PermissionBlock)permissionBlock incoming:(BOOL)isIncoming { [AVAudioSession.sharedInstance requestRecordPermission:^(BOOL granted) { if (!granted) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ACTION_REQUIRED_TITLE", @"") message:NSLocalizedString(@"AUDIO_PERMISSION_MESSAGE", @"") delegate:nil cancelButtonTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"") otherButtonTitles:NSLocalizedString(@"SETTINGS_NAV_BAR_TITLE", nil), nil]; [alertView setDelegate:self]; [alertView show]; } permissionBlock(granted); }]; } - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if (buttonIndex == 1) { // Tapped the Settings button NSURL *appSettings = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; [[UIApplication sharedApplication] openURL:appSettings]; } } - (BOOL)changeAudioSessionCategoryTo:(NSString *)category { NSError *e; [AVAudioSession.sharedInstance setCategory:category error:&e]; return (nil != e); } - (BOOL)setAudioEnabled:(BOOL)enable { NSError *e; if (enable) { [[AVAudioSession sharedInstance] setActive:enable error:&e]; [_soundPlayer awake]; } else { [[AVAudioSession sharedInstance] setActive:enable withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&e]; } return (nil != e); } - (void)awake { [_soundPlayer awake]; } #pragma mark Sound Player Delegate - (void)didCompleteSoundInstanceOfType:(SoundInstanceType)instanceType { if (instanceType == SoundInstanceTypeBusySound || instanceType == SoundInstanceTypeErrorAlert || instanceType == SoundInstanceTypeAlert) { [sharedAppAudioManager setAudioEnabled:NO]; } } @end