Fix the silent bug but have a significant sounding fault on RPI

pull/495/head
Vincent GAUCHE 2 years ago
parent 9395d7b5c8
commit 17e6a74b52
  1. 34
      src/CFileDevice.hpp
  2. 1
      src/extra_features.h
  3. 1
      src/fx.h
  4. 20
      src/fx_base.h
  5. 1
      src/fx_chorus.h
  6. 1
      src/fx_components.h
  7. 1
      src/fx_delay.h
  8. 1
      src/fx_diffuser.h
  9. 5
      src/fx_dry.h
  10. 21
      src/fx_engine.hpp
  11. 1
      src/fx_flanger.h
  12. 1
      src/fx_orbitone.h
  13. 4
      src/fx_phaser.cpp
  14. 1
      src/fx_phaser.h
  15. 1
      src/fx_pitch_shifter.h
  16. 1
      src/fx_rack.h
  17. 1
      src/fx_reverberator.h
  18. 20
      src/fx_shimmer_helper.h
  19. 1
      src/fx_shimmer_reverb.h
  20. 1
      src/fx_svf.h
  21. 1
      src/fx_tube.h
  22. 1
      src/fx_unit.hpp
  23. 1
      src/fx_unit2.hpp
  24. 3
      src/kernel.cpp
  25. 31
      src/minidexed.cpp
  26. 225
      src/mixing_console.hpp
  27. 13
      src/test/arm_functions.cpp
  28. 19
      src/test/test_fx_helper.h
  29. 78
      src/test/test_fx_mixing_console.cpp
  30. 8
      src/test/test_fx_mixing_console_unitary.cpp
  31. 19
      src/test/wave.h

