فهرست منبع

[macOS/iOS] Use hardware sampling rates for audio I/O.

bruvzg 1 سال پیش
والد
کامیت
c8609f4c0f

+ 6 - 0
doc/classes/AudioServer.xml

@@ -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.
 			</description>
 		</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">
 			<return type="float" />
 			<description>

+ 2 - 1
drivers/SCsub

@@ -14,7 +14,8 @@ SConscript("windows/SCsub")
 
 # Sounds drivers
 SConscript("alsa/SCsub")
-SConscript("coreaudio/SCsub")
+if env["platform"] == "ios" or env["platform"] == "macos":
+    SConscript("coreaudio/SCsub")
 SConscript("pulseaudio/SCsub")
 if env["platform"] == "windows":
     SConscript("wasapi/SCsub")

+ 1 - 1
drivers/coreaudio/SCsub

@@ -4,4 +4,4 @@ from misc.utility.scons_hints import *
 Import("env")
 
 # Driver source files
-env.add_source_files(env.drivers_sources, "*.cpp")
+env.add_source_files(env.drivers_sources, "*.mm")

+ 5 - 0
drivers/coreaudio/audio_driver_coreaudio.h

@@ -38,6 +38,8 @@
 #import <AudioUnit/AudioUnit.h>
 #ifdef MACOS_ENABLED
 #import <CoreAudio/AudioHardware.h>
+#else
+#import <AVFoundation/AVFoundation.h>
 #endif
 
 class AudioDriverCoreAudio : public AudioDriver {
@@ -51,9 +53,11 @@ class AudioDriverCoreAudio : public AudioDriver {
 	String input_device_name = "Default";
 
 	int mix_rate = 0;
+	int capture_mix_rate = 0;
 	unsigned int channels = 2;
 	unsigned int capture_channels = 2;
 	unsigned int buffer_frames = 0;
+	unsigned int capture_buffer_frames = 0;
 
 	Vector<int32_t> samples_in;
 	Vector<int16_t> input_buf;
@@ -94,6 +98,7 @@ public:
 	virtual Error init() override;
 	virtual void start() override;
 	virtual int get_mix_rate() const override;
+	virtual int get_input_mix_rate() const override;
 	virtual SpeakerMode get_speaker_mode() const override;
 
 	virtual void lock() override;

+ 55 - 14
drivers/coreaudio/audio_driver_coreaudio.cpp → drivers/coreaudio/audio_driver_coreaudio.mm

@@ -1,5 +1,5 @@
 /**************************************************************************/
-/*  audio_driver_coreaudio.cpp                                            */
+/*  audio_driver_coreaudio.mm                                             */
 /**************************************************************************/
 /*                         This file is part of:                          */
 /*                             GODOT ENGINE                               */
@@ -121,7 +121,24 @@ Error AudioDriverCoreAudio::init() {
 			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));
 	strdesc.mFormatID = kAudioFormatLinearPCM;
@@ -147,10 +164,10 @@ Error AudioDriverCoreAudio::init() {
 
 	unsigned int buffer_size = buffer_frames * channels;
 	samples_in.resize(buffer_size);
-	input_buf.resize(buffer_size);
 
 	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;
 	memset(&callback, 0, sizeof(AURenderCallbackStruct));
@@ -275,6 +292,10 @@ int AudioDriverCoreAudio::get_mix_rate() const {
 	return mix_rate;
 }
 
+int AudioDriverCoreAudio::get_input_mix_rate() const {
+	return capture_mix_rate;
+}
+
 AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const {
 	return get_speaker_mode_by_total_channels(channels);
 }
@@ -378,14 +399,14 @@ Error AudioDriverCoreAudio::init_input_device() {
 
 	UInt32 size;
 #ifdef MACOS_ENABLED
-	AudioDeviceID deviceId;
+	AudioDeviceID device_id;
 	size = sizeof(AudioDeviceID);
 	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);
 
-	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);
 #endif
 
@@ -410,13 +431,23 @@ Error AudioDriverCoreAudio::init_input_device() {
 			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));
 	strdesc.mFormatID = kAudioFormatLinearPCM;
 	strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
 	strdesc.mChannelsPerFrame = capture_channels;
-	strdesc.mSampleRate = mix_rate;
+	strdesc.mSampleRate = capture_mix_rate;
 	strdesc.mFramesPerPacket = 1;
 	strdesc.mBitsPerChannel = 16;
 	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));
 	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;
 	memset(&callback, 0, sizeof(AURenderCallbackStruct));
 	callback.inputProc = &AudioDriverCoreAudio::input_callback;
@@ -435,6 +473,9 @@ Error AudioDriverCoreAudio::init_input_device() {
 	result = AudioUnitInitialize(input_unit);
 	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;
 }
 
@@ -477,7 +518,7 @@ void AudioDriverCoreAudio::finish_input_device() {
 }
 
 Error AudioDriverCoreAudio::input_start() {
-	input_buffer_init(buffer_frames);
+	input_buffer_init(capture_buffer_frames);
 
 	OSStatus result = AudioOutputUnitStart(input_unit);
 	if (result != noErr) {
@@ -561,7 +602,7 @@ PackedStringArray AudioDriverCoreAudio::_get_device_list(bool input) {
 }
 
 void AudioDriverCoreAudio::_set_device(const String &output_device, bool input) {
-	AudioDeviceID deviceId;
+	AudioDeviceID device_id;
 	bool found = false;
 	if (output_device != "Default") {
 		AudioObjectPropertyAddress prop;
@@ -608,7 +649,7 @@ void AudioDriverCoreAudio::_set_device(const String &output_device, bool input)
 				if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
 					String name = String::utf8(buffer) + " (" + itos(audioDevices[i]) + ")";
 					if (name == output_device) {
-						deviceId = audioDevices[i];
+						device_id = audioDevices[i];
 						found = true;
 					}
 				}
@@ -626,14 +667,14 @@ void AudioDriverCoreAudio::_set_device(const String &output_device, bool input)
 		UInt32 elem = input ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice;
 		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);
 
 		found = true;
 	}
 
 	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);
 
 		if (input) {

+ 2 - 2
servers/audio/audio_stream.cpp

@@ -390,7 +390,7 @@ int AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_fra
 
 	Vector<int32_t> buf = AudioDriver::get_singleton()->get_input_buffer();
 	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);
 #ifdef DEBUG_ENABLED
 	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() {
-	return AudioDriver::get_singleton()->get_mix_rate();
+	return AudioDriver::get_singleton()->get_input_mix_rate();
 }
 
 void AudioStreamPlaybackMicrophone::start(double p_from_pos) {

+ 5 - 0
servers/audio_server.cpp

@@ -1614,6 +1614,10 @@ float AudioServer::get_mix_rate() const {
 	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 {
 	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_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"), &AudioServer::get_output_device);

+ 2 - 0
servers/audio_server.h

@@ -99,6 +99,7 @@ public:
 	virtual Error init() = 0;
 	virtual void start() = 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 float get_latency() { return 0; }
 
@@ -441,6 +442,7 @@ public:
 
 	virtual SpeakerMode get_speaker_mode() const;
 	virtual float get_mix_rate() const;
+	virtual float get_input_mix_rate() const;
 
 	virtual float read_output_peak_db() const;