Are headphones plugged in? iOS7
Asked Answered
S

8

66

Developing an app for an iPhone with audio files that need to be listened too through headphones.

How do I check if headphones aren't plugged in so I can tell the user to plug in headphones.

I have the following code from another thread but the audioSessionGetProperty method is deprecated. Anyone know how to alter the following code to make this work OR have there own code/solution.

Thanks.

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


    //Maybe changing it to something like the following would work for iOS7?
    //AVAudioSession* session = [AVAudioSession sharedInstance];
    //OSStatus error = [session setCategory:kAudioSessionProperty_AudioRoute...?


    //the line below is whats giving me the warning
    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 = (__bridge NSString*)route;

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

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

    }

    return NO;
}
Spile answered 22/1, 2014 at 19:51 Comment(1)
what you mean by full working code?Thermodynamics
C
121

This should work, but I cannot test it right now, I'll do in the evening.

- (BOOL)isHeadsetPluggedIn {
    AVAudioSessionRouteDescription* route = [[AVAudioSession sharedInstance] currentRoute];
    for (AVAudioSessionPortDescription* desc in [route outputs]) {
        if ([[desc portType] isEqualToString:AVAudioSessionPortHeadphones])
            return YES;
    }
    return NO;
}
Chaim answered 27/1, 2014 at 13:58 Comment(2)
Glad to have helped you :)Chaim
How do I put this into an actionDecorticate
S
59

Just to extend @Antonio's answer. If you need to detect whether the user has pulled out or plugged in the headphone.

#import <AVFoundation/AVFoundation.h>

// [AVAudioSession sharedInstance]; // @Boris edited: you may need it if there is no `AVAudioSession instance` created before. If doesn't work, uncomment this line.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:)
                                             name:AVAudioSessionRouteChangeNotification
                                           object:nil];
// don't forget to `removeObserver:`

// If the user pulls out he headphone jack, stop playing.
- (void)audioRouteChangeListenerCallback:(NSNotification*)notification
{
    NSDictionary *interuptionDict = notification.userInfo;

    NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];

    switch (routeChangeReason) {

        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
            NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
            NSLog(@"Headphone/Line plugged in");
            break;

        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
            NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
            NSLog(@"Headphone/Line was pulled. Stopping player....");
            break;

        case AVAudioSessionRouteChangeReasonCategoryChange:
            // called at start - also when other audio wants to play
            NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
            break;
    }
}
Subcontinent answered 29/1, 2014 at 16:49 Comment(3)
Thank you very much I have tested this and works great!Spile
from docs: If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its dealloc method.Ghiberti
this didn't work with me in swift until I passed AVAudioSession.sharedInstance() as the parameter object for notification center instead of nil, i.e. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];Cletus
D
12

Swift 3:

To check if headphones are connected

extension AVAudioSession {

    static var isHeadphonesConnected: Bool {
        return sharedInstance().isHeadphonesConnected
    }

    var isHeadphonesConnected: Bool {
        return !currentRoute.outputs.filter { $0.isHeadphones }.isEmpty
    }

}

extension AVAudioSessionPortDescription {
    var isHeadphones: Bool {
        return portType == AVAudioSessionPortHeadphones
    }
}

Then you can just print("isHeadphones connected: \(AVAudioSession.isHeadphonesConnected)")

Listening to Changes

In Swift 3 the syntax is this:

func handleRouteChange(_ notification: Notification) {
    guard
    let userInfo = notification.userInfo,
    let reasonRaw = userInfo[AVAudioSessionRouteChangeReasonKey] as? NSNumber,
    let reason = AVAudioSessionRouteChangeReason(rawValue: reasonRaw.uintValue)
    else { fatalError("Strange... could not get routeChange") }
    switch reason {
    case .oldDeviceUnavailable:
        print("oldDeviceUnavailable")
    case .newDeviceAvailable:
        print("newDeviceAvailable") 
        if AVAudioSession.isHeadphonesConnected {
             print("Just connected headphones")
        } 
    case .routeConfigurationChange:
        print("routeConfigurationChange")
    case .categoryChange:
        print("categoryChange")
    default:
        print("not handling reason")
    }
}

