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.