First step debugging completed

pull/409/head
abscisys 2 years ago
parent 1b33d6b94e
commit fe340f2f99
  1. 3
      src/Makefile
  2. 3
      src/fx.h
  3. 23
      src/fx_chorus.cpp
  4. 9
      src/fx_chorus.h
  5. 108
      src/fx_components.cpp
  6. 109
      src/fx_components.h
  7. 18
      src/fx_flanger.cpp
  8. 9
      src/fx_flanger.h
  9. 30
      src/fx_orbitone.cpp
  10. 11
      src/fx_orbitone.h
  11. 17
      src/fx_phaser.cpp
  12. 5
      src/fx_phaser.h
  13. 25
      src/fx_shimmer_reverb.cpp
  14. 15
      src/fx_shimmer_reverb.h
  15. 182
      src/fx_svf.cpp
  16. 63
      src/fx_svf.h
  17. 127
      src/fx_tape_delay.cpp
  18. 72
      src/fx_tape_delay.h
  19. 10
      src/minidexed.cpp

@ -10,7 +10,8 @@ OBJS = main.o kernel.o minidexed.o config.o userinterface.o uimenu.o \
mididevice.o midikeyboard.o serialmididevice.o pckeyboard.o \
sysexfileloader.o performanceconfig.o perftimer.o \
effect_compressor.o effect_platervbstereo.o \
fx.o fx_tube.o fx_chorus.o fx_flanger.o fx_orbitone.o fx_phaser.o \
fx.o fx_components.o \
fx_svf.o fx_tube.o fx_chorus.o fx_flanger.o fx_orbitone.o fx_phaser.o \
fx_tape_delay.o fx_shimmer_reverb.o fx_rack.o \
uibuttons.o midipin.o

@ -22,6 +22,9 @@
#include <arm_math.h>
#include "common.h"
#include <iostream>
using namespace std;
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&) = delete; \
void operator=(const TypeName&) = delete

@ -4,11 +4,11 @@
#define CHORUS_BUFFER_SIZE 8192
Chorus::Chorus(float32_t sampling_rate, unsigned voices, float32_t depth, float32_t rate, float32_t feedback) :
Chorus::Chorus(float32_t sampling_rate, unsigned voices, float32_t rate, float32_t depth, float32_t feedback) :
FXElement(sampling_rate),
NumVoices(voices),
sample_position_ratio_(sampling_rate / 1000.0f),
lfo_phase_(0.0f)
lfo_(sampling_rate, LFO::Waveform::Sine, 0.01f, 1.0f)
{
this->delay_buffersL_ = new float32_t*[this->NumVoices];
this->delay_buffersR_ = new float32_t*[this->NumVoices];
@ -21,6 +21,10 @@ Chorus::Chorus(float32_t sampling_rate, unsigned voices, float32_t depth, float3
this->delay_buffer_indices_ = new unsigned[this->NumVoices];
memset(this->delay_buffer_indices_, 0, this->NumVoices * sizeof(float32_t));
this->setRate(rate);
this->setDepth(depth);
this->setFeedback(feedback);
}
Chorus::~Chorus()
@ -38,13 +42,11 @@ Chorus::~Chorus()
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_);
float32_t delay = this->getDepth() * this->lfo_.process();
// Convert the delay time to samples
unsigned delay_samples = static_cast<unsigned>(delay * this->sample_position_ratio_);
@ -62,12 +64,6 @@ void Chorus::processSample(float32_t inL, float32_t inR, float32_t& outL, float3
// 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)
@ -82,13 +78,12 @@ float32_t Chorus::getDepth() const
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();
this->lfo_.setNormalizedFrequency(rate);
}
float32_t Chorus::getRate() const
{
return this->rate_;
return this->lfo_.getNormalizedFrequency();
}
void Chorus::setFeedback(float32_t feedback)

@ -18,14 +18,14 @@
//
#pragma once
#include "fx.h"
#include "fx_components.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);
Chorus(float32_t sampling_rate, unsigned voices = 4, float32_t rate = 0.1f, float32_t depth = 5.0f, float32_t feedback = 0.5f);
virtual ~Chorus();
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
@ -46,10 +46,7 @@ private:
float32_t** delay_buffersR_;
unsigned* delay_buffer_indices_;
float32_t lfo_phase_;
float32_t lfo_phase_increment_;
LFO lfo_;
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)
};

