-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose functions from AudioStreamPlaybackMicrophone
to access the microphone AudioDriver::input_buffer
indepedently of the rest of the Audio system
#100508
base: master
Are you sure you want to change the base?
Conversation
Since you modified the scripting API, make sure to update the class reference as well and fill it out: https://docs.godotengine.org/en/stable/contributing/documentation/updating_the_class_reference.html#updating-class-reference-when-working-on-the-engine |
AudioStreamPlaybackMicrophone
to access the microphone AudioDriver::input_buffer
indepedently of the rest of the Audio system
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we discussed, the design is that instead of the AudioServer using the mix callback somewhere else, I assume a node::_process(delta) will process the mic driver.
I am not convinced by the arguments to bypass the audio server to access the microphone driver directly.
TLDR There is a long and buggy route for getting microphone audio data out of the engine. It would be shorter and more reliable to extract it directly from the AudioDriver's input_buffer. What happens now All microphone data goes into the ring-buffer AudioDriver::input_buffer advancing The AudioStreamPlaybackMicrophone object has its own local pointer At the moment the only way to extract microphone data from the engine is to wait for the AudioServer to request chunks of data from an AudioBus at its own update rate. This AudioBus then requests the data from an AudioStreamPlaybackMicrophone filtered through an AudioEffectCapture object that copies any audio that passes through into its own little buffer. Then, on a process loop, you extract the data from this buffer by calling AudioEffectCapture.get_buffer() There are two problems with this. Firstly, the AudioServer is highly sensitive to data not arriving in at the rate it requires, so if the microphone buffer isn't filled fast enough the stream is terminated. There is a pull-request to fix the problem on the Android platform where the microphone keeps switching off after two minutes by padding the missing frames with zeros. Not surprisingly, this degrades the quality of the microphone audio. Secondly, we don't generally want any microphone audio data in the AudioServer because it causes feedback and is too laggy to work as realtime amplifier. So if it is too difficult to debug this buffer properly (which it is), it's not worth it. The evidence is that standard operating procedure requires us to create a special bus for the AudioStreamMicrophone to output to that is set to Mute. The solution Expose |
…odot into gtch/micplaybackmaster
This fix is working well. It ran on my cheap Android phone for 4 hours without any issues at all. Previously it would last about 3 minutes on this phone before the Although there is some skepticism, I would like to push this PR into some place where it can be critically discussed and reviewed, because it actually works, and we have don't even have an outline for fixing the Microphone any other way. The primary function added into PackedVector2Array get_microphone_buffer(int p_frames) I would prefer to have the additional function: bool mix_microphone(AudioFrame* buffer, int p_frames); which would copy the values directly into an array rather than allocating and returning a temporary array by value. The main use of this interface is the twovoip addon for doing Voice over IP. There are a numerous virtual functions like this one: void _process(src_buffer: const void*, dst_buffer: AudioFrame*, frame_count: int) virtual where the engine calls out to the GDExtension with an array pointer, but no examples I have found where a GDExtension calls into the engine with an array pointer. My attempts to implement this function using: bool AudioStreamPlaybackMicrophone::::mix_microphone(GDExtensionConstPtr<AudioFrame> p_buffer, int p_frames);
ClassDB::bind_method(D_METHOD("mix_microphone", "p_buffer", "frames"), &AudioStreamPlaybackMicrophone::mix_microphone); do not compile, so I have left the call to the bind_method commented out until it's possible to find an answer. |
This solves all the issues raised in the proposal: godotengine/godot-proposals#11347
Changes made:
My new
AudioDriver::input_start_count
counter is to prevent multiple calls toAudioDriver::input_start()
from different AudioStreamPlaybackMicrophone instances that result in multiple conflicting processes to popping data from the same buffer.Change the return type of
AudioDriver::get_input_buffer()
toVector<int32_t>&
to avoid copying out entire bufferto access it.
GDREGISTER_CLASS(AudioStreamPlaybackMicrophone)
was missing fromregister_server_types.cpp
Expose functions
start()
,stop()
andis_playing()
fromAudioStreamPlaybackMicrophone
to GDScriptNew function
PackedVector2Array AudioStreamPlaybackMicrophone::get_microphone_buffer(int p_frames)
. This does the same as PackedVector2Array AudioEffectCapture::get_buffer(frames: int) but it fetches the frames directly from theAudioDriver::input_buffer
without going through an AudioStream, AudioBus and an AudioEffect, all of which operate on the duty cycle of the Audio system output frequency.Need help with:
I would like to expose
int AudioStreamPlaybackMicrophone::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames)
to GDExtension, but this has an
AudioFrame*
pointer in its parameters listThis ought to be possible, because there is already one like this with int AudioStreamPlaybackResampled::_mix_resampled(dst_buffer: AudioFrame*, frame_count: int),
but it is created by the special virtual function template
GDVIRTUAL2R(int, _mix_resampled, GDExtensionPtr<AudioFrame>, int)