#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;
// Modulation
constexpr float MIN_MODULATION_SPEED = 0.25f;
constexpr float MAX_MODULATION_SPEED = 8.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 Modulation seed parameter
m_ModulationSpeed.Init(MY_FIRST_EFFECT_ID,// Serialize ID
2.5f, // Default value
MIN_MODULATION_SPEED,// Minimum value
MAX_MODULATION_SPEED,// Maximum value
0.5f, // Fast increment
0.1f, // Slow increment
onModSpeedChange, // Callback function
(uint32_t)this, // Callback user data
1.0f, // Transition time (seconds)
28); // MIDI CC number
// Initialize the Modulation Depth parameter
m_ModulationDepth.Init(MY_FIRST_EFFECT_ID,// Serialize ID
40.0f, // Default value
0.0f, // Minimum value
100.0f, // Maximum value
5.0f, // Fast increment
1.0f, // Slow increment
nullptr, // Callback function
0, // Callback user data
1.0f, // Transition time (seconds)
29); // 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)
// Initialize the GUI view for the parameter
m_ModulationSpeedView.Init(&m_ModulationSpeed,// Linked parameter
"Speed", // Short name
"Speed", // Long name
"Hz", // Unit (short)
"Hz"); // Unit (long)
// Initialize the GUI view for the parameter
m_ModulationDepthView.Init(&m_ModulationDepth,// Linked parameter
"Depth", // Short name
"Depth", // 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
m_ModulationPanel.Init(&m_ModulationDepthView, // Parameter View 1
nullptr, // Parameter View 2
&m_ModulationSpeedView); // 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_ModulationPanel, "Mod.");
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;
// Initialize LFO
m_DCOLeft.Initialize(SAMPLING_RATE, 0.5f, MIN_MODULATION_SPEED, MAX_MODULATION_SPEED, 0.5f);
m_DCORight.Initialize(SAMPLING_RATE, 0.5f, MIN_MODULATION_SPEED, MAX_MODULATION_SPEED, 0.5f);
#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;
// DCO step
m_DCOLeft.Step();
m_DCORight.Step();
// 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;
// We apply the modulation
float DelayL = DelaySample - (m_DCOLeft.getTriangleValue() * m_ModulationDepth); // Modulated left delay
float DelayR = DelaySample - (m_DCORight.getTriangleValue() * m_ModulationDepth); // Modulated right delay
// Reading the delayed line with a DelaySample delay
float DelayLeftOut = m_LeftDelayLine.Pull(DelayL);
float DelayRightOut = m_RightDelayLine.Pull(DelayR);
// 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();
}
// -------------------------------------------------------------------------
// Modulation speed callback
// -------------------------------------------------------------------------
void cMyFirstEffect::onModSpeedChange(DadDSP::cParameter* pModSpeedParameter, uint32_t Data){
cMyFirstEffect* pThis = (cMyFirstEffect *) Data;
float DCOSpeed = pModSpeedParameter->getValue();
pThis->m_DCOLeft.setFreq(DCOSpeed);
pThis->m_DCORight.setFreq(DCOSpeed + 0.02f);
}
#endif