mirror of https://github.com/probonopd/MiniDexed
parent
d42b8afc58
commit
6ca3e3d340
@ -0,0 +1,24 @@ |
|||||||
|
#include "fx.h" |
||||||
|
|
||||||
|
FXBase::FXBase(float32_t sampling_rate) : |
||||||
|
SamplingRate(sampling_rate) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
FXBase::~FXBase() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
float32_t FXBase::getSamplingRate() const |
||||||
|
{ |
||||||
|
return this->SamplingRate; |
||||||
|
} |
||||||
|
|
||||||
|
FX::FX(float32_t sampling_rate) : |
||||||
|
FXBase(sampling_rate) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
FX::~FX() |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
// 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.h
|
||||||
|
//
|
||||||
|
// Base class for Stereo audio effects proposed in the context of the MiniDexed project
|
||||||
|
//
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <arm_math.h> |
||||||
|
#include "common.h" |
||||||
|
|
||||||
|
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ |
||||||
|
TypeName(const TypeName&) = delete; \
|
||||||
|
void operator=(const TypeName&) = delete |
||||||
|
|
||||||
|
class FXBase |
||||||
|
{ |
||||||
|
DISALLOW_COPY_AND_ASSIGN(FXBase); |
||||||
|
|
||||||
|
protected: |
||||||
|
FXBase(float32_t sampling_rate); |
||||||
|
virtual ~FXBase(); |
||||||
|
|
||||||
|
public: |
||||||
|
float32_t getSamplingRate() const; |
||||||
|
|
||||||
|
private: |
||||||
|
const float32_t SamplingRate; |
||||||
|
}; |
||||||
|
|
||||||
|
class FX : public FXBase |
||||||
|
{ |
||||||
|
DISALLOW_COPY_AND_ASSIGN(FX); |
||||||
|
|
||||||
|
protected: |
||||||
|
FX(float32_t sampling_rate); |
||||||
|
virtual ~FX(); |
||||||
|
|
||||||
|
public: |
||||||
|
virtual void process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) = 0; |
||||||
|
}; |
@ -0,0 +1,58 @@ |
|||||||
|
#include "fx_phaser.h" |
||||||
|
|
||||||
|
#include <cmath> |
||||||
|
|
||||||
|
PhaserStage::PhaserStage(float32_t sampling_rate, float32_t frequency, float32_t q) : |
||||||
|
FXBase(sampling_rate), |
||||||
|
frequency_(frequency), |
||||||
|
q_(q) |
||||||
|
{ |
||||||
|
memset(this->z1, 0, 2 * sizeof(float32_t)); |
||||||
|
memset(this->z2, 0, 2 * sizeof(float32_t)); |
||||||
|
this->computeCoefficients(); |
||||||
|
} |
||||||
|
|
||||||
|
void PhaserStage::computeCoefficients() |
||||||
|
{ |
||||||
|
float32_t w0 = 2.0f * PI * this->getFrequency() / this->getSamplingRate(); |
||||||
|
float32_t alpha = sin(w0) / (2.0f * this->q_); |
||||||
|
this->a0 = 1.0f + alpha; |
||||||
|
this->a1 = -2.0f * cos(w0); |
||||||
|
this->a2 = 1.0f - alpha; |
||||||
|
this->b1 = this->a1; |
||||||
|
this->b2 = this->a2; |
||||||
|
} |
||||||
|
|
||||||
|
void PhaserStage::process(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) |
||||||
|
{ |
||||||
|
outL = (this->a0 * inL + this->a1 * this->z1[0] + this->a2 * this->z2[0]) / this->a0; |
||||||
|
this->z2[0] = this->z1[0]; |
||||||
|
this->z2[0] = inL; |
||||||
|
|
||||||
|
outR = (this->a0 * inR + this->a1 * this->z1[1] + this->a2 * this->z2[1]) / this->a0; |
||||||
|
this->z2[1] = this->z1[1]; |
||||||
|
this->z2[1] = inR; |
||||||
|
} |
||||||
|
|
||||||
|
void PhaserStage::setFrequency(float32_t frequency) |
||||||
|
{ |
||||||
|
this->frequency_ = constrain(frequency, 0.0, 10.0); |
||||||
|
this->computeCoefficients(); |
||||||
|
} |
||||||
|
|
||||||
|
float32_t PhaserStage::getFrequency() const |
||||||
|
{ |
||||||
|
return this->frequency_; |
||||||
|
} |
||||||
|
|
||||||
|
void PhaserStage::setQ(float32_t q) |
||||||
|
{ |
||||||
|
this->q_ = constrain(q, 0.1f, 1.0f); |
||||||
|
this->computeCoefficients(); |
||||||
|
} |
||||||
|
|
||||||
|
float32_t PhaserStage::getQ() const |
||||||
|
{ |
||||||
|
return this->q_; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,62 @@ |
|||||||
|
// 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_phaser.h
|
||||||
|
//
|
||||||
|
// Stereo Phaser audio effects proposed in the context of the MiniDexed project
|
||||||
|
//
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "fx.h" |
||||||
|
|
||||||
|
class PhaserStage : public FXBase |
||||||
|
{ |
||||||
|
DISALLOW_COPY_AND_ASSIGN(PhaserStage); |
||||||
|
|
||||||
|
public: |
||||||
|
PhaserStage(float32_t sampling_rate, float32_t frequency = 0.5f, float32_t q = 1.0f); |
||||||
|
|
||||||
|
void process(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR); |
||||||
|
|
||||||
|
void setFrequency(float32_t frequency); |
||||||
|
inline float32_t getFrequency() const; |
||||||
|
|
||||||
|
void setQ(float32_t q); |
||||||
|
inline float32_t getQ() const; |
||||||
|
|
||||||
|
private: |
||||||
|
void computeCoefficients(); |
||||||
|
|
||||||
|
float32_t frequency_; // LFO frequency in Hz
|
||||||
|
float32_t q_; // Q factor for the filters
|
||||||
|
|
||||||
|
float32_t a0, a1, a2, b1, b2; // Coefficients for the stage's filter
|
||||||
|
float32_t z1[2], z2[2]; // State variables for the stage's filter
|
||||||
|
}; |
||||||
|
|
||||||
|
#define NUM_PHASER_STAGES 6 |
||||||
|
|
||||||
|
class Phaser : public FX |
||||||
|
{ |
||||||
|
DISALLOW_COPY_AND_ASSIGN(Phaser); |
||||||
|
|
||||||
|
public: |
||||||
|
Phaser(float32_t sampling_rate, float32_t frequency, float32_t q); |
||||||
|
virtual ~Phaser(); |
||||||
|
|
||||||
|
virtual void process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) override; |
||||||
|
|
||||||
|
private: |
||||||
|
PhaserStage stages_[NUM_PHASER_STAGES]; |
||||||
|
}; |
@ -0,0 +1,75 @@ |
|||||||
|
#include "fx_rack.h" |
||||||
|
|
||||||
|
FXUnit::FXUnit(float32_t sampling_rate, FX& fx, float32_t wet_level) : |
||||||
|
FX(sampling_rate), |
||||||
|
fx_(fx) |
||||||
|
{ |
||||||
|
this->setWetLevel(wet_level); |
||||||
|
} |
||||||
|
|
||||||
|
FXUnit::~FXUnit() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void FXUnit::process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) |
||||||
|
{ |
||||||
|
this->fx_.process(left_input, right_input, left_output, right_output, nSamples); |
||||||
|
|
||||||
|
for(unsigned i = 0; i < nSamples; ++i) |
||||||
|
{ |
||||||
|
// Mix wet and dry signals
|
||||||
|
*left_output = this->getWetLevel() * *left_output + (1.0f - this->getWetLevel()) * *left_input; |
||||||
|
*right_output = this->getWetLevel() * *right_output + (1.0f - this->getWetLevel()) * *left_input; |
||||||
|
|
||||||
|
// Move to next input sample
|
||||||
|
++left_input; |
||||||
|
++right_input; |
||||||
|
|
||||||
|
// Move to next output sample
|
||||||
|
++left_output; |
||||||
|
++right_output; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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 TapeDelay(sampling_rate)); |
||||||
|
this->registerFX(new ShimmerReverb(sampling_rate)); |
||||||
|
} |
||||||
|
|
||||||
|
FXRack::~FXRack() |
||||||
|
{ |
||||||
|
for(FXChain::iterator it = this->fx_chain_.begin(); it != this->fx_chain_.end(); it++) |
||||||
|
{ |
||||||
|
delete *it; |
||||||
|
} |
||||||
|
this->fx_chain_.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
void FXRack::process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) |
||||||
|
{ |
||||||
|
for(FXChain::iterator it = this->fx_chain_.begin(); it != this->fx_chain_.end(); it++) |
||||||
|
{ |
||||||
|
(*it)->process(left_input, right_input, left_output, right_output, nSamples); |
||||||
|
|
||||||
|
left_input = left_output; |
||||||
|
right_input = right_output; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void FXRack::registerFX(FX* fx) |
||||||
|
{ |
||||||
|
this->fx_chain_.push_back(new FXUnit(this->getSamplingRate(), *fx)); |
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
// 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_rack.h
|
||||||
|
//
|
||||||
|
// Rack of audio effects proposed in the context of the MiniDexed project
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "fx.h" |
||||||
|
#include "fx_tape_delay.h" |
||||||
|
#include "fx_shimmer_reverb.h" |
||||||
|
|
||||||
|
#include <vector> |
||||||
|
|
||||||
|
class FXUnit : public FX |
||||||
|
{ |
||||||
|
DISALLOW_COPY_AND_ASSIGN(FXUnit); |
||||||
|
|
||||||
|
public: |
||||||
|
FXUnit(float32_t sampling_rate, FX& fx, float32_t wet_level = 0.5f); |
||||||
|
virtual ~FXUnit(); |
||||||
|
|
||||||
|
virtual void process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) override; |
||||||
|
|
||||||
|
void setWetLevel(float32_t wet_level); |
||||||
|
inline float32_t getWetLevel() const; |
||||||
|
|
||||||
|
private: |
||||||
|
FX& 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; |
||||||
|
|
||||||
|
class FXRack : public FX |
||||||
|
{ |
||||||
|
DISALLOW_COPY_AND_ASSIGN(FXRack); |
||||||
|
|
||||||
|
public: |
||||||
|
FXRack(float32_t sampling_rate); |
||||||
|
virtual ~FXRack(); |
||||||
|
|
||||||
|
virtual void process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) override; |
||||||
|
|
||||||
|
private: |
||||||
|
void registerFX(FX* fx); |
||||||
|
|
||||||
|
FXChain fx_chain_; |
||||||
|
}; |
@ -0,0 +1,134 @@ |
|||||||
|
#include "fx_shimmer_reverb.h" |
||||||
|
|
||||||
|
#include <cmath> |
||||||
|
#include <algorithm> |
||||||
|
|
||||||
|
ShimmerReverb::ShimmerReverb(float32_t sampling_rate, |
||||||
|
float32_t left_delay_time, |
||||||
|
float32_t right_delay_time, |
||||||
|
float32_t shimmer_frequency, |
||||||
|
float32_t shimmer_amplitude, |
||||||
|
float32_t decay_time) : FX(sampling_rate), |
||||||
|
DelayLineLength(static_cast<unsigned>(SHIMMER_MAX_DELAY_TIME * sampling_rate)), |
||||||
|
write_pos_L_(0), |
||||||
|
write_pos_R_(0), |
||||||
|
shimmer_phase_(0.0f) |
||||||
|
{ |
||||||
|
this->delay_line_L_ = new float32_t[this->DelayLineLength]; |
||||||
|
this->delay_line_R_ = new float32_t[this->DelayLineLength]; |
||||||
|
|
||||||
|
memset(this->delay_line_L_, 0, this->DelayLineLength * sizeof(float32_t)); |
||||||
|
memset(this->delay_line_R_, 0, this->DelayLineLength * sizeof(float32_t)); |
||||||
|
|
||||||
|
this->setLeftDelayTime(left_delay_time); |
||||||
|
this->setRightDelayTime(right_delay_time); |
||||||
|
this->setShimmerFrequency(shimmer_frequency); |
||||||
|
this->setShimmerAmplitude(shimmer_amplitude); |
||||||
|
this->setDecayTime(decay_time); |
||||||
|
} |
||||||
|
|
||||||
|
ShimmerReverb::~ShimmerReverb() |
||||||
|
{ |
||||||
|
delete[] this->delay_line_L_; |
||||||
|
delete[] this->delay_line_R_; |
||||||
|
} |
||||||
|
|
||||||
|
void ShimmerReverb::process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) |
||||||
|
{ |
||||||
|
for(unsigned i = 0; i < nSamples; ++i) |
||||||
|
{ |
||||||
|
// Calculate shimmer offset based on current phase
|
||||||
|
float32_t shimmerOffsetL = this->getShimmerAmplitude() * sin(this->shimmer_phase_ * 2.0f * PI); |
||||||
|
float32_t shimmerOffsetR = this->getShimmerAmplitude() * cos(this->shimmer_phase_ * 2.0f * PI); |
||||||
|
|
||||||
|
// Calculate read position for left and right channel delay lines
|
||||||
|
int readPosL = this->write_pos_L_ - (int)(this->delay_time_L_ * this->getSamplingRate()) - (int)(shimmerOffsetL * this->getSamplingRate()); |
||||||
|
int readPosR = this->write_pos_R_ - (int)(this->delay_time_R_ * this->getSamplingRate()) - (int)(shimmerOffsetR * this->getSamplingRate()); |
||||||
|
|
||||||
|
// Wrap read position around the end of the delay line if necessary
|
||||||
|
if(readPosL < 0) readPosL += this->DelayLineLength; |
||||||
|
if(readPosR < 0) readPosR += this->DelayLineLength; |
||||||
|
|
||||||
|
// Read32_t left and right channel delay line samples
|
||||||
|
float32_t delaySampleL = this->delay_line_L_[readPosL]; |
||||||
|
float32_t delaySampleR = this->delay_line_R_[readPosR]; |
||||||
|
|
||||||
|
// Calculate reverb decay factor
|
||||||
|
float32_t decay = std::pow(0.001f, 1.0f / (this->decay_time_ * this->getSamplingRate())); |
||||||
|
|
||||||
|
// Calculate output samples
|
||||||
|
*left_output = *left_input + delaySampleL * decay; |
||||||
|
*right_output = *right_input + delaySampleR * decay; |
||||||
|
|
||||||
|
// Write input samples to delay lines
|
||||||
|
this->delay_line_L_[this->write_pos_L_] = *left_input; |
||||||
|
this->delay_line_R_[this->write_pos_R_] = *right_input; |
||||||
|
|
||||||
|
// Increment write position and wrap around the end of the delay line if necessary
|
||||||
|
this->write_pos_L_ = (this->write_pos_L_ + 1) % this->DelayLineLength; |
||||||
|
this->write_pos_R_ = (this->write_pos_R_ + 1) % this->DelayLineLength; |
||||||
|
|
||||||
|
// Increment shimmer phase
|
||||||
|
this->shimmer_phase_ += this->getShimmerFrequency() / this->getSamplingRate(); |
||||||
|
if(this->shimmer_phase_ > 1.0f) this->shimmer_phase_ -= 1.0f; |
||||||
|
|
||||||
|
// Move to next input sample
|
||||||
|
++left_input; |
||||||
|
++right_input; |
||||||
|
|
||||||
|
// Move to next output sample
|
||||||
|
++left_output; |
||||||
|
++right_output; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ShimmerReverb::setLeftDelayTime(float32_t delay_time_L)
|
||||||
|
{ |
||||||
|
this->delay_time_L_ = constrain(delay_time_L, 0.0f, SHIMMER_MAX_DELAY_TIME); |
||||||
|
} |
||||||
|
|
||||||
|
float32_t ShimmerReverb::getLeftDelayTime() const
|
||||||
|
{ |
||||||
|
return this->delay_time_L_; |
||||||
|
} |
||||||
|
|
||||||
|
void ShimmerReverb::setRightDelayTime(float32_t delay_time_R)
|
||||||
|
{ |
||||||
|
this->delay_time_R_ = constrain(delay_time_R, 0.0f, SHIMMER_MAX_DELAY_TIME); |
||||||
|
} |
||||||
|
|
||||||
|
float32_t ShimmerReverb::getRightDelayTime() const
|
||||||
|
{ |
||||||
|
return this->delay_time_R_; |
||||||
|
} |
||||||
|
|
||||||
|
void ShimmerReverb::setShimmerFrequency(float32_t frequency)
|
||||||
|
{ |
||||||
|
this->shimmer_frequency_ = constrain(frequency, 0.0f, this->getSamplingRate() / 2.0f); |
||||||
|
} |
||||||
|
|
||||||
|
float32_t ShimmerReverb::getShimmerFrequency() const
|
||||||
|
{ |
||||||
|
return this->shimmer_frequency_; |
||||||
|
} |
||||||
|
|
||||||
|
void ShimmerReverb::setShimmerAmplitude(float32_t amplitude)
|
||||||
|
{ |
||||||
|
this->shimmer_amplitude_ = constrain(amplitude, 0.0f, 1.0f); |
||||||
|
} |
||||||
|
|
||||||
|
float32_t ShimmerReverb::getShimmerAmplitude() const
|
||||||
|
{ |
||||||
|
return this->shimmer_amplitude_; |
||||||
|
} |
||||||
|
|
||||||
|
void ShimmerReverb::setDecayTime(float32_t decay_time)
|
||||||
|
{ |
||||||
|
this->decay_time_ = constrain(decay_time, 0.0f, SHIMMER_MAX_DELAY_TIME); |
||||||
|
} |
||||||
|
|
||||||
|
float32_t ShimmerReverb::getDecayTime() const
|
||||||
|
{ |
||||||
|
return this->decay_time_; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,72 @@ |
|||||||
|
// 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_shimmer_reverb.h
|
||||||
|
//
|
||||||
|
// Stereo Shimmer reverb proposed in the context of the MiniDexed project
|
||||||
|
//
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "fx.h" |
||||||
|
|
||||||
|
#define SHIMMER_MAX_DELAY_TIME 2.0f |
||||||
|
|
||||||
|
class ShimmerReverb : public FX |
||||||
|
{ |
||||||
|
DISALLOW_COPY_AND_ASSIGN(ShimmerReverb); |
||||||
|
|
||||||
|
public: |
||||||
|
ShimmerReverb( float32_t sampling_rate,
|
||||||
|
float32_t left_delay_time = 0.5f,
|
||||||
|
float32_t right_delay_time = 0.6f,
|
||||||
|
float32_t shimmer_frequency = 2.0f, |
||||||
|
float32_t shimmer_amplitude = 0.5f, |
||||||
|
float32_t decay_time = 2.0f); |
||||||
|
|
||||||
|
virtual ~ShimmerReverb(); |
||||||
|
|
||||||
|
virtual void process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) override; |
||||||
|
|
||||||
|
void setLeftDelayTime(float32_t delay_time_L); |
||||||
|
inline float32_t getLeftDelayTime() const; |
||||||
|
|
||||||
|
void setRightDelayTime(float32_t delay_time_R); |
||||||
|
inline float32_t getRightDelayTime() const; |
||||||
|
|
||||||
|
void setShimmerFrequency(float32_t frequency); |
||||||
|
inline float32_t getShimmerFrequency() const; |
||||||
|
|
||||||
|
void setShimmerAmplitude(float32_t amplitude); |
||||||
|
inline float32_t getShimmerAmplitude() const; |
||||||
|
|
||||||
|
void setDecayTime(float32_t decay_time); |
||||||
|
inline float32_t getDecayTime() const; |
||||||
|
|
||||||
|
private: |
||||||
|
const unsigned DelayLineLength; |
||||||
|
float32_t* delay_line_L_; |
||||||
|
float32_t* delay_line_R_; |
||||||
|
|
||||||
|
// Current write position for left and right channel delay lines
|
||||||
|
unsigned write_pos_L_; |
||||||
|
unsigned write_pos_R_; |
||||||
|
float32_t shimmer_phase_; // Current shimmer phase (0.0 - 1.0)
|
||||||
|
|
||||||
|
float32_t delay_time_L_; // Left channel delay time in seconds
|
||||||
|
float32_t delay_time_R_; // Right channel delay time in seconds
|
||||||
|
float32_t shimmer_frequency_; // Shimmer frequency in Hz
|
||||||
|
float32_t shimmer_amplitude_; // Shimmer amplitude (0.0 - 1.0)
|
||||||
|
float32_t decay_time_; // Reverb decay time in seconds
|
||||||
|
}; |
@ -0,0 +1,117 @@ |
|||||||
|
#include "fx_tape_delay.h" |
||||||
|
|
||||||
|
#include <cmath> |
||||||
|
#include <algorithm> |
||||||
|
|
||||||
|
TapeDelay::TapeDelay(const float32_t sampling_rate, float32_t default_delay_time, float32_t default_flutter_level, float32_t default_feedback_level) : |
||||||
|
FX(sampling_rate), |
||||||
|
MaxSampleDelayTime(sampling_rate * MAX_DELAY_TIME), |
||||||
|
left_read_pos_(0), |
||||||
|
right_read_pos_(0) |
||||||
|
{ |
||||||
|
this->left_buffer_ = new float32_t[this->MaxSampleDelayTime]; |
||||||
|
this->right_buffer_ = new float32_t[this->MaxSampleDelayTime]; |
||||||
|
|
||||||
|
this->setDelayTime(default_delay_time); |
||||||
|
this->setFlutterLevel(default_flutter_level); |
||||||
|
this->setFeedbakLevel(default_feedback_level); |
||||||
|
} |
||||||
|
|
||||||
|
TapeDelay::~TapeDelay() |
||||||
|
{ |
||||||
|
delete[] this->left_buffer_; |
||||||
|
delete[] this->right_buffer_; |
||||||
|
} |
||||||
|
|
||||||
|
void TapeDelay::process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) |
||||||
|
{ |
||||||
|
for(size_t i = 0; i < nSamples; ++i) |
||||||
|
{ |
||||||
|
// calculate the fluttered delay time
|
||||||
|
float32_t fluttered_delay_time = this->getDelayTime() + this->getFlutteredDelayTime(); |
||||||
|
|
||||||
|
// Calculate write positions
|
||||||
|
int left_write_pos = this->left_read_pos_ - static_cast<int>(fluttered_delay_time); |
||||||
|
while(left_write_pos < 0) |
||||||
|
{ |
||||||
|
left_write_pos += this->MaxSampleDelayTime; |
||||||
|
} |
||||||
|
|
||||||
|
int right_write_pos = this->right_read_pos_ - static_cast<int>(fluttered_delay_time); |
||||||
|
while(right_write_pos < 0) |
||||||
|
{ |
||||||
|
right_write_pos += this->MaxSampleDelayTime; |
||||||
|
} |
||||||
|
|
||||||
|
// Write input to delay buffers
|
||||||
|
this->left_buffer_[left_write_pos] = *left_input; |
||||||
|
this->right_buffer_[right_write_pos] = *right_input; |
||||||
|
|
||||||
|
// Read from delay buffers and apply feedback
|
||||||
|
*left_output = this->left_buffer_[this->left_read_pos_]; |
||||||
|
*right_output = this->right_buffer_[this->right_read_pos_]; |
||||||
|
this->left_buffer_[left_write_pos] += *left_output * this->getFeedbackLevel(); |
||||||
|
this->right_buffer_[right_write_pos] += *right_output * this->getFeedbackLevel(); |
||||||
|
|
||||||
|
// Increment read positions
|
||||||
|
++this->left_read_pos_; |
||||||
|
if(this->left_read_pos_ >= this->MaxSampleDelayTime) |
||||||
|
{ |
||||||
|
this->left_read_pos_ -= this->MaxSampleDelayTime; |
||||||
|
} |
||||||
|
++this->right_read_pos_; |
||||||
|
if(this->right_read_pos_ >= this->MaxSampleDelayTime) |
||||||
|
{ |
||||||
|
this->right_read_pos_ -= this->MaxSampleDelayTime; |
||||||
|
} |
||||||
|
|
||||||
|
// Move to next input sample
|
||||||
|
++left_input; |
||||||
|
++right_input; |
||||||
|
|
||||||
|
// Move to next output sample
|
||||||
|
++left_output; |
||||||
|
++right_output; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void TapeDelay::setDelayTime(float32_t delay_time) |
||||||
|
{ |
||||||
|
this->delay_time_ = constrain(delay_time, 0.0f, 1.0f); |
||||||
|
} |
||||||
|
|
||||||
|
inline float32_t TapeDelay::getDelayTime() const |
||||||
|
{ |
||||||
|
return this->delay_time_; |
||||||
|
} |
||||||
|
|
||||||
|
void TapeDelay::setFlutterLevel(float32_t flutter_level) |
||||||
|
{ |
||||||
|
this->flutter_level_ = constrain(flutter_level, 0.0f, 1.0f);
|
||||||
|
} |
||||||
|
|
||||||
|
inline float32_t TapeDelay::getFlutterLevel() const |
||||||
|
{ |
||||||
|
return this->flutter_level_; |
||||||
|
} |
||||||
|
|
||||||
|
void TapeDelay::setFeedbakLevel(float32_t feedback) |
||||||
|
{ |
||||||
|
this->feedback_ = constrain(feedback, 0.0, 1.0); |
||||||
|
} |
||||||
|
|
||||||
|
inline float32_t TapeDelay::getFeedbackLevel() const |
||||||
|
{ |
||||||
|
return this->feedback_; |
||||||
|
} |
||||||
|
|
||||||
|
inline float32_t TapeDelay::getFlutteredDelayTime() |
||||||
|
{ |
||||||
|
// Genarate a random number in the range [-1.0 , 1.0]
|
||||||
|
float32_t r =
|
||||||
|
static_cast<float32_t>(this->random_generator_()) / |
||||||
|
static_cast<float32_t>(this->random_generator_.max()); |
||||||
|
|
||||||
|
// Scale and bias the random number to the desired flutter range
|
||||||
|
return r * this->getFlutterLevel(); |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
// 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_tape_delay.h
|
||||||
|
//
|
||||||
|
// Stereo Tape Delay proposed in the context of the MiniDexed project
|
||||||
|
//
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "fx.h" |
||||||
|
|
||||||
|
#include <random> |
||||||
|
|
||||||
|
#define MAX_DELAY_TIME 2 |
||||||
|
|
||||||
|
class TapeDelay : public FX |
||||||
|
{ |
||||||
|
DISALLOW_COPY_AND_ASSIGN(TapeDelay); |
||||||
|
|
||||||
|
public: |
||||||
|
TapeDelay(const float32_t sampling_rate, float32_t default_delay_time = 0.25f, float32_t default_flutter_level = 0.05f, float32_t default_wet_level = 0.5f); |
||||||
|
virtual ~TapeDelay(); |
||||||
|
|
||||||
|
virtual void process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) override; |
||||||
|
|
||||||
|
void setDelayTime(float32_t delay_time); |
||||||
|
inline float32_t getDelayTime() const; |
||||||
|
|
||||||
|
void setFlutterLevel(float32_t flutter_level); |
||||||
|
inline float32_t getFlutterLevel() const; |
||||||
|
|
||||||
|
void setFeedbakLevel(float32_t feedback); |
||||||
|
inline float32_t getFeedbackLevel() const; |
||||||
|
|
||||||
|
private: |
||||||
|
inline float32_t getFlutteredDelayTime(); |
||||||
|
|
||||||
|
private: |
||||||
|
const size_t MaxSampleDelayTime; |
||||||
|
size_t left_read_pos_; |
||||||
|
size_t right_read_pos_; |
||||||
|
float32_t* left_buffer_; |
||||||
|
float32_t* right_buffer_; |
||||||
|
float32_t delay_time_; |
||||||
|
float32_t flutter_level_; |
||||||
|
float32_t feedback_; |
||||||
|
std::mt19937 random_generator_; |
||||||
|
}; |
Loading…
Reference in new issue