@ -0,0 +1,108 @@
#include "fx_components.h"
#include <cmath>
const float32_t Constants::M2PI = 2.0f * PI;
const float32_t Constants::M1_PI = 1.0f / PI;
LFO::LFO(float32_t sampling_rate, Waveform waveform, float32_t min_frequency, float32_t max_frequency) :
FXBase(sampling_rate),
min_frequency_(min_frequency),
max_frequency_(max_frequency),
phase_(0.0f),
last_sample_(0.0f),
new_phase_(true),
rnd_generator_(rnd_device_()),
rnd_distribution_(-1.0f, 1.0f)
{
this->setWaveform(waveform);
this->setFrequency(this->min_frequency_);
}
LFO::~LFO()
{
}
void LFO::setWaveform(Waveform waveform)
{
this->waveform_ = waveform;
}
LFO::Waveform LFO::getWaveform() const
{
return this->waveform_;
}
void LFO::setNormalizedFrequency(float32_t normalized_frequency)
{
normalized_frequency = constrain(normalized_frequency, 0.0f, 1.0f);
if(this->normalized_frequency_ != normalized_frequency)
{
float32_t frequency = mapfloat(normalized_frequency, 0.0f, 1.0f, this->min_frequency_, this->max_frequency_);
this->normalized_frequency_ = normalized_frequency;
this->frequency_ = frequency;
this->phase_increment_ = Constants::M2PI * this->frequency_ / this->getSamplingRate();
}
}
float32_t LFO::getNormalizedFrequency() const
{
return this->frequency_;
}
void LFO::setFrequency(float32_t frequency)
{
frequency = constrain(frequency, this->min_frequency_, this->max_frequency_);
if(this->frequency_ != frequency)
{
float32_t normalized_frequency = mapfloat(frequency, this->min_frequency_, this->max_frequency_, 0.0f, 1.0f);
this->normalized_frequency_ = normalized_frequency;
this->frequency_ = frequency;
this->phase_increment_ = Constants::M2PI * this->frequency_ / this->getSamplingRate();
}
}
float32_t LFO::process()
{
float32_t out = 0.0f;
switch(this->waveform_)
{
case Waveform::Sine:
out = std::sin(this->phase_);
break;
case Waveform::Saw:
out = Constants::M1_PI * this->phase_ - 1.0f;
break;
case Waveform::Square:
out = this->phase_ < PI ? 1.0 : -1.0;
break;
case Waveform::SH:
if(this->new_phase_)
{
out = this->rnd_distribution_(this->rnd_generator_);
}
else
{
out = this->last_sample_;
}
break;
case Waveform::Noise:
out = this->rnd_distribution_(this->rnd_generator_);
break;
}
this->last_sample_ = out;
this->phase_ += this->phase_increment_;
if(this->phase_ >= Constants::M2PI)
{
this->phase_ -= Constants::M2PI;
this->new_phase_ = true;
}
else
{
this->new_phase_ = false;
}
return out;
}

@ -0,0 +1,109 @@
// 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_components.h
//
// Several tools and components used in the implemlentation of FX
//
#pragma once
#include "fx.h"
#include <random>
struct Constants
{
const static float32_t M2PI; // 2 * PI
const static float32_t M1_PI; // 1 / PI
};
class LFO : public FXBase
{
DISALLOW_COPY_AND_ASSIGN(LFO);
public:
typedef enum {
Sine,
Saw,
Square,
SH,
Noise
} Waveform;
LFO(float32_t sampling_rate, Waveform waveform = Waveform::Sine, float32_t min_frequency = 0.01f, float32_t max_frequency = 10.0f);
~LFO();
void setWaveform(Waveform waveform);
Waveform getWaveform() const;
void setNormalizedFrequency(float32_t normalized_frequency);
float32_t getNormalizedFrequency() const;
void setFrequency(float32_t frequency);
float32_t getFrequency() const;
float32_t process();
private:
const float32_t min_frequency_;
const float32_t max_frequency_;
Waveform waveform_;
float32_t normalized_frequency_;
float32_t frequency_;
float32_t phase_;
float32_t phase_increment_;
float32_t last_sample_;
bool new_phase_;
std::random_device rnd_device_;
std::mt19937 rnd_generator_;
std::uniform_real_distribution<float32_t> rnd_distribution_;
};
template<typename T>
class Buffer
{
DISALLOW_COPY_AND_ASSIGN(Buffer);
public:
Buffer(unsigned size) :
size_(size)
{
this->values_ = new T[size];
this->reset();
}
virtual ~Buffer()
{
delete[] this->values_;
}
void reset()
{
memset(this->values_, 0, this->size_ * sizeof(T));
}
float32_t& operator[](unsigned index)
{
return this->values_[index];
}
unsigned getSize() const
{
return this->size_;
}
private:
const unsigned size_;
T* values_;
};

