14

I need to change my audio depending on whether or not headphones are plugged in. I'm aware of kAudioSessionProperty_AudioInputAvailable, which will tell me if there's a microphone, but I'd like to test for any headphones, not just headphones with a built-in microphone. Is this possible?

ekscrypto
  • 3,718
  • 1
  • 24
  • 38
morgancodes
  • 25,055
  • 38
  • 135
  • 187
  • 1
    @Brad Larson The answer to that question didn't give me the information I need. My question specifies _headphones_, not microphone. I believe the accepted answer to the question you linked to describes how to detect if a microphone is plugged in. – morgancodes Sep 16 '10 at 17:47
  • Yes, I guess you're asking a refinement of that question where simply detecting the audio input isn't good enough. Perhaps some of the discussion around [this question](http://stackoverflow.com/questions/1238758/how-can-i-detect-if-headphones-are-connected-to-an-ipod-touch-g1) might be helpful as well. – Brad Larson Sep 16 '10 at 19:56
  • Thanks for pointing me to that question. – morgancodes Sep 16 '10 at 23:15
  • 3
    Maybe, nearly two years on, you should accept the answer...? – JohnK May 14 '13 at 18:23

4 Answers4

28

Here is a method of my own which is a slightly modified version of one found on this site : http://www.iphonedevsdk.com/forum/iphone-sdk-development/9982-play-record-same-time.html

- (BOOL)isHeadsetPluggedIn {
    UInt32 routeSize = sizeof (CFStringRef);
    CFStringRef route;

    OSStatus error = AudioSessionGetProperty (kAudioSessionProperty_AudioRoute,
                                              &routeSize,
                                              &route);

    /* Known values of route:
     * "Headset"
     * "Headphone"
     * "Speaker"
     * "SpeakerAndMicrophone"
     * "HeadphonesAndMicrophone"
     * "HeadsetInOut"
     * "ReceiverAndMicrophone"
     * "Lineout"
     */

    if (!error && (route != NULL)) {

        NSString* routeStr = (NSString*)route;

        NSRange headphoneRange = [routeStr rangeOfString : @"Head"];

        if (headphoneRange.location != NSNotFound) return YES;

    }

    return NO;
}
jptsetung
  • 9,064
  • 3
  • 43
  • 55
12

Here's a solution based on rob mayoff's comment:

- (BOOL)isHeadsetPluggedIn
{
    AVAudioSessionRouteDescription *route = [[AVAudioSession sharedInstance] currentRoute];

    BOOL headphonesLocated = NO;
    for( AVAudioSessionPortDescription *portDescription in route.outputs )
    {
        headphonesLocated |= ( [portDescription.portType isEqualToString:AVAudioSessionPortHeadphones] );
    }
    return headphonesLocated;
}

Simply link to the AVFoundation framework.

ekscrypto
  • 3,718
  • 1
  • 24
  • 38
3

Just a heads up for any future readers of this post.

Most of the AVToolbox methods have been deprecated with the release of iOS 7 without alternative so audio listeners are now largely redundancy

Anthony Main
  • 6,039
  • 12
  • 64
  • 89
1

I started with the code given above by jpsetung, but there were a few issues with it for my use case:

  • No evidence of something called kAudioSessionProperty_AudioRoute in the docs
  • Leaks route
  • No audio session check
  • String check for headphones instead of logical awareness of categories
  • I was more interested in whether the iPhone was using its speakers, with "headphones" meaning "anything other than speakers". I feel that leaving out options like "bluetooth", "airplay", or "lineout" was dangerous.

This implementation broadens the check to allow for any type of specified output:

BOOL isAudioRouteAvailable(CFStringRef routeType)
{
    /*
    As of iOS 5:
    kAudioSessionOutputRoute_LineOut;
    kAudioSessionOutputRoute_Headphones;
    kAudioSessionOutputRoute_BluetoothHFP;
    kAudioSessionOutputRoute_BluetoothA2DP;
    kAudioSessionOutputRoute_BuiltInReceiver;
    kAudioSessionOutputRoute_BuiltInSpeaker;
    kAudioSessionOutputRoute_USBAudio;
    kAudioSessionOutputRoute_HDMI;
    kAudioSessionOutputRoute_AirPlay;
    */

    //Prep
    BOOL foundRoute = NO;
    CFDictionaryRef description = NULL;

    //Session
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        AudioSessionInitialize(NULL, NULL, NULL, NULL);
    });

    //Property
    UInt32 propertySize;
    AudioSessionGetPropertySize(kAudioSessionProperty_AudioRouteDescription, &propertySize);
    OSStatus error = AudioSessionGetProperty(kAudioSessionProperty_AudioRouteDescription, &propertySize, &description);
    if ( !error && description ) {
        CFArrayRef outputs = CFDictionaryGetValue(description, kAudioSession_AudioRouteKey_Outputs);
        CFIndex count = CFArrayGetCount(outputs);
        if ( outputs && count ) {
            for (CFIndex i = 0; i < count; i++) {
                CFDictionaryRef route = CFArrayGetValueAtIndex(outputs, i);
                CFStringRef type = CFDictionaryGetValue(route, kAudioSession_AudioRouteKey_Type);
                NSLog(@"Got audio route %@", type);

                //Audio route type
                if ( CFStringCompare(type, routeType, 0) == kCFCompareEqualTo ) {
                    foundRoute = YES;
                    break;
                }
            }
        }
    } else if ( error ) {
        NSLog(@"Audio route error %ld", error);
    }

    //Cleanup
    if ( description ) {
        CFRelease(description);
    }

    //Done
    return foundRoute;  
}

Used like so:

if ( isAudioRouteAvailable(kAudioSessionOutputRoute_BuiltInSpeaker) ) {
    //Do great things...
}
SG1
  • 2,871
  • 1
  • 29
  • 41
  • 2
    Check out `-[AVAudioSession currentRoute]` for an Objective-C interface to this information as of iOS 6.0. – rob mayoff Jul 14 '13 at 06:10
  • Definitely a good note, but only if you are linking to AVFoundation (the code above is for AudioToolbox) – SG1 Sep 02 '13 at 17:26