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
#include "extra_features.h"
@ -14,8 +35,8 @@ class CFileDevice : public CDevice
DISALLOW_COPY_AND_ASSIGN(CFileDevice);
private:
CFileDevice()
: m_file(new FIL())
CFileDevice() :
m_file(new FIL())
{
FRESULT res = f_open(this->m_file, "SD:/debuglog.txt", FA_WRITE | FA_CREATE_ALWAYS);
assert(res == FR_OK);
@ -42,9 +63,12 @@ public:
virtual int Write(const void* pBuffer, size_t nCount) override
{
UINT nb;
f_write(this->m_file, pBuffer, nCount, &nb);
f_sync(this->m_file);
UINT nb = 0;
if(nCount > 0)
{
f_write(this->m_file, pBuffer, nCount, &nb);
f_sync(this->m_file);
}
return (int)nb;
}

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

@ -15,6 +15,7 @@
// fx.h
//
// Base classes for Stereo audio effects proposed in the context of the MiniDexed project
// Author: Vincent Gauché
//
#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
#include "extra_features.h"

@ -16,6 +16,7 @@
//
// 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
// Ported by: Vincent Gauché
//
#pragma once

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

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

@ -17,6 +17,7 @@
//
// 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
// Ported by: Vincent Gauché
//
#pragma once

@ -12,9 +12,10 @@
// 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

@ -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
#include <algorithm>

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

@ -16,6 +16,7 @@
//
// 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
// Ported by: Vincent Gauché
//
#pragma once

@ -72,8 +72,8 @@ void Phaser::reset()
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 dR = this->dmin_ + (this->dmax_ - this->dmin_) * ((1.0f + this->lfo_[StereoChannels::Right]->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_) * this->lfo_[StereoChannels::Right]->process();
float32_t sampleL = inL + this->feedback_ * this->z_[StereoChannels::Left ];
float32_t sampleR = inR + this->feedback_ * this->z_[StereoChannels::Right];

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

@ -17,6 +17,7 @@
//
// 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
// Ported by: Vincent Gauché
//
#pragma once

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

@ -17,6 +17,7 @@
//
// 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
// Ported by: Vincent Gauché
//
#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
#include "fx.h"

@ -17,6 +17,7 @@
//
// 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
// Ported by: Vincent Gauché
//
#pragma once

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

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

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

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

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

@ -152,7 +152,8 @@ CMiniDexed::CMiniDexed (
this->mixing_console_ = new Mixer(static_cast<float32_t>(pConfig->GetSampleRate()), CConfig::MaxChunkSize);
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
@ -277,10 +278,12 @@ bool CMiniDexed::Initialize (void)
#if defined(MIXING_CONSOLE_ENABLE)
// 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);
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_->setReturnLevel(MixerOutput::FX_PlateReverb, MixerOutput::MainOutput, sendRev);
#elif defined(PLATE_REVERB_ENABLE)
@ -541,6 +544,10 @@ void CMiniDexed::SetVolume (unsigned nVolume, unsigned nTG)
assert (m_pTG[nTG]);
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 ();
}
@ -1780,6 +1787,8 @@ void CMiniDexed::ProcessSound (void)
//
// 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)
// // swap stereo channels if needed
@ -1792,17 +1801,12 @@ void CMiniDexed::ProcessSound (void)
}
// 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)
{
this->m_FXSpinLock.Acquire ();
float32_t SampleBuffer[StereoChannels::kNumChannels][nFrames];
this->m_FXSpinLock.Acquire();
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)
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);
}
else
else // this->nMasterVolume == 0.0f
{
arm_fill_q15(0, tmp_int, nFrames * 2);
}
#elif defined(PLATE_REVERB_ENABLE)
uint8_t indexL=0, indexR=1;
// BEGIN TG mixing
float32_t tmp_float[nFrames*2];
int16_t tmp_int[nFrames*2];
if(nMasterVolume > 0.0f)
{
for (uint8_t i = 0; i < CConfig::ToneGenerators; i++)

@ -2,6 +2,7 @@
// mixing_console.hpp
//
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
// Author: Vincent Gauché
// Copyright (C) 2022 The MiniDexed Team
//
// This program is free software: you can redistribute it and/or modify
@ -43,12 +44,11 @@ public:
~MixingConsole();
// Send section
inline void setChannelLevel(size_t in, float32_t lvl);
inline void setPan(size_t in, float32_t pan);
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 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
inline void setReturnLevel(MixerOutput ret, MixerOutput dest, float32_t lvl);
@ -69,16 +69,22 @@ public:
// Processing
inline void init();
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);
void process(float32_t* outL, float32_t* outR);
protected:
inline void updatePan(size_t in);
inline void setLevel(size_t in, MixerOutput fx, float32_t lvl);
inline void setSample(size_t in, float32_t sampleL, float32_t sampleR);
private:
static inline float32_t weighted_sum(const float32_t* data, const float32_t* weights, size_t size);
const size_t BufferSize;
float32_t channel_level_[nb_inputs];
float32_t pan_[StereoChannels::kNumChannels + 1][nb_inputs];
float32_t* tg_input_sample_buffer_[nb_inputs];
float32_t* input_sample_buffer_[StereoChannels::kNumChannels][nb_inputs];
@ -109,6 +115,7 @@ private:
{
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");
@ -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, '+');
out << "\t" << ss.str() << std::endl;
for(size_t i = 0; i < nb_inputs; ++i)
@ -129,6 +137,7 @@ private:
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]);
@ -256,6 +265,7 @@ private:
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[ 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);
@ -285,7 +295,7 @@ private:
{
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_[ 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>
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::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;
}
}
// 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>
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;
this->pan_[StereoChannels::kNumChannels][in] = pan;
pan *= Constants::MPI_2;
this->pan_[StereoChannels::Left ][in] = InterpolatedSineOscillator::Cos(pan);
this->pan_[StereoChannels::Right][in] = InterpolatedSineOscillator::Sin(pan);
this->updatePan(in);
}
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;
}
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
template<size_t nb_inputs>
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>
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)
@ -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>
void MixingConsole<nb_inputs>::processSample(float32_t& outL, float32_t& outR)
{
const size_t nBuffers = nb_inputs + MixerOutput::kFXCount - 1;
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)
float32_t fx_input_[StereoChannels::kNumChannels];
float32_t fx_output_[StereoChannels::kNumChannels];
for(size_t fxId = 0; fxId < MixerOutput::kFXCount; ++fxId)
{
// 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], nBuffers);
fx_inputs_[i][StereoChannels::Right] = arm_weighted_sum_f32(this->input_samples_[StereoChannels::Right], this->levels_[i], nBuffers);
// Compute the samples that will feed the FX
fx_input_[StereoChannels::Left ] = MixingConsole<nb_inputs>::weighted_sum(this->input_samples_[StereoChannels::Left ], this->levels_[fxId], nBuffers);
fx_input_[StereoChannels::Right] = MixingConsole<nb_inputs>::weighted_sum(this->input_samples_[StereoChannels::Right], this->levels_[fxId], nBuffers);
// 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]
this->fx_[fxId]->processSample(
fx_input_[StereoChannels::Left ],
fx_input_[StereoChannels::Right],
fx_output_[StereoChannels::Left ],
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(
static_cast<MixerOutput>(i),
fx_outputs_[i][StereoChannels::Left],
fx_outputs_[i][StereoChannels::Right]
static_cast<MixerOutput>(fxId),
fx_output_[StereoChannels::Left],
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>
@ -636,6 +669,22 @@ void MixingConsole<nb_inputs>::process(float32_t* outL, float32_t* outR)
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>
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 m = 0.0f;
for(size_t i = 0; i < blockSize; ++i) m += in[i] * weights[i];
return m;
float32_t s = 0.0f;
float32_t w = 0.0f;
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)

@ -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
#include <random>

@ -25,6 +25,7 @@ INSTANTIATE_TEST_SUITE_P(MixerOutputTest, MixingConsoleScenarioTest, testing::Ra
void setupMixingConsoleFX(Mixer* mixer)
{
mixer->setChannelLevel(0, 1.0f);
mixer->setPan(0, 0.5f);
mixer->getTube()->setMute(false);
@ -85,6 +86,7 @@ void setupMixingConsoleFX(Mixer* mixer, int scenarioId)
ACTIVE_FX(scenarioId, FX_PlateReverb);
ACTIVE_FX(scenarioId, FX_Reverberator);
mixer->setChannelLevel(0, 1.0f);
mixer->setPan(0, 0.5f);
size_t nbActiveFX = 0;
@ -217,6 +219,9 @@ TEST(MixingConsole, ZeroSamplesTest)
setupMixingConsoleFX(&mixer);
ASSERT_EQ(0, FULL_INSPECT((&mixer), true));
mixer.setChannelLevel(0, 1.0f);
ASSERT_EQ(0, FULL_INSPECT((&mixer), true));
mixer.setPan(0, 0.5f);
ASSERT_EQ(0, FULL_INSPECT((&mixer), true));
@ -252,6 +257,8 @@ TEST(MixingConsole, DryProcessing)
Mixer mixer(SAMPLING_FREQUENCY, length);
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_Chorus, 0.0f);
@ -267,7 +274,6 @@ TEST(MixingConsole, DryProcessing)
mixer.setReturnLevel(static_cast<MixerOutput>(i), MixerOutput::MainOutput, 0.0f);
}
mixer.setPan(0, 0.5f);
mixer.setSendLevel(0, MixerOutput::MainOutput, 1.0f);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
@ -298,11 +304,12 @@ TEST(MixingConsole, ReverberatorProcessing)
Mixer mixer(SAMPLING_FREQUENCY, length);
mixer.reset();
mixer.setChannelLevel(0, 1.0f);
mixer.setPan(0, 0.5f);
mixer.setSendLevel(0, MixerOutput::MainOutput, 0.0f);
mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f);
mixer.setReturnLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 1.0f);
mixer.setPan(0, 0.5f);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
float32_t in[length] = {0.1, 0.2};
@ -343,11 +350,12 @@ TEST(MixingConsole, ReverberatorNoiseProcessing)
Mixer mixer(SAMPLING_FREQUENCY, length);
mixer.reset();
mixer.setChannelLevel(0, 1.0f);
mixer.setPan(0, 0.5f);
mixer.setSendLevel(0, MixerOutput::MainOutput, 0.0f);
mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f);
mixer.setReturnLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 1.0f);
mixer.setPan(0, 0.5f);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
float32_t in[length];
@ -366,7 +374,7 @@ TEST(MixingConsole, ReverberatorNoiseProcessing)
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
}
TEST(MixingConsole, StandardUsageProcessing)
TEST(MixingConsole, StandardUsageProcessingByInjection)
{
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_Delay, MixerOutput::MainOutput, 0.3f);
mixer.setInputSampleBuffer(0, inSamples[0]);
mixer.preProcessInputSampleBuffer(0, size);
mixer.injectInputSamples(0, inSamples[StereoChannels::Left], inSamples[StereoChannels::Right], size);
mixer.process(outSamples[0], outSamples[1]);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
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);
}
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)
// {
// const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info();

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

@ -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
#include <stdint.h>

Loading…
Cancel
Save