// player/audio_device_macos.go — macOS Core Audio output device enumeration & selection. //go:build darwin && !ios package player /* #cgo LDFLAGS: -framework CoreAudio -framework CoreFoundation #include #include #include static AudioDeviceID caDefaultOutput() { AudioObjectPropertyAddress addr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain, }; AudioDeviceID id = kAudioObjectUnknown; UInt32 sz = sizeof(id); AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &sz, &id); return id; } static int caSetDefaultOutput(AudioDeviceID id) { AudioObjectPropertyAddress addr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain, }; return AudioObjectSetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, sizeof(id), &id) == noErr ? 0 : -1; } static int caOutputChannels(AudioDeviceID id) { AudioObjectPropertyAddress addr = { kAudioDevicePropertyStreamConfiguration, kAudioObjectPropertyScopeOutput, kAudioObjectPropertyElementMain, }; UInt32 sz = 0; if (AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &sz) != noErr || sz == 0) return 0; AudioBufferList *bl = (AudioBufferList *)malloc(sz); if (AudioObjectGetPropertyData(id, &addr, 0, NULL, &sz, bl) != noErr) { free(bl); return 0; } int ch = 0; for (UInt32 i = 0; i < bl->mNumberBuffers; i++) ch += bl->mBuffers[i].mNumberChannels; free(bl); return ch; } static char* caStr(CFStringRef s) { if (s == NULL) return NULL; CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(s), kCFStringEncodingUTF8)+1; char *buf = (char*)malloc(len); CFStringGetCString(s, buf, len, kCFStringEncodingUTF8); CFRelease(s); return buf; } static char* caDevName(AudioDeviceID id) { AudioObjectPropertyAddress a = {kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain}; CFStringRef s = NULL; UInt32 sz = sizeof(s); if (AudioObjectGetPropertyData(id, &a, 0, NULL, &sz, &s) != noErr) return NULL; return caStr(s); } static char* caDevUID(AudioDeviceID id) { AudioObjectPropertyAddress a = {kAudioDevicePropertyDeviceUID, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain}; CFStringRef s = NULL; UInt32 sz = sizeof(s); if (AudioObjectGetPropertyData(id, &a, 0, NULL, &sz, &s) != noErr) return NULL; return caStr(s); } typedef struct { AudioDeviceID id; char *name; char *uid; } CADev; static int caListOutputs(CADev **out, int *count) { AudioObjectPropertyAddress a = {kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain}; UInt32 sz = 0; if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &a, 0, NULL, &sz) != noErr) { *count=0; return -1; } int n = sz / sizeof(AudioDeviceID); AudioDeviceID *ids = (AudioDeviceID*)malloc(sz); if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &a, 0, NULL, &sz, ids) != noErr) { free(ids); *count=0; return -1; } int oc = 0; for (int i=0; i0) oc++; } CADev *devs = (CADev*)calloc(oc, sizeof(CADev)); int j = 0; for (int i=0; i