Skip to content

Instantly share code, notes, and snippets.

@rlxone
Last active October 9, 2023 19:46
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save rlxone/584467a63ac0ddf4d62fe1a983b42d0e to your computer and use it in GitHub Desktop.
Save rlxone/584467a63ac0ddf4d62fe1a983b42d0e to your computer and use it in GitHub Desktop.
CoreAudio output device useful methods in Swift 4
/*
static func getOutputDevices() -> [AudioDeviceID: String]?
static func isOutputDevice(deviceID: AudioDeviceID) -> Bool
static func getAggregateDeviceSubDeviceList(deviceID: AudioDeviceID) -> [AudioDeviceID]
static func isAggregateDevice(deviceID: AudioDeviceID) -> Bool
static func setDeviceVolume(deviceID: AudioDeviceID, leftChannelLevel: Float, rightChannelLevel: Float)
static func setOutputDevice(newDeviceID: AudioDeviceID)
static func getDeviceVolume(deviceID: AudioDeviceID) -> [Float]
static func getDefaultOutputDevice() -> AudioDeviceID
*/
import Foundation
import Cocoa
import AudioToolbox
public class Audio {
static func getOutputDevices() -> [AudioDeviceID: String]? {
var result: [AudioDeviceID: String] = [:]
let devices = getAllDevices()
for device in devices {
if isOutputDevice(deviceID: device) {
result[device] = getDeviceName(deviceID: device)
}
}
return result
}
static func isOutputDevice(deviceID: AudioDeviceID) -> Bool {
var propertySize: UInt32 = 256
var propertyAddress = AudioObjectPropertyAddress(
mSelector: AudioObjectPropertySelector(kAudioDevicePropertyStreams),
mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput),
mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
_ = AudioObjectGetPropertyDataSize(deviceID, &propertyAddress, 0, nil, &propertySize)
return propertySize > 0
}
static func getAggregateDeviceSubDeviceList(deviceID: AudioDeviceID) -> [AudioDeviceID] {
let subDevicesCount = getNumberOfSubDevices(deviceID: deviceID)
var subDevices = [AudioDeviceID](repeating: 0, count: Int(subDevicesCount))
var propertyAddress = AudioObjectPropertyAddress(
mSelector: AudioObjectPropertySelector(kAudioAggregateDevicePropertyActiveSubDeviceList),
mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
var subDevicesSize = subDevicesCount * UInt32(MemoryLayout<UInt32>.size)
AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &subDevicesSize, &subDevices)
return subDevices
}
static func isAggregateDevice(deviceID: AudioDeviceID) -> Bool {
let deviceType = getDeviceTransportType(deviceID: deviceID)
return deviceType == kAudioDeviceTransportTypeAggregate
}
static func setDeviceVolume(deviceID: AudioDeviceID, leftChannelLevel: Float, rightChannelLevel: Float) {
let channelsCount = 2
var channels = [UInt32](repeating: 0, count: channelsCount)
var propertySize = UInt32(MemoryLayout<UInt32>.size * channelsCount)
var leftLevel = leftChannelLevel
var rigthLevel = rightChannelLevel
var propertyAddress = AudioObjectPropertyAddress(
mSelector: AudioObjectPropertySelector(kAudioDevicePropertyPreferredChannelsForStereo),
mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput),
mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
let status = AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &channels)
if status != noErr { return }
propertyAddress.mSelector = kAudioDevicePropertyVolumeScalar
propertySize = UInt32(MemoryLayout<Float32>.size)
propertyAddress.mElement = channels[0]
AudioObjectSetPropertyData(deviceID, &propertyAddress, 0, nil, propertySize, &leftLevel)
propertyAddress.mElement = channels[1]
AudioObjectSetPropertyData(deviceID, &propertyAddress, 0, nil, propertySize, &rigthLevel)
}
static func setOutputDevice(newDeviceID: AudioDeviceID) {
let propertySize = UInt32(MemoryLayout<UInt32>.size)
var deviceID = newDeviceID
var propertyAddress = AudioObjectPropertyAddress(
mSelector: AudioObjectPropertySelector(kAudioHardwarePropertyDefaultOutputDevice),
mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
AudioObjectSetPropertyData(AudioObjectID(kAudioObjectSystemObject), &propertyAddress, 0, nil, propertySize, &deviceID)
}
static func getDeviceVolume(deviceID: AudioDeviceID) -> [Float] {
let channelsCount = 2
var channels = [UInt32](repeating: 0, count: channelsCount)
var propertySize = UInt32(MemoryLayout<UInt32>.size * channelsCount)
var leftLevel = Float32(-1)
var rigthLevel = Float32(-1)
var propertyAddress = AudioObjectPropertyAddress(
mSelector: AudioObjectPropertySelector(kAudioDevicePropertyPreferredChannelsForStereo),
mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput),
mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
let status = AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &channels)
if status != noErr { return [-1] }
propertyAddress.mSelector = kAudioDevicePropertyVolumeScalar
propertySize = UInt32(MemoryLayout<Float32>.size)
propertyAddress.mElement = channels[0]
AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &leftLevel)
propertyAddress.mElement = channels[1]
AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &rigthLevel)
return [leftLevel, rigthLevel]
}
static func getDefaultOutputDevice() -> AudioDeviceID {
var propertySize = UInt32(MemoryLayout<AudioDeviceID>.size)
var deviceID = kAudioDeviceUnknown
var propertyAddress = AudioObjectPropertyAddress(
mSelector: AudioObjectPropertySelector(kAudioHardwarePropertyDefaultOutputDevice),
mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &propertyAddress, 0, nil, &propertySize, &deviceID)
return deviceID
}
private static func getDeviceTransportType(deviceID: AudioDeviceID) -> AudioDevicePropertyID {
var deviceTransportType = AudioDevicePropertyID()
var propertySize = UInt32(MemoryLayout<AudioDevicePropertyID>.size)
var propertyAddress = AudioObjectPropertyAddress(
mSelector: AudioObjectPropertySelector(kAudioDevicePropertyTransportType),
mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &deviceTransportType)
return deviceTransportType
}
private static func getNumberOfDevices() -> UInt32 {
var propertySize: UInt32 = 0
var propertyAddress = AudioObjectPropertyAddress(
mSelector: AudioObjectPropertySelector(kAudioHardwarePropertyDevices),
mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
_ = AudioObjectGetPropertyDataSize(AudioObjectID(kAudioObjectSystemObject), &propertyAddress, 0, nil, &propertySize)
return propertySize / UInt32(MemoryLayout<AudioDeviceID>.size)
}
private static func getNumberOfSubDevices(deviceID: AudioDeviceID) -> UInt32 {
var propertySize: UInt32 = 0
var propertyAddress = AudioObjectPropertyAddress(
mSelector: AudioObjectPropertySelector(kAudioAggregateDevicePropertyActiveSubDeviceList),
mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
_ = AudioObjectGetPropertyDataSize(deviceID, &propertyAddress, 0, nil, &propertySize)
return propertySize / UInt32(MemoryLayout<AudioDeviceID>.size)
}
private static func getDeviceName(deviceID: AudioDeviceID) -> String {
var propertySize = UInt32(MemoryLayout<CFString>.size)
var propertyAddress = AudioObjectPropertyAddress(
mSelector: AudioObjectPropertySelector(kAudioDevicePropertyDeviceNameCFString),
mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
var result: CFString = "" as CFString
AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &result)
return result as String
}
private static func getAllDevices() -> [AudioDeviceID] {
let devicesCount = getNumberOfDevices()
var devices = [AudioDeviceID](repeating: 0, count: Int(devicesCount))
var propertyAddress = AudioObjectPropertyAddress(
mSelector: AudioObjectPropertySelector(kAudioHardwarePropertyDevices),
mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
var devicesSize = devicesCount * UInt32(MemoryLayout<UInt32>.size)
AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &propertyAddress, 0, nil, &devicesSize, &devices)
return devices
}
}
@alexandrucancescu
Copy link

Thanks a lot for this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment