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
set set set set set set set set set set set set set set set set set set set set *set set set macro pixldst op &r &cond WK op &r &cond WK op &r &cond WK else op &m &cond &ia op &r &cond WK else op &m &cond &ia elseif elseif else error unsupported base if elseif elseif else error unsupported unaligned pixldst unaligned endm macro pixst base base else pixldst base endif endm macro PF ptr
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)