cMyFirstEffect.h

#pragma once
#include "cEffectBase.h"
#include "cDelayLine.h"
#include "BiquadFilter.h"

#define DECLARE_EFFECT cMyFirstEffect __Effect
#define EFFECT_NAME "MyFirstEffect"
#define EFFECT_VERSION "Version 1.0"
#define EFFECT_SPLATCH_SCREEN "Template.png"
constexpr uint32_t EFFECT_BUILD =   BUILD_ID('F', 'I', 'R', '1');

class cMyFirstEffect : public DadEffect::cEffectBase {
public:
    // -------------------------------------------------------------------------
    // Constructor - performs no initialization by itself
    // -------------------------------------------------------------------------
    cMyFirstEffect() = default;

    // -------------------------------------------------------------------------
    // Initializes DSP components and user interface parameters
    // -------------------------------------------------------------------------
    void onInitialize() override;

    // -------------------------------------------------------------------------
    // Returns the unique effect identifier
    // -------------------------------------------------------------------------
    uint32_t getEffectID() override;

    // -------------------------------------------------------------------------
    // Audio processing function - processes one input/output audio buffer
    // -------------------------------------------------------------------------
    void onProcess(AudioBuffer *pIn, AudioBuffer *pOut, DadGUI::eEffectState_t State, bool Silence) override;

    // -------------------------------------------------------------------------
    // Gain callback
    // -------------------------------------------------------------------------
    void static onGainChange(DadDSP::cParameter* pGainParameter, uint32_t Data);

    // -------------------------------------------------------------------------
    // Pan callback
    // -------------------------------------------------------------------------
    void static onPanChange(DadDSP::cParameter* pBalanceParameter, uint32_t Data);

    // -------------------------------------------------------------------------
    // Mix callback
    // -------------------------------------------------------------------------
    void static onMixChange(DadDSP::cParameter* pMixParameter, uint32_t Data);

    // -------------------------------------------------------------------------
    // Treble callback
    // -------------------------------------------------------------------------
    void static onTrebleChange(DadDSP::cParameter* pFreqTrebleParameter, uint32_t Data);

    // -------------------------------------------------------------------------
    // Bass callback
    // -------------------------------------------------------------------------
    void static onBassChange(DadDSP::cParameter* pFreqBassParameter, uint32_t Data);


    // -------------------------------------------------------------------------
    // Compute left/right channel gains using a logarithmic (power) curve
    // -------------------------------------------------------------------------
     void CalcGainLog();

protected:
    // =========================================================================
    // Protected Member Variables
    // =========================================================================

    // -------------------------------------------------------------------------
    // Parameter declarations
    // -------------------------------------------------------------------------
    DadGUI::cUIParameter                m_ParameterGain;        // Gain control parameter
    DadGUI::cUIParameter                m_ParameterPan;         // Stereo Panning parameter
    DadGUI::cUIParameter                m_ParameterStereoMode;  // Stereo mode parameter
    DadGUI::cUIParameter                m_DelayTime;            // Time parameter
    DadGUI::cUIParameter                m_DelayFeedback;        // Feedback parameter
    DadGUI::cUIParameter                m_DelayMix;             // Mix parameter
    DadGUI::cUIParameter                m_DelayTreble;          // Treble parameter
    DadGUI::cUIParameter                m_DelayBass;            // Bass parameter

    // -------------------------------------------------------------------------
    // Parameter view declarations
    // -------------------------------------------------------------------------
    DadGUI::cParameterNumNormalView     m_ParameterGainView;        // GUI view for the gain parameter
    DadGUI::cParameterNumLeftRightView  m_ParameterPanView;         // GUI view for Panning parameter
    DadGUI::cParameterDiscretView       m_ParameterStereoModeView;  // GUI view for stereo mode parameter
    DadGUI::cParameterNumNormalView     m_DelayTimeView;            // GUI view for Time parameter
    DadGUI::cParameterNumNormalView     m_DelayFeedbackView;        // GUI view for Feedback parameter
    DadGUI::cParameterNumNormalView     m_DelayMixView;             // GUI view for Mix parameter
    DadGUI::cParameterNumNormalView     m_DelayTrebleView;          // GUI view for Treble parameter
    DadGUI::cParameterNumNormalView     m_DelayBassView;            // GUI view for Bass parameter

    // -------------------------------------------------------------------------
    // Panel declarations
    // -------------------------------------------------------------------------
    DadGUI::cPanelOfParameterView       m_DelayPanel;           // Delay panel
    DadGUI::cPanelOfParameterView       m_FilterPanel;          // Filter panel
    DadGUI::cPanelOfParameterView       m_ParameterFirstPanel;  // Demo panel containing parameter views


