diff --git a/src/Makefile b/src/Makefile index 6e0e621..0144f0a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -10,7 +10,7 @@ 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_phaser.o fx_tape_delay.o fx_shimmer_reverb.o fx_rack.o \ + fx.o fx_orbitone.o fx_phaser.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 c23efe7..8f37a2e 100644 --- a/src/fx.cpp +++ b/src/fx.cpp @@ -14,6 +14,15 @@ float32_t FXBase::getSamplingRate() const return this->SamplingRate; } +FXElement::FXElement(float32_t sampling_rate) : + FXBase(sampling_rate) +{ +} + +FXElement::~FXElement() +{ +} + FX::FX(float32_t sampling_rate) : FXBase(sampling_rate) { diff --git a/src/fx.h b/src/fx.h index 94387c6..c8d9203 100644 --- a/src/fx.h +++ b/src/fx.h @@ -41,14 +41,28 @@ private: const float32_t SamplingRate; }; +class FXElement : public FXBase +{ + DISALLOW_COPY_AND_ASSIGN(FXElement); + +protected: + FXElement(float32_t sampling_rate); + +public: + virtual ~FXElement(); + + virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) = 0; +}; + class FX : public FXBase { DISALLOW_COPY_AND_ASSIGN(FX); protected: FX(float32_t sampling_rate); - virtual ~FX(); public: + virtual ~FX(); + virtual void process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) = 0; }; diff --git a/src/fx_orbitone.cpp b/src/fx_orbitone.cpp new file mode 100644 index 0000000..1f8b984 --- /dev/null +++ b/src/fx_orbitone.cpp @@ -0,0 +1,98 @@ +#include "fx_orbitone.h" + +#include + +OrbitoneParameter::OrbitoneParameter(float32_t sampling_rate, float32_t feedback) : + FXBase(sampling_rate), + feedback_(feedback) +{ +} + +OrbitoneParameter::~OrbitoneParameter() +{ +} + +void OrbitoneParameter::setFeedback(float32_t feedback) +{ + this->feedback_ = constrain(feedback, 0.0f, 1.0f); +} + +float32_t OrbitoneParameter::getFeedback() const +{ + return this->feedback_; +} + + + +// OrbitoneStage implementation +OrbitoneStage::OrbitoneStage(float32_t sampling_rate, OrbitoneParameter* params, float32_t frequency) : + FXElement(sampling_rate), + params_(params), + frequency_(frequency), + phase_(0.0f) +{ + 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; + } + + // Apply feedback to the stage's input + outL = inL + osc * this->params_->getFeedback(); + outR = inR + osc * this->params_->getFeedback(); +} + + + +// Orbitone implementation +Orbitone::Orbitone(float32_t sampling_rate, float32_t feedback) : + FXElement(sampling_rate), + params_(sampling_rate, feedback) +{ + 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); + } +} + +Orbitone::~Orbitone() +{ + for(unsigned i = 0; i < NUM_ORBITONR_STAGES; ++i) + { + delete this->stages_[i]; + } +} + +void Orbitone::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) +{ + // Process the input sample through each stage of the phaser + float32_t sampleL = inL; + float32_t sampleR = inR; + for(unsigned s = 0; s < NUM_ORBITONR_STAGES; ++s) + { + this->stages_[s]->processSample(sampleL, sampleR, sampleL, sampleR); + } + + // Modulate the output of the phaser using the LFO + outL = sampleL; + outR = sampleR; +} + +void Orbitone::setFeedback(float32_t feedback) +{ + this->params_.setFeedback(feedback); +} + +float32_t Orbitone::getFeedback() const +{ + return this->params_.getFeedback(); +} diff --git a/src/fx_orbitone.h b/src/fx_orbitone.h new file mode 100644 index 0000000..9add593 --- /dev/null +++ b/src/fx_orbitone.h @@ -0,0 +1,75 @@ +// 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_orbitone.h +// +// Stereo Orbitone audio effects proposed in the context of the MiniDexed project +// +#pragma once + +#include "fx.h" + +class OrbitoneStage; + +class OrbitoneParameter : public FXBase +{ + friend class OrbitoneStage; + DISALLOW_COPY_AND_ASSIGN(OrbitoneParameter); + +public: + OrbitoneParameter(float32_t sampling_rate, float32_t feedback = 0.5f); + virtual ~OrbitoneParameter(); + + void setFeedback(float32_t feedback); + inline float32_t getFeedback() const; + +private: + float32_t feedback_; // Amount of feedback to apply to the stage's input +}; + +class OrbitoneStage : public FXElement +{ + DISALLOW_COPY_AND_ASSIGN(OrbitoneStage); + +public: + OrbitoneStage(float32_t sampling_rate, OrbitoneParameter* params, float32_t frequency); + + 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 +}; + +#define NUM_ORBITONR_STAGES 4 + +class Orbitone : public FXElement +{ + DISALLOW_COPY_AND_ASSIGN(Orbitone); + +public: + Orbitone(float32_t sampling_rate, float32_t feedback = 0.5f); + virtual ~Orbitone(); + + virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override; + + void setFeedback(float32_t feedback); + inline float32_t getFeedback() const; + +private: + OrbitoneParameter params_; + OrbitoneStage* stages_[NUM_ORBITONR_STAGES]; +}; \ No newline at end of file diff --git a/src/fx_phaser.cpp b/src/fx_phaser.cpp index 1dc360d..6c780b5 100644 --- a/src/fx_phaser.cpp +++ b/src/fx_phaser.cpp @@ -51,14 +51,14 @@ float32_t PhaserParameter::getQ() const // PhaserStage implementation PhaserStage::PhaserStage(float32_t sampling_rate, PhaserParameter* params) : - FXBase(sampling_rate), + FXElement(sampling_rate), params_(params) { memset(this->z1, 0, 2 * sizeof(float32_t)); memset(this->z2, 0, 2 * sizeof(float32_t)); } -void PhaserStage::process(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) +void PhaserStage::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { outL = (this->params_->a0 * inL + this->params_->a1 * this->z1[0] + this->params_->a2 * this->z2[0]) / this->params_->a0; this->z2[0] = this->z1[0]; @@ -73,7 +73,7 @@ void PhaserStage::process(float32_t inL, float32_t inR, float32_t& outL, float32 // Phaser implementation Phaser::Phaser(float32_t sampling_rate, float32_t frequency, float32_t q) : - FX(sampling_rate), + FXElement(sampling_rate), params_(sampling_rate, frequency, q), phase_(0.0f), phase_increment_(0.0f) @@ -93,37 +93,24 @@ Phaser::~Phaser() } } -void Phaser::process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) +void Phaser::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { - float sampleL; - float sampleR; - for(unsigned i = 0; i < nSamples; ++i) + // Process the input sample through each stage of the phaser + float32_t sampleL = inL; + float32_t sampleR = inR; + for(unsigned s = 0; s < NUM_PHASER_STAGES; ++s) { - // Process the input sample through each stage of the phaser - sampleL = *left_input; - sampleR = *right_input; - for(unsigned s = 0; s < NUM_PHASER_STAGES; ++s) - { - this->stages_[s]->process(sampleL, sampleR, sampleL, sampleR); - } - - // Modulate the output of the phaser using the LFO - *left_output = sampleL * (0.5f + 0.5f * cos(this->phase_)); - *right_output = 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; - } - - // Move to next input sample - ++left_input; - ++right_input; - - // Move to next output sample - ++left_output; - ++right_output; + this->stages_[s]->processSample(sampleL, sampleR, sampleL, sampleR); + } + + // 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; } } diff --git a/src/fx_phaser.h b/src/fx_phaser.h index c1e91ab..63da390 100644 --- a/src/fx_phaser.h +++ b/src/fx_phaser.h @@ -46,14 +46,14 @@ private: float32_t a0, a1, a2, b1, b2; // Coefficients for the stage's filter }; -class PhaserStage : public FXBase +class PhaserStage : public FXElement { DISALLOW_COPY_AND_ASSIGN(PhaserStage); public: PhaserStage(float32_t sampling_rate, PhaserParameter* params); - void process(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR); + virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override; private: PhaserParameter* params_; // All paremters of the phaser including the inner coefficients @@ -62,7 +62,7 @@ private: #define NUM_PHASER_STAGES 6 -class Phaser : public FX +class Phaser : public FXElement { DISALLOW_COPY_AND_ASSIGN(Phaser); @@ -70,7 +70,7 @@ public: Phaser(float32_t sampling_rate, float32_t frequency = 0.5f, float32_t q = 1.0f); 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; + virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override; void setFrequency(float32_t frequency); inline float32_t getFrequency() const; diff --git a/src/fx_rack.cpp b/src/fx_rack.cpp index 3778984..e35901d 100644 --- a/src/fx_rack.cpp +++ b/src/fx_rack.cpp @@ -1,7 +1,7 @@ #include "fx_rack.h" -FXUnit::FXUnit(float32_t sampling_rate, FX& fx, float32_t wet_level) : - FX(sampling_rate), +FXUnit::FXUnit(float32_t sampling_rate, FXElement& fx, float32_t wet_level) : + FXElement(sampling_rate), fx_(fx) { this->setWetLevel(wet_level); @@ -11,24 +11,13 @@ FXUnit::~FXUnit() { } -void FXUnit::process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) +void FXUnit::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { - this->fx_.process(left_input, right_input, left_output, right_output, nSamples); + this->fx_.processSample(inL, inR, outL, outR); - 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; - } + // Mix wet and dry signals + outL = this->getWetLevel() * outL + (1.0f - this->getWetLevel()) * inL; + outR = this->getWetLevel() * outR + (1.0f - this->getWetLevel()) * inR; } void FXUnit::setWetLevel(float32_t wet_level) @@ -61,16 +50,40 @@ FXRack::~FXRack() 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++) + float32_t sampleInL; + float32_t sampleInR; + float32_t sampleOutL; + float32_t sampleOutR; + + for(unsigned i = 0; i < nSamples; ++i) { - (*it)->process(left_input, right_input, left_output, right_output, nSamples); + sampleInL = *left_input; + sampleInR = *right_input; + sampleOutL = 0.0f; + sampleOutR = 0.0f; + + for(FXChain::iterator it = this->fx_chain_.begin(); it != this->fx_chain_.end(); it++) + { + (*it)->processSample(sampleInL, sampleInR, sampleOutL, sampleOutR); + + sampleInL = sampleOutL; + sampleInR = sampleOutR; + } - left_input = left_output; - right_input = right_output; + *left_output = sampleOutL; + *right_output = sampleOutR; + + // Move inputs by 1 sample + ++left_input; + ++right_input; + + // Move outputs by 1 sample + ++left_input; + ++right_input; } } -void FXRack::registerFX(FX* fx) +void FXRack::registerFX(FXElement* fx) { this->fx_chain_.push_back(new FXUnit(this->getSamplingRate(), *fx)); } diff --git a/src/fx_rack.h b/src/fx_rack.h index d425a1d..73dc586 100644 --- a/src/fx_rack.h +++ b/src/fx_rack.h @@ -26,21 +26,21 @@ #include -class FXUnit : public FX +class FXUnit : public FXElement { DISALLOW_COPY_AND_ASSIGN(FXUnit); public: - FXUnit(float32_t sampling_rate, FX& fx, float32_t wet_level = 0.5f); + FXUnit(float32_t sampling_rate, FXElement& 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; + virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override; void setWetLevel(float32_t wet_level); inline float32_t getWetLevel() const; private: - FX& fx_; // Embedded FX + FXElement& fx_; // Embedded FX float32_t wet_level_; // How much the signal is affected by the inner FX (0.0 - 1.0) }; @@ -57,7 +57,7 @@ public: 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); + void registerFX(FXElement* fx); FXChain fx_chain_; }; \ No newline at end of file diff --git a/src/fx_shimmer_reverb.cpp b/src/fx_shimmer_reverb.cpp index 651f657..e16b3c9 100644 --- a/src/fx_shimmer_reverb.cpp +++ b/src/fx_shimmer_reverb.cpp @@ -8,7 +8,7 @@ ShimmerReverb::ShimmerReverb(float32_t sampling_rate, float32_t right_delay_time, float32_t shimmer_frequency, float32_t shimmer_amplitude, - float32_t decay_time) : FX(sampling_rate), + float32_t decay_time) : FXElement(sampling_rate), DelayLineLength(static_cast(SHIMMER_MAX_DELAY_TIME * sampling_rate)), write_pos_L_(0), write_pos_R_(0), @@ -33,53 +33,42 @@ ShimmerReverb::~ShimmerReverb() 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) +void ShimmerReverb::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { - 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; - } + // 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 + outL = inL + delaySampleL * decay; + outR = inR + delaySampleR * decay; + + // 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->getShimmerFrequency() / this->getSamplingRate(); + if(this->shimmer_phase_ > 1.0f) this->shimmer_phase_ -= 1.0f; } void ShimmerReverb::setLeftDelayTime(float32_t delay_time_L) diff --git a/src/fx_shimmer_reverb.h b/src/fx_shimmer_reverb.h index a7b8129..27bbb45 100644 --- a/src/fx_shimmer_reverb.h +++ b/src/fx_shimmer_reverb.h @@ -23,7 +23,7 @@ #define SHIMMER_MAX_DELAY_TIME 2.0f -class ShimmerReverb : public FX +class ShimmerReverb : public FXElement { DISALLOW_COPY_AND_ASSIGN(ShimmerReverb); @@ -37,7 +37,7 @@ public: 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; + virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override; void setLeftDelayTime(float32_t delay_time_L); inline float32_t getLeftDelayTime() const; diff --git a/src/fx_tape_delay.cpp b/src/fx_tape_delay.cpp index faccabe..c3231f6 100644 --- a/src/fx_tape_delay.cpp +++ b/src/fx_tape_delay.cpp @@ -4,7 +4,7 @@ #include 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), + FXElement(sampling_rate), MaxSampleDelayTime(sampling_rate * MAX_DELAY_TIME), left_read_pos_(0), right_read_pos_(0) @@ -23,55 +23,44 @@ TapeDelay::~TapeDelay() 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) +void TapeDelay::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { - 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(fluttered_delay_time); + while(left_write_pos < 0) + { + left_write_pos += this->MaxSampleDelayTime; + } + + int right_write_pos = this->right_read_pos_ - static_cast(fluttered_delay_time); + while(right_write_pos < 0) + { + right_write_pos += this->MaxSampleDelayTime; + } + + // Write input to delay buffers + this->left_buffer_[left_write_pos] = inL; + this->right_buffer_[right_write_pos] = inR; + + // 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(); + + // 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) { - // 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(fluttered_delay_time); - while(left_write_pos < 0) - { - left_write_pos += this->MaxSampleDelayTime; - } - - int right_write_pos = this->right_read_pos_ - static_cast(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; + this->right_read_pos_ -= this->MaxSampleDelayTime; } } diff --git a/src/fx_tape_delay.h b/src/fx_tape_delay.h index a8caf89..a90d005 100644 --- a/src/fx_tape_delay.h +++ b/src/fx_tape_delay.h @@ -25,7 +25,7 @@ #define MAX_DELAY_TIME 2 -class TapeDelay : public FX +class TapeDelay : public FXElement { DISALLOW_COPY_AND_ASSIGN(TapeDelay); @@ -33,7 +33,7 @@ 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; + virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override; void setDelayTime(float32_t delay_time); inline float32_t getDelayTime() const;