Skip to content

Instantly share code, notes, and snippets.

@chajka
Created December 30, 2018 08:59
Show Gist options
  • Save chajka/f835ad4e78f7843a53b4b04f25387bff to your computer and use it in GitHub Desktop.
Save chajka/f835ad4e78f7843a53b4b04f25387bff to your computer and use it in GitHub Desktop.
import Cocoa
import CoreAudio
public struct DeviceID {
let deviceID: AudioDeviceID
let UID: String
}// end struct DeviceID
final class AudioDevices: NSObject {
// MARK: Outlets
// MARK: - Properties
public var inputDevices: Dictionary<String, DeviceID>
public var outputDevices: Dictionary<String, DeviceID>
// MARK: - Member variables
// MARK: - class method
public static func currentOutputtDevice () -> (name: String, deviceID: AudioDeviceID, UID: String) {
let outputDeviceIdentifier: AudioDeviceID = self.get(deviceIdentifierOf: kAudioHardwarePropertyDefaultOutputDevice)
let outputDeviceName: String = get(nameOf: outputDeviceIdentifier)
let outputDeviceUID: String = get(deviceUniqueIdentifierOf: outputDeviceIdentifier)
return (outputDeviceName, outputDeviceIdentifier, outputDeviceUID)
}// end static func cuurent outputDevice
public static func currenIntputtDevice () -> (name: String, deviceID: AudioDeviceID, UID: String) {
let inputDeviceIdentifier: AudioDeviceID = self.get(deviceIdentifierOf: kAudioHardwarePropertyDefaultInputDevice)
let inputDeviceName: String = get(nameOf: inputDeviceIdentifier)
let inputDeviceUID: String = get(deviceUniqueIdentifierOf: inputDeviceIdentifier)
return (inputDeviceName, inputDeviceIdentifier, inputDeviceUID)
}// end static func cuurent outputDevice
// MARK: - Constructor/Destructor
override init() {
inputDevices = Dictionary()
outputDevices = Dictionary()
super.init()
let devices:Array<AudioDeviceID> = availableDevices()
for identifier: AudioDeviceID in devices {
deviceInfo(identifier)
}// end foreach device identifier
}// end override init
// MARK: - Override
// MARK: - Actions
// MARK: - Public methods
// MARK: - Private methods
private func availableDevices() -> Array<AudioDeviceID> {
var property: AudioObjectPropertyAddress = AudioObjectPropertyAddress(mSelector: kAudioHardwarePropertyDevices, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster)
var dataSize:UInt32 = 0
let status: OSStatus = AudioObjectGetPropertyDataSize(AudioObjectID(kAudioObjectSystemObject), &property, 0, nil, &dataSize)
var devices:Array<AudioDeviceID> = Array()
if status != kAudioHardwareNoError {
devices = Array()
} else {
let deviceCount: Int = Int(dataSize) / MemoryLayout<AudioDeviceID>.size
var foundDevices: Array<AudioDeviceID> = Array<AudioDeviceID>(repeating: 0, count: deviceCount)
foundDevices.withUnsafeMutableBufferPointer { ( item: inout UnsafeMutableBufferPointer<AudioDeviceID>) -> () in
let error: OSStatus = AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &property, 0, nil, &dataSize, item.baseAddress!)
if error != kAudioHardwareNoError { print("each device ID Get Error: \(error)") }
}// end withUnsafeMutableBufferPointer
devices = Array(foundDevices)
}// end if
return devices
}// end availableDevices
private func deviceInfo(_ deviceIdentifier: AudioDeviceID) {
var name: String = ""
var UID: String = ""
var buffer: UnsafeMutablePointer<UInt8>
var property: AudioObjectPropertyAddress = AudioObjectPropertyAddress(mSelector: kAudioDevicePropertyDeviceName, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster)
var size: UInt32 = 0
var status: OSStatus = AudioObjectGetPropertyDataSize(deviceIdentifier, &property, 0, nil, &size)
if (status != kAudioHardwareNoError) || (size == 0) { return }
buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(size))
status = AudioObjectGetPropertyData(deviceIdentifier, &property, 0, nil, &size, buffer)
if let string: String = String(bytesNoCopy: buffer, length: Int(size - 1), encoding: String.Encoding.utf8, freeWhenDone: true) {
name = string
} else {
return
}// end if result can covert to string or not
property.mSelector = kAudioDevicePropertyDeviceUID
size = UInt32(MemoryLayout<CFString>.size);
var cfStringUID: CFString = "" as CFString
status = AudioObjectGetPropertyData(deviceIdentifier, &property, 0, nil, &size, &cfStringUID)
UID = String(cfStringUID)
if UID.count == 0 {
return
}// end if result can convert to string or not
if device(deviceIdentifier, haveDirectionStream: kAudioDevicePropertyScopeInput) {
inputDevices[name] = DeviceID(deviceID: deviceIdentifier, UID: UID)
}
if device(deviceIdentifier, haveDirectionStream: kAudioDevicePropertyScopeOutput) {
outputDevices[name] = DeviceID(deviceID: deviceIdentifier, UID: UID)
}
}// end deviceInfo
private func device(_ deviceIdentifier: AudioDeviceID, haveDirectionStream direction: AudioObjectPropertyScope) -> Bool {
var property: AudioObjectPropertyAddress = AudioObjectPropertyAddress(mSelector: kAudioDevicePropertyStreams, mScope: direction, mElement: kAudioObjectPropertyElementMaster)
var dataSize:UInt32 = 0
let status: OSStatus = AudioObjectGetPropertyDataSize(deviceIdentifier, &property, 0, nil, &dataSize)
if status == kAudioHardwareNoError {
let streamCount = UInt32(dataSize) / UInt32(MemoryLayout<AudioStreamID>.size)
if streamCount > 0 { return true }
}// end check stream have required direction
return false
}// end device
private func get(_ scope: AudioObjectPropertyScope) -> AudioDeviceID {
var property: AudioObjectPropertyAddress = AudioObjectPropertyAddress(mSelector: kAudioHardwarePropertyDevices, mScope: scope, mElement: kAudioObjectPropertyElementMaster)
var identifier: AudioDeviceID = 0
var size: UInt32 = 0
let status: OSStatus = AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &property, 0, nil, &size, &identifier)
if status == kAudioHardwareNoError {
return identifier
}
return 0
}// end func get
static private func get(deviceIdentifierOf selector: AudioObjectPropertyScope) -> AudioDeviceID {
var property: AudioObjectPropertyAddress = AudioObjectPropertyAddress(mSelector: selector, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster)
var identifier: AudioDeviceID = 0
var size: UInt32 = 0
let status: OSStatus = AudioObjectGetPropertyDataSize(AudioObjectID(kAudioObjectSystemObject), &property, 0, nil, &size)
if status == kAudioHardwareNoError && Int(size) / MemoryLayout<AudioDeviceID>.size == 1 {
AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &property, 0, nil, &size, &identifier)
return identifier
}// end if no error
return 0
}// end func get
static private func get(nameOf deviceIdentifier: AudioDeviceID) -> String {
var property: AudioObjectPropertyAddress = AudioObjectPropertyAddress(mSelector: kAudioDevicePropertyDeviceName, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster)
var buffer: UnsafeMutablePointer<UInt8>
var size: UInt32 = 0
var status: OSStatus = AudioObjectGetPropertyDataSize(deviceIdentifier, &property, 0, nil, &size)
if status == kAudioHardwareNoError && size > 0 {
buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(size))
status = AudioObjectGetPropertyData(deviceIdentifier, &property, 0, nil, &size, buffer)
if status == kAudioHardwareNoError, let deviceName: String = String(bytesNoCopy: buffer, length: Int(size - 1), encoding: String.Encoding.utf8, freeWhenDone: true) {
return deviceName
}// end if make device name is success
}// end if no error
return String()
}// end func get nameOf
static private func get(deviceUniqueIdentifierOf deviceIdentifier: AudioDeviceID) -> String {
var property: AudioObjectPropertyAddress = AudioObjectPropertyAddress(mSelector: kAudioDevicePropertyDeviceUID, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster)
var uid: CFString = "" as CFString
var size: UInt32 = UInt32(MemoryLayout<CFString>.size)
let status: OSStatus = AudioObjectGetPropertyData(deviceIdentifier, &property, 0, nil, &size, &uid)
if status == kAudioHardwareNoError {
return String(uid)
}
return String()
}// end func get device unique identifier
// MARK: - Delegates
}// end class AudioDevices
@chajka
Copy link
Author

chajka commented Dec 30, 2018

Swift class make a input and output device dictionary associating device name with DeviceIdentifier (for CoreAudio) and DeviceUniqueIdentifier (for NSSound playback)

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