46b617dc063bbc1b8763e38637ea477992f0ee86
[platform/upstream/SDL.git] / src / audio / coreaudio / SDL_coreaudio.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
4
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22
23 #if SDL_AUDIO_DRIVER_COREAUDIO
24
25 #include "SDL_audio.h"
26 #include "../SDL_audio_c.h"
27 #include "../SDL_sysaudio.h"
28 #include "SDL_coreaudio.h"
29 #include "SDL_assert.h"
30
31 #define DEBUG_COREAUDIO 0
32
33 static void COREAUDIO_CloseDevice(_THIS);
34
35 #define CHECK_RESULT(msg) \
36     if (result != noErr) { \
37         COREAUDIO_CloseDevice(this); \
38         SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
39         return 0; \
40     }
41
42 #if MACOSX_COREAUDIO
43 static const AudioObjectPropertyAddress devlist_address = {
44     kAudioHardwarePropertyDevices,
45     kAudioObjectPropertyScopeGlobal,
46     kAudioObjectPropertyElementMaster
47 };
48
49 typedef void (*addDevFn)(const char *name, const int iscapture, AudioDeviceID devId, void *data);
50
51 typedef struct AudioDeviceList
52 {
53     AudioDeviceID devid;
54     SDL_bool alive;
55     struct AudioDeviceList *next;
56 } AudioDeviceList;
57
58 static AudioDeviceList *output_devs = NULL;
59 static AudioDeviceList *capture_devs = NULL;
60
61 static SDL_bool
62 add_to_internal_dev_list(const int iscapture, AudioDeviceID devId)
63 {
64     AudioDeviceList *item = (AudioDeviceList *) SDL_malloc(sizeof (AudioDeviceList));
65     if (item == NULL) {
66         return SDL_FALSE;
67     }
68     item->devid = devId;
69     item->alive = SDL_TRUE;
70     item->next = iscapture ? capture_devs : output_devs;
71     if (iscapture) {
72         capture_devs = item;
73     } else {
74         output_devs = item;
75     }
76
77     return SDL_TRUE;
78 }
79
80 static void
81 addToDevList(const char *name, const int iscapture, AudioDeviceID devId, void *data)
82 {
83     if (add_to_internal_dev_list(iscapture, devId)) {
84         SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
85     }
86 }
87
88 static void
89 build_device_list(int iscapture, addDevFn addfn, void *addfndata)
90 {
91     OSStatus result = noErr;
92     UInt32 size = 0;
93     AudioDeviceID *devs = NULL;
94     UInt32 i = 0;
95     UInt32 max = 0;
96
97     result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
98                                             &devlist_address, 0, NULL, &size);
99     if (result != kAudioHardwareNoError)
100         return;
101
102     devs = (AudioDeviceID *) alloca(size);
103     if (devs == NULL)
104         return;
105
106     result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
107                                         &devlist_address, 0, NULL, &size, devs);
108     if (result != kAudioHardwareNoError)
109         return;
110
111     max = size / sizeof (AudioDeviceID);
112     for (i = 0; i < max; i++) {
113         CFStringRef cfstr = NULL;
114         char *ptr = NULL;
115         AudioDeviceID dev = devs[i];
116         AudioBufferList *buflist = NULL;
117         int usable = 0;
118         CFIndex len = 0;
119         const AudioObjectPropertyAddress addr = {
120             kAudioDevicePropertyStreamConfiguration,
121             iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
122             kAudioObjectPropertyElementMaster
123         };
124
125         const AudioObjectPropertyAddress nameaddr = {
126             kAudioObjectPropertyName,
127             iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
128             kAudioObjectPropertyElementMaster
129         };
130
131         result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
132         if (result != noErr)
133             continue;
134
135         buflist = (AudioBufferList *) SDL_malloc(size);
136         if (buflist == NULL)
137             continue;
138
139         result = AudioObjectGetPropertyData(dev, &addr, 0, NULL,
140                                             &size, buflist);
141
142         if (result == noErr) {
143             UInt32 j;
144             for (j = 0; j < buflist->mNumberBuffers; j++) {
145                 if (buflist->mBuffers[j].mNumberChannels > 0) {
146                     usable = 1;
147                     break;
148                 }
149             }
150         }
151
152         SDL_free(buflist);
153
154         if (!usable)
155             continue;
156
157
158         size = sizeof (CFStringRef);
159         result = AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr);
160         if (result != kAudioHardwareNoError)
161             continue;
162
163         len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
164                                                 kCFStringEncodingUTF8);
165
166         ptr = (char *) SDL_malloc(len + 1);
167         usable = ((ptr != NULL) &&
168                   (CFStringGetCString
169                    (cfstr, ptr, len + 1, kCFStringEncodingUTF8)));
170
171         CFRelease(cfstr);
172
173         if (usable) {
174             len = strlen(ptr);
175             /* Some devices have whitespace at the end...trim it. */
176             while ((len > 0) && (ptr[len - 1] == ' ')) {
177                 len--;
178             }
179             usable = (len > 0);
180         }
181
182         if (usable) {
183             ptr[len] = '\0';
184
185 #if DEBUG_COREAUDIO
186             printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
187                    ((iscapture) ? "capture" : "output"),
188                    (int) *devCount, ptr, (int) dev);
189 #endif
190             addfn(ptr, iscapture, dev, addfndata);
191         }
192         SDL_free(ptr);  /* addfn() would have copied the string. */
193     }
194 }
195
196 static void
197 free_audio_device_list(AudioDeviceList **list)
198 {
199     AudioDeviceList *item = *list;
200     while (item) {
201         AudioDeviceList *next = item->next;
202         SDL_free(item);
203         item = next;
204     }
205     *list = NULL;
206 }
207
208 static void
209 COREAUDIO_DetectDevices(void)
210 {
211     build_device_list(SDL_TRUE, addToDevList, NULL);
212     build_device_list(SDL_FALSE, addToDevList, NULL);
213 }
214
215 static void
216 build_device_change_list(const char *name, const int iscapture, AudioDeviceID devId, void *data)
217 {
218     AudioDeviceList **list = (AudioDeviceList **) data;
219     AudioDeviceList *item;
220     for (item = *list; item != NULL; item = item->next) {
221         if (item->devid == devId) {
222             item->alive = SDL_TRUE;
223             return;
224         }
225     }
226
227     add_to_internal_dev_list(iscapture, devId);  /* new device, add it. */
228     SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
229 }
230
231 static void
232 reprocess_device_list(const int iscapture, AudioDeviceList **list)
233 {
234     AudioDeviceList *item;
235     AudioDeviceList *prev = NULL;
236     for (item = *list; item != NULL; item = item->next) {
237         item->alive = SDL_FALSE;
238     }
239
240     build_device_list(iscapture, build_device_change_list, list);
241
242     /* free items in the list that aren't still alive. */
243     item = *list;
244     while (item != NULL) {
245         AudioDeviceList *next = item->next;
246         if (item->alive) {
247             prev = item;
248         } else {
249             SDL_RemoveAudioDevice(iscapture, (void *) ((size_t) item->devid));
250             if (prev) {
251                 prev->next = item->next;
252             } else {
253                 *list = item->next;
254             }
255             SDL_free(item);
256         }
257         item = next;
258     }
259 }
260
261 /* this is called when the system's list of available audio devices changes. */
262 static OSStatus
263 device_list_changed(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
264 {
265     reprocess_device_list(SDL_TRUE, &capture_devs);
266     reprocess_device_list(SDL_FALSE, &output_devs);
267     return 0;
268 }
269 #endif
270
271 /* The CoreAudio callback */
272 static OSStatus
273 outputCallback(void *inRefCon,
274                AudioUnitRenderActionFlags * ioActionFlags,
275                const AudioTimeStamp * inTimeStamp,
276                UInt32 inBusNumber, UInt32 inNumberFrames,
277                AudioBufferList * ioData)
278 {
279     SDL_AudioDevice *this = (SDL_AudioDevice *) inRefCon;
280     AudioBuffer *abuf;
281     UInt32 remaining, len;
282     void *ptr;
283     UInt32 i;
284
285     /* Only do anything if audio is enabled and not paused */
286     if (!this->enabled || this->paused) {
287         for (i = 0; i < ioData->mNumberBuffers; i++) {
288             abuf = &ioData->mBuffers[i];
289             SDL_memset(abuf->mData, this->spec.silence, abuf->mDataByteSize);
290         }
291         return 0;
292     }
293
294     /* No SDL conversion should be needed here, ever, since we accept
295        any input format in OpenAudio, and leave the conversion to CoreAudio.
296      */
297     /*
298        SDL_assert(!this->convert.needed);
299        SDL_assert(this->spec.channels == ioData->mNumberChannels);
300      */
301
302     for (i = 0; i < ioData->mNumberBuffers; i++) {
303         abuf = &ioData->mBuffers[i];
304         remaining = abuf->mDataByteSize;
305         ptr = abuf->mData;
306         while (remaining > 0) {
307             if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
308                 /* Generate the data */
309                 SDL_LockMutex(this->mixer_lock);
310                 (*this->spec.callback)(this->spec.userdata,
311                             this->hidden->buffer, this->hidden->bufferSize);
312                 SDL_UnlockMutex(this->mixer_lock);
313                 this->hidden->bufferOffset = 0;
314             }
315
316             len = this->hidden->bufferSize - this->hidden->bufferOffset;
317             if (len > remaining)
318                 len = remaining;
319             SDL_memcpy(ptr, (char *)this->hidden->buffer +
320                        this->hidden->bufferOffset, len);
321             ptr = (char *)ptr + len;
322             remaining -= len;
323             this->hidden->bufferOffset += len;
324         }
325     }
326
327     return 0;
328 }
329
330 static OSStatus
331 inputCallback(void *inRefCon,
332               AudioUnitRenderActionFlags * ioActionFlags,
333               const AudioTimeStamp * inTimeStamp,
334               UInt32 inBusNumber, UInt32 inNumberFrames,
335               AudioBufferList * ioData)
336 {
337     /* err = AudioUnitRender(afr->fAudioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, afr->fAudioBuffer); */
338     /* !!! FIXME: write me! */
339     return noErr;
340 }
341
342
343 #if MACOSX_COREAUDIO
344 static const AudioObjectPropertyAddress alive_address =
345 {
346     kAudioDevicePropertyDeviceIsAlive,
347     kAudioObjectPropertyScopeGlobal,
348     kAudioObjectPropertyElementMaster
349 };
350
351 static OSStatus
352 device_unplugged(AudioObjectID devid, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
353 {
354     SDL_AudioDevice *this = (SDL_AudioDevice *) data;
355     SDL_bool dead = SDL_FALSE;
356     UInt32 isAlive = 1;
357     UInt32 size = sizeof (isAlive);
358     OSStatus error;
359
360     if (!this->enabled) {
361         return 0;  /* already known to be dead. */
362     }
363
364     error = AudioObjectGetPropertyData(this->hidden->deviceID, &alive_address,
365                                        0, NULL, &size, &isAlive);
366
367     if (error == kAudioHardwareBadDeviceError) {
368         dead = SDL_TRUE;  /* device was unplugged. */
369     } else if ((error == kAudioHardwareNoError) && (!isAlive)) {
370         dead = SDL_TRUE;  /* device died in some other way. */
371     }
372
373     if (dead) {
374         SDL_OpenedAudioDeviceDisconnected(this);
375     }
376
377     return 0;
378 }
379 #endif
380
381 static void
382 COREAUDIO_CloseDevice(_THIS)
383 {
384     if (this->hidden != NULL) {
385         if (this->hidden->audioUnitOpened) {
386             #if MACOSX_COREAUDIO
387             /* Unregister our disconnect callback. */
388             AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
389             #endif
390
391             AURenderCallbackStruct callback;
392             const AudioUnitElement output_bus = 0;
393             const AudioUnitElement input_bus = 1;
394             const int iscapture = this->iscapture;
395             const AudioUnitElement bus =
396                 ((iscapture) ? input_bus : output_bus);
397             const AudioUnitScope scope =
398                 ((iscapture) ? kAudioUnitScope_Output :
399                  kAudioUnitScope_Input);
400
401             /* stop processing the audio unit */
402             AudioOutputUnitStop(this->hidden->audioUnit);
403
404             /* Remove the input callback */
405             SDL_memset(&callback, 0, sizeof(AURenderCallbackStruct));
406             AudioUnitSetProperty(this->hidden->audioUnit,
407                                  kAudioUnitProperty_SetRenderCallback,
408                                  scope, bus, &callback, sizeof(callback));
409
410             #if MACOSX_COREAUDIO
411             CloseComponent(this->hidden->audioUnit);
412             #else
413             AudioComponentInstanceDispose(this->hidden->audioUnit);
414             #endif
415
416             this->hidden->audioUnitOpened = 0;
417         }
418         SDL_free(this->hidden->buffer);
419         SDL_free(this->hidden);
420         this->hidden = NULL;
421     }
422 }
423
424 #if MACOSX_COREAUDIO
425 static int
426 prepare_device(_THIS, void *handle, int iscapture)
427 {
428     AudioDeviceID devid = (AudioDeviceID) ((size_t) handle);
429     OSStatus result = noErr;
430     UInt32 size = 0;
431     UInt32 alive = 0;
432     pid_t pid = 0;
433
434     AudioObjectPropertyAddress addr = {
435         0,
436         kAudioObjectPropertyScopeGlobal,
437         kAudioObjectPropertyElementMaster
438     };
439
440     if (handle == NULL) {
441         size = sizeof (AudioDeviceID);
442         addr.mSelector =
443             ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
444             kAudioHardwarePropertyDefaultOutputDevice);
445         result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
446                                             0, NULL, &size, &devid);
447         CHECK_RESULT("AudioHardwareGetProperty (default device)");
448     }
449
450     addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
451     addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
452                     kAudioDevicePropertyScopeOutput;
453
454     size = sizeof (alive);
455     result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &alive);
456     CHECK_RESULT
457         ("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
458
459     if (!alive) {
460         SDL_SetError("CoreAudio: requested device exists, but isn't alive.");
461         return 0;
462     }
463
464     addr.mSelector = kAudioDevicePropertyHogMode;
465     size = sizeof (pid);
466     result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &pid);
467
468     /* some devices don't support this property, so errors are fine here. */
469     if ((result == noErr) && (pid != -1)) {
470         SDL_SetError("CoreAudio: requested device is being hogged.");
471         return 0;
472     }
473
474     this->hidden->deviceID = devid;
475     return 1;
476 }
477 #endif
478
479 static int
480 prepare_audiounit(_THIS, void *handle, int iscapture,
481                   const AudioStreamBasicDescription * strdesc)
482 {
483     OSStatus result = noErr;
484     AURenderCallbackStruct callback;
485 #if MACOSX_COREAUDIO
486     ComponentDescription desc;
487     Component comp = NULL;
488 #else
489     AudioComponentDescription desc;
490     AudioComponent comp = NULL;
491 #endif
492     const AudioUnitElement output_bus = 0;
493     const AudioUnitElement input_bus = 1;
494     const AudioUnitElement bus = ((iscapture) ? input_bus : output_bus);
495     const AudioUnitScope scope = ((iscapture) ? kAudioUnitScope_Output :
496                                   kAudioUnitScope_Input);
497
498 #if MACOSX_COREAUDIO
499     if (!prepare_device(this, handle, iscapture)) {
500         return 0;
501     }
502 #endif
503
504     SDL_zero(desc);
505     desc.componentType = kAudioUnitType_Output;
506     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
507
508 #if MACOSX_COREAUDIO
509     desc.componentSubType = kAudioUnitSubType_DefaultOutput;
510     comp = FindNextComponent(NULL, &desc);
511 #else
512     desc.componentSubType = kAudioUnitSubType_RemoteIO;
513     comp = AudioComponentFindNext(NULL, &desc);
514 #endif
515
516     if (comp == NULL) {
517         SDL_SetError("Couldn't find requested CoreAudio component");
518         return 0;
519     }
520
521     /* Open & initialize the audio unit */
522 #if MACOSX_COREAUDIO
523     result = OpenAComponent(comp, &this->hidden->audioUnit);
524     CHECK_RESULT("OpenAComponent");
525 #else
526     /*
527        AudioComponentInstanceNew only available on iPhone OS 2.0 and Mac OS X 10.6
528        We can't use OpenAComponent on iPhone because it is not present
529      */
530     result = AudioComponentInstanceNew(comp, &this->hidden->audioUnit);
531     CHECK_RESULT("AudioComponentInstanceNew");
532 #endif
533
534     this->hidden->audioUnitOpened = 1;
535
536 #if MACOSX_COREAUDIO
537     result = AudioUnitSetProperty(this->hidden->audioUnit,
538                                   kAudioOutputUnitProperty_CurrentDevice,
539                                   kAudioUnitScope_Global, 0,
540                                   &this->hidden->deviceID,
541                                   sizeof(AudioDeviceID));
542     CHECK_RESULT
543         ("AudioUnitSetProperty (kAudioOutputUnitProperty_CurrentDevice)");
544 #endif
545
546     /* Set the data format of the audio unit. */
547     result = AudioUnitSetProperty(this->hidden->audioUnit,
548                                   kAudioUnitProperty_StreamFormat,
549                                   scope, bus, strdesc, sizeof(*strdesc));
550     CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)");
551
552     /* Set the audio callback */
553     SDL_memset(&callback, 0, sizeof(AURenderCallbackStruct));
554     callback.inputProc = ((iscapture) ? inputCallback : outputCallback);
555     callback.inputProcRefCon = this;
556     result = AudioUnitSetProperty(this->hidden->audioUnit,
557                                   kAudioUnitProperty_SetRenderCallback,
558                                   scope, bus, &callback, sizeof(callback));
559     CHECK_RESULT
560         ("AudioUnitSetProperty (kAudioUnitProperty_SetRenderCallback)");
561
562     /* Calculate the final parameters for this audio specification */
563     SDL_CalculateAudioSpec(&this->spec);
564
565     /* Allocate a sample buffer */
566     this->hidden->bufferOffset = this->hidden->bufferSize = this->spec.size;
567     this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
568
569     result = AudioUnitInitialize(this->hidden->audioUnit);
570     CHECK_RESULT("AudioUnitInitialize");
571
572     /* Finally, start processing of the audio unit */
573     result = AudioOutputUnitStart(this->hidden->audioUnit);
574     CHECK_RESULT("AudioOutputUnitStart");
575
576 #if MACOSX_COREAUDIO
577     /* Fire a callback if the device stops being "alive" (disconnected, etc). */
578     AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
579 #endif
580
581     /* We're running! */
582     return 1;
583 }
584
585
586 static int
587 COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
588 {
589     AudioStreamBasicDescription strdesc;
590     SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
591     int valid_datatype = 0;
592
593     /* Initialize all variables that we clean on shutdown */
594     this->hidden = (struct SDL_PrivateAudioData *)
595         SDL_malloc((sizeof *this->hidden));
596     if (this->hidden == NULL) {
597         return SDL_OutOfMemory();
598     }
599     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
600
601     /* Setup a AudioStreamBasicDescription with the requested format */
602     SDL_memset(&strdesc, '\0', sizeof(AudioStreamBasicDescription));
603     strdesc.mFormatID = kAudioFormatLinearPCM;
604     strdesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
605     strdesc.mChannelsPerFrame = this->spec.channels;
606     strdesc.mSampleRate = this->spec.freq;
607     strdesc.mFramesPerPacket = 1;
608
609     while ((!valid_datatype) && (test_format)) {
610         this->spec.format = test_format;
611         /* Just a list of valid SDL formats, so people don't pass junk here. */
612         switch (test_format) {
613         case AUDIO_U8:
614         case AUDIO_S8:
615         case AUDIO_U16LSB:
616         case AUDIO_S16LSB:
617         case AUDIO_U16MSB:
618         case AUDIO_S16MSB:
619         case AUDIO_S32LSB:
620         case AUDIO_S32MSB:
621         case AUDIO_F32LSB:
622         case AUDIO_F32MSB:
623             valid_datatype = 1;
624             strdesc.mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
625             if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
626                 strdesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
627
628             if (SDL_AUDIO_ISFLOAT(this->spec.format))
629                 strdesc.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
630             else if (SDL_AUDIO_ISSIGNED(this->spec.format))
631                 strdesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
632             break;
633         }
634     }
635
636     if (!valid_datatype) {      /* shouldn't happen, but just in case... */
637         COREAUDIO_CloseDevice(this);
638         return SDL_SetError("Unsupported audio format");
639     }
640
641     strdesc.mBytesPerFrame =
642         strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
643     strdesc.mBytesPerPacket =
644         strdesc.mBytesPerFrame * strdesc.mFramesPerPacket;
645
646     if (!prepare_audiounit(this, handle, iscapture, &strdesc)) {
647         COREAUDIO_CloseDevice(this);
648         return -1;      /* prepare_audiounit() will call SDL_SetError()... */
649     }
650
651     return 0;   /* good to go. */
652 }
653
654 static void
655 COREAUDIO_Deinitialize(void)
656 {
657 #if MACOSX_COREAUDIO
658     AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
659     free_audio_device_list(&capture_devs);
660     free_audio_device_list(&output_devs);
661 #endif
662 }
663
664 static int
665 COREAUDIO_Init(SDL_AudioDriverImpl * impl)
666 {
667     /* Set the function pointers */
668     impl->OpenDevice = COREAUDIO_OpenDevice;
669     impl->CloseDevice = COREAUDIO_CloseDevice;
670     impl->Deinitialize = COREAUDIO_Deinitialize;
671
672 #if MACOSX_COREAUDIO
673     impl->DetectDevices = COREAUDIO_DetectDevices;
674     AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
675 #else
676     impl->OnlyHasDefaultOutputDevice = 1;
677
678     /* Set category to ambient sound so that other music continues playing.
679        You can change this at runtime in your own code if you need different
680        behavior.  If this is common, we can add an SDL hint for this.
681     */
682     AudioSessionInitialize(NULL, NULL, NULL, nil);
683     UInt32 category = kAudioSessionCategory_AmbientSound;
684     AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(UInt32), &category);
685 #endif
686
687     impl->ProvidesOwnCallbackThread = 1;
688
689     return 1;   /* this audio target is available. */
690 }
691
692 AudioBootStrap COREAUDIO_bootstrap = {
693     "coreaudio", "CoreAudio", COREAUDIO_Init, 0
694 };
695
696 #endif /* SDL_AUDIO_DRIVER_COREAUDIO */
697
698 /* vi: set ts=4 sw=4 expandtab: */