SDL  2.0
SDL_coreaudio.m
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2020 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 "../../thread/SDL_systhread.h"
33 
34 #define DEBUG_COREAUDIO 0
35 
36 #if DEBUG_COREAUDIO
37  #define CHECK_RESULT(msg) \
38  if (result != noErr) { \
39  printf("COREAUDIO: Got error %d from '%s'!\n", (int) result, msg); \
40  SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
41  return 0; \
42  }
43 #else
44  #define CHECK_RESULT(msg) \
45  if (result != noErr) { \
46  SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
47  return 0; \
48  }
49 #endif
50 
51 
52 #if MACOSX_COREAUDIO
53 static const AudioObjectPropertyAddress devlist_address = {
54  kAudioHardwarePropertyDevices,
55  kAudioObjectPropertyScopeGlobal,
56  kAudioObjectPropertyElementMaster
57 };
58 
59 typedef void (*addDevFn)(const char *name, const int iscapture, AudioDeviceID devId, void *data);
60 
61 typedef struct AudioDeviceList
62 {
63  AudioDeviceID devid;
65  struct AudioDeviceList *next;
66 } AudioDeviceList;
67 
68 static AudioDeviceList *output_devs = NULL;
69 static AudioDeviceList *capture_devs = NULL;
70 
71 static SDL_bool
72 add_to_internal_dev_list(const int iscapture, AudioDeviceID devId)
73 {
74  AudioDeviceList *item = (AudioDeviceList *) SDL_malloc(sizeof (AudioDeviceList));
75  if (item == NULL) {
76  return SDL_FALSE;
77  }
78  item->devid = devId;
79  item->alive = SDL_TRUE;
80  item->next = iscapture ? capture_devs : output_devs;
81  if (iscapture) {
82  capture_devs = item;
83  } else {
84  output_devs = item;
85  }
86 
87  return SDL_TRUE;
88 }
89 
90 static void
91 addToDevList(const char *name, const int iscapture, AudioDeviceID devId, void *data)
92 {
93  if (add_to_internal_dev_list(iscapture, devId)) {
94  SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
95  }
96 }
97 
98 static void
99 build_device_list(int iscapture, addDevFn addfn, void *addfndata)
100 {
101  OSStatus result = noErr;
102  UInt32 size = 0;
103  AudioDeviceID *devs = NULL;
104  UInt32 i = 0;
105  UInt32 max = 0;
106 
107  result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
108  &devlist_address, 0, NULL, &size);
109  if (result != kAudioHardwareNoError)
110  return;
111 
112  devs = (AudioDeviceID *) alloca(size);
113  if (devs == NULL)
114  return;
115 
116  result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
117  &devlist_address, 0, NULL, &size, devs);
118  if (result != kAudioHardwareNoError)
119  return;
120 
121  max = size / sizeof (AudioDeviceID);
122  for (i = 0; i < max; i++) {
123  CFStringRef cfstr = NULL;
124  char *ptr = NULL;
125  AudioDeviceID dev = devs[i];
126  AudioBufferList *buflist = NULL;
127  int usable = 0;
128  CFIndex len = 0;
129  const AudioObjectPropertyAddress addr = {
130  kAudioDevicePropertyStreamConfiguration,
131  iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
132  kAudioObjectPropertyElementMaster
133  };
134 
135  const AudioObjectPropertyAddress nameaddr = {
136  kAudioObjectPropertyName,
137  iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
138  kAudioObjectPropertyElementMaster
139  };
140 
141  result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
142  if (result != noErr)
143  continue;
144 
145  buflist = (AudioBufferList *) SDL_malloc(size);
146  if (buflist == NULL)
147  continue;
148 
149  result = AudioObjectGetPropertyData(dev, &addr, 0, NULL,
150  &size, buflist);
151 
152  if (result == noErr) {
153  UInt32 j;
154  for (j = 0; j < buflist->mNumberBuffers; j++) {
155  if (buflist->mBuffers[j].mNumberChannels > 0) {
156  usable = 1;
157  break;
158  }
159  }
160  }
161 
162  SDL_free(buflist);
163 
164  if (!usable)
165  continue;
166 
167 
168  size = sizeof (CFStringRef);
169  result = AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr);
170  if (result != kAudioHardwareNoError)
171  continue;
172 
173  len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
174  kCFStringEncodingUTF8);
175 
176  ptr = (char *) SDL_malloc(len + 1);
177  usable = ((ptr != NULL) &&
178  (CFStringGetCString
179  (cfstr, ptr, len + 1, kCFStringEncodingUTF8)));
180 
181  CFRelease(cfstr);
182 
183  if (usable) {
184  len = strlen(ptr);
185  /* Some devices have whitespace at the end...trim it. */
186  while ((len > 0) && (ptr[len - 1] == ' ')) {
187  len--;
188  }
189  usable = (len > 0);
190  }
191 
192  if (usable) {
193  ptr[len] = '\0';
194 
195 #if DEBUG_COREAUDIO
196  printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
197  ((iscapture) ? "capture" : "output"),
198  (int) i, ptr, (int) dev);
199 #endif
200  addfn(ptr, iscapture, dev, addfndata);
201  }
202  SDL_free(ptr); /* addfn() would have copied the string. */
203  }
204 }
205 
206 static void
207 free_audio_device_list(AudioDeviceList **list)
208 {
209  AudioDeviceList *item = *list;
210  while (item) {
211  AudioDeviceList *next = item->next;
212  SDL_free(item);
213  item = next;
214  }
215  *list = NULL;
216 }
217 
218 static void
219 COREAUDIO_DetectDevices(void)
220 {
221  build_device_list(SDL_TRUE, addToDevList, NULL);
222  build_device_list(SDL_FALSE, addToDevList, NULL);
223 }
224 
225 static void
226 build_device_change_list(const char *name, const int iscapture, AudioDeviceID devId, void *data)
227 {
228  AudioDeviceList **list = (AudioDeviceList **) data;
229  AudioDeviceList *item;
230  for (item = *list; item != NULL; item = item->next) {
231  if (item->devid == devId) {
232  item->alive = SDL_TRUE;
233  return;
234  }
235  }
236 
237  add_to_internal_dev_list(iscapture, devId); /* new device, add it. */
238  SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
239 }
240 
241 static void
242 reprocess_device_list(const int iscapture, AudioDeviceList **list)
243 {
244  AudioDeviceList *item;
245  AudioDeviceList *prev = NULL;
246  for (item = *list; item != NULL; item = item->next) {
247  item->alive = SDL_FALSE;
248  }
249 
250  build_device_list(iscapture, build_device_change_list, list);
251 
252  /* free items in the list that aren't still alive. */
253  item = *list;
254  while (item != NULL) {
255  AudioDeviceList *next = item->next;
256  if (item->alive) {
257  prev = item;
258  } else {
259  SDL_RemoveAudioDevice(iscapture, (void *) ((size_t) item->devid));
260  if (prev) {
261  prev->next = item->next;
262  } else {
263  *list = item->next;
264  }
265  SDL_free(item);
266  }
267  item = next;
268  }
269 }
270 
271 /* this is called when the system's list of available audio devices changes. */
272 static OSStatus
273 device_list_changed(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
274 {
275  reprocess_device_list(SDL_TRUE, &capture_devs);
276  reprocess_device_list(SDL_FALSE, &output_devs);
277  return 0;
278 }
279 #endif
280 
281 
282 static int open_playback_devices;
283 static int open_capture_devices;
284 static int num_open_devices;
286 
287 #if !MACOSX_COREAUDIO
288 
289 static BOOL session_active = NO;
290 
291 static void pause_audio_devices()
292 {
293  int i;
294 
295  if (!open_devices) {
296  return;
297  }
298 
299  for (i = 0; i < num_open_devices; ++i) {
301  if (device->hidden->audioQueue && !device->hidden->interrupted) {
302  AudioQueuePause(device->hidden->audioQueue);
303  }
304  }
305 }
306 
307 static void resume_audio_devices()
308 {
309  int i;
310 
311  if (!open_devices) {
312  return;
313  }
314 
315  for (i = 0; i < num_open_devices; ++i) {
317  if (device->hidden->audioQueue && !device->hidden->interrupted) {
318  AudioQueueStart(device->hidden->audioQueue, NULL);
319  }
320  }
321 }
322 
323 static void interruption_begin(_THIS)
324 {
325  if (this != NULL && this->hidden->audioQueue != NULL) {
326  this->hidden->interrupted = SDL_TRUE;
327  AudioQueuePause(this->hidden->audioQueue);
328  }
329 }
330 
331 static void interruption_end(_THIS)
332 {
333  if (this != NULL && this->hidden != NULL && this->hidden->audioQueue != NULL
334  && this->hidden->interrupted
335  && AudioQueueStart(this->hidden->audioQueue, NULL) == AVAudioSessionErrorCodeNone) {
336  this->hidden->interrupted = SDL_FALSE;
337  }
338 }
339 
340 @interface SDLInterruptionListener : NSObject
341 
342 @property (nonatomic, assign) SDL_AudioDevice *device;
343 
344 @end
345 
346 @implementation SDLInterruptionListener
347 
348 - (void)audioSessionInterruption:(NSNotification *)note
349 {
350  @synchronized (self) {
351  NSNumber *type = note.userInfo[AVAudioSessionInterruptionTypeKey];
352  if (type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
353  interruption_begin(self.device);
354  } else {
355  interruption_end(self.device);
356  }
357  }
358 }
359 
360 - (void)applicationBecameActive:(NSNotification *)note
361 {
362  @synchronized (self) {
363  interruption_end(self.device);
364  }
365 }
366 
367 @end
368 
369 static BOOL update_audio_session(_THIS, SDL_bool open, SDL_bool allow_playandrecord)
370 {
371  @autoreleasepool {
372  AVAudioSession *session = [AVAudioSession sharedInstance];
373  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
374 
375  NSString *category = AVAudioSessionCategoryPlayback;
376  NSString *mode = AVAudioSessionModeDefault;
377  NSUInteger options = AVAudioSessionCategoryOptionMixWithOthers;
378  NSError *err = nil;
379  const char *hint;
380 
382  if (hint) {
383  if (SDL_strcasecmp(hint, "AVAudioSessionCategoryAmbient") == 0) {
384  category = AVAudioSessionCategoryAmbient;
385  } else if (SDL_strcasecmp(hint, "AVAudioSessionCategorySoloAmbient") == 0) {
386  category = AVAudioSessionCategorySoloAmbient;
387  options &= ~AVAudioSessionCategoryOptionMixWithOthers;
388  } else if (SDL_strcasecmp(hint, "AVAudioSessionCategoryPlayback") == 0 ||
389  SDL_strcasecmp(hint, "playback") == 0) {
390  category = AVAudioSessionCategoryPlayback;
391  options &= ~AVAudioSessionCategoryOptionMixWithOthers;
392  } else if (SDL_strcasecmp(hint, "AVAudioSessionCategoryPlayAndRecord") == 0 ||
393  SDL_strcasecmp(hint, "playandrecord") == 0) {
394  if (allow_playandrecord) {
395  category = AVAudioSessionCategoryPlayAndRecord;
396  }
397  }
398  } else if (open_playback_devices && open_capture_devices) {
399  category = AVAudioSessionCategoryPlayAndRecord;
400  } else if (open_capture_devices) {
401  category = AVAudioSessionCategoryRecord;
402  }
403 
404 #if !TARGET_OS_TV
405  if (category == AVAudioSessionCategoryPlayAndRecord) {
406  options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
407  }
408 #endif
409  if (category == AVAudioSessionCategoryRecord ||
410  category == AVAudioSessionCategoryPlayAndRecord) {
411  /* AVAudioSessionCategoryOptionAllowBluetooth isn't available in the SDK for
412  Apple TV but is still needed in order to output to Bluetooth devices.
413  */
414  options |= 0x4; /* AVAudioSessionCategoryOptionAllowBluetooth; */
415  }
416  if (category == AVAudioSessionCategoryPlayAndRecord) {
417  options |= AVAudioSessionCategoryOptionAllowBluetoothA2DP |
418  AVAudioSessionCategoryOptionAllowAirPlay;
419  }
420  if (category == AVAudioSessionCategoryPlayback ||
421  category == AVAudioSessionCategoryPlayAndRecord) {
422  options |= AVAudioSessionCategoryOptionDuckOthers;
423  }
424 
425  if ([session respondsToSelector:@selector(setCategory:mode:options:error:)]) {
426  if (![session.category isEqualToString:category] || session.categoryOptions != options) {
427  /* Stop the current session so we don't interrupt other application audio */
428  pause_audio_devices();
429  [session setActive:NO error:nil];
430  session_active = NO;
431 
432  if (![session setCategory:category mode:mode options:options error:&err]) {
433  NSString *desc = err.description;
434  SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
435  return NO;
436  }
437  }
438  } else {
439  if (![session.category isEqualToString:category]) {
440  /* Stop the current session so we don't interrupt other application audio */
441  pause_audio_devices();
442  [session setActive:NO error:nil];
443  session_active = NO;
444 
445  if (![session setCategory:category error:&err]) {
446  NSString *desc = err.description;
447  SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
448  return NO;
449  }
450  }
451  }
452 
453  if ((open_playback_devices || open_capture_devices) && !session_active) {
454  if (![session setActive:YES error:&err]) {
455  if ([err code] == AVAudioSessionErrorCodeResourceNotAvailable &&
456  category == AVAudioSessionCategoryPlayAndRecord) {
457  return update_audio_session(this, open, SDL_FALSE);
458  }
459 
460  NSString *desc = err.description;
461  SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String);
462  return NO;
463  }
464  session_active = YES;
465  resume_audio_devices();
466  } else if (!open_playback_devices && !open_capture_devices && session_active) {
467  pause_audio_devices();
468  [session setActive:NO error:nil];
469  session_active = NO;
470  }
471 
472  if (open) {
473  SDLInterruptionListener *listener = [SDLInterruptionListener new];
474  listener.device = this;
475 
476  [center addObserver:listener
477  selector:@selector(audioSessionInterruption:)
478  name:AVAudioSessionInterruptionNotification
479  object:session];
480 
481  /* An interruption end notification is not guaranteed to be sent if
482  we were previously interrupted... resuming if needed when the app
483  becomes active seems to be the way to go. */
484  // Note: object: below needs to be nil, as otherwise it filters by the object, and session doesn't send foreground / active notifications. johna
485  [center addObserver:listener
486  selector:@selector(applicationBecameActive:)
487  name:UIApplicationDidBecomeActiveNotification
488  object:nil];
489 
490  [center addObserver:listener
491  selector:@selector(applicationBecameActive:)
492  name:UIApplicationWillEnterForegroundNotification
493  object:nil];
494 
495  this->hidden->interruption_listener = CFBridgingRetain(listener);
496  } else {
497  SDLInterruptionListener *listener = nil;
498  listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener);
499  [center removeObserver:listener];
500  @synchronized (listener) {
501  listener.device = NULL;
502  }
503  }
504  }
505 
506  return YES;
507 }
508 #endif
509 
510 
511 /* The AudioQueue callback */
512 static void
513 outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
514 {
515  SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
516  if (SDL_AtomicGet(&this->hidden->shutdown)) {
517  return; /* don't do anything. */
518  }
519 
520  if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
521  /* Supply silence if audio is not enabled or paused */
522  SDL_memset(inBuffer->mAudioData, this->spec.silence, inBuffer->mAudioDataBytesCapacity);
523  } else if (this->stream) {
524  UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
525  Uint8 *ptr = (Uint8 *) inBuffer->mAudioData;
526 
527  while (remaining > 0) {
528  if (SDL_AudioStreamAvailable(this->stream) == 0) {
529  /* Generate the data */
530  SDL_LockMutex(this->mixer_lock);
531  (*this->callbackspec.callback)(this->callbackspec.userdata,
532  this->hidden->buffer, this->hidden->bufferSize);
533  SDL_UnlockMutex(this->mixer_lock);
534  this->hidden->bufferOffset = 0;
535  SDL_AudioStreamPut(this->stream, this->hidden->buffer, this->hidden->bufferSize);
536  }
537  if (SDL_AudioStreamAvailable(this->stream) > 0) {
538  int got;
539  UInt32 len = SDL_AudioStreamAvailable(this->stream);
540  if (len > remaining)
541  len = remaining;
542  got = SDL_AudioStreamGet(this->stream, ptr, len);
543  SDL_assert((got < 0) || (got == len));
544  if (got != len) {
545  SDL_memset(ptr, this->spec.silence, len);
546  }
547  ptr = ptr + len;
548  remaining -= len;
549  }
550  }
551  } else {
552  UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
553  Uint8 *ptr = (Uint8 *) inBuffer->mAudioData;
554 
555  while (remaining > 0) {
556  UInt32 len;
557  if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
558  /* Generate the data */
559  SDL_LockMutex(this->mixer_lock);
560  (*this->callbackspec.callback)(this->callbackspec.userdata,
561  this->hidden->buffer, this->hidden->bufferSize);
562  SDL_UnlockMutex(this->mixer_lock);
563  this->hidden->bufferOffset = 0;
564  }
565 
566  len = this->hidden->bufferSize - this->hidden->bufferOffset;
567  if (len > remaining) {
568  len = remaining;
569  }
570  SDL_memcpy(ptr, (char *)this->hidden->buffer +
571  this->hidden->bufferOffset, len);
572  ptr = ptr + len;
573  remaining -= len;
574  this->hidden->bufferOffset += len;
575  }
576  }
577 
578  AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
579 
580  inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
581 }
582 
583 static void
584 inputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
585  const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions,
586  const AudioStreamPacketDescription *inPacketDescs)
587 {
588  SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
589 
590  if (SDL_AtomicGet(&this->shutdown)) {
591  return; /* don't do anything. */
592  }
593 
594  /* ignore unless we're active. */
595  if (!SDL_AtomicGet(&this->paused) && SDL_AtomicGet(&this->enabled) && !SDL_AtomicGet(&this->paused)) {
596  const Uint8 *ptr = (const Uint8 *) inBuffer->mAudioData;
597  UInt32 remaining = inBuffer->mAudioDataByteSize;
598  while (remaining > 0) {
599  UInt32 len = this->hidden->bufferSize - this->hidden->bufferOffset;
600  if (len > remaining) {
601  len = remaining;
602  }
603 
604  SDL_memcpy((char *)this->hidden->buffer + this->hidden->bufferOffset, ptr, len);
605  ptr += len;
606  remaining -= len;
607  this->hidden->bufferOffset += len;
608 
609  if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
610  SDL_LockMutex(this->mixer_lock);
611  (*this->callbackspec.callback)(this->callbackspec.userdata, this->hidden->buffer, this->hidden->bufferSize);
612  SDL_UnlockMutex(this->mixer_lock);
613  this->hidden->bufferOffset = 0;
614  }
615  }
616  }
617 
618  AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
619 }
620 
621 
622 #if MACOSX_COREAUDIO
623 static const AudioObjectPropertyAddress alive_address =
624 {
625  kAudioDevicePropertyDeviceIsAlive,
626  kAudioObjectPropertyScopeGlobal,
627  kAudioObjectPropertyElementMaster
628 };
629 
630 static OSStatus
631 device_unplugged(AudioObjectID devid, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
632 {
634  SDL_bool dead = SDL_FALSE;
635  UInt32 isAlive = 1;
636  UInt32 size = sizeof (isAlive);
637  OSStatus error;
638 
639  if (!SDL_AtomicGet(&this->enabled)) {
640  return 0; /* already known to be dead. */
641  }
642 
643  error = AudioObjectGetPropertyData(this->hidden->deviceID, &alive_address,
644  0, NULL, &size, &isAlive);
645 
646  if (error == kAudioHardwareBadDeviceError) {
647  dead = SDL_TRUE; /* device was unplugged. */
648  } else if ((error == kAudioHardwareNoError) && (!isAlive)) {
649  dead = SDL_TRUE; /* device died in some other way. */
650  }
651 
652  if (dead) {
654  }
655 
656  return 0;
657 }
658 
659 /* macOS calls this when the default device changed (if we have a default device open). */
660 static OSStatus
661 default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inUserData)
662 {
663  SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
664  #if DEBUG_COREAUDIO
665  printf("COREAUDIO: default device changed for SDL audio device %p!\n", this);
666  #endif
667  SDL_AtomicSet(&this->hidden->device_change_flag, 1); /* let the audioqueue thread pick up on this when safe to do so. */
668  return noErr;
669 }
670 #endif
671 
672 static void
673 COREAUDIO_CloseDevice(_THIS)
674 {
675  const SDL_bool iscapture = this->iscapture;
676  int i;
677 
678 /* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
679 /* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
680 #if MACOSX_COREAUDIO
681  if (this->handle != NULL) { /* we don't register this listener for default devices. */
682  AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
683  }
684 #endif
685 
686  if (iscapture) {
687  open_capture_devices--;
688  } else {
689  open_playback_devices--;
690  }
691 
692 #if !MACOSX_COREAUDIO
693  update_audio_session(this, SDL_FALSE, SDL_TRUE);
694 #endif
695 
696  for (i = 0; i < num_open_devices; ++i) {
697  if (open_devices[i] == this) {
698  --num_open_devices;
699  if (i < num_open_devices) {
700  SDL_memmove(&open_devices[i], &open_devices[i+1], sizeof(open_devices[i])*(num_open_devices - i));
701  }
702  break;
703  }
704  }
705  if (num_open_devices == 0) {
707  open_devices = NULL;
708  }
709 
710  /* if callback fires again, feed silence; don't call into the app. */
711  SDL_AtomicSet(&this->paused, 1);
712 
713  if (this->hidden->audioQueue) {
714  AudioQueueDispose(this->hidden->audioQueue, 1);
715  }
716 
717  if (this->hidden->thread) {
718  SDL_AtomicSet(&this->hidden->shutdown, 1);
719  SDL_WaitThread(this->hidden->thread, NULL);
720  }
721 
722  if (this->hidden->ready_semaphore) {
723  SDL_DestroySemaphore(this->hidden->ready_semaphore);
724  }
725 
726  /* AudioQueueDispose() frees the actual buffer objects. */
727  SDL_free(this->hidden->audioBuffer);
728  SDL_free(this->hidden->thread_error);
729  SDL_free(this->hidden->buffer);
730  SDL_free(this->hidden);
731 }
732 
733 #if MACOSX_COREAUDIO
734 static int
735 prepare_device(_THIS, void *handle, int iscapture)
736 {
737  AudioDeviceID devid = (AudioDeviceID) ((size_t) handle);
738  OSStatus result = noErr;
739  UInt32 size = 0;
740  UInt32 alive = 0;
741  pid_t pid = 0;
742 
743  AudioObjectPropertyAddress addr = {
744  0,
745  kAudioObjectPropertyScopeGlobal,
746  kAudioObjectPropertyElementMaster
747  };
748 
749  if (handle == NULL) {
750  size = sizeof (AudioDeviceID);
751  addr.mSelector =
752  ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
753  kAudioHardwarePropertyDefaultOutputDevice);
754  result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
755  0, NULL, &size, &devid);
756  CHECK_RESULT("AudioHardwareGetProperty (default device)");
757  }
758 
759  addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
760  addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
761  kAudioDevicePropertyScopeOutput;
762 
763  size = sizeof (alive);
764  result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &alive);
765  CHECK_RESULT
766  ("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
767 
768  if (!alive) {
769  SDL_SetError("CoreAudio: requested device exists, but isn't alive.");
770  return 0;
771  }
772 
773  addr.mSelector = kAudioDevicePropertyHogMode;
774  size = sizeof (pid);
775  result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &pid);
776 
777  /* some devices don't support this property, so errors are fine here. */
778  if ((result == noErr) && (pid != -1)) {
779  SDL_SetError("CoreAudio: requested device is being hogged.");
780  return 0;
781  }
782 
783  this->hidden->deviceID = devid;
784  return 1;
785 }
786 
787 static int
788 assign_device_to_audioqueue(_THIS)
789 {
790  const AudioObjectPropertyAddress prop = {
791  kAudioDevicePropertyDeviceUID,
792  this->iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
793  kAudioObjectPropertyElementMaster
794  };
795 
796  OSStatus result;
797  CFStringRef devuid;
798  UInt32 devuidsize = sizeof (devuid);
799  result = AudioObjectGetPropertyData(this->hidden->deviceID, &prop, 0, NULL, &devuidsize, &devuid);
800  CHECK_RESULT("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID)");
801  result = AudioQueueSetProperty(this->hidden->audioQueue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize);
802  CHECK_RESULT("AudioQueueSetProperty (kAudioQueueProperty_CurrentDevice)");
803 
804  return 1;
805 }
806 #endif
807 
808 static int
809 prepare_audioqueue(_THIS)
810 {
811  const AudioStreamBasicDescription *strdesc = &this->hidden->strdesc;
812  const int iscapture = this->iscapture;
813  OSStatus result;
814  int i;
815 
816  SDL_assert(CFRunLoopGetCurrent() != NULL);
817 
818  if (iscapture) {
819  result = AudioQueueNewInput(strdesc, inputCallback, this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
820  CHECK_RESULT("AudioQueueNewInput");
821  } else {
822  result = AudioQueueNewOutput(strdesc, outputCallback, this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
823  CHECK_RESULT("AudioQueueNewOutput");
824  }
825 
826  #if MACOSX_COREAUDIO
827  if (!assign_device_to_audioqueue(this)) {
828  return 0;
829  }
830 
831  /* only listen for unplugging on specific devices, not the default device, as that should
832  switch to a different device (or hang out silently if there _is_ no other device). */
833  if (this->handle != NULL) {
834  /* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
835  /* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
836  /* Fire a callback if the device stops being "alive" (disconnected, etc). */
837  /* If this fails, oh well, we won't notice a device had an extraordinary event take place. */
838  AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
839  }
840  #endif
841 
842  /* Calculate the final parameters for this audio specification */
844 
845  /* Set the channel layout for the audio queue */
846  AudioChannelLayout layout;
847  SDL_zero(layout);
848  switch (this->spec.channels) {
849  case 1:
850  layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
851  break;
852  case 2:
853  layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
854  break;
855  case 3:
856  layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4;
857  break;
858  case 4:
859  layout.mChannelLayoutTag = kAudioChannelLayoutTag_Quadraphonic;
860  break;
861  case 5:
862  layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_0_A;
863  break;
864  case 6:
865  layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_1_A;
866  break;
867  case 7:
868  /* FIXME: Need to move channel[4] (BC) to channel[6] */
869  layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A;
870  break;
871  case 8:
872  layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A;
873  break;
874  }
875  if (layout.mChannelLayoutTag != 0) {
876  result = AudioQueueSetProperty(this->hidden->audioQueue, kAudioQueueProperty_ChannelLayout, &layout, sizeof(layout));
877  CHECK_RESULT("AudioQueueSetProperty(kAudioQueueProperty_ChannelLayout)");
878  }
879 
880  /* Allocate a sample buffer */
881  this->hidden->bufferSize = this->spec.size;
882  this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize;
883 
884  this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
885  if (this->hidden->buffer == NULL) {
886  SDL_OutOfMemory();
887  return 0;
888  }
889 
890  /* Make sure we can feed the device a minimum amount of time */
891  double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0;
892 #if defined(__IPHONEOS__)
893  if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) {
894  /* Older iOS hardware, use 40 ms as a minimum time */
895  MINIMUM_AUDIO_BUFFER_TIME_MS = 40.0;
896  }
897 #endif
898  const double msecs = (this->spec.samples / ((double) this->spec.freq)) * 1000.0;
899  int numAudioBuffers = 2;
900  if (msecs < MINIMUM_AUDIO_BUFFER_TIME_MS) { /* use more buffers if we have a VERY small sample set. */
901  numAudioBuffers = ((int)SDL_ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2);
902  }
903 
904  this->hidden->numAudioBuffers = numAudioBuffers;
905  this->hidden->audioBuffer = SDL_calloc(1, sizeof (AudioQueueBufferRef) * numAudioBuffers);
906  if (this->hidden->audioBuffer == NULL) {
907  SDL_OutOfMemory();
908  return 0;
909  }
910 
911 #if DEBUG_COREAUDIO
912  printf("COREAUDIO: numAudioBuffers == %d\n", numAudioBuffers);
913 #endif
914 
915  for (i = 0; i < numAudioBuffers; i++) {
916  result = AudioQueueAllocateBuffer(this->hidden->audioQueue, this->spec.size, &this->hidden->audioBuffer[i]);
917  CHECK_RESULT("AudioQueueAllocateBuffer");
918  SDL_memset(this->hidden->audioBuffer[i]->mAudioData, this->spec.silence, this->hidden->audioBuffer[i]->mAudioDataBytesCapacity);
919  this->hidden->audioBuffer[i]->mAudioDataByteSize = this->hidden->audioBuffer[i]->mAudioDataBytesCapacity;
920  /* !!! FIXME: should we use AudioQueueEnqueueBufferWithParameters and specify all frames be "trimmed" so these are immediately ready to refill with SDL callback data? */
921  result = AudioQueueEnqueueBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i], 0, NULL);
922  CHECK_RESULT("AudioQueueEnqueueBuffer");
923  }
924 
925  result = AudioQueueStart(this->hidden->audioQueue, NULL);
926  CHECK_RESULT("AudioQueueStart");
927 
928  /* We're running! */
929  return 1;
930 }
931 
932 static int
933 audioqueue_thread(void *arg)
934 {
935  SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
936 
937  #if MACOSX_COREAUDIO
938  const AudioObjectPropertyAddress default_device_address = {
939  this->iscapture ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice,
940  kAudioObjectPropertyScopeGlobal,
941  kAudioObjectPropertyElementMaster
942  };
943 
944  if (this->handle == NULL) { /* opened the default device? Register to know if the user picks a new default. */
945  /* we don't care if this fails; we just won't change to new default devices, but we still otherwise function in this case. */
946  AudioObjectAddPropertyListener(kAudioObjectSystemObject, &default_device_address, default_device_changed, this);
947  }
948  #endif
949 
950  const int rc = prepare_audioqueue(this);
951  if (!rc) {
952  this->hidden->thread_error = SDL_strdup(SDL_GetError());
953  SDL_SemPost(this->hidden->ready_semaphore);
954  return 0;
955  }
956 
958 
959  /* init was successful, alert parent thread and start running... */
960  SDL_SemPost(this->hidden->ready_semaphore);
961 
962  while (!SDL_AtomicGet(&this->hidden->shutdown)) {
963  CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
964 
965  #if MACOSX_COREAUDIO
966  if ((this->handle == NULL) && SDL_AtomicGet(&this->hidden->device_change_flag)) {
967  SDL_AtomicSet(&this->hidden->device_change_flag, 0);
968 
969  #if DEBUG_COREAUDIO
970  printf("COREAUDIO: audioqueue_thread is trying to switch to new default device!\n");
971  #endif
972 
973  /* if any of this fails, there's not much to do but wait to see if the user gives up
974  and quits (flagging the audioqueue for shutdown), or toggles to some other system
975  output device (in which case we'll try again). */
976  const AudioDeviceID prev_devid = this->hidden->deviceID;
977  if (prepare_device(this, this->handle, this->iscapture) && (prev_devid != this->hidden->deviceID)) {
978  AudioQueueStop(this->hidden->audioQueue, 1);
979  if (assign_device_to_audioqueue(this)) {
980  int i;
981  for (i = 0; i < this->hidden->numAudioBuffers; i++) {
982  SDL_memset(this->hidden->audioBuffer[i]->mAudioData, this->spec.silence, this->hidden->audioBuffer[i]->mAudioDataBytesCapacity);
983  /* !!! FIXME: should we use AudioQueueEnqueueBufferWithParameters and specify all frames be "trimmed" so these are immediately ready to refill with SDL callback data? */
984  AudioQueueEnqueueBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i], 0, NULL);
985  }
986  AudioQueueStart(this->hidden->audioQueue, NULL);
987  }
988  }
989  }
990  #endif
991  }
992 
993  if (!this->iscapture) { /* Drain off any pending playback. */
994  const CFTimeInterval secs = (((this->spec.size / (SDL_AUDIO_BITSIZE(this->spec.format) / 8)) / this->spec.channels) / ((CFTimeInterval) this->spec.freq)) * 2.0;
995  CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0);
996  }
997 
998  #if MACOSX_COREAUDIO
999  if (this->handle == NULL) {
1000  /* we don't care if this fails; we just won't change to new default devices, but we still otherwise function in this case. */
1001  AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &default_device_address, default_device_changed, this);
1002  }
1003  #endif
1004 
1005  return 0;
1006 }
1007 
1008 static int
1009 COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
1010 {
1011  AudioStreamBasicDescription *strdesc;
1012  SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
1013  int valid_datatype = 0;
1014  SDL_AudioDevice **new_open_devices;
1015 
1016  /* Initialize all variables that we clean on shutdown */
1017  this->hidden = (struct SDL_PrivateAudioData *)
1018  SDL_malloc((sizeof *this->hidden));
1019  if (this->hidden == NULL) {
1020  return SDL_OutOfMemory();
1021  }
1022  SDL_zerop(this->hidden);
1023 
1024  strdesc = &this->hidden->strdesc;
1025 
1026  if (iscapture) {
1027  open_capture_devices++;
1028  } else {
1029  open_playback_devices++;
1030  }
1031 
1032  new_open_devices = (SDL_AudioDevice **)SDL_realloc(open_devices, sizeof(open_devices[0]) * (num_open_devices + 1));
1033  if (new_open_devices) {
1034  open_devices = new_open_devices;
1035  open_devices[num_open_devices++] = this;
1036  }
1037 
1038 #if !MACOSX_COREAUDIO
1039  if (!update_audio_session(this, SDL_TRUE, SDL_TRUE)) {
1040  return -1;
1041  }
1042 
1043  /* Stop CoreAudio from doing expensive audio rate conversion */
1044  @autoreleasepool {
1045  AVAudioSession* session = [AVAudioSession sharedInstance];
1046  [session setPreferredSampleRate:this->spec.freq error:nil];
1047  this->spec.freq = (int)session.sampleRate;
1048 #if TARGET_OS_TV
1049  if (iscapture) {
1050  [session setPreferredInputNumberOfChannels:this->spec.channels error:nil];
1051  this->spec.channels = session.preferredInputNumberOfChannels;
1052  } else {
1053  [session setPreferredOutputNumberOfChannels:this->spec.channels error:nil];
1054  this->spec.channels = session.preferredOutputNumberOfChannels;
1055  }
1056 #else
1057  /* Calling setPreferredOutputNumberOfChannels seems to break audio output on iOS */
1058 #endif /* TARGET_OS_TV */
1059  }
1060 #endif
1061 
1062  /* Setup a AudioStreamBasicDescription with the requested format */
1063  SDL_zerop(strdesc);
1064  strdesc->mFormatID = kAudioFormatLinearPCM;
1065  strdesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
1066  strdesc->mChannelsPerFrame = this->spec.channels;
1067  strdesc->mSampleRate = this->spec.freq;
1068  strdesc->mFramesPerPacket = 1;
1069 
1070  while ((!valid_datatype) && (test_format)) {
1071  this->spec.format = test_format;
1072  /* CoreAudio handles most of SDL's formats natively, but not U16, apparently. */
1073  switch (test_format) {
1074  case AUDIO_U8:
1075  case AUDIO_S8:
1076  case AUDIO_S16LSB:
1077  case AUDIO_S16MSB:
1078  case AUDIO_S32LSB:
1079  case AUDIO_S32MSB:
1080  case AUDIO_F32LSB:
1081  case AUDIO_F32MSB:
1082  valid_datatype = 1;
1083  strdesc->mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
1084  if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
1085  strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
1086 
1087  if (SDL_AUDIO_ISFLOAT(this->spec.format))
1088  strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
1089  else if (SDL_AUDIO_ISSIGNED(this->spec.format))
1090  strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
1091  break;
1092 
1093  default:
1094  test_format = SDL_NextAudioFormat();
1095  break;
1096  }
1097  }
1098 
1099  if (!valid_datatype) { /* shouldn't happen, but just in case... */
1100  return SDL_SetError("Unsupported audio format");
1101  }
1102 
1103  strdesc->mBytesPerFrame = strdesc->mChannelsPerFrame * strdesc->mBitsPerChannel / 8;
1104  strdesc->mBytesPerPacket = strdesc->mBytesPerFrame * strdesc->mFramesPerPacket;
1105 
1106 #if MACOSX_COREAUDIO
1107  if (!prepare_device(this, handle, iscapture)) {
1108  return -1;
1109  }
1110 #endif
1111 
1112  /* This has to init in a new thread so it can get its own CFRunLoop. :/ */
1113  SDL_AtomicSet(&this->hidden->shutdown, 0);
1114  this->hidden->ready_semaphore = SDL_CreateSemaphore(0);
1115  if (!this->hidden->ready_semaphore) {
1116  return -1; /* oh well. */
1117  }
1118 
1119  this->hidden->thread = SDL_CreateThreadInternal(audioqueue_thread, "AudioQueue thread", 512 * 1024, this);
1120  if (!this->hidden->thread) {
1121  return -1;
1122  }
1123 
1124  SDL_SemWait(this->hidden->ready_semaphore);
1125  SDL_DestroySemaphore(this->hidden->ready_semaphore);
1126  this->hidden->ready_semaphore = NULL;
1127 
1128  if ((this->hidden->thread != NULL) && (this->hidden->thread_error != NULL)) {
1129  SDL_SetError("%s", this->hidden->thread_error);
1130  return -1;
1131  }
1132 
1133  return (this->hidden->thread != NULL) ? 0 : -1;
1134 }
1135 
1136 static void
1137 COREAUDIO_Deinitialize(void)
1138 {
1139 #if MACOSX_COREAUDIO
1140  AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
1141  free_audio_device_list(&capture_devs);
1142  free_audio_device_list(&output_devs);
1143 #endif
1144 }
1145 
1146 static int
1147 COREAUDIO_Init(SDL_AudioDriverImpl * impl)
1148 {
1149  /* Set the function pointers */
1150  impl->OpenDevice = COREAUDIO_OpenDevice;
1151  impl->CloseDevice = COREAUDIO_CloseDevice;
1152  impl->Deinitialize = COREAUDIO_Deinitialize;
1153 
1154 #if MACOSX_COREAUDIO
1155  impl->DetectDevices = COREAUDIO_DetectDevices;
1156  AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
1157 #else
1158  impl->OnlyHasDefaultOutputDevice = 1;
1159  impl->OnlyHasDefaultCaptureDevice = 1;
1160 #endif
1161 
1162  impl->ProvidesOwnCallbackThread = 1;
1163  impl->HasCaptureSupport = 1;
1164 
1165  return 1; /* this audio target is available. */
1166 }
1167 
1169  "coreaudio", "CoreAudio", COREAUDIO_Init, 0
1170 };
1171 
1172 #endif /* SDL_AUDIO_DRIVER_COREAUDIO */
1173 
1174 /* vi: set ts=4 sw=4 expandtab: */
#define _THIS
#define SDL_assert(condition)
Definition: SDL_assert.h:171
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
Definition: SDL_audio.c:534
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
Definition: SDL_audio.c:472
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1689
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1650
static SDL_AudioDevice * open_devices[16]
Definition: SDL_audio.c:34
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:489
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1662
#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
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_AudioStreamGet
#define SDL_SetThreadPriority
#define SDL_SemPost
#define SDL_SemWait
#define SDL_LockMutex
#define SDL_AudioStreamAvailable
#define SDL_malloc
#define SDL_realloc
#define SDL_strcasecmp
#define SDL_DestroySemaphore
#define SDL_free
#define SDL_strdup
#define SDL_CreateSemaphore
#define SDL_AudioStreamPut
#define SDL_WaitThread
#define SDL_AtomicGet
#define SDL_memcpy
#define SDL_memmove
#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:88
#define SDL_HINT_AUDIO_CATEGORY
A variable controlling the audio category on iOS and Mac OS X.
Definition: SDL_hints.h:1278
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
GLuint GLuint stream
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLenum mode
GLuint64EXT * result
GLenum GLsizei len
GLenum const void * addr
GLuint const GLchar * name
GLsizeiptr size
#define SDL_zero(x)
Definition: SDL_stdinc.h:426
SDL_bool
Definition: SDL_stdinc.h:168
@ SDL_TRUE
Definition: SDL_stdinc.h:170
@ SDL_FALSE
Definition: SDL_stdinc.h:169
uint8_t Uint8
Definition: SDL_stdinc.h:185
#define SDL_zerop(x)
Definition: SDL_stdinc.h:427
AudioBootStrap COREAUDIO_bootstrap
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:391
@ SDL_THREAD_PRIORITY_HIGH
Definition: SDL_thread.h:67
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
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
#define NULL
Definition: begin_code.h:163
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
SDL_AudioSpec spec
Definition: loopwave.c:31
static SDL_AudioDeviceID device
Definition: loopwave.c:37
set set set set set set set macro pixldst1 abits if abits op else op endif endm macro pixldst2 abits if abits op else op endif endm macro pixldst4 abits if abits op else op endif endm macro pixldst0 abits op endm macro pixldst3 mem_operand op endm macro pixldst30 mem_operand op endm macro pixldst abits if abits elseif abits elseif abits elseif abits elseif abits pixldst0 abits else pixldst0 abits pixldst0 abits pixldst0 abits pixldst0 abits endif elseif abits else pixldst0 abits pixldst0 abits endif elseif abits else error unsupported bpp *numpix else pixst endif endm macro pixld1_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl else error unsupported endif endm macro pixld2_s mem_operand if mov asr add asl add asl mov asr sub UNIT_X add asl mov asr add asl add asl mov asr add UNIT_X add asl else pixld1_s mem_operand pixld1_s mem_operand endif endm macro pixld0_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl endif endm macro pixld_s_internal mem_operand if mem_operand pixld2_s mem_operand pixdeinterleave basereg elseif mem_operand elseif mem_operand elseif mem_operand elseif mem_operand pixld0_s mem_operand else pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else error unsupported mem_operand if bpp mem_operand endif endm macro vuzp8 reg2 vuzp d d &reg2 endm macro vzip8 reg2 vzip d d &reg2 endm macro pixdeinterleave basereg basereg basereg basereg basereg endif endm macro pixinterleave basereg basereg basereg basereg basereg endif endm macro PF boost_increment endif if endif PF tst PF addne PF subne PF cmp ORIG_W if endif if endif if endif PF subge ORIG_W PF subges if endif if endif if endif endif endm macro cache_preload_simple endif if dst_r_bpp pld[DST_R, #(PREFETCH_DISTANCE_SIMPLE *dst_r_bpp/8)] endif if mask_bpp pld if[MASK, #(PREFETCH_DISTANCE_SIMPLE *mask_bpp/8)] endif endif endm macro fetch_mask_pixblock pixld mask_basereg pixblock_size MASK endm macro ensure_destination_ptr_alignment process_pixblock_tail_head if beq irp skip1(dst_w_bpp<=(lowbit *8)) &&((lowbit *8)<(pixblock_size *dst_w_bpp)) .if lowbit< 16 tst DST_R
set set set set set set set set set set set set set set set set set set set set *set set set macro pixldst op &r &cond WK op &r &cond WK op &r &cond WK else op &m &cond &ia op &r &cond WK else op &m &cond &ia elseif elseif else error unsupported base if elseif elseif else error unsupported unaligned pixldst unaligned endm macro pixst base base else pixldst base endif endm macro PF ptr
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
Uint8 silence
Definition: SDL_audio.h:183
SDL_AudioFormat format
Definition: SDL_audio.h:181
AudioStreamBasicDescription strdesc
Definition: SDL_coreaudio.h:54
int paused
Definition: testoverlay2.c:149
int alive
Definition: testsem.c:24
typedef int(__stdcall *FARPROC)()