@ -8,7 +8,7 @@ Flanger::Flanger(float32_t sampling_rate, float32_t delay_time, float32_t freque
FXElement(sampling_rate),
MaxDelayLineSize(static_cast<unsigned>(2.0f * MAX_FLANGER_DELAY * sampling_rate / 1000.0f)),
delay_line_index_(0),
lfo_phase_(0.0f)
lfo_(sampling_rate, LFO::Waveform::Sine, 0.1f, 10.0f)
{
this->delay_lineL_ = new float32_t[this->MaxDelayLineSize];
this->delay_lineR_ = new float32_t[this->MaxDelayLineSize];
@ -27,10 +27,8 @@ Flanger::~Flanger()
void Flanger::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
static float32_t M2PI = 2.0f * PI;
// Calculate the delay time based on the depth and rate parameters
float32_t delay = this->getDelayTime() + this->getDepth() * std::sin(this->lfo_phase_);
float32_t delay = this->getDelayTime() + this->getDepth() * this->lfo_.process();
// Convert the delay time to samples
unsigned delay_samples = static_cast<unsigned>(delay * this->getSamplingRate() / 1000.0f);
@ -44,12 +42,6 @@ void Flanger::processSample(float32_t inL, float32_t inR, float32_t& outL, float
this->delay_lineR_[this->delay_line_index_] = inR + outR * this->getFeedback();
this->delay_line_index_ = (this->delay_line_index_ + 1) % this->delay_line_size_;
this->lfo_phase_ += this->lfo_phase_increment_;
if(this->lfo_phase_ > M2PI)
{
this->lfo_phase_ -= M2PI;
}
}
void Flanger::setDelayTime(float32_t delayMS)
@ -65,14 +57,12 @@ float32_t Flanger::getDelayTime() const
void Flanger::setFrequency(float32_t frequency)
{
frequency = constrain(frequency, 0.1f, 1.0f);
this->frequency_ = frequency;
this->lfo_phase_increment_ = 2.0f * PI * frequency / this->getSamplingRate();
this->lfo_.setNormalizedFrequency(frequency);
}
float32_t Flanger::getFrequency() const
{
return this->frequency_;
return this->lfo_.getNormalizedFrequency();
}
void Flanger::setDepth(float32_t depth)

@ -18,14 +18,14 @@
//
#pragma once
#include "fx.h"
#include "fx_components.h"
class Flanger : public FXElement
{
DISALLOW_COPY_AND_ASSIGN(Flanger);
public:
Flanger(float32_t sampling_rate, float32_t delay_time = 5.0f, float32_t frequency = 0.5f, float32_t depth = 1.0f, float32_t feedback = 0.5f);
Flanger(float32_t sampling_rate, float32_t delay_time = 5.0f, float32_t frequency = 0.05f, float32_t depth = 1.0f, float32_t feedback = 0.25f);
virtual ~Flanger();
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
@ -51,11 +51,8 @@ private:
float32_t* delay_lineL_;
float32_t* delay_lineR_;
float32_t lfo_phase_;
float32_t lfo_phase_increment_;
float32_t delay_time_ms_; // Delay time in milliseconds (0.0 - 10.0)
float32_t frequency_; // LFO frequency in HZ (0.1 - 10.0)
LFO lfo_;
float32_t depth_; // Depth of the flanger effect in milliseconds (0.0 - 10.0)
float32_t feedback_; // Amount of feedback to apply to the delay line
};

@ -25,29 +25,29 @@ inline float32_t OrbitoneParameter::getFeedback() const
// OrbitoneStage implementation
OrbitoneStage::OrbitoneStage(float32_t sampling_rate, OrbitoneParameter* params, float32_t frequency) :
OrbitoneStage::OrbitoneStage(float32_t sampling_rate, OrbitoneParameter* params, float32_t frequency, float32_t level) :
FXElement(sampling_rate),
params_(params),
frequency_(frequency),
phase_(0.0f)
lfo_(sampling_rate, LFO::Waveform::Sine, 0.0f, 20000.0f),
level_(level)
{
this->lfo_.setFrequency(frequency);
this->x_[0] = this->x_[1] = 0.0f;
}
OrbitoneStage::~OrbitoneStage()
{
this->phase_increment_ = 2.0f * PI * frequency / this->getSamplingRate();
}
void OrbitoneStage::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
// Generate a sine wave using the stage's oscillator
float32_t osc = sin(this->phase_);
// Update the phase of the oscillator
this->phase_ += this->phase_increment_;
if(this->phase_ > 2.0f * PI) {
this->phase_ -= 2.0f * PI;
}
float32_t osc = this->level_ * this->lfo_.process();
// Apply feedback to the stage's input
outL = inL + osc * this->params_->getFeedback();
outR = inR + osc * this->params_->getFeedback();
outL = inL + inL * osc + this->params_->getFeedback() * this->x_[0];
outR = inR + inR * osc + this->params_->getFeedback() * this->x_[1];
}
@ -57,10 +57,12 @@ Orbitone::Orbitone(float32_t sampling_rate, float32_t feedback) :
FXElement(sampling_rate),
params_(sampling_rate, feedback)
{
float32_t level = 1.0f;
for(unsigned i = 0; i < NUM_ORBITONR_STAGES; ++i)
{
float32_t frequency = 440.0 * pow(2.0f, (i - 1) / 12.0f); // Sets the frequency of each stage to be a multiple of 440 Hz
this->stages_[i] = new OrbitoneStage(sampling_rate, &this->params_, frequency);
level /= 2.0f;
this->stages_[i] = new OrbitoneStage(sampling_rate, &this->params_, frequency, level);
}
}

@ -18,7 +18,7 @@
//
#pragma once
#include "fx.h"
#include "fx_components.h"
class OrbitoneStage;
@ -43,15 +43,16 @@ class OrbitoneStage : public FXElement
DISALLOW_COPY_AND_ASSIGN(OrbitoneStage);
public:
OrbitoneStage(float32_t sampling_rate, OrbitoneParameter* params, float32_t frequency);
OrbitoneStage(float32_t sampling_rate, OrbitoneParameter* params, float32_t frequency, float32_t level);
virtual ~OrbitoneStage();
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
private:
OrbitoneParameter* params_;
float32_t frequency_; // Frequency of the stage oscillator in Hz
float32_t phase_; // Phase of the stage's oscillator
float32_t phase_increment_; // Amount to increment the phase at each sample
LFO lfo_;
float32_t level_;
float32_t x_[2];
};
#define NUM_ORBITONR_STAGES 4

