How to get notifications when the headphones are plugged in/out? Mac

I was looking for a similar solution and found AutoMute in the app store. It works well.

I'm also working on some scripts of my own, and wrote this script to test if headphones are plugged in:

#!/bin/bash
if system_profiler SPAudioDataType | grep --quiet Headphones; then
  echo plugged in
else
  echo not plugged in
fi

You can observe changes using the CoreAudio framework.

Both headphones and the speakers are data sources on the same audio output device (of type built-in). One of both will be on the audio device based on headphones being plugged in or not.

To get notifications you listen to changes of the active datasource on the built-in output device.

1. Get the built-in output device

To keep this short we'll use the default output device. In most cases this is the built-in output device. In real-life applications you'll want to loop all available devices to find it, because the default device could be set to a different audio device (soundflower or airplay for example).

AudioDeviceID defaultDevice = 0;
UInt32 defaultSize = sizeof(AudioDeviceID);

const AudioObjectPropertyAddress defaultAddr = {
    kAudioHardwarePropertyDefaultOutputDevice,
    kAudioObjectPropertyScopeGlobal,
    kAudioObjectPropertyElementMaster
};

AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultAddr, 0, NULL, &defaultSize, &defaultDevice); 

2. Read its current data source

The current datasource on a device is identified by an ID of type UInt32.

AudioObjectPropertyAddress sourceAddr;
sourceAddr.mSelector = kAudioDevicePropertyDataSource;
sourceAddr.mScope = kAudioDevicePropertyScopeOutput;
sourceAddr.mElement = kAudioObjectPropertyElementMaster;

UInt32 dataSourceId = 0;
UInt32 dataSourceIdSize = sizeof(UInt32);
AudioObjectGetPropertyData(defaultDevice, &sourceAddr, 0, NULL, &dataSourceIdSize, &dataSourceId);

3. Observe for changes to the data source

AudioObjectAddPropertyListenerBlock(_defaultDevice, &sourceAddr, dispatch_get_current_queue(), ^(UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses) {
    // move to step 2. to read the updated value
});

Determine the data source type

When you have the data source id as UInt32 you can query the audio object for properties using a value transformer. For example to get the source name as string use kAudioDevicePropertyDataSourceNameForIDCFString. This will result in the string "Internal Speaker" or "Headphones". However this might differ based on user locale.

An easier way is to compare the data source id directly:

if (dataSourceId == 'ispk') {
    // Recognized as internal speakers
} else if (dataSourceId == 'hdpn') {
    // Recognized as headphones
}

However I couldn't find any constants defined for these values, so this is kind of undocumented.