diff --git a/src/CFileDevice.hpp b/src/CFileDevice.hpp
index ccd6cd6..0cd4cf4 100644
--- a/src/CFileDevice.hpp
+++ b/src/CFileDevice.hpp
@@ -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 .
+
+//
+// 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;
}
diff --git a/src/extra_features.h b/src/extra_features.h
index d982388..a081577 100644
--- a/src/extra_features.h
+++ b/src/extra_features.h
@@ -15,6 +15,7 @@
// extra_features.h
//
// Header file that centralizes MACROS to enable / disable extra features
+// Author: Vincent Gauché
//
#pragma once
diff --git a/src/fx.h b/src/fx.h
index 56ca858..bca0408 100644
--- a/src/fx.h
+++ b/src/fx.h
@@ -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
diff --git a/src/fx_base.h b/src/fx_base.h
index 958e434..b57eb1c 100644
--- a/src/fx_base.h
+++ b/src/fx_base.h
@@ -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 .
+
+//
+// fx_base.h
+//
+// Base header file for the FX section of the MiniDexed project
+// Author: Vincent Gauché
+//
+
#pragma once
#include "extra_features.h"
diff --git a/src/fx_chorus.h b/src/fx_chorus.h
index 03d0c8f..6a7d842 100644
--- a/src/fx_chorus.h
+++ b/src/fx_chorus.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
diff --git a/src/fx_components.h b/src/fx_components.h
index 1d23e87..1f62ae1 100644
--- a/src/fx_components.h
+++ b/src/fx_components.h
@@ -15,6 +15,7 @@
// fx_components.h
//
// Several tools and components used in the implemlentation of FX
+// Quthor: Vincent Gauché
//
#pragma once
diff --git a/src/fx_delay.h b/src/fx_delay.h
index c81dee3..e4e5f1c 100644
--- a/src/fx_delay.h
+++ b/src/fx_delay.h
@@ -16,6 +16,7 @@
// fx_tape_delay.h
//
// Stereo Delay proposed in the context of the MiniDexed project
+// Author: Vincent Gauché
//
#pragma once
diff --git a/src/fx_diffuser.h b/src/fx_diffuser.h
index 9f2ef44..a6fdfda 100644
--- a/src/fx_diffuser.h
+++ b/src/fx_diffuser.h
@@ -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
diff --git a/src/fx_dry.h b/src/fx_dry.h
index 0890f8a..42ea0ac 100644
--- a/src/fx_dry.h
+++ b/src/fx_dry.h
@@ -12,9 +12,10 @@
// along with this program. If not, see .
//
-// 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
diff --git a/src/fx_engine.hpp b/src/fx_engine.hpp
index 02f1166..4cef466 100644
--- a/src/fx_engine.hpp
+++ b/src/fx_engine.hpp
@@ -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 .
+
+//
+// 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
diff --git a/src/fx_flanger.h b/src/fx_flanger.h
index 86979a4..84b3fe3 100644
--- a/src/fx_flanger.h
+++ b/src/fx_flanger.h
@@ -15,6 +15,7 @@
// fx_flanger.h
//
// Stereo Flanger audio effects proposed in the context of the MiniDexed project
+// Author: Vincent Gauché
//
#pragma once
diff --git a/src/fx_orbitone.h b/src/fx_orbitone.h
index fe2dd36..e901b68 100644
--- a/src/fx_orbitone.h
+++ b/src/fx_orbitone.h
@@ -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
diff --git a/src/fx_phaser.cpp b/src/fx_phaser.cpp
index da34deb..eadbb77 100644
--- a/src/fx_phaser.cpp
+++ b/src/fx_phaser.cpp
@@ -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];
diff --git a/src/fx_phaser.h b/src/fx_phaser.h
index 63712d4..537cdd4 100644
--- a/src/fx_phaser.h
+++ b/src/fx_phaser.h
@@ -15,6 +15,7 @@
// fx_phaser.h
//
// Stereo Phaser audio effects proposed in the context of the MiniDexed project
+// Author: Vincent Gauché
//
#pragma once
diff --git a/src/fx_pitch_shifter.h b/src/fx_pitch_shifter.h
index 35ba366..18fb664 100644
--- a/src/fx_pitch_shifter.h
+++ b/src/fx_pitch_shifter.h
@@ -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
diff --git a/src/fx_rack.h b/src/fx_rack.h
index 2d3eac3..b018e7d 100644
--- a/src/fx_rack.h
+++ b/src/fx_rack.h
@@ -15,6 +15,7 @@
// fx_rack.h
//
// Rack of audio effects proposed in the context of the MiniDexed project
+// Author: Vincent Gauché
//
#pragma once
diff --git a/src/fx_reverberator.h b/src/fx_reverberator.h
index c01cbff..2e9ac2f 100644
--- a/src/fx_reverberator.h
+++ b/src/fx_reverberator.h
@@ -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
diff --git a/src/fx_shimmer_helper.h b/src/fx_shimmer_helper.h
index 20c42c9..378a7a9 100644
--- a/src/fx_shimmer_helper.h
+++ b/src/fx_shimmer_helper.h
@@ -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 .
+
+//
+// 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"
diff --git a/src/fx_shimmer_reverb.h b/src/fx_shimmer_reverb.h
index e0535e5..81e11eb 100644
--- a/src/fx_shimmer_reverb.h
+++ b/src/fx_shimmer_reverb.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
diff --git a/src/fx_svf.h b/src/fx_svf.h
index 3f8119b..edaea61 100644
--- a/src/fx_svf.h
+++ b/src/fx_svf.h
@@ -16,6 +16,7 @@
// fx_svf.h
//
// State Variable Filter used in Tape Delay
+// Author: Vincent Gauché
//
#pragma once
diff --git a/src/fx_tube.h b/src/fx_tube.h
index b4cfdba..e749c23 100644
--- a/src/fx_tube.h
+++ b/src/fx_tube.h
@@ -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
diff --git a/src/fx_unit.hpp b/src/fx_unit.hpp
index 173a1c6..955569c 100644
--- a/src/fx_unit.hpp
+++ b/src/fx_unit.hpp
@@ -15,6 +15,7 @@
// fx_unit.h
//
// Unit of FX that handle enable and wet parameters
+// Author: Vincent Gauché
//
#pragma once
diff --git a/src/fx_unit2.hpp b/src/fx_unit2.hpp
index ee5c85f..906bc15 100644
--- a/src/fx_unit2.hpp
+++ b/src/fx_unit2.hpp
@@ -15,6 +15,7 @@
// fx_unit2.h
//
// Unit of FX that handle the mute parameter
+// Author: Vincent Gauché
//
#pragma once
diff --git a/src/kernel.cpp b/src/kernel.cpp
index a5d848a..4397cd9 100644
--- a/src/kernel.cpp
+++ b/src/kernel.cpp
@@ -22,8 +22,6 @@
#include
#include
-#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 ())
diff --git a/src/minidexed.cpp b/src/minidexed.cpp
index 465dc36..1a13925 100644
--- a/src/minidexed.cpp
+++ b/src/minidexed.cpp
@@ -152,7 +152,8 @@ CMiniDexed::CMiniDexed (
this->mixing_console_ = new Mixer(static_cast(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++)
diff --git a/src/mixing_console.hpp b/src/mixing_console.hpp
index 72db76e..7210898 100644
--- a/src/mixing_console.hpp
+++ b/src/mixing_console.hpp
@@ -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
+float32_t MixingConsole::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
MixingConsole::MixingConsole(float32_t sampling_rate, size_t buffer_size) :
@@ -348,11 +365,26 @@ MixingConsole::~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
+void MixingConsole::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
void MixingConsole::setPan(size_t in, float32_t pan)
{
@@ -363,10 +395,7 @@ void MixingConsole::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
@@ -394,67 +423,6 @@ void MixingConsole::setInputSampleBuffer(size_t in, float32_t* sample
this->tg_input_sample_buffer_[in] = samples;
}
-template
-void MixingConsole::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
-void MixingConsole::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
void MixingConsole::setReturnLevel(MixerOutput ret, MixerOutput dest, float32_t lvl)
@@ -545,6 +513,7 @@ FXUnit2* MixingConsole::getDry()
template
void MixingConsole::init()
{
+ memset(this->channel_level_, 0, nb_inputs * sizeof(float32_t));
for(size_t i = 0; i <= StereoChannels::kNumChannels; ++i) memset(this->pan_[i], 0, nb_inputs * sizeof(float32_t));
for(size_t i = 0; i < MixerOutput::kFXCount; ++i)
@@ -576,41 +545,105 @@ void MixingConsole::reset()
}
}
+template
+void MixingConsole::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
+void MixingConsole::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
void MixingConsole::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::weighted_sum(this->input_samples_[StereoChannels::Left ], this->levels_[fxId], nBuffers);
+ fx_input_[StereoChannels::Right] = MixingConsole::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(i),
- fx_outputs_[i][StereoChannels::Left],
- fx_outputs_[i][StereoChannels::Right]
+ static_cast(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
@@ -636,6 +669,22 @@ void MixingConsole::process(float32_t* outL, float32_t* outR)
this->m_nSamples = 0;
}
+template
+void MixingConsole::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
void MixingConsole::setLevel(size_t in, MixerOutput fx, float32_t lvl)
{
diff --git a/src/test/arm_functions.cpp b/src/test/arm_functions.cpp
index 84385ce..5ba4673 100644
--- a/src/test/arm_functions.cpp
+++ b/src/test/arm_functions.cpp
@@ -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)
diff --git a/src/test/test_fx_helper.h b/src/test/test_fx_helper.h
index e694a8f..c7383b0 100644
--- a/src/test/test_fx_helper.h
+++ b/src/test/test_fx_helper.h
@@ -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 .
+
+//
+// test_fx_helper.h
+//
+// Set og helpers dedicated to code rationalization for the elaboration on unit tests.
+// Author: Vincent Gauché
+//
#pragma once
#include
diff --git a/src/test/test_fx_mixing_console.cpp b/src/test/test_fx_mixing_console.cpp
index d92c806..bafbf5d 100644
--- a/src/test/test_fx_mixing_console.cpp
+++ b/src/test/test_fx_mixing_console.cpp
@@ -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(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(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(SAMPLING_FREQUENCY), 16);
+
+ CLEANUP_AUDIO_TEST(inSamples, outSamples);
+}
+
// TEST_P(FXScenarioTest, FXProcessingScenario)
// {
// const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info();
diff --git a/src/test/test_fx_mixing_console_unitary.cpp b/src/test/test_fx_mixing_console_unitary.cpp
index e7e7f83..d63e233 100644
--- a/src/test/test_fx_mixing_console_unitary.cpp
+++ b/src/test/test_fx_mixing_console_unitary.cpp
@@ -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);
diff --git a/src/test/wave.h b/src/test/wave.h
index 6f9c2c4..3388431 100644
--- a/src/test/wave.h
+++ b/src/test/wave.h
@@ -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 .
+
+//
+// wave.h
+//
+// Set of helpers to manipulate RIFF Wave files. These helpers are used in the unit tests.
+// Author: Vincent Gauché
+//
#pragma once
#include