AudioObjectGetPropertyData для получения списка устройств ввода
Как я могу использовать AudioObjectGetPropertyData
в OS X, чтобы получить список устройств ввода системы? В настоящее время у меня есть следующий фиктивный код для извлечения глобального списка устройств:
AudioDeviceID devices[12];
UInt32 arraySize = sizeof(devices);
AudioObjectPropertyAddress thePropertyAddress = { kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
AudioObjectGetPropertyData(kAudioObjectSystemObject,
&thePropertyAddress,
0,
NULL,
&arraySize,
&devices);
4 ответа
Чтобы определить, является ли устройство устройством ввода, необходимо проверить и проверить, есть ли у него какие-либо входные каналы. Вот код, который я преобразовал, который должен работать (хотя и не проверенный):
CFArrayRef CreateInputDeviceArray()
{
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
UInt32 dataSize = 0;
OSStatus status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
if(kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyDataSize (kAudioHardwarePropertyDevices) failed: %i\n", status);
return NULL;
}
UInt32 deviceCount = static_cast<UInt32>(dataSize / sizeof(AudioDeviceID));
AudioDeviceID *audioDevices = static_cast<AudioDeviceID *>(malloc(dataSize));
if(NULL == audioDevices) {
fputs("Unable to allocate memory", stderr);
return NULL;
}
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, audioDevices);
if(kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyData (kAudioHardwarePropertyDevices) failed: %i\n", status);
free(audioDevices), audioDevices = NULL;
return NULL;
}
CFMutableArrayRef inputDeviceArray = CFArrayCreateMutable(kCFAllocatorDefault, deviceCount, &kCFTypeArrayCallBacks);
if(NULL == inputDeviceArray) {
fputs("CFArrayCreateMutable failed", stderr);
free(audioDevices), audioDevices = NULL;
return NULL;
}
// Iterate through all the devices and determine which are input-capable
propertyAddress.mScope = kAudioDevicePropertyScopeInput;
for(UInt32 i = 0; i < deviceCount; ++i) {
// Query device UID
CFStringRef deviceUID = NULL;
dataSize = sizeof(deviceUID);
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceUID);
if(kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID) failed: %i\n", status);
continue;
}
// Query device name
CFStringRef deviceName = NULL;
dataSize = sizeof(deviceName);
propertyAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceName);
if(kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyData (kAudioDevicePropertyDeviceNameCFString) failed: %i\n", status);
continue;
}
// Query device manufacturer
CFStringRef deviceManufacturer = NULL;
dataSize = sizeof(deviceManufacturer);
propertyAddress.mSelector = kAudioDevicePropertyDeviceManufacturerCFString;
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceManufacturer);
if(kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyData (kAudioDevicePropertyDeviceManufacturerCFString) failed: %i\n", status);
continue;
}
// Determine if the device is an input device (it is an input device if it has input channels)
dataSize = 0;
propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
status = AudioObjectGetPropertyDataSize(audioDevices[i], &propertyAddress, 0, NULL, &dataSize);
if(kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyDataSize (kAudioDevicePropertyStreamConfiguration) failed: %i\n", status);
continue;
}
AudioBufferList *bufferList = static_cast<AudioBufferList *>(malloc(dataSize));
if(NULL == bufferList) {
fputs("Unable to allocate memory", stderr);
break;
}
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, bufferList);
if(kAudioHardwareNoError != status || 0 == bufferList->mNumberBuffers) {
if(kAudioHardwareNoError != status)
fprintf(stderr, "AudioObjectGetPropertyData (kAudioDevicePropertyStreamConfiguration) failed: %i\n", status);
free(bufferList), bufferList = NULL;
continue;
}
free(bufferList), bufferList = NULL;
// Add a dictionary for this device to the array of input devices
CFStringRef keys [] = { CFSTR("deviceUID"), CFSTR("deviceName"), CFSTR("deviceManufacturer") };
CFStringRef values [] = { deviceUID, deviceName, deviceManufacturer };
CFDictionaryRef deviceDictionary = CFDictionaryCreate(kCFAllocatorDefault,
reinterpret_cast<const void **>(keys),
reinterpret_cast<const void **>(values),
3,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFArrayAppendValue(inputDeviceArray, deviceDictionary);
CFRelease(deviceDictionary), deviceDictionary = NULL;
}
free(audioDevices), audioDevices = NULL;
// Return a non-mutable copy of the array
CFArrayRef copy = CFArrayCreateCopy(kCFAllocatorDefault, inputDeviceArray);
CFRelease(inputDeviceArray), inputDeviceArray = NULL;
return copy;
}
Swift 3.0 Xcode 8 Beta 5
С этим долго боролись, но сейчас вроде бы все работает.
func handle(_ errorCode: OSStatus) throws {
if errorCode != kAudioHardwareNoError {
let error = NSError(domain: NSOSStatusErrorDomain, code: Int(errorCode), userInfo: [NSLocalizedDescriptionKey : "CAError: \(errorCode)" ])
NSApplication.shared().presentError(error)
throw error
}
}
func getInputDevices() throws -> [AudioDeviceID] {
var inputDevices: [AudioDeviceID] = []
// Construct the address of the property which holds all available devices
var devicesPropertyAddress = AudioObjectPropertyAddress(mSelector: kAudioHardwarePropertyDevices, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster)
var propertySize = UInt32(0)
// Get the size of the property in the kAudioObjectSystemObject so we can make space to store it
try handle(AudioObjectGetPropertyDataSize(AudioObjectID(kAudioObjectSystemObject), &devicesPropertyAddress, 0, nil, &propertySize))
// Get the number of devices by dividing the property address by the size of AudioDeviceIDs
let numberOfDevices = Int(propertySize) / sizeof(AudioDeviceID.self)
// Create space to store the values
var deviceIDs: [AudioDeviceID] = []
for _ in 0 ..< numberOfDevices {
deviceIDs.append(AudioDeviceID())
}
// Get the available devices
try handle(AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &devicesPropertyAddress, 0, nil, &propertySize, &deviceIDs))
// Iterate
for id in deviceIDs {
// Get the device name for fun
var name: CFString = ""
var propertySize = UInt32(sizeof(CFString.self))
var deviceNamePropertyAddress = AudioObjectPropertyAddress(mSelector: kAudioDevicePropertyDeviceNameCFString, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster)
try handle(AudioObjectGetPropertyData(id, &deviceNamePropertyAddress, 0, nil, &propertySize, &name))
// Check the input scope of the device for any channels. That would mean it's an input device
// Get the stream configuration of the device. It's a list of audio buffers.
var streamConfigAddress = AudioObjectPropertyAddress(mSelector: kAudioDevicePropertyStreamConfiguration, mScope: kAudioDevicePropertyScopeInput, mElement: 0)
// Get the size so we can make room again
try handle(AudioObjectGetPropertyDataSize(id, &streamConfigAddress, 0, nil, &propertySize))
// Create a buffer list with the property size we just got and let core audio fill it
let audioBufferList = AudioBufferList.allocate(maximumBuffers: Int(propertySize))
try handle(AudioObjectGetPropertyData(id, &streamConfigAddress, 0, nil, &propertySize, audioBufferList.unsafeMutablePointer))
// Get the number of channels in all the audio buffers in the audio buffer list
var channelCount = 0
for i in 0 ..< Int(audioBufferList.unsafeMutablePointer.pointee.mNumberBuffers) {
channelCount = channelCount + Int(audioBufferList[i].mNumberChannels)
}
free(audioBufferList.unsafeMutablePointer)
// If there are channels, it's an input device
if channelCount > 0 {
Swift.print("Found input device '\(name)' with \(channelCount) channels")
inputDevices.append(id)
}
}
return inputDevices
}
Вот лучший способ, который я нашел для сортировки входных данных из выходных данных при переборе идентификаторов устройств CoreAudio.
Это просто часть внутри цикла:
BOOL isMic = NO;
BOOL isSpeaker = NO;
AudioDeviceID device = audioDevices[i];
// Determine direction of the device by asking for the number of input or
// output streams.
propertyAddress.mSelector = kAudioDevicePropertyStreams;
propertyAddress.mScope = kAudioDevicePropertyScopeInput;
UInt32 dataSize = 0;
OSStatus status = AudioObjectGetPropertyDataSize(device,
&propertyAddress,
0,
NULL,
&dataSize);
UInt32 streamCount = dataSize / sizeof(AudioStreamID);
if (streamCount > 0)
{
isMic = YES;
}
propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
dataSize = 0;
status = AudioObjectGetPropertyDataSize(device,
&propertyAddress,
0,
NULL,
&dataSize);
streamCount = dataSize / sizeof(AudioStreamID);
if (streamCount > 0)
{
isSpeaker = YES;
}
Я надеюсь, что это поможет кому-то еще, я закончил, узнав, что Apple предоставляет источник для их интерфейса C+++ HAL в xcode/Extras/CoreAudio/HAL/HPBase, который был ключевым в выяснении этого.
Я немного изменил код, представленный "sbooth", чтобы напечатать все устройства ввода вместе с no. буферов для каждого устройства и нет. каналов для каждого буфера.
CFArrayRef CreateInputDeviceArray()
{
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
UInt32 dataSize = 0;
OSStatus status = AudioHardwareServiceGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
if(kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyDataSize (kAudioHardwarePropertyDevices) failed: %i\n", status);
return NULL;
}
UInt32 deviceCount = (UInt32)(dataSize / sizeof(AudioDeviceID));
AudioDeviceID *audioDevices = (AudioDeviceID *)(malloc(dataSize));
if(NULL == audioDevices) {
fputs("Unable to allocate memory", stderr);
return NULL;
}
status = AudioHardwareServiceGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, audioDevices);
if(kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyData (kAudioHardwarePropertyDevices) failed: %i\n", status);
free(audioDevices), audioDevices = NULL;
return NULL;
}
CFMutableArrayRef inputDeviceArray = CFArrayCreateMutable(kCFAllocatorDefault, deviceCount, &kCFTypeArrayCallBacks);
if(NULL == inputDeviceArray) {
fputs("CFArrayCreateMutable failed", stderr);
free(audioDevices), audioDevices = NULL;
return NULL;
}
// Iterate through all the devices and determine which are input-capable
propertyAddress.mScope = kAudioDevicePropertyScopeInput;
for(UInt32 i = 0; i < deviceCount; ++i) {
// Query device UID
CFStringRef deviceUID = NULL;
dataSize = sizeof(deviceUID);
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
status = AudioHardwareServiceGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceUID);
if(kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID) failed: %i\n", status);
continue;
}
// Query device name
CFStringRef deviceName = NULL;
dataSize = sizeof(deviceName);
propertyAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
status = AudioHardwareServiceGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceName);
if(kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyData (kAudioDevicePropertyDeviceNameCFString) failed: %i\n", status);
continue;
}
// Query device manufacturer
CFStringRef deviceManufacturer = NULL;
dataSize = sizeof(deviceManufacturer);
propertyAddress.mSelector = kAudioDevicePropertyDeviceManufacturerCFString;
status = AudioHardwareServiceGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceManufacturer);
if(kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyData (kAudioDevicePropertyDeviceManufacturerCFString) failed: %i\n", status);
continue;
}
// Determine if the device is an input device (it is an input device if it has input channels)
dataSize = 0;
propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
status = AudioHardwareServiceGetPropertyDataSize(audioDevices[i], &propertyAddress, 0, NULL, &dataSize);
if(kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyDataSize (kAudioDevicePropertyStreamConfiguration) failed: %i\n", status);
continue;
}
AudioBufferList *bufferList = (AudioBufferList *)(malloc(dataSize));
if(NULL == bufferList) {
fputs("Unable to allocate memory", stderr);
break;
}
status = AudioHardwareServiceGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, bufferList);
if(kAudioHardwareNoError != status || 0 == bufferList->mNumberBuffers) {
if(kAudioHardwareNoError != status)
fprintf(stderr, "AudioObjectGetPropertyData (kAudioDevicePropertyStreamConfiguration) failed: %i\n", status);
free(bufferList), bufferList = NULL;
continue;
}
UInt32 numBuffers = bufferList->mNumberBuffers;
printf("\n\ndeviceUID:%s \tdeviceName: %s\ndeviceManufacturer: %s\t#Buffers:%d", \
CFStringGetCStringPtr(deviceUID, kCFStringEncodingMacRoman),\
CFStringGetCStringPtr(deviceName, kCFStringEncodingMacRoman), \
CFStringGetCStringPtr(deviceManufacturer, kCFStringEncodingMacRoman), \
numBuffers
);
for (UInt8 j = 0; j < numBuffers; j++) {
AudioBuffer ab = bufferList->mBuffers[j];
printf("\n#Channels: %d DataByteSize: %d", ab.mNumberChannels, ab.mDataByteSize);
}
free(bufferList), bufferList = NULL;
// Add a dictionary for this device to the array of input devices
CFStringRef keys [] = { CFSTR("deviceUID"), CFSTR("deviceName"), CFSTR("deviceManufacturer") };
CFStringRef values [] = { deviceUID, deviceName, deviceManufacturer };
CFDictionaryRef deviceDictionary = CFDictionaryCreate(kCFAllocatorDefault,
(const void **)(keys),
(const void **)(values),
3,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFArrayAppendValue(inputDeviceArray, deviceDictionary);
CFRelease(deviceDictionary), deviceDictionary = NULL;
}
free(audioDevices), audioDevices = NULL;
// Return a non-mutable copy of the array
CFArrayRef copy = CFArrayCreateCopy(kCFAllocatorDefault, inputDeviceArray);
CFRelease(inputDeviceArray), inputDeviceArray = NULL;
return copy;
}