|
@@ -185,7 +185,7 @@ build_device_list(int iscapture, addDevFn addfn, void *addfndata) |
|
|
#if DEBUG_COREAUDIO |
|
|
printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n", |
|
|
((iscapture) ? "capture" : "output"), |
|
|
(int) *devCount, ptr, (int) dev); |
|
|
(int) i, ptr, (int) dev); |
|
|
#endif |
|
|
addfn(ptr, iscapture, dev, addfndata); |
|
|
} |
|
@@ -324,18 +324,52 @@ outputCallback(void *inRefCon, |
|
|
} |
|
|
} |
|
|
|
|
|
return 0; |
|
|
return noErr; |
|
|
} |
|
|
|
|
|
static OSStatus |
|
|
inputCallback(void *inRefCon, |
|
|
AudioUnitRenderActionFlags * ioActionFlags, |
|
|
const AudioTimeStamp * inTimeStamp, |
|
|
AudioUnitRenderActionFlags *ioActionFlags, |
|
|
const AudioTimeStamp *inTimeStamp, |
|
|
UInt32 inBusNumber, UInt32 inNumberFrames, |
|
|
AudioBufferList * ioData) |
|
|
AudioBufferList *ioData) |
|
|
{ |
|
|
/* err = AudioUnitRender(afr->fAudioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, afr->fAudioBuffer); */ |
|
|
/* !!! FIXME: write me! */ |
|
|
SDL_AudioDevice *this = (SDL_AudioDevice *) inRefCon; |
|
|
if (!this->enabled || this->paused) { |
|
|
return noErr; /* just drop this if we're not accepting input. */ |
|
|
} |
|
|
|
|
|
const OSStatus err = AudioUnitRender(this->hidden->audioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &this->hidden->captureBufferList); |
|
|
SDL_assert(this->hidden->captureBufferList.mNumberBuffers == 1); |
|
|
|
|
|
if (err == noErr) { |
|
|
const AudioBuffer *abuf = &this->hidden->captureBufferList.mBuffers[0]; |
|
|
UInt32 remaining = abuf->mDataByteSize; |
|
|
const Uint8 *ptr = (const Uint8 *) abuf->mData; |
|
|
|
|
|
/* No SDL conversion should be needed here, ever, since we accept |
|
|
any input format in OpenAudio, and leave the conversion to CoreAudio. |
|
|
*/ |
|
|
while (remaining > 0) { |
|
|
UInt32 len = this->hidden->bufferSize - this->hidden->bufferOffset; |
|
|
if (len > remaining) |
|
|
len = remaining; |
|
|
|
|
|
SDL_memcpy((char *)this->hidden->buffer + this->hidden->bufferOffset, ptr, len); |
|
|
ptr += len; |
|
|
remaining -= len; |
|
|
this->hidden->bufferOffset += len; |
|
|
|
|
|
if (this->hidden->bufferOffset >= this->hidden->bufferSize) { |
|
|
SDL_LockMutex(this->mixer_lock); |
|
|
(*this->spec.callback)(this->spec.userdata, |
|
|
this->hidden->buffer, this->hidden->bufferSize); |
|
|
SDL_UnlockMutex(this->mixer_lock); |
|
|
this->hidden->bufferOffset = 0; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return noErr; |
|
|
} |
|
|
|
|
@@ -394,20 +428,21 @@ COREAUDIO_CloseDevice(_THIS) |
|
|
const int iscapture = this->iscapture; |
|
|
const AudioUnitElement bus = |
|
|
((iscapture) ? input_bus : output_bus); |
|
|
const AudioUnitScope scope = |
|
|
((iscapture) ? kAudioUnitScope_Output : |
|
|
kAudioUnitScope_Input); |
|
|
|
|
|
/* stop processing the audio unit */ |
|
|
AudioOutputUnitStop(this->hidden->audioUnit); |
|
|
|
|
|
/* Remove the input callback */ |
|
|
SDL_memset(&callback, 0, sizeof(AURenderCallbackStruct)); |
|
|
SDL_zero(callback); |
|
|
AudioUnitSetProperty(this->hidden->audioUnit, |
|
|
kAudioUnitProperty_SetRenderCallback, |
|
|
scope, bus, &callback, sizeof(callback)); |
|
|
iscapture ? kAudioOutputUnitProperty_SetInputCallback : kAudioUnitProperty_SetRenderCallback, |
|
|
kAudioUnitScope_Global, bus, &callback, sizeof(callback)); |
|
|
AudioComponentInstanceDispose(this->hidden->audioUnit); |
|
|
this->hidden->audioUnitOpened = 0; |
|
|
|
|
|
#if MACOSX_COREAUDIO |
|
|
SDL_free(this->hidden->captureBufferList.mBuffers[0].mData); |
|
|
#endif |
|
|
} |
|
|
SDL_free(this->hidden->buffer); |
|
|
SDL_free(this->hidden); |
|
@@ -480,9 +515,6 @@ prepare_audiounit(_THIS, void *handle, int iscapture, |
|
|
AudioComponent comp = NULL; |
|
|
const AudioUnitElement output_bus = 0; |
|
|
const AudioUnitElement input_bus = 1; |
|
|
const AudioUnitElement bus = ((iscapture) ? input_bus : output_bus); |
|
|
const AudioUnitScope scope = ((iscapture) ? kAudioUnitScope_Output : |
|
|
kAudioUnitScope_Input); |
|
|
|
|
|
#if MACOSX_COREAUDIO |
|
|
if (!prepare_device(this, handle, iscapture)) { |
|
@@ -495,7 +527,7 @@ prepare_audiounit(_THIS, void *handle, int iscapture, |
|
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple; |
|
|
|
|
|
#if MACOSX_COREAUDIO |
|
|
desc.componentSubType = kAudioUnitSubType_DefaultOutput; |
|
|
desc.componentSubType = iscapture ? kAudioUnitSubType_HALOutput : kAudioUnitSubType_DefaultOutput; |
|
|
#else |
|
|
desc.componentSubType = kAudioUnitSubType_RemoteIO; |
|
|
#endif |
|
@@ -513,9 +545,28 @@ prepare_audiounit(_THIS, void *handle, int iscapture, |
|
|
this->hidden->audioUnitOpened = 1; |
|
|
|
|
|
#if MACOSX_COREAUDIO |
|
|
if (iscapture) { /* have to do EnableIO only for capture devices. */ |
|
|
UInt32 enable = 1; |
|
|
result = AudioUnitSetProperty(this->hidden->audioUnit, |
|
|
kAudioOutputUnitProperty_EnableIO, |
|
|
kAudioUnitScope_Input, input_bus, |
|
|
&enable, sizeof (enable)); |
|
|
CHECK_RESULT |
|
|
("AudioUnitSetProperty (kAudioOutputUnitProperty_EnableIO input bus)"); |
|
|
|
|
|
enable = 0; |
|
|
result = AudioUnitSetProperty(this->hidden->audioUnit, |
|
|
kAudioOutputUnitProperty_EnableIO, |
|
|
kAudioUnitScope_Output, output_bus, |
|
|
&enable, sizeof (enable)); |
|
|
CHECK_RESULT |
|
|
("AudioUnitSetProperty (kAudioOutputUnitProperty_EnableIO output bus)"); |
|
|
} |
|
|
|
|
|
/* this is always on the output_bus, even for capture devices. */ |
|
|
result = AudioUnitSetProperty(this->hidden->audioUnit, |
|
|
kAudioOutputUnitProperty_CurrentDevice, |
|
|
kAudioUnitScope_Global, 0, |
|
|
kAudioUnitScope_Global, output_bus, |
|
|
&this->hidden->deviceID, |
|
|
sizeof(AudioDeviceID)); |
|
|
CHECK_RESULT |
|
@@ -525,25 +576,63 @@ prepare_audiounit(_THIS, void *handle, int iscapture, |
|
|
/* Set the data format of the audio unit. */ |
|
|
result = AudioUnitSetProperty(this->hidden->audioUnit, |
|
|
kAudioUnitProperty_StreamFormat, |
|
|
scope, bus, strdesc, sizeof(*strdesc)); |
|
|
iscapture ? kAudioUnitScope_Output : kAudioUnitScope_Input, |
|
|
iscapture ? input_bus : output_bus, |
|
|
strdesc, sizeof (*strdesc)); |
|
|
CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)"); |
|
|
|
|
|
#if MACOSX_COREAUDIO |
|
|
if (iscapture) { /* only need to do this for capture devices. */ |
|
|
void *ptr; |
|
|
UInt32 framesize = 0; |
|
|
UInt32 propsize = sizeof (UInt32); |
|
|
|
|
|
result = AudioUnitGetProperty(this->hidden->audioUnit, |
|
|
kAudioDevicePropertyBufferFrameSize, |
|
|
kAudioUnitScope_Global, output_bus, |
|
|
&framesize, &propsize); |
|
|
CHECK_RESULT |
|
|
("AudioUnitGetProperty (kAudioDevicePropertyBufferFrameSize)"); |
|
|
|
|
|
framesize *= SDL_AUDIO_BITSIZE(this->spec.format) / 8; |
|
|
ptr = SDL_calloc(1, framesize); |
|
|
if (ptr == NULL) { |
|
|
COREAUDIO_CloseDevice(this); |
|
|
SDL_OutOfMemory(); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
this->hidden->captureBufferList.mNumberBuffers = 1; |
|
|
this->hidden->captureBufferList.mBuffers[0].mNumberChannels = this->spec.channels; |
|
|
this->hidden->captureBufferList.mBuffers[0].mDataByteSize = framesize; |
|
|
this->hidden->captureBufferList.mBuffers[0].mData = ptr; |
|
|
} |
|
|
#endif |
|
|
|
|
|
/* Set the audio callback */ |
|
|
SDL_memset(&callback, 0, sizeof(AURenderCallbackStruct)); |
|
|
SDL_zero(callback); |
|
|
callback.inputProc = ((iscapture) ? inputCallback : outputCallback); |
|
|
callback.inputProcRefCon = this; |
|
|
|
|
|
result = AudioUnitSetProperty(this->hidden->audioUnit, |
|
|
kAudioUnitProperty_SetRenderCallback, |
|
|
scope, bus, &callback, sizeof(callback)); |
|
|
iscapture ? kAudioOutputUnitProperty_SetInputCallback : kAudioUnitProperty_SetRenderCallback, |
|
|
kAudioUnitScope_Global, output_bus, &callback, sizeof(callback)); |
|
|
CHECK_RESULT |
|
|
("AudioUnitSetProperty (kAudioUnitProperty_SetRenderCallback)"); |
|
|
|
|
|
/* Calculate the final parameters for this audio specification */ |
|
|
SDL_CalculateAudioSpec(&this->spec); |
|
|
|
|
|
/* Allocate a sample buffer */ |
|
|
this->hidden->bufferOffset = this->hidden->bufferSize = this->spec.size; |
|
|
this->hidden->bufferSize = this->spec.size; |
|
|
this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize; |
|
|
|
|
|
this->hidden->buffer = SDL_malloc(this->hidden->bufferSize); |
|
|
if (this->hidden->buffer == NULL) { |
|
|
COREAUDIO_CloseDevice(this); |
|
|
SDL_OutOfMemory(); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
result = AudioUnitInitialize(this->hidden->audioUnit); |
|
|
CHECK_RESULT("AudioUnitInitialize"); |
|
@@ -552,6 +641,8 @@ prepare_audiounit(_THIS, void *handle, int iscapture, |
|
|
result = AudioOutputUnitStart(this->hidden->audioUnit); |
|
|
CHECK_RESULT("AudioOutputUnitStart"); |
|
|
|
|
|
/* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */ |
|
|
/* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */ |
|
|
#if MACOSX_COREAUDIO |
|
|
/* Fire a callback if the device stops being "alive" (disconnected, etc). */ |
|
|
AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this); |
|
@@ -575,10 +666,10 @@ COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) |
|
|
if (this->hidden == NULL) { |
|
|
return SDL_OutOfMemory(); |
|
|
} |
|
|
SDL_memset(this->hidden, 0, (sizeof *this->hidden)); |
|
|
SDL_zerop(this->hidden); |
|
|
|
|
|
/* Setup a AudioStreamBasicDescription with the requested format */ |
|
|
SDL_memset(&strdesc, '\0', sizeof(AudioStreamBasicDescription)); |
|
|
SDL_zero(strdesc); |
|
|
strdesc.mFormatID = kAudioFormatLinearPCM; |
|
|
strdesc.mFormatFlags = kLinearPCMFormatFlagIsPacked; |
|
|
strdesc.mChannelsPerFrame = this->spec.channels; |
|
@@ -651,6 +742,7 @@ COREAUDIO_Init(SDL_AudioDriverImpl * impl) |
|
|
#if MACOSX_COREAUDIO |
|
|
impl->DetectDevices = COREAUDIO_DetectDevices; |
|
|
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL); |
|
|
impl->HasCaptureSupport = 1; |
|
|
#else |
|
|
impl->OnlyHasDefaultOutputDevice = 1; |
|
|
|
|
|