@ -1,3 +1,24 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// CFileDevice.h
//
// Device implementation that writes into a file.
// This device is a singleton dedicated to allow the Circle CLogger class to output log event into the file instead of the internal buffer.
// Author: Vincent Gauché
//
#pragma once #pragma once
#include "extra_features.h" #include "extra_features.h"
@ -14,8 +35,8 @@ class CFileDevice : public CDevice
DISALLOW_COPY_AND_ASSIGN(CFileDevice); DISALLOW_COPY_AND_ASSIGN(CFileDevice);
private: private:
CFileDevice() CFileDevice() :
: m_file(new FIL()) m_file(new FIL())
{ {
FRESULT res = f_open(this->m_file, "SD:/debuglog.txt", FA_WRITE | FA_CREATE_ALWAYS); FRESULT res = f_open(this->m_file, "SD:/debuglog.txt", FA_WRITE | FA_CREATE_ALWAYS);
assert(res == FR_OK); assert(res == FR_OK);
@ -42,9 +63,12 @@ public:
virtual int Write(const void* pBuffer, size_t nCount) override virtual int Write(const void* pBuffer, size_t nCount) override
{ {
UINT nb; UINT nb = 0;
f_write(this->m_file, pBuffer, nCount, &nb); if(nCount > 0)
f_sync(this->m_file); {
f_write(this->m_file, pBuffer, nCount, &nb);
f_sync(this->m_file);
}
return (int)nb; return (int)nb;
} }

@ -15,6 +15,7 @@
// extra_features.h // extra_features.h
// //
// Header file that centralizes MACROS to enable / disable extra features // Header file that centralizes MACROS to enable / disable extra features
// Author: Vincent Gauché
// //
#pragma once #pragma once

@ -15,6 +15,7 @@
// fx.h // fx.h
// //
// Base classes for Stereo audio effects proposed in the context of the MiniDexed project // Base classes for Stereo audio effects proposed in the context of the MiniDexed project
// Author: Vincent Gauché
// //
#pragma once #pragma once

@ -1,3 +1,23 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// fx_base.h
//
// Base header file for the FX section of the MiniDexed project
// Author: Vincent Gauché
//
#pragma once #pragma once
#include "extra_features.h" #include "extra_features.h"

@ -16,6 +16,7 @@
// //
// Stereo Chorus audio effects proposed in the context of the MiniDexed project // Stereo Chorus audio effects proposed in the context of the MiniDexed project
// This implemelntation is based on the Chorus FX from the Rings Eurorack module from Mutable Instruments // This implemelntation is based on the Chorus FX from the Rings Eurorack module from Mutable Instruments
// Ported by: Vincent Gauché
// //
#pragma once #pragma once

@ -15,6 +15,7 @@
// fx_components.h // fx_components.h
// //
// Several tools and components used in the implemlentation of FX // Several tools and components used in the implemlentation of FX
// Quthor: Vincent Gauché
// //
#pragma once #pragma once

@ -16,6 +16,7 @@
// fx_tape_delay.h // fx_tape_delay.h
// //
// Stereo Delay proposed in the context of the MiniDexed project // Stereo Delay proposed in the context of the MiniDexed project
// Author: Vincent Gauché
// //
#pragma once #pragma once

@ -17,6 +17,7 @@
// //
// Stereo Diffuser proposed in the context of the MiniDexed project // Stereo Diffuser proposed in the context of the MiniDexed project
// It is adapted from the Diffuser that could be found on Cloud EuroRack module from Mutable Instrruments // It is adapted from the Diffuser that could be found on Cloud EuroRack module from Mutable Instrruments
// Ported by: Vincent Gauché
// //
#pragma once #pragma once

@ -12,9 +12,10 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
// fx_tube.h // fx_dry.h
// //
// Stereo Tube overdrive audio effects proposed in the context of the MiniDexed project // An FX that does nothing but used to generalize the processing.
// Author: Vincent Gauché
// //
#pragma once #pragma once

@ -1,3 +1,24 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// fx_engine.h
//
// FX Engine used in some of the Mutable Instruments Eurorack modules.
// This version is ported for the MiniDexed project and brings some additional optimization to avoid unecessary multiplications.
// Ported by: Vincent Gauché
//
#pragma once #pragma once
#include <algorithm> #include <algorithm>

@ -15,6 +15,7 @@
// fx_flanger.h // fx_flanger.h
// //
// Stereo Flanger audio effects proposed in the context of the MiniDexed project // Stereo Flanger audio effects proposed in the context of the MiniDexed project
// Author: Vincent Gauché
// //
#pragma once #pragma once

@ -16,6 +16,7 @@
// //
// Stereo Orbitone audio effects proposed in the context of the MiniDexed project // Stereo Orbitone audio effects proposed in the context of the MiniDexed project
// This audio effect is based on the Ensemble audio effect of the Rings Eurorack module by Mutable Instruments // This audio effect is based on the Ensemble audio effect of the Rings Eurorack module by Mutable Instruments
// Ported by: Vincent Gauché
// //
#pragma once #pragma once

@ -72,8 +72,8 @@ void Phaser::reset()
void Phaser::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) void Phaser::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{ {
float32_t dL = this->dmin_ + (this->dmax_ - this->dmin_) * ((1.0f + this->lfo_[StereoChannels::Left ]->process()) / 2.0f); float32_t dL = this->dmin_ + (this->dmax_ - this->dmin_) * this->lfo_[StereoChannels::Left ]->process();
float32_t dR = this->dmin_ + (this->dmax_ - this->dmin_) * ((1.0f + this->lfo_[StereoChannels::Right]->process()) / 2.0f); float32_t dR = this->dmin_ + (this->dmax_ - this->dmin_) * this->lfo_[StereoChannels::Right]->process();
float32_t sampleL = inL + this->feedback_ * this->z_[StereoChannels::Left ]; float32_t sampleL = inL + this->feedback_ * this->z_[StereoChannels::Left ];
float32_t sampleR = inR + this->feedback_ * this->z_[StereoChannels::Right]; float32_t sampleR = inR + this->feedback_ * this->z_[StereoChannels::Right];

@ -15,6 +15,7 @@
// fx_phaser.h // fx_phaser.h
// //
// Stereo Phaser audio effects proposed in the context of the MiniDexed project // Stereo Phaser audio effects proposed in the context of the MiniDexed project
// Author: Vincent Gauché
// //
#pragma once #pragma once

@ -17,6 +17,7 @@
// //
// Stereo Pitch Shifter proposed in the context of the MiniDexed project // Stereo Pitch Shifter proposed in the context of the MiniDexed project
// It is adapted from the Pitch Shifter that could be found on Cloud EuroRack module from Mutable Instrruments // It is adapted from the Pitch Shifter that could be found on Cloud EuroRack module from Mutable Instrruments
// Ported by: Vincent Gauché
// //
#pragma once #pragma once

@ -15,6 +15,7 @@
// fx_rack.h // fx_rack.h
// //
// Rack of audio effects proposed in the context of the MiniDexed project // Rack of audio effects proposed in the context of the MiniDexed project
// Author: Vincent Gauché
// //
#pragma once #pragma once

@ -17,6 +17,7 @@
// //
// Stereo Reverberator proposed in the context of the MiniDexed project // Stereo Reverberator proposed in the context of the MiniDexed project
// It is adapted from the Reverb that could be found on Cloud EuroRack module from Mutable Instrruments // It is adapted from the Reverb that could be found on Cloud EuroRack module from Mutable Instrruments
// Ported by: Vincent Gauché
// //
#pragma once #pragma once

@ -1,3 +1,23 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// fx_shimmer_helper.h
//
// Helper class for the FX Shimmer FX that is ported from Mutable Instruments
// Ported by: Vincent Gauché
//
#pragma once #pragma once
#include "fx.h" #include "fx.h"

@ -17,6 +17,7 @@
// //
// Stereo ShimmerReverb Reverb proposed in the context of the MiniDexed project // Stereo ShimmerReverb Reverb proposed in the context of the MiniDexed project
// It is adapted from the ShimmerReverb Reverb that could be found on Cloud EuroRack module from Mutable Instrruments // It is adapted from the ShimmerReverb Reverb that could be found on Cloud EuroRack module from Mutable Instrruments
// Ported by: Vincent Gauché
// //
#pragma once #pragma once

@ -16,6 +16,7 @@
// fx_svf.h // fx_svf.h
// //
// State Variable Filter used in Tape Delay // State Variable Filter used in Tape Delay
// Author: Vincent Gauché
// //
#pragma once #pragma once

@ -15,6 +15,7 @@
// fx_tube.h // fx_tube.h
// //
// Stereo Tube overdrive audio effects proposed in the context of the MiniDexed project // Stereo Tube overdrive audio effects proposed in the context of the MiniDexed project
// Author: Vincent Gauché
// //
#pragma once #pragma once

@ -15,6 +15,7 @@
// fx_unit.h // fx_unit.h
// //
// Unit of FX that handle enable and wet parameters // Unit of FX that handle enable and wet parameters
// Author: Vincent Gauché
// //
#pragma once #pragma once

@ -15,6 +15,7 @@
// fx_unit2.h // fx_unit2.h
// //
// Unit of FX that handle the mute parameter // Unit of FX that handle the mute parameter
// Author: Vincent Gauché
// //
#pragma once #pragma once

@ -22,8 +22,6 @@
#include <circle/synchronize.h> #include <circle/synchronize.h>
#include <assert.h> #include <assert.h>
#include "CFileDevice.hpp"
LOGMODULE ("kernel"); LOGMODULE ("kernel");
CKernel *CKernel::s_pThis = 0; CKernel *CKernel::s_pThis = 0;
@ -52,7 +50,6 @@ bool CKernel::Initialize (void)
return FALSE; return FALSE;
} }
CFileDevice::UseMeForLogger();
mLogger.RegisterPanicHandler (PanicHandler); mLogger.RegisterPanicHandler (PanicHandler);
if (!m_GPIOManager.Initialize ()) if (!m_GPIOManager.Initialize ())

@ -152,7 +152,8 @@ CMiniDexed::CMiniDexed (
this->mixing_console_ = new Mixer(static_cast<float32_t>(pConfig->GetSampleRate()), CConfig::MaxChunkSize); this->mixing_console_ = new Mixer(static_cast<float32_t>(pConfig->GetSampleRate()), CConfig::MaxChunkSize);
for (uint8_t i = 0; i < CConfig::ToneGenerators; i++) for (uint8_t i = 0; i < CConfig::ToneGenerators; i++)
{ {
this->mixing_console_->setInputSampleBuffer(i, m_OutputLevel[i]); memset(this->m_OutputLevel[i], 0, CConfig::MaxChunkSize * sizeof(float32_t));
this->mixing_console_->setInputSampleBuffer(i, this->m_OutputLevel[i]);
} }
// Tube parameters // Tube parameters
@ -277,10 +278,12 @@ bool CMiniDexed::Initialize (void)
#if defined(MIXING_CONSOLE_ENABLE) #if defined(MIXING_CONSOLE_ENABLE)
// setup the mixer so that it remains identical to the initial version of the synth // setup the mixer so that it remains identical to the initial version of the synth
this->mixing_console_->reset();
this->mixing_console_->setPan(i, this->m_nPan[i] / 127.0f); this->mixing_console_->setPan(i, this->m_nPan[i] / 127.0f);
float32_t sendRev = this->m_nFXSendLevel[i][MixerOutput::FX_PlateReverb] / 99.0f; float32_t sendRev = this->m_nFXSendLevel[i][MixerOutput::FX_PlateReverb] / 99.0f;
this->mixing_console_->setSendLevel(i, MixerOutput::FX_PlateReverb, sendRev); this->mixing_console_->setSendLevel(i, MixerOutput::FX_PlateReverb, 1.0f);
this->mixing_console_->setSendLevel(i, MixerOutput::MainOutput, 1.0f - sendRev); this->mixing_console_->setSendLevel(i, MixerOutput::MainOutput, 1.0f - sendRev);
this->mixing_console_->setReturnLevel(MixerOutput::FX_PlateReverb, MixerOutput::MainOutput, sendRev);
#elif defined(PLATE_REVERB_ENABLE) #elif defined(PLATE_REVERB_ENABLE)
@ -541,6 +544,10 @@ void CMiniDexed::SetVolume (unsigned nVolume, unsigned nTG)
assert (m_pTG[nTG]); assert (m_pTG[nTG]);
m_pTG[nTG]->setGain (nVolume / 127.0f); m_pTG[nTG]->setGain (nVolume / 127.0f);
#if defined(MIXING_CONSOLE_ENABLE)
this->mixing_console_->setChannelLevel(nTG, nVolume == 0 ? 0.0f : 1.0f);
#endif
m_UI.ParameterChanged (); m_UI.ParameterChanged ();
} }
@ -1780,6 +1787,8 @@ void CMiniDexed::ProcessSound (void)
// //
// Audio signal path after tone generators starts here // Audio signal path after tone generators starts here
// //
float32_t tmp_float[nFrames * 2];
int16_t tmp_int[nFrames * 2];
#if defined(MIXING_CONSOLE_ENABLE) #if defined(MIXING_CONSOLE_ENABLE)
// // swap stereo channels if needed // // swap stereo channels if needed
@ -1792,17 +1801,12 @@ void CMiniDexed::ProcessSound (void)
} }
// BEGIN TG mixing // BEGIN TG mixing
float32_t tmp_float[nFrames * 2];
int16_t tmp_int[nFrames * 2];
float32_t SampleBuffer[StereoChannels::kNumChannels][nFrames];
if(this->nMasterVolume > 0.0f) if(this->nMasterVolume > 0.0f)
{ {
this->m_FXSpinLock.Acquire (); float32_t SampleBuffer[StereoChannels::kNumChannels][nFrames];
this->m_FXSpinLock.Acquire();
this->mixing_console_->process(SampleBuffer[indexL], SampleBuffer[indexR]); this->mixing_console_->process(SampleBuffer[indexL], SampleBuffer[indexR]);
this->m_FXSpinLock.Release (); this->m_FXSpinLock.Release();
// Convert dual float array (left, right) to single int16 array (left/right) // Convert dual float array (left, right) to single int16 array (left/right)
this->nMasterVolume = constrain(this->nMasterVolume, 0.0f, 1.0f); this->nMasterVolume = constrain(this->nMasterVolume, 0.0f, 1.0f);
@ -1818,17 +1822,16 @@ void CMiniDexed::ProcessSound (void)
} }
arm_float_to_q15(tmp_float, tmp_int, nFrames * 2); arm_float_to_q15(tmp_float, tmp_int, nFrames * 2);
} }
else else // this->nMasterVolume == 0.0f
{
arm_fill_q15(0, tmp_int, nFrames * 2); arm_fill_q15(0, tmp_int, nFrames * 2);
}
#elif defined(PLATE_REVERB_ENABLE) #elif defined(PLATE_REVERB_ENABLE)
uint8_t indexL=0, indexR=1; uint8_t indexL=0, indexR=1;
// BEGIN TG mixing // BEGIN TG mixing
float32_t tmp_float[nFrames*2];
int16_t tmp_int[nFrames*2];
if(nMasterVolume > 0.0f) if(nMasterVolume > 0.0f)
{ {
for (uint8_t i = 0; i < CConfig::ToneGenerators; i++) for (uint8_t i = 0; i < CConfig::ToneGenerators; i++)

@ -2,6 +2,7 @@
// mixing_console.hpp // mixing_console.hpp
// //
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi // MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
// Author: Vincent Gauché
// Copyright (C) 2022 The MiniDexed Team // Copyright (C) 2022 The MiniDexed Team
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
@ -43,12 +44,11 @@ public:
~MixingConsole(); ~MixingConsole();
// Send section // Send section
inline void setChannelLevel(size_t in, float32_t lvl);
inline void setPan(size_t in, float32_t pan); inline void setPan(size_t in, float32_t pan);
inline void setSendLevel(size_t in, MixerOutput fx, float32_t lvl); inline void setSendLevel(size_t in, MixerOutput fx, float32_t lvl);
inline void setInputSample(size_t in, float32_t sampleL, float32_t sampleR); inline void setInputSample(size_t in, float32_t sampleL, float32_t sampleR);
inline void setInputSampleBuffer(size_t in, float32_t* samples); inline void setInputSampleBuffer(size_t in, float32_t* samples);
inline void copyInputSampleBuffer(size_t in, float32_t* samplesL, float32_t* samplesR);
inline void preProcessInputSampleBuffer(size_t in, size_t nSamples);
// Return section // Return section
inline void setReturnLevel(MixerOutput ret, MixerOutput dest, float32_t lvl); inline void setReturnLevel(MixerOutput ret, MixerOutput dest, float32_t lvl);
@ -69,16 +69,22 @@ public:
// Processing // Processing
inline void init(); inline void init();
inline void reset() override; inline void reset() override;
inline void preProcessInputSampleBuffer(size_t in, size_t nSamples);
inline void injectInputSamples(size_t in, float32_t* samplesL, float32_t* samplesR, size_t nSamples);
inline void processSample(float32_t& outL, float32_t& outR); inline void processSample(float32_t& outL, float32_t& outR);
void process(float32_t* outL, float32_t* outR); void process(float32_t* outL, float32_t* outR);
protected: protected:
inline void updatePan(size_t in);
inline void setLevel(size_t in, MixerOutput fx, float32_t lvl); inline void setLevel(size_t in, MixerOutput fx, float32_t lvl);
inline void setSample(size_t in, float32_t sampleL, float32_t sampleR); inline void setSample(size_t in, float32_t sampleL, float32_t sampleR);
private: private:
static inline float32_t weighted_sum(const float32_t* data, const float32_t* weights, size_t size);
const size_t BufferSize; const size_t BufferSize;
float32_t channel_level_[nb_inputs];
float32_t pan_[StereoChannels::kNumChannels + 1][nb_inputs]; float32_t pan_[StereoChannels::kNumChannels + 1][nb_inputs];
float32_t* tg_input_sample_buffer_[nb_inputs]; float32_t* tg_input_sample_buffer_[nb_inputs];
float32_t* input_sample_buffer_[StereoChannels::kNumChannels][nb_inputs]; float32_t* input_sample_buffer_[StereoChannels::kNumChannels][nb_inputs];
@ -109,6 +115,7 @@ private:
{ {
SS_RESET(ss, precision, std::left); SS_RESET(ss, precision, std::left);
SS_SPACE(ss, ' ', space, 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 L");
SS__TEXT(ss, ' ', space, std::left, '|', "Pan R"); SS__TEXT(ss, ' ', space, std::left, '|', "Pan R");
SS__TEXT(ss, ' ', space, std::left, '|', "Pan"); SS__TEXT(ss, ' ', space, std::left, '|', "Pan");
@ -119,6 +126,7 @@ private:
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, '+'); SS_SPACE(ss, '-', space, std::left, '+');
SS_SPACE(ss, '-', space, std::left, '+');
out << "\t" << ss.str() << std::endl; out << "\t" << ss.str() << std::endl;
for(size_t i = 0; i < nb_inputs; ++i) for(size_t i = 0; i < nb_inputs; ++i)
@ -129,6 +137,7 @@ private:
SS_RESET(ss, precision, std::left); SS_RESET(ss, precision, std::left);
SS__TEXT(ss, ' ', space, std::left, '|', s.str()); 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::Left][i]);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->pan_[StereoChannels::Right][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]); SS__TEXT(ss, ' ', space - 1, std::right, " |", this->pan_[StereoChannels::kNumChannels][i]);
@ -256,6 +265,7 @@ private:
for(size_t i = 0; i < nb_inputs; ++i) for(size_t i = 0; i < nb_inputs; ++i)
{ {
nb_errors += inspector(tag + ".level[ input #" + std::to_string(i) + " ]" , this->channel_level_[i], -1.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".pan[ L ][ input #" + std::to_string(i) + " ]", this->pan_[StereoChannels::Left][i], -1.0f, 1.0f, deepInspection); nb_errors += inspector(tag + ".pan[ L ][ input #" + std::to_string(i) + " ]", this->pan_[StereoChannels::Left][i], -1.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".pan[ R ][ input #" + std::to_string(i) + " ]", this->pan_[StereoChannels::Right][i], -1.0f, 1.0f, deepInspection); nb_errors += inspector(tag + ".pan[ R ][ input #" + std::to_string(i) + " ]", this->pan_[StereoChannels::Right][i], -1.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".pan[ input #" + std::to_string(i) + " ]", this->pan_[StereoChannels::kNumChannels][i], -1.0f, 1.0f, deepInspection); nb_errors += inspector(tag + ".pan[ input #" + std::to_string(i) + " ]", this->pan_[StereoChannels::kNumChannels][i], -1.0f, 1.0f, deepInspection);
@ -285,7 +295,7 @@ private:
{ {
for(size_t i = 0; i < nb_inputs; ++i) for(size_t i = 0; i < nb_inputs; ++i)
{ {
for(size_t k = 0; k < this->BufferSize; ++k) for(size_t k = 0; k < this->m_nSamples; ++k)
{ {
nb_errors += inspector(tag + ".input_sample_buffer_[ L ][ " + std::to_string(i) + " ][ " + std::to_string(k) +" ] ", this->input_sample_buffer_[StereoChannels::Left ][i][k], -1.0f, 1.0f, deepInspection); nb_errors += inspector(tag + ".input_sample_buffer_[ L ][ " + std::to_string(i) + " ][ " + std::to_string(k) +" ] ", this->input_sample_buffer_[StereoChannels::Left ][i][k], -1.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".input_sample_buffer_[ R ][ " + std::to_string(i) + " ][ " + std::to_string(k) +" ] ", this->input_sample_buffer_[StereoChannels::Right][i][k], -1.0f, 1.0f, deepInspection); nb_errors += inspector(tag + ".input_sample_buffer_[ R ][ " + std::to_string(i) + " ][ " + std::to_string(k) +" ] ", this->input_sample_buffer_[StereoChannels::Right][i][k], -1.0f, 1.0f, deepInspection);
@ -307,6 +317,13 @@ private:
) )
}; };
template<size_t nb_inputs>
float32_t MixingConsole<nb_inputs>::weighted_sum(const float32_t* data, const float32_t* weights, size_t size)
{
float32_t res = arm_weighted_sum_f32(data, weights, size);
return std::isnan(res) ? 0.0f : res;
}
template<size_t nb_inputs> template<size_t nb_inputs>
MixingConsole<nb_inputs>::MixingConsole(float32_t sampling_rate, size_t buffer_size) : MixingConsole<nb_inputs>::MixingConsole(float32_t sampling_rate, size_t buffer_size) :
@ -348,11 +365,26 @@ MixingConsole<nb_inputs>::~MixingConsole()
{ {
delete[] this->input_sample_buffer_[StereoChannels::Left ][i]; delete[] this->input_sample_buffer_[StereoChannels::Left ][i];
delete[] this->input_sample_buffer_[StereoChannels::Right][i]; delete[] this->input_sample_buffer_[StereoChannels::Right][i];
// The tg_input_sample_buffer_ buffers are not freed as MixingConsole is not the creator
// They must be freed by the creator of the buffers
this->tg_input_sample_buffer_[i] = nullptr; this->tg_input_sample_buffer_[i] = nullptr;
} }
} }
// Send section // Send section
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::setChannelLevel(size_t in, float32_t lvl)
{
assert(in < nb_inputs);
lvl = constrain(lvl, 0.0f, 1.0f);
if(lvl == this->channel_level_[in]) return;
this->channel_level_[in] = lvl;
this->updatePan(in);
}
template<size_t nb_inputs> template<size_t nb_inputs>
void MixingConsole<nb_inputs>::setPan(size_t in, float32_t pan) void MixingConsole<nb_inputs>::setPan(size_t in, float32_t pan)
{ {
@ -363,10 +395,7 @@ void MixingConsole<nb_inputs>::setPan(size_t in, float32_t pan)
if(pan == this->pan_[StereoChannels::kNumChannels][in]) return; if(pan == this->pan_[StereoChannels::kNumChannels][in]) return;
this->pan_[StereoChannels::kNumChannels][in] = pan; this->pan_[StereoChannels::kNumChannels][in] = pan;
this->updatePan(in);
pan *= Constants::MPI_2;
this->pan_[StereoChannels::Left ][in] = InterpolatedSineOscillator::Cos(pan);
this->pan_[StereoChannels::Right][in] = InterpolatedSineOscillator::Sin(pan);
} }
template<size_t nb_inputs> template<size_t nb_inputs>
@ -394,67 +423,6 @@ void MixingConsole<nb_inputs>::setInputSampleBuffer(size_t in, float32_t* sample
this->tg_input_sample_buffer_[in] = samples; this->tg_input_sample_buffer_[in] = samples;
} }
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::copyInputSampleBuffer(size_t in, float32_t* samplesL, float32_t* samplesR)
{
// Only used to input stereo samples
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));
}
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>::preProcessInputSampleBuffer(size_t in, size_t nSamples)
{
assert(in < nb_inputs);
assert(nSamples <= this->BufferSize);
float32_t* samples = this->tg_input_sample_buffer_[in];
if(samples == nullptr) return;
this->m_nSamples = nSamples;
if(nSamples > 0)
{
if(this->pan_[StereoChannels::Left ][in] != 0.0f)
{
arm_scale_f32(samples, this->pan_[StereoChannels::Left ][in], this->input_sample_buffer_[StereoChannels::Left ][in], nSamples);
}
else
{
memset(this->input_sample_buffer_[StereoChannels::Left ][in], 0, nSamples * sizeof(float32_t));
}
if(this->pan_[StereoChannels::Right][in] != 0.0f)
{
arm_scale_f32(samples, this->pan_[StereoChannels::Right][in], this->input_sample_buffer_[StereoChannels::Right][in], nSamples);
}
else
{
memset(this->input_sample_buffer_[StereoChannels::Right][in], 0, nSamples * sizeof(float32_t));
}
}
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));
}
}
// Return section // Return section
template<size_t nb_inputs> template<size_t nb_inputs>
void MixingConsole<nb_inputs>::setReturnLevel(MixerOutput ret, MixerOutput dest, float32_t lvl) void MixingConsole<nb_inputs>::setReturnLevel(MixerOutput ret, MixerOutput dest, float32_t lvl)
@ -545,6 +513,7 @@ FXUnit2<Dry>* MixingConsole<nb_inputs>::getDry()
template<size_t nb_inputs> template<size_t nb_inputs>
void MixingConsole<nb_inputs>::init() 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 <= StereoChannels::kNumChannels; ++i) memset(this->pan_[i], 0, nb_inputs * sizeof(float32_t));
for(size_t i = 0; i < MixerOutput::kFXCount; ++i) for(size_t i = 0; i < MixerOutput::kFXCount; ++i)
@ -576,41 +545,105 @@ void MixingConsole<nb_inputs>::reset()
} }
} }
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::injectInputSamples(size_t in, float32_t* samplesL, float32_t* samplesR, size_t nSamples)
{
// Only used to input stereo samples
assert(in < nb_inputs);
this->m_nSamples = std::min(nSamples, this->BufferSize);
if(samplesL != nullptr)
{
memcpy(this->input_sample_buffer_[StereoChannels::Left ][in], samplesL, this->m_nSamples * sizeof(float32_t));
}
else
{
memset(this->input_sample_buffer_[StereoChannels::Left ][in], 0, this->m_nSamples * sizeof(float32_t));
}
if(samplesR != nullptr)
{
memcpy(this->input_sample_buffer_[StereoChannels::Right][in], samplesR, this->m_nSamples * sizeof(float32_t));
}
else
{
memset(this->input_sample_buffer_[StereoChannels::Right][in], 0, this->m_nSamples * sizeof(float32_t));
}
}
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::preProcessInputSampleBuffer(size_t in, size_t nSamples)
{
assert(in < nb_inputs);
assert(nSamples <= this->BufferSize);
float32_t* samples = this->tg_input_sample_buffer_[in];
if(samples == nullptr) return;
this->m_nSamples = nSamples;
if(nSamples > 0)
{
if(this->pan_[StereoChannels::Left ][in] != 0.0f)
{
arm_scale_f32(samples, this->pan_[StereoChannels::Left ][in], this->input_sample_buffer_[StereoChannels::Left ][in], nSamples);
}
else
{
memset(this->input_sample_buffer_[StereoChannels::Left ][in], 0, nSamples * sizeof(float32_t));
}
if(this->pan_[StereoChannels::Right][in] != 0.0f)
{
arm_scale_f32(samples, this->pan_[StereoChannels::Right][in], this->input_sample_buffer_[StereoChannels::Right][in], nSamples);
}
else
{
memset(this->input_sample_buffer_[StereoChannels::Right][in], 0, nSamples * sizeof(float32_t));
}
}
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));
}
}
template<size_t nb_inputs> template<size_t nb_inputs>
void MixingConsole<nb_inputs>::processSample(float32_t& outL, float32_t& outR) void MixingConsole<nb_inputs>::processSample(float32_t& outL, float32_t& outR)
{ {
const size_t nBuffers = nb_inputs + MixerOutput::kFXCount - 1; const size_t nBuffers = nb_inputs + MixerOutput::kFXCount - 1;
float32_t fx_inputs_[MixerOutput::kFXCount][StereoChannels::kNumChannels]; float32_t fx_input_[StereoChannels::kNumChannels];
float32_t fx_outputs_[MixerOutput::kFXCount][StereoChannels::kNumChannels]; float32_t fx_output_[StereoChannels::kNumChannels];
for(size_t i = 0; i < MixerOutput::kFXCount; ++i) for(size_t fxId = 0; fxId < MixerOutput::kFXCount; ++fxId)
{ {
// Compute the samples that will feed the MixerOutput and process MixerOutput // Compute the samples that will feed the FX
fx_inputs_[i][StereoChannels::Left ] = arm_weighted_sum_f32(this->input_samples_[StereoChannels::Left ], this->levels_[i], nBuffers); fx_input_[StereoChannels::Left ] = MixingConsole<nb_inputs>::weighted_sum(this->input_samples_[StereoChannels::Left ], this->levels_[fxId], nBuffers);
fx_inputs_[i][StereoChannels::Right] = arm_weighted_sum_f32(this->input_samples_[StereoChannels::Right], this->levels_[i], nBuffers); fx_input_[StereoChannels::Right] = MixingConsole<nb_inputs>::weighted_sum(this->input_samples_[StereoChannels::Right], this->levels_[fxId], nBuffers);
// Process the FX // Process the FX
this->fx_[i]->processSample( this->fx_[fxId]->processSample(
fx_inputs_[i][StereoChannels::Left], fx_input_[StereoChannels::Left ],
fx_inputs_[i][StereoChannels::Right], fx_input_[StereoChannels::Right],
fx_outputs_[i][StereoChannels::Left], fx_output_[StereoChannels::Left ],
fx_outputs_[i][StereoChannels::Right] fx_output_[StereoChannels::Right]
); );
if(i != MixerOutput::MainOutput) if(fxId != MixerOutput::MainOutput)
{ {
// Feedback the resulting samples except for the main output // Feedback the processed samples except for the main output
this->setReturnSample( this->setReturnSample(
static_cast<MixerOutput>(i), static_cast<MixerOutput>(fxId),
fx_outputs_[i][StereoChannels::Left], fx_output_[StereoChannels::Left],
fx_outputs_[i][StereoChannels::Right] fx_output_[StereoChannels::Right]
); );
} }
else
{
// Returns the main output sample
outL = fx_output_[StereoChannels::Left];
outR = fx_output_[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> template<size_t nb_inputs>
@ -636,6 +669,22 @@ void MixingConsole<nb_inputs>::process(float32_t* outL, float32_t* outR)
this->m_nSamples = 0; this->m_nSamples = 0;
} }
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::updatePan(size_t in)
{
float32_t pan = this->pan_[StereoChannels::kNumChannels][in] * Constants::MPI_2;
if(this->channel_level_[in] != 0.0f)
{
this->pan_[StereoChannels::Left ][in] = InterpolatedSineOscillator::Cos(pan) * this->channel_level_[in];
this->pan_[StereoChannels::Right][in] = InterpolatedSineOscillator::Sin(pan) * this->channel_level_[in];
}
else
{
this->pan_[StereoChannels::Left ][in] =
this->pan_[StereoChannels::Right][in] = 0.0f;
}
}
template<size_t nb_inputs> template<size_t nb_inputs>
void MixingConsole<nb_inputs>::setLevel(size_t in, MixerOutput fx, float32_t lvl) void MixingConsole<nb_inputs>::setLevel(size_t in, MixerOutput fx, float32_t lvl)
{ {

@ -35,9 +35,16 @@ void arm_fill_f32(float32_t value, float32_t *pDst, uint32_t blockSize)
float32_t arm_weighted_sum_f32(const float32_t *in, const float32_t *weights, uint32_t blockSize) float32_t arm_weighted_sum_f32(const float32_t *in, const float32_t *weights, uint32_t blockSize)
{ {
float32_t m = 0.0f; float32_t s = 0.0f;
for(size_t i = 0; i < blockSize; ++i) m += in[i] * weights[i]; float32_t w = 0.0f;
return m;
for(size_t i = 0; i < blockSize; ++i)
{
s += in[i] * weights[i];
w += weights[i];
}
return s / w;
} }
void arm_clip_f32(const float32_t *pSrc, float32_t *pDst, float32_t low, float32_t high, uint32_t numSamples) void arm_clip_f32(const float32_t *pSrc, float32_t *pDst, float32_t low, float32_t high, uint32_t numSamples)

@ -1,3 +1,22 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// test_fx_helper.h
//
// Set og helpers dedicated to code rationalization for the elaboration on unit tests.
// Author: Vincent Gauché
//
#pragma once #pragma once
#include <random> #include <random>

@ -25,6 +25,7 @@ INSTANTIATE_TEST_SUITE_P(MixerOutputTest, MixingConsoleScenarioTest, testing::Ra
void setupMixingConsoleFX(Mixer* mixer) void setupMixingConsoleFX(Mixer* mixer)
{ {
mixer->setChannelLevel(0, 1.0f);
mixer->setPan(0, 0.5f); mixer->setPan(0, 0.5f);
mixer->getTube()->setMute(false); mixer->getTube()->setMute(false);
@ -85,6 +86,7 @@ void setupMixingConsoleFX(Mixer* mixer, int scenarioId)
ACTIVE_FX(scenarioId, FX_PlateReverb); ACTIVE_FX(scenarioId, FX_PlateReverb);
ACTIVE_FX(scenarioId, FX_Reverberator); ACTIVE_FX(scenarioId, FX_Reverberator);
mixer->setChannelLevel(0, 1.0f);
mixer->setPan(0, 0.5f); mixer->setPan(0, 0.5f);
size_t nbActiveFX = 0; size_t nbActiveFX = 0;
@ -217,6 +219,9 @@ TEST(MixingConsole, ZeroSamplesTest)
setupMixingConsoleFX(&mixer); setupMixingConsoleFX(&mixer);
ASSERT_EQ(0, FULL_INSPECT((&mixer), true)); ASSERT_EQ(0, FULL_INSPECT((&mixer), true));
mixer.setChannelLevel(0, 1.0f);
ASSERT_EQ(0, FULL_INSPECT((&mixer), true));
mixer.setPan(0, 0.5f); mixer.setPan(0, 0.5f);
ASSERT_EQ(0, FULL_INSPECT((&mixer), true)); ASSERT_EQ(0, FULL_INSPECT((&mixer), true));
@ -252,6 +257,8 @@ TEST(MixingConsole, DryProcessing)
Mixer mixer(SAMPLING_FREQUENCY, length); Mixer mixer(SAMPLING_FREQUENCY, length);
mixer.reset(); mixer.reset();
mixer.setChannelLevel(0, 1.0f);
mixer.setPan(0, 0.5f);
mixer.setSendLevel(0, MixerOutput::FX_Tube, 0.0f); mixer.setSendLevel(0, MixerOutput::FX_Tube, 0.0f);
mixer.setSendLevel(0, MixerOutput::FX_Chorus, 0.0f); mixer.setSendLevel(0, MixerOutput::FX_Chorus, 0.0f);
@ -267,7 +274,6 @@ TEST(MixingConsole, DryProcessing)
mixer.setReturnLevel(static_cast<MixerOutput>(i), MixerOutput::MainOutput, 0.0f); mixer.setReturnLevel(static_cast<MixerOutput>(i), MixerOutput::MainOutput, 0.0f);
} }
mixer.setPan(0, 0.5f);
mixer.setSendLevel(0, MixerOutput::MainOutput, 1.0f); mixer.setSendLevel(0, MixerOutput::MainOutput, 1.0f);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
@ -298,11 +304,12 @@ TEST(MixingConsole, ReverberatorProcessing)
Mixer mixer(SAMPLING_FREQUENCY, length); Mixer mixer(SAMPLING_FREQUENCY, length);
mixer.reset(); mixer.reset();
mixer.setChannelLevel(0, 1.0f);
mixer.setPan(0, 0.5f);
mixer.setSendLevel(0, MixerOutput::MainOutput, 0.0f); mixer.setSendLevel(0, MixerOutput::MainOutput, 0.0f);
mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f); mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f);
mixer.setReturnLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 1.0f); mixer.setReturnLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 1.0f);
mixer.setPan(0, 0.5f);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
float32_t in[length] = {0.1, 0.2}; float32_t in[length] = {0.1, 0.2};
@ -343,11 +350,12 @@ TEST(MixingConsole, ReverberatorNoiseProcessing)
Mixer mixer(SAMPLING_FREQUENCY, length); Mixer mixer(SAMPLING_FREQUENCY, length);
mixer.reset(); mixer.reset();
mixer.setChannelLevel(0, 1.0f);
mixer.setPan(0, 0.5f);
mixer.setSendLevel(0, MixerOutput::MainOutput, 0.0f); mixer.setSendLevel(0, MixerOutput::MainOutput, 0.0f);
mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f); mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f);
mixer.setReturnLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 1.0f); mixer.setReturnLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 1.0f);
mixer.setPan(0, 0.5f);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
float32_t in[length]; float32_t in[length];
@ -366,7 +374,7 @@ TEST(MixingConsole, ReverberatorNoiseProcessing)
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
} }
TEST(MixingConsole, StandardUsageProcessing) TEST(MixingConsole, StandardUsageProcessingByInjection)
{ {
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
@ -390,8 +398,7 @@ TEST(MixingConsole, StandardUsageProcessing)
mixer.setReturnLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.3f); mixer.setReturnLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.3f);
mixer.setReturnLevel(MixerOutput::FX_Delay, MixerOutput::MainOutput, 0.3f); mixer.setReturnLevel(MixerOutput::FX_Delay, MixerOutput::MainOutput, 0.3f);
mixer.setInputSampleBuffer(0, inSamples[0]); mixer.injectInputSamples(0, inSamples[StereoChannels::Left], inSamples[StereoChannels::Right], size);
mixer.preProcessInputSampleBuffer(0, size);
mixer.process(outSamples[0], outSamples[1]); mixer.process(outSamples[0], outSamples[1]);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamples[0], outSamples[1], size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16); saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamples[0], outSamples[1], size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16);
@ -399,6 +406,65 @@ TEST(MixingConsole, StandardUsageProcessing)
CLEANUP_AUDIO_TEST(inSamples, outSamples); CLEANUP_AUDIO_TEST(inSamples, outSamples);
} }
TEST(MixingConsole, StandardUsageProcessing)
{
static const size_t MAX_BUFFER_SIZE = 4096;
static const size_t BUFFER_SIZE = 256;
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
Mixer mixer(SAMPLING_FREQUENCY, MAX_BUFFER_SIZE);
float32_t channelBuffer[MAX_BUFFER_SIZE];
memset(channelBuffer, 0, MAX_BUFFER_SIZE * sizeof(float32_t));
mixer.setInputSampleBuffer(0, channelBuffer);
setupMixingConsoleFX(&mixer);
mixer.getTube()->setOverdrive(0.15f);
mixer.setSendLevel(0, MixerOutput::FX_Tube, 1.0f);
mixer.setSendLevel(0, MixerOutput::FX_Phaser, 1.0f);
// mixer.setReturnLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, 1.0f);
// mixer.setSendLevel(0, MixerOutput::FX_Chorus, 1.0f);
// mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f);
mixer.setReturnLevel(MixerOutput::FX_Tube, MixerOutput::FX_Chorus, 1.0f);
mixer.setReturnLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Reverberator, 1.0f);
mixer.setReturnLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Delay, 1.0f);
mixer.setSendLevel(0, MixerOutput::MainOutput, 0.25f);
mixer.setReturnLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, 0.1f);
mixer.setReturnLevel(MixerOutput::FX_Chorus, MixerOutput::MainOutput, 0.15f);
mixer.setReturnLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.3f);
mixer.setReturnLevel(MixerOutput::FX_Delay, MixerOutput::MainOutput, 0.3f);
float32_t* inS = inSamples[StereoChannels::Left];
float32_t* outS[StereoChannels::kNumChannels];
outS[StereoChannels::Left ] = outSamples[StereoChannels::Left ];
outS[StereoChannels::Right] = outSamples[StereoChannels::Right];
size_t s = size;
while(s > 0)
{
size_t nb = (s > BUFFER_SIZE) ? BUFFER_SIZE : s;
memcpy(channelBuffer, inS, nb * sizeof(float32_t));
mixer.preProcessInputSampleBuffer(0, nb);
mixer.process(outS[StereoChannels::Left ], outS[StereoChannels::Right]);
// ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
inS += nb;
outS[StereoChannels::Left ] += nb;
outS[StereoChannels::Right] += nb;
s -= nb;
}
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamples[0], outSamples[1], size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
// TEST_P(FXScenarioTest, FXProcessingScenario) // TEST_P(FXScenarioTest, FXProcessingScenario)
// { // {
// const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); // const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info();

