verbfx freeverb

This commit is contained in:
chriskiefer 2026-03-18 16:29:54 +00:00
parent 5d38e36e0e
commit 6c66802b4e
5 changed files with 97 additions and 88 deletions

View file

@ -1,3 +1,8 @@
//machine config
#define JOYSTICK_IS_4D false
#define MEMLNAUT_ANALOG_INPUTS 3 + (JOYSTICK_IS_4D ? 1 : 0)
#define MEMLNAUT_INPUT_MODE InterfaceRL::INPUT_MODES::JOYSTICK
//hardware //hardware
#include "src/memllib/utils/perf.hpp" #include "src/memllib/utils/perf.hpp"
@ -31,9 +36,9 @@
// #define MEMLNAUT_MODE_TYPE MEMLNautModeSoundAnalysisMIDI // #define MEMLNAUT_MODE_TYPE MEMLNautModeSoundAnalysisMIDI
// #define MEMLNAUT_MODE_TYPE MEMLNautModeXIASRI // #define MEMLNAUT_MODE_TYPE MEMLNautModeXIASRI
// #define MEMLNAUT_MODE_TYPE MEMLNautModeVerbFX #define MEMLNAUT_MODE_TYPE MEMLNautModeVerbFX
// #define MEMLNAUT_MODE_TYPE MEMLNautModeBreakOr // #define MEMLNAUT_MODE_TYPE MEMLNautModeBreakOr
#define MEMLNAUT_MODE_TYPE MEMLNautModeElysiamorfs // #define MEMLNAUT_MODE_TYPE MEMLNautModeElysiamorfs
// #define MEMLNAUT_MODE_TYPE MEMLNautModeChannelStrip // #define MEMLNAUT_MODE_TYPE MEMLNautModeChannelStrip
// #define MEMLNAUT_MODE_TYPE MEMLNautModePAFSynth // #define MEMLNAUT_MODE_TYPE MEMLNautModePAFSynth

View file

@ -213,7 +213,7 @@ public:
firstParamsReceived = true; firstParamsReceived = true;
if (queue_try_remove(&bpmQueue, &bpm)) { if (queue_try_remove(&bpmQueue, &bpm)) {
updateBPM(bpm); updateBPM(bpm);
Serial.printf("BPM updated: %f\n", bpm); // Serial.printf("BPM updated: %f\n", bpm);
} }
int sequencerControl; int sequencerControl;
if (queue_try_remove(&sequencerControlQueue, &sequencerControl)) { if (queue_try_remove(&sequencerControlQueue, &sequencerControl)) {
@ -263,8 +263,8 @@ public:
v.modFreq = (0.25f + params[paramIdx++] * 0.75f) * 0.25f; // 1 to 10 v.modFreq = (0.25f + params[paramIdx++] * 0.75f) * 0.25f; // 1 to 10
v.modIndex = params[paramIdx++] * 4.f; // 0 to 10 v.modIndex = params[paramIdx++] * 4.f; // 0 to 10
v.fbLevel = 0.f;//params[paramIdx++] ; // 0 to 1 v.fbLevel = 0.f;//params[paramIdx++] ; // 0 to 1
static float muls[4] = {1.f}; static float muls[4] = {1.f,2.f, 3.f,4.f};
v.phasorMul = 1.f;//muls[(int)(params[paramIdx++])]; v.phasorMul = muls[(int)(params[paramIdx++]* 3.999f) ];
v.phaseOffset = ((int)(params[paramIdx++] * timeSigBeats)) * timeSigBeatsInv; v.phaseOffset = ((int)(params[paramIdx++] * timeSigBeats)) * timeSigBeatsInv;
} }
// Serial.printf("pm: %f", ratioSeqStates[0].phasorMul); // Serial.printf("pm: %f", ratioSeqStates[0].phasorMul);

View file

