chiark / gitweb /
Core Audio support should now include descriptions in error strings.
[disorder] / lib / coreaudio.c
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"
28 #include "printf.h"
29 #include <CoreFoundation/CFString.h>
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 }
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)
63     coreaudio_fatal(status, "AudioHardwareGetProperty kAudioHardwarePropertyDefaultOutputDevice");
64   if(adid == kAudioDeviceUnknown)
65     disorder_fatal(0, "no output device");
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)
93       coreaudio_fatal(status, "AudioDeviceGetProperty");
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)
136     disorder_fatal(0, "CFStringCreateWithBytes failed");
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)
145     coreaudio_fatal(status, "AudioHardwareGetProperty kAudioHardwarePropertyDevices");
146   ndevs = propertySize / sizeof *devs;
147   if(!ndevs)
148     disorder_fatal(0, "no sound devices found");
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)
158     disorder_fatal(0, "cannot find device '%s'", name);
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 */