SDL 2.0
SDL_coreaudio.m
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 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/* !!! FIXME: clean out some of the macro salsa in here. */
26
27#include "SDL_audio.h"
28#include "SDL_hints.h"
29#include "../SDL_audio_c.h"
30#include "../SDL_sysaudio.h"
31#include "SDL_coreaudio.h"
32#include "SDL_assert.h"
33#include "../../thread/SDL_systhread.h"
34
35#define DEBUG_COREAUDIO 0
36
37#define CHECK_RESULT(msg) \
38 if (result != noErr) { \
39 SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
40 return 0; \
41 }
42
43#if MACOSX_COREAUDIO
44static const AudioObjectPropertyAddress devlist_address = {
45 kAudioHardwarePropertyDevices,
46 kAudioObjectPropertyScopeGlobal,
47 kAudioObjectPropertyElementMaster
48};
49
50typedef void (*addDevFn)(const char *name, const int iscapture, AudioDeviceID devId, void *data);
51
52typedef struct AudioDeviceList
53{
54 AudioDeviceID devid;
56 struct AudioDeviceList *next;
57} AudioDeviceList;
58
59static AudioDeviceList *output_devs = NULL;
60static AudioDeviceList *capture_devs = NULL;
61
62static SDL_bool
63add_to_internal_dev_list(const int iscapture, AudioDeviceID devId)
64{
65 AudioDeviceList *item = (AudioDeviceList *) SDL_malloc(sizeof (AudioDeviceList));
66 if (item == NULL) {
67 return SDL_FALSE;
68 }
69 item->devid = devId;
70 item->alive = SDL_TRUE;
71 item->next = iscapture ? capture_devs : output_devs;
72 if (iscapture) {
73 capture_devs = item;
74 } else {
75 output_devs = item;
76 }
77
78 return SDL_TRUE;
79}
80
81static void
82addToDevList(const char *name, const int iscapture, AudioDeviceID devId, void *data)
83{
84 if (add_to_internal_dev_list(iscapture, devId)) {
85 SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
86 }
87}
88
89static void
90build_device_list(int iscapture, addDevFn addfn, void *addfndata)
91{
92 OSStatus result = noErr;
93 UInt32 size = 0;
94 AudioDeviceID *devs = NULL;
95 UInt32 i = 0;
96 UInt32 max = 0;
97
98 result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
99 &devlist_address, 0, NULL, &size);
100 if (result != kAudioHardwareNoError)
101 return;
102
103 devs = (AudioDeviceID *) alloca(size);
104 if (devs == NULL)
105 return;
106
107 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
108 &devlist_address, 0, NULL, &size, devs);
109 if (result != kAudioHardwareNoError)
110 return;
111
112 max = size / sizeof (AudioDeviceID);
113 for (i = 0; i < max; i++) {
114 CFStringRef cfstr = NULL;
115 char *ptr = NULL;
116 AudioDeviceID dev = devs[i];
117 AudioBufferList *buflist = NULL;
118 int usable = 0;
119 CFIndex len = 0;
120 const AudioObjectPropertyAddress addr = {
121 kAudioDevicePropertyStreamConfiguration,
122 iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
123 kAudioObjectPropertyElementMaster
124 };
125
126 const AudioObjectPropertyAddress nameaddr = {
127 kAudioObjectPropertyName,
128 iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
129 kAudioObjectPropertyElementMaster
130 };
131
132 result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
133 if (result != noErr)
134 continue;
135
136 buflist = (AudioBufferList *) SDL_malloc(size);
137 if (buflist == NULL)
138 continue;
139
140 result = AudioObjectGetPropertyData(dev, &addr, 0, NULL,
141 &size, buflist);
142
143 if (result == noErr) {
144 UInt32 j;
145 for (j = 0; j < buflist->mNumberBuffers; j++) {
146 if (buflist->mBuffers[j].mNumberChannels > 0) {
147 usable = 1;
148 break;
149 }
150 }
151 }
152
153 SDL_free(buflist);
154
155 if (!usable)
156 continue;
157
158
159 size = sizeof (CFStringRef);
160 result = AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr);
161 if (result != kAudioHardwareNoError)
162 continue;
163
164 len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
165 kCFStringEncodingUTF8);
166
167 ptr = (char *) SDL_malloc(len + 1);
168 usable = ((ptr != NULL) &&
169 (CFStringGetCString
170 (cfstr, ptr, len + 1, kCFStringEncodingUTF8)));
171
172 CFRelease(cfstr);
173
174 if (usable) {
175 len = strlen(ptr);
176 /* Some devices have whitespace at the end...trim it. */
177 while ((len > 0) && (ptr[len - 1] == ' ')) {
178 len--;
179 }
180 usable = (len > 0);
181 }
182
183 if (usable) {
184 ptr[len] = '\0';
185
186#if DEBUG_COREAUDIO
187 printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
188 ((iscapture) ? "capture" : "output"),
189 (int) i, ptr, (int) dev);
190#endif
191 addfn(ptr, iscapture, dev, addfndata);
192 }
193 SDL_free(ptr); /* addfn() would have copied the string. */
194 }
195}
196
197static void
198free_audio_device_list(AudioDeviceList **list)
199{
200 AudioDeviceList *item = *list;
201 while (item) {
202 AudioDeviceList *next = item->next;
203 SDL_free(item);
204 item = next;
205 }
206 *list = NULL;
207}
208
209static void
210COREAUDIO_DetectDevices(void)
211{
212 build_device_list(SDL_TRUE, addToDevList, NULL);
213 build_device_list(SDL_FALSE, addToDevList, NULL);
214}
215
216static void
217build_device_change_list(const char *name, const int iscapture, AudioDeviceID devId, void *data)
218{
219 AudioDeviceList **list = (AudioDeviceList **) data;
220 AudioDeviceList *item;
221 for (item = *list; item != NULL; item = item->next) {
222 if (item->devid == devId) {
223 item->alive = SDL_TRUE;
224 return;
225 }
226 }
227
228 add_to_internal_dev_list(iscapture, devId); /* new device, add it. */
229 SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
230}
231
232static void
233reprocess_device_list(const int iscapture, AudioDeviceList **list)
234{
235 AudioDeviceList *item;
236 AudioDeviceList *prev = NULL;
237 for (item = *list; item != NULL; item = item->next) {
238 item->alive = SDL_FALSE;
239 }
240
241 build_device_list(iscapture, build_device_change_list, list);
242
243 /* free items in the list that aren't still alive. */
244 item = *list;
245 while (item != NULL) {
246 AudioDeviceList *next = item->next;
247 if (item->alive) {
248 prev = item;
249 } else {
250 SDL_RemoveAudioDevice(iscapture, (void *) ((size_t) item->devid));
251 if (prev) {
252 prev->next = item->next;
253 } else {
254 *list = item->next;
255 }
256 SDL_free(item);
257 }
258 item = next;
259 }
260}
261
262/* this is called when the system's list of available audio devices changes. */
263static OSStatus
264device_list_changed(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
265{
266 reprocess_device_list(SDL_TRUE, &capture_devs);
267 reprocess_device_list(SDL_FALSE, &output_devs);
268 return 0;
269}
270#endif
271
272
273static int open_playback_devices = 0;
274static int open_capture_devices = 0;
275
276#if !MACOSX_COREAUDIO
277
278static void interruption_begin(_THIS)
279{
280 if (this != NULL && this->hidden->audioQueue != NULL) {
281 this->hidden->interrupted = SDL_TRUE;
282 AudioQueuePause(this->hidden->audioQueue);
283 }
284}
285
286static void interruption_end(_THIS)
287{
288 if (this != NULL && this->hidden != NULL && this->hidden->audioQueue != NULL
289 && this->hidden->interrupted
290 && AudioQueueStart(this->hidden->audioQueue, NULL) == AVAudioSessionErrorCodeNone) {
291 this->hidden->interrupted = SDL_FALSE;
292 }
293}
294
295@interface SDLInterruptionListener : NSObject
296
297@property (nonatomic, assign) SDL_AudioDevice *device;
298
299@end
300
301@implementation SDLInterruptionListener
302
303- (void)audioSessionInterruption:(NSNotification *)note
304{
305 @synchronized (self) {
306 NSNumber *type = note.userInfo[AVAudioSessionInterruptionTypeKey];
307 if (type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
308 interruption_begin(self.device);
309 } else {
310 interruption_end(self.device);
311 }
312 }
313}
314
315- (void)applicationBecameActive:(NSNotification *)note
316{
317 @synchronized (self) {
318 interruption_end(self.device);
319 }
320}
321
322@end
323
324static BOOL update_audio_session(_THIS, SDL_bool open)
325{
326 @autoreleasepool {
327 AVAudioSession *session = [AVAudioSession sharedInstance];
328 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
329 /* Set category to ambient by default so that other music continues playing. */
330 NSString *category = AVAudioSessionCategoryAmbient;
331 NSError *err = nil;
332
333 if (open_playback_devices && open_capture_devices) {
334 category = AVAudioSessionCategoryPlayAndRecord;
335 } else if (open_capture_devices) {
336 category = AVAudioSessionCategoryRecord;
337 } else {
338 const char *hint = SDL_GetHint(SDL_HINT_AUDIO_CATEGORY);
339 if (hint) {
340 if (SDL_strcasecmp(hint, "AVAudioSessionCategoryAmbient") == 0) {
341 category = AVAudioSessionCategoryAmbient;
342 } else if (SDL_strcasecmp(hint, "AVAudioSessionCategorySoloAmbient") == 0) {
343 category = AVAudioSessionCategorySoloAmbient;
344 } else if (SDL_strcasecmp(hint, "AVAudioSessionCategoryPlayback") == 0 ||
345 SDL_strcasecmp(hint, "playback") == 0) {
346 category = AVAudioSessionCategoryPlayback;
347 }
348 }
349 }
350
351 if (![session setCategory:category error:&err]) {
352 NSString *desc = err.description;
353 SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
354 return NO;
355 }
356
357 if (open && (open_playback_devices + open_capture_devices) == 1) {
358 if (![session setActive:YES error:&err]) {
359 NSString *desc = err.description;
360 SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String);
361 return NO;
362 }
363 } else if (!open_playback_devices && !open_capture_devices) {
364 [session setActive:NO error:nil];
365 }
366
367 if (open) {
368 SDLInterruptionListener *listener = [SDLInterruptionListener new];
369 listener.device = this;
370
371 [center addObserver:listener
372 selector:@selector(audioSessionInterruption:)
373 name:AVAudioSessionInterruptionNotification
374 object:session];
375
376 /* An interruption end notification is not guaranteed to be sent if
377 we were previously interrupted... resuming if needed when the app
378 becomes active seems to be the way to go. */
379 // Note: object: below needs to be nil, as otherwise it filters by the object, and session doesn't send foreground / active notifications. johna
380 [center addObserver:listener
381 selector:@selector(applicationBecameActive:)
382 name:UIApplicationDidBecomeActiveNotification
383 object:nil];
384
385 [center addObserver:listener
386 selector:@selector(applicationBecameActive:)
387 name:UIApplicationWillEnterForegroundNotification
388 object:nil];
389
390 this->hidden->interruption_listener = CFBridgingRetain(listener);
391 } else {
392 if (this->hidden->interruption_listener != NULL) {
393 SDLInterruptionListener *listener = nil;
394 listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener);
395 [center removeObserver:listener];
396 @synchronized (listener) {
397 listener.device = NULL;
398 }
399 }
400 }
401 }
402
403 return YES;
404}
405#endif
406
407
408/* The AudioQueue callback */
409static void
410outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
411{
412 SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
413 if (SDL_AtomicGet(&this->hidden->shutdown)) {
414 return; /* don't do anything. */
415 }
416
417 if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
418 /* Supply silence if audio is not enabled or paused */
419 SDL_memset(inBuffer->mAudioData, this->spec.silence, inBuffer->mAudioDataBytesCapacity);
420 } else {
421 UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
422 Uint8 *ptr = (Uint8 *) inBuffer->mAudioData;
423
424 while (remaining > 0) {
425 UInt32 len;
426 if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
427 /* Generate the data */
428 SDL_LockMutex(this->mixer_lock);
429 (*this->callbackspec.callback)(this->callbackspec.userdata,
430 this->hidden->buffer, this->hidden->bufferSize);
431 SDL_UnlockMutex(this->mixer_lock);
432 this->hidden->bufferOffset = 0;
433 }
434
435 len = this->hidden->bufferSize - this->hidden->bufferOffset;
436 if (len > remaining) {
437 len = remaining;
438 }
439 SDL_memcpy(ptr, (char *)this->hidden->buffer +
440 this->hidden->bufferOffset, len);
441 ptr = ptr + len;
442 remaining -= len;
443 this->hidden->bufferOffset += len;
444 }
445 }
446
447 AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
448
449 inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
450}
451
452static void
453inputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
454 const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions,
455 const AudioStreamPacketDescription *inPacketDescs )
456{
457 SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
458
459 if (SDL_AtomicGet(&this->shutdown)) {
460 return; /* don't do anything. */
461 }
462
463 /* ignore unless we're active. */
464 if (!SDL_AtomicGet(&this->paused) && SDL_AtomicGet(&this->enabled) && !SDL_AtomicGet(&this->paused)) {
465 const Uint8 *ptr = (const Uint8 *) inBuffer->mAudioData;
466 UInt32 remaining = inBuffer->mAudioDataByteSize;
467 while (remaining > 0) {
468 UInt32 len = this->hidden->bufferSize - this->hidden->bufferOffset;
469 if (len > remaining) {
470 len = remaining;
471 }
472
473 SDL_memcpy((char *)this->hidden->buffer + this->hidden->bufferOffset, ptr, len);
474 ptr += len;
475 remaining -= len;
476 this->hidden->bufferOffset += len;
477
478 if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
479 SDL_LockMutex(this->mixer_lock);
480 (*this->callbackspec.callback)(this->callbackspec.userdata, this->hidden->buffer, this->hidden->bufferSize);
481 SDL_UnlockMutex(this->mixer_lock);
482 this->hidden->bufferOffset = 0;
483 }
484 }
485 }
486
487 AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
488}
489
490
491#if MACOSX_COREAUDIO
492static const AudioObjectPropertyAddress alive_address =
493{
494 kAudioDevicePropertyDeviceIsAlive,
495 kAudioObjectPropertyScopeGlobal,
496 kAudioObjectPropertyElementMaster
497};
498
499static OSStatus
500device_unplugged(AudioObjectID devid, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
501{
503 SDL_bool dead = SDL_FALSE;
504 UInt32 isAlive = 1;
505 UInt32 size = sizeof (isAlive);
506 OSStatus error;
507
508 if (!SDL_AtomicGet(&this->enabled)) {
509 return 0; /* already known to be dead. */
510 }
511
512 error = AudioObjectGetPropertyData(this->hidden->deviceID, &alive_address,
513 0, NULL, &size, &isAlive);
514
515 if (error == kAudioHardwareBadDeviceError) {
516 dead = SDL_TRUE; /* device was unplugged. */
517 } else if ((error == kAudioHardwareNoError) && (!isAlive)) {
518 dead = SDL_TRUE; /* device died in some other way. */
519 }
520
521 if (dead) {
523 }
524
525 return 0;
526}
527#endif
528
529static void
530COREAUDIO_CloseDevice(_THIS)
531{
532 const SDL_bool iscapture = this->iscapture;
533
534/* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
535/* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
536#if MACOSX_COREAUDIO
537 /* Fire a callback if the device stops being "alive" (disconnected, etc). */
538 AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
539#endif
540
541#if !MACOSX_COREAUDIO
542 update_audio_session(this, SDL_FALSE);
543#endif
544
545 /* if callback fires again, feed silence; don't call into the app. */
546 SDL_AtomicSet(&this->paused, 1);
547
548 if (this->hidden->audioQueue) {
549 AudioQueueDispose(this->hidden->audioQueue, 1);
550 }
551
552 if (this->hidden->thread) {
553 SDL_AtomicSet(&this->hidden->shutdown, 1);
554 SDL_WaitThread(this->hidden->thread, NULL);
555 }
556
557 if (this->hidden->ready_semaphore) {
558 SDL_DestroySemaphore(this->hidden->ready_semaphore);
559 }
560
561 /* AudioQueueDispose() frees the actual buffer objects. */
562 SDL_free(this->hidden->audioBuffer);
563 SDL_free(this->hidden->thread_error);
564 SDL_free(this->hidden->buffer);
565 SDL_free(this->hidden);
566
567 if (iscapture) {
568 open_capture_devices--;
569 } else {
570 open_playback_devices--;
571 }
572}
573
574#if MACOSX_COREAUDIO
575static int
576prepare_device(_THIS, void *handle, int iscapture)
577{
578 AudioDeviceID devid = (AudioDeviceID) ((size_t) handle);
579 OSStatus result = noErr;
580 UInt32 size = 0;
581 UInt32 alive = 0;
582 pid_t pid = 0;
583
584 AudioObjectPropertyAddress addr = {
585 0,
586 kAudioObjectPropertyScopeGlobal,
587 kAudioObjectPropertyElementMaster
588 };
589
590 if (handle == NULL) {
591 size = sizeof (AudioDeviceID);
592 addr.mSelector =
593 ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
594 kAudioHardwarePropertyDefaultOutputDevice);
595 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
596 0, NULL, &size, &devid);
597 CHECK_RESULT("AudioHardwareGetProperty (default device)");
598 }
599
600 addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
601 addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
602 kAudioDevicePropertyScopeOutput;
603
604 size = sizeof (alive);
605 result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &alive);
606 CHECK_RESULT
607 ("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
608
609 if (!alive) {
610 SDL_SetError("CoreAudio: requested device exists, but isn't alive.");
611 return 0;
612 }
613
614 addr.mSelector = kAudioDevicePropertyHogMode;
615 size = sizeof (pid);
616 result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &pid);
617
618 /* some devices don't support this property, so errors are fine here. */
619 if ((result == noErr) && (pid != -1)) {
620 SDL_SetError("CoreAudio: requested device is being hogged.");
621 return 0;
622 }
623
624 this->hidden->deviceID = devid;
625 return 1;
626}
627#endif
628
629static int
630prepare_audioqueue(_THIS)
631{
632 const AudioStreamBasicDescription *strdesc = &this->hidden->strdesc;
633 const int iscapture = this->iscapture;
634 OSStatus result;
635 int i;
636
637 SDL_assert(CFRunLoopGetCurrent() != NULL);
638
639 if (iscapture) {
640 result = AudioQueueNewInput(strdesc, inputCallback, this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
641 CHECK_RESULT("AudioQueueNewInput");
642 } else {
643 result = AudioQueueNewOutput(strdesc, outputCallback, this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
644 CHECK_RESULT("AudioQueueNewOutput");
645 }
646
647#if MACOSX_COREAUDIO
648{
649 const AudioObjectPropertyAddress prop = {
650 kAudioDevicePropertyDeviceUID,
651 iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
652 kAudioObjectPropertyElementMaster
653 };
654 CFStringRef devuid;
655 UInt32 devuidsize = sizeof (devuid);
656 result = AudioObjectGetPropertyData(this->hidden->deviceID, &prop, 0, NULL, &devuidsize, &devuid);
657 CHECK_RESULT("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID)");
658 result = AudioQueueSetProperty(this->hidden->audioQueue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize);
659 CHECK_RESULT("AudioQueueSetProperty (kAudioQueueProperty_CurrentDevice)");
660
661 /* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
662 /* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
663 /* Fire a callback if the device stops being "alive" (disconnected, etc). */
664 AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
665}
666#endif
667
668 /* Calculate the final parameters for this audio specification */
670
671 /* Allocate a sample buffer */
672 this->hidden->bufferSize = this->spec.size;
673 this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize;
674
675 this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
676 if (this->hidden->buffer == NULL) {
678 return 0;
679 }
680
681 /* Make sure we can feed the device a minimum amount of time */
682 double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0;
683#if defined(__IPHONEOS__)
684 if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) {
685 /* Older iOS hardware, use 40 ms as a minimum time */
686 MINIMUM_AUDIO_BUFFER_TIME_MS = 40.0;
687 }
688#endif
689 const double msecs = (this->spec.samples / ((double) this->spec.freq)) * 1000.0;
690 int numAudioBuffers = 2;
691 if (msecs < MINIMUM_AUDIO_BUFFER_TIME_MS) { /* use more buffers if we have a VERY small sample set. */
692 numAudioBuffers = ((int)SDL_ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2);
693 }
694
695 this->hidden->audioBuffer = SDL_calloc(1, sizeof (AudioQueueBufferRef) * numAudioBuffers);
696 if (this->hidden->audioBuffer == NULL) {
698 return 0;
699 }
700
701#if DEBUG_COREAUDIO
702 printf("COREAUDIO: numAudioBuffers == %d\n", numAudioBuffers);
703#endif
704
705 for (i = 0; i < numAudioBuffers; i++) {
706 result = AudioQueueAllocateBuffer(this->hidden->audioQueue, this->spec.size, &this->hidden->audioBuffer[i]);
707 CHECK_RESULT("AudioQueueAllocateBuffer");
708 SDL_memset(this->hidden->audioBuffer[i]->mAudioData, this->spec.silence, this->hidden->audioBuffer[i]->mAudioDataBytesCapacity);
709 this->hidden->audioBuffer[i]->mAudioDataByteSize = this->hidden->audioBuffer[i]->mAudioDataBytesCapacity;
710 result = AudioQueueEnqueueBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i], 0, NULL);
711 CHECK_RESULT("AudioQueueEnqueueBuffer");
712 }
713
714 result = AudioQueueStart(this->hidden->audioQueue, NULL);
715 CHECK_RESULT("AudioQueueStart");
716
717 /* We're running! */
718 return 1;
719}
720
721static int
722audioqueue_thread(void *arg)
723{
724 SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
725 const int rc = prepare_audioqueue(this);
726 if (!rc) {
727 this->hidden->thread_error = SDL_strdup(SDL_GetError());
728 SDL_SemPost(this->hidden->ready_semaphore);
729 return 0;
730 }
731
733
734 /* init was successful, alert parent thread and start running... */
735 SDL_SemPost(this->hidden->ready_semaphore);
736 while (!SDL_AtomicGet(&this->hidden->shutdown)) {
737 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
738 }
739
740 if (!this->iscapture) { /* Drain off any pending playback. */
741 const CFTimeInterval secs = (((this->spec.size / (SDL_AUDIO_BITSIZE(this->spec.format) / 8)) / this->spec.channels) / ((CFTimeInterval) this->spec.freq)) * 2.0;
742 CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0);
743 }
744
745 return 0;
746}
747
748static int
749COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
750{
751 AudioStreamBasicDescription *strdesc;
752 SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
753 int valid_datatype = 0;
754
755 /* Initialize all variables that we clean on shutdown */
756 this->hidden = (struct SDL_PrivateAudioData *)
757 SDL_malloc((sizeof *this->hidden));
758 if (this->hidden == NULL) {
759 return SDL_OutOfMemory();
760 }
761 SDL_zerop(this->hidden);
762
763 strdesc = &this->hidden->strdesc;
764
765 if (iscapture) {
766 open_capture_devices++;
767 } else {
768 open_playback_devices++;
769 }
770
771#if !MACOSX_COREAUDIO
772 if (!update_audio_session(this, SDL_TRUE)) {
773 return -1;
774 }
775
776 /* Stop CoreAudio from doing expensive audio rate conversion */
777 @autoreleasepool {
778 AVAudioSession* session = [AVAudioSession sharedInstance];
779 [session setPreferredSampleRate:this->spec.freq error:nil];
780 this->spec.freq = (int)session.sampleRate;
781 }
782#endif
783
784 /* Setup a AudioStreamBasicDescription with the requested format */
786 strdesc->mFormatID = kAudioFormatLinearPCM;
787 strdesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
788 strdesc->mChannelsPerFrame = this->spec.channels;
789 strdesc->mSampleRate = this->spec.freq;
790 strdesc->mFramesPerPacket = 1;
791
792 while ((!valid_datatype) && (test_format)) {
793 this->spec.format = test_format;
794 /* Just a list of valid SDL formats, so people don't pass junk here. */
795 switch (test_format) {
796 case AUDIO_U8:
797 case AUDIO_S8:
798 case AUDIO_U16LSB:
799 case AUDIO_S16LSB:
800 case AUDIO_U16MSB:
801 case AUDIO_S16MSB:
802 case AUDIO_S32LSB:
803 case AUDIO_S32MSB:
804 case AUDIO_F32LSB:
805 case AUDIO_F32MSB:
806 valid_datatype = 1;
807 strdesc->mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
809 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
810
811 if (SDL_AUDIO_ISFLOAT(this->spec.format))
812 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
813 else if (SDL_AUDIO_ISSIGNED(this->spec.format))
814 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
815 break;
816 }
817 }
818
819 if (!valid_datatype) { /* shouldn't happen, but just in case... */
820 return SDL_SetError("Unsupported audio format");
821 }
822
823 strdesc->mBytesPerFrame = strdesc->mBitsPerChannel * strdesc->mChannelsPerFrame / 8;
824 strdesc->mBytesPerPacket = strdesc->mBytesPerFrame * strdesc->mFramesPerPacket;
825
826#if MACOSX_COREAUDIO
827 if (!prepare_device(this, handle, iscapture)) {
828 return -1;
829 }
830#endif
831
832 /* This has to init in a new thread so it can get its own CFRunLoop. :/ */
833 SDL_AtomicSet(&this->hidden->shutdown, 0);
834 this->hidden->ready_semaphore = SDL_CreateSemaphore(0);
835 if (!this->hidden->ready_semaphore) {
836 return -1; /* oh well. */
837 }
838
839 this->hidden->thread = SDL_CreateThreadInternal(audioqueue_thread, "AudioQueue thread", 512 * 1024, this);
840 if (!this->hidden->thread) {
841 return -1;
842 }
843
844 SDL_SemWait(this->hidden->ready_semaphore);
845 SDL_DestroySemaphore(this->hidden->ready_semaphore);
846 this->hidden->ready_semaphore = NULL;
847
848 if ((this->hidden->thread != NULL) && (this->hidden->thread_error != NULL)) {
849 SDL_SetError("%s", this->hidden->thread_error);
850 return -1;
851 }
852
853 return (this->hidden->thread != NULL) ? 0 : -1;
854}
855
856static void
857COREAUDIO_Deinitialize(void)
858{
859#if MACOSX_COREAUDIO
860 AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
861 free_audio_device_list(&capture_devs);
862 free_audio_device_list(&output_devs);
863#endif
864}
865
866static int
867COREAUDIO_Init(SDL_AudioDriverImpl * impl)
868{
869 /* Set the function pointers */
870 impl->OpenDevice = COREAUDIO_OpenDevice;
871 impl->CloseDevice = COREAUDIO_CloseDevice;
872 impl->Deinitialize = COREAUDIO_Deinitialize;
873
874#if MACOSX_COREAUDIO
875 impl->DetectDevices = COREAUDIO_DetectDevices;
876 AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
877#else
880#endif
881
883 impl->HasCaptureSupport = 1;
884
885 return 1; /* this audio target is available. */
886}
887
889 "coreaudio", "CoreAudio", COREAUDIO_Init, 0
890};
891
892#endif /* SDL_AUDIO_DRIVER_COREAUDIO */
893
894/* vi: set ts=4 sw=4 expandtab: */
#define _THIS
#define SDL_assert(condition)
Definition: SDL_assert.h:169
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
Definition: SDL_audio.c:531
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
Definition: SDL_audio.c:469
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1668
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1647
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:486
#define AUDIO_U16LSB
Definition: SDL_audio.h:91
#define AUDIO_S16LSB
Definition: SDL_audio.h:92
#define SDL_AUDIO_ISBIGENDIAN(x)
Definition: SDL_audio.h:77
#define AUDIO_F32MSB
Definition: SDL_audio.h:113
#define AUDIO_S32MSB
Definition: SDL_audio.h:104
#define AUDIO_S16MSB
Definition: SDL_audio.h:94
#define AUDIO_U16MSB
Definition: SDL_audio.h:93
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
#define SDL_AUDIO_ISSIGNED(x)
Definition: SDL_audio.h:78
#define AUDIO_U8
Definition: SDL_audio.h:89
#define SDL_AUDIO_ISFLOAT(x)
Definition: SDL_audio.h:76
#define AUDIO_S8
Definition: SDL_audio.h:90
#define SDL_AUDIO_BITSIZE(x)
Definition: SDL_audio.h:75
#define AUDIO_S32LSB
Definition: SDL_audio.h:103
#define AUDIO_F32LSB
Definition: SDL_audio.h:112
#define SDL_AtomicSet
#define SDL_SetError
#define SDL_memset
#define SDL_GetError
#define SDL_SetThreadPriority
#define SDL_SemPost
#define SDL_SemWait
#define SDL_LockMutex
#define SDL_malloc
#define SDL_strcasecmp
#define SDL_DestroySemaphore
#define SDL_free
#define SDL_strdup
#define SDL_CreateSemaphore
#define SDL_WaitThread
#define SDL_AtomicGet
#define SDL_memcpy
#define SDL_calloc
#define SDL_GetHint
#define SDL_UnlockMutex
#define SDL_ceil
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
#define SDL_HINT_AUDIO_CATEGORY
A variable controlling the audio category on iOS and Mac OS X.
Definition: SDL_hints.h:1063
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLuint64EXT * result
GLenum GLsizei len
GLenum const void * addr
GLuint const GLchar * name
GLsizeiptr size
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
uint8_t Uint8
Definition: SDL_stdinc.h:179
AudioBootStrap COREAUDIO_bootstrap
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:429
@ SDL_THREAD_PRIORITY_HIGH
Definition: SDL_thread.h:62
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int int in j)
Definition: SDL_x11sym.h:50
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:167
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
SDL_AudioSpec spec
Definition: loopwave.c:31
static SDL_AudioDeviceID device
Definition: loopwave.c:37
double floor(double x)
Definition: s_floor.c:33
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:78
void(* DetectDevices)(void)
Definition: SDL_sysaudio.h:67
void(* Deinitialize)(void)
Definition: SDL_sysaudio.h:82
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:68
Uint32 size
Definition: SDL_audio.h:186
Uint16 samples
Definition: SDL_audio.h:184
Uint8 channels
Definition: SDL_audio.h:182
SDL_AudioFormat format
Definition: SDL_audio.h:181
AudioStreamBasicDescription strdesc
Definition: SDL_coreaudio.h:54
int paused
Definition: testoverlay2.c:147
int alive
Definition: testsem.c:24