Adding Tube and Chorus and update the way the FXChain is structured

pull/409/head
abscisys 2 years ago
parent 992f275643
commit 650db9a267
  1. 103
      src/fx_chorus.cpp
  2. 55
      src/fx_chorus.h
  3. 58
      src/fx_rack.cpp
  4. 55
      src/fx_rack.h
  5. 60
      src/fx_tube.cpp
  6. 41
      src/fx_tube.h

@ -0,0 +1,103 @@
#include "fx_chorus.h"
#include <cmath>
#define CHORUS_BUFFER_SIZE 8192
Chorus::Chorus(float32_t sampling_rate, unsigned voices, float32_t depth, float32_t rate, float32_t feedback) :
FXElement(sampling_rate),
NumVoices(voices),
sample_position_ratio_(sampling_rate / 1000.0f),
lfo_phase_(0.0f)
{
this->delay_buffersL_ = new float32_t*[this->NumVoices];
this->delay_buffersR_ = new float32_t*[this->NumVoices];
for(unsigned i = 0; i < this->NumVoices; ++i)
{
this->delay_buffersL_[i] = new float32_t[CHORUS_BUFFER_SIZE];
this->delay_buffersR_[i] = new float32_t[CHORUS_BUFFER_SIZE];
}
this->delay_buffer_indices_ = new unsigned[this->NumVoices];
memset(this->delay_buffer_indices_, 0, this->NumVoices * sizeof(float32_t));
}
Chorus::~Chorus()
{
for(unsigned i = 0; i < this->NumVoices; ++i)
{
delete[] this->delay_buffersL_[i];
delete[] this->delay_buffersR_[i];
}
delete[] this->delay_buffersL_;
delete[] this->delay_buffersR_;
delete[] this->delay_buffer_indices_;
}
void Chorus::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
static float32_t M2PI = 2.0f * PI;
float32_t sumL = 0.0f;
float32_t sumR = 0.0f;
for(unsigned i = 0; i < this->NumVoices; ++i) {
// Calculate the delay time based on the depth and rate parameters
float32_t delay = this->getDepth() * std::sin(this->lfo_phase_);
// Convert the delay time to samples
unsigned delay_samples = static_cast<unsigned>(delay * this->sample_position_ratio_);
// Mix the input audio with the delayed audio
sumL += inL + this->delay_buffersL_[i][(CHORUS_BUFFER_SIZE + this->delay_buffer_indices_[i] - delay_samples) % CHORUS_BUFFER_SIZE];
sumR += inR + this->delay_buffersR_[i][(CHORUS_BUFFER_SIZE + this->delay_buffer_indices_[i] - delay_samples) % CHORUS_BUFFER_SIZE];
// Update the delay buffer for this voice
this->delay_buffersL_[i][this->delay_buffer_indices_[i]] = inL + sumL * this->getFeedback() / static_cast<float32_t>(i + 1);
this->delay_buffersR_[i][this->delay_buffer_indices_[i]] = inR + sumR * this->getFeedback() / static_cast<float32_t>(i + 1);
this->delay_buffer_indices_[i] = (delay_buffer_indices_[i] + 1) % CHORUS_BUFFER_SIZE;
}
// Average the mixed audio from all voices to create the output
outL = sumL / static_cast<float32_t>(this->NumVoices);
outR = sumR / static_cast<float32_t>(this->NumVoices);
this->lfo_phase_ += this->lfo_phase_increment_;
if(this->lfo_phase_ > M2PI)
{
this->lfo_phase_ -= M2PI;
}
}
void Chorus::setDepth(float32_t depth)
{
this->depth_ = constrain(depth, 0.0f, 10.0f);
}
float32_t Chorus::getDepth() const
{
return this->depth_;
}
void Chorus::setRate(float32_t rate)
{
this->rate_ = constrain(rate, 0.1f, 1.0f);
this->lfo_phase_increment_ = 2.0f * PI * this->rate_ / this->getSamplingRate();
}
float32_t Chorus::getRate() const
{
return this->rate_;
}
void Chorus::setFeedback(float32_t feedback)
{
this->feedback_ = constrain(feedback, 0.0f, 1.0f);
}
float32_t Chorus::getFeedback() const
{
return this->feedback_;
}

