summaryrefslogtreecommitdiff
path: root/drivers/coreaudio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/coreaudio')
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.cpp304
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.h36
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();
};