2011-01-01 35 views
9

Làm cách nào để sử dụng AudioObjectGetPropertyData trong OS X để truy xuất danh sách các thiết bị đầu vào của hệ thống? Tôi hiện có mã giả sau đây để lấy một danh sách toàn cầu của thiết bị:AudioObjectGetPropertyData để lấy danh sách thiết bị đầu vào

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

AudioObjectPropertyAddress thePropertyAddress = { kAudioHardwarePropertyDevices, 
                kAudioObjectPropertyScopeGlobal, 
                kAudioObjectPropertyElementMaster }; 

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

Trả lời

22

Để xác định xem một thiết bị là một thiết bị đầu vào bạn cần phải kiểm tra và xem nếu nó có bất kỳ kênh đầu vào. Đây là một số mã tôi chuyển đổi mà nên làm việc (chưa được kiểm tra mặc dù):

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

Tôi chỉ muốn cảm ơn bạn vì câu trả lời và đoạn mã này - nó cực kỳ hữu ích! – iKenndac

+0

Bạn được chào đón! – sbooth

+0

Công trình này tuyệt vời! Nó sẽ được đánh dấu là câu trả lời. – rocky

7

Đây là cách tốt nhất tôi đã tìm thấy để sắp xếp đầu vào từ đầu ra khi lặp qua id thiết bị CoreAudio.

Đây mới chỉ là một phần bên trong vòng lặp:

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

Tôi hy vọng điều này sẽ giúp người khác, tôi đã kết thúc việc tìm kiếm ra mà Apple cung cấp nguồn cho C+++ HAL giao diện của họ trong xcode/Extras/CoreAudio/HAL/HPBase là chìa khóa trong việc tìm ra điều này.

4

Tôi đã sửa đổi một chút mã do "sbooth" gửi để in tất cả các thiết bị đầu vào cùng với số không. bộ đệm cho mỗi thiết bị và không. của các kênh cho mỗi bộ đệm.

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

Làm thế nào để bạn làm điều này trong Swift? – Boggartfly

5

Swift 3.0 Xcode 8 Beta 5

vật lộn với điều này trong một thời gian tốt nhưng điều này dường như làm việc tốt cho bây giờ.

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 
} 
Các vấn đề liên quan