@ -52,72 +52,65 @@ public:
__attribute__((hot)) stereosample_t __force_inline Process(const stereosample_t x) override __attribute__((hot)) stereosample_t __force_inline Process(const stereosample_t x) override
{ {
float mix = x.L + x.R; float mix = x.L + x.R;
mix *= 2.0f;
smoother.Process(neuralNetOutputs.data(), smoothParams.data()); smoother.Process(neuralNetOutputs.data(), smoothParams.data());
float dl1mix = smoothParams[0] * 0.4f;
float dl2mix = smoothParams[1] * 0.4f;
float dl3mix = smoothParams[2] * 0.8f;
float dl4mix = smoothParams[22] * smoothParams[22] * 0.41f;
float allp1fb = smoothParams[4] * 0.99f; // float allp1fb = smoothParams[4] * 0.99f;
float allp2fb = smoothParams[5] * 0.99f; // float allp2fb = smoothParams[5] * 0.99f;
float comb1fb = (smoothParams[6] * 0.95f); // float allp3fb = smoothParams[14] * 0.99f;
float comb2fb = (smoothParams[7] * 0.95f);
float dl1fb = (smoothParams[8] * 0.95f); wetdry_mix_ = (smoothParams[0] * 0.9f) + 0.1f;
float dl2fb = (smoothParams[9] * 0.95f); const float lp0fb = smoothParams[1] * 0.98f;
float dl3fb = (smoothParams[10] * 0.95f); const float lp0cutoff = (smoothParams[2] * 0.5f) + 0.05f;
float dl4fb = (smoothParams[23] * 0.95f);
// Wet-dry mix between 0.2 and 1
wetdry_mix_ = (smoothParams[11] * 0.7f) + 0.3f;
// Pitch shift transposition between -12 and +12 semitones
// float pitchshift_transpose = (smoothParams[12] * 24.f) - 12.f; // Scale to -12 to +12 semitones
pitchshifter_.SetTransposition(12.f + smoothParams[12]);
//pitchshifter_.SetTransposition(-5.f);
// Set pitch shifter mix
pitchshifter_mix_ = smoothParams[13] * 0.99f;
float allp3fb = smoothParams[14] * 0.99f; const float lp1fb = smoothParams[3] * 0.98f;
float allp4fb = smoothParams[15] * 0.99f; const float lp1cutoff = (smoothParams[4] * 0.5f) + 0.05f;
float allp5fb = smoothParams[16] * 0.99f;
float allp6fb = smoothParams[17] * 0.99f;
float allp3fbmix = smoothParams[18];
float allp4fbmix = smoothParams[19];
float allp5fbmix = smoothParams[20];
float allp6fbmix = smoothParams[21];
// // PROCESS const float lp2fb = smoothParams[5] * 0.98f;
float pitchshifted = pitchshifter_.Process(mix); const float lp2cutoff = (smoothParams[6] * 0.5f) + 0.05f;
// // Mix the original signal with the pitch-shifted signal
pitchshifted = (mix * (1.f - pitchshifter_mix_)) + (pitchshifted * pitchshifter_mix_);
float y = dcb.play(pitchshifted, 0.99f) * 2.f; const float lp3fb = smoothParams[7] * 0.98f;
// float y = dcb.play(pitchshifted, 0.99f) * 3.f; const float lp3cutoff = (smoothParams[8] * 0.5f) + 0.05f;
float y1 = allp1.allpass(y, 30, allp1fb);
y1 = comb1.combfb(y1, 127, comb1fb);
float y2 = allp2.allpass(y, 482, allp2fb); const float lp4fb = smoothParams[9] * 0.98f;
y2 = comb2.combfb(y2, 808, comb2fb); const float lp4cutoff = (smoothParams[10] * 0.5f) + 0.05f;
float y3 = allp3.allpass(y, 19, allp3fb) * allp3fbmix; const float lp5fb = smoothParams[11] * 0.98f;
float y4 = allp4.allpass(y, 69, allp4fb) * allp4fbmix; const float lp5cutoff = (smoothParams[12] * 0.5f) + 0.05f;
float y5 = allp5.allpass(y, 131, allp5fb) * allp5fbmix;
float y6 = allp6.allpass(y, 287, allp6fb) * allp6fbmix; const float lp6fb = smoothParams[13] * 0.98f;
y = y1 + y2 + y3 + y4 +y5 +y6; const float lp6cutoff = (smoothParams[14] * 0.5f) + 0.05f;
float d1 = (dl1.play(y, 3500, dl1fb) * dl1mix);
float d2 = (dl2.play(y, 7886, dl2fb) * dl2mix); const float lp7fb = smoothParams[15] * 0.98f;
float d3 = (dl3.play(y, 299, dl3fb) * dl3mix); const float lp7cutoff = (smoothParams[16] * 0.5f) + 0.05f;
float d4 = (dl4.play(y, 15873, dl4fb) * dl4mix);
float lp0 = lpcomb0.lpcombfb(mix, SIZE_comb0, lp0fb, lp0cutoff);
float lp1 = lpcomb1.lpcombfb(mix, SIZE_comb1, lp1fb, lp1cutoff);
float lp2 = lpcomb2.lpcombfb(mix, SIZE_comb2, lp2fb, lp2cutoff);
float lp3 = lpcomb3.lpcombfb(mix, SIZE_comb3, lp3fb, lp3cutoff);
float lp4 = lpcomb4.lpcombfb(mix, SIZE_comb4, lp4fb, lp4cutoff);
float lp5 = lpcomb5.lpcombfb(mix, SIZE_comb5, lp5fb, lp5cutoff);
float lp6 = lpcomb6.lpcombfb(mix, SIZE_comb6, lp6fb, lp6cutoff);
float lp7 = lpcomb7.lpcombfb(mix, SIZE_comb7, lp7fb, lp7cutoff);
float y = lp0 + lp1 + lp2 + lp3 + lp4 + lp5 + lp6 + lp7;
y = y + d1 + d2 + d3; const float allp0fb = smoothParams[17] * 0.98f;
const float allp1fb = smoothParams[18] * 0.98f;
const float allp2fb = smoothParams[19] * 0.98f;
const float allp3fb = smoothParams[20] * 0.98f;
y = allp0.allpass(y, SIZE_allp0, allp0fb);
y = allp1.allpass(y, SIZE_allp1, allp1fb);
y = allp2.allpass(y, SIZE_allp2, allp2fb);
y = allp3.allpass(y, SIZE_allp3, allp3fb);
// Mix dry // Mix dry
y = (y * wetdry_mix_) + (mix * (1.f - wetdry_mix_)); y = (y * wetdry_mix_) + (mix * (1.f - wetdry_mix_));
y = tanhf(y*1.2f);
stereosample_t ret { y, y }; stereosample_t ret { y, y };
return ret; return ret;
@ -127,7 +120,6 @@ public:
{ {
AudioAppBase<NPARAMS>::Setup(sample_rate, interface); AudioAppBase<NPARAMS>::Setup(sample_rate, interface);
maxiSettings::sampleRate = sample_rate; maxiSettings::sampleRate = sample_rate;
pitchshifter_.Init(sample_rate);
} }
__attribute__((always_inline)) void ProcessParams(const std::array<float, NPARAMS>& params) __attribute__((always_inline)) void ProcessParams(const std::array<float, NPARAMS>& params)
@ -141,25 +133,37 @@ protected:
std::array<float,NPARAMS> neuralNetOutputs{0}, smoothParams{0}; std::array<float,NPARAMS> neuralNetOutputs{0}, smoothParams{0};
// https://ccrma.stanford.edu/~jos/pasp/Freeverb.html
static constexpr size_t SIZE_allp0=244;
static constexpr size_t SIZE_allp1=605;
static constexpr size_t SIZE_allp2=479;
static constexpr size_t SIZE_allp3=371;
maxiDelayline<5000> dl1; maxiReverbFilters<SIZE_allp0> allp0;
maxiDelayline<8000> dl2; maxiReverbFilters<SIZE_allp1> allp1;
maxiDelayline<1201> dl3; maxiReverbFilters<SIZE_allp2> allp2;
maxiDelayline<16000> dl4; maxiReverbFilters<SIZE_allp3> allp3;
maxiReverbFilters<300> allp1; static constexpr size_t SIZE_comb0=1694;
maxiReverbFilters<500> allp2; static constexpr size_t SIZE_comb1=1759;
maxiReverbFilters<500> allp3; static constexpr size_t SIZE_comb2=1622;
maxiReverbFilters<500> allp4; static constexpr size_t SIZE_comb3=1547;
maxiReverbFilters<500> allp5; static constexpr size_t SIZE_comb4=1379;
maxiReverbFilters<500> allp6; static constexpr size_t SIZE_comb5=1464;
maxiReverbFilters<200> comb1; static constexpr size_t SIZE_comb6=1283;
maxiReverbFilters<900> comb2; static constexpr size_t SIZE_comb7=1205;
maxiReverbFilters<SIZE_comb0> lpcomb0;
maxiReverbFilters<SIZE_comb1> lpcomb1;
maxiReverbFilters<SIZE_comb2> lpcomb2;
maxiReverbFilters<SIZE_comb3> lpcomb3;
maxiReverbFilters<SIZE_comb4> lpcomb4;
maxiReverbFilters<SIZE_comb5> lpcomb5;
maxiReverbFilters<SIZE_comb6> lpcomb6;
maxiReverbFilters<SIZE_comb7> lpcomb7;
maxiDCBlocker dcb; maxiDCBlocker dcb;
daisysp::PitchShifter pitchshifter_;
float pitchshifter_mix_{0.5f};
float wetdry_mix_{0.5f}; float wetdry_mix_{0.5f};
OnePoleSmoother<kN_Params> smoother{150.f, kSampleRate}; OnePoleSmoother<kN_Params> smoother{150.f, kSampleRate};

View file

@ -25,7 +25,7 @@ public:
InterfaceRL interface; InterfaceRL interface;
std::shared_ptr<InterfaceRL> interfacePtr; std::shared_ptr<InterfaceRL> interfacePtr;
ElysiamorfAudioApp<>::SequencerClockModes clockMode = ElysiamorfAudioApp<>::MIDI_CLOCK; ElysiamorfAudioApp<>::SequencerClockModes clockMode = ElysiamorfAudioApp<>::INTERNAL;
bool sequencerPlaying = false; bool sequencerPlaying = false;

View file

@ -15,18 +15,18 @@
class MEMLNautModeVerbFX { class MEMLNautModeVerbFX {
public: public:
constexpr static size_t kN_InputParams = XiasriAnalysis::kN_Params +4; //ML + joystick constexpr static size_t kN_InputParams = MEMLNAUT_ANALOG_INPUTS;
InterfaceRL interface; InterfaceRL interface;
std::shared_ptr<InterfaceRL> interfacePtr; std::shared_ptr<InterfaceRL> interfacePtr;
XiasriAnalysis mlAnalysis{kSampleRate}; // XiasriAnalysis mlAnalysis{kSampleRate};
SharedBuffer<float, XiasriAnalysis::kN_Params> machine_list_buffer; // SharedBuffer<float, XiasriAnalysis::kN_Params> machine_list_buffer;
VerbFXAudioApp<> audioAppVerbFX; VerbFXAudioApp<> audioAppVerbFX;
std::shared_ptr<MIDIInOut> midi_interf; std::shared_ptr<MIDIInOut> midi_interf;
void setupInterface() { void setupInterface() {
interface.setup(kN_InputParams, VerbFXAudioApp<>::kN_Params); interface.setup(kN_InputParams, VerbFXAudioApp<>::kN_Params);
interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK_AND_MACHINE_LISTENING); interface.bindInterface(MEMLNAUT_INPUT_MODE, JOYSTICK_IS_4D);
interfacePtr = make_non_owning(interface); interfacePtr = make_non_owning(interface);
} }
@ -51,7 +51,7 @@ public:
void setupAudio(float sample_rate) { void setupAudio(float sample_rate) {
audioAppVerbFX.Setup(sample_rate, interfacePtr); audioAppVerbFX.Setup(sample_rate, interfacePtr);
// Reinitialize XiasriAnalysis filters after maxiSettings is properly configured // Reinitialize XiasriAnalysis filters after maxiSettings is properly configured
mlAnalysis.ReinitFilters(); // mlAnalysis.ReinitFilters();
} }
__force_inline void loop() { __force_inline void loop() {
@ -59,21 +59,21 @@ public:
} }
__force_inline void analyse(stereosample_t x) { __force_inline void analyse(stereosample_t x) {
union { // union {
XiasriAnalysis::parameters_t p; // XiasriAnalysis::parameters_t p;
float v[XiasriAnalysis::kN_Params]; // float v[XiasriAnalysis::kN_Params];
} param_u; // } param_u;
param_u.p = mlAnalysis.Process(x.L + x.R); // param_u.p = mlAnalysis.Process(x.L + x.R);
// Write params into shared_buffer // // Write params into shared_buffer
machine_list_buffer.writeNonBlocking(param_u.v, XiasriAnalysis::kN_Params); // machine_list_buffer.writeNonBlocking(param_u.v, XiasriAnalysis::kN_Params);
} }
__force_inline void processAnalysisParams() { __force_inline void processAnalysisParams() {
// Read SharedBuffer // // Read SharedBuffer
std::vector<float> mlist_params(XiasriAnalysis::kN_Params, 0); // std::vector<float> mlist_params(XiasriAnalysis::kN_Params, 0);
machine_list_buffer.readNonBlocking(mlist_params); // machine_list_buffer.readNonBlocking(mlist_params);
// Send parameters to RL interface // // Send parameters to RL interface
interface.readAnalysisParameters(mlist_params); // interface.readAnalysisParameters(mlist_params);
// PERIODIC_RUN( // PERIODIC_RUN(
// Serial.printf("%f %f %f\n", mlist_params[0], mlist_params[1], mlist_params[2]); // Serial.printf("%f %f %f\n", mlist_params[0], mlist_params[1], mlist_params[2]);
// , 100); // , 100);