2011-01-01 8 views
9

Wie verwende ich AudioObjectGetPropertyData in OS X, um eine Liste der Eingabegeräte des Systems abzurufen? Im Moment habe ich den folgenden Dummy-Code für eine globale Liste der Geräte abrufen:AudioObjectGetPropertyData, um eine Liste von Eingabegeräten zu erhalten

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

AudioObjectPropertyAddress thePropertyAddress = { kAudioHardwarePropertyDevices, 
                kAudioObjectPropertyScopeGlobal, 
                kAudioObjectPropertyElementMaster }; 

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

Antwort

22

Um zu bestimmen, ob ein Gerät ein Eingabegerät ist, Sie müssen prüfen und sehen, ob es irgendwelche Eingangskanäle. Hier ist ein Code umgewandelt ich, dass sollte funktionieren (ungetestet obwohl):

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

Ich wollte nur sagen, danke für diese Antwort und Code-Schnipsel - es ist unglaublich nützlich! – iKenndac

+0

Gern geschehen! – sbooth

+0

Das funktioniert super! Es sollte als Antwort markiert sein. – rocky

7

Hier ist der beste Weg, die ich gefunden habe Eingänge von den Ausgängen sortieren, wenn sie durch CoreAudio- Geräte-IDs laufen. Diese

ist nur der Teil innerhalb der Schleife:

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

Ich hoffe, das hilft jemand anderes, ich am Ende herauszufinden, dass Apple die Quelle für ihr C bietet +++ HAL-Schnittstelle in Xcode/Extras/Core Audio/HAL/HPBase, das war der Schlüssel, um dies herauszufinden.

4

Ich habe den Code von "sbooth" leicht geändert, um alle Eingabegeräte zusammen mit der Nr. Zu drucken. von Puffern für jedes Gerät und nein. von Kanälen für jeden Puffer.

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

Wie machst du das in Swift? – Boggartfly

5

Swift 3.0 Xcode 8 Beta 5

mit diesem Kämpfte für eine gute Weile, aber das scheint jetzt zu funktionieren.

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 
} 
Verwandte Themen