AudioObjectGetPropertyData to get a list of input devices

后端 未结 4 1839
旧时难觅i
旧时难觅i 2020-11-30 04:15

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

4条回答
  •  旧时难觅i
    2020-11-30 04:44

    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 * 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 * AudioOutputDevices()
    {
        NSMutableArray *outputDevices = [NSMutableArray array];
    
        NSArray *allDevices = AllAudioDevices();
        for(NSNumber *device in allDevices) {
            if(DeviceSupportsOutput(device.unsignedIntValue)) {
                [outputDevices addObject:device];
            }
        }
    
        return outputDevices;
    }
    
    NSArray * 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(dataSize / sizeof(AudioDeviceID));
    
        AudioDeviceID *audioDevices = static_cast(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(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(keys), 
                                                                  reinterpret_cast(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;
    }
    

提交回复
热议问题