    // -------------------------------------------------------------------------
    // Delay lines
    // -------------------------------------------------------------------------
    DadDSP::cDelayLine                m_LeftDelayLine;          // Left delay line
    DadDSP::cDelayLine                m_RightDelayLine;         // Right delay line

    // -------------------------------------------------------------------------
    // Biquad Filter
    // -------------------------------------------------------------------------
    DadDSP::cBiQuad                   m_TrebleFilter;           // Left canal treble filter
    DadDSP::cBiQuad                   m_BassFilter;             // Left canal bass filter


    // -------------------------------------------------------------------------
    // Variables
    // -------------------------------------------------------------------------
    float   m_RightGain;  // Right gain calculated
    float   m_LeftGain;   // Left gain calculated
    float   m_PrevDelaySample;
};

cMyFirstEffect.cpp

#include "EffectsConfig.h"

#if ACTIVE_EFFECT == MY_FIRST_EFFECT
#include "cMyFirstEffect.h"
#include <cmath>

// Unique effect identifier (32-bit)
constexpr uint32_t MY_FIRST_EFFECT_ID = BUILD_ID('M', 'F', 'E', '1');

// Delay
constexpr float DELAY_MAX_TIME = 2.0f;  // Maximum delay time in milliseconds
constexpr float DELAY_MIN_TIME = 0.1f;  // Minimum delay time in milliseconds
constexpr float MAX_CHANGE_DELAY_SAMPLE = 0.50f;	// Maximum variation in the number of sample delays
// Filter
constexpr float MAX_TREBLE_FILTER = 15000.0f;
constexpr float MIN_TREBLE_FILTER = 1500.0f;
constexpr float MAX_BASS_FILTER = 500.0f;
constexpr float MIN_BASS_FILTER = 50.0f;

// Calculate buffer size based on sampling rate and max delay time
constexpr uint32_t DELAY_BUFFER_SIZE = static_cast<uint32_t>((SAMPLING_RATE * DELAY_MAX_TIME) + 0.999f);

// Allocate delay buffers in SDRAM (extra 100 samples for interpolation safety)
SDRAM_SECTION float __DelayBufferLeft[DELAY_BUFFER_SIZE + 100];   // Left channel delay buffer
SDRAM_SECTION float __DelayBufferRight[DELAY_BUFFER_SIZE + 100];  // Right channel delay buffer



