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