21#include "../../SDL_internal.h"
23#if SDL_AUDIO_DRIVER_COREAUDIO
29#include "../SDL_audio_c.h"
30#include "../SDL_sysaudio.h"
33#include "../../thread/SDL_systhread.h"
35#define DEBUG_COREAUDIO 0
37#define CHECK_RESULT(msg) \
38 if (result != noErr) { \
39 SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
44static const AudioObjectPropertyAddress devlist_address = {
45 kAudioHardwarePropertyDevices,
46 kAudioObjectPropertyScopeGlobal,
47 kAudioObjectPropertyElementMaster
50typedef void (*addDevFn)(
const char *
name,
const int iscapture, AudioDeviceID devId,
void *
data);
52typedef struct AudioDeviceList
56 struct AudioDeviceList *next;
59static AudioDeviceList *output_devs =
NULL;
60static AudioDeviceList *capture_devs =
NULL;
63add_to_internal_dev_list(
const int iscapture, AudioDeviceID devId)
65 AudioDeviceList *item = (AudioDeviceList *)
SDL_malloc(
sizeof (AudioDeviceList));
71 item->next = iscapture ? capture_devs : output_devs;
82addToDevList(
const char *
name,
const int iscapture, AudioDeviceID devId,
void *
data)
84 if (add_to_internal_dev_list(iscapture, devId)) {
90build_device_list(
int iscapture, addDevFn addfn,
void *addfndata)
94 AudioDeviceID *devs =
NULL;
98 result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
100 if (
result != kAudioHardwareNoError)
103 devs = (AudioDeviceID *) alloca(
size);
107 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
108 &devlist_address, 0,
NULL, &
size, devs);
109 if (
result != kAudioHardwareNoError)
112 max =
size /
sizeof (AudioDeviceID);
113 for (
i = 0;
i < max;
i++) {
114 CFStringRef cfstr =
NULL;
116 AudioDeviceID dev = devs[i];
117 AudioBufferList *buflist =
NULL;
120 const AudioObjectPropertyAddress
addr = {
121 kAudioDevicePropertyStreamConfiguration,
122 iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
123 kAudioObjectPropertyElementMaster
126 const AudioObjectPropertyAddress nameaddr = {
127 kAudioObjectPropertyName,
128 iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
129 kAudioObjectPropertyElementMaster
145 for (
j = 0;
j < buflist->mNumberBuffers;
j++) {
146 if (buflist->mBuffers[
j].mNumberChannels > 0) {
159 size =
sizeof (CFStringRef);
160 result = AudioObjectGetPropertyData(dev, &nameaddr, 0,
NULL, &
size, &cfstr);
161 if (
result != kAudioHardwareNoError)
164 len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
165 kCFStringEncodingUTF8);
168 usable = ((ptr !=
NULL) &&
170 (cfstr, ptr,
len + 1, kCFStringEncodingUTF8)));
177 while ((
len > 0) && (ptr[
len - 1] ==
' ')) {
187 printf(
"COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
188 ((iscapture) ?
"capture" :
"output"),
189 (
int)
i, ptr, (
int) dev);
191 addfn(ptr, iscapture, dev, addfndata);
198free_audio_device_list(AudioDeviceList **list)
200 AudioDeviceList *item = *list;
202 AudioDeviceList *next = item->next;
210COREAUDIO_DetectDevices(
void)
217build_device_change_list(
const char *
name,
const int iscapture, AudioDeviceID devId,
void *
data)
219 AudioDeviceList **list = (AudioDeviceList **)
data;
220 AudioDeviceList *item;
221 for (item = *list; item !=
NULL; item = item->next) {
222 if (item->devid == devId) {
228 add_to_internal_dev_list(iscapture, devId);
233reprocess_device_list(
const int iscapture, AudioDeviceList **list)
235 AudioDeviceList *item;
236 AudioDeviceList *prev =
NULL;
237 for (item = *list; item !=
NULL; item = item->next) {
241 build_device_list(iscapture, build_device_change_list, list);
245 while (item !=
NULL) {
246 AudioDeviceList *next = item->next;
252 prev->next = item->next;
264device_list_changed(AudioObjectID systemObj, UInt32 num_addr,
const AudioObjectPropertyAddress *addrs,
void *
data)
266 reprocess_device_list(
SDL_TRUE, &capture_devs);
267 reprocess_device_list(
SDL_FALSE, &output_devs);
273static int open_playback_devices = 0;
274static int open_capture_devices = 0;
278static void interruption_begin(
_THIS)
280 if (
this !=
NULL && this->hidden->audioQueue !=
NULL) {
281 this->hidden->interrupted =
SDL_TRUE;
282 AudioQueuePause(this->hidden->audioQueue);
286static void interruption_end(
_THIS)
288 if (
this !=
NULL && this->hidden !=
NULL && this->hidden->audioQueue !=
NULL
289 && this->hidden->interrupted
290 && AudioQueueStart(this->hidden->audioQueue,
NULL) == AVAudioSessionErrorCodeNone) {
295@interface SDLInterruptionListener : NSObject
301@implementation SDLInterruptionListener
303- (
void)audioSessionInterruption:(NSNotification *)note
305 @
synchronized (
self) {
306 NSNumber *
type = note.userInfo[AVAudioSessionInterruptionTypeKey];
307 if (
type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
308 interruption_begin(
self.
device);
310 interruption_end(
self.
device);
315- (
void)applicationBecameActive:(NSNotification *)note
317 @
synchronized (
self) {
318 interruption_end(
self.
device);
327 AVAudioSession *session = [AVAudioSession sharedInstance];
328 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
330 NSString *category = AVAudioSessionCategoryAmbient;
333 if (open_playback_devices && open_capture_devices) {
334 category = AVAudioSessionCategoryPlayAndRecord;
335 }
else if (open_capture_devices) {
336 category = AVAudioSessionCategoryRecord;
341 category = AVAudioSessionCategoryAmbient;
342 }
else if (
SDL_strcasecmp(hint,
"AVAudioSessionCategorySoloAmbient") == 0) {
343 category = AVAudioSessionCategorySoloAmbient;
344 }
else if (
SDL_strcasecmp(hint,
"AVAudioSessionCategoryPlayback") == 0 ||
346 category = AVAudioSessionCategoryPlayback;
351 if (![session setCategory:category error:&err]) {
352 NSString *desc = err.description;
353 SDL_SetError(
"Could not set Audio Session category: %s", desc.UTF8String);
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);
363 }
else if (!open_playback_devices && !open_capture_devices) {
364 [session setActive:NO error:nil];
368 SDLInterruptionListener *listener = [SDLInterruptionListener new];
369 listener.device =
this;
371 [center addObserver:listener
372 selector:@selector(audioSessionInterruption:)
373 name:AVAudioSessionInterruptionNotification
380 [center addObserver:listener
381 selector:@selector(applicationBecameActive:)
382 name:UIApplicationDidBecomeActiveNotification
385 [center addObserver:listener
386 selector:@selector(applicationBecameActive:)
387 name:UIApplicationWillEnterForegroundNotification
390 this->hidden->interruption_listener = CFBridgingRetain(listener);
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;
410outputCallback(
void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
419 SDL_memset(inBuffer->mAudioData, this->spec.silence, inBuffer->mAudioDataBytesCapacity);
421 UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
424 while (remaining > 0) {
426 if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
429 (*this->callbackspec.callback)(this->callbackspec.userdata,
430 this->hidden->buffer, this->hidden->bufferSize);
432 this->hidden->bufferOffset = 0;
435 len = this->hidden->bufferSize - this->hidden->bufferOffset;
436 if (
len > remaining) {
439 SDL_memcpy(ptr, (
char *)this->hidden->buffer +
440 this->hidden->bufferOffset,
len);
443 this->hidden->bufferOffset +=
len;
447 AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0,
NULL);
449 inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
453inputCallback(
void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
454 const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions,
455 const AudioStreamPacketDescription *inPacketDescs )
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) {
473 SDL_memcpy((
char *)this->hidden->buffer + this->hidden->bufferOffset, ptr,
len);
476 this->hidden->bufferOffset +=
len;
478 if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
480 (*this->callbackspec.callback)(this->callbackspec.userdata, this->hidden->buffer, this->hidden->bufferSize);
482 this->hidden->bufferOffset = 0;
487 AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0,
NULL);
492static const AudioObjectPropertyAddress alive_address =
494 kAudioDevicePropertyDeviceIsAlive,
495 kAudioObjectPropertyScopeGlobal,
496 kAudioObjectPropertyElementMaster
500device_unplugged(AudioObjectID devid, UInt32 num_addr,
const AudioObjectPropertyAddress *addrs,
void *
data)
505 UInt32
size =
sizeof (isAlive);
512 error = AudioObjectGetPropertyData(this->hidden->deviceID, &alive_address,
515 if (error == kAudioHardwareBadDeviceError) {
517 }
else if ((error == kAudioHardwareNoError) && (!isAlive)) {
530COREAUDIO_CloseDevice(
_THIS)
532 const SDL_bool iscapture = this->iscapture;
538 AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged,
this);
548 if (this->hidden->audioQueue) {
549 AudioQueueDispose(this->hidden->audioQueue, 1);
552 if (this->hidden->thread) {
557 if (this->hidden->ready_semaphore) {
562 SDL_free(this->hidden->audioBuffer);
563 SDL_free(this->hidden->thread_error);
568 open_capture_devices--;
570 open_playback_devices--;
578 AudioDeviceID devid = (AudioDeviceID) ((
size_t)
handle);
584 AudioObjectPropertyAddress
addr = {
586 kAudioObjectPropertyScopeGlobal,
587 kAudioObjectPropertyElementMaster
591 size =
sizeof (AudioDeviceID);
593 ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
594 kAudioHardwarePropertyDefaultOutputDevice);
595 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &
addr,
597 CHECK_RESULT(
"AudioHardwareGetProperty (default device)");
600 addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
601 addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
602 kAudioDevicePropertyScopeOutput;
607 (
"AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
610 SDL_SetError(
"CoreAudio: requested device exists, but isn't alive.");
614 addr.mSelector = kAudioDevicePropertyHogMode;
619 if ((
result == noErr) && (pid != -1)) {
620 SDL_SetError(
"CoreAudio: requested device is being hogged.");
624 this->hidden->deviceID = devid;
630prepare_audioqueue(
_THIS)
632 const AudioStreamBasicDescription *strdesc = &this->hidden->strdesc;
633 const int iscapture = this->iscapture;
640 result = AudioQueueNewInput(strdesc, inputCallback,
this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
641 CHECK_RESULT(
"AudioQueueNewInput");
643 result = AudioQueueNewOutput(strdesc, outputCallback,
this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
644 CHECK_RESULT(
"AudioQueueNewOutput");
649 const AudioObjectPropertyAddress prop = {
650 kAudioDevicePropertyDeviceUID,
651 iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
652 kAudioObjectPropertyElementMaster
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)");
664 AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged,
this);
672 this->hidden->bufferSize = this->
spec.
size;
673 this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize;
675 this->hidden->buffer =
SDL_malloc(this->hidden->bufferSize);
676 if (this->hidden->buffer ==
NULL) {
682 double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0;
683#if defined(__IPHONEOS__)
684 if (
floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) {
686 MINIMUM_AUDIO_BUFFER_TIME_MS = 40.0;
690 int numAudioBuffers = 2;
691 if (msecs < MINIMUM_AUDIO_BUFFER_TIME_MS) {
692 numAudioBuffers = ((int)
SDL_ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2);
695 this->hidden->audioBuffer =
SDL_calloc(1,
sizeof (AudioQueueBufferRef) * numAudioBuffers);
696 if (this->hidden->audioBuffer ==
NULL) {
702 printf(
"COREAUDIO: numAudioBuffers == %d\n", numAudioBuffers);
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");
714 result = AudioQueueStart(this->hidden->audioQueue,
NULL);
715 CHECK_RESULT(
"AudioQueueStart");
722audioqueue_thread(
void *arg)
725 const int rc = prepare_audioqueue(
this);
737 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
740 if (!this->iscapture) {
742 CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0);
749COREAUDIO_OpenDevice(
_THIS,
void *
handle,
const char *devname,
int iscapture)
751 AudioStreamBasicDescription *strdesc;
753 int valid_datatype = 0;
758 if (this->hidden ==
NULL) {
763 strdesc = &this->hidden->strdesc;
766 open_capture_devices++;
768 open_playback_devices++;
772 if (!update_audio_session(
this,
SDL_TRUE)) {
778 AVAudioSession* session = [AVAudioSession sharedInstance];
779 [session setPreferredSampleRate:this->spec.freq error:nil];
780 this->
spec.
freq = (int)session.sampleRate;
786 strdesc->mFormatID = kAudioFormatLinearPCM;
787 strdesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
789 strdesc->mSampleRate = this->
spec.
freq;
790 strdesc->mFramesPerPacket = 1;
792 while ((!valid_datatype) && (test_format)) {
795 switch (test_format) {
809 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
812 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
814 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
819 if (!valid_datatype) {
835 if (!this->hidden->ready_semaphore) {
840 if (!this->hidden->thread) {
846 this->hidden->ready_semaphore =
NULL;
848 if ((this->hidden->thread !=
NULL) && (this->hidden->thread_error !=
NULL)) {
853 return (this->hidden->thread !=
NULL) ? 0 : -1;
857COREAUDIO_Deinitialize(
void)
860 AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed,
NULL);
861 free_audio_device_list(&capture_devs);
862 free_audio_device_list(&output_devs);
876 AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed,
NULL);
889 "coreaudio",
"CoreAudio", COREAUDIO_Init, 0
#define SDL_assert(condition)
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
#define SDL_AUDIO_ISBIGENDIAN(x)
Uint16 SDL_AudioFormat
Audio format flags.
#define SDL_AUDIO_ISSIGNED(x)
#define SDL_AUDIO_ISFLOAT(x)
#define SDL_AUDIO_BITSIZE(x)
#define SDL_SetThreadPriority
#define SDL_DestroySemaphore
#define SDL_CreateSemaphore
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()
#define SDL_HINT_AUDIO_CATEGORY
A variable controlling the audio category on iOS and Mac OS X.
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
GLuint GLuint GLsizei GLenum type
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLuint const GLchar * name
AudioBootStrap COREAUDIO_bootstrap
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
@ SDL_THREAD_PRIORITY_HIGH
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)
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)
EGLImageKHR EGLint EGLint * handle
static SDL_AudioDeviceID device
void(* CloseDevice)(_THIS)
int OnlyHasDefaultCaptureDevice
void(* DetectDevices)(void)
int OnlyHasDefaultOutputDevice
void(* Deinitialize)(void)
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
int ProvidesOwnCallbackThread
AudioStreamBasicDescription strdesc