diff --git a/src/extra_features.h b/src/extra_features.h index b763bc6..14792a8 100644 --- a/src/extra_features.h +++ b/src/extra_features.h @@ -64,6 +64,8 @@ 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) diff --git a/src/fx_components.cpp b/src/fx_components.cpp index e8013fb..516f0f9 100644 --- a/src/fx_components.cpp +++ b/src/fx_components.cpp @@ -20,6 +20,7 @@ FastLFO::FastLFO(float32_t sampling_rate, float32_t min_frequency, float32_t max InitialPhase(initial_phase), min_frequency_(min_frequency), max_frequency_(max_frequency), + frequency_(0.0f), y0_(0.0f), y1_(0.0f), iir_coefficient_(0.0f), @@ -628,9 +629,9 @@ float32_t softSaturator3(float32_t input, float32_t overdrive) float32_t softSaturator4(float32_t input, float32_t saturator_factor) { - float32_t x = input * (saturator_factor); - float32_t abs_x = std::fabs(x); - float32_t sat_x = std::log(1.0 + abs_x) / std::log(1.0f + saturator_factor); + float32_t x = input * saturator_factor; + float32_t abs_x = std::abs(x); + float32_t sat_x = std::log(1.0f + abs_x) / std::log(1.0f + saturator_factor); return x > 0 ? sat_x : -sat_x; } diff --git a/src/fx_engine.hpp b/src/fx_engine.hpp index 2688bd1..0595ede 100644 --- a/src/fx_engine.hpp +++ b/src/fx_engine.hpp @@ -121,7 +121,8 @@ public: }; FxEngine(float32_t sampling_rate, float32_t max_lfo_frequency = 20.0f) : - FXBase(sampling_rate) + FXBase(sampling_rate), + write_ptr_(0) { this->buffer_ = new T[size]; for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i) this->lfo_[i] = enable_lfo ? new LFO(sampling_rate, 0.0f, max_lfo_frequency) : nullptr; diff --git a/src/fx_tube.cpp b/src/fx_tube.cpp index a92ca63..69b2775 100644 --- a/src/fx_tube.cpp +++ b/src/fx_tube.cpp @@ -4,8 +4,9 @@ Tube::Tube(float32_t samplingRate) : FXElement(samplingRate), - overdrive_(0.0f), - saturator_factor_(0.0f) + overdrive_(1.0f), + saturator_factor_(1.0f), + gain_factor_(1.0f) { this->setOverdrive(0.0f); } @@ -21,19 +22,29 @@ void Tube::reset() void Tube::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { - outL = softSaturator4(inL, this->saturator_factor_); - outR = softSaturator4(inR, this->saturator_factor_); + 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_; + + 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_; + + outR = inR > 0 ? sat_x : -sat_x; } void Tube::setOverdrive(float32_t overdrive) { - static const float32_t N = 200.0f; + static constexpr float32_t N = 200.0f; overdrive = constrain(overdrive, 0.0f, 1.0f); if(this->overdrive_ != overdrive) { this->overdrive_ = overdrive; this->saturator_factor_ = 1.0f + N * overdrive; + this->gain_factor_ = 1.0f / std::log(1.0f + this->saturator_factor_); } } diff --git a/src/fx_tube.h b/src/fx_tube.h index 9a96e13..8f6f0b2 100644 --- a/src/fx_tube.h +++ b/src/fx_tube.h @@ -18,7 +18,7 @@ // #pragma once -#include "fx_components.h" +#include "fx.h" class Tube : public FXElement { @@ -37,4 +37,5 @@ public: private: float32_t overdrive_; float32_t saturator_factor_; + float32_t gain_factor_; }; \ No newline at end of file diff --git a/src/mixing_console.hpp b/src/mixing_console.hpp index f8956ce..be0c67f 100644 --- a/src/mixing_console.hpp +++ b/src/mixing_console.hpp @@ -33,471 +33,578 @@ #include "fx_dry.h" #include "fx_unit2.hpp" -template +template class MixingConsole : public FXBase { DISALLOW_COPY_AND_ASSIGN(MixingConsole); public: - MixingConsole(float32_t sampling_rate, size_t buffer_size) : - FXBase(sampling_rate), - BufferSize(buffer_size) - { - for(size_t i = 0; i < nb_inputs; ++i) - { - this->input_sample_buffer_[StereoChannels::Left ][i] = new float32_t[this->BufferSize]; - this->input_sample_buffer_[StereoChannels::Right][i] = new float32_t[this->BufferSize]; - memset(this->input_sample_buffer_[StereoChannels::Left ][i], 0, this->BufferSize); - memset(this->input_sample_buffer_[StereoChannels::Right][i], 0, this->BufferSize); - } + MixingConsole(float32_t sampling_rate, size_t buffer_size); + ~MixingConsole(); - memset(this->fx_, 0, MixerOutput::kFXCount * sizeof(FXElement*)); + // Send section + void setChannelLevel(size_t in, float32_t lvl); + void setPan(size_t in, float32_t pan); + void setSendLevel(size_t in, MixerOutput fx, float32_t lvl); + void setInputSample(size_t in, float32_t sampleL, float32_t sampleR); + void setInputSampleBuffer(size_t in, float32_t* samples); + void setInputSampleBuffer(size_t in, float32_t* samplesL, float32_t* samplesR); - this->fx_[MixerOutput::FX_Tube] = this->tube_ = new FXUnit2(sampling_rate); - this->fx_[MixerOutput::FX_Chorus] = this->chorus_ = new FXUnit2(sampling_rate); - this->fx_[MixerOutput::FX_Flanger] = this->flanger_ = new FXUnit2(sampling_rate); - this->fx_[MixerOutput::FX_Orbitone] = this->orbitone_ = new FXUnit2(sampling_rate); - this->fx_[MixerOutput::FX_Phaser] = this->phaser_ = new FXUnit2(sampling_rate); - this->fx_[MixerOutput::FX_Delay] = this->delay_ = new FXUnit2(sampling_rate); - this->fx_[MixerOutput::FX_PlateReverb] = this->plate_reverb_ = new FXUnit2(sampling_rate); - this->fx_[MixerOutput::FX_ShimmerReverb] = this->shimmer_reverb_ = new FXUnit2(sampling_rate); - this->fx_[MixerOutput::MainOutput] = this->dry_ = new FXUnit2(sampling_rate); + // Return section + void setReturnLevel(MixerOutput ret, MixerOutput dest, float32_t lvl); + void setReturnSample(MixerOutput ret, float32_t sampleL, float32_t sampleR); - this->init(); - } + // Get FX + FXElement* getFX(size_t fx); + FXUnit2* getTube(); + FXUnit2* getChorus(); + FXUnit2* getFlanger(); + FXUnit2* getOrbitone(); + FXUnit2* getPhaser(); + FXUnit2* getDelay(); + FXUnit2* getPlateReverb(); + FXUnit2* getShimmerReverb(); + FXUnit2* getDry(); - ~MixingConsole() - { - for(size_t i = 0; i < nb_inputs; ++i) - { - delete this->input_sample_buffer_[StereoChannels::Left ][i]; - delete this->input_sample_buffer_[StereoChannels::Right][i]; - } + // Processing + void init(); + void reset(); + void processSample(float32_t& outL, float32_t& outR); + void prepare(); + void process(float32_t* outL, float32_t* outR); - for(size_t i = 0; i < MixerOutput::kFXCount; ++i) - { - delete this->fx_[i]; - } - } +protected: + void updatePan(size_t in); + void setLevel(size_t in, MixerOutput fx, float32_t lvl); + void setSample(size_t in, float32_t sampleL, float32_t sampleR); - // Send section +private: + const size_t BufferSize; - void setChannelLevel(size_t in, float32_t lvl) - { - assert(in < nb_inputs); + float32_t channel_level_[nb_inputs]; + float32_t pan_[StereoChannels::kNumChannels + 1][nb_inputs]; + float32_t* input_sample_buffer_[StereoChannels::kNumChannels][nb_inputs]; + float32_t input_samples_[StereoChannels::kNumChannels][nb_inputs + MixerOutput::kFXCount - 1]; + float32_t levels_[MixerOutput::kFXCount][nb_inputs + MixerOutput::kFXCount - 1]; - lvl = constrain(lvl, 0.0f, 1.0f); - if(lvl == this->channel_level_[in]) return; + FXElement* fx_[MixerOutput::kFXCount]; + FXUnit2* tube_; + FXUnit2* chorus_; + FXUnit2* flanger_; + FXUnit2* orbitone_; + FXUnit2* phaser_; + FXUnit2* delay_; + FXUnit2* plate_reverb_; + FXUnit2* shimmer_reverb_; + FXUnit2* dry_; + +#if defined(DEBUG) +public: + void dump(std::ostream& out, const std::string& key = "") const; +#endif +}; - this->channel_level_[in] = lvl; - this->updatePan(); - } - void setPan(size_t in, float32_t pan) +template +MixingConsole::MixingConsole(float32_t sampling_rate, size_t buffer_size) : + FXBase(sampling_rate), + BufferSize(buffer_size) +{ + for(size_t i = 0; i < nb_inputs; ++i) { - assert(in < nb_inputs); + this->input_sample_buffer_[StereoChannels::Left ][i] = new float32_t[this->BufferSize]; + this->input_sample_buffer_[StereoChannels::Right][i] = new float32_t[this->BufferSize]; + memset(this->input_sample_buffer_[StereoChannels::Left ][i], 0, this->BufferSize); + memset(this->input_sample_buffer_[StereoChannels::Right][i], 0, this->BufferSize); + } - pan = constrain(pan, 0.0f, 1.0f); - - if(pan == this->pan_[StereoChannels::kNumChannels][in]) return; + memset(this->fx_, 0, MixerOutput::kFXCount * sizeof(FXElement*)); - this->pan_[StereoChannels::kNumChannels][in] = pan; - this->updatePan(in); - } + this->fx_[MixerOutput::FX_Tube] = this->tube_ = new FXUnit2(sampling_rate); + this->fx_[MixerOutput::FX_Chorus] = this->chorus_ = new FXUnit2(sampling_rate); + this->fx_[MixerOutput::FX_Flanger] = this->flanger_ = new FXUnit2(sampling_rate); + this->fx_[MixerOutput::FX_Orbitone] = this->orbitone_ = new FXUnit2(sampling_rate); + this->fx_[MixerOutput::FX_Phaser] = this->phaser_ = new FXUnit2(sampling_rate); + this->fx_[MixerOutput::FX_Delay] = this->delay_ = new FXUnit2(sampling_rate); + this->fx_[MixerOutput::FX_PlateReverb] = this->plate_reverb_ = new FXUnit2(sampling_rate); + this->fx_[MixerOutput::FX_ShimmerReverb] = this->shimmer_reverb_ = new FXUnit2(sampling_rate); + this->fx_[MixerOutput::MainOutput] = this->dry_ = new FXUnit2(sampling_rate); - void setSendLevel(size_t in, MixerOutput fx, float32_t lvl) - { - assert(in < nb_inputs); - assert(fx < kFXCount); + this->init(); +} - this->setLevel(in, fx, lvl); +template +MixingConsole::~MixingConsole() +{ + for(size_t i = 0; i < nb_inputs; ++i) + { + delete this->input_sample_buffer_[StereoChannels::Left ][i]; + delete this->input_sample_buffer_[StereoChannels::Right][i]; } - void setInputSample(size_t in, float32_t sampleL, float32_t sampleR) + for(size_t i = 0; i < MixerOutput::kFXCount; ++i) { - assert(in < nb_inputs); - - this->setSample(in, sampleL, sampleR); + delete this->fx_[i]; } +} - void setInputSampleBuffer(size_t in, float32_t* samples) - { - assert(in < nb_inputs); +// Send section +template +void MixingConsole::setChannelLevel(size_t in, float32_t lvl) +{ + assert(in < nb_inputs); - if(samples != nullptr) - { - arm_scale_f32(samples, this->pan_[StereoChannels::Left ][in], this->input_sample_buffer_[StereoChannels::Left ][in], this->BufferSize); - arm_scale_f32(samples, this->pan_[StereoChannels::Right][in], this->input_sample_buffer_[StereoChannels::Right][in], this->BufferSize); - } - else - { - memset(this->input_sample_buffer_[StereoChannels::Left ][in], 0, this->BufferSize * sizeof(float32_t)); - memset(this->input_sample_buffer_[StereoChannels::Right][in], 0, this->BufferSize * sizeof(float32_t)); - } - } + lvl = constrain(lvl, 0.0f, 1.0f); + if(lvl == this->channel_level_[in]) return; - void setInputSampleBuffer(size_t in, float32_t* samplesL, float32_t* samplesR) - { - assert(in < nb_inputs); - if(samplesL != nullptr) - { - memcpy(this->input_sample_buffer_[StereoChannels::Left ][in], samplesL, this->BufferSize * sizeof(float32_t)); - } - else - { - memset(this->input_sample_buffer_[StereoChannels::Left ][in], 0, this->BufferSize * sizeof(float32_t)); - } + this->channel_level_[in] = lvl; + this->updatePan(in); +} - if(samplesR != nullptr) - { - memcpy(this->input_sample_buffer_[StereoChannels::Right][in], samplesR, this->BufferSize * sizeof(float32_t)); - } - else - { - memset(this->input_sample_buffer_[StereoChannels::Right][in], 0, this->BufferSize * sizeof(float32_t)); - } - } +template +void MixingConsole::setPan(size_t in, float32_t pan) +{ + assert(in < nb_inputs); - // Return section + pan = constrain(pan, 0.0f, 1.0f); + + if(pan == this->pan_[StereoChannels::kNumChannels][in]) return; - void setReturnLevel(MixerOutput ret, MixerOutput dest, float32_t lvl) - { - assert(ret < (kFXCount - 1)); - assert(dest < kFXCount); + this->pan_[StereoChannels::kNumChannels][in] = pan; + this->updatePan(in); +} - if(ret == dest) - { - // An FX cannot feedback on itself - return; - } +template +void MixingConsole::setSendLevel(size_t in, MixerOutput fx, float32_t lvl) +{ + assert(in < nb_inputs); + assert(fx < kFXCount); - this->setLevel(nb_inputs + ret, dest, lvl); - } + this->setLevel(in, fx, lvl); +} - void setReturnSample(MixerOutput ret, float32_t sampleL, float32_t sampleR) - { - assert(ret < (kFXCount - 1)); +template +void MixingConsole::setInputSample(size_t in, float32_t sampleL, float32_t sampleR) +{ + assert(in < nb_inputs); - this->setSample(nb_inputs + ret, sampleL, sampleR); - } + this->setSample(in, sampleL, sampleR); +} - // Get FX - FXElement* getFX(size_t fx) - { - assert(fx < MixerOutput::kFXCount); - return this->fx_[fx]; - } +template +void MixingConsole::setInputSampleBuffer(size_t in, float32_t* samples) +{ + assert(in < nb_inputs); - FXUnit2* getTube() + if(samples != nullptr) { - return this->tube_; + arm_scale_f32(samples, this->pan_[StereoChannels::Left ][in], this->input_sample_buffer_[StereoChannels::Left ][in], this->BufferSize); + arm_scale_f32(samples, this->pan_[StereoChannels::Right][in], this->input_sample_buffer_[StereoChannels::Right][in], this->BufferSize); } - - FXUnit2* getChorus() + else { - return this->chorus_; + memset(this->input_sample_buffer_[StereoChannels::Left ][in], 0, this->BufferSize * sizeof(float32_t)); + memset(this->input_sample_buffer_[StereoChannels::Right][in], 0, this->BufferSize * sizeof(float32_t)); } +} - FXUnit2* getFlanger() +template +void MixingConsole::setInputSampleBuffer(size_t in, float32_t* samplesL, float32_t* samplesR) +{ + assert(in < nb_inputs); + if(samplesL != nullptr) { - return this->flanger_; + memcpy(this->input_sample_buffer_[StereoChannels::Left ][in], samplesL, this->BufferSize * sizeof(float32_t)); } - - FXUnit2* getOrbitone() + else { - return this->orbitone_; + memset(this->input_sample_buffer_[StereoChannels::Left ][in], 0, this->BufferSize * sizeof(float32_t)); } - FXUnit2* getPhaser() + if(samplesR != nullptr) { - return this->phaser_; + memcpy(this->input_sample_buffer_[StereoChannels::Right][in], samplesR, this->BufferSize * sizeof(float32_t)); } - - FXUnit2* getDelay() + else { - return this->delay_; + memset(this->input_sample_buffer_[StereoChannels::Right][in], 0, this->BufferSize * sizeof(float32_t)); } +} - FXUnit2* getPlateReverb() - { - return this->plate_reverb_; - } +// Return section +template +void MixingConsole::setReturnLevel(MixerOutput ret, MixerOutput dest, float32_t lvl) +{ + assert(ret < (kFXCount - 1)); + assert(dest < kFXCount); - FXUnit2* getShimmerReverb() + if(ret == dest) { - return this->shimmer_reverb_; + // An FX cannot feedback on itself + return; } - FXUnit2* getDry() - { - return this->dry_; - } + this->setLevel(nb_inputs + ret, dest, lvl); +} - // Processing +template +void MixingConsole::setReturnSample(MixerOutput ret, float32_t sampleL, float32_t sampleR) +{ + assert(ret < (kFXCount - 1)); - void init() - { - memset(this->channel_level_, 0, nb_inputs * sizeof(float32_t)); - for(size_t i = 0; i <= StereoChannels::kNumChannels; ++i) memset(this->pan_[i], 0, nb_inputs * sizeof(float32_t)); + this->setSample(nb_inputs + ret, sampleL, sampleR); +} - for(size_t i = 0; i < MixerOutput::kFXCount; ++i) - memset(this->levels_[i], 0, (nb_inputs + MixerOutput::kFXCount - 1) * sizeof(float32_t)); - - for(size_t i = 0; i < StereoChannels::kNumChannels; ++i) - memset(this->input_samples_[i], 0, (nb_inputs + MixerOutput::kFXCount - 1) * sizeof(float32_t)); +// Get FX +template +FXElement* MixingConsole::getFX(size_t fx) +{ + assert(fx < MixerOutput::kFXCount); + return this->fx_[fx]; +} - this->reset(); - } +template +FXUnit2* MixingConsole::getTube() +{ + return this->tube_; +} + +template +FXUnit2* MixingConsole::getChorus() +{ + return this->chorus_; +} + +template +FXUnit2* MixingConsole::getFlanger() +{ + return this->flanger_; +} + +template +FXUnit2* MixingConsole::getOrbitone() +{ + return this->orbitone_; +} + +template +FXUnit2* MixingConsole::getPhaser() +{ + return this->phaser_; +} - void reset() +template +FXUnit2* MixingConsole::getDelay() +{ + return this->delay_; +} + +template +FXUnit2* MixingConsole::getPlateReverb() +{ + return this->plate_reverb_; +} + +template +FXUnit2* MixingConsole::getShimmerReverb() +{ + return this->shimmer_reverb_; +} + +template +FXUnit2* MixingConsole::getDry() +{ + return this->dry_; +} + +// Processing +template +void MixingConsole::init() +{ + memset(this->channel_level_, 0, nb_inputs * sizeof(float32_t)); + for(size_t i = 0; i <= StereoChannels::kNumChannels; ++i) memset(this->pan_[i], 0, nb_inputs * sizeof(float32_t)); + + for(size_t i = 0; i < MixerOutput::kFXCount; ++i) + memset(this->levels_[i], 0, (nb_inputs + MixerOutput::kFXCount - 1) * sizeof(float32_t)); + + for(size_t i = 0; i < StereoChannels::kNumChannels; ++i) + memset(this->input_samples_[i], 0, (nb_inputs + MixerOutput::kFXCount - 1) * sizeof(float32_t)); + + this->reset(); +} + +template +void MixingConsole::reset() +{ + for(size_t i = 0; i < nb_inputs; ++i) { - for(size_t i = 0; i < nb_inputs; ++i) - { - memset(this->input_sample_buffer_[StereoChannels::Left ][i], 0, this->BufferSize); - memset(this->input_sample_buffer_[StereoChannels::Right][i], 0, this->BufferSize); - } + memset(this->input_sample_buffer_[StereoChannels::Left ][i], 0, this->BufferSize); + memset(this->input_sample_buffer_[StereoChannels::Right][i], 0, this->BufferSize); + } - for(size_t i = 0; i < MixerOutput::kFXCount; ++i) - { - this->fx_[i]->reset(); - } + for(size_t i = 0; i < MixerOutput::kFXCount; ++i) + { + this->fx_[i]->reset(); - for(size_t i = 0; i < MixerOutput::MainOutput; ++i) + if(i != MixerOutput::MainOutput) { this->setReturnSample(static_cast(i), 0.0f, 0.0f); } } +} - void processSample(float32_t& outL, float32_t& outR) - { - float32_t fx_inputs_[MixerOutput::kFXCount][StereoChannels::kNumChannels]; - float32_t fx_outputs_[MixerOutput::kFXCount][StereoChannels::kNumChannels]; +template +void MixingConsole::processSample(float32_t& outL, float32_t& outR) +{ + constexpr size_t bufferSize = nb_inputs + MixerOutput::kFXCount - 1; - for(size_t i = 0; i < MixerOutput::kFXCount; ++i) + float32_t fx_inputs_[MixerOutput::kFXCount][StereoChannels::kNumChannels]; + float32_t fx_outputs_[MixerOutput::kFXCount][StereoChannels::kNumChannels]; + for(size_t i = 0; i < MixerOutput::kFXCount; ++i) + { + // Compute the samples that will feed the MixerOutput and process MixerOutput + fx_inputs_[i][StereoChannels::Left ] = arm_weighted_sum_f32(this->input_samples_[StereoChannels::Left ], this->levels_[i], bufferSize); + fx_inputs_[i][StereoChannels::Right] = arm_weighted_sum_f32(this->input_samples_[StereoChannels::Right], this->levels_[i], bufferSize); + + // Process the FX + this->fx_[i]->processSample( + fx_inputs_[i][StereoChannels::Left], + fx_inputs_[i][StereoChannels::Right], + fx_outputs_[i][StereoChannels::Left], + fx_outputs_[i][StereoChannels::Right] + ); + + if(i != MixerOutput::MainOutput) { - // Compute the samples that will feed the MixerOutput and process MixerOutput - fx_inputs_[i][StereoChannels::Left ] = arm_weighted_sum_f32(this->input_samples_[StereoChannels::Left ], this->levels_[i], nb_inputs + MixerOutput::kFXCount - 1); - fx_inputs_[i][StereoChannels::Right] = arm_weighted_sum_f32(this->input_samples_[StereoChannels::Right], this->levels_[i], nb_inputs + MixerOutput::kFXCount - 1); - - // Process the FX - this->fx_[i]->processSample( - fx_inputs_[i][StereoChannels::Left], - fx_inputs_[i][StereoChannels::Right], + // Feedback the resulting samples except for the main output + this->setReturnSample( + static_cast(i), fx_outputs_[i][StereoChannels::Left], fx_outputs_[i][StereoChannels::Right] ); - - if(i != MixerOutput::MainOutput) - { - // Feedback the resulting samples except for the main output - this->setReturnSample( - static_cast(i), - fx_outputs_[i][StereoChannels::Left], - fx_outputs_[i][StereoChannels::Right] - ); - } } + } + + // Return this main output sample + outL = fx_inputs_[MixerOutput::MainOutput][StereoChannels::Left]; + outR = fx_inputs_[MixerOutput::MainOutput][StereoChannels::Right]; +} - // Return this main output sample - outL = fx_inputs_[MixerOutput::MainOutput][StereoChannels::Left]; - outR = fx_inputs_[MixerOutput::MainOutput][StereoChannels::Right]; +template +void MixingConsole::prepare() +{ + for(size_t i = 0; i < MixerOutput::kFXCount; ++i) + { + this->fx_[i]->prepare(); } +} - void prepare() +template +void MixingConsole::process(float32_t* outL, float32_t* outR) +{ + this->prepare(); + + for(size_t s = 0; s < this->BufferSize; ++s) { - for(size_t i = 0; i < MixerOutput::kFXCount; ++i) + for(size_t in = 0; in < nb_inputs; ++in) { - this->fx_[i]->prepare(); + this->setSample( + in, + this->input_sample_buffer_[StereoChannels::Left ][in][s], + this->input_sample_buffer_[StereoChannels::Right][in][s] + ); } + + this->processSample(*outL, *outR); + ++outL; + ++outR; } +} - void process(float32_t* outL, float32_t* outR) - { - this->prepare(); +template +void MixingConsole::updatePan(size_t in) +{ + float32_t pan = mapfloat(this->pan_[StereoChannels::kNumChannels][in], 0.0f, 1.0f, 0.0, Constants::MPI_2); + this->pan_[StereoChannels::Left ][in] = arm_cos_f32(pan) * this->channel_level_[in]; + this->pan_[StereoChannels::Right][in] = arm_sin_f32(pan) * this->channel_level_[in]; +} - for(size_t s = 0; s < this->BufferSize; ++s) - { - for(size_t in = 0; in < nb_inputs; ++in) - { - this->setSample( - in, - this->input_sample_buffer_[StereoChannels::Left ][in][s], - this->input_sample_buffer_[StereoChannels::Right][in][s] - ); - } +template +void MixingConsole::setLevel(size_t in, MixerOutput fx, float32_t lvl) +{ + assert(in < (nb_inputs + MixerOutput::kFXCount - 1)); + assert(fx < MixerOutput::kFXCount); - this->processSample(*outL, *outR); - ++outL; - ++outR; - } - } + this->levels_[fx][in] = constrain(lvl, 0.0f, 1.0f); +} -protected: - void updatePan(size_t in) - { - float32_t pan = mapfloat(this->pan_[StereoChannels::kNumChannels][in], 0.0f, 1.0f, 0.0, Constants::MPI_2); - this->pan_[StereoChannels::Left ][in] = arm_sin_f32(pan) * this->channel_level_[in]; - this->pan_[StereoChannels::Right][in] = arm_cos_f32(pan) * this->channel_level_[in]; - } +template +void MixingConsole::setSample(size_t in, float32_t sampleL, float32_t sampleR) +{ + assert(in < (nb_inputs + MixerOutput::kFXCount - 1)); + this->input_samples_[StereoChannels::Left ][in] = sampleL; + this->input_samples_[StereoChannels::Right][in] = sampleR; +} - void setLevel(size_t in, MixerOutput fx, float32_t lvl) - { - assert(in < (nb_inputs + MixerOutput::kFXCount - 1)); - assert(fx < MixerOutput::kFXCount); - this->levels_[fx][in] = constrain(lvl, 0.0f, 1.0f); - } +#if defined(DEBUG) - void setSample(size_t in, float32_t sampleL, float32_t sampleR) - { - assert(in < (nb_inputs + MixerOutput::kFXCount - 1)); - this->input_samples_[StereoChannels::Left ][in] = sampleL; - this->input_samples_[StereoChannels::Right][in] = sampleR; - } +#define SS_RESET(ss, prec, align) ss.str(""); ss.precision(prec); ss << align; ss << std::fixed +#define SS_SPACE(ss, spc, nb, align, sep) ss.fill(spc); ss.width(nb); ss << "" << sep +#define SS__TEXT(ss, spc, nb, align, sep, txt) ss << align; ss.fill(spc); ss.width(nb); ss << txt << sep -private: - const size_t BufferSize; +template +void MixingConsole::dump(std::ostream& out, const std::string& key) const +{ + constexpr size_t space = 10; + constexpr size_t precision = 5; - float32_t channel_level_[nb_inputs]; - float32_t pan_[StereoChannels::kNumChannels + 1][nb_inputs]; - float32_t* input_sample_buffer_[StereoChannels::kNumChannels][nb_inputs]; - float32_t input_samples_[StereoChannels::kNumChannels][nb_inputs + MixerOutput::kFXCount - 1]; - float32_t levels_[MixerOutput::kFXCount][nb_inputs + MixerOutput::kFXCount - 1]; + std::stringstream ss; - FXElement* fx_[MixerOutput::kFXCount]; - FXUnit2* tube_; - FXUnit2* chorus_; - FXUnit2* flanger_; - FXUnit2* orbitone_; - FXUnit2* phaser_; - FXUnit2* delay_; - FXUnit2* plate_reverb_; - FXUnit2* shimmer_reverb_; - FXUnit2* dry_; + out << "MixingConsole dump - START - " << key.c_str() << std::endl << std::endl; -#if defined(DEBUG) -public: - void dump(std::ostream& out, const std::string& key = "") const + out << "\t" << "Input levels & Pan:" << std::endl; + { + SS_RESET(ss, precision, std::left); + SS_SPACE(ss, ' ', space, std::left, '|'); + SS__TEXT(ss, ' ', space, std::left, '|', "Level"); + SS__TEXT(ss, ' ', space, std::left, '|', "Pan L"); + SS__TEXT(ss, ' ', space, std::left, '|', "Pan R"); + SS__TEXT(ss, ' ', space, std::left, '|', "Pan"); + out << "\t" << ss.str() << std::endl; + + SS_RESET(ss, precision, std::left); + SS_SPACE(ss, '-', space, std::left, '+'); + SS_SPACE(ss, '-', space, std::left, '+'); + SS_SPACE(ss, '-', space, std::left, '+'); + SS_SPACE(ss, '-', space, std::left, '+'); + SS_SPACE(ss, '-', space, std::left, '+'); + out << "\t" << ss.str() << std::endl; + + for(size_t i = 0; i < nb_inputs; ++i) + { + std::stringstream s; + s << "* Input "; + s << (i + 1); + + SS_RESET(ss, precision, std::left); + SS__TEXT(ss, ' ', space, std::left, '|', s.str()); + SS__TEXT(ss, ' ', space - 1, std::right, " |", this->channel_level_[i]); + SS__TEXT(ss, ' ', space - 1, std::right, " |", this->pan_[StereoChannels::Left][i]); + SS__TEXT(ss, ' ', space - 1, std::right, " |", this->pan_[StereoChannels::Right][i]); + SS__TEXT(ss, ' ', space - 1, std::right, " |", this->pan_[StereoChannels::kNumChannels][i]); + + out << "\t" << ss.str() << std::endl; + } + } + out << std::endl; + + out << "\t" << "Mixing Console input samples:" << std::endl; { - std::stringstream ss; - ss.fill(' '); - ss.precision(4); - ss << std::fixed; - - out << "\t" << "MixingConsole dump - START - " << key.c_str() << std::endl; - - out << "\t" << "Input levels & Pan:" << std::endl; - - ss.str(""); - ss << std::left; - ss << "\t"; - ss.width(11); - ss << ""; - ss.width(8); - ss << "Level"; - ss.width(8); - ss << "Pan L"; - ss.width(8); - ss << "Pan R"; - ss.width(8); - ss << "Pan"; - out << ss.str() << std::endl; - - ss.str(""); + SS_RESET(ss, precision, std::left); + SS_SPACE(ss, ' ', space, std::left, '|'); for(size_t i = 0; i < nb_inputs; ++i) { - ss << std::left; - ss << "\t" << "* Input " << (i + 1) << ": "; - - ss.precision(4); - ss << std::showpos; - ss << std::fixed; - ss.width(8); - ss << this->channel_level_[i]; - ss.width(8); - ss << this->pan_[StereoChannels::Left][i]; - ss.width(8); - ss << this->pan_[StereoChannels::Right][i]; - ss.width(8); - ss << this->pan_[StereoChannels::kNumChannels][i]; - ss << std::endl; + std::stringstream s; + s << "Input "; + s << (i + 1); + + SS__TEXT(ss, ' ', space, std::left, '|', s.str()); } - out << ss.str() << std::endl; - - out << "\t" << "Mixing Console input samples:" << std::endl; - ss.str(""); - ss.fill(' '); - ss << std::left; - ss << "\t"; - ss.width(11); - ss << ""; + for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i) + { + std::string s = toString(static_cast(i)); + s.resize(space); + SS__TEXT(ss, ' ', space, std::left, '|', s.c_str()); + } + out << "\t" << ss.str() << std::endl; + + SS_RESET(ss, precision, std::left); + SS_SPACE(ss, '-', space, std::left, '+'); for(size_t i = 0; i < nb_inputs; ++i) { - ss << "Input " << (i + 1) << " "; + SS_SPACE(ss, '-', space, std::left, '+'); } for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i) { - ss.width(7); - ss << toString(static_cast(i)) << " "; + SS_SPACE(ss, '-', space, std::left, '+'); } - out << ss.str() << std::endl; + out << "\t" << ss.str() << std::endl; const char* LR = "LR"; - ss.str(""); for(size_t c = 0; c < StereoChannels::kNumChannels; ++c) { - ss << std::left; - ss << "\t" << "* Input " << LR[c] << ": "; - ss.precision(4); - ss << std::showpos; - ss << std::fixed; + std::stringstream s; + s << "* Input "; + s << LR[c]; + + SS_RESET(ss, precision, std::left); + SS__TEXT(ss, ' ', space, std::left, '|', s.str()); for(size_t i = 0; i < (nb_inputs + MixerOutput::kFXCount - 1); ++i) { - ss.width(8); - ss << this->input_samples_[c][i]; + SS__TEXT(ss, ' ', space - 1, std::right, " |", this->input_samples_[c][i]); } - ss << std::endl; + out << "\t" << ss.str() << std::endl; } - out << ss.str() << std::endl; - - out << "\t" << "Mixing Console levels:" << std::endl; - ss.str(""); - ss << std::left; - ss << "\t"; - ss.width(11); - ss << " "; + } + out << std::endl; + + out << "\t" << "Mixing Console levels:" << std::endl; + { + SS_RESET(ss, precision, std::left); + SS_SPACE(ss, ' ', space, std::left, '|'); for(size_t i = 0; i < nb_inputs; ++i) { - ss << "Input " << (i + 1) << " "; + std::stringstream s; + s << "Input "; + s << (i + 1); + + SS__TEXT(ss, ' ', space, std::left, '|', s.str()); } for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i) { - ss.width(7); - ss << toString(static_cast(i)) << " "; + std::string s = toString(static_cast(i)); + s.resize(space); + SS__TEXT(ss, ' ', space, std::left, '|', s.c_str()); } - out << ss.str() << std::endl; + out << "\t" << ss.str() << std::endl; + + SS_RESET(ss, precision, std::left); + SS_SPACE(ss, '-', space, std::left, '+'); + for(size_t i = 0; i < nb_inputs; ++i) + { + SS_SPACE(ss, '-', space, std::left, '+'); + } + for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i) + { + SS_SPACE(ss, '-', space, std::left, '+'); + } + out << "\t" << ss.str() << std::endl; - ss.str(""); for(size_t c = 0; c < MixerOutput::kFXCount; ++c) { - ss << std::left; - ss << "\t"; - ss.width(9); - ss << toString(static_cast(c)); - ss << ": "; - ss.precision(4); - ss << std::showpos; - ss << std::fixed; + SS_RESET(ss, precision, std::left); + std::string s = toString(static_cast(c)); + s.resize(space); + SS__TEXT(ss, ' ', space, std::left, '|', s.c_str()); for(size_t i = 0; i < (nb_inputs + MixerOutput::kFXCount - 1); ++i) { - ss.width(8); - ss << this->levels_[c][i]; + SS__TEXT(ss, ' ', space - 1, std::right, " |", this->levels_[c][i]); } - ss << std::endl; + out << "\t" << ss.str() << std::endl; } - out << ss.str() << std::endl; - - out << "MixingConsole dump - END - " << key.c_str() << std::endl; } + out << std::endl; + + out << "MixingConsole dump - END - " << key.c_str() << std::endl; +} + +#define DUMP1(mixer, out) mixer->dump(cout) +#define DUMP2(mixer, out, tag) mixer->dump(cout, tag) + +#else + +#define DUMP1(mixer, out) +#define DUMP2(mixer, out, tag) + #endif -}; \ No newline at end of file diff --git a/src/test/Makefile b/src/test/Makefile index 32f972f..64ba288 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -1,6 +1,7 @@ OBJDIR := objects OUTPUT_FOLDER = results EXE := all_test.bin +BETA := beta.bin CXX := g++ CXXFLAGS = -g -std=c++20 @@ -14,7 +15,7 @@ INCLUDES = -I../../CMSIS_5/CMSIS/DSP/Include/ \ LD := g++ LIBS := -lm -lstdc++ -lgtest -lpthread -TST_SRCS := $(filter-out waveplay.cpp, $(wildcard *.cpp)) +TST_SRCS := $(filter-out waveplay.cpp, $(filter-out beta.cpp, $(wildcard *.cpp))) FX__SRCS := ../fx.cpp FX__SRCS += ../fx_components.cpp FX__SRCS += ../fx_svf.cpp @@ -38,6 +39,10 @@ test: $(EXE) $(OUTPUT_FOLDER) rm -f $(OUTPUT_FOLDER)/* ./$(EXE) +test-debug: $(EXE) $(OUTPUT_FOLDER) + rm -f $(OUTPUT_FOLDER)/* + valgrind --leak-check=full --leak-resolution=high --show-leak-kinds=all --xtree-leak=yes --show-mismatched-frees=yes --log-file=$(OUTPUT_FOLDER)/valgrind-analysis-results.txt ./$(EXE) + $(OBJDIR): mkdir -p $@ @@ -53,5 +58,9 @@ $(OBJDIR)/%.o: ../%.cpp $(OBJDIR) $(EXE): $(TST_OBJS) $(FX__OBJS) $(LD) $(CXXFLAGS) $(call wildcard,$(TST_OBJS)) $(call wildcard,$(FX__OBJS)) -o $@ $(LIBS) +$(BETA): $(FX__OBJS) beta.cpp + $(CXX) $(CXXFLAGS) $(DEFINES) $(INCLUDES) -c beta.cpp -o $(OBJDIR)/beta.o + $(LD) $(CXXFLAGS) $(call wildcard,$(OBJDIR)/beta.o) $(call wildcard,$(OBJDIR)/arm_functions.o) $(call wildcard,$(FX__OBJS)) -o $@ $(LIBS) + clean: rm -rf *.o $(OBJDIR) $(EXE) $(OUTPUT_FOLDER) diff --git a/src/test/beta.cpp b/src/test/beta.cpp new file mode 100644 index 0000000..8e20cb9 --- /dev/null +++ b/src/test/beta.cpp @@ -0,0 +1,55 @@ +#include "../mixing_console_constants.h" +#include "../mixing_console.hpp" + +#include + +using namespace std; + +typedef MixingConsole<8> Mixer; + +int main() +{ + Mixer* mixer = new Mixer(44100.0f, 4); + // DUMP2(mixer, cout, "Post creation"); + + mixer->reset(); + DUMP2(mixer, cout, "Post creation"); + + mixer->setChannelLevel(0, 1.0f); + // DUMP2(mixer, cout, "Post setChannelLevel"); + + mixer->setPan(0, 0.5f); + // DUMP2(mixer, cout, "Post setPan"); + + float32_t samples[] = {0.0f, 0.0f, 0.0f, 0.0f}; + mixer->setInputSampleBuffer(0, samples); + // DUMP2(mixer, cout, "Post setInputSampleBuffer"); + + mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f); + // DUMP2(mixer, cout, "Post setSendLevel - full dry"); + + float32_t outL[4]; + float32_t outR[4]; + mixer->process(outL, outR); + DUMP2(mixer, cout, "Post process"); + cout.precision(5); + cout << std::fixed; + cout << "+ outL: " << outL[0] << " - " << outL[1] << endl; + cout << "+ outR: " << outR[0] << " - " << outR[1] << endl; + + mixer->setSendLevel(0, MixerOutput::FX_Tube, 1.0f); + mixer->setSendLevel(0, MixerOutput::FX_Delay, 1.0f); + mixer->setSendLevel(0, MixerOutput::FX_PlateReverb, 1.0f); + + mixer->setReturnLevel(MixerOutput::FX_Tube, MixerOutput::FX_Orbitone, 1.0f); + mixer->setReturnLevel(MixerOutput::FX_Orbitone, MixerOutput::MainOutput, 0.5f); + mixer->setReturnLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_PlateReverb, 1.0f); + mixer->setReturnLevel(MixerOutput::FX_Delay, MixerOutput::MainOutput, 0.5f); + mixer->setReturnLevel(MixerOutput::FX_PlateReverb, MixerOutput::MainOutput, 0.5f); + // DUMP2(mixer, cout, "Post setSendLevel & setReturnLevel"); + + + delete mixer; + + return 0; +} \ No newline at end of file diff --git a/src/test/test_fx_rack.cpp b/src/test/test_fx_rack.cpp index 9f5cc22..71b0099 100644 --- a/src/test/test_fx_rack.cpp +++ b/src/test/test_fx_rack.cpp @@ -45,8 +45,8 @@ void setupRack(FXRack* rack) rack->getDelay()->setFlutterRate(0.15f); rack->getDelay()->setFlutterAmount(0.75f); - rack->getShimmerReverb()->setWetLevel(0.5f); - rack->getShimmerReverb()->setInputGain(0.35f); + rack->getShimmerReverb()->setWetLevel(0.6f); + rack->getShimmerReverb()->setInputGain(0.55f); rack->getShimmerReverb()->setTime(0.89f); rack->getShimmerReverb()->setDiffusion(0.75f); rack->getShimmerReverb()->setLP(0.8f); diff --git a/src/test/test_mixing_console.cpp b/src/test/test_mixing_console.cpp index 3e771af..bb42d0c 100644 --- a/src/test/test_mixing_console.cpp +++ b/src/test/test_mixing_console.cpp @@ -44,13 +44,13 @@ TEST(MixerOutputTest, toStringForDelay) TEST(MixerOutputTest, toStringForPlateReverb) { auto v = toString(MixerOutput::FX_PlateReverb); - EXPECT_EQ(v, "PlateReverb"); + EXPECT_EQ(v, "Plate Reverb"); } TEST(MixerOutputTest, toStringForShimmerReverb) { auto v = toString(MixerOutput::FX_ShimmerReverb); - EXPECT_EQ(v, "ShimmerReverb"); + EXPECT_EQ(v, "Shimmer Reverb"); } TEST(MixerOutputTest, toIndexTube) @@ -136,7 +136,7 @@ void setupMixingConsoleFX(Mixer* mixer) mixer->getPlateReverb()->diffusion(0.65f); mixer->getPlateReverb()->level(1.0f); - mixer->getShimmerReverb()->setInputGain(0.35f); + mixer->getShimmerReverb()->setInputGain(0.65f); mixer->getShimmerReverb()->setTime(0.89f); mixer->getShimmerReverb()->setDiffusion(0.75f); mixer->getShimmerReverb()->setLP(0.8f); @@ -148,6 +148,8 @@ TEST(MixingConsole, DryProcessing) constexpr size_t length = 2; Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, length); + mixer->reset(); + mixer->setSendLevel(0, MixerOutput::FX_Tube, 0.0f); mixer->setSendLevel(0, MixerOutput::FX_Chorus, 0.0f); mixer->setSendLevel(0, MixerOutput::FX_Flanger, 0.0f); @@ -162,54 +164,77 @@ TEST(MixingConsole, DryProcessing) mixer->setReturnLevel(static_cast(i), MixerOutput::MainOutput, 0.0f); } - mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f); + mixer->setChannelLevel(0, 1.0f); mixer->setPan(0, 0.5f); + mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f); + DUMP2(mixer, std::cout, "Post setup"); float32_t in[length] = {0.1, 0.2}; float32_t out[StereoChannels::kNumChannels][length]; for(size_t i = 0; i < StereoChannels::kNumChannels; ++i) memset(out[i], 0, length * sizeof(float32_t)); - mixer->dump(std::cout); - mixer->setInputSampleBuffer(0, in); - mixer->dump(std::cout); + DUMP2(mixer, std::cout, "Post input injection"); + mixer->process( out[StereoChannels::Left ], out[StereoChannels::Right] ); - mixer->dump(std::cout); + DUMP2(mixer, std::cout, "Post processing"); EXPECT_EQ(out[StereoChannels::Left ][0], out[StereoChannels::Right][0]); EXPECT_EQ(out[StereoChannels::Left ][1], out[StereoChannels::Right][1]); EXPECT_LT(std::abs(out[StereoChannels::Left ][0] - (sqrt(2.0f) / 20.0f)), epsilon); EXPECT_LT(std::abs(out[StereoChannels::Left ][1] - (sqrt(2.0f) / 10.0f)), epsilon); + delete mixer; +} + +TEST(MixingConsole, ShimmerProcessing) +{ + constexpr float32_t epsilon = 1e-7; + constexpr size_t length = 2; + + Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, length); + mixer->reset(); + mixer->setSendLevel(0, MixerOutput::MainOutput, 0.0f); mixer->setSendLevel(0, MixerOutput::FX_ShimmerReverb, 1.0f); mixer->setReturnLevel(MixerOutput::FX_ShimmerReverb, MixerOutput::MainOutput, 1.0f); + mixer->setChannelLevel(0, 1.0f); mixer->setPan(0, 0.5f); - mixer->dump(std::cout); + DUMP2(mixer, std::cout, "Post setup"); + + float32_t in[length] = {0.1, 0.2}; + float32_t out1[StereoChannels::kNumChannels][length]; + for(size_t i = 0; i < StereoChannels::kNumChannels; ++i) memset(out1[i], 0, length * sizeof(float32_t)); mixer->setInputSampleBuffer(0, in); - mixer->dump(std::cout); + DUMP2(mixer, std::cout, "Post input injection #1"); + mixer->process( - out[StereoChannels::Left ], - out[StereoChannels::Right] + out1[StereoChannels::Left ], + out1[StereoChannels::Right] ); - mixer->dump(std::cout); + DUMP2(mixer, std::cout, "Post processing #1"); - float32_t out2[StereoChannels::kNumChannels][length]; mixer->reset(); - mixer->dump(std::cout); + DUMP2(mixer, std::cout, "Post reset"); + + float32_t out2[StereoChannels::kNumChannels][length]; mixer->setInputSampleBuffer(0, in); - mixer->dump(std::cout); + DUMP2(mixer, std::cout, "Post input injection #2"); + mixer->process( out2[StereoChannels::Left ], out2[StereoChannels::Right] ); - mixer->dump(std::cout); - EXPECT_EQ(out[StereoChannels::Left ][0], out2[StereoChannels::Left ][0]); - EXPECT_EQ(out[StereoChannels::Left ][1], out2[StereoChannels::Left ][1]); + DUMP2(mixer, std::cout, "Post processing #2"); + + EXPECT_EQ(out1[StereoChannels::Left ][0], out2[StereoChannels::Left ][0]); + EXPECT_EQ(out1[StereoChannels::Right][0], out2[StereoChannels::Right][0]); + EXPECT_EQ(out1[StereoChannels::Left ][1], out2[StereoChannels::Left ][1]); + EXPECT_EQ(out1[StereoChannels::Right][1], out2[StereoChannels::Right][1]); delete mixer; }