21 #include "../../SDL_internal.h" 23 #if SDL_AUDIO_DRIVER_COREAUDIO 28 #include "../SDL_audio_c.h" 29 #include "../SDL_sysaudio.h" 32 #include "../../thread/SDL_systhread.h" 34 #define DEBUG_COREAUDIO 0 36 #define CHECK_RESULT(msg) \ 37 if (result != noErr) { \ 38 SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \ 43 static const AudioObjectPropertyAddress devlist_address = {
44 kAudioHardwarePropertyDevices,
45 kAudioObjectPropertyScopeGlobal,
46 kAudioObjectPropertyElementMaster
49 typedef void (*addDevFn)(
const char *
name,
const int iscapture, AudioDeviceID devId,
void *
data);
51 typedef struct AudioDeviceList
55 struct AudioDeviceList *next;
58 static AudioDeviceList *output_devs =
NULL;
59 static AudioDeviceList *capture_devs =
NULL;
62 add_to_internal_dev_list(
const int iscapture, AudioDeviceID devId)
64 AudioDeviceList *item = (AudioDeviceList *)
SDL_malloc(
sizeof (AudioDeviceList));
70 item->next = iscapture ? capture_devs : output_devs;
81 addToDevList(
const char *
name,
const int iscapture, AudioDeviceID devId,
void *
data)
83 if (add_to_internal_dev_list(iscapture, devId)) {
89 build_device_list(
int iscapture, addDevFn addfn,
void *addfndata)
93 AudioDeviceID *devs =
NULL;
97 result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
98 &devlist_address, 0,
NULL, &size);
99 if (result != kAudioHardwareNoError)
102 devs = (AudioDeviceID *) alloca(size);
106 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
107 &devlist_address, 0,
NULL, &size, devs);
108 if (result != kAudioHardwareNoError)
111 max = size /
sizeof (AudioDeviceID);
112 for (i = 0; i < max; i++) {
113 CFStringRef cfstr =
NULL;
115 AudioDeviceID dev = devs[i];
116 AudioBufferList *buflist =
NULL;
119 const AudioObjectPropertyAddress
addr = {
120 kAudioDevicePropertyStreamConfiguration,
121 iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
122 kAudioObjectPropertyElementMaster
125 const AudioObjectPropertyAddress nameaddr = {
126 kAudioObjectPropertyName,
127 iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
128 kAudioObjectPropertyElementMaster
131 result = AudioObjectGetPropertyDataSize(dev, &addr, 0,
NULL, &size);
135 buflist = (AudioBufferList *)
SDL_malloc(size);
139 result = AudioObjectGetPropertyData(dev, &addr, 0,
NULL,
142 if (result == noErr) {
144 for (j = 0; j < buflist->mNumberBuffers; j++) {
145 if (buflist->mBuffers[j].mNumberChannels > 0) {
158 size =
sizeof (CFStringRef);
159 result = AudioObjectGetPropertyData(dev, &nameaddr, 0,
NULL, &size, &cfstr);
160 if (result != kAudioHardwareNoError)
163 len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
164 kCFStringEncodingUTF8);
167 usable = ((ptr !=
NULL) &&
169 (cfstr, ptr, len + 1, kCFStringEncodingUTF8)));
176 while ((len > 0) && (ptr[len - 1] ==
' ')) {
186 printf(
"COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
187 ((iscapture) ?
"capture" :
"output"),
188 (
int) i, ptr, (
int) dev);
190 addfn(ptr, iscapture, dev, addfndata);
197 free_audio_device_list(AudioDeviceList **list)
199 AudioDeviceList *item = *list;
201 AudioDeviceList *next = item->next;
209 COREAUDIO_DetectDevices(
void)
216 build_device_change_list(
const char *name,
const int iscapture, AudioDeviceID devId,
void *data)
218 AudioDeviceList **list = (AudioDeviceList **) data;
219 AudioDeviceList *item;
220 for (item = *list; item !=
NULL; item = item->next) {
221 if (item->devid == devId) {
227 add_to_internal_dev_list(iscapture, devId);
232 reprocess_device_list(
const int iscapture, AudioDeviceList **list)
234 AudioDeviceList *item;
235 AudioDeviceList *prev =
NULL;
236 for (item = *list; item !=
NULL; item = item->next) {
240 build_device_list(iscapture, build_device_change_list, list);
244 while (item !=
NULL) {
245 AudioDeviceList *next = item->next;
251 prev->next = item->next;
263 device_list_changed(AudioObjectID systemObj, UInt32 num_addr,
const AudioObjectPropertyAddress *addrs,
void *data)
265 reprocess_device_list(
SDL_TRUE, &capture_devs);
266 reprocess_device_list(
SDL_FALSE, &output_devs);
272 static int open_playback_devices = 0;
273 static int open_capture_devices = 0;
275 #if !MACOSX_COREAUDIO 277 static void interruption_begin(
_THIS)
279 if (
this !=
NULL && this->hidden->audioQueue !=
NULL) {
280 this->hidden->interrupted =
SDL_TRUE;
281 AudioQueuePause(this->hidden->audioQueue);
285 static void interruption_end(
_THIS)
287 if (
this !=
NULL && this->hidden !=
NULL && this->hidden->audioQueue !=
NULL 288 && this->hidden->interrupted) {
290 AudioQueueStart(this->hidden->audioQueue,
NULL);
294 @interface SDLInterruptionListener : NSObject
300 @implementation SDLInterruptionListener
302 - (
void)audioSessionInterruption:(NSNotification *)note
304 @
synchronized (
self) {
305 NSNumber *
type = note.userInfo[AVAudioSessionInterruptionTypeKey];
306 if (type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
307 interruption_begin(
self.device);
309 interruption_end(
self.device);
314 - (
void)applicationBecameActive:(NSNotification *)note
316 @
synchronized (
self) {
317 interruption_end(
self.device);
326 AVAudioSession *session = [AVAudioSession sharedInstance];
327 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
331 if (open_playback_devices && open_capture_devices) {
332 category = AVAudioSessionCategoryPlayAndRecord;
333 }
else if (open_capture_devices) {
334 category = AVAudioSessionCategoryRecord;
339 category = AVAudioSessionCategoryAmbient;
342 if (![session setCategory:category error:&err]) {
343 NSString *desc = err.description;
344 SDL_SetError(
"Could not set Audio Session category: %s", desc.UTF8String);
348 if (open_playback_devices + open_capture_devices == 1) {
349 if (![session setActive:YES error:&err]) {
350 NSString *desc = err.description;
351 SDL_SetError(
"Could not activate Audio Session: %s", desc.UTF8String);
354 }
else if (!open_playback_devices && !open_capture_devices) {
355 [session setActive:NO error:nil];
359 SDLInterruptionListener *listener = [SDLInterruptionListener new];
360 listener.device =
this;
362 [center addObserver:listener
363 selector:@selector(audioSessionInterruption:)
364 name:AVAudioSessionInterruptionNotification
370 [center addObserver:listener
371 selector:@selector(applicationBecameActive:)
372 name:UIApplicationDidBecomeActiveNotification
375 [center addObserver:listener
376 selector:@selector(applicationBecameActive:)
377 name:UIApplicationWillEnterForegroundNotification
380 this->hidden->interruption_listener = CFBridgingRetain(listener);
382 if (this->hidden->interruption_listener !=
NULL) {
383 SDLInterruptionListener *listener = nil;
384 listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener);
385 @
synchronized (listener) {
386 listener.device =
NULL;
388 [center removeObserver:listener];
400 outputCallback(
void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
405 SDL_memset(inBuffer->mAudioData, this->spec.silence, inBuffer->mAudioDataBytesCapacity);
407 UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
410 while (remaining > 0) {
412 if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
416 this->hidden->buffer, this->hidden->bufferSize);
418 this->hidden->bufferOffset = 0;
421 len = this->hidden->bufferSize - this->hidden->bufferOffset;
422 if (len > remaining) {
425 SDL_memcpy(ptr, (
char *)this->hidden->buffer +
426 this->hidden->bufferOffset, len);
429 this->hidden->bufferOffset += len;
434 AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0,
NULL);
437 inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
441 inputCallback(
void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
442 const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions,
443 const AudioStreamPacketDescription *inPacketDescs )
447 const Uint8 *ptr = (
const Uint8 *) inBuffer->mAudioData;
448 UInt32 remaining = inBuffer->mAudioDataByteSize;
449 while (remaining > 0) {
450 UInt32 len = this->hidden->bufferSize - this->hidden->bufferOffset;
451 if (len > remaining) {
455 SDL_memcpy((
char *)this->hidden->buffer + this->hidden->bufferOffset, ptr, len);
458 this->hidden->bufferOffset += len;
460 if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
464 this->hidden->bufferOffset = 0;
470 AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0,
NULL);
476 static const AudioObjectPropertyAddress alive_address =
478 kAudioDevicePropertyDeviceIsAlive,
479 kAudioObjectPropertyScopeGlobal,
480 kAudioObjectPropertyElementMaster
484 device_unplugged(AudioObjectID devid, UInt32 num_addr,
const AudioObjectPropertyAddress *addrs,
void *data)
489 UInt32 size =
sizeof (isAlive);
496 error = AudioObjectGetPropertyData(this->hidden->deviceID, &alive_address,
497 0,
NULL, &size, &isAlive);
499 if (error == kAudioHardwareBadDeviceError) {
501 }
else if ((error == kAudioHardwareNoError) && (!isAlive)) {
514 COREAUDIO_CloseDevice(
_THIS)
516 const SDL_bool iscapture = this->iscapture;
523 AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged,
this);
526 #if !MACOSX_COREAUDIO 530 if (this->hidden->thread) {
535 if (this->hidden->audioQueue) {
536 for (i = 0; i <
SDL_arraysize(this->hidden->audioBuffer); i++) {
537 if (this->hidden->audioBuffer[i]) {
538 AudioQueueFreeBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i]);
541 AudioQueueDispose(this->hidden->audioQueue, 1);
544 if (this->hidden->ready_semaphore) {
548 SDL_free(this->hidden->thread_error);
553 open_capture_devices--;
555 open_playback_devices--;
561 prepare_device(
_THIS,
void *handle,
int iscapture)
563 AudioDeviceID devid = (AudioDeviceID) ((
size_t) handle);
564 OSStatus result = noErr;
569 AudioObjectPropertyAddress addr = {
571 kAudioObjectPropertyScopeGlobal,
572 kAudioObjectPropertyElementMaster
575 if (handle ==
NULL) {
576 size =
sizeof (AudioDeviceID);
578 ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
579 kAudioHardwarePropertyDefaultOutputDevice);
580 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
581 0,
NULL, &size, &devid);
582 CHECK_RESULT(
"AudioHardwareGetProperty (default device)");
585 addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
586 addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
587 kAudioDevicePropertyScopeOutput;
589 size =
sizeof (
alive);
590 result = AudioObjectGetPropertyData(devid, &addr, 0,
NULL, &size, &alive);
592 (
"AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
595 SDL_SetError(
"CoreAudio: requested device exists, but isn't alive.");
599 addr.mSelector = kAudioDevicePropertyHogMode;
601 result = AudioObjectGetPropertyData(devid, &addr, 0,
NULL, &size, &pid);
604 if ((result == noErr) && (pid != -1)) {
605 SDL_SetError(
"CoreAudio: requested device is being hogged.");
609 this->hidden->deviceID = devid;
615 prepare_audioqueue(
_THIS)
617 const AudioStreamBasicDescription *strdesc = &this->hidden->strdesc;
618 const int iscapture = this->iscapture;
625 result = AudioQueueNewInput(strdesc, inputCallback,
this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
626 CHECK_RESULT(
"AudioQueueNewInput");
628 result = AudioQueueNewOutput(strdesc, outputCallback,
this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
629 CHECK_RESULT(
"AudioQueueNewOutput");
634 const AudioObjectPropertyAddress prop = {
635 kAudioDevicePropertyDeviceUID,
636 iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
637 kAudioObjectPropertyElementMaster
640 UInt32 devuidsize =
sizeof (devuid);
641 result = AudioObjectGetPropertyData(this->hidden->deviceID, &prop, 0,
NULL, &devuidsize, &devuid);
642 CHECK_RESULT(
"AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID)");
643 result = AudioQueueSetProperty(this->hidden->audioQueue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize);
644 CHECK_RESULT(
"AudioQueueSetProperty (kAudioQueueProperty_CurrentDevice)");
649 AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged,
this);
657 this->hidden->bufferSize = this->
spec.
size;
658 this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize;
660 this->hidden->buffer =
SDL_malloc(this->hidden->bufferSize);
661 if (this->hidden->buffer ==
NULL) {
666 for (i = 0; i <
SDL_arraysize(this->hidden->audioBuffer); i++) {
667 result = AudioQueueAllocateBuffer(this->hidden->audioQueue, this->spec.size, &this->hidden->audioBuffer[i]);
668 CHECK_RESULT(
"AudioQueueAllocateBuffer");
669 SDL_memset(this->hidden->audioBuffer[i]->mAudioData, this->spec.silence, this->hidden->audioBuffer[i]->mAudioDataBytesCapacity);
670 this->hidden->audioBuffer[i]->mAudioDataByteSize = this->hidden->audioBuffer[i]->mAudioDataBytesCapacity;
671 result = AudioQueueEnqueueBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i], 0,
NULL);
672 CHECK_RESULT(
"AudioQueueEnqueueBuffer");
675 result = AudioQueueStart(this->hidden->audioQueue,
NULL);
676 CHECK_RESULT(
"AudioQueueStart");
683 audioqueue_thread(
void *arg)
686 const int rc = prepare_audioqueue(
this);
696 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
699 if (this->iscapture) {
700 AudioQueueStop(this->hidden->audioQueue, 1);
702 AudioQueueStop(this->hidden->audioQueue, 0);
704 CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0);
711 COREAUDIO_OpenDevice(
_THIS,
void *handle,
const char *devname,
int iscapture)
713 AudioStreamBasicDescription *strdesc;
715 int valid_datatype = 0;
720 if (this->hidden ==
NULL) {
725 strdesc = &this->hidden->strdesc;
728 open_capture_devices++;
730 open_playback_devices++;
733 #if !MACOSX_COREAUDIO 734 if (!update_audio_session(
this,
SDL_TRUE)) {
741 strdesc->mFormatID = kAudioFormatLinearPCM;
742 strdesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
744 strdesc->mSampleRate = this->
spec.
freq;
745 strdesc->mFramesPerPacket = 1;
747 while ((!valid_datatype) && (test_format)) {
750 switch (test_format) {
764 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
767 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
769 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
774 if (!valid_datatype) {
778 strdesc->mBytesPerFrame = strdesc->mBitsPerChannel * strdesc->mChannelsPerFrame / 8;
779 strdesc->mBytesPerPacket = strdesc->mBytesPerFrame * strdesc->mFramesPerPacket;
782 if (!prepare_device(
this, handle, iscapture)) {
790 if (!this->hidden->ready_semaphore) {
795 if (!this->hidden->thread) {
801 this->hidden->ready_semaphore =
NULL;
803 if ((this->hidden->thread !=
NULL) && (this->hidden->thread_error !=
NULL)) {
808 return (this->hidden->thread !=
NULL) ? 0 : -1;
812 COREAUDIO_Deinitialize(
void)
815 AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed,
NULL);
816 free_audio_device_list(&capture_devs);
817 free_audio_device_list(&output_devs);
831 AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed,
NULL);
844 "coreaudio",
"CoreAudio", COREAUDIO_Init, 0
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
void(* DetectDevices)(void)
#define SDL_AUDIO_ISBIGENDIAN(x)
#define SDL_CreateSemaphore
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
int ProvidesOwnCallbackThread
#define SDL_AUDIO_ISSIGNED(x)
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Uint16 SDL_AudioFormat
Audio format flags.
GLuint const GLchar * name
int OnlyHasDefaultCaptureDevice
#define SDL_AUDIO_ISFLOAT(x)
GLuint GLuint GLsizei GLenum type
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
int OnlyHasDefaultOutputDevice
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
uint8_t Uint8
An unsigned 8-bit integer type.
#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)
AudioBootStrap COREAUDIO_bootstrap
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
SDL_AudioCallback callback
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()
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
void(* CloseDevice)(_THIS)
#define SDL_DestroySemaphore
#define SDL_arraysize(array)
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)