diff --git a/src/AudioEffectAnalogDelay.h b/src/AudioEffectAnalogDelay.h
index 7fd2475..d84ad79 100644
--- a/src/AudioEffectAnalogDelay.h
+++ b/src/AudioEffectAnalogDelay.h
@@ -30,6 +30,10 @@
namespace BAGuitar {
+/// The number of stages in the analog-response Biquad filter
+constexpr unsigned MAX_NUM_FILTER_STAGES = 4;
+constexpr unsigned NUM_COEFFS_PER_STAGE = 5;
+
/**************************************************************************//**
* AudioEffectAnalogDelay models BBD based analog delays. It provides controls
* for delay, feedback (or regen), mix and output level. All parameters can be
@@ -50,9 +54,8 @@ public:
NUM_CONTROLS ///< this can be used as an alias for the number of MIDI controls
};
- AudioEffectAnalogDelay() = delete;
-
// *** CONSTRUCTORS ***
+ AudioEffectAnalogDelay() = delete;
/// Construct an analog delay using internal memory by specifying the maximum
/// delay in milliseconds.
@@ -130,6 +133,18 @@ public:
/// @param value the CC value from 0 to 127
void processMidi(int channel, int midiCC, int value);
+ /// Override the default coefficients with your own. The number of filters stages affects how
+ /// much CPU is consumed.
+ /// @details The effect uses the CMSIS-DSP library for biquads which requires coefficents
+ /// be in q31 format, which means they are 32-bit signed integers representing -1.0 to slightly
+ /// less than +1.0. The coeffShift parameter effectively multiplies the coefficients by 2^shift.
+ /// Example: If you really want +1.5, must instead use +0.75 * 2^1, thus 0.75 in q31 format is
+ /// (0.75 * 2^31) = 1610612736 and coeffShift = 1.
+ /// @param numStages the actual number of filter stages you want to use. Must be <= MAX_NUM_FILTER_STAGES.
+ /// @param coeffs pointer to an integer array of coefficients in q31 format.
+ /// @param coeffShift Coefficient scaling factor = 2^coeffShift.
+ void setFilterCoeffs(int numStages, const int32_t *coeffs, int coeffShift);
+
virtual void update(void); ///< update automatically called by the Teesny Audio Library
private:
@@ -154,7 +169,13 @@ private:
void m_preProcessing(audio_block_t *out, audio_block_t *dry, audio_block_t *wet);
void m_postProcessing(audio_block_t *out, audio_block_t *dry, audio_block_t *wet);
- size_t m_callCount = 0;
+ // Coefficients
+ void m_constructFilter(void);
+// int m_numStages;
+// int m_coeffShift;
+// int m_coeffs[MAX_NUM_FILTER_STAGES*NUM_COEFFS_PER_STAGE] = {};
+
+ //size_t m_callCount = 0;
};
}
diff --git a/src/LibBasicFunctions.h b/src/LibBasicFunctions.h
index b035031..0507e43 100644
--- a/src/LibBasicFunctions.h
+++ b/src/LibBasicFunctions.h
@@ -199,6 +199,13 @@ public:
IirBiQuadFilter(unsigned numStages, const int32_t *coeffs, int coeffShift = 0);
virtual ~IirBiQuadFilter();
+ /// Reconfigure the filter coefficients.
+ /// @details See CMSIS-DSP documentation for more details
+ /// @param numStages number of biquad stages. Each stage has 5 coefficients.
+ /// @param coeffs pointer to an array of Q31 fixed-point coefficients (range -1 to +0.999...)
+ /// @param coeffShift coeffs are multiplied by 2^coeffShift to support coefficient range scaling
+ void changeFilterCoeffs(unsigned numStages, const int32_t *coeffs, int coeffShift = 0);
+
/// Process the data using the configured IIR filter
/// @details output and input can be the same pointer if in-place modification is desired
/// @param output pointer to where the output results will be written
@@ -227,9 +234,16 @@ public:
/// @param numStages number of biquad stages. Each stage has 5 coefficients.
/// @param coeffs pointer to an array of Q31 fixed-point coefficients (range -1 to +0.999...)
/// @param coeffShift coeffs are multiplied by 2^coeffShift to support coefficient range scaling
- IirBiQuadFilterHQ(unsigned numStages, const int32_t *coeffs, int coeffShift = 0);
+ IirBiQuadFilterHQ(unsigned maxNumStages, const int32_t *coeffs, int coeffShift = 0);
virtual ~IirBiQuadFilterHQ();
+ /// Reconfigure the filter coefficients.
+ /// @details See CMSIS-DSP documentation for more details
+ /// @param numStages number of biquad stages. Each stage has 5 coefficients.
+ /// @param coeffs pointer to an array of Q31 fixed-point coefficients (range -1 to +0.999...)
+ /// @param coeffShift coeffs are multiplied by 2^coeffShift to support coefficient range scaling
+ void changeFilterCoeffs(unsigned numStages, const int32_t *coeffs, int coeffShift = 0);
+
/// Process the data using the configured IIR filter
/// @details output and input can be the same pointer if in-place modification is desired
/// @param output pointer to where the output results will be written
@@ -257,10 +271,16 @@ public:
/// Construct a Biquad filter with specified number of stages and coefficients
/// @details See CMSIS-DSP documentation for more details
/// @param numStages number of biquad stages. Each stage has 5 coefficients.
- /// @param coeffs pointer to an array of Q31 fixed-point coefficients (range -1 to +0.999...)
- IirBiQuadFilterFloat(unsigned numStages, const float *coeffs);
+ /// @param coeffs pointer to an array of single-precision floating-point coefficients
+ IirBiQuadFilterFloat(unsigned maxNumStages, const float *coeffs);
virtual ~IirBiQuadFilterFloat();
+ /// Reconfigure the filter coefficients.
+ /// @details See CMSIS-DSP documentation for more details
+ /// @param numStages number of biquad stages. Each stage has 5 coefficients.
+ /// @param coeffs pointer to an array of single-precision floating-point coefficients
+ void changeFilterCoeffs(unsigned numStages, const float *coeffs);
+
/// Process the data using the configured IIR filter
/// @details output and input can be the same pointer if in-place modification is desired
/// @param output pointer to where the output results will be written
diff --git a/src/common/IirBiquadFilter.cpp b/src/common/IirBiquadFilter.cpp
index 59b5432..f2ae1c7 100644
--- a/src/common/IirBiquadFilter.cpp
+++ b/src/common/IirBiquadFilter.cpp
@@ -26,14 +26,17 @@ namespace BAGuitar {
////////////////////////////////////////////////////
// IirBiQuadFilter
////////////////////////////////////////////////////
-IirBiQuadFilter::IirBiQuadFilter(unsigned numStages, const int32_t *coeffs, int coeffShift)
-: NUM_STAGES(numStages)
+constexpr int NUM_COEFFS_PER_STAGE = 5;
+constexpr int NUM_STATES_PER_STAGE = 4;
+IirBiQuadFilter::IirBiQuadFilter(unsigned maxNumStages, const int32_t *coeffs, int coeffShift)
+: NUM_STAGES(maxNumStages)
{
- m_coeffs = new int32_t[5*numStages];
- memcpy(m_coeffs, coeffs, 5*numStages * sizeof(int32_t));
+ m_coeffs = new int32_t[NUM_COEFFS_PER_STAGE*maxNumStages];
+ //memcpy(m_coeffs, coeffs, 5*numStages * sizeof(int32_t));
- m_state = new int32_t[4*numStages];
- arm_biquad_cascade_df1_init_q31(&m_iirCfg, numStages, m_coeffs, m_state, coeffShift);
+ m_state = new int32_t[NUM_STATES_PER_STAGE*maxNumStages];
+ //arm_biquad_cascade_df1_init_q31(&m_iirCfg, numStages, m_coeffs, m_state, coeffShift);
+ changeFilterCoeffs(maxNumStages, coeffs, coeffShift);
}
IirBiQuadFilter::~IirBiQuadFilter()
@@ -42,6 +45,15 @@ IirBiQuadFilter::~IirBiQuadFilter()
if (m_state) delete [] m_state;
}
+void IirBiQuadFilter::changeFilterCoeffs(unsigned numStages, const int32_t *coeffs, int coeffShift)
+{
+ // clear the state
+ memset(m_state, 0, sizeof(int32_t) * NUM_COEFFS_PER_STAGE * numStages);
+ // copy the coeffs
+ memcpy(m_coeffs, coeffs, NUM_COEFFS_PER_STAGE*numStages * sizeof(int32_t));
+ arm_biquad_cascade_df1_init_q31(&m_iirCfg, numStages, m_coeffs, m_state, coeffShift);
+}
+
bool IirBiQuadFilter::process(int16_t *output, int16_t *input, size_t numSamples)
{
@@ -67,15 +79,18 @@ bool IirBiQuadFilter::process(int16_t *output, int16_t *input, size_t numSamples
return true;
}
+///////////////////////////////////
// HIGH QUALITY
-IirBiQuadFilterHQ::IirBiQuadFilterHQ(unsigned numStages, const int32_t *coeffs, int coeffShift)
-: NUM_STAGES(numStages)
+///////////////////////////////////
+IirBiQuadFilterHQ::IirBiQuadFilterHQ(unsigned maxNumStages, const int32_t *coeffs, int coeffShift)
+: NUM_STAGES(maxNumStages)
{
- m_coeffs = new int32_t[5*numStages];
- memcpy(m_coeffs, coeffs, 5*numStages * sizeof(int32_t));
+ m_coeffs = new int32_t[NUM_COEFFS_PER_STAGE*maxNumStages];
+ //memcpy(m_coeffs, coeffs, 5*numStages * sizeof(int32_t));
- m_state = new int64_t[4*numStages];;
- arm_biquad_cas_df1_32x64_init_q31(&m_iirCfg, numStages, m_coeffs, m_state, coeffShift);
+ m_state = new int64_t[NUM_STATES_PER_STAGE*maxNumStages];;
+ //arm_biquad_cas_df1_32x64_init_q31(&m_iirCfg, numStages, m_coeffs, m_state, coeffShift);
+ changeFilterCoeffs(maxNumStages, coeffs, coeffShift);
}
IirBiQuadFilterHQ::~IirBiQuadFilterHQ()
@@ -84,6 +99,15 @@ IirBiQuadFilterHQ::~IirBiQuadFilterHQ()
if (m_state) delete [] m_state;
}
+void IirBiQuadFilterHQ::changeFilterCoeffs(unsigned numStages, const int32_t *coeffs, int coeffShift)
+{
+ // clear the state
+ memset(m_state, 0, sizeof(int32_t) * NUM_COEFFS_PER_STAGE * numStages);
+ // copy the coeffs
+ memcpy(m_coeffs, coeffs, NUM_COEFFS_PER_STAGE*numStages * sizeof(int32_t));
+ arm_biquad_cas_df1_32x64_init_q31(&m_iirCfg, numStages, m_coeffs, m_state, coeffShift);
+}
+
bool IirBiQuadFilterHQ::process(int16_t *output, int16_t *input, size_t numSamples)
{
@@ -109,15 +133,18 @@ bool IirBiQuadFilterHQ::process(int16_t *output, int16_t *input, size_t numSampl
return true;
}
+///////////////////////
// FLOAT
-IirBiQuadFilterFloat::IirBiQuadFilterFloat(unsigned numStages, const float *coeffs)
-: NUM_STAGES(numStages)
+///////////////////////
+IirBiQuadFilterFloat::IirBiQuadFilterFloat(unsigned maxNumStages, const float *coeffs)
+: NUM_STAGES(maxNumStages)
{
- m_coeffs = new float[5*numStages];
- memcpy(m_coeffs, coeffs, 5*numStages * sizeof(float));
+ m_coeffs = new float[NUM_COEFFS_PER_STAGE*maxNumStages];
+ //memcpy(m_coeffs, coeffs, NUM_COEFFS_PER_STAGE*maxNumStages * sizeof(float));
- m_state = new float[4*numStages];;
- arm_biquad_cascade_df2T_init_f32(&m_iirCfg, numStages, m_coeffs, m_state);
+ m_state = new float[NUM_STATES_PER_STAGE*maxNumStages];;
+ //arm_biquad_cascade_df2T_init_f32(&m_iirCfg, maxNumStages, m_coeffs, m_state);
+ changeFilterCoeffs(maxNumStages, coeffs);
}
IirBiQuadFilterFloat::~IirBiQuadFilterFloat()
@@ -127,6 +154,16 @@ IirBiQuadFilterFloat::~IirBiQuadFilterFloat()
}
+void IirBiQuadFilterFloat::changeFilterCoeffs(unsigned numStages, const float *coeffs)
+{
+ // clear the state
+ memset(m_state, 0, sizeof(float) * NUM_COEFFS_PER_STAGE * numStages);
+ // copy the coeffs
+ memcpy(m_coeffs, coeffs, NUM_COEFFS_PER_STAGE*numStages * sizeof(float));
+ arm_biquad_cascade_df2T_init_f32(&m_iirCfg, numStages, m_coeffs, m_state);
+}
+
+
bool IirBiQuadFilterFloat::process(float *output, float *input, size_t numSamples)
{
if (!output) return false;
diff --git a/src/effects/AudioEffectAnalogDelay.cpp b/src/effects/AudioEffectAnalogDelay.cpp
index 69b4c55..f822a6e 100644
--- a/src/effects/AudioEffectAnalogDelay.cpp
+++ b/src/effects/AudioEffectAnalogDelay.cpp
@@ -13,9 +13,8 @@ constexpr int MIDI_CHANNEL = 0;
constexpr int MIDI_CONTROL = 1;
// BOSS DM-3 Filters
-constexpr unsigned NUM_IIR_STAGES = 4;
-constexpr unsigned IIR_COEFF_SHIFT = 2;
-constexpr int32_t DEFAULT_COEFFS[5*NUM_IIR_STAGES] = {
+constexpr unsigned DM3_COEFF_SHIFT = 2;
+constexpr int32_t DM3[5*MAX_NUM_FILTER_STAGES] = {
536870912, 988616936, 455608573, 834606945, -482959709,
536870912, 1031466345, 498793368, 965834205, -467402235,
536870912, 1105821939, 573646688, 928470657, -448083489,
@@ -28,7 +27,7 @@ AudioEffectAnalogDelay::AudioEffectAnalogDelay(float maxDelayMs)
{
m_memory = new AudioDelay(maxDelayMs);
m_maxDelaySamples = calcAudioSamples(maxDelayMs);
- m_iir = new IirBiQuadFilterHQ(NUM_IIR_STAGES, reinterpret_cast(&DEFAULT_COEFFS), IIR_COEFF_SHIFT);
+ m_constructFilter();
}
AudioEffectAnalogDelay::AudioEffectAnalogDelay(size_t numSamples)
@@ -36,7 +35,7 @@ AudioEffectAnalogDelay::AudioEffectAnalogDelay(size_t numSamples)
{
m_memory = new AudioDelay(numSamples);
m_maxDelaySamples = numSamples;
- m_iir = new IirBiQuadFilterHQ(NUM_IIR_STAGES, reinterpret_cast(&DEFAULT_COEFFS), IIR_COEFF_SHIFT);
+ m_constructFilter();
}
// requires preallocated memory large enough
@@ -46,7 +45,7 @@ AudioEffectAnalogDelay::AudioEffectAnalogDelay(ExtMemSlot *slot)
m_memory = new AudioDelay(slot);
m_maxDelaySamples = (slot->size() / sizeof(int16_t));
m_externalMemory = true;
- m_iir = new IirBiQuadFilterHQ(NUM_IIR_STAGES, reinterpret_cast(&DEFAULT_COEFFS), IIR_COEFF_SHIFT);
+ m_constructFilter();
}
AudioEffectAnalogDelay::~AudioEffectAnalogDelay()
@@ -55,6 +54,18 @@ AudioEffectAnalogDelay::~AudioEffectAnalogDelay()
if (m_iir) delete m_iir;
}
+// This function just sets up the default filter and coefficients
+void AudioEffectAnalogDelay::m_constructFilter(void)
+{
+ // Use DM3 coefficients by default
+ m_iir = new IirBiQuadFilterHQ(MAX_NUM_FILTER_STAGES, reinterpret_cast(&DM3), DM3_COEFF_SHIFT);
+}
+
+void AudioEffectAnalogDelay::setFilterCoeffs(int numStages, const int32_t *coeffs, int coeffShift)
+{
+ m_iir->changeFilterCoeffs(numStages, coeffs, coeffShift);
+}
+
void AudioEffectAnalogDelay::update(void)
{
audio_block_t *inputAudioBlock = receiveReadOnly(); // get the next block of input samples