@ -0,0 +1,55 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// fx_chorus.h
//
// Stereo Chorus audio effects proposed in the context of the MiniDexed project
//
#pragma once
#include "fx.h"
class Chorus : public FXElement
{
DISALLOW_COPY_AND_ASSIGN(Chorus);
public:
Chorus(float32_t sampling_rate, unsigned voices = 4, float32_t depth = 5.0f, float32_t rate = 0.5f, float32_t feedback = 0.5f);
virtual ~Chorus();
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
void setDepth(float32_t depth);
inline float32_t getDepth() const;
void setRate(float32_t rate);
inline float32_t getRate() const;
void setFeedback(float32_t feedback);
inline float32_t getFeedback() const;
private:
const unsigned NumVoices; // Number of voices in the chorus
const float32_t sample_position_ratio_;
float32_t** delay_buffersL_;
float32_t** delay_buffersR_;
unsigned* delay_buffer_indices_;
float32_t lfo_phase_;
float32_t lfo_phase_increment_;
float32_t depth_; // Depth of the chorus in milliseconds (0.0 - 10.0)
float32_t rate_; // Rate of the chorus in Hz (0.1 - 1.0)
float32_t feedback_; // Feedback level of the chorus (0.0 - 1.0)
};

@ -1,42 +1,24 @@
#include "fx_rack.h"
FXUnit::FXUnit(float32_t sampling_rate, FXElement& fx, float32_t wet_level) :
FXElement(sampling_rate),
fx_(fx)
{
this->setWetLevel(wet_level);
}
FXUnit::~FXUnit()
{
}
void FXUnit::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
this->fx_.processSample(inL, inR, outL, outR);
// Mix wet and dry signals
outL = this->getWetLevel() * outL + (1.0f - this->getWetLevel()) * inL;
outR = this->getWetLevel() * outR + (1.0f - this->getWetLevel()) * inR;
}
void FXUnit::setWetLevel(float32_t wet_level)
{
this->wet_level_ = constrain(wet_level, 0.0f, 1.0f);
}
float32_t FXUnit::getWetLevel() const
{
return this->wet_level_;
}
FXRack::FXRack(float32_t sampling_rate) :
FX(sampling_rate),
fx_chain_()
{
this->registerFX(new Phaser(sampling_rate));
this->registerFX(new TapeDelay(sampling_rate));
this->registerFX(new ShimmerReverb(sampling_rate));
this->fxTube_ = new FXUnit<Tube>(sampling_rate);
this->fxChorus_ = new FXUnit<Chorus>(sampling_rate);
this->fxFlanger_ = new FXUnit<Flanger>(sampling_rate);
this->fxOrbitone_ = new FXUnit<Orbitone>(sampling_rate);
this->fxPhaser_ = new FXUnit<Phaser>(sampling_rate);
this->fxTapeDelay_ = new FXUnit<TapeDelay>(sampling_rate);
this->fxShimmerReverb_ = new FXUnit<ShimmerReverb>(sampling_rate);
this->registerFX(this->fxTube_);
this->registerFX(this->fxChorus_);
this->registerFX(this->fxFlanger_);
this->registerFX(this->fxOrbitone_);
this->registerFX(this->fxPhaser_);
this->registerFX(this->fxTapeDelay_);
this->registerFX(this->fxShimmerReverb_);
}
FXRack::~FXRack()
@ -46,6 +28,14 @@ FXRack::~FXRack()
delete *it;
}
this->fx_chain_.clear();
delete this->fxTube_;
delete this->fxChorus_;
delete this->fxFlanger_;
delete this->fxOrbitone_;
delete this->fxPhaser_;
delete this->fxTapeDelay_;
delete this->fxShimmerReverb_;
}
void FXRack::process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples)
@ -85,5 +75,5 @@ void FXRack::process(float32_t* left_input, float32_t* right_input, float32_t* l
void FXRack::registerFX(FXElement* fx)
{
this->fx_chain_.push_back(new FXUnit(this->getSamplingRate(), *fx));
this->fx_chain_.push_back(fx);
}

@ -20,31 +20,63 @@
#pragma once
#include "fx.h"
#include "fx_tube.h"
#include "fx_chorus.h"
#include "fx_flanger.h"
#include "fx_orbitone.h"
#include "fx_phaser.h"
#include "fx_tape_delay.h"
#include "fx_shimmer_reverb.h"
#include <vector>
class FXUnit : public FXElement
template<typename _FXElement>
class FXUnit : public virtual _FXElement
{
DISALLOW_COPY_AND_ASSIGN(FXUnit);
public:
FXUnit(float32_t sampling_rate, FXElement& fx, float32_t wet_level = 0.5f);
virtual ~FXUnit();
FXUnit(float32_t sampling_rate, float32_t wet_level = 0.5f) :
_FXElement(sampling_rate),
wet_level_(wet_level)
{
}
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
virtual ~FXUnit()
{
}
void setWetLevel(float32_t wet_level);
inline float32_t getWetLevel() const;
void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
if(this->getWetLevel() == 0.0f)
{
outL = inL;
outR = inR;
}
else
{
_FXElement::processSample(inL, inR, outL, outR);
float32_t dry = 1.0f - this->getWetLevel();
outL = this->getWetLevel() * outL + dry * inL;
outR = this->getWetLevel() * outR + dry * inR;
}
}
void setWetLevel(float32_t wet_level)
{
this->wet_level_ = constrain(wet_level, 0.0f, 1.0f);
}
inline float32_t getWetLevel() const
{
return this->wet_level_;
}
private:
FXElement& fx_; // Embedded FX
float32_t wet_level_; // How much the signal is affected by the inner FX (0.0 - 1.0)
};
typedef std::vector<FXUnit*> FXChain;
typedef std::vector<FXElement*> FXChain;
class FXRack : public FX
{
@ -60,4 +92,11 @@ private:
void registerFX(FXElement* fx);
FXChain fx_chain_;
FXUnit<Tube>* fxTube_;
FXUnit<Chorus>* fxChorus_;
FXUnit<Flanger>* fxFlanger_;
FXUnit<Orbitone>* fxOrbitone_;
FXUnit<Phaser>* fxPhaser_;
FXUnit<TapeDelay>* fxTapeDelay_;
FXUnit<ShimmerReverb>* fxShimmerReverb_;
};

