From 82c5f500fc162073a09f79237b4389d07f163438 Mon Sep 17 00:00:00 2001 From: abscisys Date: Sun, 12 Feb 2023 18:47:00 +0100 Subject: [PATCH] Fixing FastLFO and tests suite for it --- src/fx_components.cpp | 51 +- src/test/Makefile | 4 +- src/test/beta.cpp | 501 +++++++++++++------- src/test/beta_lowlevel.cpp | 440 ----------------- src/test/test_cpp_performance.cpp | 89 ++++ src/test/test_fx_components.cpp | 4 +- src/test/test_fx_mixing_console_unitary.cpp | 182 +++++++ 7 files changed, 644 insertions(+), 627 deletions(-) delete mode 100644 src/test/beta_lowlevel.cpp create mode 100644 src/test/test_fx_mixing_console_unitary.cpp diff --git a/src/fx_components.cpp b/src/fx_components.cpp index 869c75b..e2ec3b4 100644 --- a/src/fx_components.cpp +++ b/src/fx_components.cpp @@ -92,27 +92,28 @@ float32_t FastLFO::getFrequency() const void FastLFO::updateCoefficient() { - float32_t frequency = this->unitary_frequency_ * 268.0f / 240.0f; - - float32_t sign = 16.0f; - frequency -= 0.25f; - if(frequency < 0.0f) - { - frequency = -frequency; - } - else - { - if(frequency > 0.5f) - { - frequency -= 0.5f; - } - else - { - sign = -16.0f; - } - } - - this->iir_coefficient_ = sign * frequency * (1.0f - 2.0f * frequency); + // float32_t frequency = this->unitary_frequency_ * 1.00669889338314f; + + // float32_t sign = 16.0f; + // frequency -= 0.25f; + // if(frequency < 0.0f) + // { + // frequency = -frequency; + // } + // else + // { + // if(frequency > 0.5f) + // { + // frequency -= 0.5f; + // } + // else + // { + // sign = -16.0f; + // } + // } + + // this->iir_coefficient_ = sign * frequency * (1.0f - 2.0f * frequency); + this->iir_coefficient_ = 2.0f * cos(Constants::M2PI * this->unitary_frequency_); this->initial_amplitude_ = this->iir_coefficient_ * 0.25f; this->reset(); @@ -120,6 +121,8 @@ void FastLFO::updateCoefficient() void FastLFO::reset() { + static const float32_t epsilon = 1e-3; + this->sub_increment_ = 0.0f; // computing cos(0) = sin(-PI/2) @@ -134,13 +137,15 @@ void FastLFO::reset() float32_t p_i = Constants::M2PI * this->unitary_frequency_ / static_cast(this->nb_sub_increment_); float32_t p = Constants::MPI_2; float32_t t_p = this->InitialPhase; + const float32_t target = sin(this->InitialPhase); if(t_p < p) { p -= Constants::M2PI; } - while(p < t_p) + float32_t tuning = -3.0f; + while(p < t_p || abs(tuning - target) > epsilon) { - this->process(); + tuning = this->process(); p += p_i; } } diff --git a/src/test/Makefile b/src/test/Makefile index 25347ae..c7648e7 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -88,8 +88,8 @@ $(OBJDIR)/%.o: ../%.cpp $(OBJDIR) $(EXE): $(BINDIR) $(TST_OBJS) $(FX__OBJS) $(LD) $(CXXFLAGS) $(call wildcard,$(TST_OBJS)) $(call wildcard,$(FX__OBJS)) -o $@ $(LIBS) -$(BETA): $(BINDIR) $(BETAOBJS) $(FX__OBJS) - $(LD) $(CXXFLAGS) $(BETAOBJS) $(call wildcard,$(FX__OBJS)) -o $@ $(LIBS) +$(BETA): $(BINDIR) $(BETAOBJS) + $(LD) $(CXXFLAGS) $(BETAOBJS) -o $@ $(LIBS) clean: rm -rf $(OBJDIR) $(BINDIR) $(OUTPUT_FOLDER) diff --git a/src/test/beta.cpp b/src/test/beta.cpp index 44bee1d..3adea7e 100644 --- a/src/test/beta.cpp +++ b/src/test/beta.cpp @@ -1,205 +1,386 @@ #include "test_fx_helper.h" -TEST(BetaTest, WavefileSamplesBoundariesTest) +#include +#include +#include + +class FastLFODebugger { - 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(); +public: + FastLFODebugger(float32_t sampling_rate, float32_t min_frequency, float32_t max_frequency, float32_t initial_phase, bool centered) : + SamplingFrequency(sampling_rate), + InitialPhase(initial_phase), + min_frequency_(min_frequency), + max_frequency_(max_frequency), + centered_(centered), + frequency_(0.0f), + nb_sub_increment_(1), + sub_increment_(0), + y0_(0.0f), + y1_(0.0f), + iir_coefficient_(0.0f), + initial_amplitude_(0.0f) + { + assert(this->min_frequency_ <= this->max_frequency_); + assert(this->max_frequency_ < sampling_rate / 2.0f); - size_t size; - float32_t** samples = readWaveFile(AUDIO_SOURCE_FILE, size); + this->setFrequency(this->min_frequency_); + } - size_t nb_errors = 0; - for(size_t i = 0; i < size; ++i) + ~FastLFODebugger() { - nb_errors += fullInspector(full_test_name + ".rawWaveSampleTest", samples[0][i], -1.0f, 1.0f, true); - nb_errors += fullInspector(full_test_name + ".rawWaveSampleTest", samples[1][i], -1.0f, 1.0f, true); } - ASSERT_EQ(0, nb_errors) << full_test_name << ".rawWaveSampleTest"; - - delete[] samples[0]; - delete[] samples[1]; - delete[] samples; -} -typedef MixingConsole<1> Mixer; - -TEST(BetaTest, MixingConsoleShortBuffer) -{ - static const float32_t SINPI_4 = std::sqrt(2.0f) / 2.0f; + inline float32_t getSamplingRate() const + { + return this->SamplingFrequency; + } - 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(); + void setNormalizedFrequency(float32_t normalized_frequency) + { + normalized_frequency = constrain(normalized_frequency, 0.0f, 1.0f); + if(this->normalized_frequency_ != normalized_frequency) + { + float32_t frequency = mapfloat(normalized_frequency, 0.0f, 1.0f, this->min_frequency_, this->max_frequency_); + this->normalized_frequency_ = normalized_frequency; + this->frequency_ = frequency; + this->unitary_frequency_ = this->frequency_ / this->getSamplingRate(); + + this->nb_sub_increment_ = (frequency >= 3.0f ? 1 : 300); + this->unitary_frequency_ *= this->nb_sub_increment_; + + this->updateCoefficient(1.0f); + } + } - const size_t size = 10; - Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); + float32_t getNormalizedFrequency() const + { + return this->normalized_frequency_; + } - mixer->setChannelLevel(0, 1.0f); - mixer->setPan(0, 0.5f); + void setFrequency(float32_t frequency) + { + frequency = constrain(frequency, this->min_frequency_, this->max_frequency_); + if(this->frequency_ != frequency) + { + float32_t normalized_frequency = mapfloat(frequency, this->min_frequency_, this->max_frequency_, 0.0f, 1.0f); + this->normalized_frequency_ = normalized_frequency; + this->frequency_ = frequency; + this->unitary_frequency_ = this->frequency_ / this->getSamplingRate(); + + this->nb_sub_increment_ = (frequency >= 3.0f ? 1 : 300); + this->unitary_frequency_ *= this->nb_sub_increment_; + + this->updateCoefficient(1.0f); + } + } - mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f); + float32_t getFrequency() const + { + return this->frequency_; + } - float32_t inSamples[size]; - for(size_t s = 0; s < size; ++s) inSamples[s] = getRandomValue(); - float32_t outSamples[2][size]; - memset(outSamples[0], 0, size * sizeof(float32_t)); - memset(outSamples[1], 0, size * sizeof(float32_t)); + void updateCoefficient(float32_t correction_ratio) + { + float32_t frequency = this->unitary_frequency_ * correction_ratio; + + float32_t sign = 16.0f; + frequency -= 0.25f; + if(frequency < 0.0f) + { + frequency = -frequency; + } + else + { + if(frequency > 0.5f) + { + frequency -= 0.5f; + } + else + { + sign = -16.0f; + } + } + + this->iir_coefficient_ = sign * frequency * (1.0f - 2.0f * frequency); + this->initial_amplitude_ = this->iir_coefficient_ * 0.25f; + + this->reset(correction_ratio); + } - mixer->setInputSampleBuffer(0, inSamples); - ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.setInputSampleBuffer"; + void reset(float32_t correction_ratio = 1.0f) + { + // static const float32_t epsilon = 1e-7; + + this->sub_increment_ = 0.0f; + + // computing cos(0) = sin(-PI/2) + this->y1_ = this->initial_amplitude_; + this->y0_ = 0.5f; + + // if(this->unitary_frequency_ == 0.0f) + // { + // return; + // } + + // float32_t p_i = 2.0f * PI * this->unitary_frequency_ * correction_ratio / static_cast(this->nb_sub_increment_); + // float32_t p = PI / 2.0f; + // float32_t t_p = this->InitialPhase; + // if(t_p < p) + // { + // p -= 2.0f* PI; + // } + // float32_t current = 3.0f; + // while(abs(current, sin(this->InitialPhase)) > epsilon) + // { + // std::cout << "phase: " << p << std::endl; + // this->process(); + // p += p_i; + // if(p > (6.0f * PI)) + // cout << "ERROR: FLO is not precise enough" << + // return; + // } + } - mixer->process(outSamples[0], outSamples[1]); - ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.process"; - for(size_t s = 0; s < size; ++s) + float32_t process() { - EXPECT_FLOAT_EQ(outSamples[0][s], outSamples[1][s]); - EXPECT_FLOAT_EQ(outSamples[0][s], inSamples[s] * SINPI_4); + float32_t temp = this->y0_; + float32_t current = temp + 0.5f; + if(this->centered_) + { + current = current * 2.0f - 1.0f; + } + + if(this->sub_increment_ == 0) + { + this->y0_ = this->iir_coefficient_ * this->y0_ - this->y1_; + this->y1_ = temp; + this->current_ = current; + return current; + } + + this->sub_increment_++; + if(this->sub_increment_ >= this->nb_sub_increment_) + { + this->sub_increment_ = 0; + } + + return mapfloat(this->sub_increment_, 0, this->nb_sub_increment_, this->current_, current); } - delete mixer; -} + float32_t current() const + { + return this->current_; + } -TEST(BetaTest, MixingConsoleReverberatorShortBuffer) +private: + const float32_t SamplingFrequency; + const float32_t InitialPhase; + const float32_t min_frequency_; + const float32_t max_frequency_; + const bool centered_; + float32_t frequency_; + float32_t normalized_frequency_; + float32_t unitary_frequency_; + size_t nb_sub_increment_; + size_t sub_increment_; + + float32_t y0_; + float32_t y1_; + float32_t iir_coefficient_; + float32_t initial_amplitude_; + float32_t current_; +}; + +void updateCorrectionStep(float32_t& sign, float32_t& correction_ratio_step, int direction) { - 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 = 10; - Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); - - mixer->setChannelLevel(0, 1.0f); - mixer->setPan(0, 0.5f); - - mixer->getReverberator()->setInputGain(0.35f); - mixer->getReverberator()->setTime(0.69f); - mixer->getReverberator()->setDiffusion(0.7f); - mixer->getReverberator()->setLP(0.8f); - - mixer->setSendLevel(0, MixerOutput::MainOutput, 0.4f); - mixer->setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f); - mixer->setReturnLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.6f); - - float32_t inSamples[size]; - for(size_t s = 0; s < size; ++s) inSamples[s] = getRandomValue(); - float32_t outSamples[2][size]; - memset(outSamples[0], 0, size * sizeof(float32_t)); - memset(outSamples[1], 0, size * sizeof(float32_t)); - - mixer->setInputSampleBuffer(0, inSamples); - ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.setInputSampleBuffer"; - - mixer->process(outSamples[0], outSamples[1]); - ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.process"; + if(sign == direction) + { + // does nothing + } + else if(sign == -direction) + { + sign = static_cast(direction); + correction_ratio_step /= 2.0f; + } + else + { + sign = static_cast(direction); + } - delete mixer; + if(direction > 0) + { + std::cout << "LFO is too slow - correction ratio step becomes: " << (sign * correction_ratio_step); + } + else if(direction < 0) + { + std::cout << "LFO is too fast - correction ratio step becomes: " << (sign * correction_ratio_step); + } } -TEST(BetaTest, MixingConsoleDrySamplesBoundariesTest) +TEST(BetaTesta, FastLFO) { 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); - - Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); - mixer->reset(); - FULL_INSPECT2(mixer, true, "Mixer.reset"); - - mixer->setChannelLevel(0, 1.0f); - mixer->setPan(0, 0.5f); - mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f); - - mixer->setInputSampleBuffer(0, inSamples[0]); - - float32_t** outSamples = new float32_t*[2]; - outSamples[0] = new float32_t[size]; - outSamples[1] = new float32_t[size]; - memset(outSamples[0], 0, size * sizeof(float32_t)); - memset(outSamples[1], 0, size * sizeof(float32_t)); - - mixer->process(outSamples[0], outSamples[1]); - - size_t nb_errors = 0; - for(size_t i = 0; i < size; ++i) + + size_t NB = static_cast(1.0f * SAMPLING_FREQUENCY); + const float32_t freq = 1.5f; + const float32_t init_phase = PI / 2.0f; + float32_t correction_ratio = 1.0f; + float32_t correction_ratio_step = 8.0f/ SAMPLING_FREQUENCY; + + FastLFODebugger lfo1(SAMPLING_FREQUENCY, freq, 440.0f, init_phase, true); + lfo1.setFrequency(freq); + + const float32_t epsilon = 1e-3; + + int nbTrials = 100000; + float32_t maxDiff; + float32_t sign = 0.0f; + float32_t phase; + float32_t phase_increment; + size_t maxOK = 0; + float32_t best_correction = correction_ratio; + while(nbTrials > 0) { - nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[0][i], -1.0f, 1.0f, true); - nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[1][i], -1.0f, 1.0f, true); + maxDiff = 0.0f; + phase = init_phase; + correction_ratio += sign * correction_ratio_step; + std::cout << std::setprecision(9) << std::fixed << " - Testing correction_ratio: " << correction_ratio << std::endl; + lfo1.updateCoefficient(correction_ratio); + phase_increment = freq / SAMPLING_FREQUENCY; + + for(size_t i = 0; i < NB; ++i) + { + float32_t v1 = lfo1.process(); + float32_t v2 = sin(phase); + // std::cout << std::setprecision(9) << std::fixed << " + phase: " << phase << " // v1: " << v1 << " / v2: " << v2 << " => diff: " << (v2 - v1); + + float32_t diff = abs(v1 - v2); + if(diff > maxDiff) maxDiff = diff; + + // std::cout << " - OK: " << ((diff < epsilon) ? "Yes" : "No") << std::endl; + + if(diff > epsilon) + { + if(maxOK < i) + { + maxOK = i + 1; + best_correction = correction_ratio; + } + + int quater = 0; + if(phase > (PI / 2.0f)) ++quater; + if(phase > PI) ++quater; + if(phase > (3.0f * PI / 2.0f)) ++quater; + + if(v1 < v2) + { + switch (quater) + { + case 0: + case 4: + // Sinus phase [0, PI / 2] => [0.00, 1.00] + // Sinus phase [3 * PI / 2, 2 * PI] => [-1.00, 0.00] + // lfo1 is too slow + updateCorrectionStep(sign, correction_ratio_step, +1); + break; + + case 1: + case 3: + // Sinus phase [PI / 2, PI] => [1.00, 0.00] + // Sinus phase [PI, 3 * PI / 2] => [0.00, -1.00] + // lfo1 is too fast + updateCorrectionStep(sign, correction_ratio_step, -1); + break; + + default: + FAIL() << "Issue on phase: " << phase; + break; + } + break; + } + else + { + switch (quater) + { + case 0: + case 4: + // Sinus phase [0, PI / 2] => [0.00, 1.00] + // Sinus phase [3 * PI / 2, 2 * PI] => [-1.00, 0.00] + // lfo1 is too fast + updateCorrectionStep(sign, correction_ratio_step, -1); + break; + + case 1: + case 3: + // Sinus phase [PI / 2, PI] => [1.00, 0.00] + // Sinus phase [PI, 3 * PI / 2] => [0.00, -1.00] + // lfo1 is too slow + updateCorrectionStep(sign, correction_ratio_step, +1); + break; + + default: + FAIL() << "Issue on phase: " << phase; + break; + } + break; + } + } + + if(correction_ratio_step < 1e-9) FAIL() << "correction_ratio_step became too small. maxOK = " << maxOK << " with best_correction = " << best_correction; + + phase += phase_increment; + if(phase > 2.0f * PI) phase -= 2.0f * PI; + } + + --nbTrials; } - ASSERT_EQ(0, nb_errors) << full_test_name << ".outputSampleTest"; + if(nbTrials > -2) + std::cout << "Correct correction ratio = " << correction_ratio << " with maxDiff = " << maxDiff << std::endl; + else + std::cout << "No matching correction ratio" << std::endl; - delete[] inSamples[0]; - delete[] inSamples[1]; - delete[] inSamples; + std::cout << "maxOK = " << maxOK << " with best_correction = " << best_correction << std::endl;; - delete[] outSamples[0]; - delete[] outSamples[1]; - delete[] outSamples; - delete mixer; -} + // std::stringstream ssFst; + // std::stringstream ssSin; -TEST(BetaTest, MixingConsoleReverberatorSamplesBoundariesTest) -{ - 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); + // for(size_t i = 0; i < NB; ++i) + // { + // ssFst << lfo1.process() << (i == (NB - 1) ? "" : ", "); + // ssSin << sin(2.0f * PI * freq * i / SAMPLING_FREQUENCY + init_phase) << (i == (NB - 1) ? "" : ", "); + // } - float32_t** outSamples = new float32_t*[2]; - outSamples[0] = new float32_t[size]; - outSamples[1] = new float32_t[size]; - memset(outSamples[0], 0, size * sizeof(float32_t)); - memset(outSamples[1], 0, size * sizeof(float32_t)); + // std::ofstream _fst(getResultFile(full_test_name + ".fst.data", true)); + // _fst << ssFst.str(); + // _fst.close(); - Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); - mixer->reset(); - mixer->setChannelLevel(0, 1.0f); - mixer->setPan(0, 0.5f); + // std::ofstream _sin(getResultFile(full_test_name + ".sin.data", true)); + // _sin << ssSin.str(); + // _sin.close(); - mixer->setSendLevel(0, MixerOutput::MainOutput, 0.4f); - mixer->setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f); - mixer->setReturnLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.6f); - mixer->getReverberator()->setMute(false); - mixer->getReverberator()->setInputGain(0.35); - mixer->getReverberator()->setTime(0.65); - mixer->getReverberator()->setDiffusion(0.8); - mixer->getReverberator()->setLP(0.7f); + // std::ofstream out(getResultFile(full_test_name + ".data.m", true)); + // out << "# m file to tune FastLFO component" << std::endl << std::endl; + // out << "# Parameters:" << std::endl + // << "# + frequency: " << freq << "Hz" << std::endl + // << "# + # samples: " << NB << std::endl << std::endl; - mixer->setInputSampleBuffer(0, inSamples[0]); - mixer->process(outSamples[0], outSamples[1]); - ASSERT_EQ(0, FULL_INSPECT2(mixer, true, full_test_name + "Mixer.process")) << full_test_name << " Mixer.process"; - - saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamples[0], outSamples[1], size, static_cast(SAMPLING_FREQUENCY), 16); - - size_t nb_errors = 0; - for(size_t i = 0; i < size; ++i) - { - nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[0][i], -1.0f, 1.0f, true); - nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[1][i], -1.0f, 1.0f, true); - } - ASSERT_EQ(0, nb_errors) << full_test_name << ".outputSampleTest"; + // out << "time = 0 : " << (NB - 1) << ";" << std::endl; + // out << "fst_lfo = load(\"-ascii\", \"" << full_test_name << ".fst.data\");" << std::endl; + // out << "sin_lfo = load(\"-ascii\", \"" << full_test_name << ".sin.data\");" << std::endl; - delete[] inSamples[0]; - delete[] inSamples[1]; - delete[] inSamples; + // out << std::endl << std::endl; - delete[] outSamples[0]; - delete[] outSamples[1]; - delete[] outSamples; + // out << "plot(time, fst_lfo, '-', time, sin_lfo, '-');" << std::endl; + // out << "title('LFO tuning');" << std::endl; + // out << "legend('FastLFODebugger', 'Sinus');" << std::endl; - delete mixer; + // out.close(); } int main(int argc, char **argv) diff --git a/src/test/beta_lowlevel.cpp b/src/test/beta_lowlevel.cpp deleted file mode 100644 index 3d444e4..0000000 --- a/src/test/beta_lowlevel.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include - -#include -#include - -#include "test_fx_helper.h" -#include "../fx_engine.hpp" - -#define PRINT_EXEC(ctx, x) \ - std::cout.fill(' '); \ - std::cout.width(80); \ - std::cout << std::left; \ - std::cout.precision(6); \ - std::cout << std::fixed; \ - std::cout << #x; \ - x \ - { \ - float32_t v = 0.0f; \ - ctx.write(v); \ - std::cout << " // accumulator_: " << showpos << v; \ - } \ - std::cout << std::endl - -#define TAIL , -1 - -typedef FxEngine<16384, Format::FORMAT_FLOAT32, true> Engine; - -void processDebugReverberatorSample( - Engine& engine_, size_t index, - float32_t& lp_decay_1_, float32_t& lp_decay_2_, - float32_t inL, float32_t inR, - float32_t& outL, float32_t& outR) -{ - // This is the Griesinger topology described in the Dattorro paper - // (4 AP diffusers on the input, then a loop of 2x 2AP+1Delay). - // Modulation is applied in the loop of the first diffuser AP for additional - // smearing; and to the two long delays for a slow shimmer/chorus effect. - typedef Engine::Reserve< 113, - Engine::Reserve< 162, - Engine::Reserve< 241, - Engine::Reserve< 399, - Engine::Reserve<1653, - Engine::Reserve<2038, - Engine::Reserve<3411, - Engine::Reserve<1913, - Engine::Reserve<1663, - Engine::Reserve<4782> > > > > > > > > > Memory; - Engine::DelayLine ap1; - Engine::DelayLine ap2; - Engine::DelayLine ap3; - Engine::DelayLine ap4; - Engine::DelayLine dap1a; - Engine::DelayLine dap1b; - Engine::DelayLine del1; - Engine::DelayLine dap2a; - Engine::DelayLine dap2b; - Engine::DelayLine del2; - Engine::Context c; - - const float32_t kap = 0.8f; - const float32_t klp = 0.7f; - const float32_t krt = 0.75f; - const float32_t gain = 0.55f; - - float32_t lp_1 = lp_decay_1_; - float32_t lp_2 = lp_decay_2_; - - float32_t wet = 0.0f; - float32_t apout = 0.0f; - engine_.start(&c); - - // Smear AP1 inside the loop. - PRINT_EXEC(c, c.interpolate(ap1, 10.0f, Engine::LFOIndex::LFO_1, 60.0f, 1.0f);); - PRINT_EXEC(c, c.write(ap1, 100, 0.0f);); - PRINT_EXEC(c, c.read(inL + inR, gain);); - - // Diffuse through 4 allpasses. - PRINT_EXEC(c, c.read(ap1 TAIL, kap);); - PRINT_EXEC(c, c.writeAllPass(ap1, -kap);); - PRINT_EXEC(c, c.read(ap2 TAIL, kap);); - PRINT_EXEC(c, c.writeAllPass(ap2, -kap);); - PRINT_EXEC(c, c.read(ap3 TAIL, kap);); - PRINT_EXEC(c, c.writeAllPass(ap3, -kap);); - PRINT_EXEC(c, c.read(ap4 TAIL, kap);); - PRINT_EXEC(c, c.writeAllPass(ap4, -kap);); - PRINT_EXEC(c, c.write(apout);); - - // Main reverb loop. - PRINT_EXEC(c, c.load(apout);); - PRINT_EXEC(c, c.interpolate(del2, 4680.0f, Engine::LFOIndex::LFO_2, 100.0f, krt);); - PRINT_EXEC(c, c.lp(lp_1, klp);); - PRINT_EXEC(c, c.read(dap1a TAIL, -kap);); - PRINT_EXEC(c, c.writeAllPass(dap1a, kap);); - PRINT_EXEC(c, c.read(dap1b TAIL, kap);); - PRINT_EXEC(c, c.writeAllPass(dap1b, -kap);); - PRINT_EXEC(c, c.write(del1, 1.5f);); - PRINT_EXEC(c, c.write(wet, 0.0f);); - - outL = wet; - - - PRINT_EXEC(c, c.load(apout);); - // PRINT_EXEC(c, c.interpolate(del1, 4450.0f, Engine::LFOIndex::LFO_1, 50.0f, krt);); - PRINT_EXEC(c, c.read(del1 TAIL, krt);); - PRINT_EXEC(c, c.lp(lp_2, klp);); - PRINT_EXEC(c, c.read(dap2a TAIL, kap);); - PRINT_EXEC(c, c.writeAllPass(dap2a, -kap);); - PRINT_EXEC(c, c.read(dap2b TAIL, -kap);); - PRINT_EXEC(c, c.writeAllPass(dap2b, kap);); - PRINT_EXEC(c, c.write(del2, 1.5f);); - PRINT_EXEC(c, c.write(wet, 0.0f);); - - outR = wet; - - - lp_decay_1_ = lp_1; - lp_decay_2_ = lp_2; - - std::cout << "Index # " << index << " - ( " << inL << ", " << inR << " ) ==> ( " << outL << ", " << outR << " )" << std::endl; - std::cout << std::endl << "***********************************************************************************************************" << std::endl << std::endl; -} - -TEST(LowLevel, TestDiracReverberatorAlgo) -{ - 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(); - - Engine engine_(SAMPLING_FREQUENCY); - engine_.setLFOFrequency(Engine::LFOIndex::LFO_1, 0.5f); - engine_.setLFOFrequency(Engine::LFOIndex::LFO_2, 0.3f); - engine_.reset(); - - float32_t lp1 = 0.0f; - float32_t lp2 = 0.0f; - - const size_t size = static_cast(SAMPLING_FREQUENCY) * 4; - 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)); - - for(size_t i = 0; i < size; ++i) - { - processDebugReverberatorSample(engine_, i, lp1, lp2, inSamples[i], inSamples[i], outSamplesL[i], outSamplesR[i]); - } - - saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16); - - delete[] outSamplesL; - delete[] outSamplesR; - - delete[] inSamples; -} - -void processReverberatorSample( - Engine& engine_L_, Engine& engine_R_, size_t index, - float32_t& lp_decay_1_, float32_t& lp_decay_2_, - float32_t inL, float32_t inR, - float32_t& outL, float32_t& outR) -{ - // This is the Griesinger topology described in the Dattorro paper - // (4 AP diffusers on the input, then a loop of 2x 2AP+1Delay). - // Modulation is applied in the loop of the first diffuser AP for additional - // smearing; and to the two long delays for a slow shimmer/chorus effect. - typedef Engine::Reserve< 113, - Engine::Reserve< 162, - Engine::Reserve< 241, - Engine::Reserve< 399, - Engine::Reserve<1653, - Engine::Reserve<2038, - Engine::Reserve<3411, - Engine::Reserve<1913, - Engine::Reserve<1663, - Engine::Reserve<4782> > > > > > > > > > Memory; - Engine::DelayLine ap1; - Engine::DelayLine ap2; - Engine::DelayLine ap3; - Engine::DelayLine ap4; - Engine::DelayLine dap1a; - Engine::DelayLine dap1b; - Engine::DelayLine del1; - Engine::DelayLine dap2a; - Engine::DelayLine dap2b; - Engine::DelayLine del2; - Engine::Context cL; - Engine::Context cR; - - const float32_t kap = 0.8f; - const float32_t klp = 0.7f; - const float32_t krt = 0.75f; - const float32_t gain = 0.55f; - - float32_t lp_1 = lp_decay_1_; - float32_t lp_2 = lp_decay_2_; - - float32_t wet = 0.0f; - float32_t apout = 0.0f; - engine_L_.start(&cL); - engine_R_.start(&cR); - - // Smear AP1 inside the loop. - cL.interpolate(ap1, 10.0f, Engine::LFOIndex::LFO_1, 60.0f, 1.0f); - cL.write(ap1, 100, 0.0f); - cL.read(inL, gain); - - // Diffuse through 4 allpasses. - cL.read(ap1 TAIL, kap); - cL.writeAllPass(ap1, -kap); - cL.read(ap2 TAIL, kap); - cL.writeAllPass(ap2, -kap); - cL.read(ap3 TAIL, kap); - cL.writeAllPass(ap3, -kap); - cL.read(ap4 TAIL, kap); - cL.writeAllPass(ap4, -kap); - cL.write(apout); - - // Main reverb loop. - cL.load(apout); - cL.interpolate(del2, 4680.0f, Engine::LFOIndex::LFO_2, 100.0f, krt); - cL.lp(lp_1, klp); - cL.read(dap1a TAIL, -kap); - cL.writeAllPass(dap1a, kap); - cL.read(dap1b TAIL, kap); - cL.writeAllPass(dap1b, -kap); - cL.write(del1, 1.5f); - cL.write(wet, 0.0f); - - outL = wet; - - - // Smear AP1 inside the loop. - cR.interpolate(ap1, 10.0f, Engine::LFOIndex::LFO_1, 60.0f, 1.0f); - cR.write(ap1, 100, 0.0f); - cR.read(inL + inR, gain); - - // Diffuse through 4 allpasses. - cR.read(ap1 TAIL, kap); - cR.writeAllPass(ap1, -kap); - cR.read(ap2 TAIL, kap); - cR.writeAllPass(ap2, -kap); - cR.read(ap3 TAIL, kap); - cR.writeAllPass(ap3, -kap); - cR.read(ap4 TAIL, kap); - cR.writeAllPass(ap4, -kap); - cR.write(apout); - - // Main reverb loop. - cR.load(apout); - // cR.interpolate(del1, 4450.0f, Engine::LFOIndex::LFO_1, 50.0f, krt); - cR.read(del1 TAIL, krt); - cR.lp(lp_2, klp); - cR.read(dap2a TAIL, kap); - cR.writeAllPass(dap2a, -kap); - cR.read(dap2b TAIL, -kap); - cR.writeAllPass(dap2b, kap); - cR.write(del2, 1.5f); - cR.write(wet, 0.0f); - - outR = wet; - - - lp_decay_1_ = lp_1; - lp_decay_2_ = lp_2; -} - -TEST(LowLevel, TestStereoReverberatorAlgo) -{ - 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(); - - Engine engine_L_(SAMPLING_FREQUENCY); - Engine engine_R_(SAMPLING_FREQUENCY); - engine_L_.setLFOFrequency(Engine::LFOIndex::LFO_1, 0.5f); - engine_L_.setLFOFrequency(Engine::LFOIndex::LFO_2, 0.3f); - engine_L_.reset(); - engine_R_.setLFOFrequency(Engine::LFOIndex::LFO_1, 0.5f); - engine_R_.setLFOFrequency(Engine::LFOIndex::LFO_2, 0.3f); - engine_R_.reset(); - - float32_t lp1 = 0.0f; - float32_t lp2 = 0.0f; - - size_t size = 0; - 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)); - - for(size_t i = 0; i < size; ++i) - { - processReverberatorSample(engine_L_, engine_R_, i, lp1, lp2, inSamples[0][i], inSamples[1][i], outSamplesL[i], outSamplesR[i]); - } - - saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16); - - delete[] outSamplesL; - delete[] outSamplesR; - - delete[] inSamples[0]; - delete[] inSamples[1]; - delete[] inSamples; -} - -void processReverberatorSample( - Engine& engine_, size_t index, - float32_t& lp_decay_1_, float32_t& lp_decay_2_, - float32_t inL, float32_t inR, - float32_t& outL, float32_t& outR) -{ - // This is the Griesinger topology described in the Dattorro paper - // (4 AP diffusers on the input, then a loop of 2x 2AP+1Delay). - // Modulation is applied in the loop of the first diffuser AP for additional - // smearing; and to the two long delays for a slow shimmer/chorus effect. - typedef Engine::Reserve< 113, - Engine::Reserve< 162, - Engine::Reserve< 241, - Engine::Reserve< 399, - Engine::Reserve<1653, - Engine::Reserve<2038, - Engine::Reserve<3411, - Engine::Reserve<1913, - Engine::Reserve<1663, - Engine::Reserve<4782> > > > > > > > > > Memory; - Engine::DelayLine ap1; - Engine::DelayLine ap2; - Engine::DelayLine ap3; - Engine::DelayLine ap4; - Engine::DelayLine dap1a; - Engine::DelayLine dap1b; - Engine::DelayLine del1; - Engine::DelayLine dap2a; - Engine::DelayLine dap2b; - Engine::DelayLine del2; - Engine::Context c; - - const float32_t kap = 0.8f; - const float32_t klp = 0.7f; - const float32_t krt = 0.75f; - const float32_t gain = 0.55f; - - float32_t lp_1 = lp_decay_1_; - float32_t lp_2 = lp_decay_2_; - - float32_t wet = 0.0f; - float32_t apout = 0.0f; - engine_.start(&c); - - // Smear AP1 inside the loop. - c.interpolate(ap1, 10.0f, Engine::LFOIndex::LFO_1, 60.0f, 1.0f); - c.writeAndLoad(ap1, 100, 0.0f); - c.read(inL + inR, gain); - - // Diffuse through 4 allpasses. - c.read(ap1 TAIL, kap); - c.writeAllPass(ap1, -kap); - c.read(ap2 TAIL, kap); - c.writeAllPass(ap2, -kap); - c.read(ap3 TAIL, kap); - c.writeAllPass(ap3, -kap); - c.read(ap4 TAIL, kap); - c.writeAllPass(ap4, -kap); - c.write(apout); - - // Main reverb loop. - c.load(apout); - c.interpolate(del2, 4680.0f, Engine::LFOIndex::LFO_2, 100.0f, krt); - c.lp(lp_1, klp); - c.read(dap1a TAIL, -kap); - c.writeAllPass(dap1a, kap); - c.read(dap1b TAIL, kap); - c.writeAllPass(dap1b, -kap); - c.write(del1, 2.0f); - c.writeAndLoad(wet, 0.0f); - - outL = wet; - - c.load(apout); - c.read(del1 TAIL, krt); - c.lp(lp_2, klp); - c.read(dap2a TAIL, kap); - c.writeAllPass(dap2a, -kap); - c.read(dap2b TAIL, -kap); - c.writeAllPass(dap2b, kap); - c.write(del2, 2.0f); - c.writeAndLoad(wet, 0.0f); - - outR = wet; - - - lp_decay_1_ = lp_1; - lp_decay_2_ = lp_2; -} - -TEST(LowLevel, TestMonoReverberatorAlgo) -{ - 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(); - - Engine engine_(SAMPLING_FREQUENCY); - engine_.setLFOFrequency(Engine::LFOIndex::LFO_1, 0.5f); - engine_.setLFOFrequency(Engine::LFOIndex::LFO_2, 0.3f); - engine_.reset(); - - float32_t lp1 = 0.0f; - float32_t lp2 = 0.0f; - - size_t size = 0; - 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)); - - for(size_t i = 0; i < size; ++i) - { - processReverberatorSample(engine_, i, lp1, lp2, inSamples[0][i], inSamples[1][i], outSamplesL[i], outSamplesR[i]); - } - - saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16); - - delete[] outSamplesL; - delete[] outSamplesR; - - delete[] inSamples[0]; - delete[] inSamples[1]; - delete[] inSamples; -} diff --git a/src/test/test_cpp_performance.cpp b/src/test/test_cpp_performance.cpp index ce60681..c42bfe0 100644 --- a/src/test/test_cpp_performance.cpp +++ b/src/test/test_cpp_performance.cpp @@ -59,6 +59,33 @@ TEST(CppPerformance, LFOPerformance_ComplexLFO_FastLFO) EXPECT_GE(d1, d2); } +TEST(CppPerformance, LFOPerformance_InterpolatedSineOscillator_FastLFO) +{ + const size_t NB = 10000000; + float32_t freq = 0.1f; + + InterpolatedSineOscillator lfo1(SAMPLING_FREQUENCY, 0.0f, 10.0f, Constants::MPI_2); + FastLFO lfo2(SAMPLING_FREQUENCY, 0.0f, 10.0f); + + lfo1.setFrequency(freq); + LAP_TIME("lfo1"); + for(size_t i = 0; i < NB; ++i) + { + lfo1.process(); + } + auto d1 = LAP_TIME("lfo1"); + + lfo2.setFrequency(freq); + LAP_TIME("lfo2"); + for(size_t i = 0; i < NB; ++i) + { + lfo2.process(); + } + auto d2 = LAP_TIME("lfo2"); + + EXPECT_GE(d1, d2); +} + TEST(CppPerformance, FastLFOTuning) { const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); @@ -91,3 +118,65 @@ TEST(CppPerformance, FastLFOTuning) } out.close(); } + +TEST(CppPerformance, FastLFOTuningOctave) +{ + 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 NB = static_cast(1.0f * SAMPLING_FREQUENCY); + float32_t freq = 1.5f; + + FastLFO lfo1(SAMPLING_FREQUENCY, freq, 440.0f); + lfo1.setFrequency(freq); + + InterpolatedSineOscillator lfo2(SAMPLING_FREQUENCY, freq, 440.0f); + lfo2.setFrequency(freq); + + ComplexLFO lfo3(SAMPLING_FREQUENCY, freq, 440.0f); + lfo3.setFrequency(freq); + + std::stringstream ssLFO1; + std::stringstream ssLFO2; + std::stringstream ssLFO3; + + for(size_t i = 0; i < NB; ++i) + { + ssLFO1 << lfo1.process() << (i == (NB - 1) ? "" : ", "); + ssLFO2 << lfo2.process() << (i == (NB - 1) ? "" : ", "); + ssLFO3 << lfo3.process() << (i == (NB - 1) ? "" : ", "); + } + + std::ofstream _lfo1(getResultFile(full_test_name + ".fast.data", true)); + _lfo1 << ssLFO1.str(); + _lfo1.close(); + + std::ofstream _lfo2(getResultFile(full_test_name + ".intr.data", true)); + _lfo2 << ssLFO2.str(); + _lfo2.close(); + + std::ofstream _lfo3(getResultFile(full_test_name + ".cplx.data", true)); + _lfo3 << ssLFO3.str(); + _lfo3.close(); + + std::ofstream out(getResultFile(full_test_name + ".data.m", true)); + out << "# m file to tune FastLFO component" << std::endl << std::endl; + out << "# Parameters:" << std::endl + << "# + frequency: " << freq << "Hz" << std::endl + << "# + # samples: " << NB << std::endl << std::endl; + + out << "time = 0 : " << (NB - 1) << ";" << std::endl; + out << "fast_lfo = load(\"-ascii\", \"" << full_test_name << ".fast.data\");" << std::endl; + out << "intr_lfo = load(\"-ascii\", \"" << full_test_name << ".intr.data\");" << std::endl; + out << "cplx_lfo = load(\"-ascii\", \"" << full_test_name << ".cplx.data\");" << std::endl; + + out << std::endl << std::endl; + + out << "plot(time, fast_lfo, '-', time, intr_lfo, '-', cplx_lfo, '-');" << std::endl; + out << "title('LFO tuning');" << std::endl; + out << "legend('FastLFO', 'InterpolatedSineOscillator', 'ComplexLFO');" << std::endl; + + out.close(); +} diff --git a/src/test/test_fx_components.cpp b/src/test/test_fx_components.cpp index 7084da8..e5be074 100644 --- a/src/test/test_fx_components.cpp +++ b/src/test/test_fx_components.cpp @@ -133,8 +133,8 @@ TEST(CppOptimization, FastLFOPrecisionTest) const float32_t epsilon = 1e-3; - ComplexLFO lfo1(SAMPLING_FREQUENCY, 0.0f, 10.0f); - FastLFO lfo2(SAMPLING_FREQUENCY, 0.0f, 10.0f); + ComplexLFO lfo1(SAMPLING_FREQUENCY, 0.0f, 10.0f, 0.0f, true); + FastLFO lfo2(SAMPLING_FREQUENCY, 0.0f, 10.0f, 0.0f, true); lfo1.setFrequency(freq); lfo2.setFrequency(freq); float32_t max_delta = 0.0f; diff --git a/src/test/test_fx_mixing_console_unitary.cpp b/src/test/test_fx_mixing_console_unitary.cpp new file mode 100644 index 0000000..b5b570d --- /dev/null +++ b/src/test/test_fx_mixing_console_unitary.cpp @@ -0,0 +1,182 @@ +#include "test_fx_helper.h" + +#include "../mixing_console.hpp" + +typedef MixingConsole<1> Mixer; + +TEST(MixingConsole, ShortBuffer) +{ + static const float32_t SINPI_4 = std::sqrt(2.0f) / 2.0f; + + 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 = 10; + Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); + + mixer->setChannelLevel(0, 1.0f); + mixer->setPan(0, 0.5f); + + mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f); + + float32_t inSamples[size]; + for(size_t s = 0; s < size; ++s) inSamples[s] = getRandomValue(); + float32_t outSamples[2][size]; + memset(outSamples[0], 0, size * sizeof(float32_t)); + memset(outSamples[1], 0, size * sizeof(float32_t)); + + mixer->setInputSampleBuffer(0, inSamples); + ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.setInputSampleBuffer"; + + mixer->process(outSamples[0], outSamples[1]); + ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.process"; + for(size_t s = 0; s < size; ++s) + { + EXPECT_FLOAT_EQ(outSamples[0][s], outSamples[1][s]); + EXPECT_FLOAT_EQ(outSamples[0][s], inSamples[s] * SINPI_4); + } + + delete mixer; +} + +TEST(MixingConsole, ReverberatorShortBuffer) +{ + 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 = 10; + Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); + + mixer->setChannelLevel(0, 1.0f); + mixer->setPan(0, 0.5f); + + mixer->getReverberator()->setInputGain(0.35f); + mixer->getReverberator()->setTime(0.69f); + mixer->getReverberator()->setDiffusion(0.7f); + mixer->getReverberator()->setLP(0.8f); + + mixer->setSendLevel(0, MixerOutput::MainOutput, 0.4f); + mixer->setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f); + mixer->setReturnLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.6f); + + float32_t inSamples[size]; + for(size_t s = 0; s < size; ++s) inSamples[s] = getRandomValue(); + float32_t outSamples[2][size]; + memset(outSamples[0], 0, size * sizeof(float32_t)); + memset(outSamples[1], 0, size * sizeof(float32_t)); + + mixer->setInputSampleBuffer(0, inSamples); + ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.setInputSampleBuffer"; + + mixer->process(outSamples[0], outSamples[1]); + ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.process"; + + delete mixer; +} + +TEST(MixingConsole, DrySamplesBoundariesTest) +{ + 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); + + Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); + mixer->reset(); + FULL_INSPECT2(mixer, true, "Mixer.reset"); + + mixer->setChannelLevel(0, 1.0f); + mixer->setPan(0, 0.5f); + mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f); + + mixer->setInputSampleBuffer(0, inSamples[0]); + + float32_t** outSamples = new float32_t*[2]; + outSamples[0] = new float32_t[size]; + outSamples[1] = new float32_t[size]; + memset(outSamples[0], 0, size * sizeof(float32_t)); + memset(outSamples[1], 0, size * sizeof(float32_t)); + + mixer->process(outSamples[0], outSamples[1]); + + size_t nb_errors = 0; + for(size_t i = 0; i < size; ++i) + { + nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[0][i], -1.0f, 1.0f, true); + nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[1][i], -1.0f, 1.0f, true); + } + ASSERT_EQ(0, nb_errors) << full_test_name << ".outputSampleTest"; + + delete[] inSamples[0]; + delete[] inSamples[1]; + delete[] inSamples; + + delete[] outSamples[0]; + delete[] outSamples[1]; + delete[] outSamples; + + delete mixer; +} + +TEST(MixingConsole, ReverberatorSamplesBoundariesTest) +{ + 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** outSamples = new float32_t*[2]; + outSamples[0] = new float32_t[size]; + outSamples[1] = new float32_t[size]; + memset(outSamples[0], 0, size * sizeof(float32_t)); + memset(outSamples[1], 0, size * sizeof(float32_t)); + + Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); + mixer->reset(); + mixer->setChannelLevel(0, 1.0f); + mixer->setPan(0, 0.5f); + + mixer->setSendLevel(0, MixerOutput::MainOutput, 0.4f); + mixer->setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f); + mixer->setReturnLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.6f); + + mixer->getReverberator()->setMute(false); + mixer->getReverberator()->setInputGain(0.35); + mixer->getReverberator()->setTime(0.65); + mixer->getReverberator()->setDiffusion(0.8); + mixer->getReverberator()->setLP(0.7f); + + mixer->setInputSampleBuffer(0, inSamples[0]); + mixer->process(outSamples[0], outSamples[1]); + ASSERT_EQ(0, FULL_INSPECT2(mixer, true, full_test_name + "Mixer.process")) << full_test_name << " Mixer.process"; + + saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamples[0], outSamples[1], size, static_cast(SAMPLING_FREQUENCY), 16); + + size_t nb_errors = 0; + for(size_t i = 0; i < size; ++i) + { + nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[0][i], -1.0f, 1.0f, true); + nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[1][i], -1.0f, 1.0f, true); + } + ASSERT_EQ(0, nb_errors) << full_test_name << ".outputSampleTest"; + + delete[] inSamples[0]; + delete[] inSamples[1]; + delete[] inSamples; + + delete[] outSamples[0]; + delete[] outSamples[1]; + delete[] outSamples; + + delete mixer; +}