Skip to content

Instantly share code, notes, and snippets.

@mblsha
Created August 17, 2020 14:44
Show Gist options
  • Save mblsha/f6db715ca6245b36eb1fa6d8af23cf4b to your computer and use it in GitHub Desktop.
Save mblsha/f6db715ca6245b36eb1fa6d8af23cf4b to your computer and use it in GitHub Desktop.
Getting/setting volume, balance, mute using CoreAudio (inspired by https://github.com/digital-pers0n/keepQuiet)
import CoreAudio
import AudioToolbox
let defaultDeviceProp = AudioObjectPropertyAddress(
mSelector: kAudioHardwarePropertyDefaultOutputDevice,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster)
let masterVolumeProp = AudioObjectPropertyAddress(
mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
mScope: kAudioDevicePropertyScopeOutput,
mElement: kAudioObjectPropertyElementMaster)
let balanceProp = AudioObjectPropertyAddress(
mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterBalance,
mScope: kAudioDevicePropertyScopeOutput,
mElement: kAudioObjectPropertyElementMaster)
let muteVolumeProp = AudioObjectPropertyAddress(
mSelector: kAudioDevicePropertyMute,
mScope: kAudioDevicePropertyScopeOutput,
mElement: kAudioObjectPropertyElementMaster)
enum AudioPropertyError : Error {
case NoProperty
case PropertyNotSettable
case GetPropertyData(OSStatus)
case SetPropertyData(OSStatus)
case EmptyResult
}
struct AudioProperty<T> {
let emptyValue: T?
let device: AudioDeviceID
let prop: AudioObjectPropertyAddress
// For numerical values if we don't initialize 'result: T?' to non-nil
// value we'll get .EmptyResult error if the value is zero.
init(device: AudioDeviceID?, emptyValue: T?, prop: AudioObjectPropertyAddress) {
self.emptyValue = emptyValue
self.device = device ?? UInt32(kAudioObjectSystemObject)
self.prop = prop
}
func get() -> Result<T, AudioPropertyError> {
var p = prop
if (!AudioObjectHasProperty(device, &p)) {
return .failure(.NoProperty)
}
var size = UInt32(MemoryLayout<T>.size)
var result: T? = emptyValue
let error = AudioObjectGetPropertyData(device, &p, 0, nil, &size, &result)
if (error != noErr) {
return .failure(.GetPropertyData(error))
}
if let result = result {
return .success(result)
}
return .failure(.EmptyResult)
}
func set(_ newValue: T) -> Result<T, AudioPropertyError> {
var p = prop
if (!AudioObjectHasProperty(device, &p)) {
return .failure(.NoProperty)
}
var settable: DarwinBoolean = false
var error = AudioObjectIsPropertySettable(device, &p, &settable)
if (error != noErr) {
return .failure(.PropertyNotSettable)
}
let size = UInt32(MemoryLayout<T>.size)
var value = newValue
error = AudioObjectSetPropertyData(device, &p, 0, nil, size, &value)
if (error != noErr) {
return .failure(.SetPropertyData(error))
}
return .success(value)
}
}
let device = AudioProperty<AudioDeviceID>(device: nil, emptyValue: nil, prop: defaultDeviceProp)
let volume = AudioProperty<Float>(device: device.get().get(), emptyValue: 0, prop: masterVolumeProp)
let balance = AudioProperty<Float>(device: device.get().get(), emptyValue: 0, prop: balanceProp)
let mute = AudioProperty<UInt32>(device: device.get().get(), emptyValue: 0, prop: muteVolumeProp)
device.get()
volume.get()
balance.get()
mute.get()
balance.set(0.58)
volume.set(0.3)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment