Fixing the Phaser

pull/409/head
abscisys 2 years ago
parent f2006bd880
commit 4fb79f858a
  1. 150
      src/fx_phaser.cpp
  2. 67
      src/fx_phaser.h
  3. 30
      src/minidexed.cpp
  4. 4
      src/minidexed.h
  5. 4
      src/performance.ini
  6. 36
      src/performanceconfig.cpp
  7. 12
      src/performanceconfig.h
  8. 8
      src/test/fxrack_test.cpp
  9. 8
      src/uimenu.cpp

@ -1,129 +1,125 @@
#include "fx_phaser.h"
#include <algorithm>
#include <cmath>
PhaserParameter::PhaserParameter(float32_t sampling_rate, float32_t frequency, float32_t resonance) :
FXBase(sampling_rate),
frequency_(frequency),
resonance_(resonance)
AllpassDelay::AllpassDelay() :
a1_(0.0f)
{
this->computeCoefficients();
memset(this->z_, 0, 2 * sizeof(float32_t));
}
PhaserParameter::~PhaserParameter()
AllpassDelay::~AllpassDelay()
{
}
void PhaserParameter::computeCoefficients()
void AllpassDelay::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
float32_t w0 = 2.0f * PI * this->getFrequency() / this->getSamplingRate();
float32_t alpha = sin(w0) / (2.0f * this->resonance_);
this->a0 = 1.0f + alpha;
this->a1 = -2.0f * cos(w0);
this->a2 = 1.0f - alpha;
this->b1 = this->a1;
this->b2 = this->a2;
outL = inL * -this->a1_ + this->z_[0];
this->z_[0] = outL * this->a1_ + inL;
outR = inR * -this->a1_ + this->z_[1];
this->z_[1] = outR * this->a1_ + inR;
}
void PhaserParameter::setFrequency(float32_t frequency)
void AllpassDelay::setDelay(float32_t delay)
{
this->frequency_ = frequency;
this->computeCoefficients();
this->a1_ = (1.0f - delay) / (1.0f + delay);
}
float32_t PhaserParameter::getFrequency() const
Phaser::Phaser(float32_t sampling_rate, float32_t rate, float32_t depth, float32_t feedback) :
FXElement(sampling_rate),
lfo_(sampling_rate, LFO::Waveform::Sine, 0.0f, 2.5f),
depth_(0.0f),
feedback_(0.0f),
dmin_(0.0f),
dmax_(0.0f)
{
return this->frequency_;
this->setRate(rate);
this->setDepth(depth);
this->setFeedback(feedback);
this->setFrequencyRange(440.0f, 1600.0f);
memset(this->z_, 0, 2 * sizeof(float32_t));
}
void PhaserParameter::setResonance(float32_t resonance)
Phaser::~Phaser()
{
this->resonance_ = constrain(resonance, 0.5f, 10.0f);
this->computeCoefficients();
}
float32_t PhaserParameter::getResonance() const
void Phaser::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
return this->resonance_;
}
float32_t d = this->dmin_ + (this->dmax_ - this->dmin_) * ((1.0f + this->lfo_.process()) / 2.0f);
float32_t sampleL = inL + this->feedback_ * this->z_[0];
float32_t sampleR = inR + this->feedback_ * this->z_[1];
for(unsigned i = 0; i < this->nb_stages_; ++i)
{
this->stages_[i].setDelay(d);
this->stages_[i].processSample(sampleL, sampleR, sampleL, sampleR);
}
this->z_[0] = sampleL;
this->z_[1] = sampleR;
// PhaserStage implementation
PhaserStage::PhaserStage(float32_t sampling_rate, PhaserParameter* params) :
FXElement(sampling_rate),
params_(params)
{
memset(this->z1, 0, 2 * sizeof(float32_t));
memset(this->z2, 0, 2 * sizeof(float32_t));
outL = inL + this->z_[0] * this->depth_;
outR = inR + this->z_[1] * this->depth_;
}
void PhaserStage::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
void Phaser::setFrequencyRange(float32_t min_frequency, float32_t max_frequency)
{
outL = (this->params_->a0 * inL + this->params_->a1 * this->z1[0] + this->params_->a2 * this->z2[0]) / this->params_->a0;
this->z2[0] = this->z1[0];
this->z2[0] = inL;
outR = (this->params_->a0 * inR + this->params_->a1 * this->z1[1] + this->params_->a2 * this->z2[1]) / this->params_->a0;
this->z2[1] = this->z1[1];
this->z2[1] = inR;
this->dmin_ = 2.0f * std::min(min_frequency, max_frequency) / this->getSamplingRate();
this->dmax_ = 2.0f * std::max(min_frequency, max_frequency) / this->getSamplingRate();
}
// Phaser implementation
Phaser::Phaser(float32_t sampling_rate, float32_t frequency, float32_t q) :
FXElement(sampling_rate),
params_(sampling_rate, frequency, q),
lfo_(sampling_rate, LFO::Waveform::Sine, 0.01f, 1.0f)
void Phaser::setRate(float32_t rate)
{
for(unsigned i = 0; i < NUM_PHASER_STAGES; ++i)
{
this->stages_[i] = new PhaserStage(sampling_rate, &this->params_);
}
rate = constrain(rate, 0.0f, 1.0f);
this->lfo_.setNormalizedFrequency(rate);
}
Phaser::~Phaser()
inline float32_t Phaser::getRate() const
{
for(unsigned i = 0; i < NUM_PHASER_STAGES; ++i)
{
delete this->stages_[i];
}
return this->lfo_.getNormalizedFrequency();
}
void Phaser::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
void Phaser::setDepth(float32_t depth)
{
// Process the input sample through each stage of the phaser
float32_t sampleL = inL;
float32_t sampleR = inR;
for(unsigned s = 0; s < NUM_PHASER_STAGES; ++s)
{
this->stages_[s]->processSample(sampleL, sampleR, sampleL, sampleR);
}
depth = constrain(depth, 0.0f, 1.0f);
this->depth_ = depth;
}
// Modulate the output of the phaser using the LFO
float32_t lfo = this->lfo_.process();
outR = sampleR * (0.5f + 0.5f * lfo);
outL = sampleL * (0.5f + 0.5f * lfo);
inline float32_t Phaser::getDepth() const
{
return this->depth_;
}
void Phaser::setFrequency(float32_t frequency)
void Phaser::setFeedback(float32_t feedback)
{
this->lfo_.setNormalizedFrequency(frequency);
this->params_.setFrequency(this->lfo_.getFrequency());
feedback = constrain(feedback, 0.0f, 0.97f);
this->feedback_ = feedback;
}
inline float32_t Phaser::getFrequency() const
inline float32_t Phaser::getFeedback() const
{
return this->lfo_.getNormalizedFrequency();
return this->feedback_;
}
void Phaser::setResonance(float32_t q)
void Phaser::setNbStages(unsigned nb_stages)
{
this->params_.setResonance(q);
if(nb_stages < 2)
{
nb_stages = 2;
}
else if(nb_stages > MAX_NB_PHASES)
{
nb_stages = MAX_NB_PHASES;
}
this->nb_stages_ = nb_stages;
}
inline float32_t Phaser::getResonance() const
unsigned Phaser::getNbStages() const
{
return this->params_.getResonance();
return this->nb_stages_;
}

@ -20,66 +20,57 @@
#include "fx_components.h"
class PhaserStage;
class PhaserParameter : public FXBase
class AllpassDelay
{
friend class PhaserStage;
DISALLOW_COPY_AND_ASSIGN(PhaserParameter);
DISALLOW_COPY_AND_ASSIGN(AllpassDelay);
public:
PhaserParameter(float32_t sampling_rate, float32_t frequency = 0.5f, float32_t q = 1.0f);
virtual ~PhaserParameter();
AllpassDelay();
virtual ~AllpassDelay();
void setFrequency(float32_t frequency);
inline float32_t getFrequency() const;
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR);
void setResonance(float32_t q);
inline float32_t getResonance() const;
void setDelay(float32_t delay);
private:
void computeCoefficients();
float32_t frequency_; // LFO frequency in Hz (0.01 - 5.0)
float32_t resonance_; // Resonance factor for the filters (0.5 - 10.0)
float32_t a0, a1, a2, b1, b2; // Coefficients for the stage's filter
float32_t a1_;
float32_t z_[2];
};
class PhaserStage : public FXElement
{
DISALLOW_COPY_AND_ASSIGN(PhaserStage);
public:
PhaserStage(float32_t sampling_rate, PhaserParameter* params);
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
private:
PhaserParameter* params_; // All paremters of the phaser including the inner coefficients
float32_t z1[2], z2[2]; // State variables for the stage's filter
};
#define NUM_PHASER_STAGES 6
#define MAX_NB_PHASES 24
class Phaser : public FXElement
{
DISALLOW_COPY_AND_ASSIGN(Phaser);
public:
Phaser(float32_t sampling_rate, float32_t frequency = 0.5f, float32_t resonance = 1.0f);
Phaser(float32_t sampling_rate, float32_t rate = 0.5f, float32_t depth = 1.0f, float32_t feedback = 0.7f);
virtual ~Phaser();
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
void setFrequency(float32_t frequency);
float32_t getFrequency() const;
void setFrequencyRange(float32_t min_frequency, float32_t max_frequency);
void setRate(float32_t rate);
float32_t getRate() const;
void setDepth(float32_t depth);
float32_t getDepth() const;
void setFeedback(float32_t depth);
float32_t getFeedback() const;
void setResonance(float32_t resonance);
float32_t getResonance() const;
void setNbStages(unsigned nb_stages);
unsigned getNbStages() const;
private:
PhaserParameter params_;
LFO lfo_;
PhaserStage* stages_[NUM_PHASER_STAGES];
float32_t depth_;
float32_t feedback_;
float32_t dmin_;
float32_t dmax_;
unsigned nb_stages_;
AllpassDelay stages_[MAX_NB_PHASES];
float32_t z_[2];
};

@ -195,7 +195,7 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
this->SetParameter(ParameterFXChainPhaserEnable, 1);
this->SetParameter(ParameterFXChainPhaserWet, 50);
this->SetParameter(ParameterFXChainPhaserRate, 5);
this->SetParameter(ParameterFXChainPhaserResonance, 45);
this->SetParameter(ParameterFXChainPhaserDepth, 45);
// FXChain > Delay parameters
this->SetParameter(ParameterFXChainDelayEnable, 1);
@ -883,16 +883,28 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
case ParameterFXChainPhaserRate:
nValue = constrain((int)nValue, 0, 99);
this->m_FXSpinLock.Acquire();
this->fx_rack->getPhaser()->setFrequency(nValue / 99.0f);
this->fx_rack->getPhaser()->setRate(nValue / 99.0f);
this->m_FXSpinLock.Release();
break;
case ParameterFXChainPhaserResonance:
case ParameterFXChainPhaserDepth:
nValue = constrain((int)nValue, 0, 99);
this->m_FXSpinLock.Acquire();
this->fx_rack->getPhaser()->setResonance(mapfloat(nValue, 0, 99, 0.5f, 10.0f));
this->fx_rack->getPhaser()->setDepth(nValue / 99.0f);
this->m_FXSpinLock.Release();
break;
case ParameterFXChainPhaserFeedback:
nValue = constrain((int)nValue, 0, 99);
this->m_FXSpinLock.Acquire();
this->fx_rack->getPhaser()->setFeedback(mapfloat(nValue, 0, 99, 0.0f, 0.97f));
this->m_FXSpinLock.Release();
break;
case ParameterFXChainPhaserNbStages:
nValue = constrain((int)nValue, 2, MAX_NB_PHASES);
this->m_FXSpinLock.Acquire();
this->fx_rack->getPhaser()->setNbStages(nValue);
this->m_FXSpinLock.Release();
break;
// FXChain > Delay parameters
case ParameterFXChainDelayEnable:
nValue = constrain((int)nValue, 0, 1);
@ -1406,7 +1418,9 @@ bool CMiniDexed::DoSavePerformance (void)
this->m_PerformanceConfig.SetFXChainPhaserEnable(!!this->m_nParameter[ParameterFXChainPhaserEnable]);
this->m_PerformanceConfig.SetFXChainPhaserWet(this->m_nParameter[ParameterFXChainPhaserWet]);
this->m_PerformanceConfig.SetFXChainPhaserRate(this->m_nParameter[ParameterFXChainPhaserRate]);
this->m_PerformanceConfig.SetFXChainPhaserResonance(this->m_nParameter[ParameterFXChainPhaserResonance]);
this->m_PerformanceConfig.SetFXChainPhaserDepth(this->m_nParameter[ParameterFXChainPhaserDepth]);
this->m_PerformanceConfig.SetFXChainPhaserFeedback(this->m_nParameter[ParameterFXChainPhaserFeedback]);
this->m_PerformanceConfig.SetFXChainPhaserNbStages(this->m_nParameter[ParameterFXChainPhaserNbStages]);
this->m_PerformanceConfig.SetFXChainDelayEnable(!!this->m_nParameter[ParameterFXChainDelayEnable]);
this->m_PerformanceConfig.SetFXChainDelayWet(this->m_nParameter[ParameterFXChainDelayWet]);
this->m_PerformanceConfig.SetFXChainDelayLeftDelayTime(this->m_nParameter[ParameterFXChainDelayLeftDelayTime]);
@ -1833,7 +1847,9 @@ void CMiniDexed::LoadPerformanceParameters(void)
this->SetParameter(ParameterFXChainPhaserEnable, this->m_PerformanceConfig.GetFXChainPhaserEnable());
this->SetParameter(ParameterFXChainPhaserWet, this->m_PerformanceConfig.GetFXChainPhaserWet());
this->SetParameter(ParameterFXChainPhaserRate, this->m_PerformanceConfig.GetFXChainPhaserRate());
this->SetParameter(ParameterFXChainPhaserResonance, this->m_PerformanceConfig.GetFXChainPhaserResonance());
this->SetParameter(ParameterFXChainPhaserDepth, this->m_PerformanceConfig.GetFXChainPhaserDepth());
this->SetParameter(ParameterFXChainPhaserFeedback, this->m_PerformanceConfig.GetFXChainPhaserFeedback());
this->SetParameter(ParameterFXChainPhaserNbStages, this->m_PerformanceConfig.GetFXChainPhaserNbStages());
this->SetParameter(ParameterFXChainDelayEnable, this->m_PerformanceConfig.GetFXChainDelayEnable());
this->SetParameter(ParameterFXChainDelayWet, this->m_PerformanceConfig.GetFXChainDelayWet());
this->SetParameter(ParameterFXChainDelayLeftDelayTime, this->m_PerformanceConfig.GetFXChainDelayLeftDelayTime());