// -----------------------------------------------------------------------------
// Method: onInitialize
// Description: Called once when the effect is loaded. Used to initialize
//              parameters, GUI elements, and internal DSP objects.
// -----------------------------------------------------------------------------
void cMyFirstEffect::onInitialize()
{
    // Initialize the Gain parameter
    m_ParameterGain.Init(MY_FIRST_EFFECT_ID,// Serialize ID
                         100.0f,            // Default value
                         0.0f,              // Minimum value
                         100.0f,            // Maximum value
                         5.0f,              // Fast increment
                         1.0f,              // Slow increment
                         onGainChange,      // Callback function
                         (uint32_t)this,    // Callback user data
                         1.0f,              // Transition time (seconds)
                         20);               // MIDI CC number

    // Initialize the Gain parameter
    m_ParameterPan.Init(MY_FIRST_EFFECT_ID, // Serialize ID
                         0.0f,              // Default value
                         -100.0f,           // Minimum value
                         100.0f,            // Maximum value
                         5.0f,              // Fast increment
                         1.0f,              // Slow increment
                         onPanChange,       // Callback function
                         (uint32_t)this,    // Callback user data
                         1.0f,              // Transition time (seconds)
                         21);               // MIDI CC number

    // Initialize the Gain parameter
    m_ParameterStereoMode.Init(MY_FIRST_EFFECT_ID,// Serialize ID
                         0.0f,              // Default value
                         0.0f,              // Minimum value
                         0.0f,              // Maximum value
                         1.0f,              // Fast increment
                         1.0f,              // Slow increment
                         nullptr,           // Callback function
                         0,                 // Callback user data
                         0.0f,              // Transition time (seconds)
                         22);               // MIDI CC number

    // Initialize the Time parameter
    m_DelayTime.Init(MY_FIRST_EFFECT_ID,    // Serialize ID
                         0.35f,             // Default value
                         DELAY_MIN_TIME,    // Minimum value
                         DELAY_MAX_TIME,    // Maximum value
                         0.15f,             // Fast increment
                         0.05f,             // Slow increment
                         nullptr,           // Callback function
                         0,                 // Callback user data
                         0.0f,              // Transition time (seconds)
                         23);               // MIDI CC number

    // Initialize the Feedback parameter
    m_DelayFeedback.Init(MY_FIRST_EFFECT_ID,// Serialize ID
                         35.0f,             // Default value
                         0.0f,              // Minimum value
                         100.0f,            // Maximum value
                         10.0f,             // Fast increment
                         1.0f,              // Slow increment
                         nullptr,           // Callback function
                         0,                 // Callback user data
                         1.0f,              // Transition time (seconds)
                         24);               // MIDI CC number

    // Initialize the Mix parameter
    m_DelayMix.Init(MY_FIRST_EFFECT_ID,     // Serialize ID
                         30.0f,             // Default value
                         0.0f,              // Minimum value
                         100.0f,            // Maximum value
                         10.0f,             // Fast increment
                         1.0f,              // Slow increment
                         onMixChange,       // Callback function
                         (uint32_t)this,    // Callback user data
                         1.0f,              // Transition time (seconds)
                         25);               // MIDI CC number

    // Initialize the Treble parameter
    m_DelayTreble.Init(MY_FIRST_EFFECT_ID,  // Serialize ID
                         -50.0f,            // Default value
                         0.0f,              // Minimum value
                         -100.0f,           // Maximum value
                         5.0f,              // Fast increment
                         1.0f,              // Slow increment
                         onTrebleChange,    // Callback function
                         (uint32_t)this,    // Callback user data
                         1.0f,              // Transition time (seconds)
                         26);               // MIDI CC number

    // Initialize the Bass parameter
    m_DelayBass.Init(MY_FIRST_EFFECT_ID,    // Serialize ID
                         -50.0f,            // Default value
                         0.0f,              // Minimum value
                         -100.0f,           // Maximum value
                         5.0f,              // Fast increment
                         1.0f,              // Slow increment
                         onBassChange,      // Callback function
                         (uint32_t)this,    // Callback user data
                         1.0f,              // Transition time (seconds)
                         27);               // MIDI CC number

    // Initialize the GUI view for the parameter
    m_ParameterGainView.Init(&m_ParameterGain,// Linked parameter
                             "Gain",        // Short name
                             "Gain",        // Long name
                             "%",           // Unit (short)
                             "percent");    // Unit (long)

    // Initialize the GUI view for the parameter
    m_ParameterPanView.Init(&m_ParameterPan,// Linked parameter
                             "Pan",         // Short name
                             "Pan control", // Long name
                             "%",           // Unit (short)
                             "percent");    // Unit (long)

    // Initialize the GUI view for the parameter
    m_ParameterStereoModeView.Init(&m_ParameterStereoMode, // Linked parameter
                             "Mode",        // Short name
                             "Stereo Mode");// Long name

    m_ParameterStereoModeView.AddDiscreteValue(
                            "Stereo",       // ShortDiscretValue
                            "Stereo");      // LongDiscretValue

    m_ParameterStereoModeView.AddDiscreteValue(
                            "Mono",         // ShortDiscretValue
                            "Mono");        // LongDiscretValue

    // Initialize the GUI view for the parameter
    m_DelayTimeView.Init(& m_DelayTime,     // Linked parameter
                             "Time",        // Short name
                             "Time",        // Long name
                             "s",           // Unit (short)
                             "seconds");    // Unit (long)

    // Initialize the GUI view for the parameter
    m_DelayFeedbackView.Init(&m_DelayFeedback,  // Linked parameter
                             "Feed.",       // Short name
                             "Feedback",    // Long name
                             "%",           // Unit (short)
                             "percent");    // Unit (long)

    // Initialize the GUI view for the parameter
    m_DelayMixView.Init(&m_DelayMix,        // Linked parameter
                             "Mix",         // Short name
                             "Mix",         // Long name
                             "%",           // Unit (short)
                             "percent");    // Unit (long)

    // Initialize the GUI view for the parameter
    m_DelayTrebleView.Init(&m_DelayTreble,	// Linked parameter
                             "Treble",      // Short name
                             "Treble",      // Long name
                             "%",           // Unit (short)
                             "percent");	// Unit (long)

    // Initialize the GUI view for the parameter
    m_DelayBassView.Init(&m_DelayBass,      // Linked parameter
                             "Bass",        // Short name
                             "Bass",        // Long name
                             "%",           // Unit (short)
                             "percent");    // Unit (long)

    // Create a parameter panel for the user interface
    m_ParameterFirstPanel.Init(&m_ParameterGainView,      // Parameter View 1
                              &m_ParameterPanView,        // Parameter View 2
                              &m_ParameterStereoModeView);// Parameter View 3

    #ifndef HARD_DRYWET
    m_DelayPanel.Init(      &m_DelayTimeView,             // Parameter View 1
                            &m_DelayFeedbackView,         // Parameter View 2
                            &m_DelayMixView);             // Parameter View 3
    #else
    m_DelayPanel.Init(      &m_DelayTimeView,             // Parameter View 1
                            nullptr,
                            &m_DelayFeedbackView);        // Parameter View 3
    #endif

    m_FilterPanel.Init(   &m_DelayBassView,             // Parameter View 1
                          nullptr,                      // Parameter View 2
                          &m_DelayTrebleView);          // Parameter View 3

    // Add the panel to the effect's menu
    m_Menu.addMenuItem(&m_DelayPanel, "Delay");
    m_Menu.addMenuItem(&m_FilterPanel, "Filter");
    m_Menu.addMenuItem(&m_ParameterFirstPanel, "First");

    // Delay lines initialization
    m_LeftDelayLine.Initialize(__DelayBufferLeft, DELAY_BUFFER_SIZE);
    m_RightDelayLine.Initialize(__DelayBufferRight, DELAY_BUFFER_SIZE);

    // Filter initialization
    m_TrebleFilter.Initialize(SAMPLING_RATE, MAX_TREBLE_FILTER, 0.0f, 1.7f, DadDSP::FilterType::LPF24);
    m_BassFilter.Initialize(SAMPLING_RATE, MIN_BASS_FILTER, 0.0f, 1.7f, DadDSP::FilterType::HPF24);

    // Initialize Tap Tempo
    m_pTapTempoParameter = &m_DelayTime;
    m_TempoType = DadGUI::eTempoType::period;

    // Initialize PrevDelayTime
    m_PrevDelaySample =  m_DelayTime.getTargetValue() * SAMPLING_RATE;

    #ifdef HARD_DRYWET
       __DryWet.setMix(100.0);
    #endif
}

