SDL  2.0
SDL_coreaudio.m
Go to the documentation of this file.
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 /* !!! FIXME: clean out some of the macro salsa in here. */
26 
27 #include "SDL_audio.h"
28 #include "../SDL_audio_c.h"
29 #include "../SDL_sysaudio.h"
30 #include "SDL_coreaudio.h"
31 #include "SDL_assert.h"
32 #include "../../thread/SDL_systhread.h"
33 
34 #define DEBUG_COREAUDIO 0
35 
36 #define CHECK_RESULT(msg) \
37  if (result != noErr) { \
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;
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) i, 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 
272 static int open_playback_devices = 0;
273 static int open_capture_devices = 0;
274 
275 #if !MACOSX_COREAUDIO
276 
277 static void interruption_begin(_THIS)
278 {
279  if (this != NULL && this->hidden->audioQueue != NULL) {
280  this->hidden->interrupted = SDL_TRUE;
281  AudioQueuePause(this->hidden->audioQueue);
282  }
283 }
284 
285 static void interruption_end(_THIS)
286 {
287  if (this != NULL && this->hidden != NULL && this->hidden->audioQueue != NULL
288  && this->hidden->interrupted) {
289  this->hidden->interrupted = SDL_FALSE;
290  AudioQueueStart(this->hidden->audioQueue, NULL);
291  }
292 }
293 
294 @interface SDLInterruptionListener : NSObject
295 
296 @property (nonatomic, assign) SDL_AudioDevice *device;
297 
298 @end
299 
300 @implementation SDLInterruptionListener
301 
302 - (void)audioSessionInterruption:(NSNotification *)note
303 {
304  @synchronized (self) {
305  NSNumber *type = note.userInfo[AVAudioSessionInterruptionTypeKey];
306  if (type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
307  interruption_begin(self.device);
308  } else {
309  interruption_end(self.device);
310  }
311  }
312 }
313 
314 - (void)applicationBecameActive:(NSNotification *)note
315 {
316  @synchronized (self) {
317  interruption_end(self.device);
318  }
319 }
320 
321 @end
322 
323 static BOOL update_audio_session(_THIS, SDL_bool open)
324 {
325  @autoreleasepool {
326  AVAudioSession *session = [AVAudioSession sharedInstance];
327  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
328  NSString *category;
329  NSError *err = nil;
330 
331  if (open_playback_devices && open_capture_devices) {
332  category = AVAudioSessionCategoryPlayAndRecord;
333  } else if (open_capture_devices) {
334  category = AVAudioSessionCategoryRecord;
335  } else {
336  /* Set category to ambient so that other music continues playing.
337  You can change this at runtime in your own code if you need different
338  behavior. If this is common, we can add an SDL hint for this. */
339  category = AVAudioSessionCategoryAmbient;
340  }
341 
342  if (![session setCategory:category error:&err]) {
343  NSString *desc = err.description;
344  SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
345  return NO;
346  }
347 
348  if (open_playback_devices + open_capture_devices == 1) {
349  if (![session setActive:YES error:&err]) {
350  NSString *desc = err.description;
351  SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String);
352  return NO;
353  }
354  } else if (!open_playback_devices && !open_capture_devices) {
355  [session setActive:NO error:nil];
356  }
357 
358  if (open) {
359  SDLInterruptionListener *listener = [SDLInterruptionListener new];
360  listener.device = this;
361 
362  [center addObserver:listener
363  selector:@selector(audioSessionInterruption:)
364  name:AVAudioSessionInterruptionNotification
365  object:session];
366 
367  /* An interruption end notification is not guaranteed to be sent if
368  we were previously interrupted... resuming if needed when the app
369  becomes active seems to be the way to go. */
370  [center addObserver:listener
371  selector:@selector(applicationBecameActive:)
372  name:UIApplicationDidBecomeActiveNotification
373  object:session];
374 
375  [center addObserver:listener
376  selector:@selector(applicationBecameActive:)
377  name:UIApplicationWillEnterForegroundNotification
378  object:session];
379 
380  this->hidden->interruption_listener = CFBridgingRetain(listener);
381  } else {
382  if (this->hidden->interruption_listener != NULL) {
383  SDLInterruptionListener *listener = nil;
384  listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener);
385  @synchronized (listener) {
386  listener.device = NULL;
387  }
388  [center removeObserver:listener];
389  }
390  }
391  }
392 
393  return YES;
394 }
395 #endif
396 
397 
398 /* The AudioQueue callback */
399 static void
400 outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
401 {
402  SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
403  if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
404  /* Supply silence if audio is enabled and not paused */
405  SDL_memset(inBuffer->mAudioData, this->spec.silence, inBuffer->mAudioDataBytesCapacity);
406  } else {
407  UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
408  Uint8 *ptr = (Uint8 *) inBuffer->mAudioData;
409 
410  while (remaining > 0) {
411  UInt32 len;
412  if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
413  /* Generate the data */
414  SDL_LockMutex(this->mixer_lock);
415  (*this->spec.callback)(this->spec.userdata,
416  this->hidden->buffer, this->hidden->bufferSize);
417  SDL_UnlockMutex(this->mixer_lock);
418  this->hidden->bufferOffset = 0;
419  }
420 
421  len = this->hidden->bufferSize - this->hidden->bufferOffset;
422  if (len > remaining) {
423  len = remaining;
424  }
425  SDL_memcpy(ptr, (char *)this->hidden->buffer +
426  this->hidden->bufferOffset, len);
427  ptr = ptr + len;
428  remaining -= len;
429  this->hidden->bufferOffset += len;
430  }
431  }
432 
433  if (!SDL_AtomicGet(&this->hidden->shutdown)) {
434  AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
435  }
436 
437  inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
438 }
439 
440 static void
441 inputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
442  const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions,
443  const AudioStreamPacketDescription *inPacketDescs )
444 {
445  SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
446  if (SDL_AtomicGet(&this->enabled) && !SDL_AtomicGet(&this->paused)) { /* ignore unless we're active. */
447  const Uint8 *ptr = (const Uint8 *) inBuffer->mAudioData;
448  UInt32 remaining = inBuffer->mAudioDataByteSize;
449  while (remaining > 0) {
450  UInt32 len = this->hidden->bufferSize - this->hidden->bufferOffset;
451  if (len > remaining) {
452  len = remaining;
453  }
454 
455  SDL_memcpy((char *)this->hidden->buffer + this->hidden->bufferOffset, ptr, len);
456  ptr += len;
457  remaining -= len;
458  this->hidden->bufferOffset += len;
459 
460  if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
461  SDL_LockMutex(this->mixer_lock);
462  (*this->spec.callback)(this->spec.userdata, this->hidden->buffer, this->hidden->bufferSize);
463  SDL_UnlockMutex(this->mixer_lock);
464  this->hidden->bufferOffset = 0;
465  }
466  }
467  }
468 
469  if (!SDL_AtomicGet(&this->hidden->shutdown)) {
470  AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
471  }
472 }
473 
474 
475 #if MACOSX_COREAUDIO
476 static const AudioObjectPropertyAddress alive_address =
477 {
478  kAudioDevicePropertyDeviceIsAlive,
479  kAudioObjectPropertyScopeGlobal,
480  kAudioObjectPropertyElementMaster
481 };
482 
483 static OSStatus
484 device_unplugged(AudioObjectID devid, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
485 {
486  SDL_AudioDevice *this = (SDL_AudioDevice *) data;
487  SDL_bool dead = SDL_FALSE;
488  UInt32 isAlive = 1;
489  UInt32 size = sizeof (isAlive);
490  OSStatus error;
491 
492  if (!SDL_AtomicGet(&this->enabled)) {
493  return 0; /* already known to be dead. */
494  }
495 
496  error = AudioObjectGetPropertyData(this->hidden->deviceID, &alive_address,
497  0, NULL, &size, &isAlive);
498 
499  if (error == kAudioHardwareBadDeviceError) {
500  dead = SDL_TRUE; /* device was unplugged. */
501  } else if ((error == kAudioHardwareNoError) && (!isAlive)) {
502  dead = SDL_TRUE; /* device died in some other way. */
503  }
504 
505  if (dead) {
507  }
508 
509  return 0;
510 }
511 #endif
512 
513 static void
514 COREAUDIO_CloseDevice(_THIS)
515 {
516  const SDL_bool iscapture = this->iscapture;
517  int i;
518 
519 /* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
520 /* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
521 #if MACOSX_COREAUDIO
522  /* Fire a callback if the device stops being "alive" (disconnected, etc). */
523  AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
524 #endif
525 
526 #if !MACOSX_COREAUDIO
527  update_audio_session(this, SDL_FALSE);
528 #endif
529 
530  if (this->hidden->thread) {
531  SDL_AtomicSet(&this->hidden->shutdown, 1);
532  SDL_WaitThread(this->hidden->thread, NULL);
533  }
534 
535  if (this->hidden->audioQueue) {
536  for (i = 0; i < SDL_arraysize(this->hidden->audioBuffer); i++) {
537  if (this->hidden->audioBuffer[i]) {
538  AudioQueueFreeBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i]);
539  }
540  }
541  AudioQueueDispose(this->hidden->audioQueue, 1);
542  }
543 
544  if (this->hidden->ready_semaphore) {
545  SDL_DestroySemaphore(this->hidden->ready_semaphore);
546  }
547 
548  SDL_free(this->hidden->thread_error);
549  SDL_free(this->hidden->buffer);
550  SDL_free(this->hidden);
551 
552  if (iscapture) {
553  open_capture_devices--;
554  } else {
555  open_playback_devices--;
556  }
557 }
558 
559 #if MACOSX_COREAUDIO
560 static int
561 prepare_device(_THIS, void *handle, int iscapture)
562 {
563  AudioDeviceID devid = (AudioDeviceID) ((size_t) handle);
564  OSStatus result = noErr;
565  UInt32 size = 0;
566  UInt32 alive = 0;
567  pid_t pid = 0;
568 
569  AudioObjectPropertyAddress addr = {
570  0,
571  kAudioObjectPropertyScopeGlobal,
572  kAudioObjectPropertyElementMaster
573  };
574 
575  if (handle == NULL) {
576  size = sizeof (AudioDeviceID);
577  addr.mSelector =
578  ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
579  kAudioHardwarePropertyDefaultOutputDevice);
580  result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
581  0, NULL, &size, &devid);
582  CHECK_RESULT("AudioHardwareGetProperty (default device)");
583  }
584 
585  addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
586  addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
587  kAudioDevicePropertyScopeOutput;
588 
589  size = sizeof (alive);
590  result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &alive);
591  CHECK_RESULT
592  ("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
593 
594  if (!alive) {
595  SDL_SetError("CoreAudio: requested device exists, but isn't alive.");
596  return 0;
597  }
598 
599  addr.mSelector = kAudioDevicePropertyHogMode;
600  size = sizeof (pid);
601  result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &pid);
602 
603  /* some devices don't support this property, so errors are fine here. */
604  if ((result == noErr) && (pid != -1)) {
605  SDL_SetError("CoreAudio: requested device is being hogged.");
606  return 0;
607  }
608 
609  this->hidden->deviceID = devid;
610  return 1;
611 }
612 #endif
613 
614 static int
615 prepare_audioqueue(_THIS)
616 {
617  const AudioStreamBasicDescription *strdesc = &this->hidden->strdesc;
618  const int iscapture = this->iscapture;
619  OSStatus result;
620  int i;
621 
622  SDL_assert(CFRunLoopGetCurrent() != NULL);
623 
624  if (iscapture) {
625  result = AudioQueueNewInput(strdesc, inputCallback, this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
626  CHECK_RESULT("AudioQueueNewInput");
627  } else {
628  result = AudioQueueNewOutput(strdesc, outputCallback, this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
629  CHECK_RESULT("AudioQueueNewOutput");
630  }
631 
632 #if MACOSX_COREAUDIO
633 {
634  const AudioObjectPropertyAddress prop = {
635  kAudioDevicePropertyDeviceUID,
636  iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
637  kAudioObjectPropertyElementMaster
638  };
639  CFStringRef devuid;
640  UInt32 devuidsize = sizeof (devuid);
641  result = AudioObjectGetPropertyData(this->hidden->deviceID, &prop, 0, NULL, &devuidsize, &devuid);
642  CHECK_RESULT("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID)");
643  result = AudioQueueSetProperty(this->hidden->audioQueue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize);
644  CHECK_RESULT("AudioQueueSetProperty (kAudioQueueProperty_CurrentDevice)");
645 
646  /* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
647  /* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
648  /* Fire a callback if the device stops being "alive" (disconnected, etc). */
649  AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
650 }
651 #endif
652 
653  /* Calculate the final parameters for this audio specification */
655 
656  /* Allocate a sample buffer */
657  this->hidden->bufferSize = this->spec.size;
658  this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize;
659 
660  this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
661  if (this->hidden->buffer == NULL) {
662  SDL_OutOfMemory();
663  return 0;
664  }
665 
666  for (i = 0; i < SDL_arraysize(this->hidden->audioBuffer); i++) {
667  result = AudioQueueAllocateBuffer(this->hidden->audioQueue, this->spec.size, &this->hidden->audioBuffer[i]);
668  CHECK_RESULT("AudioQueueAllocateBuffer");
669  SDL_memset(this->hidden->audioBuffer[i]->mAudioData, this->spec.silence, this->hidden->audioBuffer[i]->mAudioDataBytesCapacity);
670  this->hidden->audioBuffer[i]->mAudioDataByteSize = this->hidden->audioBuffer[i]->mAudioDataBytesCapacity;
671  result = AudioQueueEnqueueBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i], 0, NULL);
672  CHECK_RESULT("AudioQueueEnqueueBuffer");
673  }
674 
675  result = AudioQueueStart(this->hidden->audioQueue, NULL);
676  CHECK_RESULT("AudioQueueStart");
677 
678  /* We're running! */
679  return 1;
680 }
681 
682 static int
683 audioqueue_thread(void *arg)
684 {
685  SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
686  const int rc = prepare_audioqueue(this);
687  if (!rc) {
688  this->hidden->thread_error = SDL_strdup(SDL_GetError());
689  SDL_SemPost(this->hidden->ready_semaphore);
690  return 0;
691  }
692 
693  /* init was successful, alert parent thread and start running... */
694  SDL_SemPost(this->hidden->ready_semaphore);
695  while (!SDL_AtomicGet(&this->hidden->shutdown)) {
696  CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
697  }
698 
699  if (this->iscapture) { /* just stop immediately for capture devices. */
700  AudioQueueStop(this->hidden->audioQueue, 1);
701  } else { /* Drain off any pending playback. */
702  AudioQueueStop(this->hidden->audioQueue, 0);
703  const CFTimeInterval secs = (((this->spec.size / (SDL_AUDIO_BITSIZE(this->spec.format) / 8)) / this->spec.channels) / ((CFTimeInterval) this->spec.freq)) * 2.0;
704  CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0);
705  }
706 
707  return 0;
708 }
709 
710 static int
711 COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
712 {
713  AudioStreamBasicDescription *strdesc;
714  SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
715  int valid_datatype = 0;
716 
717  /* Initialize all variables that we clean on shutdown */
718  this->hidden = (struct SDL_PrivateAudioData *)
719  SDL_malloc((sizeof *this->hidden));
720  if (this->hidden == NULL) {
721  return SDL_OutOfMemory();
722  }
723  SDL_zerop(this->hidden);
724 
725  strdesc = &this->hidden->strdesc;
726 
727  if (iscapture) {
728  open_capture_devices++;
729  } else {
730  open_playback_devices++;
731  }
732 
733 #if !MACOSX_COREAUDIO
734  if (!update_audio_session(this, SDL_TRUE)) {
735  return -1;
736  }
737 #endif
738 
739  /* Setup a AudioStreamBasicDescription with the requested format */
740  SDL_zerop(strdesc);
741  strdesc->mFormatID = kAudioFormatLinearPCM;
742  strdesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
743  strdesc->mChannelsPerFrame = this->spec.channels;
744  strdesc->mSampleRate = this->spec.freq;
745  strdesc->mFramesPerPacket = 1;
746 
747  while ((!valid_datatype) && (test_format)) {
748  this->spec.format = test_format;
749  /* Just a list of valid SDL formats, so people don't pass junk here. */
750  switch (test_format) {
751  case AUDIO_U8:
752  case AUDIO_S8:
753  case AUDIO_U16LSB:
754  case AUDIO_S16LSB:
755  case AUDIO_U16MSB:
756  case AUDIO_S16MSB:
757  case AUDIO_S32LSB:
758  case AUDIO_S32MSB:
759  case AUDIO_F32LSB:
760  case AUDIO_F32MSB:
761  valid_datatype = 1;
762  strdesc->mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
763  if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
764  strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
765 
766  if (SDL_AUDIO_ISFLOAT(this->spec.format))
767  strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
768  else if (SDL_AUDIO_ISSIGNED(this->spec.format))
769  strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
770  break;
771  }
772  }
773 
774  if (!valid_datatype) { /* shouldn't happen, but just in case... */
775  return SDL_SetError("Unsupported audio format");
776  }
777 
778  strdesc->mBytesPerFrame = strdesc->mBitsPerChannel * strdesc->mChannelsPerFrame / 8;
779  strdesc->mBytesPerPacket = strdesc->mBytesPerFrame * strdesc->mFramesPerPacket;
780 
781 #if MACOSX_COREAUDIO
782  if (!prepare_device(this, handle, iscapture)) {
783  return -1;
784  }
785 #endif
786 
787  /* This has to init in a new thread so it can get its own CFRunLoop. :/ */
788  SDL_AtomicSet(&this->hidden->shutdown, 0);
789  this->hidden->ready_semaphore = SDL_CreateSemaphore(0);
790  if (!this->hidden->ready_semaphore) {
791  return -1; /* oh well. */
792  }
793 
794  this->hidden->thread = SDL_CreateThreadInternal(audioqueue_thread, "AudioQueue thread", 512 * 1024, this);
795  if (!this->hidden->thread) {
796  return -1;
797  }
798 
799  SDL_SemWait(this->hidden->ready_semaphore);
800  SDL_DestroySemaphore(this->hidden->ready_semaphore);
801  this->hidden->ready_semaphore = NULL;
802 
803  if ((this->hidden->thread != NULL) && (this->hidden->thread_error != NULL)) {
804  SDL_SetError("%s", this->hidden->thread_error);
805  return -1;
806  }
807 
808  return (this->hidden->thread != NULL) ? 0 : -1;
809 }
810 
811 static void
812 COREAUDIO_Deinitialize(void)
813 {
814 #if MACOSX_COREAUDIO
815  AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
816  free_audio_device_list(&capture_devs);
817  free_audio_device_list(&output_devs);
818 #endif
819 }
820 
821 static int
822 COREAUDIO_Init(SDL_AudioDriverImpl * impl)
823 {
824  /* Set the function pointers */
825  impl->OpenDevice = COREAUDIO_OpenDevice;
826  impl->CloseDevice = COREAUDIO_CloseDevice;
827  impl->Deinitialize = COREAUDIO_Deinitialize;
828 
829 #if MACOSX_COREAUDIO
830  impl->DetectDevices = COREAUDIO_DetectDevices;
831  AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
832 #else
833  impl->OnlyHasDefaultOutputDevice = 1;
834  impl->OnlyHasDefaultCaptureDevice = 1;
835 #endif
836 
837  impl->ProvidesOwnCallbackThread = 1;
838  impl->HasCaptureSupport = 1;
839 
840  return 1; /* this audio target is available. */
841 }
842 
844  "coreaudio", "CoreAudio", COREAUDIO_Init, 0
845 };
846 
847 #endif /* SDL_AUDIO_DRIVER_COREAUDIO */
848 
849 /* vi: set ts=4 sw=4 expandtab: */
int alive
Definition: testsem.c:24
#define SDL_LockMutex
#define SDL_GetError
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1611
#define AUDIO_S32MSB
Definition: SDL_audio.h:104
GLuint64EXT * result
void(* DetectDevices)(void)
Definition: SDL_sysaudio.h:75
#define SDL_AUDIO_ISBIGENDIAN(x)
Definition: SDL_audio.h:77
#define AUDIO_U16LSB
Definition: SDL_audio.h:91
#define SDL_CreateSemaphore
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1967
#define SDL_AUDIO_ISSIGNED(x)
Definition: SDL_audio.h:78
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:381
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
GLuint const GLchar * name
#define SDL_zerop(x)
Definition: SDL_stdinc.h:360
GLsizeiptr size
GLenum GLsizei len
#define SDL_SemPost
#define AUDIO_F32MSB
Definition: SDL_audio.h:113
SDL_AudioSpec spec
Definition: loopwave.c:35
#define SDL_AUDIO_ISFLOAT(x)
Definition: SDL_audio.h:76
#define AUDIO_U8
Definition: SDL_audio.h:89
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1564
#define SDL_memcpy
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:427
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
Definition: SDL_audio.c:422
Uint8 channels
Definition: SDL_audio.h:172
#define _THIS
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:143
void SDL_free(void *mem)
GLenum const void * addr
#define SDL_AUDIO_BITSIZE(x)
Definition: SDL_audio.h:75
#define AUDIO_F32LSB
Definition: SDL_audio.h:112
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
void(* Deinitialize)(void)
Definition: SDL_sysaudio.h:89
AudioBootStrap COREAUDIO_bootstrap
#define AUDIO_S32LSB
Definition: SDL_audio.h:103
int paused
Definition: testoverlay2.c:149
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1632
SDL_AudioCallback callback
Definition: SDL_audio.h:177
GLenum GLenum GLsizei const GLuint GLboolean enabled
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
Uint32 size
Definition: SDL_audio.h:176
#define SDL_assert(condition)
Definition: SDL_assert.h:167
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:76
#define NULL
Definition: begin_code.h:143
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:130
#define SDL_SetError
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
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:85
#define AUDIO_S16MSB
Definition: SDL_audio.h:94
#define SDL_strdup
SDL_AudioFormat format
Definition: SDL_audio.h:171
#define SDL_SemWait
#define SDL_DestroySemaphore
#define AUDIO_S16LSB
Definition: SDL_audio.h:92
#define SDL_AtomicSet
#define SDL_AtomicGet
void * userdata
Definition: SDL_audio.h:178
#define SDL_UnlockMutex
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:90
#define SDL_malloc
#define AUDIO_S8
Definition: SDL_audio.h:90
#define SDL_memset
#define SDL_WaitThread
#define AUDIO_U16MSB
Definition: SDL_audio.h:93
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
Definition: SDL_audio.c:364