AudioObjectGetPropertyData 获取输入设备列表

发布于 2024-10-10 03:05:10 字数 731 浏览 0 评论 0原文

如何在 OS X 中利用 AudioObjectGetPropertyData 检索系统输入设备的列表?我目前有以下虚拟代码用于检索全局设备列表:

AudioDeviceID devices[12];
UInt32 arraySize = sizeof(devices);

AudioObjectPropertyAddress thePropertyAddress = { kAudioHardwarePropertyDevices, 
                                                  kAudioObjectPropertyScopeGlobal, 
                                                  kAudioObjectPropertyElementMaster };

AudioObjectGetPropertyData(kAudioObjectSystemObject, 
                           &thePropertyAddress, 
                           0, 
                           NULL, 
                           &arraySize, 
                           &devices);

How do I utilize AudioObjectGetPropertyData in OS X to retrieve a list of the system's input devices? I currently have the following dummy code for retrieving a global list of devices:

AudioDeviceID devices[12];
UInt32 arraySize = sizeof(devices);

AudioObjectPropertyAddress thePropertyAddress = { kAudioHardwarePropertyDevices, 
                                                  kAudioObjectPropertyScopeGlobal, 
                                                  kAudioObjectPropertyElementMaster };

AudioObjectGetPropertyData(kAudioObjectSystemObject, 
                           &thePropertyAddress, 
                           0, 
                           NULL, 
                           &arraySize, 
                           &devices);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

明媚如初 2024-10-17 03:05:10

要确定设备是否是输入设备,您需要检查它是否有任何输入通道。

以下是从 Objective-C 类此处修改的代码:

static BOOL DeviceHasBuffersInScope(AudioObjectID deviceID, AudioObjectPropertyScope scope)
{
    NSCParameterAssert(deviceID != kAudioObjectUnknown);

    AudioObjectPropertyAddress propertyAddress = {
        .mSelector  = kAudioDevicePropertyStreamConfiguration,
        .mScope     = scope,
        .mElement   = kAudioObjectPropertyElementWildcard
    };

    UInt32 dataSize = 0;
    OSStatus result = AudioObjectGetPropertyDataSize(deviceID, &propertyAddress, 0, NULL, &dataSize);
    if(result != kAudioHardwareNoError) {
        return NO;
    }

    AudioBufferList *bufferList = malloc(dataSize);
    if(!bufferList) {
        return NO;
    }

    result = AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, NULL, &dataSize, bufferList);
    if(result != kAudioHardwareNoError) {
        free(bufferList);
        return NO;
    }

    BOOL supportsScope = bufferList->mNumberBuffers > 0;
    free(bufferList);

    return supportsScope;
}

static BOOL DeviceSupportsInput(AudioObjectID deviceID)
{
    return DeviceHasBuffersInScope(deviceID, kAudioObjectPropertyScopeInput);
}

static BOOL DeviceSupportsOutput(AudioObjectID deviceID)
{
    return DeviceHasBuffersInScope(deviceID, kAudioObjectPropertyScopeOutput);
}

NSArray<NSNumber *> * AllAudioDevices()
{
    AudioObjectPropertyAddress propertyAddress = {
        .mSelector  = kAudioHardwarePropertyDevices,
        .mScope     = kAudioObjectPropertyScopeGlobal,
        .mElement   = kAudioObjectPropertyElementWildcard
    };

    UInt32 dataSize = 0;
    OSStatus result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
    if(result != kAudioHardwareNoError) {
        return nil;
    }

    AudioObjectID *deviceIDs = (AudioObjectID *)malloc(dataSize);
    if(!deviceIDs) {
        return nil;
    }

    result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, deviceIDs);
    if(kAudioHardwareNoError != result) {
        free(deviceIDs);
        return nil;
    }

    NSMutableArray *allDevices = [NSMutableArray array];
    for(NSInteger i = 0; i < (NSInteger)(dataSize / sizeof(AudioObjectID)); ++i) {
        [allDevices addObject:[NSNumber numberWithUnsignedInt:deviceIDs[i]]];
    }

    free(deviceIDs);

    return allDevices;
}

NSArray<NSNumber *> * AudioOutputDevices()
{
    NSMutableArray *outputDevices = [NSMutableArray array];

    NSArray *allDevices = AllAudioDevices();
    for(NSNumber *device in allDevices) {
        if(DeviceSupportsOutput(device.unsignedIntValue)) {
            [outputDevices addObject:device];
        }
    }

    return outputDevices;
}

NSArray<NSNumber *> * AudioInputDevices()
{
    NSMutableArray *inputDevices = [NSMutableArray array];

    NSArray *allDevices = AllAudioDevices();
    for(NSNumber *device in allDevices) {
        if(DeviceSupportsInput(device.unsignedIntValue)) {
            [inputDevices addObject:device];
        }
    }

    return inputDevices;
}

