From 6da53dcac5133c90c24dfdf48b5789d169aa58a5 Mon Sep 17 00:00:00 2001 From: abscisys Date: Wed, 4 Jan 2023 01:48:12 +0100 Subject: [PATCH] Fixing the Shimmer Reverb --- .gitignore | 3 +- src/Makefile | 2 +- src/fx.cpp | 1 + src/fx_components.cpp | 18 ++- src/fx_components.h | 3 +- src/fx_engine.hpp | 307 +++++++++++++++++++++++++++++++++++++ src/fx_rack.cpp | 4 +- src/fx_rack.h | 6 +- src/fx_shimmer_reverb.cpp | 193 +++++++++++++---------- src/fx_shimmer_reverb.h | 55 +++---- src/fx_shimmer_reverb2.cpp | 116 -------------- src/fx_shimmer_reverb2.h | 67 -------- src/fx_svf.cpp | 5 +- src/minidexed.cpp | 29 ++-- src/minidexed.h | 5 +- src/performance.ini | 9 +- src/performanceconfig.cpp | 36 +++-- src/performanceconfig.h | 15 +- src/test/Makefile | 21 +-- src/test/fxrack_test.cpp | 199 +++++++++++++++++++----- src/test/wave.h | 10 +- src/test/wavein.cpp | 60 ++++++-- src/test/waveout.cpp | 1 + src/uimenu.cpp | 10 +- 24 files changed, 761 insertions(+), 414 deletions(-) create mode 100644 src/fx_engine.hpp delete mode 100644 src/fx_shimmer_reverb2.cpp delete mode 100644 src/fx_shimmer_reverb2.h diff --git a/.gitignore b/.gitignore index caa07b1..2a85f63 100644 --- a/.gitignore +++ b/.gitignore @@ -48,4 +48,5 @@ sdcard .vscode/ # temporary tests -src/test/fxrack_test \ No newline at end of file +src/test/fxrack_test +src/test/result.wav \ No newline at end of file diff --git a/src/Makefile b/src/Makefile index abb52f3..7ac97f7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -12,7 +12,7 @@ OBJS = main.o kernel.o minidexed.o config.o userinterface.o uimenu.o \ effect_compressor.o effect_platervbstereo.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_reverb2.o fx_rack.o \ + fx_tape_delay.o fx_shimmer_reverb.o fx_rack.o \ uibuttons.o midipin.o OPTIMIZE = -O3 diff --git a/src/fx.cpp b/src/fx.cpp index 8f37a2e..6a88694 100644 --- a/src/fx.cpp +++ b/src/fx.cpp @@ -23,6 +23,7 @@ FXElement::~FXElement() { } + FX::FX(float32_t sampling_rate) : FXBase(sampling_rate) { diff --git a/src/fx_components.cpp b/src/fx_components.cpp index 3c240d3..d00d822 100644 --- a/src/fx_components.cpp +++ b/src/fx_components.cpp @@ -15,8 +15,12 @@ LFO::LFO(float32_t sampling_rate, Waveform waveform, float32_t min_frequency, fl FXBase(sampling_rate), min_frequency_(min_frequency), max_frequency_(max_frequency), + waveform_(waveform), + normalized_frequency_(-1.0f), + frequency_(0.0f), phase_(0.0f), - last_sample_(0.0f), + phase_increment_(0.0f), + current_sample_(0.0f), new_phase_(true), rnd_generator_(rnd_device_()), rnd_distribution_(-1.0f, 1.0f) @@ -94,7 +98,7 @@ float32_t LFO::process() } else { - out = this->last_sample_; + out = this->current_sample_; } break; case Waveform::Noise: @@ -102,7 +106,7 @@ float32_t LFO::process() break; } - this->last_sample_ = out; + this->current_sample_ = out; this->phase_ += this->phase_increment_; if(this->phase_ >= Constants::M2PI) @@ -118,6 +122,12 @@ float32_t LFO::process() return out; } +float32_t LFO::current() const +{ + return this->current_sample_; +} + + //////////////////////////////////// // JitterGenerator implementation // //////////////////////////////////// @@ -125,6 +135,8 @@ JitterGenerator::JitterGenerator(float32_t sampling_rate) : FXBase(sampling_rate), rnd_generator_(rnd_device_()), rnd_distribution_(-1.0f, 1.0f), + speed_(-1.0f), + magnitude_(-1.0f), phase_(0.0f), phase_increment_(0.0f) { diff --git a/src/fx_components.h b/src/fx_components.h index f9eb0c3..112f0af 100644 --- a/src/fx_components.h +++ b/src/fx_components.h @@ -54,6 +54,7 @@ public: float32_t getFrequency() const; float32_t process(); + float32_t current() const; private: const float32_t min_frequency_; @@ -63,7 +64,7 @@ private: float32_t frequency_; float32_t phase_; float32_t phase_increment_; - float32_t last_sample_; + float32_t current_sample_; bool new_phase_; std::random_device rnd_device_; std::mt19937 rnd_generator_; diff --git a/src/fx_engine.hpp b/src/fx_engine.hpp new file mode 100644 index 0000000..f813c24 --- /dev/null +++ b/src/fx_engine.hpp @@ -0,0 +1,307 @@ +#pragma once + +#include "fx_components.h" + +#include +#include +#include + +#define MAKE_INTEGRAL_FRACTIONAL(x) \ + int32_t x ## _integral = static_cast(x); \ + float x ## _fractional = x - static_cast(x ## _integral); + +enum Format +{ + FORMAT_12_BIT, + FORMAT_16_BIT, + FORMAT_32_BIT +}; + +enum LFOIndex +{ + LFO_1, + LFO_2 +}; + +template +struct DataType +{ +}; + +inline int16_t clip16(int32_t x) +{ + if(x > INT16_MAX) + { + return INT16_MAX; + } + + if(x < INT16_MIN) + { + return INT16_MIN; + } + + return static_cast(x); +} + +template <> +struct DataType +{ + typedef uint16_t T; + + static inline float32_t decompress(T value) + { + return static_cast(static_cast(value)) / 4096.0f; + } + + static inline T compress(float32_t value) + { + return clip16(static_cast(value * 4096.0f)); + } +}; + +template < + size_t size, + Format format = FORMAT_16_BIT> +class FxEngine : public FXBase +{ + DISALLOW_COPY_AND_ASSIGN(FxEngine); + +public: + typedef typename DataType::T T; + + FxEngine(float32_t sampling_rate) : + FXBase(sampling_rate) + { + this->buffer_ = new uint16_t[size]; + this->lfo_[LFO_1] = new LFO(sampling_rate, LFO::Waveform::Sine, 0.0f, 32.0f); + this->lfo_[LFO_2] = new LFO(sampling_rate, LFO::Waveform::Sine, 0.0f, 32.0f); + this->clear(); + } + + ~FxEngine() + { + delete[] this->buffer_; + delete this->lfo_[LFO_1]; + delete this->lfo_[LFO_2]; + } + + void clear() + { + memset(this->buffer_, 0, size * sizeof(uint16_t)); + this->write_ptr_ = 0; + } + + struct Empty + { + }; + + template + struct Reserve + { + typedef T Tail; + enum + { + length = l + }; + }; + + template + struct DelayLine + { + enum + { + length = DelayLine::length, + base = DelayLine::base + DelayLine::length + 1 + }; + }; + + template + struct DelayLine + { + enum + { + length = Memory::length, + base = 0 + }; + }; + + class Context + { + DISALLOW_COPY_AND_ASSIGN(Context); + friend class FxEngine; + + public: + Context() : + accumulator_(0.0f), + previous_read_(0.0f), + buffer_(nullptr), + write_ptr_(0) + { + memset(this->lfo_value_, 0, 2 * sizeof(float32_t)); + } + + ~Context() + { + } + + inline void load(float32_t value) + { + this->accumulator_ = value; + } + + inline void read(float32_t value, float32_t scale) + { + this->accumulator_ += value * scale; + } + + inline void read(float32_t value) + { + this->accumulator_ += value; + } + + inline void write(float32_t &value) + { + value = this->accumulator_; + } + + inline void write(float32_t &value, float32_t scale) + { + value = this->accumulator_; + this->accumulator_ *= scale; + } + + template + inline void write(D &d, int32_t offset, float32_t scale) + { + assert(D::base + D::length <= size); + T w = DataType::compress(this->accumulator_); + if(offset == -1) + { + this->buffer_[(this->write_ptr_ + D::base + D::length - 1) & MASK] = w; + } + else + { + this->buffer_[(this->write_ptr_ + D::base + offset) & MASK] = w; + } + this->accumulator_ *= scale; + } + + template + inline void write(D &d, float32_t scale) + { + this->write(d, 0, scale); + } + + template + inline void writeAllPass(D &d, int32_t offset, float32_t scale) + { + this->write(d, offset, scale); + this->accumulator_ += this->previous_read_; + } + + template + inline void writeAllPass(D &d, float32_t scale) + { + this->writeAllPass(d, 0, scale); + } + + template + inline void read(D &d, int32_t offset, float32_t scale) + { + assert(D::base + D::length <= size); + T r; + if(offset == -1) + { + r = this->buffer_[(this->write_ptr_ + D::base + D::length - 1) & MASK]; + } + else + { + r = this->buffer_[(this->write_ptr_ + D::base + offset) & MASK]; + } + float32_t r_f = DataType::decompress(r); + this->previous_read_ = r_f; + this->accumulator_ += r_f * scale; + } + + template + inline void read(D &d, float32_t scale) + { + this->read(d, 0, scale); + } + + inline void lp(float32_t &state, float32_t coefficient) + { + state += coefficient * (this->accumulator_ - state); + this->accumulator_ = state; + } + + inline void hp(float32_t &state, float32_t coefficient) + { + state += coefficient * (this->accumulator_ - state); + this->accumulator_ -= state; + } + + template + inline void interpolate(D &d, float32_t offset, float32_t scale) + { + assert(D::base + D::length <= size); + MAKE_INTEGRAL_FRACTIONAL(offset); + float32_t a = DataType::decompress(this->buffer_[(this->write_ptr_ + offset_integral + D::base) & MASK]); + float32_t b = DataType::decompress(this->buffer_[(this->write_ptr_ + offset_integral + D::base + 1) & MASK]); + float32_t x = a + (b - a) * offset_fractional; + this->previous_read_ = x; + this->accumulator_ += x * scale; + } + + template + inline void interpolate(D &d, float32_t offset, LFOIndex index, float32_t amplitude, float32_t scale) + { + assert(D::base + D::length <= size); + offset += amplitude * lfo_value_[index]; + MAKE_INTEGRAL_FRACTIONAL(offset); + float32_t a = DataType::decompress(this->buffer_[(this->write_ptr_ + offset_integral + D::base) & MASK]); + float32_t b = DataType::decompress(this->buffer_[(this->write_ptr_ + offset_integral + D::base + 1) & MASK]); + float32_t x = a + (b - a) * offset_fractional; + this->previous_read_ = x; + this->accumulator_ += x * scale; + } + + private: + float32_t accumulator_; + float32_t previous_read_; + float32_t lfo_value_[2]; + T* buffer_; + int32_t write_ptr_; + }; + + inline void setLFOFrequency(LFOIndex index, float32_t frequency) + { + this->lfo_[index]->setFrequency(frequency); + } + + inline void start(Context *c) + { + --this->write_ptr_; + if(this->write_ptr_ < 0) + { + this->write_ptr_ += size; + } + c->accumulator_ = 0.0f; + c->previous_read_ = 0.0f; + c->buffer_ = buffer_; + c->write_ptr_ = write_ptr_; + c->lfo_value_[LFO_1] = this->lfo_[LFO_1]->process(); + c->lfo_value_[LFO_2] = this->lfo_[LFO_2]->process(); + } + +private: + enum + { + MASK = size - 1 + }; + + uint16_t* buffer_; + unsigned write_ptr_; + + LFO* lfo_[2]; +}; diff --git a/src/fx_rack.cpp b/src/fx_rack.cpp index 9b855d9..f2a9cde 100644 --- a/src/fx_rack.cpp +++ b/src/fx_rack.cpp @@ -15,7 +15,7 @@ FXRack::FXRack(float32_t sampling_rate, bool enable, float32_t wet) : this->fxOrbitone_ = new FXUnit(sampling_rate); this->fxPhaser_ = new FXUnit(sampling_rate); this->fxTapeDelay_ = new FXUnit(sampling_rate); - this->fxShimmerReverb_ = new ShimmerReverb2(sampling_rate); + this->fxShimmerReverb_ = new FXUnit(sampling_rate); this->registerFX(this->fxTube_); this->registerFX(this->fxChorus_); @@ -144,7 +144,7 @@ FXUnit* FXRack::getTapeDelay() return this->fxTapeDelay_; } -ShimmerReverb2* FXRack::getShimmerReverb() +FXUnit* FXRack::getShimmerReverb() { return this->fxShimmerReverb_; } diff --git a/src/fx_rack.h b/src/fx_rack.h index 3477035..a549c5b 100644 --- a/src/fx_rack.h +++ b/src/fx_rack.h @@ -26,7 +26,7 @@ #include "fx_orbitone.h" #include "fx_phaser.h" #include "fx_tape_delay.h" -#include "fx_shimmer_reverb2.h" +#include "fx_shimmer_reverb.h" #include @@ -55,7 +55,7 @@ public: FXUnit* getOrbitone(); FXUnit* getPhaser(); FXUnit* getTapeDelay(); - ShimmerReverb2* getShimmerReverb(); + FXUnit* getShimmerReverb(); private: void registerFX(FXElement* fx); @@ -70,5 +70,5 @@ private: FXUnit* fxOrbitone_; FXUnit* fxPhaser_; FXUnit* fxTapeDelay_; - ShimmerReverb2* fxShimmerReverb_; + FXUnit* fxShimmerReverb_; }; \ No newline at end of file diff --git a/src/fx_shimmer_reverb.cpp b/src/fx_shimmer_reverb.cpp index 2541482..1eab3f6 100644 --- a/src/fx_shimmer_reverb.cpp +++ b/src/fx_shimmer_reverb.cpp @@ -3,124 +3,147 @@ #include #include -#define SHIMMER_MAX_DELAY_TIME 2.0f - -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) : FXElement(sampling_rate), - DelayLineLength(static_cast(2.0f * 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]; +#define TAIL , -1 - 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(float32_t sampling_rate) : + FXElement(sampling_rate), + engine_(sampling_rate) +{ + this->engine_.setLFOFrequency(LFO_1, 0.5f / sampling_rate); + this->engine_.setLFOFrequency(LFO_2, 0.3f / sampling_rate); + this->setInputGain(1.0f); + this->setLP(0.7f); + this->setDiffusion(0.625f); } ShimmerReverb::~ShimmerReverb() { - delete[] this->delay_line_L_; - delete[] this->delay_line_R_; } 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_); - float32_t shimmerOffsetR = this->getShimmerAmplitude() * cos(this->shimmer_phase_); - - // Calculate read position for left and right channel delay lines - unsigned readPosL = static_cast(this->DelayLineLength + this->write_pos_L_ - (this->delay_time_L_ + shimmerOffsetL) * this->getSamplingRate()) % this->DelayLineLength; - unsigned readPosR = static_cast(this->DelayLineLength + this->write_pos_R_ - (this->delay_time_R_ + shimmerOffsetR) * this->getSamplingRate()) % 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 - outL = inL + delaySampleL * decay; - outR = inR + delaySampleR * decay; + // This is the Griesinger topology described in the Dattorro paper + // (4 AP diffusers on the input, then a loop of 2x 2AP+1Delay). + // Modulation is applied in the loop of the first diffuser AP for additional + // smearing; and to the two long delays for a slow shimmer/chorus effect. + typedef Engine::Reserve<113, + Engine::Reserve<162, + Engine::Reserve<241, + Engine::Reserve<399, + Engine::Reserve<1653, + Engine::Reserve<2038, + Engine::Reserve<3411, + Engine::Reserve<1913, + Engine::Reserve<1663, + Engine::Reserve<4782> > > > > > > > > > Memory; + Engine::DelayLine ap1; + Engine::DelayLine ap2; + Engine::DelayLine ap3; + Engine::DelayLine ap4; + Engine::DelayLine dap1a; + Engine::DelayLine dap1b; + Engine::DelayLine del1; + Engine::DelayLine dap2a; + Engine::DelayLine dap2b; + Engine::DelayLine del2; + Engine::Context c; + + const float32_t kap = this->diffusion_; + const float32_t klp = this->lp_; + const float32_t krt = this->reverb_time_; + const float32_t gain = this->input_gain_; + + float32_t lp_1 = this->lp_decay_1_; + float32_t lp_2 = this->lp_decay_2_; + + float32_t wet; + float32_t apout = 0.0f; + engine_.start(&c); - // Write input samples to delay lines - this->delay_line_L_[this->write_pos_L_] = outL; - this->delay_line_R_[this->write_pos_R_] = outR; - - // 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->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, 1.0f); -} - -float32_t ShimmerReverb::getLeftDelayTime() const -{ - return this->delay_time_L_; + // Smear AP1 inside the loop. + c.interpolate(ap1, 10.0f, LFO_1, 60.0f, 1.0f); + c.write(ap1, 100, 0.0f); + + c.read(inL + inR, gain); + + // Diffuse through 4 allpasses. + c.read(ap1 TAIL, kap); + c.writeAllPass(ap1, -kap); + c.read(ap2 TAIL, kap); + c.writeAllPass(ap2, -kap); + c.read(ap3 TAIL, kap); + c.writeAllPass(ap3, -kap); + c.read(ap4 TAIL, kap); + c.writeAllPass(ap4, -kap); + c.write(apout); + + // Main reverb loop. + c.load(apout); + c.interpolate(del2, 4680.0f, LFO_2, 100.0f, krt); + c.lp(lp_1, klp); + c.read(dap1a TAIL, -kap); + c.writeAllPass(dap1a, kap); + c.read(dap1b TAIL, kap); + c.writeAllPass(dap1b, -kap); + c.write(del1, 2.0f); + c.write(wet, 0.0f); + + outR += wet; + + c.load(apout); + // c.Interpolate(del1, 4450.0f, LFO_1, 50.0f, krt); + c.read(del1 TAIL, krt); + c.lp(lp_2, klp); + c.read(dap2a TAIL, kap); + c.writeAllPass(dap2a, -kap); + c.read(dap2b TAIL, -kap); + c.writeAllPass(dap2b, kap); + c.write(del2, 2.0f); + c.write(wet, 0.0f); + + outR += wet; + + this->lp_decay_1_ = lp_1; + this->lp_decay_2_ = lp_2; } -void ShimmerReverb::setRightDelayTime(float32_t delay_time_R) +void ShimmerReverb::setInputGain(float32_t gain) { - this->delay_time_R_ = constrain(delay_time_R, 0.0f, 1.0f); + this->input_gain_ = constrain(gain, 0.0f, 1.0f); } -float32_t ShimmerReverb::getRightDelayTime() const +float32_t ShimmerReverb::getInputGain() const { - return this->delay_time_R_; + return this->input_gain_; } -void ShimmerReverb::setShimmerFrequency(float32_t frequency) +void ShimmerReverb::setTime(float32_t time) { - this->shimmer_frequency_ = constrain(frequency, 0.0f, 1.0f); - this->shimmer_phase_increment_ = Constants::M2PI * mapfloat(this->shimmer_frequency_, 0.0f, 1.0f, 20.0f, 20000.0f) / this->getSamplingRate(); + this->reverb_time_ = constrain(time, 0.0f, 1.0f); } -float32_t ShimmerReverb::getShimmerFrequency() const +float32_t ShimmerReverb::getTime() const { - return this->shimmer_frequency_; + return this->reverb_time_; } -void ShimmerReverb::setShimmerAmplitude(float32_t amplitude) +void ShimmerReverb::setDiffusion(float32_t diffusion) { - this->shimmer_amplitude_ = constrain(amplitude, 0.0f, 1.0f); + this->diffusion_ = constrain(diffusion, 0.0f, 1.0f); } -float32_t ShimmerReverb::getShimmerAmplitude() const +float32_t ShimmerReverb::getDiffusion() const { - return this->shimmer_amplitude_; + return this->diffusion_; } -void ShimmerReverb::setDecayTime(float32_t decay_time) +void ShimmerReverb::setLP(float32_t lp) { - this->decay_time_ = constrain(decay_time, 0.0f, 1.0f); + this->lp_ = constrain(lp, 0.0f, 1.0f); } -float32_t ShimmerReverb::getDecayTime() const +float32_t ShimmerReverb::getLP() const { - return this->decay_time_; + return this->lp_; } diff --git a/src/fx_shimmer_reverb.h b/src/fx_shimmer_reverb.h index 3c99a75..34d235b 100644 --- a/src/fx_shimmer_reverb.h +++ b/src/fx_shimmer_reverb.h @@ -13,59 +13,48 @@ // // -// fx_shimmer_reverb.h +// fx_shimmer_reverb3.h // -// Stereo Shimmer reverb proposed in the context of the MiniDexed project +// Stereo Shimmer Reverb proposed in the context of the MiniDexed project +// It is adapted from the Shimmer Reverb that could be found on Cloud EuroRack module from Mutable Instrruments // #pragma once #include "fx_components.h" +#include "fx_engine.hpp" class ShimmerReverb : public FXElement { 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); + ShimmerReverb(float32_t sampling_rate); virtual ~ShimmerReverb(); virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override; - void setLeftDelayTime(float32_t delay_time_L); - float32_t getLeftDelayTime() const; + void setInputGain(float32_t gain); + float32_t getInputGain() const; - void setRightDelayTime(float32_t delay_time_R); - float32_t getRightDelayTime() const; + void setTime(float32_t time); + float32_t getTime() const; - void setShimmerFrequency(float32_t frequency); - float32_t getShimmerFrequency() const; + void setDiffusion(float32_t diffusion); + float32_t getDiffusion() const; - void setShimmerAmplitude(float32_t amplitude); - float32_t getShimmerAmplitude() const; - - void setDecayTime(float32_t decay_time); - float32_t getDecayTime() const; + void setLP(float32_t lp); + float32_t getLP() const; private: - const unsigned DelayLineLength; - float32_t* delay_line_L_; - float32_t* delay_line_R_; + typedef FxEngine<16384, FORMAT_16_BIT> Engine; + Engine engine_; - // Current write position for left and right channel delay lines - unsigned write_pos_L_; - unsigned write_pos_R_; - float32_t shimmer_phase_; - float32_t shimmer_phase_increment_; + float32_t input_gain_; + float32_t reverb_time_; + float32_t diffusion_; + float32_t lp_; - 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 -}; \ No newline at end of file + float32_t lp_decay_1_; + float32_t lp_decay_2_; +}; diff --git a/src/fx_shimmer_reverb2.cpp b/src/fx_shimmer_reverb2.cpp deleted file mode 100644 index bde9494..0000000 --- a/src/fx_shimmer_reverb2.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "fx_shimmer_reverb2.h" - -#include -#include - -ShimmerReverb2::ShimmerReverb2( float32_t sampling_rate, - float32_t decay, - float32_t diffusion, - float32_t pitch_shift) : - FXElement(sampling_rate), - FXUnitModule(), - reverb_buffer_index_(0) -{ - this->setDecay(decay); - this->setDiffusion(diffusion); - this->setPitchShift(pitch_shift); -} - -ShimmerReverb2::~ShimmerReverb2() -{ -} - -void ShimmerReverb2::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) -{ - if(!this->isEnable()) - { - outL = inL; - outR = inR; - return; - } - - // Processing left channel - { - // Read the sample from the reverb buffer - float32_t reverb_sample = this->reverb_buffer_L_[this->reverb_buffer_index_]; - - // Calculate the pitch-shifted sample - float32_t pitch_shift_sample = reverb_sample * this->getPitchShift(); - - // Interpolate the pitch-shifted sample to the original pitch - float32_t pitch_shift_frac = pitch_shift_sample - std::floor(pitch_shift_sample); - unsigned pitch_shift_index = static_cast(SHIMMER_BUFFER_SIZE + std::floor(pitch_shift_sample)) % SHIMMER_BUFFER_SIZE; - float32_t pitch_shift_interp = - (1.0f - pitch_shift_frac) * this->reverb_buffer_L_[pitch_shift_index] + - pitch_shift_frac * this->reverb_buffer_L_[(pitch_shift_index + 1) % SHIMMER_BUFFER_SIZE]; - - // Calculate the wet (reverb) and dry (original) samples - float32_t dry_level = 1.0f - this->getWetLevel(); - float32_t wet_sample = dry_level * inL + this->getWetLevel() * pitch_shift_interp; - float32_t dry_sample = this->getWetLevel() * inL + dry_level * pitch_shift_interp; - - outL = dry_sample; - - // Write the wet sample to the reverb buffer with the diffusion coefficient applied - this->reverb_buffer_L_[this->reverb_buffer_index_] = wet_sample + (reverb_sample * (1.0f - this->getDiffusion() / this->getSamplingRate() / this->getDecay())); - } - - // Processing right channel - { - // Read the sample from the reverb buffer - float32_t reverb_sample = this->reverb_buffer_R_[this->reverb_buffer_index_]; - - // Calculate the pitch-shifted sample - float32_t pitch_shift_sample = reverb_sample * this->getPitchShift(); - - // Interpolate the pitch-shifted sample to the original pitch - float32_t pitch_shift_frac = pitch_shift_sample - std::floor(pitch_shift_sample); - unsigned pitch_shift_index = static_cast(SHIMMER_BUFFER_SIZE + std::floor(pitch_shift_sample)) % SHIMMER_BUFFER_SIZE; - float32_t pitch_shift_interp = - (1.0f - pitch_shift_frac) * this->reverb_buffer_R_[pitch_shift_index] + - pitch_shift_frac * this->reverb_buffer_R_[(pitch_shift_index + 1) % SHIMMER_BUFFER_SIZE]; - - // Calculate the wet (reverb) and dry (original) samples - float32_t dry_level = 1.0f - this->getWetLevel(); - float32_t wet_sample = dry_level * inR + this->getWetLevel() * pitch_shift_interp; - float32_t dry_sample = this->getWetLevel() * inR + dry_level * pitch_shift_interp; - - outR = dry_sample; - - // Write the wet sample to the reverb buffer with the diffusion coefficient applied - this->reverb_buffer_R_[this->reverb_buffer_index_] = wet_sample + (reverb_sample * (1.0f - this->getDiffusion() / this->getSamplingRate() / this->getDecay())); - } - - // Increment the buffer index and wrap around if necessary - this->reverb_buffer_index_ = (this->reverb_buffer_index_ + 1) % SHIMMER_BUFFER_SIZE; -} - -void ShimmerReverb2::setDecay(float32_t decay) -{ - this->decay_ = constrain(decay, SHIMMER_MIN_DECAY, SHIMMER_MAX_DECAY); -} - -float32_t ShimmerReverb2::getDecay() const -{ - return this->decay_; -} - -void ShimmerReverb2::setDiffusion(float32_t diffusion) -{ - this->diffusion_ = constrain(diffusion, 0.0f, 1.0f); -} - -float32_t ShimmerReverb2::getDiffusion() const -{ - return this->diffusion_; -} - -void ShimmerReverb2::setPitchShift(float32_t pitch_shift) -{ - this->pitch_shift_ = constrain(pitch_shift, SHIMMER_MIN_PITCH_RATIO, SHIMMER_MAX_PITCH_RATIO); -} - -float32_t ShimmerReverb2::getPitchShift() const -{ - return this->pitch_shift_; -} diff --git a/src/fx_shimmer_reverb2.h b/src/fx_shimmer_reverb2.h deleted file mode 100644 index 26ffbda..0000000 --- a/src/fx_shimmer_reverb2.h +++ /dev/null @@ -1,67 +0,0 @@ -// 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 . -// - -// -// fx_shimmer_reverb2.h -// -// Stereo Shimmer reverb proposed in the context of the MiniDexed project -// -#pragma once - -#include "fx_components.h" -#include "fx_unit.hpp" - -#define SHIMMER_BUFFER_SIZE 1024 - -#define SHIMMER_MIN_DECAY 0.0f -#define SHIMMER_MAX_DECAY 10.0f - -#define SHIMMER_MIN_PITCH_RATIO 0.5f -#define SHIMMER_MAX_PITCH_RATIO 2.0f - -class ShimmerReverb2 : - public virtual FXElement, - public virtual FXUnitModule -{ - DISALLOW_COPY_AND_ASSIGN(ShimmerReverb2); - -public: - ShimmerReverb2( float32_t sampling_rate, - float32_t decay = 3.0f, - float32_t diffusion = 0.5f, - float32_t pitch_shift = 2.0f); - - virtual ~ShimmerReverb2(); - - virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override; - - void setDecay(float32_t delay_time_L); - float32_t getDecay() const; - - void setDiffusion(float32_t delay_time_R); - float32_t getDiffusion() const; - - void setPitchShift(float32_t frequency); - float32_t getPitchShift() const; - -private: - float32_t reverb_buffer_L_[SHIMMER_BUFFER_SIZE]; - float32_t reverb_buffer_R_[SHIMMER_BUFFER_SIZE]; - unsigned reverb_buffer_index_; - - // Current write position for left and right channel delay lines - float32_t decay_; // Reverb decay time in seconds (0 - 10) - float32_t diffusion_; // The degree to which the reverb is spread out over time (0 - 1) - float32_t pitch_shift_; // Determines the pitch shift ratio applied to the reverb (0.5 - 2.0) -}; diff --git a/src/fx_svf.cpp b/src/fx_svf.cpp index b786f54..22451ad 100644 --- a/src/fx_svf.cpp +++ b/src/fx_svf.cpp @@ -5,11 +5,14 @@ StateVariableFilter::StateVariableFilter(float32_t sampling_rate, Type type, float32_t cutoff) : FXElement(sampling_rate), type_(type), - cutoff_(0.0f) + cutoff_(0.0f), + resonance_(0.0f), + peak_gain_(0.0f) { memset(this->z1_, 0, 2 * sizeof(float32_t)); memset(this->z2_, 0, 2 * sizeof(float32_t)); + this->setPeakGainDB(1.0f); this->setCutoff(cutoff); this->setResonance(0.0f); } diff --git a/src/minidexed.cpp b/src/minidexed.cpp index cc94a71..52ad3b4 100644 --- a/src/minidexed.cpp +++ b/src/minidexed.cpp @@ -208,9 +208,10 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, // FXChain > ShimmerReverb parameters this->SetParameter(ParameterFXChainShimmerReverbEnable, 1); this->SetParameter(ParameterFXChainShimmerReverbWet, 70); - this->SetParameter(ParameterFXChainShimmerReverbDecay, 30); + this->SetParameter(ParameterFXChainShimmerReverbInputGain, 99); + this->SetParameter(ParameterFXChainShimmerReverbTime, 80); this->SetParameter(ParameterFXChainShimmerReverbDiffusion, 80); - this->SetParameter(ParameterFXChainShimmerReverbPitchShift, 99); + this->SetParameter(ParameterFXChainShimmerReverbLP, 70); #endif // END setup FXRack }; @@ -944,10 +945,16 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue) this->fx_rack->getShimmerReverb()->setWetLevel(nValue / 99.0f); this->m_FXSpinLock.Release(); break; - case ParameterFXChainShimmerReverbDecay: + case ParameterFXChainShimmerReverbInputGain: nValue = constrain((int)nValue, 0, 99); this->m_FXSpinLock.Acquire(); - this->fx_rack->getShimmerReverb()->setDecay(mapfloat(nValue, 0, 99, SHIMMER_MIN_DECAY, SHIMMER_MAX_DECAY)); + this->fx_rack->getShimmerReverb()->setInputGain(nValue / 99.0f); + this->m_FXSpinLock.Release(); + break; + case ParameterFXChainShimmerReverbTime: + nValue = constrain((int)nValue, 0, 99); + this->m_FXSpinLock.Acquire(); + this->fx_rack->getShimmerReverb()->setTime(nValue / 99.0f); this->m_FXSpinLock.Release(); break; case ParameterFXChainShimmerReverbDiffusion: @@ -956,10 +963,10 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue) this->fx_rack->getShimmerReverb()->setDiffusion(nValue / 99.0f); this->m_FXSpinLock.Release(); break; - case ParameterFXChainShimmerReverbPitchShift: + case ParameterFXChainShimmerReverbLP: nValue = constrain((int)nValue, 0, 99); this->m_FXSpinLock.Acquire(); - this->fx_rack->getShimmerReverb()->setPitchShift(mapfloat(nValue, 0, 99, SHIMMER_MIN_PITCH_RATIO, SHIMMER_MAX_PITCH_RATIO)); + this->fx_rack->getShimmerReverb()->setLP(nValue / 99.0f); this->m_FXSpinLock.Release(); break; #endif @@ -1415,9 +1422,10 @@ bool CMiniDexed::DoSavePerformance (void) this->m_PerformanceConfig.SetFXChainTapeDelayFeedback(this->m_nParameter[ParameterFXChainTapeDelayFeedback]); this->m_PerformanceConfig.SetFXChainShimmerReverbEnable(!!this->m_nParameter[ParameterFXChainShimmerReverbEnable]); this->m_PerformanceConfig.SetFXChainShimmerReverbWet(this->m_nParameter[ParameterFXChainShimmerReverbWet]); - this->m_PerformanceConfig.SetFXChainShimmerReverbDecay(this->m_nParameter[ParameterFXChainShimmerReverbDecay]); + this->m_PerformanceConfig.SetFXChainShimmerReverbInputGain(this->m_nParameter[ParameterFXChainShimmerReverbInputGain]); + this->m_PerformanceConfig.SetFXChainShimmerReverbTime(this->m_nParameter[ParameterFXChainShimmerReverbTime]); this->m_PerformanceConfig.SetFXChainShimmerReverbDiffusion(this->m_nParameter[ParameterFXChainShimmerReverbDiffusion]); - this->m_PerformanceConfig.SetFXChainShimmerReverbPitchShift(this->m_nParameter[ParameterFXChainShimmerReverbPitchShift]); + this->m_PerformanceConfig.SetFXChainShimmerReverbLP(this->m_nParameter[ParameterFXChainShimmerReverbLP]); #endif // END FXRack parameters @@ -1842,9 +1850,10 @@ void CMiniDexed::LoadPerformanceParameters(void) this->SetParameter(ParameterFXChainTapeDelayFeedback, this->m_PerformanceConfig.GetFXChainTapeDelayFeedback()); this->SetParameter(ParameterFXChainShimmerReverbEnable, this->m_PerformanceConfig.GetFXChainShimmerReverbEnable()); this->SetParameter(ParameterFXChainShimmerReverbWet, this->m_PerformanceConfig.GetFXChainShimmerReverbWet()); - this->SetParameter(ParameterFXChainShimmerReverbDecay, this->m_PerformanceConfig.GetFXChainShimmerReverbDecay()); + this->SetParameter(ParameterFXChainShimmerReverbInputGain, this->m_PerformanceConfig.GetFXChainShimmerReverbInputGain()); + this->SetParameter(ParameterFXChainShimmerReverbTime, this->m_PerformanceConfig.GetFXChainShimmerReverbTime()); this->SetParameter(ParameterFXChainShimmerReverbDiffusion, this->m_PerformanceConfig.GetFXChainShimmerReverbDiffusion()); - this->SetParameter(ParameterFXChainShimmerReverbPitchShift, this->m_PerformanceConfig.GetFXChainShimmerReverbPitchShift()); + this->SetParameter(ParameterFXChainShimmerReverbLP, this->m_PerformanceConfig.GetFXChainShimmerReverbLP()); #endif } diff --git a/src/minidexed.h b/src/minidexed.h index 496e832..a21e1b1 100644 --- a/src/minidexed.h +++ b/src/minidexed.h @@ -188,9 +188,10 @@ public: // FXChain > ShimmerReverb parameters ParameterFXChainShimmerReverbEnable, ParameterFXChainShimmerReverbWet, - ParameterFXChainShimmerReverbDecay, + ParameterFXChainShimmerReverbInputGain, + ParameterFXChainShimmerReverbTime, ParameterFXChainShimmerReverbDiffusion, - ParameterFXChainShimmerReverbPitchShift, + ParameterFXChainShimmerReverbLP, #endif // END FXRack global parameters definition diff --git a/src/performance.ini b/src/performance.ini index b1f440a..3dc13ed 100644 --- a/src/performance.ini +++ b/src/performance.ini @@ -313,8 +313,9 @@ FXChainTapeDelayLeftDelayTime=15 FXChainTapeDelayRightDelayTime=22 FXChainTapeDelayFlutter=0 FXChainTapeDelayFeedback=35 -FXChainShimmerReverbEnable=0 +FXChainShimmerReverbEnable=1 FXChainShimmerReverbWet=70 -FXChainShimmerReverbDecay=30 -FXChainShimmerReverbDiffusion=80 -FXChainShimmerReverbPitchShift=99 +FXChainShimmerReverbInputGain=45 +FXChainShimmerReverbTime=89 +FXChainShimmerReverbDiffusion=75 +FXChainShimmerReverbLP=80 diff --git a/src/performanceconfig.cpp b/src/performanceconfig.cpp index 47b7ab1..90769fc 100644 --- a/src/performanceconfig.cpp +++ b/src/performanceconfig.cpp @@ -191,9 +191,10 @@ bool CPerformanceConfig::Load (void) this->m_nFXChainTapeDelayFeedback = this->m_Properties.GetNumber("FXChainTapeDelayFeedback", 35); this->m_bFXChainShimmerReverbEnable = this->m_Properties.GetNumber("FXChainShimmerReverbEnable", 1); this->m_nFXChainShimmerReverbWet = this->m_Properties.GetNumber("FXChainShimmerReverbWet", 70); - this->m_nFXChainShimmerReverbDecay = this->m_Properties.GetNumber("FXChainShimmerReverbDecay", 30); + this->m_nFXChainShimmerReverbInputGain = this->m_Properties.GetNumber("FXChainShimmerReverbInputGain", 30); + this->m_nFXChainShimmerReverbTime = this->m_Properties.GetNumber("FXChainShimmerReverbTime", 30); this->m_nFXChainShimmerReverbDiffusion = this->m_Properties.GetNumber("FXChainShimmerReverbDiffusion", 30); - this->m_nFXChainShimmerReverbPitchShift = this->m_Properties.GetNumber("FXChainShimmerReverbPitchShift", 99); + this->m_nFXChainShimmerReverbLP = this->m_Properties.GetNumber("FXChainShimmerReverbLP", 99); #endif return bResult; @@ -346,9 +347,10 @@ bool CPerformanceConfig::Save (void) this->m_Properties.SetNumber("FXChainTapeDelayFeedback", m_nFXChainTapeDelayFeedback); this->m_Properties.SetNumber("FXChainShimmerReverbEnable", m_bFXChainShimmerReverbEnable ? 1 : 0); this->m_Properties.SetNumber("FXChainShimmerReverbWet", m_nFXChainShimmerReverbWet); - this->m_Properties.SetNumber("FXChainShimmerReverbDecay", m_nFXChainShimmerReverbDecay); + this->m_Properties.SetNumber("FXChainShimmerReverbInputGain", m_nFXChainShimmerReverbInputGain); + this->m_Properties.SetNumber("FXChainShimmerReverbTime", m_nFXChainShimmerReverbTime); this->m_Properties.SetNumber("FXChainShimmerReverbDiffusion", m_nFXChainShimmerReverbDiffusion); - this->m_Properties.SetNumber("FXChainShimmerReverbPitchShift", m_nFXChainShimmerReverbPitchShift); + this->m_Properties.SetNumber("FXChainShimmerReverbLP", m_nFXChainShimmerReverbLP); #endif return m_Properties.Save (); @@ -1154,9 +1156,14 @@ unsigned CPerformanceConfig::GetFXChainShimmerReverbWet(void) const return this->m_nFXChainShimmerReverbWet; } -unsigned CPerformanceConfig::GetFXChainShimmerReverbDecay(void) const +unsigned CPerformanceConfig::GetFXChainShimmerReverbInputGain(void) const { - return this->m_nFXChainShimmerReverbDecay; + return this->m_nFXChainShimmerReverbInputGain; +} + +unsigned CPerformanceConfig::GetFXChainShimmerReverbTime(void) const +{ + return this->m_nFXChainShimmerReverbTime; } unsigned CPerformanceConfig::GetFXChainShimmerReverbDiffusion(void) const @@ -1164,9 +1171,9 @@ unsigned CPerformanceConfig::GetFXChainShimmerReverbDiffusion(void) const return this->m_nFXChainShimmerReverbDiffusion; } -unsigned CPerformanceConfig::GetFXChainShimmerReverbPitchShift(void) const +unsigned CPerformanceConfig::GetFXChainShimmerReverbLP(void) const { - return this->m_nFXChainShimmerReverbPitchShift; + return this->m_nFXChainShimmerReverbLP; } void CPerformanceConfig::SetFXChainEnable(bool bValue) @@ -1324,9 +1331,14 @@ void CPerformanceConfig::SetFXChainShimmerReverbWet(unsigned nValue) this->m_nFXChainShimmerReverbWet = nValue; } -void CPerformanceConfig::SetFXChainShimmerReverbDecay(unsigned nValue) +void CPerformanceConfig::SetFXChainShimmerReverbInputGain(unsigned nValue) +{ + this->m_nFXChainShimmerReverbInputGain = nValue; +} + +void CPerformanceConfig::SetFXChainShimmerReverbTime(unsigned nValue) { - this->m_nFXChainShimmerReverbDecay = nValue; + this->m_nFXChainShimmerReverbTime = nValue; } void CPerformanceConfig::SetFXChainShimmerReverbDiffusion(unsigned nValue) @@ -1334,9 +1346,9 @@ void CPerformanceConfig::SetFXChainShimmerReverbDiffusion(unsigned nValue) this->m_nFXChainShimmerReverbDiffusion = nValue; } -void CPerformanceConfig::SetFXChainShimmerReverbPitchShift(unsigned nValue) +void CPerformanceConfig::SetFXChainShimmerReverbLP(unsigned nValue) { - this->m_nFXChainShimmerReverbPitchShift = nValue; + this->m_nFXChainShimmerReverbLP = nValue; } #endif diff --git a/src/performanceconfig.h b/src/performanceconfig.h index ef42483..dca1e90 100644 --- a/src/performanceconfig.h +++ b/src/performanceconfig.h @@ -149,9 +149,10 @@ public: unsigned GetFXChainTapeDelayFeedback(void) const; bool GetFXChainShimmerReverbEnable(void) const; unsigned GetFXChainShimmerReverbWet(void) const; - unsigned GetFXChainShimmerReverbDecay(void) const; + unsigned GetFXChainShimmerReverbInputGain(void) const; + unsigned GetFXChainShimmerReverbTime(void) const; unsigned GetFXChainShimmerReverbDiffusion(void) const; - unsigned GetFXChainShimmerReverbPitchShift(void) const; + unsigned GetFXChainShimmerReverbLP(void) const; void SetFXChainEnable(bool bValue); void SetFXChainWet(unsigned nValue); @@ -184,9 +185,10 @@ public: void SetFXChainTapeDelayFeedback(unsigned nValue); void SetFXChainShimmerReverbEnable(unsigned nValue); void SetFXChainShimmerReverbWet(unsigned nValue); - void SetFXChainShimmerReverbDecay(unsigned nValue); + void SetFXChainShimmerReverbInputGain(unsigned nValue); + void SetFXChainShimmerReverbTime(unsigned nValue); void SetFXChainShimmerReverbDiffusion(unsigned nValue); - void SetFXChainShimmerReverbPitchShift(unsigned nValue); + void SetFXChainShimmerReverbLP(unsigned nValue); #endif bool VoiceDataFilled(unsigned nTG); @@ -288,9 +290,10 @@ private: unsigned m_nFXChainTapeDelayFeedback; bool m_bFXChainShimmerReverbEnable; unsigned m_nFXChainShimmerReverbWet; - unsigned m_nFXChainShimmerReverbDecay; + unsigned m_nFXChainShimmerReverbInputGain; + unsigned m_nFXChainShimmerReverbTime; unsigned m_nFXChainShimmerReverbDiffusion; - unsigned m_nFXChainShimmerReverbPitchShift; + unsigned m_nFXChainShimmerReverbLP; #endif }; diff --git a/src/test/Makefile b/src/test/Makefile index 50b18b1..c2b855b 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -1,8 +1,9 @@ CXX := gcc -CXXFLAGS := -g -O2 -DEFINES := +# CXXFLAGS := -O2 +CXXFLAGS := -g +DEFINES := -DCPU=x86 INCLUDES := -I../../CMSIS_5/CMSIS/DSP/Include/ -I../../CMSIS_5/CMSIS/Core/Include/ -GCC := $(CXX) $(INCLUDES) $(CXXFLAGS) +GCC := $(CXX) $(INCLUDES) $(CXXFLAGS) LD := gcc LIBS := -lm -lstdc++ @@ -10,7 +11,6 @@ LIBS := -lm -lstdc++ OBJS := \ wavein.o \ waveout.o \ - waveplay.o \ fx.o \ fx_components.o \ fx_svf.o \ @@ -20,14 +20,14 @@ OBJS := \ fx_orbitone.o \ fx_flanger.o \ fx_tape_delay.o \ - fx_shimmer_reverb2.o \ + fx_shimmer_reverb.o \ fx_rack.o \ fxrack_test.o test: fxrack_test ./fxrack_test -# %.o: ../%.cpp +# %.o: ../%.cpp # $(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@ wavein.o: wavein.cpp @@ -36,8 +36,8 @@ wavein.o: wavein.cpp waveout.o: waveout.cpp $(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@ -waveplay.o: waveplay.cpp - $(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@ +# waveplay.o: waveplay.cpp +# $(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@ fx.o: ../fx.cpp $(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@ @@ -66,7 +66,10 @@ fx_flanger.o: ../fx_flanger.cpp fx_tape_delay.o: ../fx_tape_delay.cpp $(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@ -fx_shimmer_reverb2.o: ../fx_shimmer_reverb2.cpp +../fx_shimmer_reverb3.cpp: ../fx_engine.hpp + touch ../fx_shimmer_reverb3.cpp + +fx_shimmer_reverb.o: ../fx_shimmer_reverb.cpp $(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@ fx_rack.o: ../fx_rack.cpp diff --git a/src/test/fxrack_test.cpp b/src/test/fxrack_test.cpp index c691397..3c3e8b9 100644 --- a/src/test/fxrack_test.cpp +++ b/src/test/fxrack_test.cpp @@ -10,30 +10,117 @@ using namespace std; #define MAX_SVF_SAMPLES 10000000 #define MAX_NB_ERRORS 100 -void testFlutter() +enum CosineOscillatorMode { + COSINE_OSCILLATOR_APPROXIMATE, + COSINE_OSCILLATOR_EXACT +}; + +class CosineOscillator +{ +public: + CosineOscillator() {} + ~CosineOscillator() {} + + template + inline void Init(float frequency) + { + if (mode == COSINE_OSCILLATOR_APPROXIMATE) + { + InitApproximate(frequency); + } + else + { + iir_coefficient_ = 2.0f * cosf(2.0f * M_PI * frequency); + initial_amplitude_ = iir_coefficient_ * 0.25f; + } + Start(); + } + + inline void InitApproximate(float frequency) + { + float sign = 16.0f; + frequency -= 0.25f; + if (frequency < 0.0f) + { + frequency = -frequency; + } + else + { + if (frequency > 0.5f) + { + frequency -= 0.5f; + } + else + { + sign = -16.0f; + } + } + iir_coefficient_ = sign * frequency * (1.0f - 2.0f * frequency); + initial_amplitude_ = iir_coefficient_ * 0.25f; + } + + inline void Start() + { + y1_ = initial_amplitude_; + y0_ = 0.5f; + } + + inline float value() const + { + return y1_ + 0.5f; + } + + inline float Next() + { + float temp = y0_; + y0_ = iir_coefficient_ * y0_ - y1_; + y1_ = temp; + return temp + 0.5f; + } + +private: + float y1_; + float y0_; + float iir_coefficient_; + float initial_amplitude_; + + DISALLOW_COPY_AND_ASSIGN(CosineOscillator); +}; + +void testCosineOsc(unsigned& step) +{ + cout << "Step #" << (++step) << ": Testing CosineOscillator" << endl; + + CosineOscillator osc; + osc.template Init(32.0f * 0.5f / 32000.0f); + + for(unsigned i = 0; i < 2000; ++i) + { + cout << "LFO #" << i << ": " << osc.Next() << endl; + } +} + +void testFlutter(unsigned& step) +{ + cout << "Step #" << (++step) << ": Testing JitterGenerator" << endl; + JitterGenerator jg(44100.0f); jg.setSpeed(1.0f); jg.setMagnitude(0.1f); - for(unsigned i = 0; i < 1000; ++i) + for (unsigned i = 0; i < 1000; ++i) { cout << jg.process() << endl; } } -int main() +void testSVF(unsigned& step, std::mt19937& gen, std::uniform_real_distribution dist) { - // testFlutter(); - - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_real_distribution dist(-1.0f, 1.0f); - - unsigned step = 0; float32_t inL, inR; float32_t outL, outR; StateVariableFilter svf(44100.0f, StateVariableFilter::Type::LPF, 12000.0f); + cout << "Step #" << (++step) << ": Testing SVF in LPF mode" << endl; { svf.setFilterType(StateVariableFilter::Type::LPF); @@ -41,15 +128,17 @@ int main() svf.setResonance(0.0f); unsigned nbSamples = 0; unsigned nbErrors = 0; - while(nbErrors < MAX_NB_ERRORS && nbSamples < MAX_SVF_SAMPLES) + while (nbErrors < MAX_NB_ERRORS && nbSamples < MAX_SVF_SAMPLES) { nbSamples++; inL = dist(gen); inR = dist(gen); svf.processSample(inL, inR, outL, outR); - if(std::abs(outL) > 1.0f) nbErrors++; - if(std::abs(outR) > 1.0f) nbErrors++; + if (std::abs(outL) > 1.0f) + nbErrors++; + if (std::abs(outR) > 1.0f) + nbErrors++; } cout << "nbSamples: " << nbSamples << " -- nbErrors: " << nbErrors << endl; } @@ -61,48 +150,73 @@ int main() svf.setResonance(0.0f); unsigned nbSamples = 0; unsigned nbErrors = 0; - while(nbErrors < MAX_NB_ERRORS && nbSamples < MAX_SVF_SAMPLES) + while (nbErrors < MAX_NB_ERRORS && nbSamples < MAX_SVF_SAMPLES) { nbSamples++; inL = dist(gen); inR = dist(gen); svf.processSample(inL, inR, outL, outR); - if(std::abs(outL) > 1.0f) nbErrors++; - if(std::abs(outR) > 1.0f) nbErrors++; - } + if (std::abs(outL) > 1.0f) + nbErrors++; + if (std::abs(outR) > 1.0f) + nbErrors++; + } cout << "nbSamples: " << nbSamples << " -- nbErrors: " << nbErrors << endl; } +} + +int main() +{ + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution dist(-1.0f, 1.0f); + + unsigned step = 0; + + // testCosineOsc(step); + // testFlutter(step, gen, dist); + // testSVF(step); cout << "Step #" << (++step) << ": Intanciation FXRack" << endl; - FXRack* rack = new FXRack(44100.0f); + FXRack *rack = new FXRack(44100.0f); cout << "Step #" << (++step) << ": Test preparation" << endl; rack->setEnable(true); rack->setWetLevel(1.0f); rack->getTube()->setEnable(false); + rack->getTube()->setWetLevel(1.0f); + rack->getTube()->setOverdrive(1.0f); + rack->getChorus()->setEnable(false); rack->getPhaser()->setEnable(false); - rack->getOrbitone()->setEnable(true); + + rack->getOrbitone()->setEnable(false); + rack->getOrbitone()->setWetLevel(0.8f); + rack->getOrbitone()->setFeedback(1.0f); + rack->getFlanger()->setEnable(false); + rack->getTapeDelay()->setEnable(false); - // rack->getTapeDelay()->setWetLevel(0.6f); - // rack->getTapeDelay()->setLeftDelayTime(0.1f); - // rack->getTapeDelay()->setLeftDelayTime(0.05f); - // rack->getTapeDelay()->setFlutterLevel(0.25f); - // rack->getTapeDelay()->setFeedbak(0.5f); + rack->getTapeDelay()->setWetLevel(0.6f); + rack->getTapeDelay()->setLeftDelayTime(0.075f); + rack->getTapeDelay()->setLeftDelayTime(0.05f); + rack->getTapeDelay()->setFlutterLevel(0.0f); + rack->getTapeDelay()->setFeedbak(0.5f); + rack->getShimmerReverb()->setEnable(true); - rack->getShimmerReverb()->setWetLevel(60); - rack->getShimmerReverb()->setDecay(30); - rack->getShimmerReverb()->setDiffusion(80); - rack->getShimmerReverb()->setPitchShift(99); + rack->getShimmerReverb()->setWetLevel(0.7f); + rack->getShimmerReverb()->setInputGain(0.45f); + rack->getShimmerReverb()->setTime(0.89f); + rack->getShimmerReverb()->setDiffusion(0.75f); + rack->getShimmerReverb()->setLP(0.8f); - const unsigned nSamples = 3000; + const unsigned nSamples = 1; float32_t inSamples[2][nSamples]; float32_t outSamples[2][nSamples]; - for(unsigned i = 0; i < nSamples; ++i) + for (unsigned i = 0; i < nSamples; ++i) { inSamples[0][i] = dist(gen); inSamples[1][i] = dist(gen); @@ -115,22 +229,35 @@ int main() rack->process(inSamples[0], inSamples[1], outSamples[0], outSamples[1], nSamples); cout << "Step #" << (++step) << ": Render results" << endl; - for(unsigned i = 0; i < nSamples; ++i) + for (unsigned i = 0; i < nSamples; ++i) { std::cout << "#" << i << " " << inSamples[0][i] << " --> " << outSamples[0][i] << " = " << ((outSamples[0][i] - inSamples[0][i]) * 100.0f / inSamples[0][i]) << "%" << std::endl; } + unsigned nbRepeats = 4; unsigned size; float32_t** samples = readWaveFile("test.wav", size); - float32_t* sampleOutL = new float32_t[size]; - float32_t* sampleOutR = new float32_t[size]; - rack->process(samples[0], samples[1], sampleOutL, sampleOutR, size); + float32_t* sampleOutL = new float32_t[size * nbRepeats]; + float32_t* sampleOutR = new float32_t[size * nbRepeats]; + memset(sampleOutL, 0, size * nbRepeats * sizeof(float32_t)); + memset(sampleOutR, 0, size * nbRepeats * sizeof(float32_t)); + + for (unsigned i = 0; i < nbRepeats; ++i) + { + rack->process(samples[0], samples[1], sampleOutL + i * size, sampleOutR + i * size, size); + } - playSound(sampleOutL, sampleOutR, size, 44100, 16); + saveWaveFile("result.wav", sampleOutL, sampleOutR, nbRepeats * size, 44100, 16); + + delete[] sampleOutL; + delete[] sampleOutR; + delete[] samples[0]; + delete[] samples[1]; + delete[] samples; cout << "Step #" << (++step) << ": Test cleanup" << endl; delete rack; return 0; -} \ No newline at end of file +} diff --git a/src/test/wave.h b/src/test/wave.h index fc325c9..21b6fcb 100644 --- a/src/test/wave.h +++ b/src/test/wave.h @@ -29,8 +29,8 @@ void saveWaveFile(const std::string& fileName, int sampleRate, int bitsPerSample); -void playSound(float32_t* LChannel, - float32_t* RChannel, - unsigned size, - int sampleRate, - int bitsPerSample); \ No newline at end of file +// void playSound(float32_t* LChannel, +// float32_t* RChannel, +// unsigned size, +// int sampleRate, +// int bitsPerSample); diff --git a/src/test/wavein.cpp b/src/test/wavein.cpp index 6758c66..c69d8b8 100644 --- a/src/test/wavein.cpp +++ b/src/test/wavein.cpp @@ -2,6 +2,7 @@ #include #include +#include float32_t** readWaveFile(const std::string& fileName, unsigned& size) { @@ -15,6 +16,10 @@ float32_t** readWaveFile(const std::string& fileName, unsigned& size) WaveHeader header; file.read((char*)&header, sizeof(header)); + std::cout << "Sampling rate: " << header.sampleRate << std::endl; + std::cout << "# channels: " << header.numChannels << std::endl; + std::cout << "Resolution: " << header.bitsPerSample << " bits" << std::endl; + if(strncmp(header.chunkId, "RIFF", 4) != 0 || strncmp(header.format, "WAVE", 4) != 0) { std::cerr << "Error: not a WAVE file" << std::endl; @@ -32,43 +37,71 @@ float32_t** readWaveFile(const std::string& fileName, unsigned& size) float32_t* RChannel = new float32_t[size]; unsigned i = 0; - while(!file.eof()) + while(!file.eof() && i < size) { if(header.bitsPerSample == 8) { uint8_t LSample; - uint8_t RSample; file.read((char*)&LSample, 1); - file.read((char*)&RSample, 1); LChannel[i] = LSample / 128.0f - 1.0f; - RChannel[i] = RSample / 128.0f - 1.0f; + if(header.numChannels == 2) + { + uint8_t RSample; + file.read((char*)&RSample, 1); + RChannel[i] = RSample / 128.0f - 1.0f; + } + else + { + RChannel[i] = LChannel[i]; + } } else if (header.bitsPerSample == 16) { int16_t LSample; - int16_t RSample; file.read((char*)&LSample, 2); - file.read((char*)&RSample, 2); LChannel[i] = LSample / 32768.0f; - RChannel[i] = RSample / 32768.0f; + if(header.numChannels == 2) + { + int16_t RSample; + file.read((char*)&RSample, 2); + RChannel[i] = RSample / 32768.0f; + } + else + { + RChannel[i] = LChannel[i]; + } } else if (header.bitsPerSample == 24) { int32_t LSample; - int32_t RSample; file.read((char*)&LSample, 3); - file.read((char*)&RSample, 3); LChannel[i] = LSample / 8388608.0f; - RChannel[i] = RSample / 8388608.0f; + if(header.numChannels == 2) + { + int32_t RSample; + file.read((char*)&RSample, 3); + RChannel[i] = RSample / 8388608.0f; + } + else + { + RChannel[i] = LChannel[i]; + } } else if (header.bitsPerSample == 32) { int32_t LSample; - int32_t RSample; file.read((char*)&LSample, 4); - file.read((char*)&RSample, 4); LChannel[i] = LSample / 2147483648.0f; - RChannel[i] = RSample / 2147483648.0f; + if(header.numChannels == 2) + { + int32_t RSample; + file.read((char*)&RSample, 4); + RChannel[i] = RSample / 2147483648.0f; + } + else + { + RChannel[i] = LChannel[i]; + } } else { @@ -78,6 +111,7 @@ float32_t** readWaveFile(const std::string& fileName, unsigned& size) ++i; } + assert(i == size); float32_t** result = new float32_t*[2]; result[0] = LChannel; diff --git a/src/test/waveout.cpp b/src/test/waveout.cpp index 7ddc7df..5ebf5cd 100644 --- a/src/test/waveout.cpp +++ b/src/test/waveout.cpp @@ -28,6 +28,7 @@ void saveWaveFile(const std::string& fileName, header.subchunk2Size = size * header.blockAlign; header.chunkSize = 36 + header.subchunk2Size; header.subchunk1Size = 16; + header.audioFormat = 1; std::strncpy(header.chunkId, "RIFF", 4); std::strncpy(header.format, "WAVE", 4); diff --git a/src/uimenu.cpp b/src/uimenu.cpp index 1ef3100..9d5d89a 100644 --- a/src/uimenu.cpp +++ b/src/uimenu.cpp @@ -217,9 +217,10 @@ const CUIMenu::TMenuItem CUIMenu::s_FXChainShimmerReverb[] = { {"Enable", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainShimmerReverbEnable}, {"Wet Lvl", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainShimmerReverbWet}, - {"Decay", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainShimmerReverbDecay}, + {"Gain", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainShimmerReverbInputGain}, + {"Time", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainShimmerReverbTime}, {"Diffus", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainShimmerReverbDiffusion}, - {"PtchShft", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainShimmerReverbPitchShift}, + {"LowPass", EditGlobalParameter, 0, CMiniDexed::ParameterFXChainShimmerReverbLP}, {0} }; @@ -353,9 +354,10 @@ const CUIMenu::TParameter CUIMenu::s_GlobalParameter[CMiniDexed::ParameterUnknow // FXChain > ShimmerReverb parameters {0, 1, 1, ToOnOff}, // ParameterFXChainShimmerReverbEnable {0, 99, 1}, // ParameterFXChainShimmerReverbWet - {0, 99, 1}, // ParameterFXChainShimmerReverbDecay + {0, 99, 1}, // ParameterFXChainShimmerReverbInputGain + {0, 99, 1}, // ParameterFXChainShimmerReverbTime {0, 99, 1}, // ParameterFXChainShimmerReverbDiffusion - {0, 99, 1}, // ParameterFXChainShimmerReverbPitchShift + {0, 99, 1}, // ParameterFXChainShimmerReverbLP #endif // END FXRack global parameters mapping definition