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 | |
19 | * @brief Support for @ref BACKEND_COREAUDIO | |
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 | ||
44 | disorder_fatal(0, "%s: error %d (%s, %s)", | |
45 | msg, (int)err, | |
46 | GetMacOSStatusErrorString(err), | |
47 | GetMacOSStatusCommentString(err)); | |
48 | } | |
f5fd9a6b RK |
49 | |
50 | /** @brief Return the default device ID */ | |
51 | static AudioDeviceID coreaudio_use_default(void) { | |
52 | OSStatus status; | |
53 | UInt32 propertySize; | |
54 | AudioDeviceID adid; | |
55 | ||
56 | /* TODO should we use kAudioHardwarePropertyDefaultSystemOutputDevice | |
57 | * instead? It is listed in the enumeration but is not documented, so we | |
58 | * leave it alone until better information is available. */ | |
59 | propertySize = sizeof adid; | |
60 | status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, | |
61 | &propertySize, &adid); | |
62 | if(status) | |
ca6b4a12 | 63 | coreaudio_fatal(status, "AudioHardwareGetProperty kAudioHardwarePropertyDefaultOutputDevice"); |
f5fd9a6b | 64 | if(adid == kAudioDeviceUnknown) |
ca6b4a12 | 65 | disorder_fatal(0, "no output device"); |
f5fd9a6b RK |
66 | return adid; |
67 | } | |
68 | ||
69 | /** @brief Find a device by some string | |
70 | * @param selector Selector for property to look for | |
71 | * @param description Property description | |
72 | * @param devs List of device IDs | |
73 | * @param ndevs Number of device IDs in @p devs | |
74 | * @param resultp Where to put device ID | |
75 | * @return 1 if found, 0 if not found | |
76 | */ | |
77 | static int coreaudio_find_device(AudioObjectPropertySelector selector, | |
78 | //const char *description, | |
79 | const AudioDeviceID *devs, | |
80 | unsigned ndevs, | |
81 | CFStringRef dev, | |
82 | AudioDeviceID *resultp) { | |
83 | OSStatus status; | |
84 | UInt32 propertySize; | |
85 | ||
86 | for(unsigned n = 0; n < ndevs; ++n) { | |
87 | CFStringRef name; | |
88 | propertySize = sizeof name; | |
89 | status = AudioDeviceGetProperty(devs[n], 0, FALSE, | |
90 | selector, | |
91 | &propertySize, &name); | |
92 | if(status) | |
ca6b4a12 | 93 | coreaudio_fatal(status, "AudioDeviceGetProperty"); |
f5fd9a6b RK |
94 | #if 0 |
95 | UInt8 output[1024]; | |
96 | CFIndex used; | |
97 | CFRange r = { 0, CFStringGetLength(name) }; | |
98 | CFStringGetBytes(name, r, kCFStringEncodingUTF8, | |
99 | '?', FALSE, output, sizeof output, | |
100 | &used); | |
101 | output[used] = 0; | |
102 | info("device %u %s: %s", n, description, (char *)output); | |
103 | #endif | |
104 | if(CFStringCompare(dev, name, | |
105 | kCFCompareCaseInsensitive|kCFCompareNonliteral) | |
106 | == kCFCompareEqualTo) { | |
107 | *resultp = devs[n]; | |
108 | CFRelease(name); | |
109 | return 1; | |
110 | } | |
111 | CFRelease(name); | |
112 | } | |
113 | return 0; /* didn't find it */ | |
114 | } | |
115 | ||
116 | /** @brief Identify audio device | |
117 | * @param name Device name | |
118 | * @return Device ID | |
119 | */ | |
120 | AudioDeviceID coreaudio_getdevice(const char *name) { | |
121 | OSStatus status; | |
122 | UInt32 propertySize; | |
123 | int found; | |
124 | AudioDeviceID adid; | |
125 | ||
126 | if(!name | |
127 | || !*name | |
128 | || !strcmp(name, "default")) | |
129 | return coreaudio_use_default(); | |
130 | /* Convert the configured device name to a CFString */ | |
131 | CFStringRef dev; | |
132 | dev = CFStringCreateWithCString(NULL/*default allocator*/, | |
133 | name, | |
134 | kCFStringEncodingUTF8); | |
135 | if(!dev) | |
ca6b4a12 | 136 | disorder_fatal(0, "CFStringCreateWithBytes failed"); |
f5fd9a6b RK |
137 | /* Get a list of available devices */ |
138 | AudioDeviceID devs[1024]; | |
139 | unsigned ndevs; | |
140 | ||
141 | propertySize = sizeof devs; | |
142 | status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, | |
143 | &propertySize, devs); | |
144 | if(status) | |
ca6b4a12 | 145 | coreaudio_fatal(status, "AudioHardwareGetProperty kAudioHardwarePropertyDevices"); |
f5fd9a6b RK |
146 | ndevs = propertySize / sizeof *devs; |
147 | if(!ndevs) | |
ca6b4a12 | 148 | disorder_fatal(0, "no sound devices found"); |
f5fd9a6b RK |
149 | /* Try looking up by UID first */ |
150 | found = coreaudio_find_device(kAudioDevicePropertyDeviceUID, //"UID", | |
151 | devs, ndevs, dev, &adid); | |
152 | /* Failing that try looking up by name */ | |
153 | if(!found) | |
154 | found = coreaudio_find_device(kAudioObjectPropertyName, //"name", | |
155 | devs, ndevs, dev, &adid); | |
156 | CFRelease(dev); | |
157 | if(!found) | |
ca6b4a12 | 158 | disorder_fatal(0, "cannot find device '%s'", name); |
f5fd9a6b RK |
159 | return adid; |
160 | } | |
161 | ||
162 | #endif | |
163 | ||
164 | /* | |
165 | Local Variables: | |
166 | c-basic-offset:2 | |
167 | comment-column:40 | |
168 | fill-column:79 | |
169 | indent-tabs-mode:nil | |
170 | End: | |
171 | */ |