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 547 if (this->hidden->audioQueue) {
548 AudioQueueDispose(this->hidden->audioQueue, 1);
551 if (this->hidden->thread) {
556 if (this->hidden->ready_semaphore) {
561 SDL_free(this->hidden->audioBuffer);
562 SDL_free(this->hidden->thread_error);
567 open_capture_devices--;
569 open_playback_devices--;
577 AudioDeviceID devid = (AudioDeviceID) ((
size_t)
handle);
578 OSStatus result = noErr;
583 AudioObjectPropertyAddress addr = {
585 kAudioObjectPropertyScopeGlobal,
586 kAudioObjectPropertyElementMaster
589 if (handle ==
NULL) {
590 size =
sizeof (AudioDeviceID);
592 ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
593 kAudioHardwarePropertyDefaultOutputDevice);
594 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
595 0,
NULL, &size, &devid);
596 CHECK_RESULT(
"AudioHardwareGetProperty (default device)");
599 addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
600 addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
601 kAudioDevicePropertyScopeOutput;
603 size =
sizeof (
alive);
604 result = AudioObjectGetPropertyData(devid, &addr, 0,
NULL, &size, &alive);
606 (
"AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
609 SDL_SetError(
"CoreAudio: requested device exists, but isn't alive.");
613 addr.mSelector = kAudioDevicePropertyHogMode;
615 result = AudioObjectGetPropertyData(devid, &addr, 0,
NULL, &size, &pid);
618 if ((result == noErr) && (pid != -1)) {
619 SDL_SetError(
"CoreAudio: requested device is being hogged.");
623 this->hidden->deviceID = devid;
629 prepare_audioqueue(
_THIS)
631 const AudioStreamBasicDescription *strdesc = &this->hidden->strdesc;
632 const int iscapture = this->iscapture;
639 result = AudioQueueNewInput(strdesc, inputCallback,
this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
640 CHECK_RESULT(
"AudioQueueNewInput");
642 result = AudioQueueNewOutput(strdesc, outputCallback,
this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
643 CHECK_RESULT(
"AudioQueueNewOutput");
648 const AudioObjectPropertyAddress prop = {
649 kAudioDevicePropertyDeviceUID,
650 iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
651 kAudioObjectPropertyElementMaster
654 UInt32 devuidsize =
sizeof (devuid);
655 result = AudioObjectGetPropertyData(this->hidden->deviceID, &prop, 0,
NULL, &devuidsize, &devuid);
656 CHECK_RESULT(
"AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID)");
657 result = AudioQueueSetProperty(this->hidden->audioQueue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize);
658 CHECK_RESULT(
"AudioQueueSetProperty (kAudioQueueProperty_CurrentDevice)");
663 AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged,
this);
671 this->hidden->bufferSize = this->
spec.
size;
672 this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize;
674 this->hidden->buffer =
SDL_malloc(this->hidden->bufferSize);
675 if (this->hidden->buffer ==
NULL) {
681 double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0;
682 #if defined(__IPHONEOS__) 683 if (
floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) {
685 MINIMUM_AUDIO_BUFFER_TIME_MS = 40.0;
689 int numAudioBuffers = 2;
690 if (msecs < MINIMUM_AUDIO_BUFFER_TIME_MS) {
691 numAudioBuffers = ((int)
SDL_ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2);
694 this->hidden->audioBuffer =
SDL_calloc(1,
sizeof (AudioQueueBufferRef) * numAudioBuffers);
695 if (this->hidden->audioBuffer ==
NULL) {
701 printf(
"COREAUDIO: numAudioBuffers == %d\n", numAudioBuffers);
704 for (i = 0; i < numAudioBuffers; i++) {
705 result = AudioQueueAllocateBuffer(this->hidden->audioQueue, this->spec.size, &this->hidden->audioBuffer[i]);
706 CHECK_RESULT(
"AudioQueueAllocateBuffer");
707 SDL_memset(this->hidden->audioBuffer[i]->mAudioData, this->spec.silence, this->hidden->audioBuffer[i]->mAudioDataBytesCapacity);
708 this->hidden->audioBuffer[i]->mAudioDataByteSize = this->hidden->audioBuffer[i]->mAudioDataBytesCapacity;
709 result = AudioQueueEnqueueBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i], 0,
NULL);
710 CHECK_RESULT(
"AudioQueueEnqueueBuffer");
713 result = AudioQueueStart(this->hidden->audioQueue,
NULL);
714 CHECK_RESULT(
"AudioQueueStart");
721 audioqueue_thread(
void *arg)
724 const int rc = prepare_audioqueue(
this);
734 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
737 if (!this->iscapture) {
739 CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 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
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
void(* DetectDevices)(void)
#define SDL_AUDIO_ISBIGENDIAN(x)
#define SDL_CreateSemaphore
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.
#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)
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(* Deinitialize)(void)
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
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)
#define SDL_assert(condition)
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
#define SDL_OutOfMemory()
void(* CloseDevice)(_THIS)
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
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)