Looking for resources for creating a custom AudioEffect

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