Set/get volume in OS X

June 25, 2019

How to get/set master volume in OS X. Many sample codes online use the deprecated API. This uses the currently 'legal' API.

#include <CoreAudio/CoreAudio.h>
#include <AudioToolbox/AudioToolbox.h>
#include <AudioToolbox/AudioServices.h>
#include <AppKit/AppKit.h>
#include <CoreGraphics/CGEvent.h>

static AudioDeviceID getDefaultOutputDeviceID()
{
    AudioDeviceID outputDeviceID = kAudioObjectUnknown;

    // get output device device
    OSStatus status = noErr;
    AudioObjectPropertyAddress propertyAOPA;
    propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
    propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
    propertyAOPA.mSelector = kAudioHardwarePropertyDefaultOutputDevice;

    if (!AudioObjectHasProperty(
                kAudioObjectSystemObject, 
                &propertyAOPA))
    {
        printf("Cannot find default output device!");
        return outputDeviceID;
    }

    status = AudioObjectGetPropertyData(
            kAudioObjectSystemObject, 
            &propertyAOPA, 
            0, 
            NULL, 
            (UInt32[]){sizeof(AudioDeviceID)}, 
            &outputDeviceID);

    if (status != 0)
    {
        printf("Cannot find default output device!");
    }
    return outputDeviceID;
}

float getVolume()
{
    Float32 outputVolume;

    OSStatus status = noErr;
    AudioObjectPropertyAddress propertyAOPA;
    propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
    propertyAOPA.mSelector = kAudioHardwareServiceDeviceProperty_VirtualMasterVolume;
    propertyAOPA.mScope = kAudioDevicePropertyScopeOutput;

    AudioDeviceID outputDeviceID = getDefaultOutputDeviceID();

    if (outputDeviceID == kAudioObjectUnknown)
    {
        printf("Unknown device");
        return 0.0;
    }

    if (!AudioObjectHasProperty(outputDeviceID, &propertyAOPA))
    {
        printf("No volume returned for device 0x%0x", outputDeviceID);
        return 0.0;
    }

    status = AudioObjectGetPropertyData(
            outputDeviceID, 
            &propertyAOPA, 
            0, 
            NULL, 
            (UInt32[]){sizeof(Float32)}, 
            &outputVolume);

    if (status)
    {
        printf("No volume returned for device 0x%0x", outputDeviceID);
        return 0.0;
    }

    if (outputVolume < 0.0 || outputVolume > 1.0) return 0.0;

    return outputVolume;
}

void setVolume(float volume)
{
    AudioDeviceID outputDeviceID;
    AudioObjectPropertyAddress propertyAddress = { 
        kAudioHardwareServiceDeviceProperty_VirtualMasterVolume, 
        kAudioDevicePropertyScopeOutput,
        kAudioObjectPropertyElementMaster 
    };

    outputDeviceID = getDefaultOutputDeviceID();

    // Deprecated API. Switch to AudioObjectSetPropertyData
    /*
    AudioHardwareServiceSetPropertyData(outputDeviceID, 
                                        &propertyAddress, 
                                        0, 
                                        NULL, 
                                        sizeof(Float32),
                                        &volume);
    */
    AudioObjectSetPropertyData(
            outputDeviceID,
            &propertyAddress,
            0,
            NULL,
            sizeof(Float32),
            &volume);
}

Adapted from this SO post.