@ -75,10 +75,8 @@ void PhaserStage::processSample(float32_t inL, float32_t inR, float32_t& outL, f
Phaser::Phaser(float32_t sampling_rate, float32_t frequency, float32_t q) :
FXElement(sampling_rate),
params_(sampling_rate, frequency, q),
phase_(0.0f),
phase_increment_(0.0f)
lfo_(sampling_rate, LFO::Waveform::Sine, 0.1f, 10.0f)
{
this->phase_increment_ = 2.0f * PI * frequency / this->getSamplingRate();
for(unsigned i = 0; i < NUM_PHASER_STAGES; ++i)
{
this->stages_[i] = new PhaserStage(sampling_rate, &this->params_);
@ -104,20 +102,15 @@ void Phaser::processSample(float32_t inL, float32_t inR, float32_t& outL, float3
}
// Modulate the output of the phaser using the LFO
outL = sampleL * (0.5f + 0.5f * cos(this->phase_));
outR = sampleR * (0.5f + 0.5f * cos(this->phase_));;
// Update the phase of the LFO
this->phase_ += this->phase_increment_;
if(this->phase_ > 2.0f * PI) {
this->phase_ -= 2.0 * PI;
}
float32_t lfo = this->lfo_.process();
outR = sampleR * (0.5f + 0.5f * lfo);
outL = sampleL * (0.5f + 0.5f * lfo);
}
void Phaser::setFrequency(float32_t frequency)
{
this->params_.setFrequency(frequency);
this->phase_increment_ = 2.0f * PI * frequency / this->getSamplingRate();
this->lfo_.setNormalizedFrequency(frequency);
}
inline float32_t Phaser::getFrequency() const

@ -18,7 +18,7 @@
//
#pragma once
#include "fx.h"
#include "fx_components.h"
class PhaserStage;
@ -80,7 +80,6 @@ public:
private:
PhaserParameter params_;
float32_t phase_; // Current phase of the LFO (0.01 - 5.0)
float32_t phase_increment_; // Amount to increment the phase at each sample
LFO lfo_;
PhaserStage* stages_[NUM_PHASER_STAGES];
};

@ -3,6 +3,8 @@
#include <cmath>
#include <algorithm>
#define SHIMMER_MAX_DELAY_TIME 2.0f
ShimmerReverb::ShimmerReverb(float32_t sampling_rate,
float32_t left_delay_time,
float32_t right_delay_time,
@ -35,9 +37,11 @@ ShimmerReverb::~ShimmerReverb()
void ShimmerReverb::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
const static float32_t M2PI = 2.0f * PI;
// 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);
float32_t shimmerOffsetL = this->getShimmerAmplitude() * sin(this->shimmer_phase_);
float32_t shimmerOffsetR = this->getShimmerAmplitude() * cos(this->shimmer_phase_);
// Calculate read position for left and right channel delay lines
unsigned readPosL = static_cast<unsigned>(this->DelayLineLength + this->write_pos_L_ - (this->delay_time_L_ + shimmerOffsetL) * this->getSamplingRate()) % this->DelayLineLength;
@ -63,13 +67,16 @@ void ShimmerReverb::processSample(float32_t inL, float32_t inR, float32_t& outL,
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;
this->shimmer_phase_ += this->shimmer_phase_increment_;
if(this->shimmer_phase_ > M2PI)
{
this->shimmer_phase_ -= M2PI;
}
}
void ShimmerReverb::setLeftDelayTime(float32_t delay_time_L)
{
this->delay_time_L_ = constrain(delay_time_L, 0.0f, SHIMMER_MAX_DELAY_TIME);
this->delay_time_L_ = constrain(delay_time_L, 0.0f, 1.0f);
}
float32_t ShimmerReverb::getLeftDelayTime() const
@ -79,7 +86,7 @@ float32_t ShimmerReverb::getLeftDelayTime() const
void ShimmerReverb::setRightDelayTime(float32_t delay_time_R)
{
this->delay_time_R_ = constrain(delay_time_R, 0.0f, SHIMMER_MAX_DELAY_TIME);
this->delay_time_R_ = constrain(delay_time_R, 0.0f, 1.0f);
}
float32_t ShimmerReverb::getRightDelayTime() const
@ -89,7 +96,9 @@ float32_t ShimmerReverb::getRightDelayTime() const
void ShimmerReverb::setShimmerFrequency(float32_t frequency)
{
this->shimmer_frequency_ = constrain(frequency, 0.0f, this->getSamplingRate() / 2.0f);
const static float32_t M2PI = 2.0f * PI;
this->shimmer_frequency_ = constrain(frequency, 0.0f, 1.0f);
this->shimmer_phase_increment_ = M2PI * mapfloat(this->shimmer_frequency_, 0.0f, 1.0f, 20.0f, 20000.0f) / this->getSamplingRate();
}
float32_t ShimmerReverb::getShimmerFrequency() const
@ -109,7 +118,7 @@ float32_t ShimmerReverb::getShimmerAmplitude() const
void ShimmerReverb::setDecayTime(float32_t decay_time)
{
this->decay_time_ = constrain(decay_time, 0.0f, SHIMMER_MAX_DELAY_TIME);
this->decay_time_ = constrain(decay_time, 0.0f, 1.0f);
}
float32_t ShimmerReverb::getDecayTime() const

@ -21,8 +21,6 @@
#include "fx.h"
#define SHIMMER_MAX_DELAY_TIME 2.0f
class ShimmerReverb : public FXElement
{
DISALLOW_COPY_AND_ASSIGN(ShimmerReverb);
@ -62,11 +60,12 @@ private:
// 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 shimmer_phase_;
float32_t shimmer_phase_increment_;
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
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 parameter in Hz (0.0 === 20Hz - 1.0 === 20kHz)
float32_t shimmer_amplitude_; // Shimmer amplitude (0.0 - 1.0)
float32_t decay_time_; // Reverb decay time in seconds
};

@ -0,0 +1,182 @@
#include "fx_svf.h"
#include <cmath>
StateVariableFilter::StateVariableFilter(float32_t sampling_rate, Type type, float32_t cutoff) :
FXElement(sampling_rate),
type_(type),
cutoff_(0.0f)
{
memset(this->z1_, 0, 2 * sizeof(float32_t));
memset(this->z2_, 0, 2 * sizeof(float32_t));
this->setCutoff(cutoff);
this->setResonance(0.0f);
}
StateVariableFilter::~StateVariableFilter()
{
}
void StateVariableFilter::setFilterType(Type type)
{
if(this->type_ != type)
{
this->type_ = type;
this->updateCoefficients();
}
}
void StateVariableFilter::setCutoff(float32_t cutoff)
{
cutoff = constrain(cutoff, 1.0f, this->getSamplingRate() / 2.0f);
if(this->cutoff_ != cutoff)
{
this->cutoff_ = cutoff;
this->updateCoefficients();
}
}
void StateVariableFilter::setResonance(float32_t resonance)
{
resonance = constrain(resonance, 0.005f, 1.0f);
if(this->resonance_ != resonance)
{
this->resonance_ = resonance;
this->updateCoefficients();
}
}
void StateVariableFilter::setPeakGainDB(float32_t gain)
{
if(this->peak_gain_ != gain)
{
this->peak_gain_ = gain;
this->updateCoefficients();
}
}
void StateVariableFilter::updateCoefficients()
{
// Compute the filter coefficients based on the current parameter values
float32_t w0 = PI * this->cutoff_ / this->getSamplingRate();
float32_t V = pow(10, fabs(this->peak_gain_) / 20.0f);
float32_t K = std::tan(w0);
float32_t K2 = K * K;
float32_t norm;
switch(this->type_)
{
case Type::LPF:
norm = 1.0f / (1.0f + K / this->resonance_ + K2);
this->a0_ = K2 * norm;
this->a1_ = 2.0f * this->a0_;
this->a2_ = this->a0_;
this->b1_ = 2.0f * (K2 - 1.0f) * norm;
this->b2_ = (1.0f - K / this->resonance_ + K2) * norm;
break;
case Type::HPF:
norm = 1.0f / (1.0f + K / this->resonance_ + K2);
this->a0_ = norm;
this->a1_ = -2.0f * this->a0_;
this->a2_ = this->a0_;
this->b1_ = 2.0f * (K2 - 1.0f) * norm;
this->b2_ = (1.0f - K / this->resonance_ + K2) * norm;
break;
case Type::BPF:
norm = 1.0f / (1.0f + K / this->resonance_ + K2);
this->a0_ = K / this->resonance_ * norm;
this->a1_ = 0.0f;
this->a2_ = -this->a0_;
this->b1_ = 2.0f * (K2 - 1.0f) * norm;
this->b2_ = (1.0f - K / this->resonance_ + K2) * norm;
break;
case Type::NOTCH:
norm = 1.0f / (1.0f + K / this->resonance_ + K2);
this->a0_ = (1.0f + K2) * norm;
this->a1_ = 2.0f * (K2 - 1.0f) * norm;
this->a2_ = this->a0_;
this->b1_ = 2.0f * (K2 - 1.0f) * norm;
this->b2_ = (1.0f - K / this->resonance_ + K2) * norm;
break;
case Type::PEQ:
if(this->peak_gain_ >= 0)
{
// boost
norm = 1.0f / (1.0f + 1.0f / this->resonance_ * K + K2);
this->a0_ = (1.0f + V / this->resonance_ * K + K2) * norm;
this->a1_ = 2.0f * (K2 - 1) * norm;
this->a2_ = (1.0f - V / this->resonance_ * K + K2) * norm;
this->b1_ = this->a1_;
this->b2_ = (1.0f - 1.0f / this->resonance_ * K + K2) * norm;
}
else
{
// cut
norm = 1.0f / (1 + V / this->resonance_ * K + K2);
this->a0_ = (1.0f + 1.0f / this->resonance_ * K + K2) * norm;
this->a1_ = 2.0f * (K2 - 1) * norm;
this->a2_ = (1.0f - 1.0f / this->resonance_ * K + K2) * norm;
this->b1_ = this->a1_;
this->b2_ = (1.0f - V / this->resonance_ * K + K2) * norm;
}
break;
case Type::LSH:
if(this->peak_gain_ >= 0)
{
// boost
norm = 1 / (1 + std::sqrt(2) * K + K2);
this->a0_ = (1.0f + std::sqrt(2.0f * V) * K + V * K2) * norm;
this->a1_ = 2.0f * (V * K2 - 1.0f) * norm;
this->a2_ = (1.0f - std::sqrt(2.0f * V) * K + V * K2) * norm;
this->b1_ = 2.0f * (K2 - 1.0f) * norm;
this->b2_ = (1.0f - std::sqrt(2.0f) * K + K2) * norm;
}
else
{
// cutK * K
norm = 1.0f / (1.0f + std::sqrt(2.0f * V) * K + V * K2);
this->a0_ = (1.0f + std::sqrt(2.0f) * K + K2) * norm;
this->a1_ = 2.0f * (K2 - 1.0f) * norm;
this->a2_ = (1.0f - std::sqrt(2.0f) * K + K2) * norm;
this->b1_ = 2.0f * (V * K2 - 1.0f) * norm;
this->b2_ = (1.0f - std::sqrt(2.0f * V) * K + V * K2) * norm;
}
break;
case Type::HSH:
if(this->peak_gain_ >= 0)
{
// boost
norm = 1.0f / (1.0f + std::sqrt(2.0f) * K + K2);
this->a0_ = (V + std::sqrt(2.0f * V) * K + K2) * norm;
this->a1_ = 2.0f * (K2 - V) * norm;
this->a2_ = (V - std::sqrt(2.0f * V) * K + K2) * norm;
this->b1_ = 2.0f * (K2 - 1.0f) * norm;
this->b2_ = (1.0f - std::sqrt(2.0f) * K + K2) * norm;
}
else
{
// cut
norm = 1.0f / (V + std::sqrt(2.0f * V) * K + K2);
this->a0_ = (1.0f + std::sqrt(2.0f) * K + K2) * norm;
this->a1_ = 2.0f * (K2 - 1.0f) * norm;
this->a2_ = (1.0f - std::sqrt(2.0f) * K + K2) * norm;
this->b1_ = 2.0f * (K2 - V) * norm;
this->b2_ = (V - std::sqrt(2.0f * V) * K + K2) * norm;
}
break;
}
}
void StateVariableFilter::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
const float32_t gain = 10.0f;
outL = (inL * this->a0_ + this->z1_[0]) * gain;
this->z1_[0] = inL * this->a1_ + this->z2_[0] - this->b1_ * outL;
this->z2_[0] = inL * this->a2_ - this->b2_ * outL;
outR = (inR * this->a0_ + this->z1_[1]) * gain;
this->z1_[0] = inR * this->a1_ + this->z2_[1] - this->b1_ * outR;
this->z2_[0] = inR * this->a2_ - this->b2_ * outR;
}

@ -0,0 +1,63 @@
// 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_svf.h
//
// State Variable Filter used in Tape Delay
//
#include "fx.h"
class StateVariableFilter : public FXElement
{
DISALLOW_COPY_AND_ASSIGN(StateVariableFilter);
public:
typedef enum
{
LPF, // Low pass filter
HPF, // High pass filter
BPF, // Band pass filter
NOTCH, // Notch Filter
PEQ, // Peaking band EQ filter
LSH, // Low shelf filter
HSH // High shelf filter
} Type;
StateVariableFilter(float32_t sampling_rate, Type type, float32_t cutoff);
virtual ~StateVariableFilter();
void setFilterType(Type type);
void setCutoff(float32_t cutoff);
void setResonance(float32_t resonance);
void setPeakGainDB(float32_t gainDB);
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
private:
void updateCoefficients();
Type type_;
float32_t cutoff_;
float32_t resonance_;
float32_t peak_gain_;
float32_t a0_;
float32_t a1_;
float32_t a2_;
float32_t b1_;
float32_t b2_;
float32_t z1_[2];
float32_t z2_[2];
};

@ -1,18 +1,53 @@
#include "fx_tape_delay.h"
#include <cmath>
#include <algorithm>
#include <iostream>
#define MAX_DELAY_TIME 1.0f
#define MAX_FLUTTER_DELAY_TIME 0.1f
#define LPF_CUTOFF_REF 14000.0f
#define HPF_CUTOFF_REF 60.0f
TapeDelay::LowHighPassFilter::LowHighPassFilter(float32_t sampling_rate) :
FXElement(sampling_rate),
lpf_(sampling_rate, StateVariableFilter::Type::LPF, LPF_CUTOFF_REF),
hpf_(sampling_rate, StateVariableFilter::Type::HPF, HPF_CUTOFF_REF)
{
this->setCutoffChangeRatio(0.0f);
}
TapeDelay::LowHighPassFilter::~LowHighPassFilter()
{
}
void TapeDelay::LowHighPassFilter::setCutoffChangeRatio(float32_t ratio)
{
ratio += 1.0f;
this->lpf_.setCutoff(LPF_CUTOFF_REF * ratio);
this->hpf_.setCutoff(HPF_CUTOFF_REF * ratio);
}
void TapeDelay::LowHighPassFilter::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
this->lpf_.processSample(inL, inR, outL, outR);
this->hpf_.processSample(outL, outR, outL, outR);
}
TapeDelay::TapeDelay(const float32_t sampling_rate, float32_t default_delay_time, float32_t default_flutter_level, float32_t default_feedback_level) :
FXElement(sampling_rate),
MaxSampleDelayTime(2.0f * sampling_rate * MAX_DELAY_TIME),
left_read_pos_(0),
right_read_pos_(0)
MaxSampleDelayTime((MAX_DELAY_TIME + MAX_FLUTTER_DELAY_TIME) * sampling_rate * MAX_DELAY_TIME),
read_pos_L_(0),
read_pos_R_(0),
filter_(sampling_rate),
rnd_generator_(rnd_device_()),
rnd_distribution_(-1.0f, 1.0f)
{
this->left_buffer_ = new float32_t[this->MaxSampleDelayTime];
this->right_buffer_ = new float32_t[this->MaxSampleDelayTime];
this->buffer_L_ = new float32_t[this->MaxSampleDelayTime];
this->buffer_R_ = new float32_t[this->MaxSampleDelayTime];
memset(this->buffer_L_, 0, this->MaxSampleDelayTime * sizeof(float32_t));
memset(this->buffer_R_, 0, this->MaxSampleDelayTime * sizeof(float32_t));
this->setLeftDelayTime(default_delay_time);
this->setRightDelayTime(default_delay_time);
@ -22,69 +57,85 @@ TapeDelay::TapeDelay(const float32_t sampling_rate, float32_t default_delay_time
TapeDelay::~TapeDelay()
{
delete[] this->left_buffer_;
delete[] this->right_buffer_;
delete[] this->buffer_L_;
delete[] this->buffer_R_;
}
void TapeDelay::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
std::cout << "Processing effect: " << typeid(this).name() << std::endl;
// calculate the fluttered delay time
float32_t fluttered_delay_time_L = (MAX_DELAY_TIME * this->getLeftDelayTime() + this->getFlutteredDelayTime()) * this->getSamplingRate();
// Calculate write positions
int left_write_pos = (this->MaxSampleDelayTime + this->left_read_pos_ - static_cast<int>(fluttered_delay_time_L)) % this->MaxSampleDelayTime;
int step = 0;
cout << "Delay processing" << endl;
cout << "Processing #" << (++step) << ": Calculate the fluttered delay time" << endl;
// Calculate the fluttered delay time
float32_t fluttered_delay_time = this->getFlutteredDelayTime();
this->filter_.setCutoffChangeRatio(fluttered_delay_time);
// calculate the fluttered delay time
float32_t fluttered_delay_time_R = (MAX_DELAY_TIME * this->getRightDelayTime() + this->getFlutteredDelayTime()) * this->getSamplingRate();
float32_t fluttered_delay_time_L = (MAX_DELAY_TIME * this->getLeftDelayTime() + fluttered_delay_time) * this->getSamplingRate();
float32_t fluttered_delay_time_R = (MAX_DELAY_TIME * this->getRightDelayTime() + fluttered_delay_time) * this->getSamplingRate();
cout << "Processing #" << (++step) << ": Calculate write positions" << endl;
// Calculate write positions
int right_write_pos = (this->MaxSampleDelayTime + this->right_read_pos_ - static_cast<int>(fluttered_delay_time_R)) % this->MaxSampleDelayTime;
unsigned write_pos_L = static_cast<unsigned>(this->MaxSampleDelayTime + this->read_pos_L_ + fluttered_delay_time_L) % this->MaxSampleDelayTime;
unsigned write_pos_R = static_cast<unsigned>(this->MaxSampleDelayTime + this->read_pos_R_ + fluttered_delay_time_R) % this->MaxSampleDelayTime;
cout << "Processing #" << (++step) << ": Write input to delay buffers" << endl;
// Write input to delay buffers
this->left_buffer_[left_write_pos] = inL;
this->right_buffer_[right_write_pos] = inR;
this->buffer_L_[write_pos_L] = inL;
this->buffer_R_[write_pos_R] = inR;
cout << "Processing #" << (++step) << ": Read from delay buffers and apply feedback" << endl;
// Read from delay buffers and apply feedback
outL = this->left_buffer_[this->left_read_pos_];
outR = this->right_buffer_[this->right_read_pos_];
this->left_buffer_[left_write_pos] += outL * this->getFeedbackLevel();
this->right_buffer_[right_write_pos] += outR * this->getFeedbackLevel();
this->filter_.processSample(
this->buffer_L_[this->read_pos_L_],
this->buffer_R_[this->read_pos_R_],
outL,
outR
);
// outL = this->buffer_L_[this->read_pos_L_];
// outR = this->buffer_R_[this->read_pos_R_];
this->buffer_L_[write_pos_L] += outL * this->getFeedbackLevel();
this->buffer_R_[write_pos_R] += outR * this->getFeedbackLevel();
cout << "Processing #" << (++step) << ": Increment read positions" << endl;
// Increment read positions
++this->left_read_pos_;
if(this->left_read_pos_ >= this->MaxSampleDelayTime)
++this->read_pos_L_;
if(this->read_pos_L_ >= this->MaxSampleDelayTime)
{
this->left_read_pos_ -= this->MaxSampleDelayTime;
this->read_pos_L_ -= this->MaxSampleDelayTime;
}
++this->right_read_pos_;
if(this->right_read_pos_ >= this->MaxSampleDelayTime)
++this->read_pos_R_;
if(this->read_pos_R_ >= this->MaxSampleDelayTime)
{
this->right_read_pos_ -= this->MaxSampleDelayTime;
this->read_pos_R_ -= this->MaxSampleDelayTime;
}
cout << "Processing #" << (++step) << ": Completed" << endl;
}
void TapeDelay::setLeftDelayTime(float32_t delay_time)
{
this->left_delay_time_ = constrain(delay_time, 0.0f, 1.0f);
this->delay_time_L_ = constrain(delay_time, 0.0f, 1.0f);
}
float32_t TapeDelay::getLeftDelayTime() const
{
return this->left_delay_time_;
return this->delay_time_L_;
}
void TapeDelay::setRightDelayTime(float32_t delay_time)
{
this->right_delay_time_ = constrain(delay_time, 0.0f, 1.0f);
this->delay_time_R_ = constrain(delay_time, 0.0f, 1.0f);
}
float32_t TapeDelay::getRightDelayTime() const
{
return this->right_delay_time_;
return this->delay_time_R_;
}
void TapeDelay::setFlutterLevel(float32_t flutter_level)
{
this->flutter_level_ = constrain(flutter_level, 0.0f, 0.1f);
this->flutter_level_ = constrain(flutter_level, 0.0f, 1.0f);
}
float32_t TapeDelay::getFlutterLevel() const
@ -105,10 +156,8 @@ float32_t TapeDelay::getFeedbackLevel() const
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());
float32_t r = this->rnd_distribution_(this->rnd_generator_);
// Scale and bias the random number to the desired flutter range
return r * this->getFlutterLevel();
return MAX_FLUTTER_DELAY_TIME * r * this->getFlutterLevel();
}

