mirror of
https://github.com/godotengine/godot.git
synced 2024-11-21 11:32:13 +00:00
[macOS/iOS] Use hardware sampling rates for audio I/O.
This commit is contained in:
parent
6732a0fd86
commit
c8609f4c0f
@ -117,6 +117,12 @@
|
|||||||
[b]Note:[/b] [member ProjectSettings.audio/driver/enable_input] must be [code]true[/code] for audio input to work. See also that setting's description for caveats related to permissions and operating system privacy settings.
|
[b]Note:[/b] [member ProjectSettings.audio/driver/enable_input] must be [code]true[/code] for audio input to work. See also that setting's description for caveats related to permissions and operating system privacy settings.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="get_input_mix_rate" qualifiers="const">
|
||||||
|
<return type="float" />
|
||||||
|
<description>
|
||||||
|
Returns the sample rate at the input of the [AudioServer].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="get_mix_rate" qualifiers="const">
|
<method name="get_mix_rate" qualifiers="const">
|
||||||
<return type="float" />
|
<return type="float" />
|
||||||
<description>
|
<description>
|
||||||
|
@ -14,7 +14,8 @@ SConscript("windows/SCsub")
|
|||||||
|
|
||||||
# Sounds drivers
|
# Sounds drivers
|
||||||
SConscript("alsa/SCsub")
|
SConscript("alsa/SCsub")
|
||||||
SConscript("coreaudio/SCsub")
|
if env["platform"] == "ios" or env["platform"] == "macos":
|
||||||
|
SConscript("coreaudio/SCsub")
|
||||||
SConscript("pulseaudio/SCsub")
|
SConscript("pulseaudio/SCsub")
|
||||||
if env["platform"] == "windows":
|
if env["platform"] == "windows":
|
||||||
SConscript("wasapi/SCsub")
|
SConscript("wasapi/SCsub")
|
||||||
|
@ -4,4 +4,4 @@ from misc.utility.scons_hints import *
|
|||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
# Driver source files
|
# Driver source files
|
||||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
env.add_source_files(env.drivers_sources, "*.mm")
|
||||||
|
@ -38,6 +38,8 @@
|
|||||||
#import <AudioUnit/AudioUnit.h>
|
#import <AudioUnit/AudioUnit.h>
|
||||||
#ifdef MACOS_ENABLED
|
#ifdef MACOS_ENABLED
|
||||||
#import <CoreAudio/AudioHardware.h>
|
#import <CoreAudio/AudioHardware.h>
|
||||||
|
#else
|
||||||
|
#import <AVFoundation/AVFoundation.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class AudioDriverCoreAudio : public AudioDriver {
|
class AudioDriverCoreAudio : public AudioDriver {
|
||||||
@ -51,9 +53,11 @@ class AudioDriverCoreAudio : public AudioDriver {
|
|||||||
String input_device_name = "Default";
|
String input_device_name = "Default";
|
||||||
|
|
||||||
int mix_rate = 0;
|
int mix_rate = 0;
|
||||||
|
int capture_mix_rate = 0;
|
||||||
unsigned int channels = 2;
|
unsigned int channels = 2;
|
||||||
unsigned int capture_channels = 2;
|
unsigned int capture_channels = 2;
|
||||||
unsigned int buffer_frames = 0;
|
unsigned int buffer_frames = 0;
|
||||||
|
unsigned int capture_buffer_frames = 0;
|
||||||
|
|
||||||
Vector<int32_t> samples_in;
|
Vector<int32_t> samples_in;
|
||||||
Vector<int16_t> input_buf;
|
Vector<int16_t> input_buf;
|
||||||
@ -94,6 +98,7 @@ public:
|
|||||||
virtual Error init() override;
|
virtual Error init() override;
|
||||||
virtual void start() override;
|
virtual void start() override;
|
||||||
virtual int get_mix_rate() const override;
|
virtual int get_mix_rate() const override;
|
||||||
|
virtual int get_input_mix_rate() const override;
|
||||||
virtual SpeakerMode get_speaker_mode() const override;
|
virtual SpeakerMode get_speaker_mode() const override;
|
||||||
|
|
||||||
virtual void lock() override;
|
virtual void lock() override;
|
||||||
|
@ -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) {
|
@ -390,7 +390,7 @@ int AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_fra
|
|||||||
|
|
||||||
Vector<int32_t> buf = AudioDriver::get_singleton()->get_input_buffer();
|
Vector<int32_t> buf = AudioDriver::get_singleton()->get_input_buffer();
|
||||||
unsigned int input_size = AudioDriver::get_singleton()->get_input_size();
|
unsigned int input_size = AudioDriver::get_singleton()->get_input_size();
|
||||||
int mix_rate = AudioDriver::get_singleton()->get_mix_rate();
|
int mix_rate = AudioDriver::get_singleton()->get_input_mix_rate();
|
||||||
unsigned int playback_delay = MIN(((50 * mix_rate) / 1000) * 2, buf.size() >> 1);
|
unsigned int playback_delay = MIN(((50 * mix_rate) / 1000) * 2, buf.size() >> 1);
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
unsigned int input_position = AudioDriver::get_singleton()->get_input_position();
|
unsigned int input_position = AudioDriver::get_singleton()->get_input_position();
|
||||||
@ -441,7 +441,7 @@ int AudioStreamPlaybackMicrophone::mix(AudioFrame *p_buffer, float p_rate_scale,
|
|||||||
}
|
}
|
||||||
|
|
||||||
float AudioStreamPlaybackMicrophone::get_stream_sampling_rate() {
|
float AudioStreamPlaybackMicrophone::get_stream_sampling_rate() {
|
||||||
return AudioDriver::get_singleton()->get_mix_rate();
|
return AudioDriver::get_singleton()->get_input_mix_rate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlaybackMicrophone::start(double p_from_pos) {
|
void AudioStreamPlaybackMicrophone::start(double p_from_pos) {
|
||||||
|
@ -1614,6 +1614,10 @@ float AudioServer::get_mix_rate() const {
|
|||||||
return AudioDriver::get_singleton()->get_mix_rate();
|
return AudioDriver::get_singleton()->get_mix_rate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float AudioServer::get_input_mix_rate() const {
|
||||||
|
return AudioDriver::get_singleton()->get_input_mix_rate();
|
||||||
|
}
|
||||||
|
|
||||||
float AudioServer::read_output_peak_db() const {
|
float AudioServer::read_output_peak_db() const {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1946,6 +1950,7 @@ void AudioServer::_bind_methods() {
|
|||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("get_speaker_mode"), &AudioServer::get_speaker_mode);
|
ClassDB::bind_method(D_METHOD("get_speaker_mode"), &AudioServer::get_speaker_mode);
|
||||||
ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioServer::get_mix_rate);
|
ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioServer::get_mix_rate);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_input_mix_rate"), &AudioServer::get_input_mix_rate);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("get_output_device_list"), &AudioServer::get_output_device_list);
|
ClassDB::bind_method(D_METHOD("get_output_device_list"), &AudioServer::get_output_device_list);
|
||||||
ClassDB::bind_method(D_METHOD("get_output_device"), &AudioServer::get_output_device);
|
ClassDB::bind_method(D_METHOD("get_output_device"), &AudioServer::get_output_device);
|
||||||
|
@ -99,6 +99,7 @@ public:
|
|||||||
virtual Error init() = 0;
|
virtual Error init() = 0;
|
||||||
virtual void start() = 0;
|
virtual void start() = 0;
|
||||||
virtual int get_mix_rate() const = 0;
|
virtual int get_mix_rate() const = 0;
|
||||||
|
virtual int get_input_mix_rate() const { return get_mix_rate(); }
|
||||||
virtual SpeakerMode get_speaker_mode() const = 0;
|
virtual SpeakerMode get_speaker_mode() const = 0;
|
||||||
virtual float get_latency() { return 0; }
|
virtual float get_latency() { return 0; }
|
||||||
|
|
||||||
@ -441,6 +442,7 @@ public:
|
|||||||
|
|
||||||
virtual SpeakerMode get_speaker_mode() const;
|
virtual SpeakerMode get_speaker_mode() const;
|
||||||
virtual float get_mix_rate() const;
|
virtual float get_mix_rate() const;
|
||||||
|
virtual float get_input_mix_rate() const;
|
||||||
|
|
||||||
virtual float read_output_peak_db() const;
|
virtual float read_output_peak_db() const;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user