func listenForNotifications() {
    NotificationCenter.default.addObserver(self, selector: #selector(handleRouteChange(_:)), name: NSNotification.Name.AVAudioSessionRouteChange, object: nil)
}

Notice use of:

 if AVAudioSession.isHeadphonesConnected {
    print("Just connected headphones")
 }
Dogberry answered 28/9, 2016 at 14:7 Comment(4)
How does this answer the question? It does not answer if headphones are plugged in or not.Horrendous
@Horrendous I edited my answer to include check of headphones. Please upvote my answer if it was helpful to you.Dogberry
Does this work? There is no member isHeadphonesConnected in AVAudioSession in Swift 3 developer.apple.com/documentation/avfoundation/avaudiosessionPreponderate
@thuanle, look carefully at the code, isHeadphonesConnected used in the extension.Uam
D
4

@Warif's code in Swift 2.0 with little changes ...

func audioRouteChangeListenerCallback (notif: NSNotification){
        let userInfo:[NSObject:AnyObject] = notif.userInfo!
        println("\(userInfo)")
        let routChangeReason = UInt((userInfo[AVAudioSessionRouteChangeReasonKey]?.integerValue)!)
        switch routChangeReason {
        case AVAudioSessionRouteChangeReason.NewDeviceAvailable.rawValue:
            self.println("Headphone/Line plugged in");
            break;

        case AVAudioSessionRouteChangeReason.OldDeviceUnavailable.rawValue:
            //If the headphones was pulled move to speaker
            do {
                try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSessionPortOverride.Speaker)
            } catch _ {
            }
            self.println("Headphone/Line was pulled. Stopping player....");
            break;

        case AVAudioSessionRouteChangeReason.CategoryChange.rawValue:
            // called at start - also when other audio wants to play
            self.println("AVAudioSessionRouteChangeReasonCategoryChange");
            break;
        default:
            break;
        }
    }

:D

Dick answered 30/9, 2015 at 16:30 Comment(0)
B
3

In Swift (as of 1.2):

    func headsetPluggedIn() -> Bool {
    let route = AVAudioSession.sharedInstance().currentRoute
    return (route.outputs as! [AVAudioSessionPortDescription]).filter({ $0.portType == AVAudioSessionPortHeadphones }).count > 0
}
Berkowitz answered 22/9, 2015 at 16:40 Comment(0)
T
3

Swift 3.0 version

  • Method to check if headphones are plugged or any Bluetooth device with audio output connected
    func bluetoothOrHeadphonesConnected() -> Bool {

        let outputs = AVAudioSession.sharedInstance().currentRoute.outputs

        for output in outputs{

            if output.portType == AVAudioSessionPortBluetoothA2DP ||
               output.portType == AVAudioSessionPortBluetoothHFP ||
               output.portType == AVAudioSessionPortBluetoothLE ||
               output.portType == AVAudioSessionPortHeadphones {
                return true
            }

        }

        return false 
    }
  • It's important to check if the headphones are plugged out while you listen any audio.
 
    private func setupObservers() {

        NotificationCenter.default.addObserver(self, selector: #selector(self.audioRouteChangeListener), name: .AVAudioSessionRouteChange, object: nil)

    }

    func audioRouteChangeListener(notification: Notification) {

        guard let audioRouteChangeReason = notification.userInfo![AVAudioSessionRouteChangeReasonKey] as? Int else { return }

        switch audioRouteChangeReason {

            case AVAudioSessionRouteChangeReason.oldDeviceUnavailable.hashValue:
                //plugged out

            default:
                break

        }

    }

They answered 17/5, 2017 at 13:29 Comment(3)
Please don't post identical answers to multiple questions. Post one good answer, then vote/flag to close the other questions as duplicates. If the question is not a duplicate, tailor your answers to the question.Kissee
Thanks for the suggestion @PaulRoubThey
The question asserts that the app needs to be used with headphones, in particular. The Bluetooth ports you listed can also apply to cars, portable speakers, etc.Amarelle
I
0
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(plugout:) name:AVAudioSessionRouteChangeNotification object:nil];
-(void)plugout:(NSNotification*)notification
{
    isRemovedHeadset = YES;
}

and handle your code using this isRemovedHeadset boolean in your

if (moviePlayer.playbackState == MPMoviePlaybackStatePaused) 
{
    if(isRemovedHeadset)
    {
        isRemovedHeadset = NO;
        [moviePlayer prepareToPlay];
        [moviePlayer play];
        return;
    }
}
Inarch answered 5/9, 2014 at 13:46 Comment(0)
B
0

@Sajjon solution on Swift 5 with RxSwift

func handleRouteChange(_ notification: Notification) {
    guard
        let userInfo = notification.userInfo,
        let reasonRaw = userInfo[AVAudioSessionRouteChangeReasonKey] as? NSNumber,
        let reason = AVAudioSession.RouteChangeReason(rawValue: reasonRaw.uintValue)
        else { fatalError("Strange... could not get routeChange") }
    switch reason {
    case .oldDeviceUnavailable:
        print("oldDeviceUnavailable")
    case .newDeviceAvailable:
        print("newDeviceAvailable")
        if AVAudioSession.isHeadphonesConnected {
            print("Just connected headphones")
        }
    case .routeConfigurationChange:
        print("routeConfigurationChange")
    case .categoryChange:
        print("categoryChange")
    default:
        print("not handling reason")
    }
}

func listenForNotifications() {
    NotificationCenter.default.rx
        .notification(AVAudioSession.routeChangeNotification)
        .subscribe(onNext: { (n) in
            self.handleRouteChange(n)
        })
        .disposed(by: disposeBag)
}
Bittersweet answered 26/7, 2019 at 13:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.