21 #include "../../SDL_internal.h"
23 #if SDL_AUDIO_DRIVER_COREAUDIO
30 #include "../SDL_audio_c.h"
31 #include "../SDL_sysaudio.h"
34 #include "../../thread/SDL_systhread.h"
36 #define DEBUG_COREAUDIO 0
38 #define CHECK_RESULT(msg) \
39 if (result != noErr) { \
40 SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
45 static const AudioObjectPropertyAddress devlist_address = {
46 kAudioHardwarePropertyDevices,
47 kAudioObjectPropertyScopeGlobal,
48 kAudioObjectPropertyElementMaster
51 typedef void (*addDevFn)(
const char *
name,
const int iscapture, AudioDeviceID devId,
void *
data);
53 typedef struct AudioDeviceList
57 struct AudioDeviceList *next;
60 static AudioDeviceList *output_devs =
NULL;
61 static AudioDeviceList *capture_devs =
NULL;
64 add_to_internal_dev_list(
const int iscapture, AudioDeviceID devId)
66 AudioDeviceList *item = (AudioDeviceList *)
SDL_malloc(
sizeof (AudioDeviceList));
72 item->next = iscapture ? capture_devs : output_devs;
83 addToDevList(
const char *
name,
const int iscapture, AudioDeviceID devId,
void *
data)
85 if (add_to_internal_dev_list(iscapture, devId)) {
91 build_device_list(
int iscapture, addDevFn addfn,
void *addfndata)
95 AudioDeviceID *devs =
NULL;
99 result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
101 if (
result != kAudioHardwareNoError)
104 devs = (AudioDeviceID *) alloca(
size);
108 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
109 &devlist_address, 0,
NULL, &
size, devs);
110 if (
result != kAudioHardwareNoError)
113 max =
size /
sizeof (AudioDeviceID);
114 for (
i = 0;
i < max;
i++) {
115 CFStringRef cfstr =
NULL;
117 AudioDeviceID dev = devs[i];
118 AudioBufferList *buflist =
NULL;
121 const AudioObjectPropertyAddress
addr = {
122 kAudioDevicePropertyStreamConfiguration,
123 iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
124 kAudioObjectPropertyElementMaster
127 const AudioObjectPropertyAddress nameaddr = {
128 kAudioObjectPropertyName,
129 iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
130 kAudioObjectPropertyElementMaster
146 for (
j = 0;
j < buflist->mNumberBuffers;
j++) {
147 if (buflist->mBuffers[
j].mNumberChannels > 0) {
160 size =
sizeof (CFStringRef);
161 result = AudioObjectGetPropertyData(dev, &nameaddr, 0,
NULL, &
size, &cfstr);
162 if (
result != kAudioHardwareNoError)
165 len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
166 kCFStringEncodingUTF8);
169 usable = ((ptr !=
NULL) &&
171 (cfstr, ptr,
len + 1, kCFStringEncodingUTF8)));
178 while ((
len > 0) && (ptr[
len - 1] ==
' ')) {
188 printf(
"COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
189 ((iscapture) ?
"capture" :
"output"),
190 (
int)
i, ptr, (
int) dev);
192 addfn(ptr, iscapture, dev, addfndata);
199 free_audio_device_list(AudioDeviceList **list)
201 AudioDeviceList *item = *list;
203 AudioDeviceList *next = item->next;
211 COREAUDIO_DetectDevices(
void)
218 build_device_change_list(
const char *
name,
const int iscapture, AudioDeviceID devId,
void *
data)
220 AudioDeviceList **list = (AudioDeviceList **)
data;
221 AudioDeviceList *item;
222 for (item = *list; item !=
NULL; item = item->next) {
223 if (item->devid == devId) {
229 add_to_internal_dev_list(iscapture, devId);
234 reprocess_device_list(
const int iscapture, AudioDeviceList **list)
236 AudioDeviceList *item;
237 AudioDeviceList *prev =
NULL;
238 for (item = *list; item !=
NULL; item = item->next) {
242 build_device_list(iscapture, build_device_change_list, list);
246 while (item !=
NULL) {
247 AudioDeviceList *next = item->next;
253 prev->next = item->next;
265 device_list_changed(AudioObjectID systemObj, UInt32 num_addr,
const AudioObjectPropertyAddress *addrs,
void *
data)
267 reprocess_device_list(
SDL_TRUE, &capture_devs);
268 reprocess_device_list(
SDL_FALSE, &output_devs);
274 static int open_playback_devices = 0;
275 static int open_capture_devices = 0;
277 #if !MACOSX_COREAUDIO
279 static void interruption_begin(
_THIS)
281 if (
this !=
NULL && this->hidden->audioQueue !=
NULL) {
282 this->hidden->interrupted =
SDL_TRUE;
283 AudioQueuePause(this->hidden->audioQueue);
287 static void interruption_end(
_THIS)
289 if (
this !=
NULL && this->hidden !=
NULL && this->hidden->audioQueue !=
NULL
290 && this->hidden->interrupted
291 && AudioQueueStart(this->hidden->audioQueue,
NULL) == AVAudioSessionErrorCodeNone) {
296 @interface SDLInterruptionListener : NSObject
302 @implementation SDLInterruptionListener
304 - (
void)audioSessionInterruption:(NSNotification *)note
306 @
synchronized (
self) {
307 NSNumber *
type = note.userInfo[AVAudioSessionInterruptionTypeKey];
308 if (
type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
309 interruption_begin(
self.
device);
311 interruption_end(
self.
device);
316 - (
void)applicationBecameActive:(NSNotification *)note
318 @
synchronized (
self) {
319 interruption_end(
self.
device);
328 AVAudioSession *session = [AVAudioSession sharedInstance];
329 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
331 NSString *category = AVAudioSessionCategoryAmbient;
334 if (open_playback_devices && open_capture_devices) {
335 category = AVAudioSessionCategoryPlayAndRecord;
336 }
else if (open_capture_devices) {
337 category = AVAudioSessionCategoryRecord;
342 category = AVAudioSessionCategoryAmbient;
343 }
else if (
SDL_strcasecmp(hint,
"AVAudioSessionCategorySoloAmbient") == 0) {
344 category = AVAudioSessionCategorySoloAmbient;
345 }
else if (
SDL_strcasecmp(hint,
"AVAudioSessionCategoryPlayback") == 0 ||
347 category = AVAudioSessionCategoryPlayback;
352 if (![session setCategory:category error:&err]) {
353 NSString *desc = err.description;
354 SDL_SetError(
"Could not set Audio Session category: %s", desc.UTF8String);
358 if (open && (open_playback_devices + open_capture_devices) == 1) {
359 if (![session setActive:YES error:&err]) {
360 NSString *desc = err.description;
361 SDL_SetError(
"Could not activate Audio Session: %s", desc.UTF8String);
364 }
else if (!open_playback_devices && !open_capture_devices) {
365 [session setActive:NO error:nil];
369 SDLInterruptionListener *listener = [SDLInterruptionListener new];
370 listener.device =
this;
372 [center addObserver:listener
373 selector:@selector(audioSessionInterruption:)
374 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;
410 outputCallback(
void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
413 SDL_assert(inBuffer->mAudioDataBytesCapacity == this->hidden->bufferSize);
414 SDL_memcpy(inBuffer->mAudioData, this->hidden->buffer, this->hidden->bufferSize);
415 SDL_memset(this->hidden->buffer,
'\0', this->hidden->bufferSize);
416 inBuffer->mAudioDataByteSize = this->hidden->bufferSize;
417 AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0,
NULL);
422 COREAUDIO_GetDeviceBuf(
_THIS)
424 return this->hidden->buffer;
428 COREAUDIO_WaitDevice(
_THIS)
431 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
437 inputCallback(
void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
438 const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions,
439 const AudioStreamPacketDescription *inPacketDescs )
443 SDL_AudioStream *
stream = this->hidden->capturestream;
447 AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0,
NULL);
453 COREAUDIO_CaptureFromDevice(
_THIS,
void *
buffer,
int buflen)
455 SDL_AudioStream *
stream = this->hidden->capturestream;
459 const int cpy =
SDL_min(buflen, avail);
466 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
475 COREAUDIO_FlushCapture(
_THIS)
477 while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 1) == kCFRunLoopRunHandledSource) {
486 static const AudioObjectPropertyAddress alive_address =
488 kAudioDevicePropertyDeviceIsAlive,
489 kAudioObjectPropertyScopeGlobal,
490 kAudioObjectPropertyElementMaster
494 device_unplugged(AudioObjectID devid, UInt32 num_addr,
const AudioObjectPropertyAddress *addrs,
void *
data)
499 UInt32
size =
sizeof (isAlive);
506 error = AudioObjectGetPropertyData(this->hidden->deviceID, &alive_address,
509 if (error == kAudioHardwareBadDeviceError) {
511 }
else if ((error == kAudioHardwareNoError) && (!isAlive)) {
524 COREAUDIO_CloseDevice(
_THIS)
526 const SDL_bool iscapture = this->iscapture;
532 AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged,
this);
535 #if !MACOSX_COREAUDIO
539 if (this->hidden->audioQueue) {
540 AudioQueueDispose(this->hidden->audioQueue, 1);
543 if (this->hidden->capturestream) {
548 SDL_free(this->hidden->audioBuffer);
553 open_capture_devices--;
555 open_playback_devices--;
563 AudioDeviceID devid = (AudioDeviceID) ((
size_t)
handle);
569 AudioObjectPropertyAddress
addr = {
571 kAudioObjectPropertyScopeGlobal,
572 kAudioObjectPropertyElementMaster
576 size =
sizeof (AudioDeviceID);
578 ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
579 kAudioHardwarePropertyDefaultOutputDevice);
580 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &
addr,
582 CHECK_RESULT(
"AudioHardwareGetProperty (default device)");
585 addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
586 addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
587 kAudioDevicePropertyScopeOutput;
592 (
"AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
595 SDL_SetError(
"CoreAudio: requested device exists, but isn't alive.");
599 addr.mSelector = kAudioDevicePropertyHogMode;
604 if ((
result == noErr) && (pid != -1)) {
605 SDL_SetError(
"CoreAudio: requested device is being hogged.");
609 this->hidden->deviceID = devid;
617 prepare_audioqueue(
_THIS)
619 const AudioStreamBasicDescription *strdesc = &this->hidden->strdesc;
620 const int iscapture = this->iscapture;
627 result = AudioQueueNewInput(strdesc, inputCallback,
this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
628 CHECK_RESULT(
"AudioQueueNewInput");
630 result = AudioQueueNewOutput(strdesc, outputCallback,
this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
631 CHECK_RESULT(
"AudioQueueNewOutput");
636 const AudioObjectPropertyAddress prop = {
637 kAudioDevicePropertyDeviceUID,
638 iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
639 kAudioObjectPropertyElementMaster
642 UInt32 devuidsize =
sizeof (devuid);
643 result = AudioObjectGetPropertyData(this->hidden->deviceID, &prop, 0,
NULL, &devuidsize, &devuid);
644 CHECK_RESULT(
"AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID)");
645 result = AudioQueueSetProperty(this->hidden->audioQueue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize);
646 CHECK_RESULT(
"AudioQueueSetProperty (kAudioQueueProperty_CurrentDevice)");
651 AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged,
this);
656 double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0;
657 #if defined(__IPHONEOS__)
658 if (
floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) {
660 MINIMUM_AUDIO_BUFFER_TIME_MS = 40.0;
664 int numAudioBuffers = 2;
665 if (msecs < MINIMUM_AUDIO_BUFFER_TIME_MS) {
666 numAudioBuffers = ((int)
SDL_ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2);
669 this->hidden->numAudioBuffers = numAudioBuffers;
670 this->hidden->audioBuffer =
SDL_calloc(1,
sizeof (AudioQueueBufferRef) * numAudioBuffers);
671 if (this->hidden->audioBuffer ==
NULL) {
677 printf(
"COREAUDIO: numAudioBuffers == %d\n", numAudioBuffers);
680 for (
i = 0;
i < numAudioBuffers;
i++) {
681 result = AudioQueueAllocateBuffer(this->hidden->audioQueue, this->spec.size, &this->hidden->audioBuffer[
i]);
682 CHECK_RESULT(
"AudioQueueAllocateBuffer");
683 SDL_memset(this->hidden->audioBuffer[
i]->mAudioData, this->spec.silence, this->hidden->audioBuffer[
i]->mAudioDataBytesCapacity);
684 this->hidden->audioBuffer[i]->mAudioDataByteSize = this->hidden->audioBuffer[i]->mAudioDataBytesCapacity;
685 result = AudioQueueEnqueueBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[
i], 0,
NULL);
686 CHECK_RESULT(
"AudioQueueEnqueueBuffer");
689 result = AudioQueueStart(this->hidden->audioQueue,
NULL);
690 CHECK_RESULT(
"AudioQueueStart");
697 COREAUDIO_ThreadInit(
_THIS)
699 const int rc = prepare_audioqueue(
this);
707 COREAUDIO_PrepareToClose(
_THIS)
711 CFRunLoopRunInMode(kCFRunLoopDefaultMode, (((this->
spec.
samples * 1000) / this->spec.freq) * 2) / 1000.0f, 0);
712 AudioQueueStop(this->hidden->audioQueue, 1);
716 COREAUDIO_OpenDevice(
_THIS,
void *
handle,
const char *devname,
int iscapture)
718 AudioStreamBasicDescription *strdesc;
720 int valid_datatype = 0;
725 if (this->hidden ==
NULL) {
730 strdesc = &this->hidden->strdesc;
733 open_capture_devices++;
735 open_playback_devices++;
738 #if !MACOSX_COREAUDIO
739 if (!update_audio_session(
this,
SDL_TRUE)) {
745 AVAudioSession* session = [AVAudioSession sharedInstance];
746 [session setPreferredSampleRate:this->spec.freq error:nil];
747 this->
spec.
freq = (int)session.sampleRate;
753 strdesc->mFormatID = kAudioFormatLinearPCM;
754 strdesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
756 strdesc->mSampleRate = this->
spec.
freq;
757 strdesc->mFramesPerPacket = 1;
759 while ((!valid_datatype) && (test_format)) {
762 switch (test_format) {
776 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
779 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
781 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
786 if (!valid_datatype) {
803 this->hidden->capturestream =
SDL_NewAudioStream(this->
spec.
format, this->spec.channels, this->spec.freq, this->spec.format, this->spec.channels, this->spec.freq);
804 if (!this->hidden->capturestream) {
808 this->hidden->bufferSize = this->
spec.
size;
809 this->hidden->buffer =
SDL_malloc(this->hidden->bufferSize);
810 if (this->hidden->buffer ==
NULL) {
819 COREAUDIO_Deinitialize(
void)
822 AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed,
NULL);
823 free_audio_device_list(&capture_devs);
824 free_audio_device_list(&output_devs);
844 AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed,
NULL);
856 "coreaudio",
"CoreAudio", COREAUDIO_Init, 0