@ -20,17 +20,58 @@
#pragma once
#include "fx.h"
#include "fx_svf.h"
#include <random>
#define MAX_DELAY_TIME 2.0f
class TapeDelay : public FXElement
{
DISALLOW_COPY_AND_ASSIGN(TapeDelay);
class LowHighPassFilter : public FXElement
{
DISALLOW_COPY_AND_ASSIGN(LowHighPassFilter);
public:
LowHighPassFilter(float32_t sampling_rate);
virtual ~LowHighPassFilter();
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
void setCutoffChangeRatio(float32_t ratio);
private:
// void processSampleLPF(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR);
// void processSampleHPF(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR);
// float32_t a0_lpf_;
// float32_t a1_lpf_;
// float32_t a2_lpf_;
// float32_t b1_lpf_;
// float32_t b2_lpf_;
// float32_t x1_lpf_[2];
// float32_t x2_lpf_[2];
// float32_t y1_lpf_[2];
// float32_t y2_lpf_[2];
// float32_t a0_hpf_;
// float32_t a1_hpf_;
// float32_t a2_hpf_;
// float32_t b1_hpf_;
// float32_t b2_hpf_;
// float32_t x1_hpf_[2];
// float32_t x2_hpf_[2];
// float32_t y1_hpf_[2];
// float32_t y2_hpf_[2];
StateVariableFilter lpf_;
StateVariableFilter hpf_;
};
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);
TapeDelay(const float32_t sampling_rate, float32_t default_delay_time = 0.25f, float32_t default_flutter_level = 1.0f, float32_t default_wet_level = 0.5f);
virtual ~TapeDelay();
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
@ -52,13 +93,18 @@ private:
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 left_delay_time_; // Left delay time in seconds (0.0 - 2.0)
float32_t right_delay_time_; // Right delay time in seconds (0.0 - 2.0)
float32_t flutter_level_; // Flutter level (0.0 - 0.1)
float32_t feedback_; // Feedback (0.0 - 1.0)
std::mt19937 random_generator_;
size_t read_pos_L_;
size_t read_pos_R_;
float32_t* buffer_L_;
float32_t* buffer_R_;
float32_t delay_time_L_; // Left delay time in seconds (0.0 - 2.0)
float32_t delay_time_R_; // Right delay time in seconds (0.0 - 2.0)
float32_t flutter_level_; // Flutter level (0.0 - 0.1)
float32_t feedback_; // Feedback (0.0 - 1.0)
LowHighPassFilter filter_;
std::random_device rnd_device_;
std::mt19937 rnd_generator_;
std::uniform_real_distribution<float32_t> rnd_distribution_;
};

