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); \
44 static const AudioObjectPropertyAddress devlist_address = {
45 kAudioHardwarePropertyDevices,
46 kAudioObjectPropertyScopeGlobal,
47 kAudioObjectPropertyElementMaster
50 typedef void (*addDevFn)(
const char *
name,
const int iscapture, AudioDeviceID devId,
void *
data);
52 typedef struct AudioDeviceList
56 struct AudioDeviceList *next;
59 static AudioDeviceList *output_devs =
NULL;
60 static AudioDeviceList *capture_devs =
NULL;
63 add_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;
82 addToDevList(
const char *
name,
const int iscapture, AudioDeviceID devId,
void *
data)
84 if (add_to_internal_dev_list(iscapture, devId)) {
90 build_device_list(
int iscapture, addDevFn addfn,
void *addfndata)
94 AudioDeviceID *devs =
NULL;
98 result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
99 &devlist_address, 0,
NULL, &size);
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
132 result = AudioObjectGetPropertyDataSize(dev, &addr, 0,
NULL, &size);
136 buflist = (AudioBufferList *)
SDL_malloc(size);
140 result = AudioObjectGetPropertyData(dev, &addr, 0,
NULL,
143 if (result == noErr) {
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);
198 free_audio_device_list(AudioDeviceList **list)
200 AudioDeviceList *item = *list;
202 AudioDeviceList *next = item->next;
210 COREAUDIO_DetectDevices(
void)
217 build_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);
233 reprocess_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;
264 device_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);
273 static int open_playback_devices = 0;
274 static int open_capture_devices = 0;
276 #if !MACOSX_COREAUDIO
278 static void interruption_begin(
_THIS)
280 if (
this !=
NULL && this->hidden->audioQueue !=
NULL) {
281 this->hidden->interrupted =
SDL_TRUE;
282 AudioQueuePause(this->hidden->audioQueue);
286 static 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_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
379 [center addObserver:listener
380 selector:@selector(applicationBecameActive:)
381 name:UIApplicationDidBecomeActiveNotification
384 [center addObserver:listener
385 selector:@selector(applicationBecameActive:)
386 name:UIApplicationWillEnterForegroundNotification
389 this->hidden->interruption_listener = CFBridgingRetain(listener);
391 if (this->hidden->interruption_listener !=
NULL) {
392 SDLInterruptionListener *listener = nil;
393 listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener);
394 @
synchronized (listener) {
395 listener.device =
NULL;
397 [center removeObserver:listener];
409 outputCallback(
void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
418 SDL_memset(inBuffer->mAudioData, this->spec.silence, inBuffer->mAudioDataBytesCapacity);
420 UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
423 while (remaining > 0) {
425 if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
428 (*this->callbackspec.callback)(this->callbackspec.userdata,
429 this->hidden->buffer, this->hidden->bufferSize);
431 this->hidden->bufferOffset = 0;
434 len = this->hidden->bufferSize - this->hidden->bufferOffset;
435 if (len > remaining) {
438 SDL_memcpy(ptr, (
char *)this->hidden->buffer +
439 this->hidden->bufferOffset, len);
442 this->hidden->bufferOffset += len;
446 AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0,
NULL);
448 inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
452 inputCallback(
void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
453 const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions,
454 const AudioStreamPacketDescription *inPacketDescs )
464 const Uint8 *ptr = (
const Uint8 *) inBuffer->mAudioData;
465 UInt32 remaining = inBuffer->mAudioDataByteSize;
466 while (remaining > 0) {
467 UInt32 len = this->hidden->bufferSize - this->hidden->bufferOffset;
468 if (len > remaining) {
472 SDL_memcpy((
char *)this->hidden->buffer + this->hidden->bufferOffset, ptr, len);
475 this->hidden->bufferOffset += len;
477 if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
479 (*this->callbackspec.callback)(this->callbackspec.userdata, this->hidden->buffer, this->hidden->bufferSize);
481 this->hidden->bufferOffset = 0;
486 AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0,
NULL);
491 static const AudioObjectPropertyAddress alive_address =
493 kAudioDevicePropertyDeviceIsAlive,
494 kAudioObjectPropertyScopeGlobal,
495 kAudioObjectPropertyElementMaster
499 device_unplugged(AudioObjectID devid, UInt32 num_addr,
const AudioObjectPropertyAddress *addrs,
void *data)
504 UInt32 size =
sizeof (isAlive);
511 error = AudioObjectGetPropertyData(this->hidden->deviceID, &alive_address,
512 0,
NULL, &size, &isAlive);
514 if (error == kAudioHardwareBadDeviceError) {
516 }
else if ((error == kAudioHardwareNoError) && (!isAlive)) {
529 COREAUDIO_CloseDevice(
_THIS)
531 const SDL_bool iscapture = this->iscapture;
537 AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged,
this);
540 #if !MACOSX_COREAUDIO
544 if (this->hidden->thread) {
549 if (this->hidden->audioQueue) {
550 AudioQueueDispose(this->hidden->audioQueue, 1);
553 if (this->hidden->ready_semaphore) {
558 SDL_free(this->hidden->audioBuffer);
559 SDL_free(this->hidden->thread_error);
564 open_capture_devices--;
566 open_playback_devices--;
574 AudioDeviceID devid = (AudioDeviceID) ((
size_t)
handle);
575 OSStatus result = noErr;
580 AudioObjectPropertyAddress addr = {
582 kAudioObjectPropertyScopeGlobal,
583 kAudioObjectPropertyElementMaster
586 if (handle ==
NULL) {
587 size =
sizeof (AudioDeviceID);
589 ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
590 kAudioHardwarePropertyDefaultOutputDevice);
591 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
592 0,
NULL, &size, &devid);
593 CHECK_RESULT(
"AudioHardwareGetProperty (default device)");
596 addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
597 addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
598 kAudioDevicePropertyScopeOutput;
600 size =
sizeof (
alive);
601 result = AudioObjectGetPropertyData(devid, &addr, 0,
NULL, &size, &alive);
603 (
"AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
606 SDL_SetError(
"CoreAudio: requested device exists, but isn't alive.");
610 addr.mSelector = kAudioDevicePropertyHogMode;
612 result = AudioObjectGetPropertyData(devid, &addr, 0,
NULL, &size, &pid);
615 if ((result == noErr) && (pid != -1)) {
616 SDL_SetError(
"CoreAudio: requested device is being hogged.");
620 this->hidden->deviceID = devid;
626 prepare_audioqueue(
_THIS)
628 const AudioStreamBasicDescription *strdesc = &this->hidden->strdesc;
629 const int iscapture = this->iscapture;
636 result = AudioQueueNewInput(strdesc, inputCallback,
this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
637 CHECK_RESULT(
"AudioQueueNewInput");
639 result = AudioQueueNewOutput(strdesc, outputCallback,
this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
640 CHECK_RESULT(
"AudioQueueNewOutput");
645 const AudioObjectPropertyAddress prop = {
646 kAudioDevicePropertyDeviceUID,
647 iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
648 kAudioObjectPropertyElementMaster
651 UInt32 devuidsize =
sizeof (devuid);
652 result = AudioObjectGetPropertyData(this->hidden->deviceID, &prop, 0,
NULL, &devuidsize, &devuid);
653 CHECK_RESULT(
"AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID)");
654 result = AudioQueueSetProperty(this->hidden->audioQueue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize);
655 CHECK_RESULT(
"AudioQueueSetProperty (kAudioQueueProperty_CurrentDevice)");
660 AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged,
this);
668 this->hidden->bufferSize = this->
spec.
size;
669 this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize;
671 this->hidden->buffer =
SDL_malloc(this->hidden->bufferSize);
672 if (this->hidden->buffer ==
NULL) {
678 double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0;
679 #if defined(__IPHONEOS__)
680 if (
floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) {
682 MINIMUM_AUDIO_BUFFER_TIME_MS = 40.0;
686 int numAudioBuffers = 2;
687 if (msecs < MINIMUM_AUDIO_BUFFER_TIME_MS) {
688 numAudioBuffers = ((int)
SDL_ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2);
691 this->hidden->audioBuffer =
SDL_calloc(1,
sizeof (AudioQueueBufferRef) * numAudioBuffers);
692 if (this->hidden->audioBuffer ==
NULL) {
698 printf(
"COREAUDIO: numAudioBuffers == %d\n", numAudioBuffers);
701 for (i = 0; i < numAudioBuffers; i++) {
702 result = AudioQueueAllocateBuffer(this->hidden->audioQueue, this->spec.size, &this->hidden->audioBuffer[i]);
703 CHECK_RESULT(
"AudioQueueAllocateBuffer");
704 SDL_memset(this->hidden->audioBuffer[i]->mAudioData, this->spec.silence, this->hidden->audioBuffer[i]->mAudioDataBytesCapacity);
705 this->hidden->audioBuffer[i]->mAudioDataByteSize = this->hidden->audioBuffer[i]->mAudioDataBytesCapacity;
706 result = AudioQueueEnqueueBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i], 0,
NULL);
707 CHECK_RESULT(
"AudioQueueEnqueueBuffer");
710 result = AudioQueueStart(this->hidden->audioQueue,
NULL);
711 CHECK_RESULT(
"AudioQueueStart");
718 audioqueue_thread(
void *arg)
721 const int rc = prepare_audioqueue(
this);
731 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
734 if (this->iscapture) {
735 AudioQueueStop(this->hidden->audioQueue, 1);
738 CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0);
739 AudioQueueStop(this->hidden->audioQueue, 0);
746 COREAUDIO_OpenDevice(
_THIS,
void *handle,
const char *devname,
int iscapture)
748 AudioStreamBasicDescription *strdesc;
750 int valid_datatype = 0;
755 if (this->hidden ==
NULL) {
760 strdesc = &this->hidden->strdesc;
763 open_capture_devices++;
765 open_playback_devices++;
768 #if !MACOSX_COREAUDIO
769 if (!update_audio_session(
this,
SDL_TRUE)) {
775 AVAudioSession* session = [AVAudioSession sharedInstance];
776 [session setPreferredSampleRate:this->spec.freq error:nil];
777 this->
spec.
freq = (int)session.sampleRate;
783 strdesc->mFormatID = kAudioFormatLinearPCM;
784 strdesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
786 strdesc->mSampleRate = this->
spec.
freq;
787 strdesc->mFramesPerPacket = 1;
789 while ((!valid_datatype) && (test_format)) {
792 switch (test_format) {
806 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
809 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
811 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
816 if (!valid_datatype) {
820 strdesc->mBytesPerFrame = strdesc->mBitsPerChannel * strdesc->mChannelsPerFrame / 8;
821 strdesc->mBytesPerPacket = strdesc->mBytesPerFrame * strdesc->mFramesPerPacket;
824 if (!prepare_device(
this, handle, iscapture)) {
832 if (!this->hidden->ready_semaphore) {
837 if (!this->hidden->thread) {
843 this->hidden->ready_semaphore =
NULL;
845 if ((this->hidden->thread !=
NULL) && (this->hidden->thread_error !=
NULL)) {
850 return (this->hidden->thread !=
NULL) ? 0 : -1;
854 COREAUDIO_Deinitialize(
void)
857 AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed,
NULL);
858 free_audio_device_list(&capture_devs);
859 free_audio_device_list(&output_devs);
873 AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed,
NULL);
886 "coreaudio",
"CoreAudio", COREAUDIO_Init, 0
void(* CloseDevice)(_THIS)
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
#define SDL_AUDIO_ISBIGENDIAN(x)
#define SDL_CreateSemaphore
void(* DetectDevices)(void)
int ProvidesOwnCallbackThread
#define SDL_AUDIO_ISSIGNED(x)
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Uint16 SDL_AudioFormat
Audio format flags.
AudioBootStrap COREAUDIO_bootstrap
int OnlyHasDefaultCaptureDevice
GLuint const GLchar * name
#define SDL_HINT_AUDIO_CATEGORY
A variable controlling the audio category on iOS and Mac OS X.
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)
#define SDL_AUDIO_ISFLOAT(x)
static SDL_AudioDeviceID device
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
void * SDL_calloc(size_t nmemb, size_t size)
int OnlyHasDefaultOutputDevice
EGLImageKHR EGLint EGLint * handle
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
#define SDL_AUDIO_BITSIZE(x)
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)
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
void(* Deinitialize)(void)
GLenum GLenum GLsizei const GLuint GLboolean enabled
#define SDL_assert(condition)
#define SDL_OutOfMemory()
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_DestroySemaphore
GLuint GLuint GLsizei GLenum type
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)