Search code examples
iosiphoneaudioavfoundationcore-audio

Remove AVAudioSessionPortBuiltInMic as an option


I would like to remove the iPhone's built in mic as an option. Only the one that comes out the headset though. Basically I want to force output to either headphones/bluetooth (if available) or the main speaker. Apple hasn't documented a way to remove a port. Would it be possible to intercept route changes and if they select a mic force it to a speaker?


Solution

  • Yes, it's possible to intercept route changes. You need to observe for AVAudioSessionRouteChangeNotification notification:

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(audioRouteDidChanged:)
                                                     name:AVAudioSessionRouteChangeNotification
                                                   object:nil];
    

    then you will be able to distinguish route change type:

    - (void)audioRouteDidChanged:(NSNotification *)notification
    {
        // Here you can figure out the why audio route has been changed (if you need it)
        AVAudioSessionRouteChangeReason reason = [notification.userInfo valueForKey:AVAudioSessionRouteChangeReasonKey];
        switch (reason)
        {
            case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
                NSLog(@"A user action (such as plugging in a headset) has made a preferred audio route available.");
                break;
    
            case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
                NSLog(@"The previous audio output path is no longer available.");
                break;
    
            default:
                NSLog(@"Other reasons. See documentation");
                break;
        }
    
        // And then make a decision to disable earpiece(aka receiver).
        // You can request current audio route description
        AVAudioSessionRouteDescription *routeDescription = [AVAudioSession sharedInstance].currentRoute;
        // then traverse through outputs and figure out if headset is present
        __block BOOL headsetExists = NO;
        [routeDescription.outputs enumerateObjectsUsingBlock:^(AVAudioSessionPortDescription *obj, NSUInteger idx, BOOL *stop) {
            if ([obj.portType isEqualToString:AVAudioSessionPortHeadphones])
            {
                headsetExists = YES;
                return ;
            }
        }];
    
        if (headsetExists == NO)
        {
            // force sound to speaker
            NSError *err = nil;
            [[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker
                                                               error:&err];
        }
    }