@ -175,7 +175,9 @@ public:
ParameterFXChainPhaserEnable,
ParameterFXChainPhaserWet,
ParameterFXChainPhaserRate,
ParameterFXChainPhaserResonance,
ParameterFXChainPhaserDepth,
ParameterFXChainPhaserFeedback,
ParameterFXChainPhaserNbStages,
// FXChain > Delay parameters
ParameterFXChainDelayEnable,

@ -306,7 +306,9 @@ FXChainOrbitoneDepth=50
FXChainPhaserEnable=0
FXChainPhaserWet=50
FXChainPhaserRate=5
FXChainPhaserResonance=45
FXChainPhaserDepth=99
FXChainPhaserFeedback=70
FXChainPhaserNbStages=12
FXChainDelayEnable=0
FXChainDelayWet=50
FXChainDelayLeftDelayTime=15

@ -182,7 +182,9 @@ bool CPerformanceConfig::Load (void)
this->m_bFXChainPhaserEnable = this->m_Properties.GetNumber("FXChainPhaserEnable", 1);
this->m_nFXChainPhaserWet = this->m_Properties.GetNumber("FXChainPhaserWet", 50);
this->m_nFXChainPhaserRate = this->m_Properties.GetNumber("FXChainPhaserRate", 5);
this->m_nFXChainPhaserResonance = this->m_Properties.GetNumber("FXChainPhaserResonance", 45);
this->m_nFXChainPhaserDepth = this->m_Properties.GetNumber("FXChainPhaserDepth", 99);
this->m_nFXChainPhaserFeedback = this->m_Properties.GetNumber("FXChainPhaserFeedback", 50);
this->m_nFXChainPhaserNbStages = this->m_Properties.GetNumber("FXChainPhaserNbStages", 12);
this->m_bFXChainDelayEnable = this->m_Properties.GetNumber("FXChainDelayEnable", 1);
this->m_nFXChainDelayWet = this->m_Properties.GetNumber("FXChainDelayWet", 50);
this->m_nFXChainDelayLeftDelayTime = this->m_Properties.GetNumber("FXChainDelayLeftDelayTime", 15);
@ -337,7 +339,9 @@ bool CPerformanceConfig::Save (void)
this->m_Properties.SetNumber("FXChainPhaserEnable", m_bFXChainPhaserEnable ? 1 : 0);
this->m_Properties.SetNumber("FXChainPhaserWet", m_nFXChainPhaserWet);
this->m_Properties.SetNumber("FXChainPhaserRate", m_nFXChainPhaserRate);
this->m_Properties.SetNumber("FXChainPhaserResonance", m_nFXChainPhaserResonance);
this->m_Properties.SetNumber("FXChainPhaserDepth", m_nFXChainPhaserDepth);
this->m_Properties.SetNumber("FXChainPhaserFeedback", m_nFXChainPhaserFeedback);
this->m_Properties.SetNumber("FXChainPhaserNbStages", m_nFXChainPhaserNbStages);
this->m_Properties.SetNumber("FXChainDelayEnable", m_bFXChainDelayEnable ? 1 : 0);
this->m_Properties.SetNumber("FXChainDelayWet", m_nFXChainDelayWet);
this->m_Properties.SetNumber("FXChainDelayLeftDelayTime", m_nFXChainDelayLeftDelayTime);
@ -1109,9 +1113,19 @@ unsigned CPerformanceConfig::GetFXChainPhaserRate(void) const
return this->m_nFXChainPhaserRate;
}
unsigned CPerformanceConfig::GetFXChainPhaserResonance(void) const
unsigned CPerformanceConfig::GetFXChainPhaserDepth(void) const
{
return this->m_nFXChainPhaserResonance;
return this->m_nFXChainPhaserDepth;
}
unsigned CPerformanceConfig::GetFXChainPhaserFeedback(void) const
{
return this->m_nFXChainPhaserFeedback;
}
unsigned CPerformanceConfig::GetFXChainPhaserNbStages(void) const
{
return this->m_nFXChainPhaserNbStages;
}
bool CPerformanceConfig::GetFXChainDelayEnable(void) const
@ -1279,9 +1293,19 @@ void CPerformanceConfig::SetFXChainPhaserRate(unsigned nValue)
this->m_nFXChainPhaserRate = nValue;
}
void CPerformanceConfig::SetFXChainPhaserResonance(unsigned nValue)
void CPerformanceConfig::SetFXChainPhaserDepth(unsigned nValue)
{
this->m_nFXChainPhaserDepth = nValue;
}
void CPerformanceConfig::SetFXChainPhaserFeedback(unsigned nValue)
{
this->m_nFXChainPhaserFeedback = nValue;
}
void CPerformanceConfig::SetFXChainPhaserNbStages(unsigned nValue)
{
this->m_nFXChainPhaserResonance = nValue;
this->m_nFXChainPhaserNbStages = nValue;
}
void CPerformanceConfig::SetFXChainDelayEnable(unsigned bValue)

