diff --git a/src/effect_platervbstereo.cpp b/src/effect_platervbstereo.cpp index 6605caa..7aa046a 100644 --- a/src/effect_platervbstereo.cpp +++ b/src/effect_platervbstereo.cpp @@ -84,11 +84,10 @@ const int16_t AudioWaveformSine[257] = { }; AudioEffectPlateReverb::AudioEffectPlateReverb(float32_t samplerate) : - FXElement(samplerate) + FXElement(samplerate, 2.54f), + input_attn(0.5f), + in_allp_k(INP_ALLP_COEFF) { - input_attn = 0.5f; - in_allp_k = INP_ALLP_COEFF; - memset(in_allp1_bufL, 0, sizeof(in_allp1_bufL)); memset(in_allp2_bufL, 0, sizeof(in_allp2_bufL)); memset(in_allp3_bufL, 0, sizeof(in_allp3_bufL)); @@ -98,6 +97,8 @@ AudioEffectPlateReverb::AudioEffectPlateReverb(float32_t samplerate) : in_allp3_idxL = 0; in_allp4_idxL = 0; + in_allp_out_L = 0.0f; + memset(in_allp1_bufR, 0, sizeof(in_allp1_bufR)); memset(in_allp2_bufR, 0, sizeof(in_allp2_bufR)); memset(in_allp3_bufR, 0, sizeof(in_allp3_bufR)); @@ -149,6 +150,9 @@ AudioEffectPlateReverb::AudioEffectPlateReverb(float32_t samplerate) : master_lowpass_l = 0.0f; master_lowpass_r = 0.0f; + rv_time_k = 0.0f; + rv_time_scaler = 0.0f; + lfo1_phase_acc = 0; lfo1_adder = (UINT32_MAX + 1)/(samplerate * LFO1_FREQ_HZ); lfo2_phase_acc = 0; @@ -173,14 +177,14 @@ void AudioEffectPlateReverb::reset() memset(in_allp2_bufR, 0, sizeof(in_allp2_bufR)); memset(in_allp3_bufR, 0, sizeof(in_allp3_bufR)); memset(in_allp4_bufR, 0, sizeof(in_allp4_bufR)); - memset(lp_allp1_buf, 0, sizeof(lp_allp1_buf)); - memset(lp_allp2_buf, 0, sizeof(lp_allp2_buf)); - memset(lp_allp3_buf, 0, sizeof(lp_allp3_buf)); - memset(lp_allp4_buf, 0, sizeof(lp_allp4_buf)); - memset(lp_dly1_buf, 0, sizeof(lp_dly1_buf)); - memset(lp_dly2_buf, 0, sizeof(lp_dly2_buf)); - memset(lp_dly3_buf, 0, sizeof(lp_dly3_buf)); - memset(lp_dly4_buf, 0, sizeof(lp_dly4_buf)); + memset(lp_allp1_buf, 0, sizeof(lp_allp1_buf)); + memset(lp_allp2_buf, 0, sizeof(lp_allp2_buf)); + memset(lp_allp3_buf, 0, sizeof(lp_allp3_buf)); + memset(lp_allp4_buf, 0, sizeof(lp_allp4_buf)); + memset(lp_dly1_buf, 0, sizeof(lp_dly1_buf)); + memset(lp_dly2_buf, 0, sizeof(lp_dly2_buf)); + memset(lp_dly3_buf, 0, sizeof(lp_dly3_buf)); + memset(lp_dly4_buf, 0, sizeof(lp_dly4_buf)); } void AudioEffectPlateReverb::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) @@ -398,7 +402,7 @@ void AudioEffectPlateReverb::processSample(float32_t inL, float32_t inR, float32 temp1 = acc - master_lowpass_l; master_lowpass_l += temp1 * master_lowpass_f; - outL = master_lowpass_l; + outL = master_lowpass_l * this->OutputLevelCorrector; // Channel R #ifdef TAP1_MODULATED @@ -442,7 +446,7 @@ void AudioEffectPlateReverb::processSample(float32_t inL, float32_t inR, float32 temp1 = acc - master_lowpass_r; master_lowpass_r += temp1 * master_lowpass_f; - outR = master_lowpass_r; + outR = master_lowpass_r * this->OutputLevelCorrector; } void AudioEffectPlateReverb::doReverb(const float32_t* inblockL, const float32_t* inblockR, float32_t* rvbblockL, float32_t* rvbblockR, uint16_t len) diff --git a/src/extra_features.h b/src/extra_features.h index 8f6705f..1a0c373 100644 --- a/src/extra_features.h +++ b/src/extra_features.h @@ -37,6 +37,7 @@ inline long long int getElapseTime(std::string marker = "") { static std::unordered_map marker_times; + auto current_time = std::chrono::high_resolution_clock::now(); auto it = marker_times.find(marker); if (it != marker_times.end()) @@ -54,12 +55,12 @@ inline long long int getElapseTime(std::string marker = "") #define LAP_TIME(marker) getElapseTime(marker) #define LOG_LAP_TIME(marker) { auto __d = getElapseTime(marker); if(__d > 0) std::cout << "Execution time for " << marker << ": " << __d << std::endl; } + #define DEBUG_VALUE(lbl, idx, v) std::cout << lbl << " " << idx << ": " << v << std::endl #else #define LAP_TIME(marker) #define LOG_LAP_TIME(marker) -#define DEBUG_VALUE(lbl, idx, v) #endif diff --git a/src/fx.cpp b/src/fx.cpp index 6a88694..b7cc565 100644 --- a/src/fx.cpp +++ b/src/fx.cpp @@ -14,8 +14,9 @@ float32_t FXBase::getSamplingRate() const return this->SamplingRate; } -FXElement::FXElement(float32_t sampling_rate) : - FXBase(sampling_rate) +FXElement::FXElement(float32_t sampling_rate, float32_t output_level_corrector) : + FXBase(sampling_rate), + OutputLevelCorrector(output_level_corrector) { } diff --git a/src/fx.h b/src/fx.h index 30758de..e1b5d17 100644 --- a/src/fx.h +++ b/src/fx.h @@ -48,7 +48,9 @@ class FXElement : public FXBase DISALLOW_COPY_AND_ASSIGN(FXElement); protected: - FXElement(float32_t sampling_rate); + FXElement(float32_t sampling_rate, float32_t output_level_corrector = 1.0f); + + const float32_t OutputLevelCorrector; public: virtual ~FXElement(); diff --git a/src/fx_delay.cpp b/src/fx_delay.cpp index 39ccbe2..5dbe26b 100644 --- a/src/fx_delay.cpp +++ b/src/fx_delay.cpp @@ -50,7 +50,7 @@ void Delay::LowHighPassFilter::processSample(float32_t inL, float32_t inR, float } Delay::Delay(const float32_t sampling_rate, float32_t default_delay_time, float32_t default_flutter_level, float32_t default_feedback_level) : - FXElement(sampling_rate), + FXElement(sampling_rate, 3.46f), MaxSampleDelayTime((MAX_DELAY_TIME + MAX_FLUTTER_DELAY_TIME) * sampling_rate * MAX_DELAY_TIME), read_pos_L_(0), read_pos_R_(0), @@ -133,6 +133,9 @@ void Delay::processSample(float32_t inL, float32_t inR, float32_t& outL, float32 { this->read_pos_R_ -= this->MaxSampleDelayTime; } + + outL *= this->OutputLevelCorrector; + outR *= this->OutputLevelCorrector; } void Delay::setLeftDelayTime(float32_t delay_time) diff --git a/src/fx_engine.hpp b/src/fx_engine.hpp index 87850dd..907ba22 100644 --- a/src/fx_engine.hpp +++ b/src/fx_engine.hpp @@ -207,14 +207,14 @@ public: this->accumulator_ = value; } - inline void read(float32_t value, float32_t scale) + inline void read(float32_t value) { - this->accumulator_ += value * scale; + this->accumulator_ += value; } - inline void read(float32_t value) + inline void read(float32_t value, float32_t scale) { - this->accumulator_ += value; + this->accumulator_ += value * scale; } inline void write(float32_t& value) @@ -228,10 +228,24 @@ public: this->accumulator_ *= scale; } + inline void writeAndLoad(float32_t& value, float32_t newValue) + { + value = this->accumulator_; + this->load(newValue); + } + template - inline void write(D& d, int32_t offset, float32_t scale) + inline void directWrite(float32_t value, D& d) + { + this->load(value); + this->writeAndLoad(d, 0, 0.0f); + } + + template + inline void write(D& d, int32_t offset) { assert((D::base + D::length) <= size); + T w = DataType::compress(this->accumulator_); if(offset == -1) { @@ -241,15 +255,34 @@ public: { this->buffer_[(this->write_ptr_ + D::base + offset) & MASK] = w; } + } + + template + inline void write(D& d, int32_t offset, float32_t scale) + { + this->write(d, offset); this->accumulator_ *= scale; } + template + inline void writeAndLoad(D& d, int32_t offset, float32_t newValue) + { + this->write(d, offset); + this->load(newValue); + } + template inline void write(D& d, float32_t scale) { this->write(d, 0, scale); } + template + inline void writeAndLoad(D& d, float32_t newValue) + { + this->writeAndLoad(d, 0, newValue); + } + template inline void writeAllPass(D& d, int32_t offset, float32_t scale) { @@ -267,6 +300,7 @@ public: inline void read(D& d, int32_t offset, float32_t scale) { assert((D::base + D::length) <= size); + T r; if(offset == -1) { @@ -307,8 +341,9 @@ public: int32_t offset_integral = static_cast(offset); float32_t offset_fractional = offset - static_cast(offset_integral); - 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]); + int32_t index = this->write_ptr_ + offset_integral + D::base; + float32_t a = DataType::decompress(this->buffer_[index & MASK]); + float32_t b = DataType::decompress(this->buffer_[(index + 1) & MASK]); float32_t x = a + (b - a) * offset_fractional; this->previous_read_ = x; diff --git a/src/fx_flanger.cpp b/src/fx_flanger.cpp index 1d66e0b..03b2360 100644 --- a/src/fx_flanger.cpp +++ b/src/fx_flanger.cpp @@ -1,9 +1,7 @@ #include "fx_flanger.h" -#include - Flanger::Flanger(float32_t sampling_rate, float32_t rate, float32_t depth, float32_t feedback) : - FXElement(sampling_rate), + FXElement(sampling_rate, 1.17f), MaxDelayLineSize(static_cast(MAX_FLANGER_DELAY * sampling_rate)), write_index_(0) { @@ -40,7 +38,7 @@ void Flanger::reset() { memset(this->delay_lineL_, 0, this->MaxDelayLineSize * sizeof(float32_t)); memset(this->delay_lineR_, 0, this->MaxDelayLineSize * sizeof(float32_t)); - memset(this->feedback_samples_, 0, 2 * sizeof(float32_t)); + memset(this->feedback_samples_, 0, StereoChannels::kNumChannels * sizeof(float32_t)); this->write_index_ = 0; for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i) @@ -52,8 +50,8 @@ void Flanger::reset() void Flanger::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { // Write sample and any feedback into delay buffers - this->delay_lineL_[this->write_index_] = inL + this->feedback_samples_[0]; - this->delay_lineR_[this->write_index_] = inR + this->feedback_samples_[1]; + this->delay_lineL_[this->write_index_] = inL + this->feedback_samples_[StereoChannels::Left ]; + this->delay_lineR_[this->write_index_] = inR + this->feedback_samples_[StereoChannels::Right]; ++this->write_index_; if(this->write_index_ >= this->MaxDelayLineSize) @@ -86,8 +84,8 @@ void Flanger::processSample(float32_t inL, float32_t inR, float32_t& outL, float } // Calculate linear interpolation point for left channel - int currentL = (int)delayReadHeadL; - int nextL = currentL + 1; + int32_t currentL = static_cast(delayReadHeadL); + int32_t nextL = currentL + 1; float32_t fractionL = delayReadHeadL - currentL; if(nextL >= static_cast(this->MaxDelayLineSize)) { @@ -95,8 +93,8 @@ void Flanger::processSample(float32_t inL, float32_t inR, float32_t& outL, float } // Calculate linear interpolation point for right channel - int currentR = (int)delayReadHeadR; - int nextR = currentR + 1; + int32_t currentR = static_cast(delayReadHeadR); + int32_t nextR = currentR + 1; float32_t fractionR = delayReadHeadR - currentR; if(nextR >= static_cast(this->MaxDelayLineSize)) { @@ -111,8 +109,8 @@ void Flanger::processSample(float32_t inL, float32_t inR, float32_t& outL, float this->feedback_samples_[StereoChannels::Left ] = delay_sample_l * this->feedback_; this->feedback_samples_[StereoChannels::Right] = delay_sample_r * this->feedback_; - outL = delay_sample_l; - outR = delay_sample_r; + outL = delay_sample_l * this->OutputLevelCorrector; + outR = delay_sample_r * this->OutputLevelCorrector; } void Flanger::setRate(float32_t rate) diff --git a/src/fx_orbitone.cpp b/src/fx_orbitone.cpp index e19a52f..4c81c54 100644 --- a/src/fx_orbitone.cpp +++ b/src/fx_orbitone.cpp @@ -1,12 +1,10 @@ #include "fx_orbitone.h" -#include - #define LFO_SLOW_MAX_FREQUENCY 1.0f #define LFO_FAST_MAX_FREQUENCY 8.8f Orbitone::Orbitone(float32_t sampling_rate, float32_t rate, float32_t depth) : - FXElement(sampling_rate), + FXElement(sampling_rate, 1.8f), engine_(sampling_rate, 0.0f), depth_(0.0f), fullscale_depth_(0.0f) @@ -47,8 +45,8 @@ void Orbitone::reset() void Orbitone::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { typedef Engine::Reserve<2047, Engine::Reserve<2047> > Memory; - Engine::DelayLine line_l; - Engine::DelayLine line_r; + Engine::DelayLine line_l; + Engine::DelayLine line_r; Engine::Context c; this->engine_.start(&c); @@ -70,23 +68,20 @@ void Orbitone::processSample(float32_t inL, float32_t inR, float32_t& outL, floa float32_t wet = 0.0f; - // Sum L & R channel to send to chorus line. - c.read(inL, 1.0f); - c.write(line_l, 0.0f); - c.read(inR, 1.0f); - c.write(line_r, 0.0f); + c.directWrite(inL, line_l); + c.directWrite(inR, line_r); c.interpolate(line_l, mod_1 + 1024, 0.33f); c.interpolate(line_l, mod_2 + 1024, 0.33f); c.interpolate(line_r, mod_3 + 1024, 0.33f); - c.write(wet, 0.0f); - outL = wet; + c.writeAndLoad(wet, 0.0f); + outL = wet * this->OutputLevelCorrector; c.interpolate(line_r, mod_1 + 1024, 0.33f); c.interpolate(line_r, mod_2 + 1024, 0.33f); c.interpolate(line_l, mod_3 + 1024, 0.33f); - c.write(wet, 0.0f); - outR = wet; + c.writeAndLoad(wet, 0.0f); + outR = wet * this->OutputLevelCorrector; } void Orbitone::setRate(float32_t rate) diff --git a/src/fx_phaser.cpp b/src/fx_phaser.cpp index 7b81508..2d1c9f2 100644 --- a/src/fx_phaser.cpp +++ b/src/fx_phaser.cpp @@ -1,11 +1,7 @@ #include "fx_phaser.h" -#include -#include - Phaser::AllpassDelay::AllpassDelay() : - FXElement(0.0f), - a1_(0.0f) + FXElement(0.0f) { this->reset(); } @@ -16,32 +12,37 @@ Phaser::AllpassDelay::~AllpassDelay() void Phaser::AllpassDelay::reset() { - memset(this->z_, 0, 2 * sizeof(float32_t)); + memset(this->a1_, 0, StereoChannels::kNumChannels * sizeof(float32_t)); + memset(this->z_, 0, StereoChannels::kNumChannels * sizeof(float32_t)); } void Phaser::AllpassDelay::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { - outL = inL * -this->a1_ + this->z_[0]; - this->z_[0] = outL * this->a1_ + inL; + outL = inL * -this->a1_[StereoChannels::Left ] + this->z_[StereoChannels::Left ]; + this->z_[StereoChannels::Left ] = outL * this->a1_[StereoChannels::Left ] + inL; - outR = inR * -this->a1_ + this->z_[1]; - this->z_[1] = outR * this->a1_ + inR; + outR = inR * -this->a1_[StereoChannels::Right] + this->z_[StereoChannels::Right]; + this->z_[StereoChannels::Right] = outR * this->a1_[StereoChannels::Right] + inR; } -void Phaser::AllpassDelay::setDelay(float32_t delay) +void Phaser::AllpassDelay::setDelay(float32_t delayL, float32_t delayR) { - this->a1_ = (1.0f - delay) / (1.0f + delay); + this->a1_[StereoChannels::Left ] = (1.0f - delayL) / (1.0f + delayL); + this->a1_[StereoChannels::Right] = (1.0f - delayR) / (1.0f + delayR); } Phaser::Phaser(float32_t sampling_rate, float32_t rate, float32_t depth, float32_t feedback, unsigned nb_stages) : FXElement(sampling_rate), - lfo_(sampling_rate, 0.0f, 2.5f), depth_(0.0f), + gain_(1.0f), feedback_(0.0f), dmin_(0.0f), dmax_(0.0f) { + this->lfo_[StereoChannels::Left ] = new LFO(sampling_rate, 0.0f, 2.5f); + this->lfo_[StereoChannels::Right] = new LFO(sampling_rate, 0.0f, 2.5f, Constants::MPI_2); + this->setRate(rate); this->setDepth(depth); this->setFeedback(feedback); @@ -53,36 +54,42 @@ Phaser::Phaser(float32_t sampling_rate, float32_t rate, float32_t depth, float32 Phaser::~Phaser() { + delete this->lfo_[StereoChannels::Left ]; + delete this->lfo_[StereoChannels::Right]; } void Phaser::reset() { - memset(this->z_, 0, 2 * sizeof(float32_t)); + memset(this->z_, 0, StereoChannels::kNumChannels * sizeof(float32_t)); for(unsigned i = 0; i < MAX_NB_PHASES; ++i) { this->stages_[i].reset(); } - this->lfo_.reset(); + this->lfo_[StereoChannels::Left ]->reset(); + this->lfo_[StereoChannels::Right]->reset(); } void Phaser::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { - float32_t d = this->dmin_ + (this->dmax_ - this->dmin_) * ((1.0f + this->lfo_.process()) / 2.0f); + float32_t dL = this->dmin_ + (this->dmax_ - this->dmin_) * ((1.0f + this->lfo_[StereoChannels::Left ]->process()) / 2.0f); + float32_t dR = this->dmin_ + (this->dmax_ - this->dmin_) * ((1.0f + this->lfo_[StereoChannels::Right]->process()) / 2.0f); - float32_t sampleL = inL + this->feedback_ * this->z_[0]; - float32_t sampleR = inR + this->feedback_ * this->z_[1]; + float32_t sampleL = inL + this->feedback_ * this->z_[StereoChannels::Left ]; + float32_t sampleR = inR + this->feedback_ * this->z_[StereoChannels::Right]; for(unsigned i = 0; i < this->nb_stages_; ++i) { - this->stages_[i].setDelay(d); - + this->stages_[i].setDelay(dL, dR); this->stages_[i].processSample(sampleL, sampleR, sampleL, sampleR); } - this->z_[0] = sampleL; - this->z_[1] = sampleR; + this->z_[StereoChannels::Left ] = sampleL; + this->z_[StereoChannels::Right] = sampleR; outL = inL + this->z_[StereoChannels::Left ] * this->depth_; outR = inR + this->z_[StereoChannels::Right] * this->depth_; + + outL *= this->gain_; + outR *= this->gain_; } void Phaser::setFrequencyRange(float32_t min_frequency, float32_t max_frequency) @@ -94,21 +101,23 @@ void Phaser::setFrequencyRange(float32_t min_frequency, float32_t max_frequency) void Phaser::setRate(float32_t rate) { rate = constrain(rate, 0.0f, 1.0f); - this->lfo_.setNormalizedFrequency(rate); + this->lfo_[StereoChannels::Left ]->setNormalizedFrequency(rate); + this->lfo_[StereoChannels::Right]->setNormalizedFrequency(rate); } -inline float32_t Phaser::getRate() const +float32_t Phaser::getRate() const { - return this->lfo_.getNormalizedFrequency(); + return this->lfo_[StereoChannels::Left]->getNormalizedFrequency(); } void Phaser::setDepth(float32_t depth) { depth = constrain(depth, 0.0f, 1.0f); this->depth_ = depth; + this->gain_ = this->OutputLevelCorrector / (1.0f + depth); } -inline float32_t Phaser::getDepth() const +float32_t Phaser::getDepth() const { return this->depth_; } @@ -119,7 +128,7 @@ void Phaser::setFeedback(float32_t feedback) this->feedback_ = feedback; } -inline float32_t Phaser::getFeedback() const +float32_t Phaser::getFeedback() const { return this->feedback_; } diff --git a/src/fx_phaser.h b/src/fx_phaser.h index 702816b..3b46a3a 100644 --- a/src/fx_phaser.h +++ b/src/fx_phaser.h @@ -35,13 +35,13 @@ public: AllpassDelay(); virtual ~AllpassDelay(); - virtual void reset(); - virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR); + virtual void reset() override; + virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override; - void setDelay(float32_t delay); + void setDelay(float32_t delayL, float32_t delayR); private: - float32_t a1_; + float32_t a1_[StereoChannels::kNumChannels]; float32_t z_[StereoChannels::kNumChannels]; IMPLEMENT_DUMP( @@ -99,8 +99,9 @@ public: unsigned getNbStages() const; private: - LFO lfo_; + LFO* lfo_[StereoChannels::kNumChannels]; float32_t depth_; + float32_t gain_; float32_t feedback_; float32_t dmin_; float32_t dmax_; @@ -142,7 +143,8 @@ private: if(deepInspection) { - this->lfo_.dump(out, deepInspection, tag + ".lfo_"); + this->lfo_[StereoChannels::Left ]->dump(out, deepInspection, tag + ".lfo_[ L ]"); + this->lfo_[StereoChannels::Right]->dump(out, deepInspection, tag + ".lfo_[ R ]"); for(unsigned i = 0; i < MAX_NB_PHASES; ++i) { this->stages_[i].dump(out, deepInspection, tag + ".stages_[ " + std::to_string(i) + " ]"); @@ -161,7 +163,8 @@ private: if(deepInspection) { - nb_errors += this->lfo_.inspect(inspector, deepInspection, tag + ".lfo_"); + nb_errors += this->lfo_[StereoChannels::Left ]->inspect(inspector, deepInspection, tag + ".lfo_[ L ]"); + nb_errors += this->lfo_[StereoChannels::Right]->inspect(inspector, deepInspection, tag + ".lfo_[ R ]"); for(unsigned i = 0; i < MAX_NB_PHASES; ++i) { nb_errors += this->stages_[i].inspect(inspector, deepInspection, tag + ".stages_[ " + std::to_string(i) + " ]"); diff --git a/src/fx_rack.cpp b/src/fx_rack.cpp index 89e0589..2057601 100644 --- a/src/fx_rack.cpp +++ b/src/fx_rack.cpp @@ -41,19 +41,17 @@ FXRack::~FXRack() inline void FXRack::reset() { - auto end = this->fx_chain_.end(); - for(FXChain::iterator it = this->fx_chain_.begin(); it != end; it++) + for(FXElement* fx : this->fx_chain_) { - (*it)->reset();; + fx->reset(); } } inline void FXRack::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { - FXChain::iterator end = this->fx_chain_.end(); - for(FXChain::iterator it = this->fx_chain_.begin(); it != end; it++) + for(FXElement* fx : this->fx_chain_) { - (*it)->processSample(inL, inR, outL, outR); + fx->processSample(inL, inR, outL, outR); inL = outL; inR = outR; @@ -62,19 +60,14 @@ inline void FXRack::processSample(float32_t inL, float32_t inR, float32_t& outL, void FXRack::process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) { - float32_t sampleInL; - float32_t sampleInR; - float32_t sampleOutL; - float32_t sampleOutR; - for(unsigned i = 0; i < nSamples; ++i) { - sampleInL = *left_input; - sampleInR = *right_input; - sampleOutL = 0.0f; - sampleOutR = 0.0f; + float32_t sampleInL = *left_input; + float32_t sampleInR = *right_input; + float32_t sampleOutL = 0.0f; + float32_t sampleOutR = 0.0f; - if(this->isEnable()) + if(this->isEnable()) { this->processSample(sampleInL, sampleInR, sampleOutL, sampleOutR); @@ -87,7 +80,7 @@ void FXRack::process(float32_t* left_input, float32_t* right_input, float32_t* l *left_output = sampleInL; *right_output = sampleInR; } - + // Move inputs by 1 sample ++left_input; ++right_input; diff --git a/src/fx_shimmer_reverb.cpp b/src/fx_shimmer_reverb.cpp index f3980ff..6e31170 100644 --- a/src/fx_shimmer_reverb.cpp +++ b/src/fx_shimmer_reverb.cpp @@ -1,14 +1,12 @@ #include "fx_shimmer_reverb.h" -#include -#include - #define TAIL , -1 ShimmerReverb::ShimmerReverb(float32_t sampling_rate) : FXElement(sampling_rate), engine_(sampling_rate), input_gain_(-1.0f), + reverb_time_(0.0f), diffusion_(-1.0f), lp_(-1.0f), lp_decay_1_(0.0f), @@ -18,8 +16,11 @@ ShimmerReverb::ShimmerReverb(float32_t sampling_rate) : this->engine_.setLFOFrequency(Engine::LFOIndex::LFO_2, 0.3f); this->setInputGain(1.0f); - this->setLP(0.7f); + this->setTime(0.7f); this->setDiffusion(0.625f); + this->setLP(0.7f); + + this->reset(); } ShimmerReverb::~ShimmerReverb() @@ -75,7 +76,7 @@ void ShimmerReverb::processSample(float32_t inL, float32_t inR, float32_t& outL, // Smear AP1 inside the loop. c.interpolate(ap1, 10.0f, Engine::LFOIndex::LFO_1, 60.0f, 1.0f); - c.write(ap1, 100, 0.0f); + c.writeAndLoad(ap1, 100, 0.0f); c.read(inL + inR, gain); // Diffuse through 4 allpasses. @@ -98,7 +99,7 @@ void ShimmerReverb::processSample(float32_t inL, float32_t inR, float32_t& outL, c.read(dap1b TAIL, kap); c.writeAllPass(dap1b, -kap); c.write(del1, 2.0f); - c.write(wet, 0.0f); + c.writeAndLoad(wet, 0.0f); outL = wet; @@ -110,7 +111,7 @@ void ShimmerReverb::processSample(float32_t inL, float32_t inR, float32_t& outL, c.read(dap2b TAIL, -kap); c.writeAllPass(dap2b, kap); c.write(del2, 2.0f); - c.write(wet, 0.0f); + c.writeAndLoad(wet, 0.0f); outR = wet; diff --git a/src/fx_tube.cpp b/src/fx_tube.cpp index e6b1660..7e926fe 100644 --- a/src/fx_tube.cpp +++ b/src/fx_tube.cpp @@ -23,14 +23,14 @@ void Tube::reset() void Tube::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { float32_t x = inL * this->saturator_factor_; - float32_t abs_x = std::abs(x); - float32_t sat_x = std::log(1.0f + abs_x) * this->gain_factor_; + float32_t abs_x = abs(x); + float32_t sat_x = log(1.0f + abs_x) * this->gain_factor_; outL = inL > 0 ? sat_x : -sat_x; x = inR * this->saturator_factor_; - abs_x = std::abs(x); - sat_x = std::log(1.0f + abs_x) * this->gain_factor_; + abs_x = abs(x); + sat_x = log(1.0f + abs_x) * this->gain_factor_; outR = inR > 0 ? sat_x : -sat_x; } @@ -44,7 +44,7 @@ void Tube::setOverdrive(float32_t overdrive) { this->overdrive_ = overdrive; this->saturator_factor_ = 1.0f + N * overdrive; - this->gain_factor_ = 1.0f / std::log(1.0f + this->saturator_factor_); + this->gain_factor_ = this->OutputLevelCorrector / log(1.0f + this->saturator_factor_); } } diff --git a/src/fx_unit.hpp b/src/fx_unit.hpp index 1ad591d..3761497 100644 --- a/src/fx_unit.hpp +++ b/src/fx_unit.hpp @@ -81,7 +81,7 @@ public: { } - void reset() + void reset() override { if(!this->is_reset_) { @@ -90,7 +90,7 @@ public: } } - void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) + void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override { if(!this->isEnable() || this->getWetLevel() == 0.0f) { diff --git a/src/test/Makefile b/src/test/Makefile index 000f070..7d987da 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -1,15 +1,23 @@ +BINDIR := bin OBJDIR := objects -OUTPUT_FOLDER = results -EXE := all_tests.bin -BETA := beta.bin +OUTPUT_FOLDER := results -CXX := g++ -CXXFLAGS = -g -std=c++20 -MMD -MP +EXE := $(BINDIR)/all_tests.bin +BETA := $(BINDIR)/beta.bin + +CXX = g++ +CXXFLAGS = -g -Wall -std=c++20 -MMD -MP DEFINES = -DCPU=x86 -DDEBUG -DOUTPUT_FOLDER="\"$(OUTPUT_FOLDER)\"" INCLUDES = -I../../CMSIS_5/CMSIS/DSP/Include/ \ -I../../CMSIS_5/CMSIS/Core/Include/ \ -I../../Synth_Dexed/src/ +CPPCHECK = cppcheck +CHECKFLAGS = -q -j 8 --enable=all --force --language=c++ \ + $(INCLUDES) --platform=unix64 \ + --error-exitcode=0 \ + --suppressions-list=cppcheck-suppression-list.txt + -include $(TST_OBJS:.o=.d) -include $(FX__OBJS:.o=.d) @@ -36,6 +44,7 @@ TST_OBJS = $(TST_SRCS:%.cpp=$(OBJDIR)/%.o) all: $(EXE) test +build: $(EXE) test: $(EXE) $(OUTPUT_FOLDER) rm -rf $(OUTPUT_FOLDER)/* ./$(EXE) @@ -44,6 +53,9 @@ test-debug: $(EXE) $(OUTPUT_FOLDER) rm -rf $(OUTPUT_FOLDER)/* valgrind --leak-check=full --leak-resolution=high --show-leak-kinds=all --xtree-leak=yes --show-mismatched-frees=yes --error-limit=no --log-file=$(OUTPUT_FOLDER)/valgrind-analysis-results.txt ./$(EXE) +$(BINDIR): + mkdir -p $@ + $(OBJDIR): mkdir -p $@ @@ -56,8 +68,8 @@ $(OBJDIR)/%.o: %.cpp $(OBJDIR) $(OBJDIR)/%.o: ../%.cpp $(OBJDIR) $(CXX) $(CXXFLAGS) $(DEFINES) $(INCLUDES) -c $< -o $@ -$(EXE): $(TST_OBJS) $(FX__OBJS) +$(EXE): $(BINDIR) $(TST_OBJS) $(FX__OBJS) $(LD) $(CXXFLAGS) $(call wildcard,$(TST_OBJS)) $(call wildcard,$(FX__OBJS)) -o $@ $(LIBS) clean: - rm -rf *.o $(OBJDIR) $(EXE) $(OUTPUT_FOLDER) + rm -rf $(OBJDIR) $(BINDIR) $(OUTPUT_FOLDER) diff --git a/src/test/cppcheck-suppression-list.txt b/src/test/cppcheck-suppression-list.txt new file mode 100644 index 0000000..0a2740b --- /dev/null +++ b/src/test/cppcheck-suppression-list.txt @@ -0,0 +1,11 @@ +*:../../CMSIS_5/* +toomanyconfigs:* +noExplicitConstructor:* +unusedFunction:* +missingIncludeSystem:* +unmatchedSuppression:* + +// unexplained exceptions +syntaxError:beta.cpp:52 +syntaxError:test_fx_mixing_console.cpp:207 +internalAstError:test_cpp_performance.cpp:22 diff --git a/src/test/test3.wav b/src/test/test3.wav new file mode 100644 index 0000000..bf118f0 Binary files /dev/null and b/src/test/test3.wav differ diff --git a/src/test/test_cpp.cpp b/src/test/test_cpp.cpp deleted file mode 100644 index 0d1664b..0000000 --- a/src/test/test_cpp.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include - -#include "../fx_components.h" - -int nb = 0; - -int NbIteration() { - nb++; - return 3; -} - -TEST(Cpp, NbCallsInUpperBoudariesInForLoop) -{ - for(int i = 0; i < NbIteration(); ++i) - { - // Does something - } - EXPECT_EQ(nb, 4); -} - -#define CLASS_INIT(clazz) clazz::StaticInit() -class StaticCtorTest -{ -private: - static int n_; - -public: - int i_; - - static int StaticInit() - { - static int i = 0; - i++; - - StaticCtorTest::n_ = 2; - - return i; - } - - StaticCtorTest() : i_(0) - { - static int init = CLASS_INIT(StaticCtorTest); - static int NB = 0; - EXPECT_EQ(init, 1); - - this->i_ = ++NB; - - EXPECT_EQ(StaticCtorTest::n_, 2); - } - - ~StaticCtorTest() - { - } -}; - -int StaticCtorTest::n_ = 0; - -TEST(Cpp, StaticCtorTest) -{ - StaticCtorTest obj1; - StaticCtorTest obj2; - StaticCtorTest obj3; - - EXPECT_EQ(obj1.i_, 1); - EXPECT_EQ(obj2.i_, 2); - EXPECT_EQ(obj3.i_, 3); -} diff --git a/src/test/test_cpp_performance.cpp b/src/test/test_cpp_performance.cpp index e99f599..e518b25 100644 --- a/src/test/test_cpp_performance.cpp +++ b/src/test/test_cpp_performance.cpp @@ -29,7 +29,7 @@ TEST(CppPerformance, LFOPerformance_ComplexLFO_InterpolatedSineOscillator) } auto d2 = LAP_TIME("lfo2"); - EXPECT_LE(d1, d2 + 100); + EXPECT_LE(d1, d2); } TEST(CppPerformance, LFOPerformance_ComplexLFO_FastLFO) diff --git a/src/test/test_framework.cpp b/src/test/test_framework.cpp index 4eeddc7..bdcd416 100644 --- a/src/test/test_framework.cpp +++ b/src/test/test_framework.cpp @@ -17,5 +17,5 @@ TEST(Framework, TestWaveIn) nb_errors += fullInspector("R", samples[StereoChannels::Right][i], -1.0f, 1.0f, true); } - EXPECT_EQ(nb_errors, 0); -} \ No newline at end of file + ASSERT_EQ(nb_errors, 0) << "readWaveFile returns NaN of out of bounds samples: " << nb_errors << " out of " << size; +} diff --git a/src/test/test_fxLevelTuning.cpp b/src/test/test_fxLevelTuning.cpp new file mode 100644 index 0000000..20346d4 --- /dev/null +++ b/src/test/test_fxLevelTuning.cpp @@ -0,0 +1,363 @@ +#include "test_fx_helper.h" + +#include "../fx_tube.h" +#include "../fx_chorus.h" +#include "../fx_flanger.h" +#include "../fx_orbitone.h" +#include "../fx_phaser.h" +#include "../fx_delay.h" +#include "../effect_platervbstereo.h" +#include "../fx_shimmer_reverb.h" + +TEST(LevelTuning, Tube) +{ + Tube fx(SAMPLING_FREQUENCY); + fx.reset(); + fx.setOverdrive(0.75f); + + size_t size; + float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size); + + float32_t sumIn = 0.0f; + float32_t sumOut = 0.0f; + + size_t nb_errors = 0; + + for(size_t i = 0; i < size; ++i) + { + float32_t inL = inSamples[0][i]; + float32_t inR = inSamples[1][i]; + float32_t outL; + float32_t outR; + sumIn += inL * inL; + + fx.processSample(inL, inR, outL, outR); + + sumOut += outL * outL; + + nb_errors += std::abs(outL) > 1.0f ? 1 : 0; + nb_errors += std::abs(outR) > 1.0f ? 1 : 0; + } + + delete[] inSamples[0]; + delete[] inSamples[1]; + delete[] inSamples; + + float32_t ratio = std::sqrt(sumOut / sumIn); + + ASSERT_EQ(nb_errors, 0) << "Sample value error for Tube"; + EXPECT_GE(ratio, 0.9f); + EXPECT_LE(1.0f / ratio, 1.1f); +} + +TEST(LevelTuning, Chorus) +{ + Chorus fx(SAMPLING_FREQUENCY); + fx.reset(); + fx.setRate(0.4f); + fx.setDepth(0.5f); + + size_t size; + float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size); + + float32_t sumIn = 0.0f; + float32_t sumOut = 0.0f; + + size_t nb_errors = 0; + + for(size_t i = 0; i < size; ++i) + { + float32_t inL = inSamples[0][i]; + float32_t inR = inSamples[1][i]; + float32_t outL; + float32_t outR; + sumIn += inL * inL; + + fx.processSample(inL, inR, outL, outR); + + sumOut += outL * outL; + + nb_errors += std::abs(outL) > 1.0f ? 1 : 0; + nb_errors += std::abs(outR) > 1.0f ? 1 : 0; + } + + delete[] inSamples[0]; + delete[] inSamples[1]; + delete[] inSamples; + + float32_t ratio = std::sqrt(sumOut / sumIn); + + ASSERT_EQ(nb_errors, 0) << "Sample value error for Chorus"; + EXPECT_LE(ratio, 1.0f); + EXPECT_GE(ratio, 0.9f); + EXPECT_LE(1.0f / ratio, 1.1f); +} + +TEST(LevelTuning, Flanger) +{ + Flanger fx(SAMPLING_FREQUENCY); + fx.reset(); + fx.setRate(0.03f); + fx.setDepth(0.75f); + fx.setFeedback(0.5f); + + size_t size; + float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size); + + float32_t sumIn = 0.0f; + float32_t sumOut = 0.0f; + + size_t nb_errors = 0; + + for(size_t i = 0; i < size; ++i) + { + float32_t inL = inSamples[0][i]; + float32_t inR = inSamples[1][i]; + float32_t outL; + float32_t outR; + sumIn += inL * inL; + + fx.processSample(inL, inR, outL, outR); + + sumOut += outL * outL; + + nb_errors += std::abs(outL) > 1.0f ? 1 : 0; + nb_errors += std::abs(outR) > 1.0f ? 1 : 0; + } + + delete[] inSamples[0]; + delete[] inSamples[1]; + delete[] inSamples; + + float32_t ratio = std::sqrt(sumOut / sumIn); + + ASSERT_EQ(nb_errors, 0) << "Sample value error for Flanger"; + EXPECT_LE(ratio, 1.0f); + EXPECT_GE(ratio, 0.9f); + EXPECT_LE(1.0f / ratio, 1.1f); +} + +TEST(LevelTuning, Orbitone) +{ + Orbitone fx(SAMPLING_FREQUENCY); + fx.reset(); + fx.setRate(0.4f); + fx.setDepth(0.5f); + + size_t size; + float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size); + + float32_t sumIn = 0.0f; + float32_t sumOut = 0.0f; + + size_t nb_errors = 0; + + for(size_t i = 0; i < size; ++i) + { + float32_t inL = inSamples[0][i]; + float32_t inR = inSamples[1][i]; + float32_t outL; + float32_t outR; + sumIn += inL * inL; + + fx.processSample(inL, inR, outL, outR); + + sumOut += outL * outL; + + nb_errors += std::abs(outL) > 1.0f ? 1 : 0; + nb_errors += std::abs(outR) > 1.0f ? 1 : 0; + } + + delete[] inSamples[0]; + delete[] inSamples[1]; + delete[] inSamples; + + float32_t ratio = std::sqrt(sumOut / sumIn); + + ASSERT_EQ(nb_errors, 0) << "Sample value error for Orbitone"; + EXPECT_LE(ratio, 1.0f); + EXPECT_GE(ratio, 0.9f); + EXPECT_LE(1.0f / ratio, 1.1f); +} + +TEST(LevelTuning, Phaser) +{ + Phaser fx(SAMPLING_FREQUENCY); + fx.reset(); + fx.setRate(0.1f); + fx.setDepth(1.0f); + fx.setFeedback(0.5f); + fx.setNbStages(12); + + size_t size; + float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size); + + float32_t sumIn = 0.0f; + float32_t sumOut = 0.0f; + + size_t nb_errors = 0; + + for(size_t i = 0; i < size; ++i) + { + float32_t inL = inSamples[0][i]; + float32_t inR = inSamples[1][i]; + float32_t outL; + float32_t outR; + sumIn += inL * inL; + + fx.processSample(inL, inR, outL, outR); + + sumOut += outL * outL; + + nb_errors += std::abs(outL) > 1.0f ? 1 : 0; + nb_errors += std::abs(outR) > 1.0f ? 1 : 0; + } + + delete[] inSamples[0]; + delete[] inSamples[1]; + delete[] inSamples; + + float32_t ratio = std::sqrt(sumOut / sumIn); + + ASSERT_EQ(nb_errors, 0) << "Sample value error for Phaser"; + EXPECT_LE(ratio, 1.0f); + EXPECT_GE(ratio, 0.9f); + EXPECT_LE(1.0f / ratio, 1.1f); +} + +TEST(LevelTuning, Delay) +{ + Delay fx(SAMPLING_FREQUENCY); + fx.reset(); + fx.setLeftDelayTime(0.15f); + fx.setLeftDelayTime(0.2f); + fx.setFeedback(0.35f); + fx.setFlutterRate(0.0f); + fx.setFlutterAmount(0.0f); + + size_t size; + float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size); + + float32_t sumIn = 0.0f; + float32_t sumOut = 0.0f; + + size_t nb_errors = 0; + + for(size_t i = 0; i < size; ++i) + { + float32_t inL = inSamples[0][i]; + float32_t inR = inSamples[1][i]; + float32_t outL; + float32_t outR; + sumIn += inL * inL; + + fx.processSample(inL, inR, outL, outR); + + sumOut += outL * outL; + + nb_errors += std::abs(outL) > 1.0f ? 1 : 0; + nb_errors += std::abs(outR) > 1.0f ? 1 : 0; + } + + delete[] inSamples[0]; + delete[] inSamples[1]; + delete[] inSamples; + + float32_t ratio = std::sqrt(sumOut / sumIn); + + ASSERT_EQ(nb_errors, 0) << "Sample value error for Delay"; + EXPECT_LE(ratio, 1.0f); + EXPECT_GE(ratio, 0.9f); + EXPECT_LE(1.0f / ratio, 1.1f); +} + +TEST(LevelTuning, PlateReverb) +{ + AudioEffectPlateReverb fx(SAMPLING_FREQUENCY); + fx.reset(); + fx.set_bypass(false); + fx.size(0.7f); + fx.hidamp(0.5f); + fx.lodamp(0.5f); + fx.lowpass(0.3f); + fx.diffusion(0.65f); + fx.level(1.0f); + + size_t size; + float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size); + + float32_t sumIn = 0.0f; + float32_t sumOut = 0.0f; + + size_t nb_errors = 0; + + for(size_t i = 0; i < size; ++i) + { + float32_t inL = inSamples[0][i]; + float32_t inR = inSamples[1][i]; + float32_t outL; + float32_t outR; + sumIn += inL * inL; + + fx.processSample(inL, inR, outL, outR); + + sumOut += outL * outL; + + nb_errors += std::abs(outL) > 1.0f ? 1 : 0; + nb_errors += std::abs(outR) > 1.0f ? 1 : 0; + } + + delete[] inSamples[0]; + delete[] inSamples[1]; + delete[] inSamples; + + float32_t ratio = std::sqrt(sumOut / sumIn); + + ASSERT_EQ(nb_errors, 0) << "Sample value error for PlateReverb"; + EXPECT_GE(ratio, 0.9f); + EXPECT_LE(1.0f / ratio, 1.1f); +} + +TEST(LevelTuning, ShimmerReverb) +{ + ShimmerReverb fx(SAMPLING_FREQUENCY); + fx.reset(); + fx.setInputGain(0.35f); + fx.setTime(0.89f); + fx.setDiffusion(0.75f); + fx.setLP(0.8f); + + size_t size; + float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size); + + float32_t sumIn = 0.0f; + float32_t sumOut = 0.0f; + + size_t nb_errors = 0; + + for(size_t i = 0; i < size; ++i) + { + float32_t inL = inSamples[0][i]; + float32_t inR = inSamples[1][i]; + float32_t outL; + float32_t outR; + sumIn += inL * inL; + + fx.processSample(inL, inR, outL, outR); + + sumOut += outL * outL; + + nb_errors += std::abs(outL) > 1.0f ? 1 : 0; + nb_errors += std::abs(outR) > 1.0f ? 1 : 0; + } + + delete[] inSamples[0]; + delete[] inSamples[1]; + delete[] inSamples; + + float32_t ratio = std::sqrt(sumOut / sumIn); + + ASSERT_EQ(nb_errors, 0) << "Sample value error for ShimmerReverb"; + EXPECT_GE(ratio, 0.9f); + EXPECT_LE(1.0f / ratio, 1.1f); +} diff --git a/src/test/test_fx_components.cpp b/src/test/test_fx_components.cpp index 9438bc2..41927cf 100644 --- a/src/test/test_fx_components.cpp +++ b/src/test/test_fx_components.cpp @@ -145,5 +145,5 @@ TEST(CppOptimization, FastLFOPrecisionTest) max_delta = std::max(max_delta, std::abs(v1 - v2)); } - // EXPECT_GT(epsilon, max_delta); + EXPECT_GT(epsilon, max_delta); } diff --git a/src/test/test_fx_helper.cpp b/src/test/test_fx_helper.cpp index 8b5a025..f5f4c26 100644 --- a/src/test/test_fx_helper.cpp +++ b/src/test/test_fx_helper.cpp @@ -1,6 +1,5 @@ #include "test_fx_helper.h" -#include #include std::string getScenarioName(int scenario) @@ -21,7 +20,7 @@ std::string getScenarioName(int scenario) if(fxTube) { - if(!first) ss << ", "; + // if(!first) ss << ", "; ss << "Tube"; first = false; } @@ -72,7 +71,7 @@ std::string getScenarioName(int scenario) { if(!first) ss << ", "; ss << "Shim"; - first = false; + // first = false; } ss << " ]"; @@ -130,3 +129,22 @@ float32_t getRandomValue() return dist(gen); } + +float32_t** loadAudioTest(size_t& size, WaveHeader* hdr) +{ + float32_t** samples = readWaveFile(AUDIO_SOURCE_FILE, size, hdr); + assert(samples != nullptr); + + return samples; +} + +void freeAudioSamples(float32_t** samples, size_t size) +{ + assert(samples != nullptr); + + for(size_t i = 0; i < size; ++i) + { + delete[] samples[i]; + } + delete[] samples; +} diff --git a/src/test/test_fx_helper.h b/src/test/test_fx_helper.h index 95ac2f4..f21225f 100644 --- a/src/test/test_fx_helper.h +++ b/src/test/test_fx_helper.h @@ -7,10 +7,44 @@ #include "wave.h" #include "../fx.h" -#define AUDIO_SOURCE_FILE "test2.wav" +#define AUDIO_SOURCE_FILE "test3.wav" #define SAMPLING_FREQUENCY 44100.0f +#define PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name)\ + const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info();\ + std::string full_test_name = test_info->test_case_name();\ + full_test_name += ".";\ + full_test_name += test_info->name();\ + WaveHeader hdr;\ + size_t size;\ + float32_t** inSamples = loadAudioTest(size, &hdr);\ + float32_t** outSamples = new float32_t*[2];\ + outSamples[0] = new float32_t[size]; memset(outSamples[0], 0, size * sizeof(float32_t));\ + outSamples[1] = new float32_t[size]; memset(outSamples[1], 0, size * sizeof(float32_t)) + +#define CLEANUP_AUDIO_TEST(inSamples, outSamples)\ + freeAudioSamples(inSamples, 2);\ + freeAudioSamples(outSamples, 2) + +#define AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, code)\ + for(size_t i = 0; i < size; ++i)\ + {\ + float32_t inL = inSamples[0][i];\ + float32_t inR = inSamples[1][i];\ + float32_t outL;\ + float32_t outR;\ + code\ + outSamples[0][i] = outL;\ + outSamples[1][i] = outR;\ + } // + +#define SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx)\ + AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx.processSample(inL, inR, outL, outR);) + +#define SAVE_AUDIO_RESULTS(filename, samples, size)\ + saveWaveFile(getResultFile(filename + ".wav", true), samples[0], samples[1], size, static_cast(SAMPLING_FREQUENCY), 16) + #define Active(scenarioKey, FxID) ((scenarioKey & (1 << FxID)) == (1 << FxID)) std::string getScenarioName(int scenario); @@ -37,3 +71,6 @@ std::string getResultFile(const std::string& filename, bool createPath); float32_t getRandomValue(); class FXScenarioTest : public testing::TestWithParam {}; + +float32_t** loadAudioTest(size_t& size, WaveHeader* hdr); +void freeAudioSamples(float32_t** samples, size_t size); \ No newline at end of file diff --git a/src/test/test_fx_rack.cpp b/src/test/test_fx_rack.cpp index e721a3a..78d8a08 100644 --- a/src/test/test_fx_rack.cpp +++ b/src/test/test_fx_rack.cpp @@ -1,6 +1,3 @@ -#include -#include - #include "test_fx_helper.h" #include "../fx_rack.h" @@ -32,7 +29,7 @@ void setupRack(FXRack* rack, int scenario) rack->getOrbitone()->setEnable(Active(scenario, FXSwitch::FX__Orbitone)); rack->getOrbitone()->setWetLevel(0.8f); rack->getOrbitone()->setRate(0.4f); - rack->getOrbitone()->setDepth(0.5f); + rack->getOrbitone()->setDepth(0.7f); rack->getPhaser()->setEnable(Active(scenario, FXSwitch::FX__Phaser)); rack->getPhaser()->setWetLevel(1.0f); diff --git a/src/test/test_fx_shimmer_reverb.cpp b/src/test/test_fx_shimmer_reverb.cpp new file mode 100644 index 0000000..814b297 --- /dev/null +++ b/src/test/test_fx_shimmer_reverb.cpp @@ -0,0 +1,182 @@ +#include + +#include "test_fx_helper.h" +#include "../fx_shimmer_reverb.h" + +TEST(FXShimmerReverb, TransientSilence) +{ + const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); + std::string full_test_name = test_info->test_case_name(); + full_test_name += "."; + full_test_name += test_info->name(); + + const size_t size = static_cast(SAMPLING_FREQUENCY); + float32_t* inSamples = new float32_t[size]; + memset(inSamples, 0, size * sizeof(float32_t)); + + float32_t* outSamplesL = new float32_t[size]; + float32_t* outSamplesR = new float32_t[size]; + memset(outSamplesL, 0, size * sizeof(float32_t)); + memset(outSamplesR, 0, size * sizeof(float32_t)); + + ShimmerReverb* shimmer = new ShimmerReverb(SAMPLING_FREQUENCY); + + shimmer->setInputGain(0.55f); + shimmer->setTime(0.75f); + shimmer->setDiffusion(0.8f); + shimmer->setLP(0.7f); + + shimmer->reset(); + for(size_t i = 0; i < size; ++i) + { + shimmer->processSample( + inSamples[i], + inSamples[i], + outSamplesL[i], + outSamplesR[i] + ); + } + + saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16); + + delete shimmer; + + delete[] inSamples; + + delete[] outSamplesL; + delete[] outSamplesR; +} + +TEST(FXShimmerReverb, TransientSilenceWithDirac) +{ + const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); + std::string full_test_name = test_info->test_case_name(); + full_test_name += "."; + full_test_name += test_info->name(); + + const size_t size = 4 * static_cast(SAMPLING_FREQUENCY); + float32_t* inSamples = new float32_t[size]; + memset(inSamples, 0, size * sizeof(float32_t)); + inSamples[0] = 1.0f; + + float32_t* outSamplesL = new float32_t[size]; + float32_t* outSamplesR = new float32_t[size]; + memset(outSamplesL, 0, size * sizeof(float32_t)); + memset(outSamplesR, 0, size * sizeof(float32_t)); + + ShimmerReverb* shimmer = new ShimmerReverb(SAMPLING_FREQUENCY); + + shimmer->setInputGain(0.55f); + shimmer->setTime(0.75f); + shimmer->setDiffusion(0.8f); + shimmer->setLP(0.7f); + + shimmer->reset(); + for(size_t i = 0; i < size; ++i) + { + shimmer->processSample( + inSamples[i], + inSamples[i], + outSamplesL[i], + outSamplesR[i] + ); + } + + saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16); + + delete shimmer; + + delete[] inSamples; + + delete[] outSamplesL; + delete[] outSamplesR; +} + +TEST(FXShimmerReverb, TransientNoise) +{ + const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); + std::string full_test_name = test_info->test_case_name(); + full_test_name += "."; + full_test_name += test_info->name(); + + const size_t size = static_cast(SAMPLING_FREQUENCY); + float32_t* inSamples = new float32_t[size]; + for(size_t i = 0; i < size; ++i) inSamples[i] = getRandomValue(); + + float32_t* outSamplesL = new float32_t[size]; + float32_t* outSamplesR = new float32_t[size]; + memset(outSamplesL, 0, size * sizeof(float32_t)); + memset(outSamplesR, 0, size * sizeof(float32_t)); + + ShimmerReverb* shimmer = new ShimmerReverb(SAMPLING_FREQUENCY); + + shimmer->setInputGain(0.55f); + shimmer->setTime(0.75f); + shimmer->setDiffusion(0.8f); + shimmer->setLP(0.7f); + + shimmer->reset(); + for(size_t i = 0; i < size; ++i) + { + shimmer->processSample( + inSamples[i], + inSamples[i], + outSamplesL[i], + outSamplesR[i] + ); + } + + saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16); + + delete shimmer; + + delete[] inSamples; + + delete[] outSamplesL; + delete[] outSamplesR; +} + +TEST(FXShimmerReverb, TransientMusic) +{ + const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); + std::string full_test_name = test_info->test_case_name(); + full_test_name += "."; + full_test_name += test_info->name(); + + size_t size; + float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size); + + float32_t* outSamplesL = new float32_t[size]; + float32_t* outSamplesR = new float32_t[size]; + memset(outSamplesL, 0, size * sizeof(float32_t)); + memset(outSamplesR, 0, size * sizeof(float32_t)); + + ShimmerReverb* shimmer = new ShimmerReverb(SAMPLING_FREQUENCY); + + shimmer->setInputGain(0.55f); + shimmer->setTime(0.75f); + shimmer->setDiffusion(0.8f); + shimmer->setLP(0.7f); + + shimmer->reset(); + for(size_t i = 0; i < size; ++i) + { + shimmer->processSample( + inSamples[0][i], + inSamples[1][i], + outSamplesL[i], + outSamplesR[i] + ); + } + + saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16); + + delete shimmer; + + delete[] inSamples[0]; + delete[] inSamples[1]; + delete[] inSamples; + + delete[] outSamplesL; + delete[] outSamplesR; +} diff --git a/src/test/test_unitFXTuning.cpp b/src/test/test_unitFXTuning.cpp new file mode 100644 index 0000000..007882e --- /dev/null +++ b/src/test/test_unitFXTuning.cpp @@ -0,0 +1,130 @@ +#include "test_fx_helper.h" + +#include "../fx_dry.h" +#include "../fx_tube.h" +#include "../fx_chorus.h" +#include "../fx_flanger.h" +#include "../fx_orbitone.h" +#include "../fx_phaser.h" +#include "../fx_delay.h" +#include "../effect_platervbstereo.h" +#include "../fx_shimmer_reverb.h" + +TEST(UnitFXTuning, Dry) +{ + Dry fx(SAMPLING_FREQUENCY); + + PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); + SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx); + SAVE_AUDIO_RESULTS(full_test_name, outSamples, size); + CLEANUP_AUDIO_TEST(inSamples, outSamples); +} + +TEST(UnitFXTuning, Tube) +{ + Tube fx(SAMPLING_FREQUENCY); + fx.setOverdrive(0.5f); + + PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); + SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx); + SAVE_AUDIO_RESULTS(full_test_name, outSamples, size); + CLEANUP_AUDIO_TEST(inSamples, outSamples); +} + +TEST(UnitFXTuning, Chorus) +{ + Chorus fx(SAMPLING_FREQUENCY); + fx.setRate(0.4f); + fx.setDepth(0.7f); + + PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); + SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx); + SAVE_AUDIO_RESULTS(full_test_name, outSamples, size); + CLEANUP_AUDIO_TEST(inSamples, outSamples); +} + +TEST(UnitFXTuning, Flanger) +{ + Flanger fx(SAMPLING_FREQUENCY); + fx.setRate(0.03f); + fx.setDepth(0.75f); + fx.setFeedback(0.5f); + + PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); + SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx); + SAVE_AUDIO_RESULTS(full_test_name, outSamples, size); + CLEANUP_AUDIO_TEST(inSamples, outSamples); +} + +TEST(UnitFXTuning, Orbitone) +{ + Orbitone fx(SAMPLING_FREQUENCY); + fx.setRate(0.4f); + fx.setDepth(0.7f); + + PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); + SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx); + SAVE_AUDIO_RESULTS(full_test_name, outSamples, size); + CLEANUP_AUDIO_TEST(inSamples, outSamples); +} + +TEST(UnitFXTuning, Phaser) +{ + Phaser fx(SAMPLING_FREQUENCY); + fx.setRate(0.1f); + fx.setDepth(1.0f); + fx.setFeedback(0.5f); + fx.setNbStages(12); + + PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); + SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx); + SAVE_AUDIO_RESULTS(full_test_name, outSamples, size); + CLEANUP_AUDIO_TEST(inSamples, outSamples); +} + +TEST(UnitFXTuning, Delay) +{ + Delay fx(SAMPLING_FREQUENCY); + fx.setLeftDelayTime(0.25f); + fx.setLeftDelayTime(0.40f); + fx.setFeedback(0.55f); + fx.setFlutterRate(0.01f); + fx.setFlutterAmount(0.05f); + + PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); + SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx); + SAVE_AUDIO_RESULTS(full_test_name, outSamples, size); + CLEANUP_AUDIO_TEST(inSamples, outSamples); +} + +TEST(UnitFXTuning, PlateReverb) +{ + AudioEffectPlateReverb fx(SAMPLING_FREQUENCY); + fx.set_bypass(false); + fx.size(0.7f); + fx.hidamp(0.5f); + fx.lodamp(0.5f); + fx.lowpass(0.3f); + fx.diffusion(0.65f); + fx.level(1.0f); + + PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); + SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx); + SAVE_AUDIO_RESULTS(full_test_name, outSamples, size); + CLEANUP_AUDIO_TEST(inSamples, outSamples); +} + +TEST(UnitFXTuning, ShimmerReverb) +{ + ShimmerReverb fx(SAMPLING_FREQUENCY); + fx.setInputGain(0.65f); + fx.setTime(0.89f); + fx.setDiffusion(0.75f); + fx.setLP(0.8f); + + PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); + SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx); + SAVE_AUDIO_RESULTS(full_test_name, outSamples, size); + CLEANUP_AUDIO_TEST(inSamples, outSamples); +} + diff --git a/src/test/wave.h b/src/test/wave.h index 068cf0c..918a4ce 100644 --- a/src/test/wave.h +++ b/src/test/wave.h @@ -3,6 +3,7 @@ #include #include #include +#include inline uint32_t id2int(const char id[4]) { @@ -57,15 +58,21 @@ struct WaveHeaderFMT { }; struct WaveHeaderDATA { - char subchunk2Id[4]; + ChunkID subchunk2Id; uint32_t subchunk2Size; }; -float32_t** readWaveFile(const std::string& fileName, size_t& size); +std::ostream& operator<<(std::ostream& out, const ChunkID& id); +std::ostream& operator<<(std::ostream& out, const WaveHeader& hdr); +std::ostream& operator<<(std::ostream& out, const WaveHeaderRIFF& riff); +std::ostream& operator<<(std::ostream& out, const WaveHeaderFMT& fmt); +std::ostream& operator<<(std::ostream& out, const WaveHeaderDATA& data); + +float32_t** readWaveFile(const std::string& fileName, size_t& size, WaveHeader* hdr = nullptr); void saveWaveFile(const std::string& fileName, - float32_t* LChannel, - float32_t* RChannel, + const float32_t* LChannel, + const float32_t* RChannel, size_t size, int sampleRate, int bitsPerSample); diff --git a/src/test/wavein.cpp b/src/test/wavein.cpp index f66ab6c..29267a5 100644 --- a/src/test/wavein.cpp +++ b/src/test/wavein.cpp @@ -10,6 +10,72 @@ #define ASSERT_NORMALIZED(x) #endif +std::ostream& operator<<(std::ostream& out, const WaveHeader& hdr) +{ + out << "WaveHeader" << std::endl; + out << " + chunkId : " << hdr.chunkId << std::endl; + out << " + chunkSize : " << hdr.chunkSize << std::endl; + out << " + format : " << hdr.format << std::endl; + out << " + subchunk1Id : " << hdr.subchunk1Id << std::endl; + out << " + subchunk1Size : " << hdr.subchunk1Size << std::endl; + out << " + audioFormat : " << hdr.audioFormat << std::endl; + out << " + numChannels : " << hdr.numChannels << std::endl; + out << " + sampleRate : " << hdr.sampleRate << std::endl; + out << " + byteRate : " << hdr.byteRate << std::endl; + out << " + blockAlign : " << hdr.blockAlign << std::endl; + out << " + bitsPerSample : " << hdr.bitsPerSample << std::endl; + out << " + subchunk2Id : " << hdr.subchunk2Id << std::endl; + out << " + subchunk2Size : " << hdr.subchunk2Size << std::endl; + + return out; +} + +std::ostream& operator<<(std::ostream& out, const ChunkID& id) +{ + out << "'" + << id.ID[0] + << id.ID[1] + << id.ID[2] + << id.ID[3] + << "'"; + + return out; +} + +std::ostream& operator<<(std::ostream& out, const WaveHeaderRIFF& riff) +{ + out << "WaveHeaderRIFF" << std::endl; + out << " + chunkId : " << riff.chunkId << std::endl; + out << " + chunkSize : " << riff.chunkSize << std::endl; + out << " + format : " << riff.format << std::endl; + + return out; +} + +std::ostream& operator<<(std::ostream& out, const WaveHeaderFMT& fmt) +{ + out << "WaveHeaderFMT" << std::endl; + out << " + subchunk1Id : " << fmt.subchunk1Id << std::endl; + out << " + subchunk1Size : " << fmt.subchunk1Size << std::endl; + out << " + audioFormat : " << fmt.audioFormat << std::endl; + out << " + numChannels : " << fmt.numChannels << std::endl; + out << " + sampleRate : " << fmt.sampleRate << std::endl; + out << " + byteRate : " << fmt.byteRate << std::endl; + out << " + blockAlign : " << fmt.blockAlign << std::endl; + out << " + bitsPerSample : " << fmt.bitsPerSample << std::endl; + + return out; +} + +std::ostream& operator<<(std::ostream& out, const WaveHeaderDATA& data) +{ + out << "WaveHeaderDATA" << std::endl; + out << " + subchunk2Id : " << data.subchunk2Id << std::endl; + out << " + subchunk2Size : " << data.subchunk2Size << std::endl; + + return out; +} + template bool readChunk(std::ifstream& in, uint32_t id, T& chunk) { @@ -33,7 +99,7 @@ bool readChunk(std::ifstream& in, uint32_t id, T& chunk) return false; } -float32_t** readWaveFile(const std::string& fileName, size_t& size) +float32_t** readWaveFile(const std::string& fileName, size_t& size, WaveHeader* hdr) { std::ifstream file(fileName, std::ios::binary); if(!file) @@ -75,12 +141,28 @@ float32_t** readWaveFile(const std::string& fileName, size_t& size) return nullptr; } - size = data.subchunk2Size / (fmt.bitsPerSample / 8); + if(hdr != nullptr) + { + hdr->chunkId = riff.chunkId; + hdr->chunkSize = riff.chunkSize; + hdr->format = riff.format; + hdr->subchunk1Id = fmt.subchunk1Id; + hdr->subchunk1Size = fmt.subchunk1Size; + hdr->audioFormat = fmt.audioFormat; + hdr->numChannels = fmt.numChannels; + hdr->sampleRate = fmt.sampleRate; + hdr->byteRate = fmt.byteRate; + hdr->blockAlign = fmt.blockAlign; + hdr->bitsPerSample = fmt.bitsPerSample; + hdr->subchunk2Id = data.subchunk2Id; + hdr->subchunk2Size = data.subchunk2Size; + } + + size = data.subchunk2Size / fmt.blockAlign; float32_t* LChannel = new float32_t[size]; float32_t* RChannel = new float32_t[size]; - unsigned increment = fmt.numChannels; - unsigned i = 0; + size_t i = 0; while(!file.eof() && i < size) { if(fmt.bitsPerSample == 8) @@ -153,10 +235,7 @@ float32_t** readWaveFile(const std::string& fileName, size_t& size) return nullptr; } - // ASSERT_NORMALIZED(LChannel[i]); - // ASSERT_NORMALIZED(RChannel[i]); - - i += increment; + ++i; } assert(i == size); diff --git a/src/test/waveout.cpp b/src/test/waveout.cpp index 18c6a43..86710a4 100644 --- a/src/test/waveout.cpp +++ b/src/test/waveout.cpp @@ -5,8 +5,8 @@ #include void saveWaveFile(const std::string& fileName, - float32_t* LChannel, - float32_t* RChannel, + const float32_t* LChannel, + const float32_t* RChannel, size_t size, int sampleRate, int bitsPerSample)