slouken@935
|
1 |
/*
|
slouken@935
|
2 |
SDL - Simple DirectMedia Layer
|
slouken@1312
|
3 |
Copyright (C) 1997-2006 Sam Lantinga
|
slouken@935
|
4 |
|
slouken@935
|
5 |
This library is free software; you can redistribute it and/or
|
slouken@1312
|
6 |
modify it under the terms of the GNU Lesser General Public
|
slouken@935
|
7 |
License as published by the Free Software Foundation; either
|
slouken@1312
|
8 |
version 2.1 of the License, or (at your option) any later version.
|
slouken@935
|
9 |
|
slouken@935
|
10 |
This library is distributed in the hope that it will be useful,
|
slouken@935
|
11 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
slouken@935
|
12 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
slouken@1312
|
13 |
Lesser General Public License for more details.
|
slouken@935
|
14 |
|
slouken@1312
|
15 |
You should have received a copy of the GNU Lesser General Public
|
slouken@1312
|
16 |
License along with this library; if not, write to the Free Software
|
slouken@1312
|
17 |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
slouken@935
|
18 |
|
slouken@935
|
19 |
Sam Lantinga
|
slouken@935
|
20 |
slouken@libsdl.org
|
slouken@935
|
21 |
*/
|
slouken@1402
|
22 |
#include "SDL_config.h"
|
slouken@935
|
23 |
|
icculus@2049
|
24 |
#include <CoreAudio/CoreAudio.h>
|
slouken@935
|
25 |
#include <AudioUnit/AudioUnit.h>
|
slouken@2281
|
26 |
#ifdef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
|
slouken@2281
|
27 |
#include <AudioUnit/AUNTComponent.h>
|
slouken@2281
|
28 |
#endif
|
slouken@935
|
29 |
|
slouken@935
|
30 |
#include "SDL_audio.h"
|
slouken@1361
|
31 |
#include "../SDL_audio_c.h"
|
slouken@1361
|
32 |
#include "../SDL_sysaudio.h"
|
slouken@935
|
33 |
#include "SDL_coreaudio.h"
|
slouken@935
|
34 |
|
icculus@2049
|
35 |
#define DEBUG_COREAUDIO 0
|
slouken@935
|
36 |
|
icculus@2049
|
37 |
typedef struct COREAUDIO_DeviceList
|
icculus@2049
|
38 |
{
|
icculus@2049
|
39 |
AudioDeviceID id;
|
icculus@2049
|
40 |
const char *name;
|
icculus@2049
|
41 |
} COREAUDIO_DeviceList;
|
slouken@935
|
42 |
|
icculus@2049
|
43 |
static COREAUDIO_DeviceList *inputDevices = NULL;
|
icculus@2049
|
44 |
static int inputDeviceCount = 0;
|
icculus@2049
|
45 |
static COREAUDIO_DeviceList *outputDevices = NULL;
|
icculus@2049
|
46 |
static int outputDeviceCount = 0;
|
slouken@935
|
47 |
|
slouken@1895
|
48 |
static void
|
slouken@2060
|
49 |
free_device_list(COREAUDIO_DeviceList ** devices, int *devCount)
|
slouken@935
|
50 |
{
|
icculus@2049
|
51 |
if (*devices) {
|
icculus@2049
|
52 |
int i = *devCount;
|
icculus@2049
|
53 |
while (i--)
|
icculus@2049
|
54 |
SDL_free((void *) (*devices)[i].name);
|
icculus@2049
|
55 |
SDL_free(*devices);
|
icculus@2049
|
56 |
*devices = NULL;
|
icculus@2049
|
57 |
}
|
icculus@2049
|
58 |
*devCount = 0;
|
slouken@935
|
59 |
}
|
slouken@935
|
60 |
|
icculus@2049
|
61 |
|
icculus@2049
|
62 |
static void
|
slouken@2060
|
63 |
build_device_list(int iscapture, COREAUDIO_DeviceList ** devices,
|
slouken@2060
|
64 |
int *devCount)
|
slouken@935
|
65 |
{
|
icculus@2049
|
66 |
Boolean outWritable = 0;
|
icculus@2049
|
67 |
OSStatus result = noErr;
|
icculus@2049
|
68 |
UInt32 size = 0;
|
icculus@2049
|
69 |
AudioDeviceID *devs = NULL;
|
icculus@2049
|
70 |
UInt32 i = 0;
|
icculus@2049
|
71 |
UInt32 max = 0;
|
icculus@2049
|
72 |
|
icculus@2049
|
73 |
free_device_list(devices, devCount);
|
icculus@2049
|
74 |
|
icculus@2049
|
75 |
result = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
|
slouken@2060
|
76 |
&size, &outWritable);
|
icculus@2049
|
77 |
|
icculus@2049
|
78 |
if (result != kAudioHardwareNoError)
|
icculus@2049
|
79 |
return;
|
icculus@2049
|
80 |
|
icculus@2049
|
81 |
devs = (AudioDeviceID *) alloca(size);
|
icculus@2049
|
82 |
if (devs == NULL)
|
icculus@2049
|
83 |
return;
|
icculus@2049
|
84 |
|
slouken@2060
|
85 |
max = size / sizeof(AudioDeviceID);
|
slouken@2060
|
86 |
*devices = (COREAUDIO_DeviceList *) SDL_malloc(max * sizeof(**devices));
|
icculus@2049
|
87 |
if (*devices == NULL)
|
icculus@2049
|
88 |
return;
|
icculus@2049
|
89 |
|
icculus@2049
|
90 |
result = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
|
slouken@2060
|
91 |
&size, devs);
|
icculus@2049
|
92 |
if (result != kAudioHardwareNoError)
|
icculus@2049
|
93 |
return;
|
icculus@2049
|
94 |
|
icculus@2049
|
95 |
for (i = 0; i < max; i++) {
|
icculus@2049
|
96 |
CFStringRef cfstr = NULL;
|
icculus@2049
|
97 |
char *ptr = NULL;
|
icculus@2049
|
98 |
AudioDeviceID dev = devs[i];
|
icculus@2049
|
99 |
AudioBufferList *buflist = NULL;
|
icculus@2049
|
100 |
int usable = 0;
|
icculus@2049
|
101 |
CFIndex len = 0;
|
icculus@2049
|
102 |
|
icculus@2049
|
103 |
result = AudioDeviceGetPropertyInfo(dev, 0, iscapture,
|
slouken@2060
|
104 |
kAudioDevicePropertyStreamConfiguration,
|
slouken@2060
|
105 |
&size, &outWritable);
|
icculus@2049
|
106 |
if (result != noErr)
|
icculus@2049
|
107 |
continue;
|
icculus@2049
|
108 |
|
icculus@2049
|
109 |
buflist = (AudioBufferList *) SDL_malloc(size);
|
icculus@2049
|
110 |
if (buflist == NULL)
|
icculus@2049
|
111 |
continue;
|
icculus@2049
|
112 |
|
icculus@2049
|
113 |
result = AudioDeviceGetProperty(dev, 0, iscapture,
|
slouken@2060
|
114 |
kAudioDevicePropertyStreamConfiguration,
|
slouken@2060
|
115 |
&size, buflist);
|
slouken@935
|
116 |
|
icculus@2049
|
117 |
if (result == noErr) {
|
icculus@2049
|
118 |
UInt32 j;
|
icculus@2049
|
119 |
for (j = 0; j < buflist->mNumberBuffers; j++) {
|
icculus@2049
|
120 |
if (buflist->mBuffers[j].mNumberChannels > 0) {
|
icculus@2049
|
121 |
usable = 1;
|
icculus@2049
|
122 |
break;
|
icculus@2049
|
123 |
}
|
icculus@2049
|
124 |
}
|
icculus@2049
|
125 |
}
|
icculus@2049
|
126 |
|
icculus@2049
|
127 |
SDL_free(buflist);
|
icculus@2049
|
128 |
|
icculus@2049
|
129 |
if (!usable)
|
icculus@2049
|
130 |
continue;
|
icculus@2049
|
131 |
|
slouken@2060
|
132 |
size = sizeof(CFStringRef);
|
icculus@2049
|
133 |
result = AudioDeviceGetProperty(dev, 0, iscapture,
|
slouken@2110
|
134 |
kAudioDevicePropertyDeviceNameCFString,
|
icculus@2049
|
135 |
&size, &cfstr);
|
icculus@2049
|
136 |
|
icculus@2049
|
137 |
if (result != kAudioHardwareNoError)
|
icculus@2049
|
138 |
continue;
|
icculus@2049
|
139 |
|
icculus@2049
|
140 |
len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
|
icculus@2049
|
141 |
kCFStringEncodingUTF8);
|
icculus@2049
|
142 |
|
icculus@2049
|
143 |
ptr = (char *) SDL_malloc(len + 1);
|
slouken@2060
|
144 |
usable = ((ptr != NULL) &&
|
slouken@2060
|
145 |
(CFStringGetCString
|
slouken@2060
|
146 |
(cfstr, ptr, len + 1, kCFStringEncodingUTF8)));
|
icculus@2049
|
147 |
|
icculus@2049
|
148 |
CFRelease(cfstr);
|
icculus@2049
|
149 |
|
icculus@2049
|
150 |
if (usable) {
|
icculus@2049
|
151 |
len = strlen(ptr);
|
icculus@2049
|
152 |
/* Some devices have whitespace at the end...trim it. */
|
slouken@2060
|
153 |
while ((len > 0) && (ptr[len - 1] == ' ')) {
|
icculus@2049
|
154 |
len--;
|
icculus@2049
|
155 |
}
|
icculus@2049
|
156 |
usable = (len > 0);
|
icculus@2049
|
157 |
}
|
icculus@2049
|
158 |
|
icculus@2049
|
159 |
if (!usable) {
|
icculus@2049
|
160 |
SDL_free(ptr);
|
icculus@2049
|
161 |
} else {
|
icculus@2049
|
162 |
ptr[len] = '\0';
|
icculus@2049
|
163 |
|
slouken@2060
|
164 |
#if DEBUG_COREAUDIO
|
icculus@2049
|
165 |
printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
|
slouken@2060
|
166 |
((iscapture) ? "capture" : "output"),
|
slouken@2060
|
167 |
(int) *devCount, ptr, (int) dev);
|
slouken@2060
|
168 |
#endif
|
icculus@2049
|
169 |
|
icculus@2049
|
170 |
(*devices)[*devCount].id = dev;
|
icculus@2049
|
171 |
(*devices)[*devCount].name = ptr;
|
icculus@2049
|
172 |
(*devCount)++;
|
icculus@2049
|
173 |
}
|
slouken@935
|
174 |
}
|
slouken@935
|
175 |
}
|
slouken@935
|
176 |
|
icculus@2049
|
177 |
static inline void
|
icculus@2049
|
178 |
build_device_lists(void)
|
icculus@2049
|
179 |
{
|
icculus@2049
|
180 |
build_device_list(0, &outputDevices, &outputDeviceCount);
|
icculus@2049
|
181 |
build_device_list(1, &inputDevices, &inputDeviceCount);
|
icculus@2049
|
182 |
}
|
icculus@2049
|
183 |
|
icculus@2049
|
184 |
|
icculus@2049
|
185 |
static inline void
|
icculus@2049
|
186 |
free_device_lists(void)
|
icculus@2049
|
187 |
{
|
icculus@2049
|
188 |
free_device_list(&outputDevices, &outputDeviceCount);
|
icculus@2049
|
189 |
free_device_list(&inputDevices, &inputDeviceCount);
|
icculus@2049
|
190 |
}
|
icculus@2049
|
191 |
|
icculus@2049
|
192 |
|
icculus@2049
|
193 |
static int
|
slouken@2060
|
194 |
find_device_id(const char *devname, int iscapture, AudioDeviceID * id)
|
icculus@2049
|
195 |
{
|
icculus@2049
|
196 |
int i = ((iscapture) ? inputDeviceCount : outputDeviceCount);
|
icculus@2049
|
197 |
COREAUDIO_DeviceList *devs = ((iscapture) ? inputDevices : outputDevices);
|
icculus@2049
|
198 |
while (i--) {
|
icculus@2049
|
199 |
if (SDL_strcmp(devname, devs->name) == 0) {
|
icculus@2049
|
200 |
*id = devs->id;
|
icculus@2049
|
201 |
return 1;
|
icculus@2049
|
202 |
}
|
icculus@2049
|
203 |
devs++;
|
icculus@2049
|
204 |
}
|
icculus@2049
|
205 |
|
icculus@2049
|
206 |
return 0;
|
icculus@2049
|
207 |
}
|
icculus@2049
|
208 |
|
icculus@2049
|
209 |
|
icculus@2049
|
210 |
static int
|
icculus@2049
|
211 |
COREAUDIO_DetectDevices(int iscapture)
|
icculus@2049
|
212 |
{
|
icculus@2049
|
213 |
if (iscapture) {
|
icculus@2049
|
214 |
build_device_list(1, &inputDevices, &inputDeviceCount);
|
icculus@2049
|
215 |
return inputDeviceCount;
|
icculus@2049
|
216 |
} else {
|
icculus@2049
|
217 |
build_device_list(0, &outputDevices, &outputDeviceCount);
|
icculus@2049
|
218 |
return outputDeviceCount;
|
icculus@2049
|
219 |
}
|
icculus@2049
|
220 |
|
slouken@2060
|
221 |
return 0; /* shouldn't ever hit this. */
|
icculus@2049
|
222 |
}
|
icculus@2049
|
223 |
|
icculus@2049
|
224 |
|
icculus@2049
|
225 |
static const char *
|
icculus@2049
|
226 |
COREAUDIO_GetDeviceName(int index, int iscapture)
|
icculus@2049
|
227 |
{
|
icculus@2049
|
228 |
if ((iscapture) && (index < inputDeviceCount)) {
|
icculus@2049
|
229 |
return inputDevices[index].name;
|
icculus@2049
|
230 |
} else if ((!iscapture) && (index < outputDeviceCount)) {
|
icculus@2049
|
231 |
return outputDevices[index].name;
|
icculus@2049
|
232 |
}
|
icculus@2049
|
233 |
|
icculus@2049
|
234 |
SDL_SetError("No such device");
|
icculus@2049
|
235 |
return NULL;
|
icculus@2049
|
236 |
}
|
icculus@2049
|
237 |
|
icculus@2049
|
238 |
|
icculus@2049
|
239 |
static void
|
icculus@2049
|
240 |
COREAUDIO_Deinitialize(void)
|
icculus@2049
|
241 |
{
|
icculus@2049
|
242 |
free_device_lists();
|
icculus@2049
|
243 |
}
|
icculus@2049
|
244 |
|
slouken@935
|
245 |
|
slouken@935
|
246 |
/* The CoreAudio callback */
|
slouken@1895
|
247 |
static OSStatus
|
icculus@2049
|
248 |
outputCallback(void *inRefCon,
|
slouken@2060
|
249 |
AudioUnitRenderActionFlags * ioActionFlags,
|
slouken@2060
|
250 |
const AudioTimeStamp * inTimeStamp,
|
slouken@2060
|
251 |
UInt32 inBusNumber, UInt32 inNumberFrames,
|
slouken@2060
|
252 |
AudioBufferList * ioDataList)
|
slouken@935
|
253 |
{
|
slouken@1895
|
254 |
SDL_AudioDevice *this = (SDL_AudioDevice *) inRefCon;
|
icculus@2049
|
255 |
AudioBuffer *ioData = &ioDataList->mBuffers[0];
|
slouken@935
|
256 |
UInt32 remaining, len;
|
slouken@935
|
257 |
void *ptr;
|
slouken@935
|
258 |
|
icculus@2049
|
259 |
/* Is there ever more than one buffer, and what do you do with it? */
|
icculus@2049
|
260 |
if (ioDataList->mNumberBuffers != 1) {
|
icculus@2049
|
261 |
return noErr;
|
icculus@2049
|
262 |
}
|
icculus@2049
|
263 |
|
slouken@935
|
264 |
/* Only do anything if audio is enabled and not paused */
|
slouken@1895
|
265 |
if (!this->enabled || this->paused) {
|
slouken@1336
|
266 |
SDL_memset(ioData->mData, this->spec.silence, ioData->mDataByteSize);
|
slouken@935
|
267 |
return 0;
|
slouken@935
|
268 |
}
|
slouken@1895
|
269 |
|
slouken@935
|
270 |
/* No SDL conversion should be needed here, ever, since we accept
|
slouken@935
|
271 |
any input format in OpenAudio, and leave the conversion to CoreAudio.
|
slouken@935
|
272 |
*/
|
slouken@1338
|
273 |
/*
|
slouken@1895
|
274 |
assert(!this->convert.needed);
|
slouken@1895
|
275 |
assert(this->spec.channels == ioData->mNumberChannels);
|
slouken@1338
|
276 |
*/
|
slouken@1895
|
277 |
|
slouken@935
|
278 |
remaining = ioData->mDataByteSize;
|
slouken@935
|
279 |
ptr = ioData->mData;
|
slouken@935
|
280 |
while (remaining > 0) {
|
icculus@2049
|
281 |
if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
|
slouken@935
|
282 |
/* Generate the data */
|
icculus@2049
|
283 |
SDL_memset(this->hidden->buffer, this->spec.silence,
|
icculus@2049
|
284 |
this->hidden->bufferSize);
|
slouken@935
|
285 |
SDL_mutexP(this->mixer_lock);
|
icculus@2049
|
286 |
(*this->spec.callback) (this->spec.userdata, this->hidden->buffer,
|
icculus@2049
|
287 |
this->hidden->bufferSize);
|
slouken@935
|
288 |
SDL_mutexV(this->mixer_lock);
|
icculus@2049
|
289 |
this->hidden->bufferOffset = 0;
|
slouken@935
|
290 |
}
|
slouken@1895
|
291 |
|
icculus@2049
|
292 |
len = this->hidden->bufferSize - this->hidden->bufferOffset;
|
slouken@935
|
293 |
if (len > remaining)
|
slouken@935
|
294 |
len = remaining;
|
icculus@2049
|
295 |
SDL_memcpy(ptr,
|
slouken@2060
|
296 |
(char *) this->hidden->buffer + this->hidden->bufferOffset,
|
slouken@2060
|
297 |
len);
|
slouken@1895
|
298 |
ptr = (char *) ptr + len;
|
slouken@935
|
299 |
remaining -= len;
|
icculus@2049
|
300 |
this->hidden->bufferOffset += len;
|
slouken@935
|
301 |
}
|
slouken@1895
|
302 |
|
slouken@935
|
303 |
return 0;
|
slouken@935
|
304 |
}
|
slouken@935
|
305 |
|
icculus@2049
|
306 |
static OSStatus
|
icculus@2049
|
307 |
inputCallback(void *inRefCon,
|
slouken@2060
|
308 |
AudioUnitRenderActionFlags * ioActionFlags,
|
icculus@2049
|
309 |
const AudioTimeStamp * inTimeStamp,
|
icculus@2049
|
310 |
UInt32 inBusNumber, UInt32 inNumberFrames,
|
slouken@2060
|
311 |
AudioBufferList * ioData)
|
slouken@935
|
312 |
{
|
icculus@2049
|
313 |
//err = AudioUnitRender(afr->fAudioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, afr->fAudioBuffer);
|
icculus@2049
|
314 |
// !!! FIXME: write me!
|
icculus@2049
|
315 |
return noErr;
|
slouken@935
|
316 |
}
|
slouken@935
|
317 |
|
icculus@2049
|
318 |
|
icculus@2049
|
319 |
static void
|
icculus@2049
|
320 |
COREAUDIO_CloseDevice(_THIS)
|
slouken@935
|
321 |
{
|
icculus@2049
|
322 |
if (this->hidden != NULL) {
|
icculus@2055
|
323 |
if (this->hidden->audioUnitOpened) {
|
icculus@2055
|
324 |
OSStatus result = noErr;
|
icculus@2055
|
325 |
AURenderCallbackStruct callback;
|
icculus@2055
|
326 |
const AudioUnitElement output_bus = 0;
|
icculus@2055
|
327 |
const AudioUnitElement input_bus = 1;
|
icculus@2055
|
328 |
const int iscapture = this->iscapture;
|
slouken@2060
|
329 |
const AudioUnitElement bus =
|
slouken@2060
|
330 |
((iscapture) ? input_bus : output_bus);
|
slouken@2060
|
331 |
const AudioUnitScope scope =
|
slouken@2060
|
332 |
((iscapture) ? kAudioUnitScope_Output :
|
slouken@2060
|
333 |
kAudioUnitScope_Input);
|
slouken@935
|
334 |
|
icculus@2055
|
335 |
/* stop processing the audio unit */
|
icculus@2055
|
336 |
result = AudioOutputUnitStop(this->hidden->audioUnit);
|
icculus@2049
|
337 |
|
icculus@2055
|
338 |
/* Remove the input callback */
|
slouken@2060
|
339 |
SDL_memset(&callback, '\0', sizeof(AURenderCallbackStruct));
|
icculus@2055
|
340 |
result = AudioUnitSetProperty(this->hidden->audioUnit,
|
icculus@2055
|
341 |
kAudioUnitProperty_SetRenderCallback,
|
icculus@2055
|
342 |
scope, bus, &callback,
|
slouken@2060
|
343 |
sizeof(callback));
|
icculus@2049
|
344 |
|
icculus@2055
|
345 |
CloseComponent(this->hidden->audioUnit);
|
icculus@2055
|
346 |
this->hidden->audioUnitOpened = 0;
|
icculus@2055
|
347 |
}
|
icculus@2049
|
348 |
SDL_free(this->hidden->buffer);
|
icculus@2049
|
349 |
SDL_free(this->hidden);
|
icculus@2049
|
350 |
this->hidden = NULL;
|
slouken@935
|
351 |
}
|
icculus@2049
|
352 |
}
|
slouken@935
|
353 |
|
slouken@935
|
354 |
|
slouken@935
|
355 |
#define CHECK_RESULT(msg) \
|
slouken@935
|
356 |
if (result != noErr) { \
|
icculus@2049
|
357 |
COREAUDIO_CloseDevice(this); \
|
icculus@2017
|
358 |
SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
|
icculus@2049
|
359 |
return 0; \
|
icculus@2049
|
360 |
}
|
icculus@2049
|
361 |
|
icculus@2049
|
362 |
static int
|
icculus@2049
|
363 |
find_device_by_name(_THIS, const char *devname, int iscapture)
|
icculus@2049
|
364 |
{
|
icculus@2049
|
365 |
AudioDeviceID devid = 0;
|
icculus@2049
|
366 |
OSStatus result = noErr;
|
icculus@2049
|
367 |
UInt32 size = 0;
|
icculus@2049
|
368 |
UInt32 alive = 0;
|
icculus@2049
|
369 |
pid_t pid = 0;
|
icculus@2049
|
370 |
|
icculus@2049
|
371 |
if (devname == NULL) {
|
slouken@2060
|
372 |
size = sizeof(AudioDeviceID);
|
icculus@2049
|
373 |
const AudioHardwarePropertyID propid =
|
slouken@2060
|
374 |
((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
|
slouken@2060
|
375 |
kAudioHardwarePropertyDefaultOutputDevice);
|
icculus@2049
|
376 |
|
icculus@2049
|
377 |
result = AudioHardwareGetProperty(propid, &size, &devid);
|
icculus@2049
|
378 |
CHECK_RESULT("AudioHardwareGetProperty (default device)");
|
icculus@2049
|
379 |
} else {
|
icculus@2049
|
380 |
if (!find_device_id(devname, iscapture, &devid)) {
|
icculus@2049
|
381 |
SDL_SetError("CoreAudio: No such audio device.");
|
icculus@2049
|
382 |
return 0;
|
icculus@2049
|
383 |
}
|
slouken@935
|
384 |
}
|
slouken@935
|
385 |
|
slouken@2060
|
386 |
size = sizeof(alive);
|
icculus@2049
|
387 |
result = AudioDeviceGetProperty(devid, 0, iscapture,
|
icculus@2049
|
388 |
kAudioDevicePropertyDeviceIsAlive,
|
icculus@2049
|
389 |
&size, &alive);
|
slouken@2060
|
390 |
CHECK_RESULT
|
slouken@2060
|
391 |
("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
|
slouken@935
|
392 |
|
icculus@2049
|
393 |
if (!alive) {
|
icculus@2049
|
394 |
SDL_SetError("CoreAudio: requested device exists, but isn't alive.");
|
icculus@2049
|
395 |
return 0;
|
icculus@2049
|
396 |
}
|
icculus@2049
|
397 |
|
slouken@2060
|
398 |
size = sizeof(pid);
|
icculus@2049
|
399 |
result = AudioDeviceGetProperty(devid, 0, iscapture,
|
icculus@2049
|
400 |
kAudioDevicePropertyHogMode, &size, &pid);
|
icculus@2049
|
401 |
|
icculus@2049
|
402 |
/* some devices don't support this property, so errors are fine here. */
|
icculus@2049
|
403 |
if ((result == noErr) && (pid != -1)) {
|
icculus@2049
|
404 |
SDL_SetError("CoreAudio: requested device is being hogged.");
|
icculus@2049
|
405 |
return 0;
|
icculus@2049
|
406 |
}
|
icculus@2049
|
407 |
|
icculus@2049
|
408 |
this->hidden->deviceID = devid;
|
icculus@2049
|
409 |
return 1;
|
icculus@2049
|
410 |
}
|
icculus@2049
|
411 |
|
icculus@2049
|
412 |
|
icculus@2049
|
413 |
static int
|
icculus@2049
|
414 |
prepare_audiounit(_THIS, const char *devname, int iscapture,
|
slouken@2060
|
415 |
const AudioStreamBasicDescription * strdesc)
|
slouken@935
|
416 |
{
|
slouken@935
|
417 |
OSStatus result = noErr;
|
icculus@2049
|
418 |
AURenderCallbackStruct callback;
|
slouken@935
|
419 |
ComponentDescription desc;
|
icculus@2049
|
420 |
Component comp = NULL;
|
icculus@2049
|
421 |
UInt32 enableIO = 0;
|
icculus@2049
|
422 |
const AudioUnitElement output_bus = 0;
|
icculus@2049
|
423 |
const AudioUnitElement input_bus = 1;
|
icculus@2049
|
424 |
const AudioUnitElement bus = ((iscapture) ? input_bus : output_bus);
|
icculus@2049
|
425 |
const AudioUnitScope scope = ((iscapture) ? kAudioUnitScope_Output :
|
slouken@2060
|
426 |
kAudioUnitScope_Input);
|
icculus@2049
|
427 |
|
icculus@2049
|
428 |
if (!find_device_by_name(this, devname, iscapture)) {
|
icculus@2049
|
429 |
SDL_SetError("Couldn't find requested CoreAudio device");
|
icculus@2049
|
430 |
return 0;
|
icculus@2049
|
431 |
}
|
icculus@2049
|
432 |
|
icculus@2049
|
433 |
SDL_memset(&desc, '\0', sizeof(ComponentDescription));
|
icculus@2049
|
434 |
desc.componentType = kAudioUnitType_Output;
|
icculus@2049
|
435 |
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
icculus@2049
|
436 |
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
icculus@2049
|
437 |
|
icculus@2049
|
438 |
comp = FindNextComponent(NULL, &desc);
|
icculus@2049
|
439 |
if (comp == NULL) {
|
icculus@2049
|
440 |
SDL_SetError("Couldn't find requested CoreAudio component");
|
icculus@2049
|
441 |
return 0;
|
icculus@2049
|
442 |
}
|
icculus@2049
|
443 |
|
icculus@2049
|
444 |
/* Open & initialize the audio unit */
|
icculus@2049
|
445 |
result = OpenAComponent(comp, &this->hidden->audioUnit);
|
icculus@2049
|
446 |
CHECK_RESULT("OpenAComponent");
|
icculus@2049
|
447 |
|
icculus@2055
|
448 |
this->hidden->audioUnitOpened = 1;
|
icculus@2055
|
449 |
|
icculus@2049
|
450 |
// !!! FIXME: this is wrong?
|
icculus@2049
|
451 |
enableIO = ((iscapture) ? 1 : 0);
|
icculus@2049
|
452 |
result = AudioUnitSetProperty(this->hidden->audioUnit,
|
icculus@2049
|
453 |
kAudioOutputUnitProperty_EnableIO,
|
icculus@2049
|
454 |
kAudioUnitScope_Input, input_bus,
|
slouken@2060
|
455 |
&enableIO, sizeof(enableIO));
|
icculus@2049
|
456 |
CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_EnableIO input)");
|
icculus@2049
|
457 |
|
icculus@2049
|
458 |
// !!! FIXME: this is wrong?
|
icculus@2049
|
459 |
enableIO = ((iscapture) ? 0 : 1);
|
icculus@2049
|
460 |
result = AudioUnitSetProperty(this->hidden->audioUnit,
|
icculus@2049
|
461 |
kAudioOutputUnitProperty_EnableIO,
|
icculus@2049
|
462 |
kAudioUnitScope_Output, output_bus,
|
slouken@2060
|
463 |
&enableIO, sizeof(enableIO));
|
icculus@2049
|
464 |
CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_EnableIO output)");
|
icculus@2049
|
465 |
|
icculus@2049
|
466 |
result = AudioUnitSetProperty(this->hidden->audioUnit,
|
icculus@2049
|
467 |
kAudioOutputUnitProperty_CurrentDevice,
|
icculus@2049
|
468 |
kAudioUnitScope_Global, 0,
|
icculus@2049
|
469 |
&this->hidden->deviceID,
|
slouken@2060
|
470 |
sizeof(AudioDeviceID));
|
slouken@2060
|
471 |
CHECK_RESULT
|
slouken@2060
|
472 |
("AudioUnitSetProperty (kAudioOutputUnitProperty_CurrentDevice)");
|
icculus@2049
|
473 |
|
icculus@2049
|
474 |
/* Set the data format of the audio unit. */
|
icculus@2049
|
475 |
result = AudioUnitSetProperty(this->hidden->audioUnit,
|
icculus@2049
|
476 |
kAudioUnitProperty_StreamFormat,
|
slouken@2060
|
477 |
scope, bus, strdesc, sizeof(*strdesc));
|
icculus@2049
|
478 |
CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)");
|
icculus@2049
|
479 |
|
icculus@2049
|
480 |
/* Set the audio callback */
|
slouken@2060
|
481 |
SDL_memset(&callback, '\0', sizeof(AURenderCallbackStruct));
|
icculus@2049
|
482 |
callback.inputProc = ((iscapture) ? inputCallback : outputCallback);
|
icculus@2049
|
483 |
callback.inputProcRefCon = this;
|
icculus@2049
|
484 |
result = AudioUnitSetProperty(this->hidden->audioUnit,
|
icculus@2049
|
485 |
kAudioUnitProperty_SetRenderCallback,
|
slouken@2060
|
486 |
scope, bus, &callback, sizeof(callback));
|
slouken@2060
|
487 |
CHECK_RESULT
|
slouken@2060
|
488 |
("AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)");
|
icculus@2049
|
489 |
|
icculus@2049
|
490 |
/* Calculate the final parameters for this audio specification */
|
icculus@2049
|
491 |
SDL_CalculateAudioSpec(&this->spec);
|
icculus@2049
|
492 |
|
icculus@2049
|
493 |
/* Allocate a sample buffer */
|
icculus@2049
|
494 |
this->hidden->bufferOffset = this->hidden->bufferSize = this->spec.size;
|
icculus@2049
|
495 |
this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
|
icculus@2049
|
496 |
|
icculus@2049
|
497 |
result = AudioUnitInitialize(this->hidden->audioUnit);
|
icculus@2049
|
498 |
CHECK_RESULT("AudioUnitInitialize");
|
icculus@2049
|
499 |
|
icculus@2049
|
500 |
/* Finally, start processing of the audio unit */
|
icculus@2049
|
501 |
result = AudioOutputUnitStart(this->hidden->audioUnit);
|
icculus@2049
|
502 |
CHECK_RESULT("AudioOutputUnitStart");
|
icculus@2049
|
503 |
|
icculus@2049
|
504 |
/* We're running! */
|
icculus@2049
|
505 |
return 1;
|
icculus@2049
|
506 |
}
|
icculus@2049
|
507 |
|
icculus@2049
|
508 |
|
icculus@2049
|
509 |
static int
|
icculus@2049
|
510 |
COREAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
|
icculus@2049
|
511 |
{
|
icculus@2016
|
512 |
AudioStreamBasicDescription strdesc;
|
icculus@2049
|
513 |
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
|
icculus@2003
|
514 |
int valid_datatype = 0;
|
slouken@935
|
515 |
|
icculus@2049
|
516 |
/* Initialize all variables that we clean on shutdown */
|
icculus@2049
|
517 |
this->hidden = (struct SDL_PrivateAudioData *)
|
slouken@2060
|
518 |
SDL_malloc((sizeof *this->hidden));
|
icculus@2049
|
519 |
if (this->hidden == NULL) {
|
icculus@2049
|
520 |
SDL_OutOfMemory();
|
icculus@2049
|
521 |
return (0);
|
icculus@2049
|
522 |
}
|
icculus@2049
|
523 |
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
icculus@2049
|
524 |
|
slouken@935
|
525 |
/* Setup a AudioStreamBasicDescription with the requested format */
|
icculus@2049
|
526 |
SDL_memset(&strdesc, '\0', sizeof(AudioStreamBasicDescription));
|
icculus@2016
|
527 |
strdesc.mFormatID = kAudioFormatLinearPCM;
|
icculus@2016
|
528 |
strdesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
|
icculus@2049
|
529 |
strdesc.mChannelsPerFrame = this->spec.channels;
|
icculus@2049
|
530 |
strdesc.mSampleRate = this->spec.freq;
|
icculus@2016
|
531 |
strdesc.mFramesPerPacket = 1;
|
slouken@1895
|
532 |
|
icculus@2003
|
533 |
while ((!valid_datatype) && (test_format)) {
|
icculus@2049
|
534 |
this->spec.format = test_format;
|
icculus@2003
|
535 |
/* Just a list of valid SDL formats, so people don't pass junk here. */
|
icculus@2003
|
536 |
switch (test_format) {
|
slouken@2043
|
537 |
case AUDIO_U8:
|
slouken@2043
|
538 |
case AUDIO_S8:
|
slouken@2043
|
539 |
case AUDIO_U16LSB:
|
slouken@2043
|
540 |
case AUDIO_S16LSB:
|
slouken@2043
|
541 |
case AUDIO_U16MSB:
|
slouken@2043
|
542 |
case AUDIO_S16MSB:
|
slouken@2043
|
543 |
case AUDIO_S32LSB:
|
slouken@2043
|
544 |
case AUDIO_S32MSB:
|
slouken@2043
|
545 |
case AUDIO_F32LSB:
|
slouken@2043
|
546 |
case AUDIO_F32MSB:
|
slouken@2043
|
547 |
valid_datatype = 1;
|
icculus@2049
|
548 |
strdesc.mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
|
icculus@2049
|
549 |
if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
|
slouken@2043
|
550 |
strdesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
|
slouken@935
|
551 |
|
icculus@2049
|
552 |
if (SDL_AUDIO_ISFLOAT(this->spec.format))
|
slouken@2043
|
553 |
strdesc.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
|
icculus@2049
|
554 |
else if (SDL_AUDIO_ISSIGNED(this->spec.format))
|
slouken@2043
|
555 |
strdesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
|
slouken@2043
|
556 |
break;
|
icculus@2003
|
557 |
}
|
icculus@2003
|
558 |
}
|
icculus@2003
|
559 |
|
slouken@2043
|
560 |
if (!valid_datatype) { /* shouldn't happen, but just in case... */
|
icculus@2055
|
561 |
COREAUDIO_CloseDevice(this);
|
icculus@2003
|
562 |
SDL_SetError("Unsupported audio format");
|
icculus@2049
|
563 |
return 0;
|
icculus@2003
|
564 |
}
|
icculus@2003
|
565 |
|
icculus@2016
|
566 |
strdesc.mBytesPerFrame =
|
icculus@2016
|
567 |
strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
|
icculus@2016
|
568 |
strdesc.mBytesPerPacket =
|
icculus@2016
|
569 |
strdesc.mBytesPerFrame * strdesc.mFramesPerPacket;
|
slouken@935
|
570 |
|
icculus@2049
|
571 |
if (!prepare_audiounit(this, devname, iscapture, &strdesc)) {
|
icculus@2057
|
572 |
COREAUDIO_CloseDevice(this);
|
slouken@2060
|
573 |
return 0; /* prepare_audiounit() will call SDL_SetError()... */
|
slouken@935
|
574 |
}
|
slouken@1895
|
575 |
|
slouken@2060
|
576 |
return 1; /* good to go. */
|
slouken@1895
|
577 |
}
|
slouken@935
|
578 |
|
icculus@2049
|
579 |
static int
|
slouken@2060
|
580 |
COREAUDIO_Init(SDL_AudioDriverImpl * impl)
|
icculus@2049
|
581 |
{
|
icculus@2049
|
582 |
/* Set the function pointers */
|
icculus@2049
|
583 |
impl->DetectDevices = COREAUDIO_DetectDevices;
|
icculus@2049
|
584 |
impl->GetDeviceName = COREAUDIO_GetDeviceName;
|
icculus@2049
|
585 |
impl->OpenDevice = COREAUDIO_OpenDevice;
|
icculus@2049
|
586 |
impl->CloseDevice = COREAUDIO_CloseDevice;
|
icculus@2049
|
587 |
impl->Deinitialize = COREAUDIO_Deinitialize;
|
icculus@2049
|
588 |
impl->ProvidesOwnCallbackThread = 1;
|
icculus@2049
|
589 |
|
slouken@2060
|
590 |
build_device_lists(); /* do an initial check for devices... */
|
icculus@2049
|
591 |
|
icculus@2049
|
592 |
return 1;
|
icculus@2049
|
593 |
}
|
icculus@2049
|
594 |
|
icculus@2049
|
595 |
AudioBootStrap COREAUDIO_bootstrap = {
|
icculus@2049
|
596 |
"coreaudio", "Mac OS X CoreAudio", COREAUDIO_Init, 0
|
icculus@2049
|
597 |
};
|
icculus@2049
|
598 |
|
slouken@1895
|
599 |
/* vi: set ts=4 sw=4 expandtab: */
|