audio-device-enum.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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 &&
  10. astrstri(device, "wavtap") == NULL &&
  11. astrstri(device, "soundsiphon") == NULL &&
  12. astrstri(device, "ishowu") == NULL &&
  13. astrstri(device, "blackhole") == NULL &&
  14. astrstri(device, "loopback") == NULL &&
  15. astrstri(device, "groundcontrol") == NULL &&
  16. astrstri(device, "vbcable") == NULL;
  17. }
  18. static inline bool enum_success(OSStatus stat, const char *msg)
  19. {
  20. if (stat != noErr) {
  21. blog(LOG_WARNING, "[coreaudio_enum_devices] %s failed: %d", msg,
  22. (int)stat);
  23. return false;
  24. }
  25. return true;
  26. }
  27. typedef bool (*enum_device_proc_t)(void *param, CFStringRef cf_name,
  28. CFStringRef cf_uid, AudioDeviceID id);
  29. static bool coreaudio_enum_device(enum_device_proc_t proc, void *param,
  30. AudioDeviceID id)
  31. {
  32. UInt32 size = 0;
  33. CFStringRef cf_name = NULL;
  34. CFStringRef cf_uid = NULL;
  35. bool enum_next = true;
  36. OSStatus stat;
  37. AudioObjectPropertyAddress addr = {kAudioDevicePropertyStreams,
  38. kAudioDevicePropertyScopeInput,
  39. kAudioObjectPropertyElementMain};
  40. /* check to see if it's a mac input device */
  41. AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size);
  42. if (!size)
  43. return true;
  44. size = sizeof(CFStringRef);
  45. addr.mSelector = kAudioDevicePropertyDeviceUID;
  46. stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_uid);
  47. if (!enum_success(stat, "get audio device UID"))
  48. return true;
  49. addr.mSelector = kAudioDevicePropertyDeviceNameCFString;
  50. stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_name);
  51. if (!enum_success(stat, "get audio device name"))
  52. goto fail;
  53. enum_next = proc(param, cf_name, cf_uid, id);
  54. fail:
  55. if (cf_name)
  56. CFRelease(cf_name);
  57. if (cf_uid)
  58. CFRelease(cf_uid);
  59. return enum_next;
  60. }
  61. static void enum_devices(enum_device_proc_t proc, void *param)
  62. {
  63. AudioObjectPropertyAddress addr = {kAudioHardwarePropertyDevices,
  64. kAudioObjectPropertyScopeGlobal,
  65. kAudioObjectPropertyElementMain};
  66. UInt32 size = 0;
  67. UInt32 count;
  68. OSStatus stat;
  69. AudioDeviceID *ids;
  70. stat = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
  71. 0, NULL, &size);
  72. if (!enum_success(stat, "get kAudioObjectSystemObject data size"))
  73. return;
  74. ids = bmalloc(size);
  75. count = size / sizeof(AudioDeviceID);
  76. stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0,
  77. NULL, &size, ids);
  78. if (enum_success(stat, "get kAudioObjectSystemObject data"))
  79. for (UInt32 i = 0; i < count; i++)
  80. if (!coreaudio_enum_device(proc, param, ids[i]))
  81. break;
  82. bfree(ids);
  83. }
  84. struct add_data {
  85. struct device_list *list;
  86. bool input;
  87. };
  88. static bool coreaudio_enum_add_device(void *param, CFStringRef cf_name,
  89. CFStringRef cf_uid, AudioDeviceID id)
  90. {
  91. struct add_data *data = param;
  92. struct device_item item;
  93. memset(&item, 0, sizeof(item));
  94. if (!cfstr_copy_dstr(cf_name, kCFStringEncodingUTF8, &item.name))
  95. goto fail;
  96. if (!cfstr_copy_dstr(cf_uid, kCFStringEncodingUTF8, &item.value))
  97. goto fail;
  98. if (data->input || !device_is_input(item.value.array))
  99. device_list_add(data->list, &item);
  100. fail:
  101. device_item_free(&item);
  102. UNUSED_PARAMETER(id);
  103. return true;
  104. }
  105. void coreaudio_enum_devices(struct device_list *list, bool input)
  106. {
  107. struct add_data data = {list, input};
  108. enum_devices(coreaudio_enum_add_device, &data);
  109. }
  110. bool coreaudio_get_device_id(CFStringRef uid, AudioDeviceID *id)
  111. {
  112. AudioObjectPropertyAddress propertyAddress = {
  113. kAudioHardwarePropertyDeviceForUID,
  114. kAudioObjectPropertyScopeGlobal,
  115. kAudioObjectPropertyElementMain};
  116. AudioValueTranslation translation = {&uid, sizeof(CFStringRef), id,
  117. sizeof(AudioDeviceID)};
  118. UInt32 size = sizeof(translation);
  119. OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
  120. &propertyAddress, 0, NULL,
  121. &size, &translation);
  122. return result == noErr;
  123. }