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
44 static const AudioObjectPropertyAddress devlist_address = {
45  kAudioHardwarePropertyDevices,
46  kAudioObjectPropertyScopeGlobal,
47  kAudioObjectPropertyElementMaster
48 };
49 
50 typedef void (*addDevFn)(const char *name, const int iscapture, AudioDeviceID devId, void *data);
51 
52 typedef struct AudioDeviceList
53 {
54  AudioDeviceID devid;
56  struct AudioDeviceList *next;
57 } AudioDeviceList;
58 
59 static AudioDeviceList *output_devs = NULL;
60 static AudioDeviceList *capture_devs = NULL;
61 
62 static SDL_bool
63 add_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 
81 static void
82 addToDevList(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 
89 static void
90 build_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 
197 static void
198 free_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 
209 static void
210 COREAUDIO_DetectDevices(void)
211 {
212  build_device_list(SDL_TRUE, addToDevList, NULL);
213  build_device_list(SDL_FALSE, addToDevList, NULL);
214 }
215 
216 static void
217 build_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 
232 static void
233 reprocess_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. */
263 static OSStatus
264 device_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 
273 static int open_playback_devices = 0;
274 static int open_capture_devices = 0;
275 
276 #if !MACOSX_COREAUDIO
277 
278 static 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 
286 static 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 
324 static 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 */
409 static void
410 outputCallback(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 
452 static void
453 inputCallback(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
492 static const AudioObjectPropertyAddress alive_address =
493 {
494  kAudioDevicePropertyDeviceIsAlive,
495  kAudioObjectPropertyScopeGlobal,
496  kAudioObjectPropertyElementMaster
497 };
498 
499 static OSStatus
500 device_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 
529 static void
530 COREAUDIO_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
575 static int
576 prepare_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 
629 static int
630 prepare_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) {
677  SDL_OutOfMemory();
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) {
697  SDL_OutOfMemory();
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 
721 static int
722 audioqueue_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 
748 static int
749 COREAUDIO_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);
808  if (SDL_AUDIO_ISBIGENDIAN(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 
856 static void
857 COREAUDIO_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 
866 static int
867 COREAUDIO_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
878  impl->OnlyHasDefaultOutputDevice = 1;
879  impl->OnlyHasDefaultCaptureDevice = 1;
880 #endif
881 
882  impl->ProvidesOwnCallbackThread = 1;
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: */
SDL_SetThreadPriority
#define SDL_SetThreadPriority
Definition: SDL_dynapi_overrides.h:477
SDL_memset
#define SDL_memset
Definition: SDL_dynapi_overrides.h:386
SDL_FirstAudioFormat
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1647
SDL_GetError
#define SDL_GetError
Definition: SDL_dynapi_overrides.h:113
SDL_AudioSpec::channels
Uint8 channels
Definition: SDL_audio.h:182
SDL_AudioDriverImpl::HasCaptureSupport
int HasCaptureSupport
Definition: SDL_sysaudio.h:90
SDL_AudioDriverImpl::ProvidesOwnCallbackThread
int ProvidesOwnCallbackThread
Definition: SDL_sysaudio.h:88
alive
int alive
Definition: testsem.c:24
AUDIO_U16LSB
#define AUDIO_U16LSB
Definition: SDL_audio.h:91
SDL_coreaudio.h
SDL_CreateSemaphore
#define SDL_CreateSemaphore
Definition: SDL_dynapi_overrides.h:264
SDL_LockMutex
#define SDL_LockMutex
Definition: SDL_dynapi_overrides.h:260
SDL_ceil
#define SDL_ceil
Definition: SDL_dynapi_overrides.h:426
NULL
#define NULL
Definition: begin_code.h:167
handle
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
AUDIO_S32MSB
#define AUDIO_S32MSB
Definition: SDL_audio.h:104
SDL_AudioSpec::samples
Uint16 samples
Definition: SDL_audio.h:184
SDL_zerop
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
SDL_AUDIO_ISBIGENDIAN
#define SDL_AUDIO_ISBIGENDIAN(x)
Definition: SDL_audio.h:77
SDL_AudioSpec::format
SDL_AudioFormat format
Definition: SDL_audio.h:181
SDL_AUDIO_ISSIGNED
#define SDL_AUDIO_ISSIGNED(x)
Definition: SDL_audio.h:78
SDL_THREAD_PRIORITY_HIGH
@ SDL_THREAD_PRIORITY_HIGH
Definition: SDL_thread.h:62
SDL_AudioDriverImpl::OpenDevice
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:68
SDL_GetHint
#define SDL_GetHint
Definition: SDL_dynapi_overrides.h:191
SDL_OpenedAudioDeviceDisconnected
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:486
SDL_PrivateAudioData::iscapture
SDL_bool iscapture
Definition: SDL_qsa_audio.h:37
SDL_AudioFormat
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
SDL_SemPost
#define SDL_SemPost
Definition: SDL_dynapi_overrides.h:269
result
GLuint64EXT * result
Definition: SDL_opengl_glext.h:9432
AudioBootStrap
Definition: SDL_sysaudio.h:176
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
SDL_AUDIO_ISFLOAT
#define SDL_AUDIO_ISFLOAT(x)
Definition: SDL_audio.h:76
COREAUDIO_bootstrap
AudioBootStrap COREAUDIO_bootstrap
SDL_strcasecmp
#define SDL_strcasecmp
Definition: SDL_dynapi_overrides.h:419
SDL_PrivateAudioData
Definition: SDL_alsa_audio.h:33
AUDIO_U8
#define AUDIO_U8
Definition: SDL_audio.h:89
len
GLenum GLsizei len
Definition: SDL_opengl_glext.h:2926
SDL_memcpy
#define SDL_memcpy
Definition: SDL_dynapi_overrides.h:387
SDL_FALSE
@ SDL_FALSE
Definition: SDL_stdinc.h:163
AUDIO_F32MSB
#define AUDIO_F32MSB
Definition: SDL_audio.h:113
SDL_HINT_AUDIO_CATEGORY
#define SDL_HINT_AUDIO_CATEGORY
A variable controlling the audio category on iOS and Mac OS X.
Definition: SDL_hints.h:1063
SDL_audio.h
SDL_CreateThreadInternal
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:429
SDL_AudioDriverImpl
Definition: SDL_sysaudio.h:65
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
SDL_AudioDriverImpl::OnlyHasDefaultOutputDevice
int OnlyHasDefaultOutputDevice
Definition: SDL_sysaudio.h:91
name
GLuint const GLchar * name
Definition: SDL_opengl_glext.h:660
SDL_RemoveAudioDevice
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
Definition: SDL_audio.c:531
SDL_assert.h
addr
GLenum const void * addr
Definition: SDL_opengl_glext.h:7945
SDL_CalculateAudioSpec
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1668
SDL_AudioDriverImpl::DetectDevices
void(* DetectDevices)(void)
Definition: SDL_sysaudio.h:67
_THIS
#define _THIS
Definition: SDL_alsa_audio.h:31
SDL_AUDIO_BITSIZE
#define SDL_AUDIO_BITSIZE(x)
Definition: SDL_audio.h:75
AUDIO_F32LSB
#define AUDIO_F32LSB
Definition: SDL_audio.h:112
SDL_AudioSpec::freq
int freq
Definition: SDL_audio.h:180
SDL_AudioDriverImpl::OnlyHasDefaultCaptureDevice
int OnlyHasDefaultCaptureDevice
Definition: SDL_sysaudio.h:92
SDL_assert
#define SDL_assert(condition)
Definition: SDL_assert.h:169
AUDIO_S32LSB
#define AUDIO_S32LSB
Definition: SDL_audio.h:103
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
size
GLsizeiptr size
Definition: SDL_opengl_glext.h:537
spec
SDL_AudioSpec spec
Definition: loopwave.c:31
SDL_calloc
#define SDL_calloc
Definition: SDL_dynapi_overrides.h:375
SDL_AudioSpec::size
Uint32 size
Definition: SDL_audio.h:186
SDL_TRUE
@ SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_SemWait
#define SDL_SemWait
Definition: SDL_dynapi_overrides.h:266
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
j
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
SDL_hints.h
AUDIO_S16MSB
#define AUDIO_S16MSB
Definition: SDL_audio.h:94
SDL_strdup
#define SDL_strdup
Definition: SDL_dynapi_overrides.h:397
SDL_DestroySemaphore
#define SDL_DestroySemaphore
Definition: SDL_dynapi_overrides.h:265
enabled
GLenum GLenum GLsizei const GLuint GLboolean enabled
Definition: SDL_opengl_glext.h:2479
AUDIO_S16LSB
#define AUDIO_S16LSB
Definition: SDL_audio.h:92
paused
int paused
Definition: testoverlay2.c:147
AUDIO_S8
#define AUDIO_S8
Definition: SDL_audio.h:90
SDL_PrivateAudioData::strdesc
AudioStreamBasicDescription strdesc
Definition: SDL_coreaudio.h:54
SDL_AtomicSet
#define SDL_AtomicSet
Definition: SDL_dynapi_overrides.h:67
SDL_malloc
#define SDL_malloc
Definition: SDL_dynapi_overrides.h:374
SDL_AtomicGet
#define SDL_AtomicGet
Definition: SDL_dynapi_overrides.h:68
void
const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char const char const SDL_SCANF_FORMAT_STRING char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
Definition: SDL_dynapi_procs.h:89
SDL_UnlockMutex
#define SDL_UnlockMutex
Definition: SDL_dynapi_overrides.h:262
device
static SDL_AudioDeviceID device
Definition: loopwave.c:37
floor
double floor(double x)
Definition: s_floor.c:33
type
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
SDL_AudioDriverImpl::CloseDevice
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:78
AUDIO_U16MSB
#define AUDIO_U16MSB
Definition: SDL_audio.h:93
i
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
SDL_AddAudioDevice
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
Definition: SDL_audio.c:469
SDL_WaitThread
#define SDL_WaitThread
Definition: SDL_dynapi_overrides.h:478
SDL_AudioDevice
Definition: SDL_sysaudio.h:131
SDL_AudioDriverImpl::Deinitialize
void(* Deinitialize)(void)
Definition: SDL_sysaudio.h:82
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:161
Uint8
uint8_t Uint8
Definition: SDL_stdinc.h:179