Godot 3.2 will get new audio features
By: Juan Linietsky 10 April 2019
This article is from April 2019, some of its contents might be outdated and no longer accurate.
You can find up-to-date information about the engine in the official documentation.
I left these out of 3.0 and never managed to work on it again, yet users requested them a lot this past year. Finally, they will be available for Godot 3.2.
This is a new type of AudioStream that can be put in the regular stream players (normal, 2D and 3D): AudioStreamGenerator. Just put it anywhere and then get the Playback object, in this case AudioStreamGeneratorPlayback.
var playback = $StreamPlayer.get_stream_playback()
Once this object is obtained, push stereo audio frames to it. You can do this by supplying Vector2 objects via buffers or individual audio frames. From GDScript, it’s simpler to push individual frames (though less efficient). GDNative and C# allow pushing buffers.
Here is some example code on how to fill the buffer with a sine wave:
func _fill_buffer(): var increment = (1.0 / (hz / pulse_hz)) var to_fill = playback.get_frames_available() while (to_fill > 0): playback.push_frame( Vector2(1.0,1.0) * sin(phase * (PI * 2.0)) phase = fmod((phase + increment), 1.0) to_fill-=1;
Just call this function before AudioStreamPlayer.play() (to ensure buffer is full before playback begins), and then call it from _process(), to ensure it remains full.
There is also a new effect named AudioEffectSpectrumAnalyzer. It can be set as an audio bus insert. Once there, just obtain the AudioEffectSpectrumAnalyzerInstance instance via API:
# This gets the first effect in the Master bus (0) spectrum = AudioServer.get_bus_effect_instance(0,0)
This instance synchronizes with the audio playback, so the magnitudes of the frequency ranges should be exactly what is playing right now (or as close as possible). This uses Fast Fourier Transform, so you can change the buffer size to a smaller one for more accurate timing (but less accurate frequency information).
To get the magnitude of a frequency range, use the following function:
var f = spectrum.get_magnitude_for_frequency_range(from_hz,to_hz)
This returns a normalized value in linear scale (DC at 0, saturation at 1). Use the linear2db() helper to convert to Decibels if needed.
Here is a simple example for drawing a spectrum, using linear frequencies:
var w = WIDTH / VU_COUNT var prev_hz = 0 for i in range(1,VU_COUNT+1): var hz = i * FREQ_MAX / VU_COUNT; var f = spectrum.get_magnitude_for_frequency_range(prev_hz,hz) var energy = clamp((MIN_DB + linear2db(f.length()))/MIN_DB,0,1) var height = energy * HEIGHT draw_rect(Rect2(w*i,HEIGHT-height,w,height),Color(1,1,1)) prev_hz = hz
And a video of this code in action:
Example code for both projects can be found in the demo repository.
I wanted to add a BPM detector for rhythm games, but all the open source code I found is GPL (incompatible with Godot MIT), so we will probably need to roll our own. I am too busy for this at the time, but if you want to help contribute to one, let me know (I can lend a hand explaining how it’s done).
As mentioned before, this month I will be working in adding missing features for Godot 3.2. Next month I will start the Vulkan port. If you are not yet, please consider becoming our patron. It’s easy, and you help the project enormously, even if just a small donation.