@ -140,7 +140,9 @@ public:
bool GetFXChainPhaserEnable(void) const;
unsigned GetFXChainPhaserWet(void) const;
unsigned GetFXChainPhaserRate(void) const;
unsigned GetFXChainPhaserResonance(void) const;
unsigned GetFXChainPhaserDepth(void) const;
unsigned GetFXChainPhaserFeedback(void) const;
unsigned GetFXChainPhaserNbStages(void) const;
bool GetFXChainDelayEnable(void) const;
unsigned GetFXChainDelayWet(void) const;
unsigned GetFXChainDelayLeftDelayTime(void) const;
@ -175,7 +177,9 @@ public:
void SetFXChainPhaserEnable(bool bValue);
void SetFXChainPhaserWet(unsigned nValue);
void SetFXChainPhaserRate(unsigned nValue);
void SetFXChainPhaserResonance(unsigned nValue);
void SetFXChainPhaserDepth(unsigned nValue);
void SetFXChainPhaserFeedback(unsigned nValue);
void SetFXChainPhaserNbStages(unsigned nValue);
void SetFXChainDelayEnable(unsigned nValue);
void SetFXChainDelayWet(unsigned nValue);
void SetFXChainDelayLeftDelayTime(unsigned nValue);
@ -279,7 +283,9 @@ private:
bool m_bFXChainPhaserEnable;
unsigned m_nFXChainPhaserWet;
unsigned m_nFXChainPhaserRate;
unsigned m_nFXChainPhaserResonance;
unsigned m_nFXChainPhaserDepth;
unsigned m_nFXChainPhaserFeedback;
unsigned m_nFXChainPhaserNbStages;
bool m_bFXChainDelayEnable;
unsigned m_nFXChainDelayWet;
unsigned m_nFXChainDelayLeftDelayTime;

