diff --git a/src/effect_3bandeq.h b/src/effect_3bandeq.h new file mode 100644 index 0000000..2d604b3 --- /dev/null +++ b/src/effect_3bandeq.h @@ -0,0 +1,176 @@ +/* + * DISTHRO 3 Band EQ + * Ported from https://github.com/DISTRHO/Mini-Series/blob/master/plugins/3BandEQ + * + * Javier Nonis (https://github.com/jnonis) - 2024 + */ +#ifndef _EFFECT_3BANDEQ_H +#define _EFFECT_3BANDEQ_H + +#include +#include "effect_base.h" + +class AudioEffect3BandEQ : public AudioEffect +{ +public: + static constexpr float kAMP_DB = 8.656170245f; + static constexpr float kDC_ADD = 1e-30f; + static constexpr float kPI = 3.141592654f; + + enum Param + { + BYPASS, + EQ_LOW, + EQ_MID, + EQ_HIGH, + MASTER, + LOW_MID_FQ, + MID_HIGH_FQ, + UNKNOWN + }; + + AudioEffect3BandEQ(float32_t samplerate) : AudioEffect(samplerate) + { + // Default values + fLow = 0.0f; + fMid = 0.0f; + fHigh = 0.0f; + fMaster = 0.0f; + fLowMidFreq = 440.0f; + fMidHighFreq = 2000.0f; + + // Internal stuff + lowVol = midVol = highVol = outVol = 1.0f; + freqLP = 200.0f; + freqHP = 2000.0f; + + // reset filter values + xLP = std::exp(-2.0f * kPI * freqLP / samplerate); + + a0LP = 1.0f - xLP; + b1LP = -xLP; + + xHP = std::exp(-2.0f * kPI * freqHP / samplerate); + a0HP = 1.0f - xHP; + b1HP = -xHP; + + out1LP = out2LP = out1HP = out2HP = 0.0f; + tmp1LP = tmp2LP = tmp1HP = tmp2HP = 0.0f; + } + + virtual ~AudioEffect3BandEQ() + { + } + + virtual unsigned getId() + { + return EFFECT_3BANDEQ; + } + + virtual void setParameter(unsigned param, unsigned value) + { + switch (param) + { + case AudioEffect3BandEQ::Param::BYPASS: + this->setBypass(value == 1); + break; + case AudioEffect3BandEQ::Param::EQ_LOW: + fLow = (value / 100.0f) * 48.0f - 24.0f; + lowVol = std::exp( (fLow/48.0f) * 48.0f / kAMP_DB); + break; + case AudioEffect3BandEQ::Param::EQ_MID: + fMid = (value / 100.0f) * 48.0f - 24.0f; + midVol = std::exp( (fMid/48.0f) * 48.0f / kAMP_DB); + break; + case AudioEffect3BandEQ::Param::EQ_HIGH: + fHigh = (value / 100.0f) * 48.0f - 24.0f; + highVol = std::exp( (fHigh/48.0f) * 48.0f / kAMP_DB); + break; + case AudioEffect3BandEQ::Param::MASTER: + fMaster = (value / 100.0f) * 48.0f - 24.0f; + outVol = std::exp( (fMaster/48.0f) * 48.0f / kAMP_DB); + break; + case AudioEffect3BandEQ::Param::LOW_MID_FQ: + fLowMidFreq = std::min((float) value, fMidHighFreq); + freqLP = fLowMidFreq; + xLP = std::exp(-2.0f * kPI * freqLP / (float)samplerate); + a0LP = 1.0f - xLP; + b1LP = -xLP; + break; + case AudioEffect3BandEQ::Param::MID_HIGH_FQ: + fMidHighFreq = std::max((float) value, fLowMidFreq); + freqHP = fMidHighFreq; + xHP = std::exp(-2.0f * kPI * freqHP / (float)samplerate); + a0HP = 1.0f - xHP; + b1HP = -xHP; + break; + default: + break; + } + } + + virtual unsigned getParameter(unsigned param) + { + switch (param) + { + case AudioEffect3BandEQ::Param::BYPASS: + return this->getBypass() ? 1 : 0; + case AudioEffect3BandEQ::Param::EQ_LOW: + return roundf(((fLow + 24.0f) / 48.0f) * 100.0f); + case AudioEffect3BandEQ::Param::EQ_MID: + return roundf(((fMid + 24.0f) / 48.0f) * 100.0f); + case AudioEffect3BandEQ::Param::EQ_HIGH: + return roundf(((fHigh + 24.0f) / 48.0f) * 100.0f); + case AudioEffect3BandEQ::Param::MASTER: + return roundf(((fMaster + 24.0f) / 48.0f) * 100.0f); + case AudioEffect3BandEQ::Param::LOW_MID_FQ: + return fLowMidFreq; + case AudioEffect3BandEQ::Param::MID_HIGH_FQ: + return fMidHighFreq; + default: + return 0; + } + } + +protected: + virtual size_t getParametersSize() + { + return AudioEffect3BandEQ::Param::UNKNOWN; + } + virtual void doProcess(const float32_t* inblockL, const float32_t* inblockR, float32_t* outblockL, float32_t* outblockR, uint16_t len) + { + const float* in1 = inblockL; + const float* in2 = inblockR; + float* out1 = outblockL; + float* out2 = outblockR; + + for (uint32_t i=0; i < len; ++i) + { + tmp1LP = a0LP * in1[i] - b1LP * tmp1LP + kDC_ADD; + tmp2LP = a0LP * in2[i] - b1LP * tmp2LP + kDC_ADD; + out1LP = tmp1LP - kDC_ADD; + out2LP = tmp2LP - kDC_ADD; + + tmp1HP = a0HP * in1[i] - b1HP * tmp1HP + kDC_ADD; + tmp2HP = a0HP * in2[i] - b1HP * tmp2HP + kDC_ADD; + out1HP = in1[i] - tmp1HP - kDC_ADD; + out2HP = in2[i] - tmp2HP - kDC_ADD; + + out1[i] = (out1LP*lowVol + (in1[i] - out1LP - out1HP)*midVol + out1HP*highVol) * outVol; + out2[i] = (out2LP*lowVol + (in2[i] - out2LP - out2HP)*midVol + out2HP*highVol) * outVol; + } + } +private: + float fLow, fMid, fHigh, fMaster, fLowMidFreq, fMidHighFreq; + + float lowVol, midVol, highVol, outVol; + float freqLP, freqHP; + + float xLP, a0LP, b1LP; + float xHP, a0HP, b1HP; + + float out1LP, out2LP, out1HP, out2HP; + float tmp1LP, tmp2LP, tmp1HP, tmp2HP; +}; + +#endif // _EFFECT_3BANDEQ_H diff --git a/src/effect_base.h b/src/effect_base.h index 01d559c..303f37c 100644 --- a/src/effect_base.h +++ b/src/effect_base.h @@ -17,6 +17,7 @@ #define EFFECT_TALREVERB3 6 #define EFFECT_REVERB 7 #define EFFECT_MVERB 8 +#define EFFECT_3BANDEQ 9 class AudioEffect { diff --git a/src/effects.h b/src/effects.h index 2053774..ddb90bf 100644 --- a/src/effects.h +++ b/src/effects.h @@ -15,6 +15,7 @@ #include "effect_talreverb3.h" #include "effect_platervbstereo.h" #include "effect_mverb.h" +#include "effect_3bandeq.h" inline AudioEffect* newAudioEffect(unsigned type, float32_t samplerate) { @@ -36,6 +37,8 @@ inline AudioEffect* newAudioEffect(unsigned type, float32_t samplerate) return new AudioEffectPlateReverb(samplerate); case EFFECT_MVERB: return new AudioEffectMVerb(samplerate); + case EFFECT_3BANDEQ: + return new AudioEffect3BandEQ(samplerate); case EFFECT_NONE: default: return new AudioEffectNone(samplerate); @@ -54,6 +57,7 @@ inline std::string getFXTypeName(int nValue) case EFFECT_TALREVERB3: return "TalRvrb3"; case EFFECT_REVERB: return "Reverb"; case EFFECT_MVERB: return "MVerb"; + case EFFECT_3BANDEQ: return "3Band EQ"; case EFFECT_NONE: default: return "None"; } diff --git a/src/uimenu.cpp b/src/uimenu.cpp index 507ba39..d54dd93 100644 --- a/src/uimenu.cpp +++ b/src/uimenu.cpp @@ -252,6 +252,18 @@ CUIMenu::TMenuItem CUIMenu::s_FXMVerb[] = {0} }; +CUIMenu::TMenuItem CUIMenu::s_FX3BandEQ[] = +{ + {"Bypass", EditTGFXParameter, 0, AudioEffect3BandEQ::Param::BYPASS}, + {"Low", EditTGFXParameter, 0, AudioEffect3BandEQ::Param::EQ_LOW}, + {"Mid", EditTGFXParameter, 0, AudioEffect3BandEQ::Param::EQ_MID}, + {"High", EditTGFXParameter, 0, AudioEffect3BandEQ::Param::EQ_HIGH}, + {"Master", EditTGFXParameter, 0, AudioEffect3BandEQ::Param::MASTER}, + {"Low Mid FQ", EditTGFXParameter, 0, AudioEffect3BandEQ::Param::LOW_MID_FQ}, + {"Mid High FQ", EditTGFXParameter, 0, AudioEffect3BandEQ::Param::MID_HIGH_FQ}, + {0} +}; + const CUIMenu::TMenuItem CUIMenu::s_MidiFX[] = { {"Type:", EditTGParameter2, 0, CMiniDexed::TGParameterMidiFXType}, @@ -351,7 +363,7 @@ const CUIMenu::TMenuItem CUIMenu::s_SaveMenu[] = const CUIMenu::TParameter CUIMenu::s_GlobalParameter[CMiniDexed::ParameterUnknown] = { {0, 1, 1, ToOnOff}, // ParameterCompressorEnable - {0, 8, 1, ToFXType}, // ParameterSendFXType + {0, 9, 1, ToFXType}, // ParameterSendFXType {0, 100, 1}, // ParameterSendFXLevel {0, 1, 1, ToOnOff}, // ParameterReverbEnable {0, 99, 1}, // ParameterReverbSize @@ -374,7 +386,7 @@ const CUIMenu::TParameter CUIMenu::s_TGParameter[CMiniDexed::TGParameterUnknown] {0, CSysExFileLoader::VoicesPerBank-1, 1}, // TGParameterProgram {0, 127, 8, ToVolume}, // TGParameterVolume {0, 127, 8, ToPan}, // TGParameterPan - {0, 8, 1, ToFXType}, // TGParameterInsertFXType + {0, 9, 1, ToFXType}, // TGParameterInsertFXType {0, 1, 1, ToMidiFXType}, // TGParameterMidiFXType {-99, 99, 1}, // TGParameterMasterTune {0, 99, 1}, // TGParameterCutoff @@ -481,7 +493,7 @@ const CUIMenu::TParameter CUIMenu::s_TGFXReverbParam[AudioEffectPlateReverb::Par {0, 99, 1}, // LEVEL }; -// must match AudioEffectPlateReverb::Param +// must match AudioEffectMVerb::Param const CUIMenu::TParameter CUIMenu::s_TGFXMVerbParam[AudioEffectMVerb::Param::UNKNOWN] = { {0, 1, 1, ToOnOff}, // BYPASS @@ -496,6 +508,18 @@ const CUIMenu::TParameter CUIMenu::s_TGFXMVerbParam[AudioEffectMVerb::Param::UNK {0, 100, 1}, // EARLYMIX }; +// must match AudioEffect3BandEQ::Param +const CUIMenu::TParameter CUIMenu::s_TGFX3BandEQParam[AudioEffect3BandEQ::Param::UNKNOWN] = +{ + {0, 1, 1, ToOnOff}, // BYPASS + {0, 100, 1}, // LOW + {0, 100, 1}, // MID + {0, 100, 1}, // HIGH + {0, 100, 1}, // MASTER + {0, 1000, 10}, // LOW_MID_FQ + {1000, 20000, 10}, // MID_HIGH_FQ +}; + // must match MidiArp::Param const CUIMenu::TParameter CUIMenu::s_TGMidiFXArpParam[MidiArp::Param::UNKNOWN] = { @@ -2938,6 +2962,9 @@ CUIMenu::TMenuItem* CUIMenu::getFXMenuItem(unsigned type) case EFFECT_MVERB: menu = s_FXMVerb; break; + case EFFECT_3BANDEQ: + menu = s_FX3BandEQ; + break; case EFFECT_NONE: default: menu = s_FXNone; @@ -2999,6 +3026,9 @@ CUIMenu::TParameter CUIMenu::getFXParameter(unsigned type, unsigned nParam) case EFFECT_MVERB: pParam = s_TGFXMVerbParam[nParam]; break; + case EFFECT_3BANDEQ: + pParam = s_TGFX3BandEQParam[nParam]; + break; default: break; } diff --git a/src/uimenu.h b/src/uimenu.h index 0ca3b6d..7b9490a 100644 --- a/src/uimenu.h +++ b/src/uimenu.h @@ -183,6 +183,7 @@ private: static TMenuItem s_FXTalReverb3[]; static TMenuItem s_FXReverb[]; static TMenuItem s_FXMVerb[]; + static TMenuItem s_FX3BandEQ[]; static TMenuItem s_MidiFXNone[]; static TMenuItem s_MidiFXArp[]; @@ -207,6 +208,7 @@ private: static const TParameter s_TGFXTalReverb3Param[]; static const TParameter s_TGFXReverbParam[]; static const TParameter s_TGFXMVerbParam[]; + static const TParameter s_TGFX3BandEQParam[]; static const TParameter s_TGMidiFXArpParam[]; static const TParameter s_VoiceParameter[]; static const TParameter s_OPParameter[];