First steps on debugging of MixingConsole

pull/495/head
abscisys 2 years ago
parent 1d483dd4f7
commit 36546af851
  1. 2
      src/extra_features.h
  2. 7
      src/fx_components.cpp
  3. 3
      src/fx_engine.hpp
  4. 21
      src/fx_tube.cpp
  5. 3
      src/fx_tube.h
  6. 793
      src/mixing_console.hpp
  7. 11
      src/test/Makefile
  8. 55
      src/test/beta.cpp
  9. 4
      src/test/test_fx_rack.cpp
  10. 63
      src/test/test_mixing_console.cpp

@ -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)

@ -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;
}

@ -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;

@ -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_);
}
}

@ -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_;
};

@ -33,471 +33,578 @@
#include "fx_dry.h"
#include "fx_unit2.hpp"
template<size_t nb_inputs = 8>
template<size_t nb_inputs>
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<Tube>(sampling_rate);
this->fx_[MixerOutput::FX_Chorus] = this->chorus_ = new FXUnit2<Chorus>(sampling_rate);
this->fx_[MixerOutput::FX_Flanger] = this->flanger_ = new FXUnit2<Flanger>(sampling_rate);
this->fx_[MixerOutput::FX_Orbitone] = this->orbitone_ = new FXUnit2<Orbitone>(sampling_rate);
this->fx_[MixerOutput::FX_Phaser] = this->phaser_ = new FXUnit2<Phaser>(sampling_rate);
this->fx_[MixerOutput::FX_Delay] = this->delay_ = new FXUnit2<Delay>(sampling_rate);
this->fx_[MixerOutput::FX_PlateReverb] = this->plate_reverb_ = new FXUnit2<AudioEffectPlateReverb>(sampling_rate);
this->fx_[MixerOutput::FX_ShimmerReverb] = this->shimmer_reverb_ = new FXUnit2<ShimmerReverb>(sampling_rate);
this->fx_[MixerOutput::MainOutput] = this->dry_ = new FXUnit2<Dry>(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<Tube>* getTube();
FXUnit2<Chorus>* getChorus();
FXUnit2<Flanger>* getFlanger();
FXUnit2<Orbitone>* getOrbitone();
FXUnit2<Phaser>* getPhaser();
FXUnit2<Delay>* getDelay();
FXUnit2<AudioEffectPlateReverb>* getPlateReverb();
FXUnit2<ShimmerReverb>* getShimmerReverb();
FXUnit2<Dry>* 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>* tube_;
FXUnit2<Chorus>* chorus_;
FXUnit2<Flanger>* flanger_;
FXUnit2<Orbitone>* orbitone_;
FXUnit2<Phaser>* phaser_;
FXUnit2<Delay>* delay_;
FXUnit2<AudioEffectPlateReverb>* plate_reverb_;
FXUnit2<ShimmerReverb>* shimmer_reverb_;
FXUnit2<Dry>* 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<size_t nb_inputs>
MixingConsole<nb_inputs>::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<Tube>(sampling_rate);
this->fx_[MixerOutput::FX_Chorus] = this->chorus_ = new FXUnit2<Chorus>(sampling_rate);
this->fx_[MixerOutput::FX_Flanger] = this->flanger_ = new FXUnit2<Flanger>(sampling_rate);
this->fx_[MixerOutput::FX_Orbitone] = this->orbitone_ = new FXUnit2<Orbitone>(sampling_rate);
this->fx_[MixerOutput::FX_Phaser] = this->phaser_ = new FXUnit2<Phaser>(sampling_rate);
this->fx_[MixerOutput::FX_Delay] = this->delay_ = new FXUnit2<Delay>(sampling_rate);
this->fx_[MixerOutput::FX_PlateReverb] = this->plate_reverb_ = new FXUnit2<AudioEffectPlateReverb>(sampling_rate);
this->fx_[MixerOutput::FX_ShimmerReverb] = this->shimmer_reverb_ = new FXUnit2<ShimmerReverb>(sampling_rate);
this->fx_[MixerOutput::MainOutput] = this->dry_ = new FXUnit2<Dry>(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<size_t nb_inputs>
MixingConsole<nb_inputs>::~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<size_t nb_inputs>
void MixingConsole<nb_inputs>::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<size_t nb_inputs>
void MixingConsole<nb_inputs>::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<size_t nb_inputs>
void MixingConsole<nb_inputs>::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<size_t nb_inputs>
void MixingConsole<nb_inputs>::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<size_t nb_inputs>
void MixingConsole<nb_inputs>::setInputSampleBuffer(size_t in, float32_t* samples)
{
assert(in < nb_inputs);
FXUnit2<Tube>* 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<Chorus>* 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<Flanger>* getFlanger()
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::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<Orbitone>* getOrbitone()
else
{
return this->orbitone_;
memset(this->input_sample_buffer_[StereoChannels::Left ][in], 0, this->BufferSize * sizeof(float32_t));
}
FXUnit2<Phaser>* getPhaser()
if(samplesR != nullptr)
{
return this->phaser_;
memcpy(this->input_sample_buffer_[StereoChannels::Right][in], samplesR, this->BufferSize * sizeof(float32_t));
}
FXUnit2<Delay>* getDelay()
else
{
return this->delay_;
memset(this->input_sample_buffer_[StereoChannels::Right][in], 0, this->BufferSize * sizeof(float32_t));
}
}
FXUnit2<AudioEffectPlateReverb>* getPlateReverb()
{
return this->plate_reverb_;
}
// Return section
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::setReturnLevel(MixerOutput ret, MixerOutput dest, float32_t lvl)
{
assert(ret < (kFXCount - 1));
assert(dest < kFXCount);
FXUnit2<ShimmerReverb>* getShimmerReverb()
if(ret == dest)
{
return this->shimmer_reverb_;
// An FX cannot feedback on itself
return;
}
FXUnit2<Dry>* getDry()
{
return this->dry_;
}
this->setLevel(nb_inputs + ret, dest, lvl);
}
// Processing
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::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<size_t nb_inputs>
FXElement* MixingConsole<nb_inputs>::getFX(size_t fx)
{
assert(fx < MixerOutput::kFXCount);
return this->fx_[fx];
}
this->reset();
}
template<size_t nb_inputs>
FXUnit2<Tube>* MixingConsole<nb_inputs>::getTube()
{
return this->tube_;
}
template<size_t nb_inputs>
FXUnit2<Chorus>* MixingConsole<nb_inputs>::getChorus()
{
return this->chorus_;
}
template<size_t nb_inputs>
FXUnit2<Flanger>* MixingConsole<nb_inputs>::getFlanger()
{
return this->flanger_;
}
template<size_t nb_inputs>
FXUnit2<Orbitone>* MixingConsole<nb_inputs>::getOrbitone()
{
return this->orbitone_;
}
template<size_t nb_inputs>
FXUnit2<Phaser>* MixingConsole<nb_inputs>::getPhaser()
{
return this->phaser_;
}
void reset()
template<size_t nb_inputs>
FXUnit2<Delay>* MixingConsole<nb_inputs>::getDelay()
{
return this->delay_;
}
template<size_t nb_inputs>
FXUnit2<AudioEffectPlateReverb>* MixingConsole<nb_inputs>::getPlateReverb()
{
return this->plate_reverb_;
}
template<size_t nb_inputs>
FXUnit2<ShimmerReverb>* MixingConsole<nb_inputs>::getShimmerReverb()
{
return this->shimmer_reverb_;
}
template<size_t nb_inputs>
FXUnit2<Dry>* MixingConsole<nb_inputs>::getDry()
{
return this->dry_;
}
// Processing
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::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<size_t nb_inputs>
void MixingConsole<nb_inputs>::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<MixerOutput>(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<size_t nb_inputs>
void MixingConsole<nb_inputs>::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<MixerOutput>(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<MixerOutput>(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<size_t nb_inputs>
void MixingConsole<nb_inputs>::prepare()
{
for(size_t i = 0; i < MixerOutput::kFXCount; ++i)
{
this->fx_[i]->prepare();
}
}
void prepare()
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::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<size_t nb_inputs>
void MixingConsole<nb_inputs>::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<size_t nb_inputs>
void MixingConsole<nb_inputs>::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<size_t nb_inputs>
void MixingConsole<nb_inputs>::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<size_t nb_inputs>
void MixingConsole<nb_inputs>::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>* tube_;
FXUnit2<Chorus>* chorus_;
FXUnit2<Flanger>* flanger_;
FXUnit2<Orbitone>* orbitone_;
FXUnit2<Phaser>* phaser_;
FXUnit2<Delay>* delay_;
FXUnit2<AudioEffectPlateReverb>* plate_reverb_;
FXUnit2<ShimmerReverb>* shimmer_reverb_;
FXUnit2<Dry>* 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<MixerOutput>(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<MixerOutput>(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<MixerOutput>(i)) << " ";
std::string s = toString(static_cast<MixerOutput>(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<MixerOutput>(c));
ss << ": ";
ss.precision(4);
ss << std::showpos;
ss << std::fixed;
SS_RESET(ss, precision, std::left);
std::string s = toString(static_cast<MixerOutput>(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
};

@ -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)

@ -0,0 +1,55 @@
#include "../mixing_console_constants.h"
#include "../mixing_console.hpp"
#include <iostream>
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;
}

@ -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);

@ -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<MixerOutput>(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;
}

Loading…
Cancel
Save