Commit | Line | Data |
---|---|---|
f5fd9a6b RK |
1 | /* |
2 | * This file is part of DisOrder | |
3 | * Copyright (C) 2009 Richard Kettlewell | |
4 | * | |
5 | * This program is free software: you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 3 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | /** @file lib/coreaudio.c | |
8d251217 | 19 | * @brief Support for Apple Core Audio |
f5fd9a6b RK |
20 | */ |
21 | ||
22 | #include "common.h" | |
23 | ||
24 | #if HAVE_COREAUDIO_AUDIOHARDWARE_H | |
25 | ||
26 | #include "coreaudio.h" | |
27 | #include "log.h" | |
ca6b4a12 | 28 | #include "printf.h" |
f5fd9a6b | 29 | #include <CoreFoundation/CFString.h> |
ca6b4a12 RK |
30 | /* evil bodge to work around broken header file */ |
31 | #undef error | |
32 | #include <CoreServices/CoreServices.h> | |
33 | #include <stdarg.h> | |
34 | ||
35 | /** @brief Report an error with an OSStatus */ | |
36 | void coreaudio_fatal(OSStatus err, const char *fmt, ...) { | |
37 | va_list ap; | |
38 | char *msg; | |
39 | ||
40 | va_start(ap, fmt); | |
41 | byte_vasprintf(&msg, fmt, ap); | |
42 | va_end(ap); | |
43 | ||
1f3d2413 | 44 | disorder_fatal(0, "%s: error %u", msg, (unsigned)err); |
ca6b4a12 | 45 | } |
f5fd9a6b RK |
46 | |
47 | /** @brief Return the default device ID */ | |
48 | static AudioDeviceID coreaudio_use_default(void) { | |
49 | OSStatus status; | |
50 | UInt32 propertySize; | |
51 | AudioDeviceID adid; | |
52 | ||
53 | /* TODO should we use kAudioHardwarePropertyDefaultSystemOutputDevice | |
54 | * instead? It is listed in the enumeration but is not documented, so we | |
55 | * leave it alone until better information is available. */ | |
56 | propertySize = sizeof adid; | |
57 | status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, | |
58 | &propertySize, &adid); | |
59 | if(status) | |
ca6b4a12 | 60 | coreaudio_fatal(status, "AudioHardwareGetProperty kAudioHardwarePropertyDefaultOutputDevice"); |
f5fd9a6b | 61 | if(adid == kAudioDeviceUnknown) |
ca6b4a12 | 62 | disorder_fatal(0, "no output device"); |
f5fd9a6b RK |
63 | return adid; |
64 | } | |
65 | ||
66 | /** @brief Find a device by some string | |
67 | * @param selector Selector for property to look for | |
f5fd9a6b RK |
68 | * @param devs List of device IDs |
69 | * @param ndevs Number of device IDs in @p devs | |
8d251217 | 70 | * @param dev Desired device name |
f5fd9a6b RK |
71 | * @param resultp Where to put device ID |
72 | * @return 1 if found, 0 if not found | |
73 | */ | |
74 | static int coreaudio_find_device(AudioObjectPropertySelector selector, | |
75 | //const char *description, | |
76 | const AudioDeviceID *devs, | |
77 | unsigned ndevs, | |
78 | CFStringRef dev, | |
79 | AudioDeviceID *resultp) { | |
80 | OSStatus status; | |
81 | UInt32 propertySize; | |
82 | ||
83 | for(unsigned n = 0; n < ndevs; ++n) { | |
84 | CFStringRef name; | |
85 | propertySize = sizeof name; | |
86 | status = AudioDeviceGetProperty(devs[n], 0, FALSE, | |
87 | selector, | |
88 | &propertySize, &name); | |
89 | if(status) | |
ca6b4a12 | 90 | coreaudio_fatal(status, "AudioDeviceGetProperty"); |
f5fd9a6b RK |
91 | #if 0 |
92 | UInt8 output[1024]; | |
93 | CFIndex used; | |
94 | CFRange r = { 0, CFStringGetLength(name) }; | |
95 | CFStringGetBytes(name, r, kCFStringEncodingUTF8, | |
96 | '?', FALSE, output, sizeof output, | |
97 | &used); | |
98 | output[used] = 0; | |
2e9ba080 | 99 | disorder_info("device %u %s: %s", n, description, (char *)output); |
f5fd9a6b RK |
100 | #endif |
101 | if(CFStringCompare(dev, name, | |
102 | kCFCompareCaseInsensitive|kCFCompareNonliteral) | |
103 | == kCFCompareEqualTo) { | |
104 | *resultp = devs[n]; | |
105 | CFRelease(name); | |
106 | return 1; | |
107 | } | |
108 | CFRelease(name); | |
109 | } | |
110 | return 0; /* didn't find it */ | |
111 | } | |
112 | ||
113 | /** @brief Identify audio device | |
114 | * @param name Device name | |
115 | * @return Device ID | |
116 | */ | |
117 | AudioDeviceID coreaudio_getdevice(const char *name) { | |
118 | OSStatus status; | |
119 | UInt32 propertySize; | |
120 | int found; | |
121 | AudioDeviceID adid; | |
122 | ||
123 | if(!name | |
124 | || !*name | |
125 | || !strcmp(name, "default")) | |
126 | return coreaudio_use_default(); | |
127 | /* Convert the configured device name to a CFString */ | |
128 | CFStringRef dev; | |
129 | dev = CFStringCreateWithCString(NULL/*default allocator*/, | |
130 | name, | |
131 | kCFStringEncodingUTF8); | |
132 | if(!dev) | |
ca6b4a12 | 133 | disorder_fatal(0, "CFStringCreateWithBytes failed"); |
f5fd9a6b RK |
134 | /* Get a list of available devices */ |
135 | AudioDeviceID devs[1024]; | |
136 | unsigned ndevs; | |
137 | ||
138 | propertySize = sizeof devs; | |
139 | status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, | |
140 | &propertySize, devs); | |
141 | if(status) | |
ca6b4a12 | 142 | coreaudio_fatal(status, "AudioHardwareGetProperty kAudioHardwarePropertyDevices"); |
f5fd9a6b RK |
143 | ndevs = propertySize / sizeof *devs; |
144 | if(!ndevs) | |
ca6b4a12 | 145 | disorder_fatal(0, "no sound devices found"); |
f5fd9a6b | 146 | /* Try looking up by UID first */ |
1f3d2413 | 147 | found = coreaudio_find_device(-1*kAudioDevicePropertyDeviceUID, //"UID", |
f5fd9a6b RK |
148 | devs, ndevs, dev, &adid); |
149 | /* Failing that try looking up by name */ | |
150 | if(!found) | |
151 | found = coreaudio_find_device(kAudioObjectPropertyName, //"name", | |
152 | devs, ndevs, dev, &adid); | |
153 | CFRelease(dev); | |
154 | if(!found) | |
ca6b4a12 | 155 | disorder_fatal(0, "cannot find device '%s'", name); |
f5fd9a6b RK |
156 | return adid; |
157 | } | |
158 | ||
159 | #endif | |
160 | ||
161 | /* | |
162 | Local Variables: | |
163 | c-basic-offset:2 | |
164 | comment-column:40 | |
165 | fill-column:79 | |
166 | indent-tabs-mode:nil | |
167 | End: | |
168 | */ |