< strong>原始代码片段是:

这是我转换的一些代码,应该可以工作(尽管未经测试):

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;
}

To determine if a device is an input device you need to check and see if it has any input channels.

Here is code modified from the Objective-C class here:

static BOOL DeviceHasBuffersInScope(AudioObjectID deviceID, AudioObjectPropertyScope scope)
{
    NSCParameterAssert(deviceID != kAudioObjectUnknown);

    AudioObjectPropertyAddress propertyAddress = {
        .mSelector  = kAudioDevicePropertyStreamConfiguration,
        .mScope     = scope,
        .mElement   = kAudioObjectPropertyElementWildcard
    };

    UInt32 dataSize = 0;
    OSStatus result = AudioObjectGetPropertyDataSize(deviceID, &propertyAddress, 0, NULL, &dataSize);
    if(result != kAudioHardwareNoError) {
        return NO;
    }

    AudioBufferList *bufferList = malloc(dataSize);
    if(!bufferList) {
        return NO;
    }

    result = AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, NULL, &dataSize, bufferList);
    if(result != kAudioHardwareNoError) {
        free(bufferList);
        return NO;
    }

    BOOL supportsScope = bufferList->mNumberBuffers > 0;
    free(bufferList);

    return supportsScope;
}

static BOOL DeviceSupportsInput(AudioObjectID deviceID)
{
    return DeviceHasBuffersInScope(deviceID, kAudioObjectPropertyScopeInput);
}

static BOOL DeviceSupportsOutput(AudioObjectID deviceID)
{
    return DeviceHasBuffersInScope(deviceID, kAudioObjectPropertyScopeOutput);
}

NSArray<NSNumber *> * AllAudioDevices()
{
    AudioObjectPropertyAddress propertyAddress = {
        .mSelector  = kAudioHardwarePropertyDevices,
        .mScope     = kAudioObjectPropertyScopeGlobal,
        .mElement   = kAudioObjectPropertyElementWildcard
    };

    UInt32 dataSize = 0;
    OSStatus result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
    if(result != kAudioHardwareNoError) {
        return nil;
    }

    AudioObjectID *deviceIDs = (AudioObjectID *)malloc(dataSize);
    if(!deviceIDs) {
        return nil;
    }

    result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, deviceIDs);
    if(kAudioHardwareNoError != result) {
        free(deviceIDs);
        return nil;
    }

    NSMutableArray *allDevices = [NSMutableArray array];
    for(NSInteger i = 0; i < (NSInteger)(dataSize / sizeof(AudioObjectID)); ++i) {
        [allDevices addObject:[NSNumber numberWithUnsignedInt:deviceIDs[i]]];
    }

    free(deviceIDs);

    return allDevices;
}

NSArray<NSNumber *> * AudioOutputDevices()
{
    NSMutableArray *outputDevices = [NSMutableArray array];

    NSArray *allDevices = AllAudioDevices();
    for(NSNumber *device in allDevices) {
        if(DeviceSupportsOutput(device.unsignedIntValue)) {
            [outputDevices addObject:device];
        }
    }

    return outputDevices;
}

NSArray<NSNumber *> * AudioInputDevices()
{
    NSMutableArray *inputDevices = [NSMutableArray array];

    NSArray *allDevices = AllAudioDevices();
    for(NSNumber *device in allDevices) {
        if(DeviceSupportsInput(device.unsignedIntValue)) {
            [inputDevices addObject:device];
        }
    }

    return inputDevices;
}

The original code snippet was:

Here is some code I converted that should work (untested though):

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;
}
§对你不离不弃 2024-10-17 03:05:10

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
}

Swift 3.0 Xcode 8 Beta 5

Struggled with this for a good while but this seems to work fine for now.

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
}
云朵有点甜 2024-10-17 03:05:10

这是我发现的在迭代 CoreAudio 设备 ID 时对输入和输出进行排序的最佳方法。

这只是循环内的部分:

    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 在 xcode/Extras/CoreAudio/HAL/HPBase 中提供了他们的 C++ HAL 接口的源代码,这是解决这个问题的关键。

Here's the best way I have found to sort inputs from outputs when iterating through CoreAudio device ids.

This is just the part inside the loop:

    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;
    }

I hope this helps someone else, I ended finding out that Apple provides the source for their C+++ HAL interface in xcode/Extras/CoreAudio/HAL/HPBase which was key in figuring this out.

番薯 2024-10-17 03:05:10

我稍微修改了“sbooth”提交的代码,以打印所有输入设备以及编号。每个设备的缓冲区数量和数量。每个缓冲区的通道数。

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;
}

I have slightly modified the code submitted by "sbooth" to print all the input devices along with the no. of buffers for each device and no. of channels for each buffer.

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文