audio-device-enum.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. #include <CoreFoundation/CFString.h>
  2. #include <CoreAudio/CoreAudio.h>
  3. #include <util/apple/cfstring-utils.h>
  4. #include "audio-device-enum.h"
  5. /* ugh, because mac has no means of capturing output, we have to basically
  6. * mark soundflower, wavtap and sound siphon as output devices. */
  7. static inline bool device_is_input(char *device)
  8. {
  9. return astrstri(device, "soundflower") == NULL && astrstri(device, "wavtap") == NULL &&
  10. astrstri(device, "soundsiphon") == NULL && astrstri(device, "ishowu") == NULL &&
  11. astrstri(device, "blackhole") == NULL && astrstri(device, "loopback") == NULL &&
  12. astrstri(device, "groundcontrol") == NULL && astrstri(device, "vbcable") == NULL;
  13. }
  14. static inline bool enum_success(OSStatus stat, const char *msg)
  15. {
  16. if (stat != noErr) {
  17. blog(LOG_WARNING, "[coreaudio_enum_devices] %s failed: %d", msg, (int)stat);
  18. return false;
  19. }
  20. return true;
  21. }
  22. typedef bool (*enum_device_proc_t)(void *param, CFStringRef cf_name, CFStringRef cf_uid, AudioDeviceID id);
  23. static bool coreaudio_enum_device(enum_device_proc_t proc, void *param, AudioDeviceID id)
  24. {
  25. UInt32 size = 0;
  26. CFStringRef cf_name = NULL;
  27. CFStringRef cf_uid = NULL;
  28. bool enum_next = true;
  29. OSStatus stat;
  30. AudioObjectPropertyAddress addr = {kAudioDevicePropertyStreams, kAudioDevicePropertyScopeInput,
  31. kAudioObjectPropertyElementMain};
  32. /* check to see if it's a mac input device */
  33. AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size);
  34. if (!size)
  35. return true;
  36. size = sizeof(CFStringRef);
  37. addr.mSelector = kAudioDevicePropertyDeviceUID;
  38. stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_uid);
  39. if (!enum_success(stat, "get audio device UID"))
  40. return true;
  41. addr.mSelector = kAudioDevicePropertyDeviceNameCFString;
  42. stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_name);
  43. if (!enum_success(stat, "get audio device name"))
  44. goto fail;
  45. enum_next = proc(param, cf_name, cf_uid, id);
  46. fail:
  47. if (cf_name)
  48. CFRelease(cf_name);
  49. if (cf_uid)
  50. CFRelease(cf_uid);
  51. return enum_next;
  52. }
  53. static void enum_devices(enum_device_proc_t proc, void *param)
  54. {
  55. AudioObjectPropertyAddress addr = {kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
  56. kAudioObjectPropertyElementMain};
  57. UInt32 size = 0;
  58. UInt32 count;
  59. OSStatus stat;
  60. AudioDeviceID *ids;
  61. stat = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, NULL, &size);
  62. if (!enum_success(stat, "get kAudioObjectSystemObject data size"))
  63. return;
  64. ids = bmalloc(size);
  65. count = size / sizeof(AudioDeviceID);
  66. stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, ids);
  67. if (enum_success(stat, "get kAudioObjectSystemObject data"))
  68. for (UInt32 i = 0; i < count; i++)
  69. if (!coreaudio_enum_device(proc, param, ids[i]))
  70. break;
  71. bfree(ids);
  72. }
  73. struct add_data {
  74. struct device_list *list;
  75. bool input;
  76. };
  77. static bool coreaudio_enum_add_device(void *param, CFStringRef cf_name, CFStringRef cf_uid, AudioDeviceID id)
  78. {
  79. struct add_data *data = param;
  80. struct device_item item;
  81. memset(&item, 0, sizeof(item));
  82. if (!cfstr_copy_dstr(cf_name, kCFStringEncodingUTF8, &item.name))
  83. goto fail;
  84. if (!cfstr_copy_dstr(cf_uid, kCFStringEncodingUTF8, &item.value))
  85. goto fail;
  86. if (data->input || !device_is_input(item.value.array))
  87. device_list_add(data->list, &item);
  88. fail:
  89. device_item_free(&item);
  90. UNUSED_PARAMETER(id);
  91. return true;
  92. }
  93. void coreaudio_enum_devices(struct device_list *list, bool input)
  94. {
  95. struct add_data data = {list, input};
  96. enum_devices(coreaudio_enum_add_device, &data);
  97. }
  98. bool coreaudio_get_device_id(CFStringRef uid, AudioDeviceID *id)
  99. {
  100. AudioObjectPropertyAddress propertyAddress = {kAudioHardwarePropertyDeviceForUID,
  101. kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};
  102. AudioValueTranslation translation = {&uid, sizeof(CFStringRef), id, sizeof(AudioDeviceID)};
  103. UInt32 size = sizeof(translation);
  104. OSStatus result =
  105. AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &translation);
  106. return result == noErr;
  107. }