Looking for resources for creating a custom AudioEffect

Godot Version

4.3

Question

I’m trying to figure out how to create my own AudioEffect using GDExtensions in C++. I was able to complete the GDExtension tutorial, but I can’t seem to figure out what to extend and how. Even searching the forums has not yielded any results related to AudioEffects.

A reddit post I found said to extend AudioEffect and AudioEffectInstance, and also provided a link to the engine’s implementations of effects, but the imports in the godotengine repository do not line up with godot-cpp’s. Additionally, godot-cpp doesn’t even reference AudioEffectInstances except in the extension api to show that they exist, no mention of how they’re used there. As a result, I’m not even sure if mimicking the godotengine implementations will work, even if I manage to fix the imports.

Is there anyone here that can provide a working example of a custom AudioEffect and/or AudioEffectInstance using GDExtension, show me how to convert an existing effect from the godotengine repo into one I can use with godot-cpp, or even just point me to a resource that explains how/what I need to do it myself?

Originally posted this question to Reddit yesterday, but I don’t expect I’ll receive an answer there.

AudioEffect is the inteface, AudioEffectInstance is the thing that will hold all the logic to process audio, and is wrapped by Audio Effect.

you should just be able to copy paste any effect code into godot-cpp to create your own extension. the Audio server looks for all AudioEffects registered in the class database.

But As you said if there are missing includes, you got to get those into the build.

but once you have the dependencies, its just extend an audioeffect class and an audioeffectInstance class and register it just like other effects do. GDREGISTER_CLASS

example from the main source code:
godot/servers/audio/effects/audio_effect_amplify.cpp
godot/servers/audio/effects/audio_effect_amplify.h

all it seems you need is “servers/audio/audio_effect.h” for the base class definitions.

I’m working on this right now for fun, once you build the gdextension the first time it generates a header for you called godot-cpp/gen/include/godot_cpp/classes/audio_effect.hpp

Thank you, it took me a while to figure out which files to import ("godot_cpp/classes/..." instead of "servers/audio/..."), and some of the method definitions do not line up (process and instantiate), but it does look like I might be able to do this now.

Edit: Does look like the AudioFrame class in godotengine has been changed to a struct in godot-cpp. Not sure how I’ll get around that…

It took me a while to figure out the extension quirks, but I have got a working plugin. I used the existing amplifiereffect as reference to make MyAmp and MyAmpInstance classes.

MyAmp Prototype


header

my_amp.hpp
#ifndef MYAMP_H
#define MYAMP_H

#include <godot_cpp/classes/audio_effect.hpp>
#include <godot_cpp/classes/audio_effect_instance.hpp>



namespace godot {

class MyAmp;

class MyAmpInstance : public AudioEffectInstance {
	GDCLASS(MyAmpInstance, AudioEffectInstance);
	friend class MyAmp;
	Ref<MyAmp> base;

	float mix_volume_db;

protected:
	static void _bind_methods();

public:
	void _process(const void *p_src_buffer, AudioFrame *p_dst_buffer, int32_t p_frame_count) override;
};

class MyAmp : public AudioEffect {
	GDCLASS(MyAmp, AudioEffect);

	friend class MyAmpInstance;
	float volume_db;

protected:
	static void _bind_methods();

public:
	Ref<AudioEffectInstance> _instantiate() override;
	void set_volume_db(float p_volume);
	float get_volume_db() const;

	MyAmp();
    ~MyAmp();
};

}

#endif

to solve the void * p_src_buffer i static casted into an AudioFrame pointer and operate with the left/right struct variables because AudioFrame in gdextension doesn’t have the godot-src class math operator overloads for some reason.

I don’t understand the const void * in the _process parameters. nor many of the quirks of the gdextension class mechanics.

Also names for Math::db_to_linear needed to change to Math::db2linear, need different names for some reason.

source

my_amp.cpp
#include "my_amp.h"

using namespace godot;

void MyAmpInstance::_process(const void *p_src_buffer, AudioFrame *p_dst_buffer, int32_t p_frame_count)  {
	//multiply volume interpolating to avoid clicks if this changes
	float volume_db = base->volume_db;
	float vol = Math::db2linear(mix_volume_db);
	float vol_inc = (Math::db2linear(volume_db) - vol) / float(p_frame_count);
    const AudioFrame *src = static_cast<const AudioFrame*>(p_src_buffer);
	for (int i = 0; i < p_frame_count; i++) {
		p_dst_buffer[i].left = src[i].left * vol;
		p_dst_buffer[i].right = src[i].right * vol;
		vol += vol_inc;
	}
	//set volume for next mix
	mix_volume_db = volume_db;
}
void MyAmpInstance::_bind_methods() {
}

Ref<AudioEffectInstance> MyAmp::_instantiate() {
	Ref<MyAmpInstance> ins;
	ins.instantiate();
	ins->base = Ref<MyAmp>(this);
	ins->mix_volume_db = volume_db;
	return ins;
}

void MyAmp::set_volume_db(float p_volume) {
	volume_db = p_volume;
}

float MyAmp::get_volume_db() const {
	return volume_db;
}

void MyAmp::_bind_methods() {
	ClassDB::bind_method(D_METHOD("set_volume_db", "volume"), &MyAmp::set_volume_db);
	ClassDB::bind_method(D_METHOD("get_volume_db"), &MyAmp::get_volume_db);

	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_db", PROPERTY_HINT_RANGE, "-80,24,0.01,suffix:dB"), "set_volume_db", "get_volume_db");
}

MyAmp::MyAmp() {
	volume_db = 0;
}

MyAmp::~MyAmp() {
}


register_types.cpp

finally I added to the register_types.cpp

GDREGISTER_CLASS(MyAmpInstance);
GDREGISTER_CLASS(MyAmp);

everything else stays the same as the GDExample from the Godot docs.

1 Like