17

Is there a solution to setting the system's master volume from within my Swift app?

I read a lot about AudioToolbox and read some source examples in Objective-C. For example, I found this: Setting Mac OS X Volume Programatically after 10.6

But I can't get it working in Swift.

I am missing some example code in https://developer.apple.com/library/mac/documentation/AudioToolbox/Reference/AudioHardwareServicesReference/index.html#//apple_ref/c/func/AudioHardwareServiceGetPropertyData

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Peter Shaw
  • 1,867
  • 1
  • 19
  • 32

1 Answers1

29

(Code updated for Swift 4 and later, the Swift 2 and 3 versions can be found in the edit history.)

This is what I got from translating the answers to Change OS X system volume programmatically and Setting Mac OS X volume programmatically after 10.6 (Snow Leopard) to Swift (error checking omitted for brevity):

Required framework:

import AudioToolbox

Get default output device:

var defaultOutputDeviceID = AudioDeviceID(0)
var defaultOutputDeviceIDSize = UInt32(MemoryLayout.size(ofValue: defaultOutputDeviceID))

var getDefaultOutputDevicePropertyAddress = AudioObjectPropertyAddress(
    mSelector: kAudioHardwarePropertyDefaultOutputDevice,
    mScope: kAudioObjectPropertyScopeGlobal,
    mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))

let status1 = AudioObjectGetPropertyData(
    AudioObjectID(kAudioObjectSystemObject),
    &getDefaultOutputDevicePropertyAddress,
    0,
    nil,
    &defaultOutputDeviceIDSize,
    &defaultOutputDeviceID)

Set volume:

var volume = Float32(0.50) // 0.0 ... 1.0
var volumeSize = UInt32(MemoryLayout.size(ofValue: volume))

var volumePropertyAddress = AudioObjectPropertyAddress(
    mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
    mScope: kAudioDevicePropertyScopeOutput,
    mElement: kAudioObjectPropertyElementMaster)

let status2 = AudioObjectSetPropertyData(
    defaultOutputDeviceID,
    &volumePropertyAddress,
    0,
    nil,
    volumeSize,
    &volume)

Finally, for the sake of completeness, get the volume:

var volume = Float32(0.0)
var volumeSize = UInt32(MemoryLayout.size(ofValue: volume))

var volumePropertyAddress = AudioObjectPropertyAddress(
    mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
    mScope: kAudioDevicePropertyScopeOutput,
    mElement: kAudioObjectPropertyElementMaster)

let status3 = AudioObjectGetPropertyData(
    defaultOutputDeviceID,
    &volumePropertyAddress,
    0,
    nil,
    &volumeSize,
    &volume)

print(volume)

Error checking has been omitted for brevity. Of course one should check the status return values for success or failure in a real application.

Credits go to Set OS X volume in OS X 10.11 using Swift without using the deprecated AudioHardwareServiceSetPropertyData API for using AudioObjectSetPropertyData() instead of the deprecated AudioHardwareServiceSetPropertyData().

As noamtm mentions in the comments, this works also for getting and setting the left-right balance, by passing

mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterBalance

to AudioObjectPropertyAddress().

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • cool. thanks a lot. Now i see my mistakes that i made with AudioDeviceId and the kAudio.... – Peter Shaw Dec 04 '14 at 10:49
  • In Swift 5, you need to import `CoreAudio` as well, and the `kAudio` properties have the right type. – zneak Feb 01 '19 at 07:09
  • @zneak: Thanks for letting me know. I have updated the Code for Swift 4 (and 5). In my project, importing AudioToolbox was sufficient, even in Swift 5 (Xcode 10.2 beta). – Martin R Feb 01 '19 at 07:47
  • Works perfectly Catalina 10.15.2, Swift 5, Xcode 11.3.1 – user3069232 Feb 07 '20 at 13:21
  • Thanks, this is the first usable answer. Could you tell me how to get the default input device instead of output? – duongel Apr 29 '20 at 09:30
  • @duongel: Sorry, I do not know. – Martin R Apr 29 '20 at 12:33
  • 3
    Thanks for it. I want to add that left-right balance can also be controlled in the same way, just change `kAudioHardwareServiceDeviceProperty_VirtualMasterVolume` to `kAudioHardwareServiceDeviceProperty_VirtualMasterBalance`. @MartinR please consider adding this note to the answer. – noamtm Apr 30 '20 at 11:47