// -----------------------------------------------------------------------------
// Method: getEffectID
// Description: Returns the unique 32-bit identifier of the effect.
// -----------------------------------------------------------------------------
uint32_t cMyFirstEffect::getEffectID()
{
    return MY_FIRST_EFFECT_ID;
}

// -----------------------------------------------------------------------------
// Method: onProcess
// Description: Main audio processing function. Called continuously for
//              every audio buffer.
// -----------------------------------------------------------------------------
void cMyFirstEffect::onProcess(AudioBuffer *pIn, AudioBuffer *pOut, DadGUI::eEffectState_t State, bool Silence)
{
    float Left = pIn->Left;
    float Right = pIn->Right;

    // Test stereo mode
    if(m_ParameterStereoMode.getValue() == 1.0f){
    	// convert to mono
    	Left = (Left + Right) / 2.0f;
    	Right = (Right + Left) / 2.0f;
    }

    // Convert delay time to samples
    float DelaySample = m_DelayTime * SAMPLING_RATE;

    // Slew rate limitation of the delay time in number of samples
    float Delta = DelaySample - m_PrevDelaySample;

    if (Delta > MAX_CHANGE_DELAY_SAMPLE) {
        DelaySample = m_PrevDelaySample + MAX_CHANGE_DELAY_SAMPLE;
    } else if (Delta < -MAX_CHANGE_DELAY_SAMPLE) {
        DelaySample = m_PrevDelaySample - MAX_CHANGE_DELAY_SAMPLE;
    }
    m_PrevDelaySample = DelaySample;

    // Reading the delayed line with a DelaySample delay
    float DelayLeftOut = m_LeftDelayLine.Pull(DelaySample);
    float DelayRightOut = m_RightDelayLine.Pull(DelaySample);

    // Retrieve the Feedback parameter value and scale it (multiplied by 0.008f)
    // to get a suitable attenuation factor and prevent the delay line from
    // saturating or exploding too quickly
    float FeedBack = m_DelayFeedback.getValue() * 0.008f;

    float RightFeedbackInput;
    float LeftFeedbackInput;
    // Compute the feedback signal depending on the effect state
    if (State == DadGUI::eEffectState_t::on)
    {
        // Effect ON → Loop back the input signal + echoes read from the delay line
        RightFeedbackInput = Right + DelayRightOut;
        LeftFeedbackInput  = Left  + DelayLeftOut;
    }
    else
    {
        // Effect OFF → Loop back only the echoes for a natural decay
        RightFeedbackInput = DelayRightOut;
        LeftFeedbackInput  = DelayLeftOut;
    }

    // Apply treble and bass filters to the feedback signal
    RightFeedbackInput = m_TrebleFilter.Process(RightFeedbackInput, DadDSP::eChannel::Right);
    RightFeedbackInput = m_BassFilter.Process(RightFeedbackInput,   DadDSP::eChannel::Right);

    LeftFeedbackInput = m_TrebleFilter.Process(LeftFeedbackInput, DadDSP::eChannel::Left);
    LeftFeedbackInput = m_BassFilter.Process(LeftFeedbackInput,   DadDSP::eChannel::Left);

    // Inject the feedback back into the delay lines with the user-defined attenuation
    m_RightDelayLine.Push(RightFeedbackInput * FeedBack);
    m_LeftDelayLine.Push(LeftFeedbackInput  * FeedBack);

    // Apply wet gain to the outputs. The 1.25f factor compensates for the minimum
    // feedback attenuation (0.008f)
    float GainWet = __DryWet.getGainWet();

    pOut->Left  = DelayLeftOut  * GainWet * m_LeftGain  * 1.25f;
    pOut->Right = DelayRightOut * GainWet * m_RightGain * 1.25f;}

