diff options
Diffstat (limited to 'drivers/coreaudio')
-rw-r--r-- | drivers/coreaudio/audio_driver_coreaudio.cpp | 304 | ||||
-rw-r--r-- | drivers/coreaudio/audio_driver_coreaudio.h | 36 |
2 files changed, 254 insertions, 86 deletions
diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index ac21de91e4..e1f47cb8c2 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -35,8 +35,23 @@ #include "os/os.h" #define kOutputBus 0 +#define kInputBus 1 #ifdef OSX_ENABLED +OSStatus AudioDriverCoreAudio::input_device_address_cb(AudioObjectID inObjectID, + UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, + void *inClientData) { + AudioDriverCoreAudio *driver = (AudioDriverCoreAudio *)inClientData; + + // If our selected device is the Default call set_device to update the + // kAudioOutputUnitProperty_CurrentDevice property + if (driver->capture_device_name == "Default") { + driver->capture_set_device("Default"); + } + + return noErr; +} + OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData) { @@ -79,6 +94,11 @@ Error AudioDriverCoreAudio::init() { result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); ERR_FAIL_COND_V(result != noErr, FAILED); + + prop.mSelector = kAudioHardwarePropertyDefaultInputDevice; + + result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this); + ERR_FAIL_COND_V(result != noErr, FAILED); #endif AudioStreamBasicDescription strdesc; @@ -102,6 +122,26 @@ Error AudioDriverCoreAudio::init() { break; } + zeromem(&strdesc, sizeof(strdesc)); + size = sizeof(strdesc); + result = AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, &size); + ERR_FAIL_COND_V(result != noErr, FAILED); + + switch (strdesc.mChannelsPerFrame) { + case 1: // Mono + capture_channels = 1; + break; + + case 2: // Stereo + capture_channels = 2; + break; + + default: + // Unknown number of channels, default to stereo + capture_channels = 2; + break; + } + mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); zeromem(&strdesc, sizeof(strdesc)); @@ -117,6 +157,11 @@ Error AudioDriverCoreAudio::init() { result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &strdesc, sizeof(strdesc)); ERR_FAIL_COND_V(result != noErr, FAILED); + strdesc.mChannelsPerFrame = capture_channels; + + result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, sizeof(strdesc)); + ERR_FAIL_COND_V(result != noErr, FAILED); + int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels) buffer_frames = closest_power_of_2(latency * mix_rate / 1000); @@ -126,8 +171,12 @@ Error AudioDriverCoreAudio::init() { ERR_FAIL_COND_V(result != noErr, FAILED); #endif - buffer_size = buffer_frames * channels; + unsigned int buffer_size = buffer_frames * channels; samples_in.resize(buffer_size); + input_buf.resize(buffer_size); + input_buffer.resize(buffer_size * 8); + input_position = 0; + input_size = 0; if (OS::get_singleton()->is_stdout_verbose()) { print_line("CoreAudio: detected " + itos(channels) + " channels"); @@ -141,6 +190,12 @@ Error AudioDriverCoreAudio::init() { result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); ERR_FAIL_COND_V(result != noErr, FAILED); + zeromem(&callback, sizeof(AURenderCallbackStruct)); + callback.inputProc = &AudioDriverCoreAudio::input_callback; + callback.inputProcRefCon = this; + result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback)); + ERR_FAIL_COND_V(result != noErr, FAILED); + result = AudioUnitInitialize(audio_unit); ERR_FAIL_COND_V(result != noErr, FAILED); @@ -192,6 +247,45 @@ OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon, return 0; }; +OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData) { + + AudioDriverCoreAudio *ad = (AudioDriverCoreAudio *)inRefCon; + if (!ad->active) { + return 0; + } + + ad->lock(); + + AudioBufferList bufferList; + bufferList.mNumberBuffers = 1; + bufferList.mBuffers[0].mData = ad->input_buf.ptrw(); + bufferList.mBuffers[0].mNumberChannels = ad->capture_channels; + bufferList.mBuffers[0].mDataByteSize = ad->input_buf.size() * sizeof(int16_t); + + OSStatus result = AudioUnitRender(ad->audio_unit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList); + if (result == noErr) { + for (int i = 0; i < inNumberFrames * ad->capture_channels; i++) { + int32_t sample = ad->input_buf[i] << 16; + ad->input_buffer_write(sample); + + if (ad->capture_channels == 1) { + // In case input device is single channel convert it to Stereo + ad->input_buffer_write(sample); + } + } + } else { + ERR_PRINT(("AudioUnitRender failed, code: " + itos(result)).utf8().get_data()); + } + + ad->unlock(); + + return result; +} + void AudioDriverCoreAudio::start() { if (!active) { OSStatus result = AudioOutputUnitStart(audio_unit); @@ -222,9 +316,94 @@ AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); }; +void AudioDriverCoreAudio::lock() { + if (mutex) + mutex->lock(); +}; + +void AudioDriverCoreAudio::unlock() { + if (mutex) + mutex->unlock(); +}; + +bool AudioDriverCoreAudio::try_lock() { + if (mutex) + return mutex->try_lock() == OK; + return true; +} + +void AudioDriverCoreAudio::finish() { + OSStatus result; + + lock(); + + AURenderCallbackStruct callback; + zeromem(&callback, sizeof(AURenderCallbackStruct)); + result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); + if (result != noErr) { + ERR_PRINT("AudioUnitSetProperty failed"); + } + + if (active) { + result = AudioOutputUnitStop(audio_unit); + if (result != noErr) { + ERR_PRINT("AudioOutputUnitStop failed"); + } + + active = false; + } + + result = AudioUnitUninitialize(audio_unit); + if (result != noErr) { + ERR_PRINT("AudioUnitUninitialize failed"); + } + #ifdef OSX_ENABLED + AudioObjectPropertyAddress prop; + prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + prop.mScope = kAudioObjectPropertyScopeGlobal; + prop.mElement = kAudioObjectPropertyElementMaster; -Array AudioDriverCoreAudio::get_device_list() { + result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); + if (result != noErr) { + ERR_PRINT("AudioObjectRemovePropertyListener failed"); + } +#endif + + result = AudioComponentInstanceDispose(audio_unit); + if (result != noErr) { + ERR_PRINT("AudioComponentInstanceDispose failed"); + } + + unlock(); + + if (mutex) { + memdelete(mutex); + mutex = NULL; + } +}; + +Error AudioDriverCoreAudio::capture_start() { + + UInt32 flag = 1; + OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + return OK; +} + +Error AudioDriverCoreAudio::capture_stop() { + + UInt32 flag = 0; + OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + return OK; +} + +#ifdef OSX_ENABLED + +Array AudioDriverCoreAudio::_get_device_list(bool capture) { Array list; @@ -243,20 +422,20 @@ Array AudioDriverCoreAudio::get_device_list() { UInt32 deviceCount = size / sizeof(AudioDeviceID); for (UInt32 i = 0; i < deviceCount; i++) { - prop.mScope = kAudioDevicePropertyScopeOutput; + prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; prop.mSelector = kAudioDevicePropertyStreamConfiguration; AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size); AudioBufferList *bufferList = (AudioBufferList *)malloc(size); AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList); - UInt32 outputChannelCount = 0; + UInt32 channelCount = 0; for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) - outputChannelCount += bufferList->mBuffers[j].mNumberChannels; + channelCount += bufferList->mBuffers[j].mNumberChannels; free(bufferList); - if (outputChannelCount >= 1) { + if (channelCount >= 1) { CFStringRef cfname; size = sizeof(CFStringRef); @@ -281,21 +460,11 @@ Array AudioDriverCoreAudio::get_device_list() { return list; } -String AudioDriverCoreAudio::get_device() { - - return device_name; -} - -void AudioDriverCoreAudio::set_device(String device) { - - device_name = device; - if (!active) { - return; - } +void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { AudioDeviceID deviceId; bool found = false; - if (device_name != "Default") { + if (device != "Default") { AudioObjectPropertyAddress prop; prop.mSelector = kAudioHardwarePropertyDevices; @@ -309,20 +478,20 @@ void AudioDriverCoreAudio::set_device(String device) { UInt32 deviceCount = size / sizeof(AudioDeviceID); for (UInt32 i = 0; i < deviceCount && !found; i++) { - prop.mScope = kAudioDevicePropertyScopeOutput; + prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; prop.mSelector = kAudioDevicePropertyStreamConfiguration; AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size); AudioBufferList *bufferList = (AudioBufferList *)malloc(size); AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList); - UInt32 outputChannelCount = 0; + UInt32 channelCount = 0; for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) - outputChannelCount += bufferList->mBuffers[j].mNumberChannels; + channelCount += bufferList->mBuffers[j].mNumberChannels; free(bufferList); - if (outputChannelCount >= 1) { + if (channelCount >= 1) { CFStringRef cfname; size = sizeof(CFStringRef); @@ -335,7 +504,7 @@ void AudioDriverCoreAudio::set_device(String device) { char *buffer = (char *)malloc(maxSize); if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) { String name = String(buffer) + " (" + itos(audioDevices[i]) + ")"; - if (name == device_name) { + if (name == device) { deviceId = audioDevices[i]; found = true; } @@ -351,7 +520,8 @@ void AudioDriverCoreAudio::set_device(String device) { if (!found) { // If we haven't found the desired device get the system default one UInt32 size = sizeof(AudioDeviceID); - AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + UInt32 elem = capture ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice; + AudioObjectPropertyAddress property = { elem, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, &size, &deviceId); ERR_FAIL_COND(result != noErr); @@ -360,79 +530,52 @@ void AudioDriverCoreAudio::set_device(String device) { } if (found) { - OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID)); + OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, capture ? kInputBus : kOutputBus, &deviceId, sizeof(AudioDeviceID)); ERR_FAIL_COND(result != noErr); + + // Reset audio input to keep synchronisation. + input_position = 0; + input_size = 0; } } -#endif - -void AudioDriverCoreAudio::lock() { - if (mutex) - mutex->lock(); -}; - -void AudioDriverCoreAudio::unlock() { - if (mutex) - mutex->unlock(); -}; +Array AudioDriverCoreAudio::get_device_list() { -bool AudioDriverCoreAudio::try_lock() { - if (mutex) - return mutex->try_lock() == OK; - return true; + return _get_device_list(); } -void AudioDriverCoreAudio::finish() { - OSStatus result; +String AudioDriverCoreAudio::get_device() { - lock(); + return device_name; +} - AURenderCallbackStruct callback; - zeromem(&callback, sizeof(AURenderCallbackStruct)); - result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); - if (result != noErr) { - ERR_PRINT("AudioUnitSetProperty failed"); - } +void AudioDriverCoreAudio::set_device(String device) { + device_name = device; if (active) { - result = AudioOutputUnitStop(audio_unit); - if (result != noErr) { - ERR_PRINT("AudioOutputUnitStop failed"); - } - - active = false; + _set_device(device_name); } +} - result = AudioUnitUninitialize(audio_unit); - if (result != noErr) { - ERR_PRINT("AudioUnitUninitialize failed"); +void AudioDriverCoreAudio::capture_set_device(const String &p_name) { + + capture_device_name = p_name; + if (active) { + _set_device(capture_device_name, true); } +} -#ifdef OSX_ENABLED - AudioObjectPropertyAddress prop; - prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - prop.mScope = kAudioObjectPropertyScopeGlobal; - prop.mElement = kAudioObjectPropertyElementMaster; +Array AudioDriverCoreAudio::capture_get_device_list() { - result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); - if (result != noErr) { - ERR_PRINT("AudioObjectRemovePropertyListener failed"); - } -#endif + return _get_device_list(true); +} - result = AudioComponentInstanceDispose(audio_unit); - if (result != noErr) { - ERR_PRINT("AudioComponentInstanceDispose failed"); - } +String AudioDriverCoreAudio::capture_get_device() { - unlock(); + return capture_device_name; +} - if (mutex) { - memdelete(mutex); - mutex = NULL; - } -}; +#endif AudioDriverCoreAudio::AudioDriverCoreAudio() { active = false; @@ -440,14 +583,15 @@ AudioDriverCoreAudio::AudioDriverCoreAudio() { mix_rate = 0; channels = 2; + capture_channels = 2; - buffer_size = 0; buffer_frames = 0; samples_in.clear(); device_name = "Default"; -}; + capture_device_name = "Default"; +} AudioDriverCoreAudio::~AudioDriverCoreAudio(){}; diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h index 99c910498e..d3f7c8d596 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.h +++ b/drivers/coreaudio/audio_driver_coreaudio.h @@ -48,15 +48,24 @@ class AudioDriverCoreAudio : public AudioDriver { Mutex *mutex; String device_name; + String capture_device_name; int mix_rate; unsigned int channels; + unsigned int capture_channels; unsigned int buffer_frames; - unsigned int buffer_size; Vector<int32_t> samples_in; + Vector<int16_t> input_buf; #ifdef OSX_ENABLED + Array _get_device_list(bool capture = false); + void _set_device(const String &device, bool capture = false); + + static OSStatus input_device_address_cb(AudioObjectID inObjectID, + UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, + void *inClientData); + static OSStatus output_device_address_cb(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData); @@ -68,6 +77,12 @@ class AudioDriverCoreAudio : public AudioDriver { UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); + static OSStatus input_callback(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData); + public: const char *get_name() const { return "CoreAudio"; @@ -77,18 +92,27 @@ public: virtual void start(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; -#ifdef OSX_ENABLED - virtual Array get_device_list(); - virtual String get_device(); - virtual void set_device(String device); -#endif + virtual void lock(); virtual void unlock(); virtual void finish(); + virtual Error capture_start(); + virtual Error capture_stop(); + bool try_lock(); void stop(); +#ifdef OSX_ENABLED + virtual Array get_device_list(); + virtual String get_device(); + virtual void set_device(String device); + + virtual Array capture_get_device_list(); + virtual void capture_set_device(const String &p_name); + virtual String capture_get_device(); +#endif + AudioDriverCoreAudio(); ~AudioDriverCoreAudio(); }; |