|
@@ -1,5 +1,5 @@
|
|
/**************************************************************************/
|
|
/**************************************************************************/
|
|
-/* audio_driver_coreaudio.cpp */
|
|
|
|
|
|
+/* audio_driver_coreaudio.mm */
|
|
/**************************************************************************/
|
|
/**************************************************************************/
|
|
/* This file is part of: */
|
|
/* This file is part of: */
|
|
/* GODOT ENGINE */
|
|
/* GODOT ENGINE */
|
|
@@ -121,7 +121,24 @@ Error AudioDriverCoreAudio::init() {
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- mix_rate = _get_configured_mix_rate();
|
|
|
|
|
|
+#ifdef MACOS_ENABLED
|
|
|
|
+ AudioDeviceID device_id;
|
|
|
|
+ UInt32 dev_id_size = sizeof(AudioDeviceID);
|
|
|
|
+
|
|
|
|
+ AudioObjectPropertyAddress property_dev_id = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
|
|
|
|
+ result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property_dev_id, 0, nullptr, &dev_id_size, &device_id);
|
|
|
|
+ ERR_FAIL_COND_V(result != noErr, FAILED);
|
|
|
|
+
|
|
|
|
+ double hw_mix_rate;
|
|
|
|
+ UInt32 hw_mix_rate_size = sizeof(hw_mix_rate);
|
|
|
|
+
|
|
|
|
+ AudioObjectPropertyAddress property_sr = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
|
|
|
|
+ result = AudioObjectGetPropertyData(device_id, &property_sr, 0, nullptr, &hw_mix_rate_size, &hw_mix_rate);
|
|
|
|
+ ERR_FAIL_COND_V(result != noErr, FAILED);
|
|
|
|
+#else
|
|
|
|
+ double hw_mix_rate = [AVAudioSession sharedInstance].sampleRate;
|
|
|
|
+#endif
|
|
|
|
+ mix_rate = hw_mix_rate;
|
|
|
|
|
|
memset(&strdesc, 0, sizeof(strdesc));
|
|
memset(&strdesc, 0, sizeof(strdesc));
|
|
strdesc.mFormatID = kAudioFormatLinearPCM;
|
|
strdesc.mFormatID = kAudioFormatLinearPCM;
|
|
@@ -147,10 +164,10 @@ Error AudioDriverCoreAudio::init() {
|
|
|
|
|
|
unsigned int buffer_size = buffer_frames * channels;
|
|
unsigned int buffer_size = buffer_frames * channels;
|
|
samples_in.resize(buffer_size);
|
|
samples_in.resize(buffer_size);
|
|
- input_buf.resize(buffer_size);
|
|
|
|
|
|
|
|
print_verbose("CoreAudio: detected " + itos(channels) + " channels");
|
|
print_verbose("CoreAudio: detected " + itos(channels) + " channels");
|
|
- print_verbose("CoreAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
|
|
|
|
|
|
+ print_verbose("CoreAudio: output sampling rate: " + itos(mix_rate) + " Hz");
|
|
|
|
+ print_verbose("CoreAudio: output audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
|
|
|
|
|
|
AURenderCallbackStruct callback;
|
|
AURenderCallbackStruct callback;
|
|
memset(&callback, 0, sizeof(AURenderCallbackStruct));
|
|
memset(&callback, 0, sizeof(AURenderCallbackStruct));
|
|
@@ -275,6 +292,10 @@ int AudioDriverCoreAudio::get_mix_rate() const {
|
|
return mix_rate;
|
|
return mix_rate;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+int AudioDriverCoreAudio::get_input_mix_rate() const {
|
|
|
|
+ return capture_mix_rate;
|
|
|
|
+}
|
|
|
|
+
|
|
AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const {
|
|
AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const {
|
|
return get_speaker_mode_by_total_channels(channels);
|
|
return get_speaker_mode_by_total_channels(channels);
|
|
}
|
|
}
|
|
@@ -378,14 +399,14 @@ Error AudioDriverCoreAudio::init_input_device() {
|
|
|
|
|
|
UInt32 size;
|
|
UInt32 size;
|
|
#ifdef MACOS_ENABLED
|
|
#ifdef MACOS_ENABLED
|
|
- AudioDeviceID deviceId;
|
|
|
|
|
|
+ AudioDeviceID device_id;
|
|
size = sizeof(AudioDeviceID);
|
|
size = sizeof(AudioDeviceID);
|
|
AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
|
|
AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
|
|
|
|
|
|
- result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &deviceId);
|
|
|
|
|
|
+ result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &device_id);
|
|
ERR_FAIL_COND_V(result != noErr, FAILED);
|
|
ERR_FAIL_COND_V(result != noErr, FAILED);
|
|
|
|
|
|
- result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
|
|
|
|
|
|
+ result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device_id, sizeof(AudioDeviceID));
|
|
ERR_FAIL_COND_V(result != noErr, FAILED);
|
|
ERR_FAIL_COND_V(result != noErr, FAILED);
|
|
#endif
|
|
#endif
|
|
|
|
|
|
@@ -410,13 +431,23 @@ Error AudioDriverCoreAudio::init_input_device() {
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- mix_rate = _get_configured_mix_rate();
|
|
|
|
|
|
+#ifdef MACOS_ENABLED
|
|
|
|
+ double hw_mix_rate;
|
|
|
|
+ UInt32 hw_mix_rate_size = sizeof(hw_mix_rate);
|
|
|
|
+
|
|
|
|
+ AudioObjectPropertyAddress property_sr = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
|
|
|
|
+ result = AudioObjectGetPropertyData(device_id, &property_sr, 0, nullptr, &hw_mix_rate_size, &hw_mix_rate);
|
|
|
|
+ ERR_FAIL_COND_V(result != noErr, FAILED);
|
|
|
|
+#else
|
|
|
|
+ double hw_mix_rate = [AVAudioSession sharedInstance].sampleRate;
|
|
|
|
+#endif
|
|
|
|
+ capture_mix_rate = hw_mix_rate;
|
|
|
|
|
|
memset(&strdesc, 0, sizeof(strdesc));
|
|
memset(&strdesc, 0, sizeof(strdesc));
|
|
strdesc.mFormatID = kAudioFormatLinearPCM;
|
|
strdesc.mFormatID = kAudioFormatLinearPCM;
|
|
strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
|
|
strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
|
|
strdesc.mChannelsPerFrame = capture_channels;
|
|
strdesc.mChannelsPerFrame = capture_channels;
|
|
- strdesc.mSampleRate = mix_rate;
|
|
|
|
|
|
+ strdesc.mSampleRate = capture_mix_rate;
|
|
strdesc.mFramesPerPacket = 1;
|
|
strdesc.mFramesPerPacket = 1;
|
|
strdesc.mBitsPerChannel = 16;
|
|
strdesc.mBitsPerChannel = 16;
|
|
strdesc.mBytesPerFrame = strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
|
|
strdesc.mBytesPerFrame = strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
|
|
@@ -425,6 +456,13 @@ Error AudioDriverCoreAudio::init_input_device() {
|
|
result = AudioUnitSetProperty(input_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, sizeof(strdesc));
|
|
result = AudioUnitSetProperty(input_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, sizeof(strdesc));
|
|
ERR_FAIL_COND_V(result != noErr, FAILED);
|
|
ERR_FAIL_COND_V(result != noErr, FAILED);
|
|
|
|
|
|
|
|
+ int latency = Engine::get_singleton()->get_audio_output_latency();
|
|
|
|
+ // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels)
|
|
|
|
+ capture_buffer_frames = closest_power_of_2(latency * capture_mix_rate / 1000);
|
|
|
|
+
|
|
|
|
+ unsigned int buffer_size = capture_buffer_frames * capture_channels;
|
|
|
|
+ input_buf.resize(buffer_size);
|
|
|
|
+
|
|
AURenderCallbackStruct callback;
|
|
AURenderCallbackStruct callback;
|
|
memset(&callback, 0, sizeof(AURenderCallbackStruct));
|
|
memset(&callback, 0, sizeof(AURenderCallbackStruct));
|
|
callback.inputProc = &AudioDriverCoreAudio::input_callback;
|
|
callback.inputProc = &AudioDriverCoreAudio::input_callback;
|
|
@@ -435,6 +473,9 @@ Error AudioDriverCoreAudio::init_input_device() {
|
|
result = AudioUnitInitialize(input_unit);
|
|
result = AudioUnitInitialize(input_unit);
|
|
ERR_FAIL_COND_V(result != noErr, FAILED);
|
|
ERR_FAIL_COND_V(result != noErr, FAILED);
|
|
|
|
|
|
|
|
+ print_verbose("CoreAudio: input sampling rate: " + itos(capture_mix_rate) + " Hz");
|
|
|
|
+ print_verbose("CoreAudio: input audio buffer frames: " + itos(capture_buffer_frames) + " calculated latency: " + itos(capture_buffer_frames * 1000 / capture_mix_rate) + "ms");
|
|
|
|
+
|
|
return OK;
|
|
return OK;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -477,7 +518,7 @@ void AudioDriverCoreAudio::finish_input_device() {
|
|
}
|
|
}
|
|
|
|
|
|
Error AudioDriverCoreAudio::input_start() {
|
|
Error AudioDriverCoreAudio::input_start() {
|
|
- input_buffer_init(buffer_frames);
|
|
|
|
|
|
+ input_buffer_init(capture_buffer_frames);
|
|
|
|
|
|
OSStatus result = AudioOutputUnitStart(input_unit);
|
|
OSStatus result = AudioOutputUnitStart(input_unit);
|
|
if (result != noErr) {
|
|
if (result != noErr) {
|
|
@@ -561,7 +602,7 @@ PackedStringArray AudioDriverCoreAudio::_get_device_list(bool input) {
|
|
}
|
|
}
|
|
|
|
|
|
void AudioDriverCoreAudio::_set_device(const String &output_device, bool input) {
|
|
void AudioDriverCoreAudio::_set_device(const String &output_device, bool input) {
|
|
- AudioDeviceID deviceId;
|
|
|
|
|
|
+ AudioDeviceID device_id;
|
|
bool found = false;
|
|
bool found = false;
|
|
if (output_device != "Default") {
|
|
if (output_device != "Default") {
|
|
AudioObjectPropertyAddress prop;
|
|
AudioObjectPropertyAddress prop;
|
|
@@ -608,7 +649,7 @@ void AudioDriverCoreAudio::_set_device(const String &output_device, bool input)
|
|
if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
|
|
if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
|
|
String name = String::utf8(buffer) + " (" + itos(audioDevices[i]) + ")";
|
|
String name = String::utf8(buffer) + " (" + itos(audioDevices[i]) + ")";
|
|
if (name == output_device) {
|
|
if (name == output_device) {
|
|
- deviceId = audioDevices[i];
|
|
|
|
|
|
+ device_id = audioDevices[i];
|
|
found = true;
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -626,14 +667,14 @@ void AudioDriverCoreAudio::_set_device(const String &output_device, bool input)
|
|
UInt32 elem = input ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice;
|
|
UInt32 elem = input ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice;
|
|
AudioObjectPropertyAddress property = { elem, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
|
|
AudioObjectPropertyAddress property = { elem, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
|
|
|
|
|
|
- OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &deviceId);
|
|
|
|
|
|
+ OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &device_id);
|
|
ERR_FAIL_COND(result != noErr);
|
|
ERR_FAIL_COND(result != noErr);
|
|
|
|
|
|
found = true;
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
|
|
if (found) {
|
|
if (found) {
|
|
- OSStatus result = AudioUnitSetProperty(input ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
|
|
|
|
|
|
+ OSStatus result = AudioUnitSetProperty(input ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device_id, sizeof(AudioDeviceID));
|
|
ERR_FAIL_COND(result != noErr);
|
|
ERR_FAIL_COND(result != noErr);
|
|
|
|
|
|
if (input) {
|
|
if (input) {
|