#include "EffectsConfig.h"
#if ACTIVE_EFFECT == MY_FIRST_EFFECT
#include "cMyFirstEffect.h"
// 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 seconds
constexpr float DELAY_MIN_TIME = 0.1f; // Minimum delay time in seconds
// 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
2.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
50.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 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)
"second"); // 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)
// 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
// Add the panel to the effect's menu
m_Menu.addMenuItem(&m_DelayPanel, "Delay");
m_Menu.addMenuItem(&m_ParameterFirstPanel, "First");
// Delay lines initialization
m_LeftDelayLine.Initialize(__DelayBufferLeft, DELAY_BUFFER_SIZE);
m_RightDelayLine.Initialize(__DelayBufferRight, DELAY_BUFFER_SIZE);
#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;
// 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;
}
// 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
}
#endif