@ -16,7 +16,8 @@ TEST(MixingConsole, ShortBuffer)
const size_t size = 10; const size_t size = 10;
Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size);
mixer->reset();
mixer->setChannelLevel(0, 1.0f);
mixer->setPan(0, 0.5f); mixer->setPan(0, 0.5f);
mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f); mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f);
@ -51,7 +52,8 @@ TEST(MixingConsole, ReverberatorShortBuffer)
const size_t size = 10; const size_t size = 10;
Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size);
mixer->reset();
mixer->setChannelLevel(0, 1.0f);
mixer->setPan(0, 0.5f); mixer->setPan(0, 0.5f);
mixer->getReverberator()->setInputGain(0.35f); mixer->getReverberator()->setInputGain(0.35f);
@ -93,6 +95,7 @@ TEST(MixingConsole, DrySamplesBoundariesTest)
mixer->reset(); mixer->reset();
FULL_INSPECT2(mixer, true, "Mixer.reset"); FULL_INSPECT2(mixer, true, "Mixer.reset");
mixer->setChannelLevel(0, 1.0f);
mixer->setPan(0, 0.5f); mixer->setPan(0, 0.5f);
mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f); mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f);
@ -144,6 +147,7 @@ TEST(MixingConsole, ReverberatorSamplesBoundariesTest)
Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size);
mixer->reset(); mixer->reset();
mixer->setChannelLevel(0, 1.0f);
mixer->setPan(0, 0.5f); mixer->setPan(0, 0.5f);
mixer->setSendLevel(0, MixerOutput::MainOutput, 0.4f); mixer->setSendLevel(0, MixerOutput::MainOutput, 0.4f);

@ -1,3 +1,22 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// wave.h
//
// Set of helpers to manipulate RIFF Wave files. These helpers are used in the unit tests.
// Author: Vincent Gauché
//
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>

Loading…
Cancel
Save