// -------------------------------------------------------------------------
// Compute left/right channel gains using a logarithmic (power) curve
// -------------------------------------------------------------------------
void cMyFirstEffect::CalcGainLog()
{
    // Retrieve normalized parameter values [0.0 .. 1.0]
    float Gain    = m_ParameterGain.getNormalizedValue();
    float Pan = m_ParameterPan.getNormalizedValue();

    // Constant-power panning law: apply a square-root curve (exponent 0.5)
    float gain_left  = std::pow(1.0f - Pan, 0.5f); // fades out as pan moves right
    float gain_right = std::pow(Pan,         0.5f); // fades in  as pan moves right

    // Apply the master gain on top of each channel's pan gain
    m_LeftGain  = Gain * gain_left;
    m_RightGain = Gain * gain_right;
}

// -------------------------------------------------------------------------
// Gain callback
// -------------------------------------------------------------------------
void cMyFirstEffect::onGainChange(DadDSP::cParameter* pGainParameter, uint32_t Data){

	// Cast Data to cMyFirstEffect instance pointer
	cMyFirstEffect* pThis = (cMyFirstEffect *) Data;

	pThis->CalcGainLog();
}

// -------------------------------------------------------------------------
// Pan callback
// -------------------------------------------------------------------------
void cMyFirstEffect::onPanChange(DadDSP::cParameter* pPanParameter, uint32_t Data){
	cMyFirstEffect* pThis = (cMyFirstEffect *) Data;

	pThis->CalcGainLog();
}

// -------------------------------------------------------------------------
// Mix callback
// -------------------------------------------------------------------------
void cMyFirstEffect::onMixChange(DadDSP::cParameter* pMixParameter, uint32_t Data){
    #ifndef HARD_DRYWET
    __DryWet.setMix(pMixParameter->getValue());
    #endif
}

// -------------------------------------------------------------------------
// Treble callback
// -------------------------------------------------------------------------
void cMyFirstEffect::onTrebleChange(DadDSP::cParameter* pFreqTrebleParameter, uint32_t Data){
	cMyFirstEffect* pThis = (cMyFirstEffect *) Data;

	float TrebleInv = 1 - pFreqTrebleParameter->getNormalizedValue();
	float cutoffFreq = MIN_TREBLE_FILTER * std::pow(MAX_TREBLE_FILTER/MIN_TREBLE_FILTER, TrebleInv);
	pThis->m_TrebleFilter.setCutoffFreq(cutoffFreq);
	pThis->m_TrebleFilter.CalculateParameters();
}

// -------------------------------------------------------------------------
// Bass callback
// -------------------------------------------------------------------------
void cMyFirstEffect::onBassChange(DadDSP::cParameter* pFreqBassParameter, uint32_t Data){
	cMyFirstEffect* pThis = (cMyFirstEffect *) Data;

	float cutoffFreq = MIN_BASS_FILTER * std::pow(MAX_BASS_FILTER/MIN_BASS_FILTER, pFreqBassParameter->getNormalizedValue());
	pThis->m_BassFilter.setCutoffFreq(cutoffFreq);
	pThis->m_BassFilter.CalculateParameters();
}
#endif

This site uses Just the Docs, a documentation theme for Jekyll.