@ -923,7 +923,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
case ParameterFXChainTapeDelayFlutter:
nValue = constrain((int)nValue, 0, 99);
this->m_FXSpinLock.Acquire();
this->setFXChainTapeDelayFlutter(nValue / 990.0f);
this->setFXChainTapeDelayFlutter(nValue / 99.0f);
this->m_FXSpinLock.Release();
break;
case ParameterFXChainTapeDelayFeedback:
@ -949,19 +949,19 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
case ParameterFXChainShimmerReverbDelayTimeLeft:
nValue = constrain((int)nValue, 0, 99);
this->m_FXSpinLock.Acquire();
this->setFXChainShimmerReverbDelayTimeLeft(2.0f * nValue / 99.0f);
this->setFXChainShimmerReverbDelayTimeLeft(nValue / 99.0f);
this->m_FXSpinLock.Release();
break;
case ParameterFXChainShimmerReverbDelayTimeRight:
nValue = constrain((int)nValue, 0, 99);
this->m_FXSpinLock.Acquire();
this->setFXChainShimmerReverbDelayTimeRight(2.0f * nValue / 99.0f);
this->setFXChainShimmerReverbDelayTimeRight(nValue / 99.0f);
this->m_FXSpinLock.Release();
break;
case ParameterFXChainShimmerReverbFrequency:
nValue = constrain((int)nValue, 0, 99);
this->m_FXSpinLock.Acquire();
this->setFXChainShimmerReverbFrequency(2.0f * nValue / static_cast<float32_t>(this->m_pConfig->GetSampleRate()));
this->setFXChainShimmerReverbFrequency(nValue / 99.0f);
this->m_FXSpinLock.Release();
break;
case ParameterFXChainShimmerReverbAmplitude:
@ -973,7 +973,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
case ParameterFXChainShimmerReverbDecayTime:
nValue = constrain((int)nValue, 0, 99);
this->m_FXSpinLock.Acquire();
this->setFXChainShimmerReverbDecayTime(2.0f * nValue / 99.0f);
this->setFXChainShimmerReverbDecayTime(nValue / 99.0f);
this->m_FXSpinLock.Release();
break;
#endif

Loading…
Cancel
Save