Created
December 30, 2018 08:59
-
-
Save chajka/f835ad4e78f7843a53b4b04f25387bff to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Swift class make a input and output device dictionary associating device name with DeviceIdentifier (for CoreAudio) and DeviceUniqueIdentifier (for NSSound playback)