@ -0,0 +1,60 @@
#include "fx_tube.h"
#include <cmath>
Tube::Tube(float32_t samplingRate, float32_t curve, float32_t bias) :
FXElement(samplingRate),
TubeCurve(curve),
TubeBias(bias)
{
}
Tube::~Tube()
{
}
void Tube::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
float32_t absInL = abs(inL);
float32_t coeff = this->TubeCurve + this->getOverdrive();
if(absInL > this->TubeBias)
{
outL = coeff * (absInL - this->TubeBias) / (1.0f - this->TubeBias);
}
else
{
outL = coeff * absInL / (1.0f + this->TubeBias * absInL);
}
if(inL < 0.0f)
{
outL = -outL;
}
float32_t absInR = abs(inR);
if(absInR > this->TubeBias)
{
outR = coeff * (absInR - this->TubeBias) / (1.0f - this->TubeBias);
}
else
{
outR = coeff * absInR / (1.0f + this->TubeBias * absInR);
}
if(inR < 0.0f)
{
outR = -outR;
}
}
void Tube::setOverdrive(float32_t overdrive)
{
this->overdrive_ = constrain(overdrive, 0.0f, 1.0f);
}
float32_t Tube::getOverdrive() const
{
return this->overdrive_;
}

@ -0,0 +1,41 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// fx_tube.h
//
// Stereo Tube overdrive audio effects proposed in the context of the MiniDexed project
//
#pragma once
#include "fx.h"
class Tube : public FXElement
{
DISALLOW_COPY_AND_ASSIGN(Tube);
public:
Tube(float32_t sampling_rate, float32_t curve = 2.0f, float32_t bias = 0.7f);
virtual ~Tube();
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
void setOverdrive(float32_t overdrive);
inline float32_t getOverdrive() const;
private:
const float32_t TubeCurve;
const float32_t TubeBias;
float32_t overdrive_;
};
Loading…
Cancel
Save