@ -151,6 +151,10 @@ void testFXRack(unsigned& step, unsigned fxSwitch)
rack->getChorus()->setDepth(0.5f);
rack->getPhaser()->setEnable(Active(fxSwitch, FXSitch::Phaser));
rack->getPhaser()->setWetLevel(1.0f);
rack->getPhaser()->setRate(0.1f);
rack->getPhaser()->setDepth(1.0f);
rack->getPhaser()->setFeedback(0.75f);
rack->getOrbitone()->setEnable(Active(fxSwitch, FXSitch::Orbitone));
rack->getOrbitone()->setWetLevel(0.8f);
@ -210,9 +214,9 @@ int main()
// testLFO(step);
// testFlutter(step);
// testSVF(step);
testFXRack(step, FXSitch::Tube);
// testFXRack(step, FXSitch::Tube);
// testFXRack(step, FXSitch::Flanger); // to be fixed -> feedback deletes FX effect
// testFXRack(step, FXSitch::Phaser); // to be fixed -> far too load but saturation is interested
testFXRack(step, FXSitch::Phaser);
// testFXRack(step, FXSitch::Chorus);
// testFXRack(step, FXSitch::Orbitone);
// testFXRack(step, FXSitch::Delay);

@ -198,7 +198,9 @@ const CUIMenu::TMenuItem CUIMenu::s_FXChainPhaser[] =
{"Enable", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainPhaserEnable},
{"Wet Lvl", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainPhaserWet},
{"Rate", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainPhaserRate},
{"Res", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainPhaserResonance},
{"Depth", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainPhaserDepth},
{"Feedbck", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainPhaserFeedback},
{"Stages", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainPhaserNbStages},
{0}
};
@ -340,7 +342,9 @@ const CUIMenu::TParameter CUIMenu::s_GlobalParameter[CMiniDexed::ParameterUnknow
{0, 1, 1, ToOnOff}, // ParameterFXChainPhaserEnable
{0, 99, 1}, // ParameterFXChainPhaserWet
{0, 99, 1}, // ParameterFXChainPhaserRate
{0, 99, 1}, // ParameterFXChainPhaserResonance
{0, 99, 1}, // ParameterFXChainPhaserDepth
{0, 99, 1}, // ParameterFXChainPhaserFeedback
{2, MAX_NB_PHASES, 1}, // ParameterFXChainPhaserNbStages
// FXChain > Delay parameters
{0, 1, 1, ToOnOff}, // ParameterFXChainDelayEnable

Loading…
Cancel
Save