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