new fx implementation

pull/499/head
Vincent 2 years ago
parent d2f11cc299
commit 253d36d89a
  1. 10
      .gitignore
  2. 78
      src/CFileDevice.hpp
  3. 6
      src/Makefile
  4. 4
      src/debug.hpp
  5. 11
      src/extra_features.h
  6. 12
      src/fx.cpp
  7. 7
      src/fx.h
  8. 20
      src/fx_base.h
  9. 1
      src/fx_chorus.h
  10. 52
      src/fx_components.cpp
  11. 4
      src/fx_components.h
  12. 98
      src/fx_delay.cpp
  13. 36
      src/fx_delay.h
  14. 71
      src/fx_diffuser.cpp
  15. 65
      src/fx_diffuser.h
  16. 21
      src/fx_dry.cpp
  17. 43
      src/fx_dry.h
  18. 21
      src/fx_engine.hpp
  19. 1
      src/fx_flanger.h
  20. 3
      src/fx_orbitone.cpp
  21. 1
      src/fx_orbitone.h
  22. 4
      src/fx_phaser.cpp
  23. 1
      src/fx_phaser.h
  24. 104
      src/fx_pitch_shifter.cpp
  25. 115
      src/fx_pitch_shifter.h
  26. 1
      src/fx_rack.h
  27. 1
      src/fx_reverberator.h
  28. 135
      src/fx_shimmer_helper.cpp
  29. 34
      src/fx_shimmer_helper.h
  30. 169
      src/fx_shimmer_reverb.cpp
  31. 111
      src/fx_shimmer_reverb.h
  32. 1
      src/fx_svf.h
  33. 32
      src/fx_tube.cpp
  34. 1
      src/fx_tube.h
  35. 1
      src/fx_unit.hpp
  36. 104
      src/fx_unit2.hpp
  37. 533
      src/mididevice.cpp
  38. 1583
      src/minidexed.cpp
  39. 235
      src/minidexed.h
  40. 793
      src/mixing_console.hpp
  41. 58
      src/mixing_console_constants.h
  42. 224
      src/performance.ini
  43. 539
      src/performanceconfig.cpp
  44. 229
      src/performanceconfig.h
  45. 24
      src/test/Makefile
  46. 13
      src/test/arm_functions.cpp
  47. 390
      src/test/beta.cpp
  48. 2
      src/test/test_fxLevelTuning.cpp
  49. 12
      src/test/test_fx_components.cpp
  50. 19
      src/test/test_fx_helper.h
  51. 565
      src/test/test_fx_mixing_console.cpp
  52. 187
      src/test/test_fx_mixing_console_unitary.cpp
  53. 6
      src/test/test_fx_rack.cpp
  54. 182
      src/test/test_fx_shimmer_reverb.cpp
  55. 55
      src/test/test_unitFXTuning.cpp
  56. 19
      src/test/wave.h
  57. 1114
      src/uimenu.cpp
  58. 42
      src/uimenu.h
  59. BIN
      wiki-update/Chorus--Channel.png
  60. BIN
      wiki-update/Delay--Channel.png
  61. BIN
      wiki-update/Flanger--Channel.png
  62. BIN
      wiki-update/Orbitone--Channel.png
  63. BIN
      wiki-update/Phaser--Channel.png
  64. BIN
      wiki-update/PlateReverb--Channel.png
  65. BIN
      wiki-update/Reverberator--Channel.png
  66. BIN
      wiki-update/Tube--Channel.png
  67. BIN
      wiki-update/Tube--overdrive-response.png
  68. 470
      wiki-update/fx-page.md
  69. BIN
      wiki-update/mixing-console--TG-Channel.png
  70. BIN
      wiki-update/mixing-console-overview.png

10
.gitignore vendored

@ -45,10 +45,8 @@ sdcard
# Editor related files
*.swp
*.swo
.vscode/
# temporary tests
src/test/*.bin
src/test/bin
src/test/results
src/test/objects
.vscode/
src/test/bin/
src/test/objects/
src/test/results/

@ -0,0 +1,78 @@
// 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"
#include <circle/device.h>
#include <fatfs/ff.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <cassert>
class CFileDevice : public CDevice
{
DISALLOW_COPY_AND_ASSIGN(CFileDevice);
private:
CFileDevice() :
m_file(new FIL())
{
FRESULT res = f_open(this->m_file, "SD:/debuglog.txt", FA_WRITE | FA_CREATE_ALWAYS);
assert(res == FR_OK);
}
public:
static void UseMeForLogger()
{
CLogger::Get()->SetNewTarget(&CFileDevice::GetInstance());
}
static CFileDevice& GetInstance()
{
static CFileDevice Instance;
return Instance;
}
~CFileDevice()
{
f_sync(this->m_file);
f_close(this->m_file);
}
virtual int Write(const void* pBuffer, size_t nCount) override
{
UINT nb = 0;
if(nCount > 0)
{
f_write(this->m_file, pBuffer, nCount, &nb);
f_sync(this->m_file);
}
return (int)nb;
}
private:
FIL* m_file;
};

@ -30,8 +30,12 @@ OBJS += fx_flanger.o
OBJS += fx_orbitone.o
OBJS += fx_phaser.o
OBJS += fx_delay.o
OBJS += fx_shimmer_helper.o
OBJS += fx_diffuser.o
OBJS += fx_pitch_shifter.o
OBJS += fx_reverberator.o
OBJS += fx_rack.o
OBJS += fx_shimmer_reverb.o
OBJS += fx_dry.o
OBJS += uibuttons.o
OBJS += midipin.o

@ -76,7 +76,7 @@ public:
#define INSPECTABLE(clazz) clazz : public ValueInpectorDebugger
#define IMPLEMENT_DUMP(code) \
public:\
virtual void dump(std::ostream& out, bool deepInspection = true, const std::string& tag = "") const override\
__attribute__((noinline)) virtual void dump(std::ostream& out, bool deepInspection = true, const std::string& tag = "") const override\
{\
code\
}
@ -88,7 +88,7 @@ public:\
#define IMPLEMENT_INSPECT(code) \
public:\
virtual size_t inspect(ValueInpector inspector, bool deepInspection = true, const std::string& tag = "") const override\
__attribute__((noinline)) virtual size_t inspect(ValueInpector inspector, bool deepInspection = true, const std::string& tag = "") const override\
{\
code\
}

@ -15,6 +15,7 @@
// extra_features.h
//
// Header file that centralizes MACROS to enable / disable extra features
// Author: Vincent Gauché
//
#pragma once
@ -23,7 +24,13 @@
void operator=(const TypeName&) = delete
#if defined(ARM_ALLOW_MULTI_CORE)
#define FXRACK_ENABLE //Add support for the FXRack
#if RASPPI < 3
#define PLATE_REVERB_ENABLE // Add support for the PlateReverb
#else
#define MIXING_CONSOLE_ENABLE // Add support for the MixingConsole
#endif
#endif
#ifdef DEBUG
@ -34,6 +41,8 @@
#include <unordered_map>
#include <string>
using namespace std;
inline long long int getElapseTime(std::string marker = "")
{
static std::unordered_map<std::string, std::chrono::high_resolution_clock::time_point> marker_times;

@ -16,7 +16,8 @@ float32_t FXBase::getSamplingRate() const
FXElement::FXElement(float32_t sampling_rate, float32_t output_level_corrector) :
FXBase(sampling_rate),
OutputLevelCorrector(output_level_corrector)
OutputLevelCorrector(output_level_corrector),
bypass_fx_process_(false)
{
}
@ -24,6 +25,15 @@ FXElement::~FXElement()
{
}
void FXElement::bypassFXProcess(bool bypass)
{
this->bypass_fx_process_ = bypass;
}
bool FXElement::bypassFXProcess() const
{
return this->bypass_fx_process_;
}
FX::FX(float32_t sampling_rate) :
FXBase(sampling_rate)

@ -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
@ -59,7 +60,13 @@ protected:
public:
virtual ~FXElement();
virtual void bypassFXProcess(bool bypass);
virtual bool bypassFXProcess() const;
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) = 0;
private:
bool bypass_fx_process_;
};
class FX : public FXBase

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

@ -174,6 +174,7 @@ FastLFO2::FastLFO2(float32_t sampling_rate, float32_t min_frequency, float32_t m
max_frequency_(max_frequency),
centered_(centered),
frequency_(0.0f),
normalized_frequency_(0.0f),
phase_(initial_phase),
phase_increment_(0.0f),
current_(0.0f)
@ -267,18 +268,53 @@ float32_t FastLFO2::current() const
////////////////////////////////////////////////
// InterpolatedSineOscillator implemlentation //
////////////////////////////////////////////////
float32_t InterpolatedSineOscillator::Sin(float32_t phase)
{
static bool initialized = false;
if(!initialized)
{
initialized = InterpolatedSineOscillator::ClassInitializer();
}
if(phase < 0.0f) while(phase < 0.0f) phase += Constants::M2PI;
else while(phase > Constants::M2PI) phase -= Constants::M2PI;
float32_t findex = phase / InterpolatedSineOscillator::DeltaTime;
size_t index1 = static_cast<size_t>(findex);
size_t index2 = index1 + 1;
float32_t f1 = InterpolatedSineOscillator::CenteredDataPoints[index1];
float32_t f2 = InterpolatedSineOscillator::CenteredDataPoints[index2];
float32_t r = findex - index1;
return f1 + (f2 - f1) * r * InterpolatedSineOscillator::DeltaTime;
}
float32_t InterpolatedSineOscillator::Cos(float32_t phase)
{
return InterpolatedSineOscillator::Sin(Constants::MPI_2 - phase);
}
bool InterpolatedSineOscillator::ClassInitializer()
{
float32_t phase_increment = Constants::M2PI / static_cast<float32_t>(InterpolatedSineOscillator::DataPointSize);
float32_t phase = 0.0;
for(size_t i = 0; i <= InterpolatedSineOscillator::DataPointSize; ++i)
static bool initialized = false;
if(!initialized)
{
InterpolatedSineOscillator::CenteredDataPoints[i] = std::sin(phase);
InterpolatedSineOscillator::UpliftDataPoints[i] = InterpolatedSineOscillator::CenteredDataPoints[i] * 0.5f + 0.5f;
phase += phase_increment;
float32_t phase_increment = Constants::M2PI / static_cast<float32_t>(InterpolatedSineOscillator::DataPointSize);
float32_t phase = 0.0;
for(size_t i = 0; i <= InterpolatedSineOscillator::DataPointSize; ++i)
{
InterpolatedSineOscillator::CenteredDataPoints[i] = std::sin(phase);
InterpolatedSineOscillator::UpliftDataPoints[i] = InterpolatedSineOscillator::CenteredDataPoints[i] * 0.5f + 0.5f;
phase += phase_increment;
}
initialized = true;
}
return true;
return initialized;
}
float32_t InterpolatedSineOscillator::CenteredDataPoints[InterpolatedSineOscillator::DataPointSize + 1];
@ -574,7 +610,7 @@ void JitterGenerator::reset()
float32_t JitterGenerator::process()
{
float32_t out = arm_sin_f32(this->phase_);
float32_t out = InterpolatedSineOscillator::Sin(this->phase_);
this->phase_ += this->phase_increment_ * (1.0f + this->magnitude_ * this->rnd_distribution_(this->rnd_generator_));
if(this->phase_ > Constants::M2PI)

@ -15,6 +15,7 @@
// fx_components.h
//
// Several tools and components used in the implemlentation of FX
// Quthor: Vincent Gauché
//
#pragma once
@ -231,6 +232,9 @@ class InterpolatedSineOscillator : public FXBase
DISALLOW_COPY_AND_ASSIGN(InterpolatedSineOscillator);
public:
static float32_t Sin(float32_t phase);
static float32_t Cos(float32_t phase);
InterpolatedSineOscillator(float32_t sampling_rate, float32_t min_frequency = LFO_MIN_FREQUENCY, float32_t max_frequency = LFO_MAX_FREQUENCY, float32_t initial_phase = 0.0f, bool centered = true);
virtual ~InterpolatedSineOscillator();

@ -2,8 +2,9 @@
#include <cmath>
#define MAX_DELAY_TIME 2.0f
#define MAX_FLUTTER_DELAY_TIME 0.001f
#define MAX_DELAY_TIME 1.0f
#define MAX_FLUTTER_DELAY_TIME 0.2f
#define MAX_FLUTTER_DELAY_AMOUNT 0.01f
#define LPF_CUTOFF_REF 12000.0f
#define HPF_CUTOFF_REF 80.0f
@ -51,11 +52,10 @@ void Delay::LowHighPassFilter::processSample(float32_t inL, float32_t inR, float
Delay::Delay(const float32_t sampling_rate, float32_t default_delay_time, float32_t default_flutter_level, float32_t default_feedback_level) :
FXElement(sampling_rate, 2.2587f),
MaxSampleDelayTime((MAX_DELAY_TIME + MAX_FLUTTER_DELAY_TIME) * sampling_rate * MAX_DELAY_TIME),
read_pos_L_(0),
read_pos_R_(0),
filter_(sampling_rate),
jitter_generator_(sampling_rate)
MaxSampleDelayTime((MAX_DELAY_TIME + MAX_FLUTTER_DELAY_TIME) * sampling_rate),
write_pos_L_(0),
write_pos_R_(0),
filter_(sampling_rate)
{
this->buffer_L_ = new float32_t[this->MaxSampleDelayTime];
this->buffer_R_ = new float32_t[this->MaxSampleDelayTime];
@ -63,8 +63,6 @@ Delay::Delay(const float32_t sampling_rate, float32_t default_delay_time, float3
this->setLeftDelayTime(default_delay_time);
this->setRightDelayTime(default_delay_time);
this->setFeedback(default_feedback_level);
this->setFlutterRate(0.2f);
this->setFlutterAmount(0.0f);
this->reset();
}
@ -79,59 +77,49 @@ void Delay::reset()
{
memset(this->buffer_L_, 0, this->MaxSampleDelayTime * sizeof(float32_t));
memset(this->buffer_R_, 0, this->MaxSampleDelayTime * sizeof(float32_t));
this->read_pos_L_ = 0;
this->read_pos_R_ = 0;
this->write_pos_L_ = 0;
this->write_pos_R_ = 0;
this->filter_.reset();
this->jitter_generator_.reset();
}
void Delay::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
static const float32_t max_delay_time = MAX_DELAY_TIME * this->getSamplingRate();
float32_t jitter_delay_time = 0.0f;
if(this->jitter_amount_ != 0.0f)
{
float32_t jitter_ratio = this->jitter_generator_.process();
if(jitter_ratio != 0.0f)
{
jitter_ratio *= this->jitter_amount_;
jitter_delay_time = MAX_FLUTTER_DELAY_TIME * jitter_ratio * this->getSamplingRate();
}
}
// this->filter_.setCutoffChangeRatio(jitter_ratio);
float32_t delay_time_L = jitter_delay_time + max_delay_time * this->getLeftDelayTime();
float32_t delay_time_R = jitter_delay_time + max_delay_time * this->getRightDelayTime();
// Write input to delay buffers
this->buffer_L_[this->write_pos_L_] *= this->getFeedback();;
this->buffer_L_[this->write_pos_L_] += inL;
this->buffer_R_[this->write_pos_R_] *= this->getFeedback();;
this->buffer_R_[this->write_pos_R_] += inR;
// Calculate write positions
unsigned write_pos_L = static_cast<unsigned>(this->MaxSampleDelayTime + this->read_pos_L_ + delay_time_L) % this->MaxSampleDelayTime;
unsigned write_pos_R = static_cast<unsigned>(this->MaxSampleDelayTime + this->read_pos_R_ + delay_time_R) % this->MaxSampleDelayTime;
// Calculate read positions
float32_t delay_time_L = std::abs(MAX_DELAY_TIME * this->getLeftDelayTime() ) * this->getSamplingRate();
float32_t delay_time_R = std::abs(MAX_DELAY_TIME * this->getRightDelayTime()) * this->getSamplingRate();
float32_t signL = this->getLeftDelayTime() >= 0 ? 1.0f : -1.0f;
float32_t signR = this->getRightDelayTime() >= 0 ? 1.0f : -1.0f;
unsigned read_pos_L = static_cast<unsigned>(this->MaxSampleDelayTime + signL * this->write_pos_L_ - delay_time_L) % this->MaxSampleDelayTime;
unsigned read_pos_R = static_cast<unsigned>(this->MaxSampleDelayTime + signR * this->write_pos_R_ - delay_time_R) % this->MaxSampleDelayTime;
// Write input to delay buffers
this->buffer_L_[write_pos_L] = inL;
this->buffer_R_[write_pos_R] = inR;
// Read from delay buffers and apply feedback
this->filter_.processSample(
this->buffer_L_[this->read_pos_L_],
this->buffer_R_[this->read_pos_R_],
this->buffer_L_[read_pos_L],
this->buffer_R_[read_pos_R],
outL,
outR
);
this->buffer_L_[write_pos_L] += outL * this->getFeedback();
this->buffer_R_[write_pos_R] += outR * this->getFeedback();
this->buffer_L_[this->write_pos_L_] += outL * this->getFeedback();
this->buffer_R_[this->write_pos_R_] += outR * this->getFeedback();
// Increment read positions
++this->read_pos_L_;
if(this->read_pos_L_ >= this->MaxSampleDelayTime)
++ this->write_pos_L_;
if(this->write_pos_L_ >= this->MaxSampleDelayTime)
{
this->read_pos_L_ -= this->MaxSampleDelayTime;
this->write_pos_L_ -= this->MaxSampleDelayTime;
}
++this->read_pos_R_;
if(this->read_pos_R_ >= this->MaxSampleDelayTime)
++ this->write_pos_R_;
if(this->write_pos_R_ >= this->MaxSampleDelayTime)
{
this->read_pos_R_ -= this->MaxSampleDelayTime;
this->write_pos_R_ -= this->MaxSampleDelayTime;
}
outL *= this->OutputLevelCorrector;
@ -140,7 +128,7 @@ void Delay::processSample(float32_t inL, float32_t inR, float32_t& outL, float32
void Delay::setLeftDelayTime(float32_t delay_time)
{
this->delay_time_L_ = constrain(delay_time, 0.0f, 1.0f);
this->delay_time_L_ = constrain(delay_time, -1.0f, 1.0f);
}
float32_t Delay::getLeftDelayTime() const
@ -150,7 +138,7 @@ float32_t Delay::getLeftDelayTime() const
void Delay::setRightDelayTime(float32_t delay_time)
{
this->delay_time_R_ = constrain(delay_time, 0.0f, 1.0f);
this->delay_time_R_ = constrain(delay_time, -1.0f, 1.0f);
}
float32_t Delay::getRightDelayTime() const
@ -167,23 +155,3 @@ float32_t Delay::getFeedback() const
{
return this->feedback_;
}
void Delay::setFlutterRate(float32_t rate)
{
this->jitter_generator_.setRate(rate);
}
float32_t Delay::getFlutterRate() const
{
return this->jitter_generator_.getRate();
}
void Delay::setFlutterAmount(float32_t amount)
{
this->jitter_amount_ = constrain(amount, 0.0f, 1.0f);
}
float32_t Delay::getFlutterAmount() const
{
return this->jitter_amount_;
}

@ -16,6 +16,7 @@
// fx_tape_delay.h
//
// Stereo Delay proposed in the context of the MiniDexed project
// Author: Vincent Gauché
//
#pragma once
@ -104,25 +105,17 @@ public:
void setFeedback(float32_t feedback);
float32_t getFeedback() const;
void setFlutterRate(float32_t rate);
float32_t getFlutterRate() const;
void setFlutterAmount(float32_t amount);
float32_t getFlutterAmount() const;
private:
const size_t MaxSampleDelayTime;
unsigned read_pos_L_;
unsigned read_pos_R_;
unsigned write_pos_L_;
unsigned write_pos_R_;
float32_t* buffer_L_;
float32_t* buffer_R_;
float32_t delay_time_L_; // Left delay time in seconds (0.0 - 2.0)
float32_t delay_time_R_; // Right delay time in seconds (0.0 - 2.0)
float32_t feedback_; // Feedback (0.0 - 1.0)
float32_t jitter_amount_;
LowHighPassFilter filter_;
PerlinNoiseGenerator jitter_generator_;
IMPLEMENT_DUMP(
const size_t space = 18;
@ -133,13 +126,11 @@ private:
out << "START " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
SS_RESET(ss, precision, std::left);
SS__TEXT(ss, ' ', space, std::left, '|', "read_pos_L_");
SS__TEXT(ss, ' ', space, std::left, '|', "read_pos_R_");
SS__TEXT(ss, ' ', space, std::left, '|', "write_pos_L_");
SS__TEXT(ss, ' ', space, std::left, '|', "write_pos_R_");
SS__TEXT(ss, ' ', space, std::left, '|', "delay_time_L_");
SS__TEXT(ss, ' ', space, std::left, '|', "delay_time_R_");
SS__TEXT(ss, ' ', space, std::left, '|', "feedback_");
SS__TEXT(ss, ' ', space, std::left, '|', "jitter_amount_");
SS__TEXT(ss, ' ', space, std::left, '|', "filter_");
out << "\t" << ss.str() << std::endl;
SS_RESET(ss, precision, std::left);
@ -148,16 +139,14 @@ 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;
SS_RESET(ss, precision, std::left);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->read_pos_L_);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->read_pos_R_);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->write_pos_L_);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->write_pos_R_);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->delay_time_L_);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->delay_time_R_);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->feedback_);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->jitter_amount_);
out << "\t" << ss.str() << std::endl;
if(deepInspection)
@ -186,7 +175,6 @@ private:
}
this->filter_.dump(out, deepInspection, tag + ".filter_");
this->jitter_generator_.dump(out, deepInspection, tag + ".jitter_generator_");
}
out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
@ -194,12 +182,11 @@ private:
IMPLEMENT_INSPECT(
size_t nb_errors = 0;
nb_errors += inspector(tag + ".read_pos_L_", static_cast<float32_t>(this->read_pos_L_), 0.0f, static_cast<float32_t>(this->MaxSampleDelayTime), deepInspection);
nb_errors += inspector(tag + ".read_pos_R_", static_cast<float32_t>(this->read_pos_R_), 0.0f, static_cast<float32_t>(this->MaxSampleDelayTime), deepInspection);
nb_errors += inspector(tag + ".delay_time_L_", this->delay_time_L_, 0.0f, static_cast<float32_t>(this->MaxSampleDelayTime), deepInspection);
nb_errors += inspector(tag + ".delay_time_R_", this->delay_time_R_, 0.0f, static_cast<float32_t>(this->MaxSampleDelayTime), deepInspection);
nb_errors += inspector(tag + ".write_pos_L_", static_cast<float32_t>(this->write_pos_L_), 0.0f, static_cast<float32_t>(this->MaxSampleDelayTime), deepInspection);
nb_errors += inspector(tag + ".write_pos_R_", static_cast<float32_t>(this->write_pos_R_), 0.0f, static_cast<float32_t>(this->MaxSampleDelayTime), deepInspection);
nb_errors += inspector(tag + ".delay_time_L_", this->delay_time_L_, -1.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".delay_time_R_", this->delay_time_R_, -1.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".feedback_", this->feedback_, 0.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".jitter_amount_", this->jitter_amount_, 0.0f, 1.0f, deepInspection);
if(deepInspection)
{
@ -210,7 +197,6 @@ private:
}
nb_errors += this->filter_.inspect(inspector, deepInspection, tag + ".filter_");
nb_errors += this->jitter_generator_.inspect(inspector, deepInspection, tag + ".jitter_generator_");
}
return nb_errors;

@ -0,0 +1,71 @@
#include "fx_diffuser.h"
#include <cmath>
#include <algorithm>
#define TAIL , -1
Diffuser::Diffuser(float32_t sampling_frequency) :
FXElement(sampling_frequency),
engine_(sampling_frequency)
{
}
Diffuser::~Diffuser()
{
}
void Diffuser::reset()
{
this->engine_.reset();
}
void Diffuser::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
typedef Engine::Reserve<126,
Engine::Reserve<180,
Engine::Reserve<269,
Engine::Reserve<444,
Engine::Reserve<151,
Engine::Reserve<205,
Engine::Reserve<245,
Engine::Reserve<405> > > > > > > > Memory;
Engine::DelayLine<Memory, 0> apl1;
Engine::DelayLine<Memory, 1> apl2;
Engine::DelayLine<Memory, 2> apl3;
Engine::DelayLine<Memory, 3> apl4;
Engine::DelayLine<Memory, 4> apr1;
Engine::DelayLine<Memory, 5> apr2;
Engine::DelayLine<Memory, 6> apr3;
Engine::DelayLine<Memory, 7> apr4;
Engine::Context c;
const float32_t kap = 0.625f;
float wet = 0.0f;
engine_.start(&c);
c.load(inL);
c.read(apl1 TAIL, kap);
c.writeAllPass(apl1, -kap);
c.read(apl2 TAIL, kap);
c.writeAllPass(apl2, -kap);
c.read(apl3 TAIL, kap);
c.writeAllPass(apl3, -kap);
c.read(apl4 TAIL, kap);
c.writeAllPass(apl4, -kap);
c.writeAndLoad(wet, 0.0f);
outL = wet;
c.load(inR);
c.read(apr1 TAIL, kap);
c.writeAllPass(apr1, -kap);
c.read(apr2 TAIL, kap);
c.writeAllPass(apr2, -kap);
c.read(apr3 TAIL, kap);
c.writeAllPass(apr3, -kap);
c.read(apr4 TAIL, kap);
c.writeAllPass(apr4, -kap);
c.writeAndLoad(wet, 0.0f);
outR = wet;
}

@ -0,0 +1,65 @@
// 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_reverb3.h
//
// 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
#include "fx_components.h"
#include "fx_engine.hpp"
#define DIFFUSER_BUFFER_SIZE 2048
class Diffuser : public FXElement
{
DISALLOW_COPY_AND_ASSIGN(Diffuser);
public:
Diffuser(float32_t sampling_frequency);
virtual ~Diffuser();
virtual void reset() override;
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
private:
typedef FxEngine<DIFFUSER_BUFFER_SIZE, Format::FORMAT_FLOAT32, false> Engine;
Engine engine_;
IMPLEMENT_DUMP(
out << "START " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
if(deepInspection)
{
this->engine_.dump(out, deepInspection, tag + ".engine_");
}
out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
)
IMPLEMENT_INSPECT(
size_t nb_errors = 0u;
if(deepInspection)
{
nb_errors += this->engine_.inspect(inspector, deepInspection, tag + ".engine_");
}
return nb_errors;
)
};

@ -0,0 +1,21 @@
#include "fx_dry.h"
Dry::Dry(float32_t samplingRate) :
FXElement(samplingRate)
{
}
Dry::~Dry()
{
}
void Dry::reset()
{
// nothing to be done
}
void Dry::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
outL = inL;
outR = inR;
}

@ -0,0 +1,43 @@
// 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_dry.h
//
// An FX that does nothing but used to generalize the processing.
// Author: Vincent Gauché
//
#pragma once
#include "fx_components.h"
class Dry : public FXElement
{
DISALLOW_COPY_AND_ASSIGN(Dry);
public:
Dry(float32_t sampling_rate);
virtual ~Dry();
virtual void reset() override;
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
IMPLEMENT_DUMP(
out << "START " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
)
IMPLEMENT_INSPECT(
return 0u;
)
};

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

@ -69,14 +69,13 @@ void Orbitone::processSample(float32_t inL, float32_t inR, float32_t& outL, floa
float32_t wet = 0.0f;
c.directWrite(inL, line_l);
c.directWrite(inR, line_r);
c.interpolate(line_l, mod_1 + 1024, 0.33f);
c.interpolate(line_l, mod_2 + 1024, 0.33f);
c.interpolate(line_r, mod_3 + 1024, 0.33f);
c.writeAndLoad(wet, 0.0f);
outL = wet * this->OutputLevelCorrector;
c.directWrite(inR, line_r);
c.interpolate(line_r, mod_1 + 1024, 0.33f);
c.interpolate(line_r, mod_2 + 1024, 0.33f);
c.interpolate(line_l, mod_3 + 1024, 0.33f);

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

@ -0,0 +1,104 @@
#include "fx_pitch_shifter.h"
#include "fx_shimmer_helper.h"
#include <cmath>
#include <algorithm>
#define ONE_POLE(out, in, coefficient) out += (coefficient) * ((in) - out);
#define TAIL , -1
PitchShifter::PitchShifter(float32_t sampling_rate, float32_t transpose_boundary) :
FXElement(sampling_rate),
engine_(sampling_rate),
TransposeBoundary(transpose_boundary),
phase_(0.0f),
transpose_(0.0f),
ratio_(0.0f),
size_(-1.0f),
sample_size_(0.0f)
{
this->setTranspose(0.0f);
this->setSize(0.5f);
}
PitchShifter::~PitchShifter()
{
}
void PitchShifter::reset()
{
this->engine_.reset();
}
void PitchShifter::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
typedef Engine::Reserve<2047, Engine::Reserve<2047> > Memory;
Engine::DelayLine<Memory, 0> left;
Engine::DelayLine<Memory, 1> right;
Engine::Context c;
this->engine_.start(&c);
this->phase_ += (1.0f - this->ratio_) / this->sample_size_;
if(this->phase_ >= 1.0f)
{
phase_ -= 1.0f;
}
if(this->phase_ <= 0.0f)
{
this->phase_ += 1.0f;
}
float tri = 2.0f * (this->phase_ >= 0.5f ? 1.0f - phase_ : phase_);
float phase = this->phase_ * this->sample_size_;
float half = phase + this->sample_size_ * 0.5f;
if(half >= this->sample_size_)
{
half -= this->sample_size_;
}
c.load(inL);
c.writeAndLoad(left, 0.0f);
c.interpolate(left, phase, tri);
c.interpolate(left, half, 1.0f - tri);
c.writeAndLoad(outL, 0.0f);
c.load(inR);
c.writeAndLoad(right, 0.0f);
c.interpolate(right, phase, tri);
c.interpolate(right, half, 1.0f - tri);
c.writeAndLoad(outR, 0.0f);
}
void PitchShifter::setTranspose(float32_t transpose)
{
transpose = constrain(transpose, -this->TransposeBoundary, this->TransposeBoundary);
if(this->transpose_ != transpose)
{
this->transpose_ = transpose;
this->ratio_ = semitoneToRatio(transpose);
}
}
float32_t PitchShifter::getTranspose() const
{
return this->transpose_;
}
void PitchShifter::setSize(float32_t size)
{
size = constrain(size, 0.0f, 1.0f);
if(size != this->size_)
{
this->size_ = size;
float32_t target_size = 128.0f + (2047.0f - 128.0f) * size * size * size;
ONE_POLE(this->sample_size_, target_size, 0.05f);
}
}
float32_t PitchShifter::getSize() const
{
return this->size_;
}

@ -0,0 +1,115 @@
// 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_reverb3.h
//
// 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
#include "fx_components.h"
#include "fx_engine.hpp"
#define PITCH_SHIFTER_BUFFER_SIZE 4096
#define PITCH_SHIFTER_TRANSPOSE_BOUNDARY 36.0f
class PitchShifter : public FXElement
{
DISALLOW_COPY_AND_ASSIGN(PitchShifter);
public:
PitchShifter(float32_t sampling_rate, float32_t transpose_boundary = PITCH_SHIFTER_TRANSPOSE_BOUNDARY);
virtual ~PitchShifter();
virtual void reset() override;
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
void setTranspose(float32_t transpose);
float32_t getTranspose() const;
void setSize(float32_t size);
float32_t getSize() const;
private:
typedef FxEngine<PITCH_SHIFTER_BUFFER_SIZE, Format::FORMAT_FLOAT32, false> Engine;
Engine engine_;
const float32_t TransposeBoundary;
float32_t phase_;
float32_t transpose_;
float32_t ratio_;
float32_t size_;
float32_t sample_size_;
IMPLEMENT_DUMP(
const size_t space = 12;
const size_t precision = 6;
std::stringstream ss;
out << "START " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
SS_RESET(ss, precision, std::left);
SS__TEXT(ss, ' ', space, std::left, '|', "phase_");
SS__TEXT(ss, ' ', space, std::left, '|', "transpose_");
SS__TEXT(ss, ' ', space, std::left, '|', "ratio_");
SS__TEXT(ss, ' ', space, std::left, '|', "size_");
SS__TEXT(ss, ' ', space, std::left, '|', "sample_size_");
out << "\t" << ss.str() << std::endl;
SS_RESET(ss, precision, std::left);
SS_SPACE(ss, '-', space, std::left, '+');
SS_SPACE(ss, '-', space, std::left, '+');
SS_SPACE(ss, '-', space, std::left, '+');
SS_SPACE(ss, '-', space, std::left, '+');
SS_SPACE(ss, '-', space, std::left, '+');
SS_SPACE(ss, '-', space, std::left, '+');
out << "\t" << ss.str() << std::endl;
SS_RESET(ss, precision, std::left);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->phase_);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->transpose_);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->ratio_);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->size_);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->sample_size_);
out << "\t" << ss.str() << std::endl;
if(deepInspection)
{
this->engine_.dump(out, deepInspection, tag + ".engine_");
}
out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
)
IMPLEMENT_INSPECT(
size_t nb_errors = 0u;
nb_errors += inspector(tag + ".phase_", this->phase_, 0.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".transpose_", this->transpose_, -PITCH_SHIFTER_TRANSPOSE_BOUNDARY, PITCH_SHIFTER_TRANSPOSE_BOUNDARY, deepInspection);
nb_errors += inspector(tag + ".ratio_", this->ratio_, 0.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".size_", this->size_, 0.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".sample_size_", this->sample_size_, 0.0f, 1.0f, deepInspection);
if(deepInspection)
{
nb_errors += this->engine_.inspect(inspector, deepInspection, tag + ".engine_");
}
return nb_errors;
)
};

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

@ -0,0 +1,135 @@
#include "fx_shimmer_helper.h"
const float lut_pitch_ratio_high[] = {
6.151958251e-04, 6.517772725e-04, 6.905339660e-04, 7.315952524e-04,
7.750981699e-04, 8.211879055e-04, 8.700182794e-04, 9.217522585e-04,
9.765625000e-04, 1.034631928e-03, 1.096154344e-03, 1.161335073e-03,
1.230391650e-03, 1.303554545e-03, 1.381067932e-03, 1.463190505e-03,
1.550196340e-03, 1.642375811e-03, 1.740036559e-03, 1.843504517e-03,
1.953125000e-03, 2.069263856e-03, 2.192308688e-03, 2.322670146e-03,
2.460783301e-03, 2.607109090e-03, 2.762135864e-03, 2.926381010e-03,
3.100392680e-03, 3.284751622e-03, 3.480073118e-03, 3.687009034e-03,
3.906250000e-03, 4.138527712e-03, 4.384617376e-03, 4.645340293e-03,
4.921566601e-03, 5.214218180e-03, 5.524271728e-03, 5.852762019e-03,
6.200785359e-03, 6.569503244e-03, 6.960146235e-03, 7.374018068e-03,
7.812500000e-03, 8.277055425e-03, 8.769234752e-03, 9.290680586e-03,
9.843133202e-03, 1.042843636e-02, 1.104854346e-02, 1.170552404e-02,
1.240157072e-02, 1.313900649e-02, 1.392029247e-02, 1.474803614e-02,
1.562500000e-02, 1.655411085e-02, 1.753846950e-02, 1.858136117e-02,
1.968626640e-02, 2.085687272e-02, 2.209708691e-02, 2.341104808e-02,
2.480314144e-02, 2.627801298e-02, 2.784058494e-02, 2.949607227e-02,
3.125000000e-02, 3.310822170e-02, 3.507693901e-02, 3.716272234e-02,
3.937253281e-02, 4.171374544e-02, 4.419417382e-02, 4.682209615e-02,
4.960628287e-02, 5.255602595e-02, 5.568116988e-02, 5.899214454e-02,
6.250000000e-02, 6.621644340e-02, 7.015387802e-02, 7.432544469e-02,
7.874506562e-02, 8.342749089e-02, 8.838834765e-02, 9.364419230e-02,
9.921256575e-02, 1.051120519e-01, 1.113623398e-01, 1.179842891e-01,
1.250000000e-01, 1.324328868e-01, 1.403077560e-01, 1.486508894e-01,
1.574901312e-01, 1.668549818e-01, 1.767766953e-01, 1.872883846e-01,
1.984251315e-01, 2.102241038e-01, 2.227246795e-01, 2.359685782e-01,
2.500000000e-01, 2.648657736e-01, 2.806155121e-01, 2.973017788e-01,
3.149802625e-01, 3.337099635e-01, 3.535533906e-01, 3.745767692e-01,
3.968502630e-01, 4.204482076e-01, 4.454493591e-01, 4.719371563e-01,
5.000000000e-01, 5.297315472e-01, 5.612310242e-01, 5.946035575e-01,
6.299605249e-01, 6.674199271e-01, 7.071067812e-01, 7.491535384e-01,
7.937005260e-01, 8.408964153e-01, 8.908987181e-01, 9.438743127e-01,
1.000000000e+00, 1.059463094e+00, 1.122462048e+00, 1.189207115e+00,
1.259921050e+00, 1.334839854e+00, 1.414213562e+00, 1.498307077e+00,
1.587401052e+00, 1.681792831e+00, 1.781797436e+00, 1.887748625e+00,
2.000000000e+00, 2.118926189e+00, 2.244924097e+00, 2.378414230e+00,
2.519842100e+00, 2.669679708e+00, 2.828427125e+00, 2.996614154e+00,
3.174802104e+00, 3.363585661e+00, 3.563594873e+00, 3.775497251e+00,
4.000000000e+00, 4.237852377e+00, 4.489848193e+00, 4.756828460e+00,
5.039684200e+00, 5.339359417e+00, 5.656854249e+00, 5.993228308e+00,
6.349604208e+00, 6.727171322e+00, 7.127189745e+00, 7.550994501e+00,
8.000000000e+00, 8.475704755e+00, 8.979696386e+00, 9.513656920e+00,
1.007936840e+01, 1.067871883e+01, 1.131370850e+01, 1.198645662e+01,
1.269920842e+01, 1.345434264e+01, 1.425437949e+01, 1.510198900e+01,
1.600000000e+01, 1.695140951e+01, 1.795939277e+01, 1.902731384e+01,
2.015873680e+01, 2.135743767e+01, 2.262741700e+01, 2.397291323e+01,
2.539841683e+01, 2.690868529e+01, 2.850875898e+01, 3.020397801e+01,
3.200000000e+01, 3.390281902e+01, 3.591878555e+01, 3.805462768e+01,
4.031747360e+01, 4.271487533e+01, 4.525483400e+01, 4.794582646e+01,
5.079683366e+01, 5.381737058e+01, 5.701751796e+01, 6.040795601e+01,
6.400000000e+01, 6.780563804e+01, 7.183757109e+01, 7.610925536e+01,
8.063494719e+01, 8.542975067e+01, 9.050966799e+01, 9.589165292e+01,
1.015936673e+02, 1.076347412e+02, 1.140350359e+02, 1.208159120e+02,
1.280000000e+02, 1.356112761e+02, 1.436751422e+02, 1.522185107e+02,
1.612698944e+02, 1.708595013e+02, 1.810193360e+02, 1.917833058e+02,
2.031873347e+02, 2.152694823e+02, 2.280700718e+02, 2.416318240e+02,
2.560000000e+02, 2.712225522e+02, 2.873502844e+02, 3.044370214e+02,
3.225397888e+02, 3.417190027e+02, 3.620386720e+02, 3.835666117e+02,
4.063746693e+02, 4.305389646e+02, 4.561401437e+02, 4.832636481e+02,
5.120000000e+02, 5.424451043e+02, 5.747005687e+02, 6.088740429e+02,
6.450795775e+02, 6.834380053e+02, 7.240773439e+02, 7.671332234e+02,
8.127493386e+02, 8.610779292e+02, 9.122802874e+02, 9.665272962e+02,
1.024000000e+03, 1.084890209e+03, 1.149401137e+03, 1.217748086e+03,
1.290159155e+03, 1.366876011e+03, 1.448154688e+03, 1.534266447e+03,
};
const float lut_pitch_ratio_low[] = {
1.000000000e+00, 1.000225659e+00, 1.000451370e+00, 1.000677131e+00,
1.000902943e+00, 1.001128806e+00, 1.001354720e+00, 1.001580685e+00,
1.001806701e+00, 1.002032768e+00, 1.002258886e+00, 1.002485055e+00,
1.002711275e+00, 1.002937546e+00, 1.003163868e+00, 1.003390242e+00,
1.003616666e+00, 1.003843141e+00, 1.004069668e+00, 1.004296246e+00,
1.004522874e+00, 1.004749554e+00, 1.004976285e+00, 1.005203068e+00,
1.005429901e+00, 1.005656786e+00, 1.005883722e+00, 1.006110709e+00,
1.006337747e+00, 1.006564836e+00, 1.006791977e+00, 1.007019169e+00,
1.007246412e+00, 1.007473707e+00, 1.007701053e+00, 1.007928450e+00,
1.008155898e+00, 1.008383398e+00, 1.008610949e+00, 1.008838551e+00,
1.009066205e+00, 1.009293910e+00, 1.009521667e+00, 1.009749475e+00,
1.009977334e+00, 1.010205245e+00, 1.010433207e+00, 1.010661221e+00,
1.010889286e+00, 1.011117403e+00, 1.011345571e+00, 1.011573790e+00,
1.011802061e+00, 1.012030384e+00, 1.012258758e+00, 1.012487183e+00,
1.012715661e+00, 1.012944189e+00, 1.013172770e+00, 1.013401401e+00,
1.013630085e+00, 1.013858820e+00, 1.014087607e+00, 1.014316445e+00,
1.014545335e+00, 1.014774277e+00, 1.015003270e+00, 1.015232315e+00,
1.015461411e+00, 1.015690560e+00, 1.015919760e+00, 1.016149011e+00,
1.016378315e+00, 1.016607670e+00, 1.016837077e+00, 1.017066536e+00,
1.017296046e+00, 1.017525609e+00, 1.017755223e+00, 1.017984889e+00,
1.018214607e+00, 1.018444376e+00, 1.018674198e+00, 1.018904071e+00,
1.019133996e+00, 1.019363973e+00, 1.019594002e+00, 1.019824083e+00,
1.020054216e+00, 1.020284401e+00, 1.020514637e+00, 1.020744926e+00,
1.020975266e+00, 1.021205659e+00, 1.021436104e+00, 1.021666600e+00,
1.021897149e+00, 1.022127749e+00, 1.022358402e+00, 1.022589107e+00,
1.022819863e+00, 1.023050672e+00, 1.023281533e+00, 1.023512446e+00,
1.023743411e+00, 1.023974428e+00, 1.024205498e+00, 1.024436619e+00,
1.024667793e+00, 1.024899019e+00, 1.025130297e+00, 1.025361627e+00,
1.025593009e+00, 1.025824444e+00, 1.026055931e+00, 1.026287470e+00,
1.026519061e+00, 1.026750705e+00, 1.026982401e+00, 1.027214149e+00,
1.027445949e+00, 1.027677802e+00, 1.027909707e+00, 1.028141664e+00,
1.028373674e+00, 1.028605736e+00, 1.028837851e+00, 1.029070017e+00,
1.029302237e+00, 1.029534508e+00, 1.029766832e+00, 1.029999209e+00,
1.030231638e+00, 1.030464119e+00, 1.030696653e+00, 1.030929239e+00,
1.031161878e+00, 1.031394569e+00, 1.031627313e+00, 1.031860109e+00,
1.032092958e+00, 1.032325859e+00, 1.032558813e+00, 1.032791820e+00,
1.033024879e+00, 1.033257991e+00, 1.033491155e+00, 1.033724372e+00,
1.033957641e+00, 1.034190964e+00, 1.034424338e+00, 1.034657766e+00,
1.034891246e+00, 1.035124779e+00, 1.035358364e+00, 1.035592003e+00,
1.035825694e+00, 1.036059437e+00, 1.036293234e+00, 1.036527083e+00,
1.036760985e+00, 1.036994940e+00, 1.037228947e+00, 1.037463008e+00,
1.037697121e+00, 1.037931287e+00, 1.038165506e+00, 1.038399777e+00,
1.038634102e+00, 1.038868479e+00, 1.039102910e+00, 1.039337393e+00,
1.039571929e+00, 1.039806518e+00, 1.040041160e+00, 1.040275855e+00,
1.040510603e+00, 1.040745404e+00, 1.040980258e+00, 1.041215165e+00,
1.041450125e+00, 1.041685138e+00, 1.041920204e+00, 1.042155323e+00,
1.042390495e+00, 1.042625720e+00, 1.042860998e+00, 1.043096329e+00,
1.043331714e+00, 1.043567151e+00, 1.043802642e+00, 1.044038185e+00,
1.044273782e+00, 1.044509433e+00, 1.044745136e+00, 1.044980892e+00,
1.045216702e+00, 1.045452565e+00, 1.045688481e+00, 1.045924450e+00,
1.046160473e+00, 1.046396549e+00, 1.046632678e+00, 1.046868860e+00,
1.047105096e+00, 1.047341385e+00, 1.047577727e+00, 1.047814123e+00,
1.048050572e+00, 1.048287074e+00, 1.048523630e+00, 1.048760239e+00,
1.048996902e+00, 1.049233618e+00, 1.049470387e+00, 1.049707210e+00,
1.049944086e+00, 1.050181015e+00, 1.050417999e+00, 1.050655035e+00,
1.050892125e+00, 1.051129269e+00, 1.051366466e+00, 1.051603717e+00,
1.051841021e+00, 1.052078378e+00, 1.052315790e+00, 1.052553255e+00,
1.052790773e+00, 1.053028345e+00, 1.053265971e+00, 1.053503650e+00,
1.053741383e+00, 1.053979169e+00, 1.054217010e+00, 1.054454903e+00,
1.054692851e+00, 1.054930852e+00, 1.055168907e+00, 1.055407016e+00,
1.055645178e+00, 1.055883395e+00, 1.056121664e+00, 1.056359988e+00,
1.056598366e+00, 1.056836797e+00, 1.057075282e+00, 1.057313821e+00,
1.057552413e+00, 1.057791060e+00, 1.058029760e+00, 1.058268515e+00,
1.058507323e+00, 1.058746185e+00, 1.058985101e+00, 1.059224071e+00,
};

@ -0,0 +1,34 @@
// 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"
extern const float lut_pitch_ratio_high[257];
extern const float lut_pitch_ratio_low[257];
inline float32_t semitoneToRatio(float32_t semitones)
{
float32_t pitch = semitones + 128.0f;
MAKE_INTEGRAL_FRACTIONAL(pitch);
return lut_pitch_ratio_high[pitch_integral] * lut_pitch_ratio_low[static_cast<int32_t>(pitch_fractional * 256.0f)];
}

@ -0,0 +1,169 @@
#include "fx_shimmer_reverb.h"
#define TAIL , -1
ShimmerReverb::ShimmerReverb(float32_t sampling_frequency) :
FXElement(sampling_frequency, 1.2f),
pitch_shifter_(sampling_frequency, PITCH_SHIFTER_TRANSPOSE_BOUNDARY),
lp_filter_(sampling_frequency, SVF::FilterMode::SVF_LP),
hp_filter_(sampling_frequency, SVF::FilterMode::SVF_HP),
reverberator_(sampling_frequency),
texture_(0.0f),
lp_cutoff_(0.0f),
hp_cutoff_(0.0f),
lpq_(0.0f),
amount_(0.0f),
feedback_(0.0f),
cutoff_(0.0f)
{
this->setInputGain(0.2f);
this->setDiffusion(0.7f);
this->setCutoff(1.0f);
this->reset();
}
ShimmerReverb::~ShimmerReverb()
{
}
void ShimmerReverb::reset()
{
this->pitch_shifter_.reset();
this->lp_filter_.reset();
this->hp_filter_.reset();
this->reverberator_.reset();
}
void ShimmerReverb::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
this->pitch_shifter_.processSample(inL, inR, outL, outR);
this->lp_filter_.processSample(outL, outR, outL, outR);
this->hp_filter_.processSample(outL, outR, outL, outR);
this->reverberator_.processSample(outL, outR, outL, outR);
outL *= this->OutputLevelCorrector;
outR *= this->OutputLevelCorrector;
}
void ShimmerReverb::setInputGain(float32_t input_gain)
{
this->reverberator_.setInputGain(input_gain);
}
float32_t ShimmerReverb::getInputGain() const
{
return this->reverberator_.getInputGain();
}
void ShimmerReverb::setDiffusion(float32_t diffusion)
{
this->reverberator_.setDiffusion(diffusion);
}
float32_t ShimmerReverb::getDiffusion() const
{
return this->reverberator_.getDiffusion();
}
void ShimmerReverb::setTime(float32_t time)
{
this->reverberator_.setTime(time);
}
float32_t ShimmerReverb::getTime() const
{
return this->reverberator_.getTime();
}
void ShimmerReverb::setReverbAmount(float32_t amount)
{
amount = constrain(amount, 0.0f, 1.0f);
if(this->amount_ != amount)
{
this->amount_ = amount;
this->updateReverberatorCoefficients();
}
}
void ShimmerReverb::setTexture(float32_t texture)
{
texture = constrain(texture, 0.0f, 1.0f);
if(this->texture_ != texture)
{
this->texture_ = texture;
this->updateFilterCoefficients();
}
}
float32_t ShimmerReverb::getTexture() const
{
return this->texture_;
}
void ShimmerReverb::setFeedback(float32_t feedback)
{
feedback = constrain(feedback, 0.0f, 1.0f);
if(this->feedback_ != feedback)
{
this->feedback_ = feedback;
this->updateFilterCoefficients();
this->updateReverberatorCoefficients();
}
}
float32_t ShimmerReverb::getFeedback() const
{
return this->feedback_;
}
void ShimmerReverb::setCutoff(float32_t cutoff)
{
cutoff = constrain(cutoff, 0.0f, 1.0f);
if(this->cutoff_ != cutoff)
{
this->cutoff_ = cutoff;
this->updateFilterCoefficients();
}
}
void ShimmerReverb::updateFilterCoefficients()
{
this->lp_cutoff_ = constrain(0.50f * semitoneToRatio((this->cutoff_ < 0.5f ? this->cutoff_ - 0.5f : 0.0f ) * 216.0f), 0.0f, 0.499f);
this->hp_cutoff_ = constrain(0.25f * semitoneToRatio((this->cutoff_ < 0.5f ? -0.5f : this->cutoff_ - 1.0f) * 216.0f), 0.0f, 0.499f);
this->lpq_ = 1.0f + 3.0f * (1.0f - this->feedback_) * (0.5f - this->lp_cutoff_);
this->lp_filter_.setFQ<SVF::FrequencyApproximation::FrequencyFast>(this->lp_cutoff_, this->lpq_);
this->hp_filter_.setFQ<SVF::FrequencyApproximation::FrequencyFast>(this->hp_cutoff_, 1.0f);
this->reverberator_.setLP(0.6f + 0.37f * this->feedback_);
}
void ShimmerReverb::updateReverberatorCoefficients()
{
float32_t reverb_amount = this->amount_ * 0.95f;
reverb_amount += this->feedback_ * (2.0f - this->feedback_);
reverb_amount = constrain(reverb_amount, 0.0f, 1.0f);
this->setTime(0.35f + 0.63f * reverb_amount);
}
void ShimmerReverb::setPitch(float32_t pitch)
{
this->pitch_shifter_.setTranspose(pitch);
}
float32_t ShimmerReverb::getPitch() const
{
return this->pitch_shifter_.getTranspose();
}
void ShimmerReverb::setSize(float32_t size)
{
this->pitch_shifter_.setSize(size);
}
float32_t ShimmerReverb::getSize() const
{
return this->pitch_shifter_.getSize();
}

@ -0,0 +1,111 @@
// 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_reverb.h
//
// 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
#include "fx_components.h"
#include "fx_svf.h"
#include "fx_shimmer_helper.h"
#include "fx_pitch_shifter.h"
#include "fx_reverberator.h"
class ShimmerReverb : public FXElement
{
DISALLOW_COPY_AND_ASSIGN(ShimmerReverb);
public:
ShimmerReverb(float32_t sampling_rate);
virtual ~ShimmerReverb();
virtual void reset() override;
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
void setInputGain(float32_t input_gain);
float32_t getInputGain() const;
void setDiffusion(float32_t diffusion);
float32_t getDiffusion() const;
void setTime(float32_t time);
float32_t getTime() const;
void setReverbAmount(float32_t amount);
void setTexture(float32_t texture);
float32_t getTexture() const;
void setFeedback(float32_t feedback);
float32_t getFeedback() const;
void setPitch(float32_t pitch);
float32_t getPitch() const;
void setSize(float32_t size);
float32_t getSize() const;
void setCutoff(float32_t cutoff);
float32_t getCutoff() const;
private:
void updateFilterCoefficients();
void updateReverberatorCoefficients();
PitchShifter pitch_shifter_;
SVF lp_filter_;
SVF hp_filter_;
Reverberator reverberator_;
float32_t texture_;
float32_t lp_cutoff_;
float32_t hp_cutoff_;
float32_t lpq_;
float32_t amount_;
float32_t feedback_;
float32_t cutoff_;
IMPLEMENT_DUMP(
out << "START " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
)
IMPLEMENT_INSPECT(
size_t nb_errors = 0u;
nb_errors += inspector(tag + ".texture_", this->texture_, 0.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".lp_cutoff_", this->lp_cutoff_, 0.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".hp_cutoff_", this->hp_cutoff_, 0.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".lpq_", this->lpq_, 0.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".amount_", this->amount_, 0.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".feedback_", this->feedback_, 0.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".cutoff_", this->cutoff_, 0.0f, 1.0f, deepInspection);
if(deepInspection)
{
nb_errors += this->pitch_shifter_.inspect(inspector, deepInspection, tag + ".pitch_shifter_");
nb_errors += this->lp_filter_.inspect(inspector, deepInspection, tag + ".lp_filter_");
nb_errors += this->hp_filter_.inspect(inspector, deepInspection, tag + ".hp_filter_");
nb_errors += this->reverberator_.inspect(inspector, deepInspection, tag + ".reverberator_");
}
return nb_errors;
)
};

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

@ -22,29 +22,35 @@ void Tube::reset()
void Tube::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
float32_t x = inL * this->saturator_factor_;
float32_t abs_x = abs(x);
float32_t sat_x = log(1.0f + abs_x) * this->gain_factor_;
outL = inL > 0 ? sat_x : -sat_x;
x = inR * this->saturator_factor_;
abs_x = abs(x);
sat_x = log(1.0f + abs_x) * this->gain_factor_;
if(inL == 0.0f)
{
outL = 0.0f;
}
else
{
outL = std::tanh(this->saturator_factor_ * inL) * this->gain_factor_;
}
outR = inR > 0 ? sat_x : -sat_x;
if(inR == 0.0f)
{
outR = 0.0f;
}
else
{
outR = std::tanh(this->saturator_factor_ * inR) * this->gain_factor_;
}
}
void Tube::setOverdrive(float32_t overdrive)
{
static const float32_t N = 200.0f;
static const float32_t N = 3.0f;
overdrive = constrain(overdrive, 0.0f, 1.0f);
if(this->overdrive_ != overdrive)
{
this->overdrive_ = overdrive;
this->saturator_factor_ = 1.0f + N * overdrive;
this->gain_factor_ = this->OutputLevelCorrector / log(1.0f + this->saturator_factor_);
this->saturator_factor_ = 1.0 + N * overdrive;
this->gain_factor_ = this->OutputLevelCorrector / std::tanh(this->saturator_factor_);
}
}

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

@ -0,0 +1,104 @@
// 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_unit2.h
//
// Unit of FX that handle the mute parameter
// Author: Vincent Gauché
//
#pragma once
#include "fx_components.h"
class FXUnitModule2
{
DISALLOW_COPY_AND_ASSIGN(FXUnitModule2);
public:
FXUnitModule2(bool mute = false)
{
this->setMute(mute);
}
virtual ~FXUnitModule2()
{
}
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) = 0;
inline void setMute(bool mute = false)
{
this->mute_ = mute;
}
inline bool isMute() const
{
return this->mute_;
}
protected:
bool mute_;
};
template<typename _FXElement>
class FXUnit2 : public virtual FXUnitModule2, public virtual _FXElement
{
DISALLOW_COPY_AND_ASSIGN(FXUnit2);
public:
FXUnit2(float32_t sampling_rate, bool mute = false) :
FXUnitModule2(mute),
_FXElement(sampling_rate),
is_reset_(false)
{
this->setMute(mute);
}
virtual ~FXUnit2()
{
}
inline void reset() override
{
if(!this->is_reset_)
{
_FXElement::reset();
this->is_reset_ = true;
}
}
inline void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override
{
if(this->isMute())
{
this->reset();
outL = 0.0f;
outR = 0.0f;
}
else if(this->bypassFXProcess())
{
outL = inL;
outR = inR;
}
else
{
this->is_reset_ = false;
_FXElement::processSample(inL, inR, outL, outR);
}
}
private:
bool is_reset_;
};

@ -29,41 +29,44 @@
#include <assert.h>
#include "userinterface.h"
LOGMODULE ("mididevice");
#define MIDI_NOTE_OFF 0b1000
#define MIDI_NOTE_ON 0b1001
#define MIDI_AFTERTOUCH 0b1010 // TODO
#define MIDI_CHANNEL_AFTERTOUCH 0b1101 // right now Synth_Dexed just manage Channel Aftertouch not Polyphonic AT -> 0b1010
#define MIDI_CONTROL_CHANGE 0b1011
#define MIDI_CC_BANK_SELECT_MSB 0
#define MIDI_CC_MODULATION 1
#define MIDI_CC_BREATH_CONTROLLER 2
#define MIDI_CC_FOOT_PEDAL 4
#define MIDI_CC_VOLUME 7
#define MIDI_CC_PAN_POSITION 10
#define MIDI_CC_BANK_SELECT_LSB 32
#define MIDI_CC_BANK_SUSTAIN 64
#define MIDI_CC_RESONANCE 71
#define MIDI_CC_FREQUENCY_CUTOFF 74
#define MIDI_CC_REVERB_LEVEL 91
#define MIDI_CC_DETUNE_LEVEL 94
#define MIDI_CC_ALL_SOUND_OFF 120
#define MIDI_CC_ALL_NOTES_OFF 123
#define MIDI_PROGRAM_CHANGE 0b1100
#define MIDI_PITCH_BEND 0b1110
#define MIDI_SYSTEM_EXCLUSIVE_BEGIN 0xF0
#define MIDI_SYSTEM_EXCLUSIVE_END 0xF7
#define MIDI_TIMING_CLOCK 0xF8
#define MIDI_ACTIVE_SENSING 0xFE
LOGMODULE("mididevice");
#define MIDI_NOTE_OFF 0b1000
#define MIDI_NOTE_ON 0b1001
#define MIDI_AFTERTOUCH 0b1010 // TODO
#define MIDI_CHANNEL_AFTERTOUCH 0b1101 // right now Synth_Dexed just manage Channel Aftertouch not Polyphonic AT -> 0b1010
#define MIDI_CONTROL_CHANGE 0b1011
#define MIDI_CC_BANK_SELECT_MSB 0
#define MIDI_CC_MODULATION 1
#define MIDI_CC_BREATH_CONTROLLER 2
#define MIDI_CC_FOOT_PEDAL 4
#define MIDI_CC_VOLUME 7
#define MIDI_CC_PAN_POSITION 10
#define MIDI_CC_BANK_SELECT_LSB 32
#define MIDI_CC_BANK_SUSTAIN 64
#define MIDI_CC_RESONANCE 71
#define MIDI_CC_FREQUENCY_CUTOFF 74
#define MIDI_CC_REVERB_LEVEL 91
#define MIDI_CC_ORBITONE_LEVEL 92 // added with mixing console
#define MIDI_CC_CHORUS_LEVEL 93 // added with mixing console
#define MIDI_CC_DETUNE_LEVEL 94
#define MIDI_CC_PHASER_LEVEL 95 // added with mixing console
#define MIDI_CC_ALL_SOUND_OFF 120
#define MIDI_CC_ALL_NOTES_OFF 123
#define MIDI_PROGRAM_CHANGE 0b1100
#define MIDI_PITCH_BEND 0b1110
#define MIDI_SYSTEM_EXCLUSIVE_BEGIN 0xF0
#define MIDI_SYSTEM_EXCLUSIVE_END 0xF7
#define MIDI_TIMING_CLOCK 0xF8
#define MIDI_ACTIVE_SENSING 0xFE
CMIDIDevice::TDeviceMap CMIDIDevice::s_DeviceMap;
CMIDIDevice::CMIDIDevice (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserInterface *pUI)
: m_pSynthesizer (pSynthesizer),
m_pConfig (pConfig),
m_pUI (pUI)
CMIDIDevice::CMIDIDevice(CMiniDexed *pSynthesizer, CConfig *pConfig, CUserInterface *pUI)
: m_pSynthesizer(pSynthesizer),
m_pConfig(pConfig),
m_pUI(pUI)
{
for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
{
@ -71,94 +74,93 @@ CMIDIDevice::CMIDIDevice (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserInter
}
}
CMIDIDevice::~CMIDIDevice (void)
CMIDIDevice::~CMIDIDevice(void)
{
m_pSynthesizer = 0;
}
void CMIDIDevice::SetChannel (u8 ucChannel, unsigned nTG)
void CMIDIDevice::SetChannel(u8 ucChannel, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
assert(nTG < CConfig::ToneGenerators);
m_ChannelMap[nTG] = ucChannel;
}
u8 CMIDIDevice::GetChannel (unsigned nTG) const
u8 CMIDIDevice::GetChannel(unsigned nTG) const
{
assert (nTG < CConfig::ToneGenerators);
assert(nTG < CConfig::ToneGenerators);
return m_ChannelMap[nTG];
}
void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsigned nCable)
void CMIDIDevice::MIDIMessageHandler(const u8 *pMessage, size_t nLength, unsigned nCable)
{
// The packet contents are just normal MIDI data - see
// https://www.midi.org/specifications/item/table-1-summary-of-midi-message
if (m_pConfig->GetMIDIDumpEnabled ())
if (m_pConfig->GetMIDIDumpEnabled())
{
switch (nLength)
{
case 1:
if ( pMessage[0] != MIDI_TIMING_CLOCK
&& pMessage[0] != MIDI_ACTIVE_SENSING)
if (pMessage[0] != MIDI_TIMING_CLOCK && pMessage[0] != MIDI_ACTIVE_SENSING)
{
printf ("MIDI%u: %02X\n", nCable, (unsigned) pMessage[0]);
printf("MIDI%u: %02X\n", nCable, (unsigned)pMessage[0]);
}
break;
case 2:
printf ("MIDI%u: %02X %02X\n", nCable,
(unsigned) pMessage[0], (unsigned) pMessage[1]);
printf("MIDI%u: %02X %02X\n", nCable,
(unsigned)pMessage[0], (unsigned)pMessage[1]);
break;
case 3:
printf ("MIDI%u: %02X %02X %02X\n", nCable,
(unsigned) pMessage[0], (unsigned) pMessage[1],
(unsigned) pMessage[2]);
printf("MIDI%u: %02X %02X %02X\n", nCable,
(unsigned)pMessage[0], (unsigned)pMessage[1],
(unsigned)pMessage[2]);
break;
default:
switch(pMessage[0])
switch (pMessage[0])
{
case MIDI_SYSTEM_EXCLUSIVE_BEGIN:
printf("MIDI%u: SysEx data length: [%d]:",nCable, uint16_t(nLength));
for (uint16_t i = 0; i < nLength; i++)
{
if((i % 16) == 0)
printf("\n%04d:",i);
printf(" 0x%02x",pMessage[i]);
}
printf("\n");
break;
default:
printf("MIDI%u: Unhandled MIDI event type %0x02x\n",nCable,pMessage[0]);
case MIDI_SYSTEM_EXCLUSIVE_BEGIN:
printf("MIDI%u: SysEx data length: [%d]:", nCable, uint16_t(nLength));
for (uint16_t i = 0; i < nLength; i++)
{
if ((i % 16) == 0)
printf("\n%04d:", i);
printf(" 0x%02x", pMessage[i]);
}
printf("\n");
break;
default:
printf("MIDI%u: Unhandled MIDI event type %0x02x\n", nCable, pMessage[0]);
}
break;
}
}
// Only for debugging:
/*
if(pMessage[0]==MIDI_SYSTEM_EXCLUSIVE_BEGIN)
{
printf("MIDI%u: SysEx data length: [%d]:",nCable, uint16_t(nLength));
for (uint16_t i = 0; i < nLength; i++)
/*
if(pMessage[0]==MIDI_SYSTEM_EXCLUSIVE_BEGIN)
{
if((i % 16) == 0)
printf("\n%04d:",i);
printf(" 0x%02x",pMessage[i]);
printf("MIDI%u: SysEx data length: [%d]:",nCable, uint16_t(nLength));
for (uint16_t i = 0; i < nLength; i++)
{
if((i % 16) == 0)
printf("\n%04d:",i);
printf(" 0x%02x",pMessage[i]);
}
printf("\n");
}
printf("\n");
}
*/
*/
// Handle MIDI Thru
if (m_DeviceName.compare (m_pConfig->GetMIDIThruIn ()) == 0)
if (m_DeviceName.compare(m_pConfig->GetMIDIThruIn()) == 0)
{
TDeviceMap::const_iterator Iterator;
Iterator = s_DeviceMap.find (m_pConfig->GetMIDIThruOut ());
if (Iterator != s_DeviceMap.end ())
Iterator = s_DeviceMap.find(m_pConfig->GetMIDIThruOut());
if (Iterator != s_DeviceMap.end())
{
Iterator->second->Send (pMessage, nLength, nCable);
Iterator->second->Send(pMessage, nLength, nCable);
}
}
@ -168,17 +170,17 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
return;
}
m_MIDISpinLock.Acquire ();
m_MIDISpinLock.Acquire();
u8 ucStatus = pMessage[0];
u8 ucStatus = pMessage[0];
u8 ucChannel = ucStatus & 0x0F;
u8 ucType = ucStatus >> 4;
u8 ucType = ucStatus >> 4;
// GLOBAL MIDI SYSEX
if (pMessage[0] == MIDI_SYSTEM_EXCLUSIVE_BEGIN && pMessage[3] == 0x04 && pMessage[4] == 0x01 && pMessage[nLength-1] == MIDI_SYSTEM_EXCLUSIVE_END) // MASTER VOLUME
if (pMessage[0] == MIDI_SYSTEM_EXCLUSIVE_BEGIN && pMessage[3] == 0x04 && pMessage[4] == 0x01 && pMessage[nLength - 1] == MIDI_SYSTEM_EXCLUSIVE_END) // MASTER VOLUME
{
float32_t nMasterVolume=((pMessage[5] & 0x7c) & ((pMessage[6] & 0x7c) <<7))/(1<<14);
LOGNOTE("Master volume: %f",nMasterVolume);
float32_t nMasterVolume = ((pMessage[5] & 0x7c) & ((pMessage[6] & 0x7c) << 7)) / (1 << 14);
LOGNOTE("Master volume: %f", nMasterVolume);
m_pSynthesizer->setMasterVolume(nMasterVolume);
}
else
@ -193,7 +195,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
{
break;
}
m_pUI->UIMIDICmdHandler (ucChannel, ucStatus & 0xF0, pMessage[1], pMessage[2]);
m_pUI->UIMIDICmdHandler(ucChannel, ucStatus & 0xF0, pMessage[1], pMessage[2]);
break;
}
@ -206,14 +208,13 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
uint8_t ucSysExChannel = (pMessage[2] & 0x0F);
if (m_ChannelMap[nTG] == ucSysExChannel || m_ChannelMap[nTG] == OmniMode)
{
LOGNOTE("MIDI-SYSEX: channel: %u, len: %u, TG: %u",m_ChannelMap[nTG],nLength,nTG);
LOGNOTE("MIDI-SYSEX: channel: %u, len: %u, TG: %u", m_ChannelMap[nTG], nLength, nTG);
HandleSystemExclusive(pMessage, nLength, nCable, nTG);
}
}
else
{
if ( m_ChannelMap[nTG] == ucChannel
|| m_ChannelMap[nTG] == OmniMode)
if (m_ChannelMap[nTG] == ucChannel || m_ChannelMap[nTG] == OmniMode)
{
switch (ucType)
{
@ -227,13 +228,13 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
{
if (pMessage[2] <= 127)
{
m_pSynthesizer->keydown (pMessage[1],
pMessage[2], nTG);
m_pSynthesizer->keydown(pMessage[1],
pMessage[2], nTG);
}
}
else
{
m_pSynthesizer->keyup (pMessage[1], nTG);
m_pSynthesizer->keyup(pMessage[1], nTG);
}
break;
@ -243,13 +244,13 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
break;
}
m_pSynthesizer->keyup (pMessage[1], nTG);
m_pSynthesizer->keyup(pMessage[1], nTG);
break;
case MIDI_CHANNEL_AFTERTOUCH:
m_pSynthesizer->setAftertouch (pMessage[1], nTG);
m_pSynthesizer->ControllersRefresh (nTG);
m_pSynthesizer->setAftertouch(pMessage[1], nTG);
m_pSynthesizer->ControllersRefresh(nTG);
break;
case MIDI_CONTROL_CHANGE:
@ -261,75 +262,91 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
switch (pMessage[1])
{
case MIDI_CC_MODULATION:
m_pSynthesizer->setModWheel (pMessage[2], nTG);
m_pSynthesizer->ControllersRefresh (nTG);
m_pSynthesizer->setModWheel(pMessage[2], nTG);
m_pSynthesizer->ControllersRefresh(nTG);
break;
case MIDI_CC_FOOT_PEDAL:
m_pSynthesizer->setFootController (pMessage[2], nTG);
m_pSynthesizer->ControllersRefresh (nTG);
m_pSynthesizer->setFootController(pMessage[2], nTG);
m_pSynthesizer->ControllersRefresh(nTG);
break;
case MIDI_CC_BREATH_CONTROLLER:
m_pSynthesizer->setBreathController (pMessage[2], nTG);
m_pSynthesizer->ControllersRefresh (nTG);
m_pSynthesizer->setBreathController(pMessage[2], nTG);
m_pSynthesizer->ControllersRefresh(nTG);
break;
case MIDI_CC_VOLUME:
m_pSynthesizer->SetVolume (pMessage[2], nTG);
m_pSynthesizer->SetVolume(pMessage[2], nTG);
break;
case MIDI_CC_PAN_POSITION:
m_pSynthesizer->SetPan (pMessage[2], nTG);
m_pSynthesizer->SetPan(pMessage[2], nTG);
break;
case MIDI_CC_BANK_SELECT_MSB:
m_pSynthesizer->BankSelectMSB (pMessage[2], nTG);
m_pSynthesizer->BankSelectMSB(pMessage[2], nTG);
break;
case MIDI_CC_BANK_SELECT_LSB:
m_pSynthesizer->BankSelectLSB (pMessage[2], nTG);
m_pSynthesizer->BankSelectLSB(pMessage[2], nTG);
break;
case MIDI_CC_BANK_SUSTAIN:
m_pSynthesizer->setSustain (pMessage[2] >= 64, nTG);
m_pSynthesizer->setSustain(pMessage[2] >= 64, nTG);
break;
case MIDI_CC_RESONANCE:
m_pSynthesizer->SetResonance (maplong (pMessage[2], 0, 127, 0, 99), nTG);
m_pSynthesizer->SetResonance(maplong(pMessage[2], 0, 127, 0, 99), nTG);
break;
case MIDI_CC_FREQUENCY_CUTOFF:
m_pSynthesizer->SetCutoff (maplong (pMessage[2], 0, 127, 0, 99), nTG);
m_pSynthesizer->SetCutoff(maplong(pMessage[2], 0, 127, 0, 99), nTG);
break;
case MIDI_CC_REVERB_LEVEL:
m_pSynthesizer->SetReverbSend (maplong (pMessage[2], 0, 127, 0, 99), nTG);
#if defined(MIXING_CONSOLE_ENABLE)
this->m_pSynthesizer->setMixingConsoleSendLevel(nTG, MixerOutput::FX_PlateReverb, maplong(pMessage[2], 0, 127, 0, 99));
#elif defined(PLATE_REVERB_ENABLE)
m_pSynthesizer->SetReverbSend(maplong(pMessage[2], 0, 127, 0, 99), nTG);
#endif
break;
#if defined(MIXING_CONSOLE_ENABLE)
case MIDI_CC_ORBITONE_LEVEL:
this->m_pSynthesizer->setMixingConsoleSendLevel(nTG, MixerOutput::FX_Orbitone, maplong(pMessage[2], 0, 127, 0, 99));
break;
case MIDI_CC_CHORUS_LEVEL:
this->m_pSynthesizer->setMixingConsoleSendLevel(nTG, MixerOutput::FX_Chorus, maplong(pMessage[2], 0, 127, 0, 99));
break;
case MIDI_CC_PHASER_LEVEL:
this->m_pSynthesizer->setMixingConsoleSendLevel(nTG, MixerOutput::FX_Phaser, maplong(pMessage[2], 0, 127, 0, 99));
break;
#endif
case MIDI_CC_DETUNE_LEVEL:
if (pMessage[2] == 0)
{
// "0 to 127, with 0 being no celeste (detune) effect applied at all."
m_pSynthesizer->SetMasterTune (0, nTG);
m_pSynthesizer->SetMasterTune(0, nTG);
}
else
{
m_pSynthesizer->SetMasterTune (maplong (pMessage[2], 1, 127, -99, 99), nTG);
m_pSynthesizer->SetMasterTune(maplong(pMessage[2], 1, 127, -99, 99), nTG);
}
break;
case MIDI_CC_ALL_SOUND_OFF:
m_pSynthesizer->panic (pMessage[2], nTG);
m_pSynthesizer->panic(pMessage[2], nTG);
break;
case MIDI_CC_ALL_NOTES_OFF:
// As per "MIDI 1.0 Detailed Specification" v4.2
// From "ALL NOTES OFF" states:
// "Receivers should ignore an All Notes Off message while Omni is on (Modes 1 & 2)"
if (!m_pConfig->GetIgnoreAllNotesOff () && m_ChannelMap[nTG] != OmniMode)
if (!m_pConfig->GetIgnoreAllNotesOff() && m_ChannelMap[nTG] != OmniMode)
{
m_pSynthesizer->notesOff (pMessage[2], nTG);
m_pSynthesizer->notesOff(pMessage[2], nTG);
}
break;
}
@ -337,22 +354,24 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
case MIDI_PROGRAM_CHANGE:
// do program change only if enabled in config
if( m_pConfig->GetMIDIRXProgramChange() )
m_pSynthesizer->ProgramChange (pMessage[1], nTG);
if (m_pConfig->GetMIDIRXProgramChange())
m_pSynthesizer->ProgramChange(pMessage[1], nTG);
break;
case MIDI_PITCH_BEND: {
case MIDI_PITCH_BEND:
{
if (nLength < 3)
{
break;
}
s16 nValue = pMessage[1];
nValue |= (s16) pMessage[2] << 7;
nValue |= (s16)pMessage[2] << 7;
nValue -= 0x2000;
m_pSynthesizer->setPitchbend (nValue, nTG);
} break;
m_pSynthesizer->setPitchbend(nValue, nTG);
}
break;
default:
break;
@ -361,161 +380,161 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
}
}
}
m_MIDISpinLock.Release ();
m_MIDISpinLock.Release();
}
void CMIDIDevice::AddDevice (const char *pDeviceName)
void CMIDIDevice::AddDevice(const char *pDeviceName)
{
assert (pDeviceName);
assert(pDeviceName);
assert (m_DeviceName.empty ());
assert(m_DeviceName.empty());
m_DeviceName = pDeviceName;
assert (!m_DeviceName.empty ());
assert(!m_DeviceName.empty());
s_DeviceMap.insert (std::pair<std::string, CMIDIDevice *> (pDeviceName, this));
s_DeviceMap.insert(std::pair<std::string, CMIDIDevice *>(pDeviceName, this));
}
void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG)
void CMIDIDevice::HandleSystemExclusive(const uint8_t *pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG)
{
int16_t sysex_return;
sysex_return = m_pSynthesizer->checkSystemExclusive(pMessage, nLength, nTG);
LOGDBG("SYSEX handler return value: %d", sysex_return);
switch (sysex_return)
{
case -1:
LOGERR("SysEx end status byte not detected.");
break;
case -2:
LOGERR("SysEx vendor not Yamaha.");
break;
case -3:
LOGERR("Unknown SysEx parameter change.");
break;
case -4:
LOGERR("Unknown SysEx voice or function.");
break;
case -5:
LOGERR("Not a SysEx voice bulk upload.");
break;
case -6:
LOGERR("Wrong length for SysEx voice bulk upload (not 155).");
break;
case -7:
LOGERR("Checksum error for one voice.");
break;
case -8:
LOGERR("Not a SysEx bank bulk upload.");
break;
case -9:
LOGERR("Wrong length for SysEx bank bulk upload (not 4096).");
case -10:
LOGERR("Checksum error for bank.");
break;
case -11:
LOGERR("Unknown SysEx message.");
break;
case 64:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
m_pSynthesizer->setMonoMode(pMessage[5],nTG);
break;
case 65:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
m_pSynthesizer->setPitchbendRange(pMessage[5],nTG);
break;
case 66:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
m_pSynthesizer->setPitchbendStep(pMessage[5],nTG);
break;
case 67:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
m_pSynthesizer->setPortamentoMode(pMessage[5],nTG);
break;
case 68:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
m_pSynthesizer->setPortamentoGlissando(pMessage[5],nTG);
break;
case 69:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
m_pSynthesizer->setPortamentoTime(pMessage[5],nTG);
break;
case 70:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
m_pSynthesizer->setModWheelRange(pMessage[5],nTG);
break;
case 71:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
m_pSynthesizer->setModWheelTarget(pMessage[5],nTG);
break;
case 72:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
m_pSynthesizer->setFootControllerRange(pMessage[5],nTG);
break;
case 73:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
m_pSynthesizer->setFootControllerTarget(pMessage[5],nTG);
break;
case 74:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
m_pSynthesizer->setBreathControllerRange(pMessage[5],nTG);
break;
case 75:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
m_pSynthesizer->setBreathControllerTarget(pMessage[5],nTG);
break;
case 76:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
m_pSynthesizer->setAftertouchRange(pMessage[5],nTG);
break;
case 77:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
m_pSynthesizer->setAftertouchTarget(pMessage[5],nTG);
break;
case 100:
// load sysex-data into voice memory
LOGDBG("One Voice bulk upload");
m_pSynthesizer->loadVoiceParameters(pMessage,nTG);
break;
case 200:
LOGDBG("Bank bulk upload.");
//TODO: add code for storing a bank bulk upload
LOGNOTE("Currently code for storing a bulk bank upload is missing!");
break;
default:
if(sysex_return >= 300 && sysex_return < 500)
{
LOGDBG("SysEx voice parameter change: Parameter %d value: %d",pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5]);
m_pSynthesizer->setVoiceDataElement(pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5],nTG);
switch(pMessage[4] + ((pMessage[3] & 0x03) * 128))
{
case 134:
m_pSynthesizer->notesOff(0,nTG);
break;
}
}
else if(sysex_return >= 500 && sysex_return < 600)
{
LOGDBG("SysEx send voice %u request",sysex_return-500);
SendSystemExclusiveVoice(sysex_return-500, nCable, nTG);
}
break;
}
int16_t sysex_return;
sysex_return = m_pSynthesizer->checkSystemExclusive(pMessage, nLength, nTG);
LOGDBG("SYSEX handler return value: %d", sysex_return);
switch (sysex_return)
{
case -1:
LOGERR("SysEx end status byte not detected.");
break;
case -2:
LOGERR("SysEx vendor not Yamaha.");
break;
case -3:
LOGERR("Unknown SysEx parameter change.");
break;
case -4:
LOGERR("Unknown SysEx voice or function.");
break;
case -5:
LOGERR("Not a SysEx voice bulk upload.");
break;
case -6:
LOGERR("Wrong length for SysEx voice bulk upload (not 155).");
break;
case -7:
LOGERR("Checksum error for one voice.");
break;
case -8:
LOGERR("Not a SysEx bank bulk upload.");
break;
case -9:
LOGERR("Wrong length for SysEx bank bulk upload (not 4096).");
case -10:
LOGERR("Checksum error for bank.");
break;
case -11:
LOGERR("Unknown SysEx message.");
break;
case 64:
LOGDBG("SysEx Function parameter change: %d Value %d", pMessage[4], pMessage[5]);
m_pSynthesizer->setMonoMode(pMessage[5], nTG);
break;
case 65:
LOGDBG("SysEx Function parameter change: %d Value %d", pMessage[4], pMessage[5]);
m_pSynthesizer->setPitchbendRange(pMessage[5], nTG);
break;
case 66:
LOGDBG("SysEx Function parameter change: %d Value %d", pMessage[4], pMessage[5]);
m_pSynthesizer->setPitchbendStep(pMessage[5], nTG);
break;
case 67:
LOGDBG("SysEx Function parameter change: %d Value %d", pMessage[4], pMessage[5]);
m_pSynthesizer->setPortamentoMode(pMessage[5], nTG);
break;
case 68:
LOGDBG("SysEx Function parameter change: %d Value %d", pMessage[4], pMessage[5]);
m_pSynthesizer->setPortamentoGlissando(pMessage[5], nTG);
break;
case 69:
LOGDBG("SysEx Function parameter change: %d Value %d", pMessage[4], pMessage[5]);
m_pSynthesizer->setPortamentoTime(pMessage[5], nTG);
break;
case 70:
LOGDBG("SysEx Function parameter change: %d Value %d", pMessage[4], pMessage[5]);
m_pSynthesizer->setModWheelRange(pMessage[5], nTG);
break;
case 71:
LOGDBG("SysEx Function parameter change: %d Value %d", pMessage[4], pMessage[5]);
m_pSynthesizer->setModWheelTarget(pMessage[5], nTG);
break;
case 72:
LOGDBG("SysEx Function parameter change: %d Value %d", pMessage[4], pMessage[5]);
m_pSynthesizer->setFootControllerRange(pMessage[5], nTG);
break;
case 73:
LOGDBG("SysEx Function parameter change: %d Value %d", pMessage[4], pMessage[5]);
m_pSynthesizer->setFootControllerTarget(pMessage[5], nTG);
break;
case 74:
LOGDBG("SysEx Function parameter change: %d Value %d", pMessage[4], pMessage[5]);
m_pSynthesizer->setBreathControllerRange(pMessage[5], nTG);
break;
case 75:
LOGDBG("SysEx Function parameter change: %d Value %d", pMessage[4], pMessage[5]);
m_pSynthesizer->setBreathControllerTarget(pMessage[5], nTG);
break;
case 76:
LOGDBG("SysEx Function parameter change: %d Value %d", pMessage[4], pMessage[5]);
m_pSynthesizer->setAftertouchRange(pMessage[5], nTG);
break;
case 77:
LOGDBG("SysEx Function parameter change: %d Value %d", pMessage[4], pMessage[5]);
m_pSynthesizer->setAftertouchTarget(pMessage[5], nTG);
break;
case 100:
// load sysex-data into voice memory
LOGDBG("One Voice bulk upload");
m_pSynthesizer->loadVoiceParameters(pMessage, nTG);
break;
case 200:
LOGDBG("Bank bulk upload.");
// TODO: add code for storing a bank bulk upload
LOGNOTE("Currently code for storing a bulk bank upload is missing!");
break;
default:
if (sysex_return >= 300 && sysex_return < 500)
{
LOGDBG("SysEx voice parameter change: Parameter %d value: %d", pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5]);
m_pSynthesizer->setVoiceDataElement(pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5], nTG);
switch (pMessage[4] + ((pMessage[3] & 0x03) * 128))
{
case 134:
m_pSynthesizer->notesOff(0, nTG);
break;
}
}
else if (sysex_return >= 500 && sysex_return < 600)
{
LOGDBG("SysEx send voice %u request", sysex_return - 500);
SendSystemExclusiveVoice(sysex_return - 500, nCable, nTG);
}
break;
}
}
void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable, uint8_t nTG)
{
uint8_t voicedump[163];
uint8_t voicedump[163];
// Get voice sysex dump from TG
m_pSynthesizer->getSysExVoiceDump(voicedump, nTG);
// Get voice sysex dump from TG
m_pSynthesizer->getSysExVoiceDump(voicedump, nTG);
TDeviceMap::const_iterator Iterator;
TDeviceMap::const_iterator Iterator;
// send voice dump to all MIDI interfaces
for(Iterator = s_DeviceMap.begin(); Iterator != s_DeviceMap.end(); ++Iterator)
{
Iterator->second->Send (voicedump, sizeof(voicedump)*sizeof(uint8_t));
// LOGDBG("Send SYSEX voice dump %u to \"%s\"",nVoice,Iterator->first.c_str());
}
// send voice dump to all MIDI interfaces
for (Iterator = s_DeviceMap.begin(); Iterator != s_DeviceMap.end(); ++Iterator)
{
Iterator->second->Send(voicedump, sizeof(voicedump) * sizeof(uint8_t));
// LOGDBG("Send SYSEX voice dump %u to \"%s\"",nVoice,Iterator->first.c_str());
}
}

File diff suppressed because it is too large Load Diff

@ -45,8 +45,10 @@
#include "effect_platervbstereo.h"
#include "effect_compressor.h"
#ifdef ARM_ALLOW_MULTI_CORE
#include "fx_rack.h"
#if defined(MIXING_CONSOLE_ENABLE)
#include "mixing_console.hpp"
typedef MixingConsole<CConfig::ToneGenerators> Mixer;
#endif
class CMiniDexed
@ -55,8 +57,13 @@ class CMiniDexed
#endif
{
public:
CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster, FATFS *pFileSystem);
CMiniDexed(
CConfig *pConfig,
CInterruptSystem *pInterrupt,
CGPIOManager *pGPIOManager,
CI2CMaster *pI2CMaster,
FATFS *pFileSystem
);
bool Initialize (void);
@ -93,7 +100,13 @@ public:
void setBreathController (uint8_t value, unsigned nTG);
void setAftertouch (uint8_t value, unsigned nTG);
#if defined(MIXING_CONSOLE_ENABLE)
unsigned getMixingConsoleSendLevel(unsigned nTG, MixerOutput fx) const;
void setMixingConsoleSendLevel(unsigned nTG, MixerOutput fx, unsigned nFXSend);
void setMixingConsoleFXSendLevel(MixerOutput fromFX, MixerOutput toFX, unsigned nFXReturn);
#elif defined(PLATE_REVERB_ENABLE)
void SetReverbSend (unsigned nReverbSend, unsigned nTG); // 0 .. 127
#endif
void setMonoMode(uint8_t mono, uint8_t nTG);
void setPitchbendRange(uint8_t range, uint8_t nTG);
@ -135,6 +148,9 @@ public:
enum TParameter
{
ParameterCompressorEnable,
#if defined(PLATE_REVERB_ENABLE) || defined(MIXING_CONSOLE_ENABLE)
// Plate Reverb parameters
ParameterReverbEnable,
ParameterReverbSize,
ParameterReverbHighDamp,
@ -142,61 +158,136 @@ public:
ParameterReverbLowPass,
ParameterReverbDiffusion,
ParameterReverbLevel,
// BEGIN FXRack global parameters definition
#ifdef FXRACK_ENABLE
// FXChain parameters
ParameterFXChainEnable,
ParameterFXChainWet,
// FXChain > Tube parameters
ParameterFXChainTubeEnable,
ParameterFXChainTubeWet,
ParameterFXChainTubeOverdrive,
// FXChain > Chorus parameters
ParameterFXChainChorusEnable,
ParameterFXChainChorusWet,
ParameterFXChainChorusRate,
ParameterFXChainChorusDepth,
// FXChain > Flanger parameters
ParameterFXChainFlangerEnable,
ParameterFXChainFlangerWet,
ParameterFXChainFlangerRate,
ParameterFXChainFlangerDepth,
ParameterFXChainFlangerFeedback,
// FXChain > Orbitone parameters
ParameterFXChainOrbitoneEnable,
ParameterFXChainOrbitoneWet,
ParameterFXChainOrbitoneRate,
ParameterFXChainOrbitoneDepth,
// FXChain > Phaser parameters
ParameterFXChainPhaserEnable,
ParameterFXChainPhaserWet,
ParameterFXChainPhaserRate,
ParameterFXChainPhaserDepth,
ParameterFXChainPhaserFeedback,
ParameterFXChainPhaserNbStages,
// FXChain > Delay parameters
ParameterFXChainDelayEnable,
ParameterFXChainDelayWet,
ParameterFXChainDelayLeftDelayTime,
ParameterFXChainDelayRightDelayTime,
ParameterFXChainDelayFeedback,
// FXChain > ShimmerReverb parameters
ParameterFXChainShimmerReverbEnable,
ParameterFXChainShimmerReverbWet,
ParameterFXChainShimmerReverbInputGain,
ParameterFXChainShimmerReverbTime,
ParameterFXChainShimmerReverbDiffusion,
ParameterFXChainShimmerReverbLP,
#endif
// END FXRack global parameters definition
#endif
// BEGIN FX global parameters definition
#if defined(MIXING_CONSOLE_ENABLE)
// Tube parameters
ParameterFXTubeEnable,
ParameterFXTubeOverdrive,
// Chorus parameters
ParameterFXChorusEnable,
ParameterFXChorusRate,
ParameterFXChorusDepth,
// Flanger parameters
ParameterFXFlangerEnable,
ParameterFXFlangerRate,
ParameterFXFlangerDepth,
ParameterFXFlangerFeedback,
// Orbitone parameters
ParameterFXOrbitoneEnable,
ParameterFXOrbitoneRate,
ParameterFXOrbitoneDepth,
// Phaser parameters
ParameterFXPhaserEnable,
ParameterFXPhaserRate,
ParameterFXPhaserDepth,
ParameterFXPhaserFeedback,
ParameterFXPhaserNbStages,
// Delay parameters
ParameterFXDelayEnable,
ParameterFXDelayLeftDelayTime,
ParameterFXDelayRightDelayTime,
ParameterFXDelayFeedback,
// Reverberator parameters
ParameterFXReverberatorEnable,
ParameterFXReverberatorInputGain,
ParameterFXReverberatorTime,
ParameterFXReverberatorDiffusion,
ParameterFXReverberatorLP,
// Tube Return parameters
ParameterFXTube_ChorusSend,
ParameterFXTube_FlangerSend,
ParameterFXTube_OrbitoneSend,
ParameterFXTube_PhaserSend,
ParameterFXTube_DelaySend,
ParameterFXTube_PlateReverbSend,
ParameterFXTube_ReverberatorSend,
ParameterFXTube_MainOutput,
// Chorus Return parameters
ParameterFXChorus_TubeSend,
ParameterFXChorus_FlangerSend,
ParameterFXChorus_OrbitoneSend,
ParameterFXChorus_PhaserSend,
ParameterFXChorus_DelaySend,
ParameterFXChorus_PlateReverbSend,
ParameterFXChorus_ReverberatorSend,
ParameterFXChorus_MainOutput,
// Flanger Return parameters
ParameterFXFlanger_TubeSend,
ParameterFXFlanger_ChorusSend,
ParameterFXFlanger_OrbitoneSend,
ParameterFXFlanger_PhaserSend,
ParameterFXFlanger_DelaySend,
ParameterFXFlanger_PlateReverbSend,
ParameterFXFlanger_ReverberatorSend,
ParameterFXFlanger_MainOutput,
// Orbitone Return parameters
ParameterFXOrbitone_TubeSend,
ParameterFXOrbitone_ChorusSend,
ParameterFXOrbitone_FlangerSend,
ParameterFXOrbitone_PhaserSend,
ParameterFXOrbitone_DelaySend,
ParameterFXOrbitone_PlateReverbSend,
ParameterFXOrbitone_ReverberatorSend,
ParameterFXOrbitone_MainOutput,
// Phaser Return parameters
ParameterFXPhaser_TubeSend,
ParameterFXPhaser_ChorusSend,
ParameterFXPhaser_FlangerSend,
ParameterFXPhaser_OrbitoneSend,
ParameterFXPhaser_DelaySend,
ParameterFXPhaser_PlateReverbSend,
ParameterFXPhaser_ReverberatorSend,
ParameterFXPhaser_MainOutput,
// Delay Return parameters
ParameterFXDelay_TubeSend,
ParameterFXDelay_ChorusSend,
ParameterFXDelay_FlangerSend,
ParameterFXDelay_OrbitoneSend,
ParameterFXDelay_PhaserSend,
ParameterFXDelay_PlateReverbSend,
ParameterFXDelay_ReverberatorSend,
ParameterFXDelay_MainOutput,
// Plate Reverb Return parameters
ParameterFXPlateReverb_TubeSend,
ParameterFXPlateReverb_ChorusSend,
ParameterFXPlateReverb_FlangerSend,
ParameterFXPlateReverb_OrbitoneSend,
ParameterFXPlateReverb_PhaserSend,
ParameterFXPlateReverb_DelaySend,
ParameterFXPlateReverb_ReverberatorSend,
ParameterFXPlateReverb_MainOutput,
// Reverberator Return parameters
ParameterFXReverberator_TubeSend,
ParameterFXReverberator_ChorusSend,
ParameterFXReverberator_FlangerSend,
ParameterFXReverberator_OrbitoneSend,
ParameterFXReverberator_PhaserSend,
ParameterFXReverberator_DelaySend,
ParameterFXReverberator_PlateReverbSend,
ParameterFXReverberator_MainOutput,
// Bypass FX
ParameterFXBypass,
#endif
// END FX global parameters definition
ParameterUnknown
};
@ -223,7 +314,9 @@ public:
TGParameterCutoff,
TGParameterResonance,
TGParameterMIDIChannel,
#if defined(PLATE_REVERB_ENABLE)
TGParameterReverbSend,
#endif
TGParameterPitchBendRange,
TGParameterPitchBendStep,
TGParameterPortamentoMode,
@ -251,6 +344,18 @@ public:
TGParameterATAmplitude,
TGParameterATEGBias,
#if defined(MIXING_CONSOLE_ENABLE)
TGParameterMixingSendFXTube,
TGParameterMixingSendFXChorus,
TGParameterMixingSendFXFlanger,
TGParameterMixingSendFXOrbitone,
TGParameterMixingSendFXPhaser,
TGParameterMixingSendFXDelay,
TGParameterMixingSendFXPlateReverb,
TGParameterMixingSendFXReverberator,
TGParameterMixingSendFXMainOutput,
#endif // MIXING_CONSOLE_ENABLE
TGParameterUnknown
};
@ -322,7 +427,12 @@ private:
unsigned m_nNoteLimitHigh[CConfig::ToneGenerators];
int m_nNoteShift[CConfig::ToneGenerators];
#ifdef MIXING_CONSOLE_ENABLE
unsigned m_nTGSendLevel[CConfig::ToneGenerators][MixerOutput::kFXCount];
unsigned m_nFXSendLevel[MixerOutput::kFXCount - 1][MixerOutput::kFXCount];
#elif defined(PLATE_REVERB_ENABLE)
unsigned m_nReverbSend[CConfig::ToneGenerators];
#endif
uint8_t m_nRawVoiceData[156];
@ -352,15 +462,16 @@ private:
CPerformanceTimer m_GetChunkTimer;
bool m_bProfileEnabled;
#if defined(MIXING_CONSOLE_ENABLE)
Mixer* mixing_console_;
#elif defined(PLATE_REVERB_ENABLE)
AudioEffectPlateReverb* reverb;
AudioStereoMixer<CConfig::ToneGenerators>* tg_mixer;
AudioStereoMixer<CConfig::ToneGenerators>* reverb_send_mixer;
#endif
CSpinLock m_FXSpinLock;
#ifdef FXRACK_ENABLE
FXRack* fx_rack;
#endif
bool m_bSavePerformance;
bool m_bSavePerformanceNewFile;

@ -0,0 +1,793 @@
//
// 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
// 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/>.
//
// Implementation of the MixingConsole class defined in mixing_console.h
#pragma once
#include "mixing_console_constants.h"
#include "fx_tube.h"
#include "fx_chorus.h"
#include "fx_flanger.h"
#include "fx_orbitone.h"
#include "fx_phaser.h"
#include "fx_delay.h"
#include "effect_platervbstereo.h"
#include "fx_reverberator.h"
#include "fx_dry.h"
#include "fx_unit2.hpp"
template<size_t nb_inputs>
class MixingConsole : public FXBase
{
DISALLOW_COPY_AND_ASSIGN(MixingConsole);
public:
MixingConsole(float32_t sampling_rate, size_t buffer_size, bool swapStereoImage = false);
~MixingConsole();
inline size_t getChannelNumber() const;
inline void bypass(bool bypass);
inline bool bypass() const;
// Send section
inline void setChannelLevel(size_t in, float32_t lvl);
inline void setPan(size_t in, float32_t pan);
inline void swapStereoImage(bool swap);
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);
// Return section
inline void setFXSendLevel(MixerOutput fromFX, MixerOutput toFX, float32_t lvl);
inline void setReturnSample(MixerOutput ret, float32_t sampleL, float32_t sampleR);
// Get FX
inline FXElement* getFX(size_t fx);
inline FXUnit2<Tube>* getTube();
inline FXUnit2<Chorus>* getChorus();
inline FXUnit2<Flanger>* getFlanger();
inline FXUnit2<Orbitone>* getOrbitone();
inline FXUnit2<Phaser>* getPhaser();
inline FXUnit2<Delay>* getDelay();
inline FXUnit2<AudioEffectPlateReverb>* getPlateReverb();
inline FXUnit2<Reverberator>* getReverberator();
inline FXUnit2<Dry>* getDry();
// 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);
void process(float32_t* outLR);
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;
bool bypass_;
float32_t channel_level_[nb_inputs];
float32_t pan_[StereoChannels::kNumChannels + 1][nb_inputs];
bool swap_stereo_image_;
float32_t* tg_input_sample_buffer_[nb_inputs];
float32_t* input_sample_buffer_[StereoChannels::kNumChannels][nb_inputs];
float32_t input_samples_[StereoChannels::kNumChannels][nb_inputs + MixerOutput::kFXCount - 1];
float32_t levels_[MixerOutput::kFXCount][nb_inputs + MixerOutput::kFXCount - 1];
volatile size_t m_nSamples;
FXElement* fx_[MixerOutput::kFXCount];
FXUnit2<Tube>* tube_;
FXUnit2<Chorus>* chorus_;
FXUnit2<Flanger>* flanger_;
FXUnit2<Orbitone>* orbitone_;
FXUnit2<Phaser>* phaser_;
FXUnit2<Delay>* delay_;
FXUnit2<AudioEffectPlateReverb>* plate_reverb_;
FXUnit2<Reverberator>* reverberator_;
FXUnit2<Dry>* dry_;
IMPLEMENT_DUMP(
const size_t space = 9;
const size_t precision = 5;
std::stringstream ss;
out << "START " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
out << "\t" << "Input levels & Pan:" << std::endl;
{
SS_RESET(ss, precision, std::left);
SS_SPACE(ss, ' ', space, std::left, '|');
SS__TEXT(ss, ' ', space, std::left, '|', "Level");
SS__TEXT(ss, ' ', space, std::left, '|', "Pan L");
SS__TEXT(ss, ' ', space, std::left, '|', "Pan R");
SS__TEXT(ss, ' ', space, std::left, '|', "Pan");
out << "\t" << ss.str() << std::endl;
SS_RESET(ss, precision, std::left);
SS_SPACE(ss, '-', space, std::left, '+');
SS_SPACE(ss, '-', space, std::left, '+');
SS_SPACE(ss, '-', space, std::left, '+');
SS_SPACE(ss, '-', space, std::left, '+');
SS_SPACE(ss, '-', space, std::left, '+');
out << "\t" << ss.str() << std::endl;
for(size_t i = 0; i < nb_inputs; ++i)
{
std::stringstream s;
s << "* Input ";
s << (i + 1);
SS_RESET(ss, precision, std::left);
SS__TEXT(ss, ' ', space, std::left, '|', s.str());
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->channel_level_[i]);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->pan_[StereoChannels::Left][i]);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->pan_[StereoChannels::Right][i]);
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->pan_[StereoChannels::kNumChannels][i]);
out << "\t" << ss.str() << std::endl;
}
}
out << std::endl;
out << "\t" << "Mixing Console input samples:" << std::endl;
{
SS_RESET(ss, precision, std::left);
SS_SPACE(ss, ' ', space, std::left, '|');
for(size_t i = 0; i < nb_inputs; ++i)
{
std::stringstream s;
s << "Input ";
s << (i + 1);
SS__TEXT(ss, ' ', space, std::left, '|', s.str());
}
for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i)
{
std::string s = toString(static_cast<MixerOutput>(i));
s.resize(space);
SS__TEXT(ss, ' ', space, std::left, '|', s.c_str());
}
out << "\t" << ss.str() << std::endl;
SS_RESET(ss, precision, std::left);
SS_SPACE(ss, '-', space, std::left, '+');
for(size_t i = 0; i < nb_inputs; ++i)
{
SS_SPACE(ss, '-', space, std::left, '+');
}
for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i)
{
SS_SPACE(ss, '-', space, std::left, '+');
}
out << "\t" << ss.str() << std::endl;
const char* LR = "LR";
for(size_t c = 0; c < StereoChannels::kNumChannels; ++c)
{
std::stringstream s;
s << "* Input ";
s << LR[c];
SS_RESET(ss, precision, std::left);
SS__TEXT(ss, ' ', space, std::left, '|', s.str());
for(size_t i = 0; i < (nb_inputs + MixerOutput::kFXCount - 1); ++i)
{
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->input_samples_[c][i]);
}
out << "\t" << ss.str() << std::endl;
}
}
out << std::endl;
out << "\t" << "Mixing Console levels:" << std::endl;
{
SS_RESET(ss, precision, std::left);
SS_SPACE(ss, ' ', space, std::left, '|');
for(size_t i = 0; i < nb_inputs; ++i)
{
std::stringstream s;
s << "Input ";
s << (i + 1);
SS__TEXT(ss, ' ', space, std::left, '|', s.str());
}
for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i)
{
std::string s = toString(static_cast<MixerOutput>(i));
s.resize(space);
SS__TEXT(ss, ' ', space, std::left, '|', s.c_str());
}
out << "\t" << ss.str() << std::endl;
SS_RESET(ss, precision, std::left);
SS_SPACE(ss, '-', space, std::left, '+');
for(size_t i = 0; i < nb_inputs; ++i)
{
SS_SPACE(ss, '-', space, std::left, '+');
}
for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i)
{
SS_SPACE(ss, '-', space, std::left, '+');
}
out << "\t" << ss.str() << std::endl;
for(size_t c = 0; c < MixerOutput::kFXCount; ++c)
{
SS_RESET(ss, precision, std::left);
std::string s = toString(static_cast<MixerOutput>(c));
s.resize(space);
SS__TEXT(ss, ' ', space, std::left, '|', s.c_str());
for(size_t i = 0; i < (nb_inputs + MixerOutput::kFXCount - 1); ++i)
{
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->levels_[c][i]);
}
out << "\t" << ss.str() << std::endl;
}
}
out << std::endl;
if(deepInspection)
{
this->tube_->dump(out, deepInspection, tag + ".tube_");
this->chorus_->dump(out, deepInspection, tag + ".chorus_");
this->flanger_->dump(out, deepInspection, tag + ".flanger_");
this->orbitone_->dump(out, deepInspection, tag + ".orbitone_");
this->phaser_->dump(out, deepInspection, tag + ".phaser_");
this->delay_->dump(out, deepInspection, tag + ".delay_");
this->plate_reverb_->dump(out, deepInspection, tag + ".plate_reverb_");
this->reverberator_->dump(out, deepInspection, tag + ".reverberator_");
this->dry_->dump(out, deepInspection, tag + ".dry_");
}
out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
)
IMPLEMENT_INSPECT(
size_t nb_errors = 0;
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);
}
for(size_t i = 0; i < nb_inputs; ++i)
{
nb_errors += inspector(tag + ".input[ L ][ input #" + std::to_string(i) + " ]", this->input_samples_[StereoChannels::Left ][i], -1.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".input[ R ][ input #" + std::to_string(i) + " ]", this->input_samples_[StereoChannels::Right][i], -1.0f, 1.0f, deepInspection);
}
for(size_t i = nb_inputs; i < (nb_inputs + MixerOutput::kFXCount - 1); ++i)
{
nb_errors += inspector(tag + ".input[ L ][ input " + toString(static_cast<MixerOutput>(i - nb_inputs)) + " ]", this->input_samples_[StereoChannels::Left ][i], -1.0f, 1.0f, deepInspection);
nb_errors += inspector(tag + ".input[ R ][ input " + toString(static_cast<MixerOutput>(i - nb_inputs)) + " ]", this->input_samples_[StereoChannels::Right][i], -1.0f, 1.0f, deepInspection);
}
for(size_t c = 0; c < MixerOutput::kFXCount; ++c)
{
for(size_t i = 0; i < (nb_inputs + MixerOutput::kFXCount - 1); ++i)
{
nb_errors += inspector(tag + ".levels[ " + std::to_string(c) + " ][ " + std::to_string(i) + " ]", this->levels_[c][i], -1.0f, 1.0f, deepInspection);
}
}
if(deepInspection)
{
for(size_t i = 0; i < nb_inputs; ++i)
{
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);
}
}
nb_errors += this->tube_->inspect(inspector, deepInspection, tag + ".tube_");
nb_errors += this->chorus_->inspect(inspector, deepInspection, tag + ".chorus_");
nb_errors += this->flanger_->inspect(inspector, deepInspection, tag + ".flanger_");
nb_errors += this->orbitone_->inspect(inspector, deepInspection, tag + ".orbitone_");
nb_errors += this->phaser_->inspect(inspector, deepInspection, tag + ".phaser_");
nb_errors += this->delay_->inspect(inspector, deepInspection, tag + ".delay_");
nb_errors += this->plate_reverb_->inspect(inspector, deepInspection, tag + ".plate_reverb_");
nb_errors += this->reverberator_->inspect(inspector, deepInspection, tag + ".reverberator_");
nb_errors += this->dry_->inspect(inspector, deepInspection, tag + ".dry_");
}
return nb_errors;
)
};
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, bool swapStereoImage) :
FXBase(sampling_rate),
BufferSize(buffer_size),
bypass_(true),
swap_stereo_image_(swapStereoImage),
m_nSamples(0)
{
for(size_t i = 0; i < nb_inputs; ++i)
{
this->tg_input_sample_buffer_[i] = nullptr;
this->input_sample_buffer_[StereoChannels::Left ][i] = new float32_t[this->BufferSize];
this->input_sample_buffer_[StereoChannels::Right][i] = new float32_t[this->BufferSize];
memset(this->input_sample_buffer_[StereoChannels::Left ][i], 0, sizeof(float32_t) * this->BufferSize);
memset(this->input_sample_buffer_[StereoChannels::Right][i], 0, sizeof(float32_t) * this->BufferSize);
}
this->fx_[MixerOutput::FX_Tube] = this->tube_ = new FXUnit2<Tube>(sampling_rate);
this->fx_[MixerOutput::FX_Chorus] = this->chorus_ = new FXUnit2<Chorus>(sampling_rate);
this->fx_[MixerOutput::FX_Flanger] = this->flanger_ = new FXUnit2<Flanger>(sampling_rate);
this->fx_[MixerOutput::FX_Orbitone] = this->orbitone_ = new FXUnit2<Orbitone>(sampling_rate);
this->fx_[MixerOutput::FX_Phaser] = this->phaser_ = new FXUnit2<Phaser>(sampling_rate);
this->fx_[MixerOutput::FX_Delay] = this->delay_ = new FXUnit2<Delay>(sampling_rate);
this->fx_[MixerOutput::FX_PlateReverb] = this->plate_reverb_ = new FXUnit2<AudioEffectPlateReverb>(sampling_rate);
this->fx_[MixerOutput::FX_Reverberator] = this->reverberator_ = new FXUnit2<Reverberator>(sampling_rate);
this->fx_[MixerOutput::MainOutput] = this->dry_ = new FXUnit2<Dry>(sampling_rate);
this->bypass(false);
this->init();
}
template<size_t nb_inputs>
MixingConsole<nb_inputs>::~MixingConsole()
{
for(size_t i = 0; i < MixerOutput::kFXCount; ++i)
{
delete this->fx_[i];
}
for(size_t i = 0; i < nb_inputs; ++i)
{
delete[] this->input_sample_buffer_[StereoChannels::Left ][i];
delete[] this->input_sample_buffer_[StereoChannels::Right][i];
// 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;
}
}
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::bypass(bool bypass)
{
if(this->bypass_ != bypass)
{
this->bypass_ = bypass;
for(size_t fx = MixerOutput::FX_Tube; fx < MixerOutput::kFXCount; ++fx)
{
this->getFX(fx)->bypassFXProcess(bypass);
}
if(!bypass)
{
this->reset();
}
}
}
template<size_t nb_inputs>
bool MixingConsole<nb_inputs>::bypass() const
{
return this->bypass_;
}
template<size_t nb_inputs>
size_t MixingConsole<nb_inputs>::getChannelNumber() const
{
return nb_inputs;
}
// 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)
{
assert(in < nb_inputs);
pan = constrain(pan, 0.0f, 1.0f);
if(pan == this->pan_[StereoChannels::kNumChannels][in]) return;
this->pan_[StereoChannels::kNumChannels][in] = pan;
this->updatePan(in);
}
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::swapStereoImage(bool swap)
{
this->swap_stereo_image_ = swap;
}
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::setSendLevel(size_t in, MixerOutput fx, float32_t lvl)
{
assert(in < nb_inputs);
assert(fx < MixerOutput::kFXCount);
this->setLevel(in, fx, lvl);
}
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::setInputSample(size_t in, float32_t sampleL, float32_t sampleR)
{
assert(in < nb_inputs);
this->setSample(in, sampleL, sampleR);
}
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::setInputSampleBuffer(size_t in, float32_t* samples)
{
assert(in < nb_inputs);
this->tg_input_sample_buffer_[in] = samples;
}
// Return section
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::setFXSendLevel(MixerOutput fromFX, MixerOutput toFX, float32_t lvl)
{
assert(fromFX < (MixerOutput::kFXCount - 1));
assert(toFX < MixerOutput::kFXCount);
if(fromFX == toFX)
{
// An FX cannot feedback on itself
return;
}
this->setLevel(nb_inputs + fromFX, toFX, lvl);
}
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::setReturnSample(MixerOutput ret, float32_t sampleL, float32_t sampleR)
{
assert(ret < (MixerOutput::kFXCount - 1));
this->setSample(nb_inputs + ret, sampleL, sampleR);
}
// Get FX
template<size_t nb_inputs>
FXElement* MixingConsole<nb_inputs>::getFX(size_t fx)
{
assert(fx < MixerOutput::kFXCount);
return this->fx_[fx];
}
template<size_t nb_inputs>
FXUnit2<Tube>* MixingConsole<nb_inputs>::getTube()
{
return this->tube_;
}
template<size_t nb_inputs>
FXUnit2<Chorus>* MixingConsole<nb_inputs>::getChorus()
{
return this->chorus_;
}
template<size_t nb_inputs>
FXUnit2<Flanger>* MixingConsole<nb_inputs>::getFlanger()
{
return this->flanger_;
}
template<size_t nb_inputs>
FXUnit2<Orbitone>* MixingConsole<nb_inputs>::getOrbitone()
{
return this->orbitone_;
}
template<size_t nb_inputs>
FXUnit2<Phaser>* MixingConsole<nb_inputs>::getPhaser()
{
return this->phaser_;
}
template<size_t nb_inputs>
FXUnit2<Delay>* MixingConsole<nb_inputs>::getDelay()
{
return this->delay_;
}
template<size_t nb_inputs>
FXUnit2<AudioEffectPlateReverb>* MixingConsole<nb_inputs>::getPlateReverb()
{
return this->plate_reverb_;
}
template<size_t nb_inputs>
FXUnit2<Reverberator>* MixingConsole<nb_inputs>::getReverberator()
{
return this->reverberator_;
}
template<size_t nb_inputs>
FXUnit2<Dry>* MixingConsole<nb_inputs>::getDry()
{
return this->dry_;
}
// Processing
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::init()
{
memset(this->channel_level_, 0, nb_inputs * sizeof(float32_t));
for(size_t i = 0; i <= StereoChannels::kNumChannels; ++i) memset(this->pan_[i], 0, nb_inputs * sizeof(float32_t));
for(size_t i = 0; i < MixerOutput::kFXCount; ++i)
memset(this->levels_[i], 0, (nb_inputs + MixerOutput::kFXCount - 1) * sizeof(float32_t));
for(size_t i = 0; i < StereoChannels::kNumChannels; ++i)
memset(this->input_samples_[i], 0, (nb_inputs + MixerOutput::kFXCount - 1) * sizeof(float32_t));
this->reset();
}
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::reset()
{
for(size_t i = 0; i < nb_inputs; ++i)
{
memset(this->input_sample_buffer_[StereoChannels::Left ][i], 0, this->BufferSize * sizeof(float32_t));
memset(this->input_sample_buffer_[StereoChannels::Right][i], 0, this->BufferSize * sizeof(float32_t));
}
for(size_t i = 0; i < MixerOutput::kFXCount; ++i)
{
this->fx_[i]->reset();
if(i != MixerOutput::MainOutput)
{
this->setReturnSample(static_cast<MixerOutput>(i), 0.0f, 0.0f);
}
}
}
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_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 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_[fxId]->processSample(
fx_input_[StereoChannels::Left ],
fx_input_[StereoChannels::Right],
fx_output_[StereoChannels::Left ],
fx_output_[StereoChannels::Right]
);
if(fxId != MixerOutput::MainOutput)
{
// Feedback the processed samples except for the main output
this->setReturnSample(
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];
}
}
}
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::process(float32_t* outL, float32_t* outR)
{
size_t nSamples = this->m_nSamples;
for(size_t s = 0; s < nSamples; ++s)
{
for(size_t in = 0; in < nb_inputs; ++in)
{
this->setSample(
in,
this->input_sample_buffer_[StereoChannels::Left ][in][s],
this->input_sample_buffer_[StereoChannels::Right][in][s]
);
}
if(this->swap_stereo_image_)
{
this->processSample(*outR, *outL);
}
else
{
this->processSample(*outL, *outR);
}
++outL;
++outR;
}
this->m_nSamples = 0;
}
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::process(float32_t* outLR)
{
size_t nSamples = this->m_nSamples;
for(size_t s = 0; s < nSamples; ++s)
{
for(size_t in = 0; in < nb_inputs; ++in)
{
this->setSample(
in,
this->input_sample_buffer_[StereoChannels::Left ][in][s],
this->input_sample_buffer_[StereoChannels::Right][in][s]
);
}
if(this->swap_stereo_image_)
{
this->processSample(*(outLR + 1), *outLR);
}
else
{
this->processSample(*outLR, *(outLR + 1));
}
outLR += 2;
}
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)
{
assert(in < (nb_inputs + MixerOutput::kFXCount - 1));
assert(fx < MixerOutput::kFXCount);
this->levels_[fx][in] = constrain(lvl, 0.0f, 1.0f);
}
template<size_t nb_inputs>
void MixingConsole<nb_inputs>::setSample(size_t in, float32_t sampleL, float32_t sampleR)
{
assert(in < (nb_inputs + MixerOutput::kFXCount - 1));
this->input_samples_[StereoChannels::Left ][in] = sampleL;
this->input_samples_[StereoChannels::Right][in] = sampleR;
}

@ -0,0 +1,58 @@
#pragma once
#include "extra_features.h"
#include <array>
#include <cstring>
#include <stdexcept>
enum MixerOutput
{
OutputStart = 0,
FX_Tube = 0,
FX_Chorus,
FX_Flanger,
FX_Orbitone,
FX_Phaser,
FX_Delay,
FX_PlateReverb,
FX_Reverberator,
MainOutput,
kFXCount
};
inline std::string toString(MixerOutput enum_val)
{
static const std::array<const char*, MixerOutput::kFXCount> names
{
"Tube",
"Chorus",
"Flanger",
"Orbitone",
"Phaser",
"Delay",
"PlateReverb",
"Reverberator",
"MainOutput"
};
static_assert(names.size() == MixerOutput::kFXCount, "Enum MixerOutput and string array size mismatch");
return std::string(names[static_cast<size_t>(enum_val)]);
}
#define TO_INDEX_CHECK(str, fx) if(std::strcmp(str, toString(fx).c_str()) == 0) return fx;
inline MixerOutput toIndex(const char* str)
{
TO_INDEX_CHECK(str, MixerOutput::FX_Tube);
TO_INDEX_CHECK(str, MixerOutput::FX_Chorus);
TO_INDEX_CHECK(str, MixerOutput::FX_Flanger);
TO_INDEX_CHECK(str, MixerOutput::FX_Orbitone);
TO_INDEX_CHECK(str, MixerOutput::FX_Phaser);
TO_INDEX_CHECK(str, MixerOutput::FX_Delay);
TO_INDEX_CHECK(str, MixerOutput::FX_PlateReverb);
TO_INDEX_CHECK(str, MixerOutput::FX_Reverberator);
TO_INDEX_CHECK(str, MixerOutput::MainOutput);
throw std::invalid_argument("Invalid MixerOutput string");
}

@ -275,6 +275,7 @@ AftertouchTarget8=0
# Effects
CompressorEnable=1
ReverbEnable=1
ReverbSize=70
ReverbHighDamp=50
@ -283,39 +284,190 @@ ReverbLowPass=30
ReverbDiffusion=65
ReverbLevel=99
# FXRack
FXChainEnable=1
FXChainWet=99
FXChainTubeEnable=0
FXChainTubeWet=50
FXChainTubeOverdrive=10
FXChainChorusEnable=0
FXChainChorusWet=50
FXChainChorusRate=40
FXChainChorusDepth=50
FXChainFlangerEnable=0
FXChainFlangerWet=50
FXChainFlangerRate=3
FXChainFlangerDepth=75
FXChainFlangerFeedback=50
FXChainOrbitoneEnable=0
FXChainOrbitoneWet=80
FXChainOrbitoneRate=40
FXChainOrbitoneDepth=50
FXChainPhaserEnable=0
FXChainPhaserWet=50
FXChainPhaserRate=5
FXChainPhaserDepth=99
FXChainPhaserFeedback=70
FXChainPhaserNbStages=12
FXChainDelayEnable=0
FXChainDelayWet=50
FXChainDelayLeftDelayTime=15
FXChainDelayRightDelayTime=22
FXChainDelayFeedback=35
FXChainShimmerReverbEnable=1
FXChainShimmerReverbWet=70
FXChainShimmerReverbInputGain=55
FXChainShimmerReverbTime=75
FXChainShimmerReverbDiffusion=75
FXChainShimmerReverbLP=80
# Mixing Console
FXTubeEnable=1
FXTubeOverdrive=10
FXChorusEnable=1
FXChorusRate=40
FXChorusDepth=50
FXFlangerEnable=1
FXFlangerRate=3
FXFlangerDepth=75
FXFlangerFeedback=50
FXOrbitoneEnable=1
FXOrbitoneRate=40
FXOrbitoneDepth=50
FXPhaserEnable=1
FXPhaserRate=5
FXPhaserDepth=99
FXPhaserFeedback=70
FXPhaserNbStages=12
FXDelayEnable=1
FXDelayLeftDelayTime=15
FXDelayRightDelayTime=22
FXDelayFeedback=35
FXDelayFlutterRate=0
FXDelayFlutterAmount=0
FXReverberatorEnable=1
FXReverberatorInputGain=55
FXReverberatorTime=75
FXReverberatorDiffusion=75
FXReverberatorLP=80
FXSend_TG0_to_Tube=0
FXSend_TG0_to_Chorus=0
FXSend_TG0_to_Flanger=0
FXSend_TG0_to_Orbitone=0
FXSend_TG0_to_Phaser=0
FXSend_TG0_to_Delay=0
FXSend_TG0_to_PlateReverb=99
FXSend_TG0_to_Reverberator=0
FXSend_TG0_to_MainOutput=99
FXSend_TG1_to_Tube=0
FXSend_TG1_to_Chorus=0
FXSend_TG1_to_Flanger=0
FXSend_TG1_to_Orbitone=0
FXSend_TG1_to_Phaser=0
FXSend_TG1_to_Delay=0
FXSend_TG1_to_PlateReverb=0
FXSend_TG1_to_Reverberator=0
FXSend_TG1_to_MainOutput=0
FXSend_TG2_to_Tube=0
FXSend_TG2_to_Chorus=0
FXSend_TG2_to_Flanger=0
FXSend_TG2_to_Orbitone=0
FXSend_TG2_to_Phaser=0
FXSend_TG2_to_Delay=0
FXSend_TG2_to_PlateReverb=0
FXSend_TG2_to_Reverberator=0
FXSend_TG2_to_MainOutput=0
FXSend_TG3_to_Tube=0
FXSend_TG3_to_Chorus=0
FXSend_TG3_to_Flanger=0
FXSend_TG3_to_Orbitone=0
FXSend_TG3_to_Phaser=0
FXSend_TG3_to_Delay=0
FXSend_TG3_to_PlateReverb=0
FXSend_TG3_to_Reverberator=0
FXSend_TG3_to_MainOutput=0
FXSend_TG4_to_Tube=0
FXSend_TG4_to_Chorus=0
FXSend_TG4_to_Flanger=0
FXSend_TG4_to_Orbitone=0
FXSend_TG4_to_Phaser=0
FXSend_TG4_to_Delay=0
FXSend_TG4_to_PlateReverb=0
FXSend_TG4_to_Reverberator=0
FXSend_TG4_to_MainOutput=0
FXSend_TG5_to_Tube=0
FXSend_TG5_to_Chorus=0
FXSend_TG5_to_Flanger=0
FXSend_TG5_to_Orbitone=0
FXSend_TG5_to_Phaser=0
FXSend_TG5_to_Delay=0
FXSend_TG5_to_PlateReverb=0
FXSend_TG5_to_Reverberator=0
FXSend_TG5_to_MainOutput=0
FXSend_TG6_to_Tube=0
FXSend_TG6_to_Chorus=0
FXSend_TG6_to_Flanger=0
FXSend_TG6_to_Orbitone=0
FXSend_TG6_to_Phaser=0
FXSend_TG6_to_Delay=0
FXSend_TG6_to_PlateReverb=0
FXSend_TG6_to_Reverberator=0
FXSend_TG6_to_MainOutput=0
FXSend_TG7_to_Tube=0
FXSend_TG7_to_Chorus=0
FXSend_TG7_to_Flanger=0
FXSend_TG7_to_Orbitone=0
FXSend_TG7_to_Phaser=0
FXSend_TG7_to_Delay=0
FXSend_TG7_to_PlateReverb=0
FXSend_TG7_to_Reverberator=0
FXSend_TG7_to_MainOutput=0
FXSend_Tube_to_Chorus=0
FXSend_Tube_to_Flanger=0
FXSend_Tube_to_Orbitone=0
FXSend_Tube_to_Phaser=0
FXSend_Tube_to_Delay=0
FXSend_Tube_to_PlateReverb=0
FXSend_Tube_to_Reverberator=0
FXSend_Tube_to_MainOutput=0
FXSend_Chorus_to_Tube=0
FXSend_Chorus_to_Flanger=0
FXSend_Chorus_to_Orbitone=0
FXSend_Chorus_to_Phaser=0
FXSend_Chorus_to_Delay=0
FXSend_Chorus_to_PlateReverb=0
FXSend_Chorus_to_Reverberator=0
FXSend_Chorus_to_MainOutput=0
FXSend_Flanger_to_Tube=0
FXSend_Flanger_to_Chorus=0
FXSend_Flanger_to_Orbitone=0
FXSend_Flanger_to_Phaser=0
FXSend_Flanger_to_Delay=0
FXSend_Flanger_to_PlateReverb=0
FXSend_Flanger_to_Reverberator=0
FXSend_Flanger_to_MainOutput=0
FXSend_Orbitone_to_Tube=0
FXSend_Orbitone_to_Chorus=0
FXSend_Orbitone_to_Flanger=0
FXSend_Orbitone_to_Phaser=0
FXSend_Orbitone_to_Delay=0
FXSend_Orbitone_to_PlateReverb=0
FXSend_Orbitone_to_Reverberator=0
FXSend_Orbitone_to_MainOutput=0
FXSend_Phaser_to_Tube=0
FXSend_Phaser_to_Chorus=0
FXSend_Phaser_to_Flanger=0
FXSend_Phaser_to_Orbitone=0
FXSend_Phaser_to_Delay=0
FXSend_Phaser_to_PlateReverb=0
FXSend_Phaser_to_Reverberator=0
FXSend_Phaser_to_MainOutput=0
FXSend_Delay_to_Tube=0
FXSend_Delay_to_Chorus=0
FXSend_Delay_to_Flanger=0
FXSend_Delay_to_Orbitone=0
FXSend_Delay_to_Phaser=0
FXSend_Delay_to_PlateReverb=0
FXSend_Delay_to_Reverberator=0
FXSend_Delay_to_MainOutput=0
FXSend_PlateReverb_to_Tube=0
FXSend_PlateReverb_to_Chorus=0
FXSend_PlateReverb_to_Flanger=0
FXSend_PlateReverb_to_Orbitone=0
FXSend_PlateReverb_to_Phaser=0
FXSend_PlateReverb_to_Delay=0
FXSend_PlateReverb_to_Reverberator=0
FXSend_PlateReverb_to_MainOutput=99
FXSend_Reverberator_to_Tube=0
FXSend_Reverberator_to_Chorus=0
FXSend_Reverberator_to_Flanger=0
FXSend_Reverberator_to_Orbitone=0
FXSend_Reverberator_to_Phaser=0
FXSend_Reverberator_to_Delay=0
FXSend_Reverberator_to_PlateReverb=0
FXSend_Reverberator_to_MainOutput=0

@ -102,8 +102,10 @@ bool CPerformanceConfig::Load (void)
PropertyName.Format ("NoteShift%u", nTG+1);
m_nNoteShift[nTG] = m_Properties.GetSignedNumber (PropertyName, 0);
#if defined(PLATE_REVERB_ENABLE)
PropertyName.Format ("ReverbSend%u", nTG+1);
m_nReverbSend[nTG] = m_Properties.GetNumber (PropertyName, 50);
#endif
PropertyName.Format ("PitchBendRange%u", nTG+1);
m_nPitchBendRange[nTG] = m_Properties.GetNumber (PropertyName, 2);
@ -150,54 +152,90 @@ bool CPerformanceConfig::Load (void)
PropertyName.Format ("AftertouchTarget%u", nTG+1);
m_nAftertouchTarget[nTG] = m_Properties.GetNumber (PropertyName, 0);
}
}
m_bCompressorEnable = m_Properties.GetNumber ("CompressorEnable", 1) != 0;
m_bReverbEnable = m_Properties.GetNumber ("ReverbEnable", 1) != 0;
m_nReverbSize = m_Properties.GetNumber ("ReverbSize", 70);
m_nReverbHighDamp = m_Properties.GetNumber ("ReverbHighDamp", 50);
m_nReverbLowDamp = m_Properties.GetNumber ("ReverbLowDamp", 50);
m_nReverbLowPass = m_Properties.GetNumber ("ReverbLowPass", 30);
m_nReverbDiffusion = m_Properties.GetNumber ("ReverbDiffusion", 65);
m_nReverbLevel = m_Properties.GetNumber ("ReverbLevel", 99);
#ifdef ARM_ALLOW_MULTI_CORE
this->m_bFXChainEnable = this->m_Properties.GetNumber("FXChainEnable", 1);
this->m_nFXChainWet = this->m_Properties.GetNumber("FXChainWet", 99);
this->m_bFXChainTubeEnable = this->m_Properties.GetNumber("FXChainTubeEnable", 1);
this->m_nFXChainTubeWet = this->m_Properties.GetNumber("FXChainTubeWet", 50);
this->m_nFXChainTubeOverdrive = this->m_Properties.GetNumber("FXChainTubeOverdrive", 10);
this->m_bFXChainChorusEnable = this->m_Properties.GetNumber("FXChainChorusEnable", 1);
this->m_nFXChainChorusWet = this->m_Properties.GetNumber("FXChainChorusWet", 50);
this->m_nFXChainChorusRate = this->m_Properties.GetNumber("FXChainChorusRate", 50);
this->m_nFXChainChorusDepth = this->m_Properties.GetNumber("FXChainChorusDepth", 50);
this->m_bFXChainFlangerEnable = this->m_Properties.GetNumber("FXChainFlangerEnable", 1);
this->m_nFXChainFlangerWet = this->m_Properties.GetNumber("FXChainFlangerWet", 50);
this->m_nFXChainFlangerRate = this->m_Properties.GetNumber("FXChainFlangerRate", 15);
this->m_nFXChainFlangerDepth = this->m_Properties.GetNumber("FXChainFlangerDepth", 10);
this->m_nFXChainFlangerFeedback = this->m_Properties.GetNumber("FXChainFlangerFeedback", 20);
this->m_bFXChainOrbitoneEnable = this->m_Properties.GetNumber("FXChainOrbitoneEnable", 1);
this->m_nFXChainOrbitoneWet = this->m_Properties.GetNumber("FXChainOrbitoneWet", 80);
this->m_nFXChainOrbitoneRate = this->m_Properties.GetNumber("FXChainOrbitoneRate", 40);
this->m_nFXChainOrbitoneDepth = this->m_Properties.GetNumber("FXChainOrbitoneDepth", 50);
this->m_bFXChainPhaserEnable = this->m_Properties.GetNumber("FXChainPhaserEnable", 1);
this->m_nFXChainPhaserWet = this->m_Properties.GetNumber("FXChainPhaserWet", 50);
this->m_nFXChainPhaserRate = this->m_Properties.GetNumber("FXChainPhaserRate", 5);
this->m_nFXChainPhaserDepth = this->m_Properties.GetNumber("FXChainPhaserDepth", 99);
this->m_nFXChainPhaserFeedback = this->m_Properties.GetNumber("FXChainPhaserFeedback", 50);
this->m_nFXChainPhaserNbStages = this->m_Properties.GetNumber("FXChainPhaserNbStages", 12);
this->m_bFXChainDelayEnable = this->m_Properties.GetNumber("FXChainDelayEnable", 1);
this->m_nFXChainDelayWet = this->m_Properties.GetNumber("FXChainDelayWet", 50);
this->m_nFXChainDelayLeftDelayTime = this->m_Properties.GetNumber("FXChainDelayLeftDelayTime", 15);
this->m_nFXChainDelayRightDelayTime = this->m_Properties.GetNumber("FXChainDelayRightDelayTime", 22);
this->m_nFXChainDelayFeedback = this->m_Properties.GetNumber("FXChainDelayFeedback", 35);
this->m_bFXChainShimmerReverbEnable = this->m_Properties.GetNumber("FXChainShimmerReverbEnable", 1);
this->m_nFXChainShimmerReverbWet = this->m_Properties.GetNumber("FXChainShimmerReverbWet", 70);
this->m_nFXChainShimmerReverbInputGain = this->m_Properties.GetNumber("FXChainShimmerReverbInputGain", 30);
this->m_nFXChainShimmerReverbTime = this->m_Properties.GetNumber("FXChainShimmerReverbTime", 30);
this->m_nFXChainShimmerReverbDiffusion = this->m_Properties.GetNumber("FXChainShimmerReverbDiffusion", 30);
this->m_nFXChainShimmerReverbLP = this->m_Properties.GetNumber("FXChainShimmerReverbLP", 99);
#if defined(PLATE_REVERB_ENABLE) || defined(MIXING_CONSOLE_ENABLE)
this->m_bReverbEnable = this->m_Properties.GetNumber ("ReverbEnable", 1) != 0;
this->m_nReverbSize = this->m_Properties.GetNumber ("ReverbSize", 70);
this->m_nReverbHighDamp = this->m_Properties.GetNumber ("ReverbHighDamp", 50);
this->m_nReverbLowDamp = this->m_Properties.GetNumber ("ReverbLowDamp", 50);
this->m_nReverbLowPass = this->m_Properties.GetNumber ("ReverbLowPass", 30);
this->m_nReverbDiffusion = this->m_Properties.GetNumber ("ReverbDiffusion", 65);
this->m_nReverbLevel = this->m_Properties.GetNumber ("ReverbLevel", 99);
#endif
#if defined(MIXING_CONSOLE_ENABLE)
this->m_bFXTubeEnable = this->m_Properties.GetNumber("FXTubeEnable", 1);
this->m_nFXTubeOverdrive = this->m_Properties.GetNumber("FXTubeOverdrive", 10);
this->m_bFXChorusEnable = this->m_Properties.GetNumber("FXChorusEnable", 1);
this->m_nFXChorusRate = this->m_Properties.GetNumber("FXChorusRate", 50);
this->m_nFXChorusDepth = this->m_Properties.GetNumber("FXChorusDepth", 50);
this->m_bFXFlangerEnable = this->m_Properties.GetNumber("FXFlangerEnable", 1);
this->m_nFXFlangerRate = this->m_Properties.GetNumber("FXFlangerRate", 15);
this->m_nFXFlangerDepth = this->m_Properties.GetNumber("FXFlangerDepth", 10);
this->m_nFXFlangerFeedback = this->m_Properties.GetNumber("FXFlangerFeedback", 20);
this->m_bFXOrbitoneEnable = this->m_Properties.GetNumber("FXOrbitoneEnable", 1);
this->m_nFXOrbitoneRate = this->m_Properties.GetNumber("FXOrbitoneRate", 40);
this->m_nFXOrbitoneDepth = this->m_Properties.GetNumber("FXOrbitoneDepth", 50);
this->m_bFXPhaserEnable = this->m_Properties.GetNumber("FXPhaserEnable", 1);
this->m_nFXPhaserRate = this->m_Properties.GetNumber("FXPhaserRate", 5);
this->m_nFXPhaserDepth = this->m_Properties.GetNumber("FXPhaserDepth", 99);
this->m_nFXPhaserFeedback = this->m_Properties.GetNumber("FXPhaserFeedback", 50);
this->m_nFXPhaserNbStages = this->m_Properties.GetNumber("FXPhaserNbStages", 12);
this->m_bFXDelayEnable = this->m_Properties.GetNumber("FXDelayEnable", 1);
this->m_nFXDelayLeftDelayTime = this->m_Properties.GetNumber("FXDelayLeftDelayTime", 15);
this->m_nFXDelayRightDelayTime = this->m_Properties.GetNumber("FXDelayRightDelayTime", 22);
this->m_nFXDelayFeedback = this->m_Properties.GetNumber("FXDelayFeedback", 35);
this->m_bFXReverberatorEnable = this->m_Properties.GetNumber("FXReverberatorEnable", 1);
this->m_nFXReverberatorInputGain = this->m_Properties.GetNumber("FXReverberatorInputGain", 30);
this->m_nFXReverberatorTime = this->m_Properties.GetNumber("FXReverberatorTime", 30);
this->m_nFXReverberatorDiffusion = this->m_Properties.GetNumber("FXReverberatorDiffusion", 30);
this->m_nFXReverberatorLP = this->m_Properties.GetNumber("FXReverberatorLP", 99);
bool revUsed = false;
for(unsigned nTG = 0; nTG < CConfig::ToneGenerators; ++nTG)
{
CString reverbSendProp;
reverbSendProp.Format ("ReverbSend%u", nTG + 1);
unsigned reverbSend = m_Properties.GetNumber(reverbSendProp, 50);
revUsed |= (reverbSend > 0);
for(unsigned toFX = 0; toFX < MixerOutput::kFXCount; ++toFX)
{
CString propertyName;
propertyName.Format("FXSend_TG%u_to_%s", nTG + 1, toString(static_cast<MixerOutput>(toFX)).c_str());
unsigned defaultLevel = 0;
if(nTG == 0)
{
if(toFX == MixerOutput::FX_PlateReverb) defaultLevel = reverbSend;
else if(toFX == MixerOutput::MainOutput) defaultLevel = 99 - reverbSend;
}
this->m_nTGSendLevel[nTG][toFX] = this->m_Properties.GetNumber(propertyName, defaultLevel);
}
}
size_t end = MixerOutput::kFXCount - 1;
for(size_t fromFX = 0; fromFX < end; ++fromFX)
{
for(size_t toFX = 0; toFX < MixerOutput::kFXCount; ++toFX)
{
CString propertyName;
propertyName.Format("FXSend_%s_to_%s", toString(static_cast<MixerOutput>(fromFX)).c_str(), toString(static_cast<MixerOutput>(toFX)).c_str());
unsigned defaultLevel = 0;
if(fromFX == MixerOutput::FX_PlateReverb && toFX == MixerOutput::MainOutput) defaultLevel = revUsed ? 99 : 0;
this->m_nFXSendLevel[fromFX][toFX] = this->m_Properties.GetNumber(propertyName, defaultLevel);
}
}
this->m_bFXBypass = this->m_Properties.GetNumber("FXBypass", 0);
#endif
return bResult;
@ -257,8 +295,10 @@ bool CPerformanceConfig::Save (void)
PropertyName.Format ("NoteShift%u", nTG+1);
m_Properties.SetSignedNumber (PropertyName, m_nNoteShift[nTG]);
#if defined(PLATE_REVERB_ENABLE)
PropertyName.Format ("ReverbSend%u", nTG+1);
m_Properties.SetNumber (PropertyName, m_nReverbSend[nTG]);
#endif
PropertyName.Format ("PitchBendRange%u", nTG+1);
m_Properties.SetNumber (PropertyName, m_nPitchBendRange[nTG]);
@ -310,6 +350,7 @@ bool CPerformanceConfig::Save (void)
m_Properties.SetNumber ("CompressorEnable", m_bCompressorEnable ? 1 : 0);
#if defined(PLATE_REVERB_ENABLE) || defined(MIXING_CONSOLE_ENABLE)
m_Properties.SetNumber ("ReverbEnable", m_bReverbEnable ? 1 : 0);
m_Properties.SetNumber ("ReverbSize", m_nReverbSize);
m_Properties.SetNumber ("ReverbHighDamp", m_nReverbHighDamp);
@ -317,43 +358,64 @@ bool CPerformanceConfig::Save (void)
m_Properties.SetNumber ("ReverbLowPass", m_nReverbLowPass);
m_Properties.SetNumber ("ReverbDiffusion", m_nReverbDiffusion);
m_Properties.SetNumber ("ReverbLevel", m_nReverbLevel);
#endif
#ifdef ARM_ALLOW_MULTI_CORE
this->m_Properties.SetNumber("FXChainEnable", m_bFXChainEnable ? 1 : 0);
this->m_Properties.SetNumber("FXChainWet", m_nFXChainWet);
this->m_Properties.SetNumber("FXChainTubeEnable", m_bFXChainTubeEnable ? 1 : 0);
this->m_Properties.SetNumber("FXChainTubeWet", m_nFXChainTubeWet);
this->m_Properties.SetNumber("FXChainTubeOverdrive", m_nFXChainTubeOverdrive);
this->m_Properties.SetNumber("FXChainChorusEnable", m_bFXChainChorusEnable ? 1 : 0);
this->m_Properties.SetNumber("FXChainChorusWet", m_nFXChainChorusWet);
this->m_Properties.SetNumber("FXChainChorusRate", m_nFXChainChorusRate);
this->m_Properties.SetNumber("FXChainChorusDepth", m_nFXChainChorusDepth);
this->m_Properties.SetNumber("FXChainFlangerEnable", m_bFXChainFlangerEnable ? 1 : 0);
this->m_Properties.SetNumber("FXChainFlangerWet", m_nFXChainFlangerWet);
this->m_Properties.SetNumber("FXChainFlangerRate", m_nFXChainFlangerRate);
this->m_Properties.SetNumber("FXChainFlangerDepth", m_nFXChainFlangerDepth);
this->m_Properties.SetNumber("FXChainFlangerFeedback", m_nFXChainFlangerFeedback);
this->m_Properties.SetNumber("FXChainOrbitoneEnable", m_bFXChainOrbitoneEnable ? 1 : 0);
this->m_Properties.SetNumber("FXChainOrbitoneWet", m_nFXChainOrbitoneWet);
this->m_Properties.SetNumber("FXChainOrbitoneRate", m_nFXChainOrbitoneRate);
this->m_Properties.SetNumber("FXChainOrbitoneDepth", m_nFXChainOrbitoneDepth);
this->m_Properties.SetNumber("FXChainPhaserEnable", m_bFXChainPhaserEnable ? 1 : 0);
this->m_Properties.SetNumber("FXChainPhaserWet", m_nFXChainPhaserWet);
this->m_Properties.SetNumber("FXChainPhaserRate", m_nFXChainPhaserRate);
this->m_Properties.SetNumber("FXChainPhaserDepth", m_nFXChainPhaserDepth);
this->m_Properties.SetNumber("FXChainPhaserFeedback", m_nFXChainPhaserFeedback);
this->m_Properties.SetNumber("FXChainPhaserNbStages", m_nFXChainPhaserNbStages);
this->m_Properties.SetNumber("FXChainDelayEnable", m_bFXChainDelayEnable ? 1 : 0);
this->m_Properties.SetNumber("FXChainDelayWet", m_nFXChainDelayWet);
this->m_Properties.SetNumber("FXChainDelayLeftDelayTime", m_nFXChainDelayLeftDelayTime);
this->m_Properties.SetNumber("FXChainDelayRightDelayTime", m_nFXChainDelayRightDelayTime);
this->m_Properties.SetNumber("FXChainDelayFeedback", m_nFXChainDelayFeedback);
this->m_Properties.SetNumber("FXChainShimmerReverbEnable", m_bFXChainShimmerReverbEnable ? 1 : 0);
this->m_Properties.SetNumber("FXChainShimmerReverbWet", m_nFXChainShimmerReverbWet);
this->m_Properties.SetNumber("FXChainShimmerReverbInputGain", m_nFXChainShimmerReverbInputGain);
this->m_Properties.SetNumber("FXChainShimmerReverbTime", m_nFXChainShimmerReverbTime);
this->m_Properties.SetNumber("FXChainShimmerReverbDiffusion", m_nFXChainShimmerReverbDiffusion);
this->m_Properties.SetNumber("FXChainShimmerReverbLP", m_nFXChainShimmerReverbLP);
#if defined(MIXING_CONSOLE_ENABLE)
this->m_Properties.SetNumber("FXTubeEnable", this->m_bFXTubeEnable ? 1 : 0);
this->m_Properties.SetNumber("FXTubeOverdrive", this->m_nFXTubeOverdrive);
this->m_Properties.SetNumber("FXChorusEnable", this->m_bFXChorusEnable ? 1 : 0);
this->m_Properties.SetNumber("FXChorusRate", this->m_nFXChorusRate);
this->m_Properties.SetNumber("FXChorusDepth", this->m_nFXChorusDepth);
this->m_Properties.SetNumber("FXFlangerEnable", this->m_bFXFlangerEnable ? 1 : 0);
this->m_Properties.SetNumber("FXFlangerRate", this->m_nFXFlangerRate);
this->m_Properties.SetNumber("FXFlangerDepth", this->m_nFXFlangerDepth);
this->m_Properties.SetNumber("FXFlangerFeedback", this->m_nFXFlangerFeedback);
this->m_Properties.SetNumber("FXOrbitoneEnable", this->m_bFXOrbitoneEnable ? 1 : 0);
this->m_Properties.SetNumber("FXOrbitoneRate", this->m_nFXOrbitoneRate);
this->m_Properties.SetNumber("FXOrbitoneDepth", this->m_nFXOrbitoneDepth);
this->m_Properties.SetNumber("FXPhaserEnable", this->m_bFXPhaserEnable ? 1 : 0);
this->m_Properties.SetNumber("FXPhaserRate", this->m_nFXPhaserRate);
this->m_Properties.SetNumber("FXPhaserDepth", this->m_nFXPhaserDepth);
this->m_Properties.SetNumber("FXPhaserFeedback", this->m_nFXPhaserFeedback);
this->m_Properties.SetNumber("FXPhaserNbStages", this->m_nFXPhaserNbStages);
this->m_Properties.SetNumber("FXDelayEnable", this->m_bFXDelayEnable ? 1 : 0);
this->m_Properties.SetNumber("FXDelayLeftDelayTime", this->m_nFXDelayLeftDelayTime);
this->m_Properties.SetNumber("FXDelayRightDelayTime", this->m_nFXDelayRightDelayTime);
this->m_Properties.SetNumber("FXDelayFeedback", this->m_nFXDelayFeedback);
this->m_Properties.SetNumber("FXReverberatorEnable", this->m_bFXReverberatorEnable ? 1 : 0);
this->m_Properties.SetNumber("FXReverberatorInputGain", this->m_nFXReverberatorInputGain);
this->m_Properties.SetNumber("FXReverberatorTime", this->m_nFXReverberatorTime);
this->m_Properties.SetNumber("FXReverberatorDiffusion", this->m_nFXReverberatorDiffusion);
this->m_Properties.SetNumber("FXReverberatorLP", this->m_nFXReverberatorLP);
for(unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
{
for(size_t toFX = 0; toFX < MixerOutput::kFXCount; ++toFX)
{
CString propertyName;
propertyName.Format("FXSend_TG%u_to_%s", nTG + 1, toString(static_cast<MixerOutput>(toFX)).c_str());
this->m_Properties.SetNumber(propertyName, this->m_nTGSendLevel[nTG][toFX]);
}
}
size_t end = MixerOutput::kFXCount - 1;
for(size_t fromFX = 0; fromFX < end; ++fromFX)
{
for(size_t toFX = 0; toFX < MixerOutput::kFXCount; ++toFX)
{
CString propertyName;
propertyName.Format("FXSend_%s_to_%s", toString(static_cast<MixerOutput>(fromFX)).c_str(), toString(static_cast<MixerOutput>(toFX)).c_str());
this->m_Properties.SetNumber(propertyName, this->m_nFXSendLevel[fromFX][toFX]);
}
}
this->m_Properties.SetNumber("FXBypass", this->m_bFXBypass ? 1 : 0);
#endif
return m_Properties.Save ();
@ -425,11 +487,13 @@ int CPerformanceConfig::GetNoteShift (unsigned nTG) const
return m_nNoteShift[nTG];
}
#if defined(PLATE_REVERB_ENABLE)
unsigned CPerformanceConfig::GetReverbSend (unsigned nTG) const
{
assert (nTG < CConfig::ToneGenerators);
return m_nReverbSend[nTG];
}
#endif
void CPerformanceConfig::SetBankNumber (unsigned nValue, unsigned nTG)
{
@ -497,11 +561,13 @@ void CPerformanceConfig::SetNoteShift (int nValue, unsigned nTG)
m_nNoteShift[nTG] = nValue;
}
#if defined(PLATE_REVERB_ENABLE)
void CPerformanceConfig::SetReverbSend (unsigned nValue, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
m_nReverbSend[nTG] = nValue;
}
#endif
bool CPerformanceConfig::GetCompressorEnable (void) const
{
@ -927,9 +993,12 @@ bool CPerformanceConfig::ListPerformances()
Result = f_findfirst (&Directory, &FileInfo, "SD:/" PERFORMANCE_DIR, "*.ini");
for (unsigned i = 0; Result == FR_OK && FileInfo.fname[0]; i++)
{
if (nLastPerformance >= NUM_PERFORMANCES) {
if (nLastPerformance >= NUM_PERFORMANCES)
{
LOGNOTE ("Skipping performance %s", FileInfo.fname);
} else {
}
else
{
if (!(FileInfo.fattrib & (AM_HID | AM_SYS)))
{
std::string FileName = FileInfo.fname;
@ -952,7 +1021,7 @@ bool CPerformanceConfig::ListPerformances()
// sort by performance number-name
if (nLastPerformance > 2)
{
sort (m_nPerformanceFileName+1, m_nPerformanceFileName + nLastPerformance); // default is always on first place. %%%%%%%%%%%%%%%%
sort (m_nPerformanceFileName+1, m_nPerformanceFileName + nLastPerformance); // default is always on first place. %%%%%%%%%%%%%%%%
}
}
@ -961,7 +1030,6 @@ bool CPerformanceConfig::ListPerformances()
return nInternalFolderOk;
}
void CPerformanceConfig::SetNewPerformance (unsigned nID)
{
nActualPerformance=nID;
@ -1028,355 +1096,304 @@ bool CPerformanceConfig::DeletePerformance(unsigned nID)
return bOK;
}
#ifdef ARM_ALLOW_MULTI_CORE
bool CPerformanceConfig::GetFXChainEnable(void) const
{
return this->m_bFXChainEnable;
}
unsigned CPerformanceConfig::GetFXChainWet(void) const
{
return this->m_nFXChainWet;
}
bool CPerformanceConfig::GetFXChainTubeEnable(void) const
{
return this->m_bFXChainTubeEnable;
}
unsigned CPerformanceConfig::GetFXChainTubeWet(void) const
{
return this->m_nFXChainTubeWet;
}
unsigned CPerformanceConfig::GetFXChainTubeOverdrive(void) const
{
return this->m_nFXChainTubeOverdrive;
}
bool CPerformanceConfig::GetFXChainChorusEnable(void) const
{
return this->m_bFXChainChorusEnable;
}
unsigned CPerformanceConfig::GetFXChainChorusWet(void) const
{
return this->m_nFXChainChorusWet;
}
unsigned CPerformanceConfig::GetFXChainChorusRate(void) const
{
return this->m_nFXChainChorusRate;
}
unsigned CPerformanceConfig::GetFXChainChorusDepth(void) const
{
return this->m_nFXChainChorusDepth;
}
bool CPerformanceConfig::GetFXChainFlangerEnable(void) const
{
return this->m_bFXChainFlangerEnable;
}
unsigned CPerformanceConfig::GetFXChainFlangerWet(void) const
{
return this->m_nFXChainFlangerWet;
}
unsigned CPerformanceConfig::GetFXChainFlangerRate(void) const
{
return this->m_nFXChainFlangerRate;
}
#ifdef MIXING_CONSOLE_ENABLE
unsigned CPerformanceConfig::GetFXChainFlangerDepth(void) const
bool CPerformanceConfig::GetFXTubeEnable(void) const
{
return this->m_nFXChainFlangerDepth;
return this->m_bFXTubeEnable;
}
unsigned CPerformanceConfig::GetFXChainFlangerFeedback(void) const
unsigned CPerformanceConfig::GetFXTubeOverdrive(void) const
{
return this->m_nFXChainFlangerFeedback;
return this->m_nFXTubeOverdrive;
}
bool CPerformanceConfig::GetFXChainOrbitoneEnable(void) const
bool CPerformanceConfig::GetFXChorusEnable(void) const
{
return this->m_bFXChainOrbitoneEnable;
return this->m_bFXChorusEnable;
}
unsigned CPerformanceConfig::GetFXChainOrbitoneWet(void) const
unsigned CPerformanceConfig::GetFXChorusRate(void) const
{
return this->m_nFXChainOrbitoneWet;
return this->m_nFXChorusRate;
}
unsigned CPerformanceConfig::GetFXChainOrbitoneRate(void) const
unsigned CPerformanceConfig::GetFXChorusDepth(void) const
{
return this->m_nFXChainOrbitoneRate;
return this->m_nFXChorusDepth;
}
unsigned CPerformanceConfig::GetFXChainOrbitoneDepth(void) const
bool CPerformanceConfig::GetFXFlangerEnable(void) const
{
return this->m_nFXChainOrbitoneDepth;
return this->m_bFXFlangerEnable;
}
bool CPerformanceConfig::GetFXChainPhaserEnable(void) const
unsigned CPerformanceConfig::GetFXFlangerRate(void) const
{
return this->m_bFXChainPhaserEnable;
return this->m_nFXFlangerRate;
}
unsigned CPerformanceConfig::GetFXChainPhaserWet(void) const
unsigned CPerformanceConfig::GetFXFlangerDepth(void) const
{
return this->m_nFXChainPhaserWet;
return this->m_nFXFlangerDepth;
}
unsigned CPerformanceConfig::GetFXChainPhaserRate(void) const
unsigned CPerformanceConfig::GetFXFlangerFeedback(void) const
{
return this->m_nFXChainPhaserRate;
return this->m_nFXFlangerFeedback;
}
unsigned CPerformanceConfig::GetFXChainPhaserDepth(void) const
bool CPerformanceConfig::GetFXOrbitoneEnable(void) const
{
return this->m_nFXChainPhaserDepth;
return this->m_bFXOrbitoneEnable;
}
unsigned CPerformanceConfig::GetFXChainPhaserFeedback(void) const
unsigned CPerformanceConfig::GetFXOrbitoneRate(void) const
{
return this->m_nFXChainPhaserFeedback;
return this->m_nFXOrbitoneRate;
}
unsigned CPerformanceConfig::GetFXChainPhaserNbStages(void) const
unsigned CPerformanceConfig::GetFXOrbitoneDepth(void) const
{
return this->m_nFXChainPhaserNbStages;
return this->m_nFXOrbitoneDepth;
}
bool CPerformanceConfig::GetFXChainDelayEnable(void) const
bool CPerformanceConfig::GetFXPhaserEnable(void) const
{
return this->m_bFXChainDelayEnable;
return this->m_bFXPhaserEnable;
}
unsigned CPerformanceConfig::GetFXChainDelayWet(void) const
unsigned CPerformanceConfig::GetFXPhaserRate(void) const
{
return this->m_nFXChainDelayWet;
return this->m_nFXPhaserRate;
}
unsigned CPerformanceConfig::GetFXChainDelayLeftDelayTime(void) const
unsigned CPerformanceConfig::GetFXPhaserDepth(void) const
{
return this->m_nFXChainDelayLeftDelayTime;
return this->m_nFXPhaserDepth;
}
unsigned CPerformanceConfig::GetFXChainDelayRightDelayTime(void) const
unsigned CPerformanceConfig::GetFXPhaserFeedback(void) const
{
return this->m_nFXChainDelayRightDelayTime;
return this->m_nFXPhaserFeedback;
}
unsigned CPerformanceConfig::GetFXChainDelayFeedback(void) const
unsigned CPerformanceConfig::GetFXPhaserNbStages(void) const
{
return this->m_nFXChainDelayFeedback;
return this->m_nFXPhaserNbStages;
}
bool CPerformanceConfig::GetFXChainShimmerReverbEnable(void) const
bool CPerformanceConfig::GetFXDelayEnable(void) const
{
return this->m_bFXChainShimmerReverbEnable;
return this->m_bFXDelayEnable;
}
unsigned CPerformanceConfig::GetFXChainShimmerReverbWet(void) const
unsigned CPerformanceConfig::GetFXDelayLeftDelayTime(void) const
{
return this->m_nFXChainShimmerReverbWet;
return this->m_nFXDelayLeftDelayTime;
}
unsigned CPerformanceConfig::GetFXChainShimmerReverbInputGain(void) const
unsigned CPerformanceConfig::GetFXDelayRightDelayTime(void) const
{
return this->m_nFXChainShimmerReverbInputGain;
return this->m_nFXDelayRightDelayTime;
}
unsigned CPerformanceConfig::GetFXChainShimmerReverbTime(void) const
unsigned CPerformanceConfig::GetFXDelayFeedback(void) const
{
return this->m_nFXChainShimmerReverbTime;
return this->m_nFXDelayFeedback;
}
unsigned CPerformanceConfig::GetFXChainShimmerReverbDiffusion(void) const
bool CPerformanceConfig::GetFXReverberatorEnable(void) const
{
return this->m_nFXChainShimmerReverbDiffusion;
return this->m_bFXReverberatorEnable;
}
unsigned CPerformanceConfig::GetFXChainShimmerReverbLP(void) const
unsigned CPerformanceConfig::GetFXReverberatorInputGain(void) const
{
return this->m_nFXChainShimmerReverbLP;
return this->m_nFXReverberatorInputGain;
}
void CPerformanceConfig::SetFXChainEnable(bool bValue)
unsigned CPerformanceConfig::GetFXReverberatorTime(void) const
{
this->m_bFXChainEnable = bValue;
return this->m_nFXReverberatorTime;
}
void CPerformanceConfig::SetFXChainWet(unsigned nValue)
unsigned CPerformanceConfig::GetFXReverberatorDiffusion(void) const
{
this->m_nFXChainWet = nValue;
return this->m_nFXReverberatorDiffusion;
}
void CPerformanceConfig::SetFXChainTubeEnable(bool bValue)
unsigned CPerformanceConfig::GetFXReverberatorLP(void) const
{
this->m_bFXChainTubeEnable = bValue;
return this->m_nFXReverberatorLP;
}
void CPerformanceConfig::SetFXChainTubeWet(unsigned nValue)
unsigned CPerformanceConfig::GetTGSendLevel(unsigned in, MixerOutput fx) const
{
this->m_nFXChainTubeWet = nValue;
assert(in < CConfig::ToneGenerators);
assert(fx < MixerOutput::kFXCount);
return this->m_nTGSendLevel[in][fx];
}
void CPerformanceConfig::SetFXChainTubeOverdrive(unsigned nValue)
unsigned CPerformanceConfig::GetFXSendLevel(MixerOutput fromFX, MixerOutput toFX) const
{
this->m_nFXChainTubeOverdrive = nValue;
assert(fromFX < (MixerOutput::kFXCount - 1));
assert(toFX < MixerOutput::kFXCount);
return (fromFX == toFX) ? 0 : this->m_nFXSendLevel[fromFX][toFX];
}
void CPerformanceConfig::SetFXChainChorusEnable(bool bValue)
void CPerformanceConfig::SetFXTubeEnable(bool bValue)
{
this->m_bFXChainChorusEnable = bValue;
this->m_bFXTubeEnable = bValue;
}
void CPerformanceConfig::SetFXChainChorusWet(unsigned nValue)
void CPerformanceConfig::SetFXTubeOverdrive(unsigned nValue)
{
this->m_nFXChainChorusWet = nValue;
this->m_nFXTubeOverdrive = nValue;
}
void CPerformanceConfig::SetFXChainChorusRate(unsigned nValue)
void CPerformanceConfig::SetFXChorusEnable(bool bValue)
{
this->m_nFXChainChorusRate = nValue;
this->m_bFXChorusEnable = bValue;
}
void CPerformanceConfig::SetFXChainChorusDepth(unsigned nValue)
void CPerformanceConfig::SetFXChorusRate(unsigned nValue)
{
this->m_nFXChainChorusDepth = nValue;
this->m_nFXChorusRate = nValue;
}
void CPerformanceConfig::SetFXChainFlangerEnable(bool bValue)
void CPerformanceConfig::SetFXChorusDepth(unsigned nValue)
{
this->m_bFXChainFlangerEnable = bValue;
this->m_nFXChorusDepth = nValue;
}
void CPerformanceConfig::SetFXChainFlangerWet(unsigned nValue)
void CPerformanceConfig::SetFXFlangerEnable(bool bValue)
{
this->m_nFXChainFlangerWet = nValue;
this->m_bFXFlangerEnable = bValue;
}
void CPerformanceConfig::SetFXChainFlangerRate(unsigned nValue)
void CPerformanceConfig::SetFXFlangerRate(unsigned nValue)
{
this->m_nFXChainFlangerRate = nValue;
this->m_nFXFlangerRate = nValue;
}
void CPerformanceConfig::SetFXChainFlangerDepth(unsigned nValue)
void CPerformanceConfig::SetFXFlangerDepth(unsigned nValue)
{
this->m_nFXChainFlangerDepth = nValue;
this->m_nFXFlangerDepth = nValue;
}
void CPerformanceConfig::SetFXChainFlangerFeedback(unsigned nValue)
void CPerformanceConfig::SetFXFlangerFeedback(unsigned nValue)
{
this->m_nFXChainFlangerFeedback = nValue;
this->m_nFXFlangerFeedback = nValue;
}
void CPerformanceConfig::SetFXChainOrbitoneEnable(bool bValue)
void CPerformanceConfig::SetFXOrbitoneEnable(bool bValue)
{
this->m_bFXChainOrbitoneEnable = bValue;
this->m_bFXOrbitoneEnable = bValue;
}
void CPerformanceConfig::SetFXChainOrbitoneWet(unsigned nValue)
void CPerformanceConfig::SetFXOrbitoneRate(unsigned nValue)
{
this->m_nFXChainOrbitoneWet = nValue;
this->m_nFXOrbitoneRate = nValue;
}
void CPerformanceConfig::SetFXChainOrbitoneRate(unsigned nValue)
void CPerformanceConfig::SetFXOrbitoneDepth(unsigned nValue)
{
this->m_nFXChainOrbitoneRate = nValue;
this->m_nFXOrbitoneDepth = nValue;
}
void CPerformanceConfig::SetFXChainOrbitoneDepth(unsigned nValue)
void CPerformanceConfig::SetFXPhaserEnable(bool bValue)
{
this->m_nFXChainOrbitoneDepth = nValue;
this->m_bFXPhaserEnable = bValue;
}
void CPerformanceConfig::SetFXChainPhaserEnable(bool bValue)
void CPerformanceConfig::SetFXPhaserRate(unsigned nValue)
{
this->m_bFXChainPhaserEnable = bValue;
this->m_nFXPhaserRate = nValue;
}
void CPerformanceConfig::SetFXChainPhaserWet(unsigned nValue)
void CPerformanceConfig::SetFXPhaserDepth(unsigned nValue)
{
this->m_nFXChainPhaserWet = nValue;
this->m_nFXPhaserDepth = nValue;
}
void CPerformanceConfig::SetFXChainPhaserRate(unsigned nValue)
void CPerformanceConfig::SetFXPhaserFeedback(unsigned nValue)
{
this->m_nFXChainPhaserRate = nValue;
this->m_nFXPhaserFeedback = nValue;
}
void CPerformanceConfig::SetFXChainPhaserDepth(unsigned nValue)
void CPerformanceConfig::SetFXPhaserNbStages(unsigned nValue)
{
this->m_nFXChainPhaserDepth = nValue;
this->m_nFXPhaserNbStages = nValue;
}
void CPerformanceConfig::SetFXChainPhaserFeedback(unsigned nValue)
void CPerformanceConfig::SetFXDelayEnable(unsigned bValue)
{
this->m_nFXChainPhaserFeedback = nValue;
this->m_bFXDelayEnable = bValue;
}
void CPerformanceConfig::SetFXChainPhaserNbStages(unsigned nValue)
void CPerformanceConfig::SetFXDelayLeftDelayTime(unsigned nValue)
{
this->m_nFXChainPhaserNbStages = nValue;
this->m_nFXDelayLeftDelayTime = nValue;
}
void CPerformanceConfig::SetFXChainDelayEnable(unsigned bValue)
void CPerformanceConfig::SetFXDelayRightDelayTime(unsigned nValue)
{
this->m_bFXChainDelayEnable = bValue;
this->m_nFXDelayRightDelayTime = nValue;
}
void CPerformanceConfig::SetFXChainDelayWet(unsigned nValue)
void CPerformanceConfig::SetFXDelayFeedback(unsigned nValue)
{
this->m_nFXChainDelayWet = nValue;
this->m_nFXDelayFeedback = nValue;
}
void CPerformanceConfig::SetFXChainDelayLeftDelayTime(unsigned nValue)
void CPerformanceConfig::SetFXReverberatorEnable(unsigned bValue)
{
this->m_nFXChainDelayLeftDelayTime = nValue;
this->m_bFXReverberatorEnable = bValue;
}
void CPerformanceConfig::SetFXChainDelayRightDelayTime(unsigned nValue)
void CPerformanceConfig::SetFXReverberatorInputGain(unsigned nValue)
{
this->m_nFXChainDelayRightDelayTime = nValue;
this->m_nFXReverberatorInputGain = nValue;
}
void CPerformanceConfig::SetFXChainDelayFeedback(unsigned nValue)
void CPerformanceConfig::SetFXReverberatorTime(unsigned nValue)
{
this->m_nFXChainDelayFeedback = nValue;
this->m_nFXReverberatorTime = nValue;
}
void CPerformanceConfig::SetFXChainShimmerReverbEnable(unsigned bValue)
void CPerformanceConfig::SetFXReverberatorDiffusion(unsigned nValue)
{
this->m_bFXChainShimmerReverbEnable = bValue;
this->m_nFXReverberatorDiffusion = nValue;
}
void CPerformanceConfig::SetFXChainShimmerReverbWet(unsigned nValue)
void CPerformanceConfig::SetFXReverberatorLP(unsigned nValue)
{
this->m_nFXChainShimmerReverbWet = nValue;
this->m_nFXReverberatorLP = nValue;
}
void CPerformanceConfig::SetFXChainShimmerReverbInputGain(unsigned nValue)
void CPerformanceConfig::SetTGSendLevel(unsigned in, MixerOutput fx, unsigned nValue)
{
this->m_nFXChainShimmerReverbInputGain = nValue;
assert(in < CConfig::ToneGenerators);
assert(fx < MixerOutput::kFXCount);
this->m_nTGSendLevel[in][fx] = nValue;
}
void CPerformanceConfig::SetFXChainShimmerReverbTime(unsigned nValue)
void CPerformanceConfig::SetFXSendLevel(MixerOutput fromFX, MixerOutput toFX, unsigned nValue)
{
this->m_nFXChainShimmerReverbTime = nValue;
assert(fromFX < (MixerOutput::kFXCount - 1));
assert(toFX < MixerOutput::kFXCount);
this->m_nFXSendLevel[fromFX][toFX] = (fromFX == toFX) ? 0 : nValue;
}
void CPerformanceConfig::SetFXChainShimmerReverbDiffusion(unsigned nValue)
void CPerformanceConfig::SetFXBypass(bool bypass)
{
this->m_nFXChainShimmerReverbDiffusion = nValue;
this->m_bFXBypass = bypass;
}
void CPerformanceConfig::SetFXChainShimmerReverbLP(unsigned nValue)
bool CPerformanceConfig::IsFXBypass() const
{
this->m_nFXChainShimmerReverbLP = nValue;
return this->m_bFXBypass;
}
#endif

@ -24,6 +24,7 @@
#define _performanceconfig_h
#include "config.h"
#include "mixing_console_constants.h"
#include <fatfs/ff.h>
#include <Properties/propertiesfatfsfile.h>
#define NUM_VOICE_PARAM 156
@ -52,7 +53,9 @@ public:
unsigned GetNoteLimitLow (unsigned nTG) const; // 0 .. 127
unsigned GetNoteLimitHigh (unsigned nTG) const; // 0 .. 127
int GetNoteShift (unsigned nTG) const; // -24 .. 24
#if defined(PLATE_REVERB_ENABLE)
unsigned GetReverbSend (unsigned nTG) const; // 0 .. 127
#endif
unsigned GetPitchBendRange (unsigned nTG) const; // 0 .. 12
unsigned GetPitchBendStep (unsigned nTG) const; // 0 .. 12
unsigned GetPortamentoMode (unsigned nTG) const; // 0 .. 1
@ -80,7 +83,9 @@ public:
void SetNoteLimitLow (unsigned nValue, unsigned nTG);
void SetNoteLimitHigh (unsigned nValue, unsigned nTG);
void SetNoteShift (int nValue, unsigned nTG);
#if defined(PLATE_REVERB_ENABLE)
void SetReverbSend (unsigned nValue, unsigned nTG);
#endif
void SetPitchBendRange (unsigned nValue, unsigned nTG);
void SetPitchBendStep (unsigned nValue, unsigned nTG);
void SetPortamentoMode (unsigned nValue, unsigned nTG);
@ -118,78 +123,80 @@ public:
void SetReverbDiffusion (unsigned nValue);
void SetReverbLevel (unsigned nValue);
#ifdef ARM_ALLOW_MULTI_CORE
bool GetFXChainEnable(void) const;
unsigned GetFXChainWet(void) const;
bool GetFXChainTubeEnable(void) const;
unsigned GetFXChainTubeWet(void) const;
unsigned GetFXChainTubeOverdrive(void) const;
bool GetFXChainChorusEnable(void) const;
unsigned GetFXChainChorusWet(void) const;
unsigned GetFXChainChorusRate(void) const;
unsigned GetFXChainChorusDepth(void) const;
bool GetFXChainFlangerEnable(void) const;
unsigned GetFXChainFlangerWet(void) const;
unsigned GetFXChainFlangerRate(void) const;
unsigned GetFXChainFlangerDepth(void) const;
unsigned GetFXChainFlangerFeedback(void) const;
bool GetFXChainOrbitoneEnable(void) const;
unsigned GetFXChainOrbitoneWet(void) const;
unsigned GetFXChainOrbitoneRate(void) const;
unsigned GetFXChainOrbitoneDepth(void) const;
bool GetFXChainPhaserEnable(void) const;
unsigned GetFXChainPhaserWet(void) const;
unsigned GetFXChainPhaserRate(void) const;
unsigned GetFXChainPhaserDepth(void) const;
unsigned GetFXChainPhaserFeedback(void) const;
unsigned GetFXChainPhaserNbStages(void) const;
bool GetFXChainDelayEnable(void) const;
unsigned GetFXChainDelayWet(void) const;
unsigned GetFXChainDelayLeftDelayTime(void) const;
unsigned GetFXChainDelayRightDelayTime(void) const;
unsigned GetFXChainDelayFeedback(void) const;
bool GetFXChainShimmerReverbEnable(void) const;
unsigned GetFXChainShimmerReverbWet(void) const;
unsigned GetFXChainShimmerReverbInputGain(void) const;
unsigned GetFXChainShimmerReverbTime(void) const;
unsigned GetFXChainShimmerReverbDiffusion(void) const;
unsigned GetFXChainShimmerReverbLP(void) const;
void SetFXChainEnable(bool bValue);
void SetFXChainWet(unsigned nValue);
void SetFXChainTubeEnable(bool bValue);
void SetFXChainTubeWet(unsigned nValue);
void SetFXChainTubeOverdrive(unsigned nValue);
void SetFXChainChorusEnable(bool bValue);
void SetFXChainChorusWet(unsigned nValue);
void SetFXChainChorusRate(unsigned nValue);
void SetFXChainChorusDepth(unsigned nValue);
void SetFXChainFlangerEnable(bool bValue);
void SetFXChainFlangerWet(unsigned nValue);
void SetFXChainFlangerRate(unsigned nValue);
void SetFXChainFlangerDepth(unsigned nValue);
void SetFXChainFlangerFeedback(unsigned nValue);
void SetFXChainOrbitoneEnable(bool bValue);
void SetFXChainOrbitoneWet(unsigned nValue);
void SetFXChainOrbitoneRate(unsigned nValue);
void SetFXChainOrbitoneDepth(unsigned nValue);
void SetFXChainPhaserEnable(bool bValue);
void SetFXChainPhaserWet(unsigned nValue);
void SetFXChainPhaserRate(unsigned nValue);
void SetFXChainPhaserDepth(unsigned nValue);
void SetFXChainPhaserFeedback(unsigned nValue);
void SetFXChainPhaserNbStages(unsigned nValue);
void SetFXChainDelayEnable(unsigned nValue);
void SetFXChainDelayWet(unsigned nValue);
void SetFXChainDelayLeftDelayTime(unsigned nValue);
void SetFXChainDelayRightDelayTime(unsigned nValue);
void SetFXChainDelayFeedback(unsigned nValue);
void SetFXChainShimmerReverbEnable(unsigned nValue);
void SetFXChainShimmerReverbWet(unsigned nValue);
void SetFXChainShimmerReverbInputGain(unsigned nValue);
void SetFXChainShimmerReverbTime(unsigned nValue);
void SetFXChainShimmerReverbDiffusion(unsigned nValue);
void SetFXChainShimmerReverbLP(unsigned nValue);
#ifdef MIXING_CONSOLE_ENABLE
bool GetFXTubeEnable(void) const;
unsigned GetFXTubeOverdrive(void) const;
bool GetFXChorusEnable(void) const;
unsigned GetFXChorusRate(void) const;
unsigned GetFXChorusDepth(void) const;
bool GetFXFlangerEnable(void) const;
unsigned GetFXFlangerRate(void) const;
unsigned GetFXFlangerDepth(void) const;
unsigned GetFXFlangerFeedback(void) const;
bool GetFXOrbitoneEnable(void) const;
unsigned GetFXOrbitoneRate(void) const;
unsigned GetFXOrbitoneDepth(void) const;
bool GetFXPhaserEnable(void) const;
unsigned GetFXPhaserRate(void) const;
unsigned GetFXPhaserDepth(void) const;
unsigned GetFXPhaserFeedback(void) const;
unsigned GetFXPhaserNbStages(void) const;
bool GetFXDelayEnable(void) const;
unsigned GetFXDelayLeftDelayTime(void) const;
unsigned GetFXDelayRightDelayTime(void) const;
unsigned GetFXDelayFeedback(void) const;
bool GetFXReverberatorEnable(void) const;
unsigned GetFXReverberatorInputGain(void) const;
unsigned GetFXReverberatorTime(void) const;
unsigned GetFXReverberatorDiffusion(void) const;
unsigned GetFXReverberatorLP(void) const;
unsigned GetTGSendLevel(unsigned in, MixerOutput fx) const;
unsigned GetFXSendLevel(MixerOutput ret, MixerOutput fx) const;
void SetFXTubeEnable(bool bValue);
void SetFXTubeOverdrive(unsigned nValue);
void SetFXChorusEnable(bool bValue);
void SetFXChorusRate(unsigned nValue);
void SetFXChorusDepth(unsigned nValue);
void SetFXFlangerEnable(bool bValue);
void SetFXFlangerRate(unsigned nValue);
void SetFXFlangerDepth(unsigned nValue);
void SetFXFlangerFeedback(unsigned nValue);
void SetFXOrbitoneEnable(bool bValue);
void SetFXOrbitoneRate(unsigned nValue);
void SetFXOrbitoneDepth(unsigned nValue);
void SetFXPhaserEnable(bool bValue);
void SetFXPhaserRate(unsigned nValue);
void SetFXPhaserDepth(unsigned nValue);
void SetFXPhaserFeedback(unsigned nValue);
void SetFXPhaserNbStages(unsigned nValue);
void SetFXDelayEnable(unsigned nValue);
void SetFXDelayLeftDelayTime(unsigned nValue);
void SetFXDelayRightDelayTime(unsigned nValue);
void SetFXDelayFeedback(unsigned nValue);
void SetFXReverberatorEnable(unsigned nValue);
void SetFXReverberatorInputGain(unsigned nValue);
void SetFXReverberatorTime(unsigned nValue);
void SetFXReverberatorDiffusion(unsigned nValue);
void SetFXReverberatorLP(unsigned nValue);
void SetTGSendLevel(unsigned in, MixerOutput fx, unsigned nValue);
void SetFXSendLevel(MixerOutput fromFX, MixerOutput toFX, unsigned nValue);
void SetFXBypass(bool bypass);
bool IsFXBypass() const;
#endif
bool VoiceDataFilled(unsigned nTG);
@ -222,7 +229,9 @@ private:
unsigned m_nNoteLimitLow[CConfig::ToneGenerators];
unsigned m_nNoteLimitHigh[CConfig::ToneGenerators];
int m_nNoteShift[CConfig::ToneGenerators];
#if defined(PLATE_REVERB_ENABLE)
int m_nReverbSend[CConfig::ToneGenerators];
#endif
unsigned m_nPitchBendRange[CConfig::ToneGenerators];
unsigned m_nPitchBendStep[CConfig::ToneGenerators];
unsigned m_nPortamentoMode[CConfig::ToneGenerators];
@ -260,42 +269,46 @@ private:
unsigned m_nReverbDiffusion;
unsigned m_nReverbLevel;
#ifdef ARM_ALLOW_MULTI_CORE
bool m_bFXChainEnable;
unsigned m_nFXChainWet;
bool m_bFXChainTubeEnable;
unsigned m_nFXChainTubeWet;
unsigned m_nFXChainTubeOverdrive;
bool m_bFXChainChorusEnable;
unsigned m_nFXChainChorusWet;
unsigned m_nFXChainChorusRate;
unsigned m_nFXChainChorusDepth;
bool m_bFXChainFlangerEnable;
unsigned m_nFXChainFlangerWet;
unsigned m_nFXChainFlangerRate;
unsigned m_nFXChainFlangerDepth;
unsigned m_nFXChainFlangerFeedback;
bool m_bFXChainOrbitoneEnable;
unsigned m_nFXChainOrbitoneWet;
unsigned m_nFXChainOrbitoneRate;
unsigned m_nFXChainOrbitoneDepth;
bool m_bFXChainPhaserEnable;
unsigned m_nFXChainPhaserWet;
unsigned m_nFXChainPhaserRate;
unsigned m_nFXChainPhaserDepth;
unsigned m_nFXChainPhaserFeedback;
unsigned m_nFXChainPhaserNbStages;
bool m_bFXChainDelayEnable;
unsigned m_nFXChainDelayWet;
unsigned m_nFXChainDelayLeftDelayTime;
unsigned m_nFXChainDelayRightDelayTime;
unsigned m_nFXChainDelayFeedback;
bool m_bFXChainShimmerReverbEnable;
unsigned m_nFXChainShimmerReverbWet;
unsigned m_nFXChainShimmerReverbInputGain;
unsigned m_nFXChainShimmerReverbTime;
unsigned m_nFXChainShimmerReverbDiffusion;
unsigned m_nFXChainShimmerReverbLP;
#if defined(MIXING_CONSOLE_ENABLE)
bool m_bFXTubeEnable;
unsigned m_nFXTubeWet;
unsigned m_nFXTubeOverdrive;
bool m_bFXChorusEnable;
unsigned m_nFXChorusRate;
unsigned m_nFXChorusDepth;
bool m_bFXFlangerEnable;
unsigned m_nFXFlangerRate;
unsigned m_nFXFlangerDepth;
unsigned m_nFXFlangerFeedback;
bool m_bFXOrbitoneEnable;
unsigned m_nFXOrbitoneRate;
unsigned m_nFXOrbitoneDepth;
bool m_bFXPhaserEnable;
unsigned m_nFXPhaserRate;
unsigned m_nFXPhaserDepth;
unsigned m_nFXPhaserFeedback;
unsigned m_nFXPhaserNbStages;
bool m_bFXDelayEnable;
unsigned m_nFXDelayLeftDelayTime;
unsigned m_nFXDelayRightDelayTime;
unsigned m_nFXDelayFeedback;
bool m_bFXReverberatorEnable;
unsigned m_nFXReverberatorInputGain;
unsigned m_nFXReverberatorTime;
unsigned m_nFXReverberatorDiffusion;
unsigned m_nFXReverberatorLP;
unsigned m_nTGSendLevel[CConfig::ToneGenerators + MixerOutput::kFXCount - 1][MixerOutput::kFXCount];
unsigned m_nFXSendLevel[MixerOutput::kFXCount - 1][MixerOutput::kFXCount];
bool m_bFXBypass;
#endif
};

@ -6,8 +6,23 @@ EXE := $(BINDIR)/all_tests.bin
BETA := $(BINDIR)/beta.bin
CXX = g++
CXXFLAGS = -g -Wall -std=c++20 -MMD -MP
DEFINES = -DCPU=x86 -DDEBUG -DOUTPUT_FOLDER="\"$(OUTPUT_FOLDER)\""
CXXFLAGS = -g -std=c++20 -MMD -MP -Wall -Wextra -Wformat-nonliteral -Wcast-align -Wpointer-arith -Wmissing-declarations -Winline -Wundef -Wno-unused-parameter
DEFINES = -DCPU=x86 -DOUTPUT_FOLDER="\"$(OUTPUT_FOLDER)\""
ifeq ($(shell echo $(MODE) | tr '[:upper:]' '[:lower:]'), release)
# RELEASE
CXXFLAGS += -O3
DEFINES += -DNDEBUG
else
# DEBUG
CXXFLAGS += -g3
DEFINES += -DDEBUG
endif
INCLUDES = -I../../CMSIS_5/CMSIS/DSP/Include/ \
-I../../CMSIS_5/CMSIS/Core/Include/ \
-I../../Synth_Dexed/src/
@ -34,7 +49,12 @@ FX__SRCS += ../fx_orbitone.cpp
FX__SRCS += ../fx_flanger.cpp
FX__SRCS += ../fx_delay.cpp
FX__SRCS += ../effect_platervbstereo.cpp
FX__SRCS += ../fx_shimmer_helper.cpp
FX__SRCS += ../fx_diffuser.cpp
FX__SRCS += ../fx_pitch_shifter.cpp
FX__SRCS += ../fx_reverberator.cpp
FX__SRCS += ../fx_shimmer_reverb.cpp
FX__SRCS += ../fx_dry.cpp
FX__SRCS += ../fx_rack.cpp
TST_SRCS := $(filter-out waveplay.cpp $(wildcard beta*.cpp), $(wildcard *.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)

@ -0,0 +1,390 @@
#include "test_fx_helper.h"
#include <fstream>
#include <sstream>
#include <iomanip>
class FastLFODebugger
{
public:
FastLFODebugger(float32_t sampling_rate, float32_t min_frequency, float32_t max_frequency, float32_t initial_phase, bool centered) :
SamplingFrequency(sampling_rate),
InitialPhase(initial_phase),
min_frequency_(min_frequency),
max_frequency_(max_frequency),
centered_(centered),
frequency_(0.0f),
nb_sub_increment_(1),
sub_increment_(0),
y0_(0.0f),
y1_(0.0f),
iir_coefficient_(0.0f),
initial_amplitude_(0.0f)
{
assert(this->min_frequency_ <= this->max_frequency_);
assert(this->max_frequency_ < sampling_rate / 2.0f);
this->setFrequency(this->min_frequency_);
}
~FastLFODebugger()
{
}
inline float32_t getSamplingRate() const
{
return this->SamplingFrequency;
}
void setNormalizedFrequency(float32_t normalized_frequency)
{
normalized_frequency = constrain(normalized_frequency, 0.0f, 1.0f);
if(this->normalized_frequency_ != normalized_frequency)
{
float32_t frequency = mapfloat(normalized_frequency, 0.0f, 1.0f, this->min_frequency_, this->max_frequency_);
this->normalized_frequency_ = normalized_frequency;
this->frequency_ = frequency;
this->unitary_frequency_ = this->frequency_ / this->getSamplingRate();
this->nb_sub_increment_ = (frequency >= 3.0f ? 1 : 300);
this->unitary_frequency_ *= this->nb_sub_increment_;
this->updateCoefficient(1.0f);
}
}
float32_t getNormalizedFrequency() const
{
return this->normalized_frequency_;
}
void setFrequency(float32_t frequency)
{
frequency = constrain(frequency, this->min_frequency_, this->max_frequency_);
if(this->frequency_ != frequency)
{
float32_t normalized_frequency = mapfloat(frequency, this->min_frequency_, this->max_frequency_, 0.0f, 1.0f);
this->normalized_frequency_ = normalized_frequency;
this->frequency_ = frequency;
this->unitary_frequency_ = this->frequency_ / this->getSamplingRate();
this->nb_sub_increment_ = (frequency >= 3.0f ? 1 : 300);
this->unitary_frequency_ *= this->nb_sub_increment_;
this->updateCoefficient(1.0f);
}
}
float32_t getFrequency() const
{
return this->frequency_;
}
void updateCoefficient(float32_t correction_ratio)
{
float32_t frequency = this->unitary_frequency_ * correction_ratio;
float32_t sign = 16.0f;
frequency -= 0.25f;
if(frequency < 0.0f)
{
frequency = -frequency;
}
else
{
if(frequency > 0.5f)
{
frequency -= 0.5f;
}
else
{
sign = -16.0f;
}
}
this->iir_coefficient_ = sign * frequency * (1.0f - 2.0f * frequency);
this->initial_amplitude_ = this->iir_coefficient_ * 0.25f;
this->reset(correction_ratio);
}
void reset(float32_t correction_ratio = 1.0f)
{
// static const float32_t epsilon = 1e-7;
this->sub_increment_ = 0.0f;
// computing cos(0) = sin(-PI/2)
this->y1_ = this->initial_amplitude_;
this->y0_ = 0.5f;
// if(this->unitary_frequency_ == 0.0f)
// {
// return;
// }
// float32_t p_i = 2.0f * PI * this->unitary_frequency_ * correction_ratio / static_cast<float32_t>(this->nb_sub_increment_);
// float32_t p = PI / 2.0f;
// float32_t t_p = this->InitialPhase;
// if(t_p < p)
// {
// p -= 2.0f* PI;
// }
// float32_t current = 3.0f;
// while(abs(current, sin(this->InitialPhase)) > epsilon)
// {
// std::cout << "phase: " << p << std::endl;
// this->process();
// p += p_i;
// if(p > (6.0f * PI))
// cout << "ERROR: FLO is not precise enough" <<
// return;
// }
}
float32_t process()
{
float32_t temp = this->y0_;
float32_t current = temp + 0.5f;
if(this->centered_)
{
current = current * 2.0f - 1.0f;
}
if(this->sub_increment_ == 0)
{
this->y0_ = this->iir_coefficient_ * this->y0_ - this->y1_;
this->y1_ = temp;
this->current_ = current;
return current;
}
this->sub_increment_++;
if(this->sub_increment_ >= this->nb_sub_increment_)
{
this->sub_increment_ = 0;
}
return mapfloat(this->sub_increment_, 0, this->nb_sub_increment_, this->current_, current);
}
float32_t current() const
{
return this->current_;
}
private:
const float32_t SamplingFrequency;
const float32_t InitialPhase;
const float32_t min_frequency_;
const float32_t max_frequency_;
const bool centered_;
float32_t frequency_;
float32_t normalized_frequency_;
float32_t unitary_frequency_;
size_t nb_sub_increment_;
size_t sub_increment_;
float32_t y0_;
float32_t y1_;
float32_t iir_coefficient_;
float32_t initial_amplitude_;
float32_t current_;
};
void updateCorrectionStep(float32_t& sign, float32_t& correction_ratio_step, int direction)
{
if(sign == direction)
{
// does nothing
}
else if(sign == -direction)
{
sign = static_cast<float32_t>(direction);
correction_ratio_step /= 2.0f;
}
else
{
sign = static_cast<float32_t>(direction);
}
if(direction > 0)
{
std::cout << "LFO is too slow - correction ratio step becomes: " << (sign * correction_ratio_step);
}
else if(direction < 0)
{
std::cout << "LFO is too fast - correction ratio step becomes: " << (sign * correction_ratio_step);
}
}
TEST(BetaTesta, FastLFO)
{
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info();
std::string full_test_name = test_info->test_case_name();
full_test_name += ".";
full_test_name += test_info->name();
size_t NB = static_cast<size_t>(1.0f * SAMPLING_FREQUENCY);
const float32_t freq = 1.5f;
const float32_t init_phase = PI / 2.0f;
float32_t correction_ratio = 1.0f;
float32_t correction_ratio_step = 8.0f/ SAMPLING_FREQUENCY;
FastLFODebugger lfo1(SAMPLING_FREQUENCY, freq, 440.0f, init_phase, true);
lfo1.setFrequency(freq);
const float32_t epsilon = 1e-3;
int nbTrials = 100000;
float32_t maxDiff;
float32_t sign = 0.0f;
float32_t phase;
float32_t phase_increment;
size_t maxOK = 0;
float32_t best_correction = correction_ratio;
while(nbTrials > 0)
{
maxDiff = 0.0f;
phase = init_phase;
correction_ratio += sign * correction_ratio_step;
std::cout << std::setprecision(9) << std::fixed << " - Testing correction_ratio: " << correction_ratio << std::endl;
lfo1.updateCoefficient(correction_ratio);
phase_increment = freq / SAMPLING_FREQUENCY;
for(size_t i = 0; i < NB; ++i)
{
float32_t v1 = lfo1.process();
float32_t v2 = sin(phase);
// std::cout << std::setprecision(9) << std::fixed << " + phase: " << phase << " // v1: " << v1 << " / v2: " << v2 << " => diff: " << (v2 - v1);
float32_t diff = abs(v1 - v2);
if(diff > maxDiff) maxDiff = diff;
// std::cout << " - OK: " << ((diff < epsilon) ? "Yes" : "No") << std::endl;
if(diff > epsilon)
{
if(maxOK < i)
{
maxOK = i + 1;
best_correction = correction_ratio;
}
int quater = 0;
if(phase > (PI / 2.0f)) ++quater;
if(phase > PI) ++quater;
if(phase > (3.0f * PI / 2.0f)) ++quater;
if(v1 < v2)
{
switch (quater)
{
case 0:
case 4:
// Sinus phase [0, PI / 2] => [0.00, 1.00]
// Sinus phase [3 * PI / 2, 2 * PI] => [-1.00, 0.00]
// lfo1 is too slow
updateCorrectionStep(sign, correction_ratio_step, +1);
break;
case 1:
case 3:
// Sinus phase [PI / 2, PI] => [1.00, 0.00]
// Sinus phase [PI, 3 * PI / 2] => [0.00, -1.00]
// lfo1 is too fast
updateCorrectionStep(sign, correction_ratio_step, -1);
break;
default:
FAIL() << "Issue on phase: " << phase;
break;
}
break;
}
else
{
switch (quater)
{
case 0:
case 4:
// Sinus phase [0, PI / 2] => [0.00, 1.00]
// Sinus phase [3 * PI / 2, 2 * PI] => [-1.00, 0.00]
// lfo1 is too fast
updateCorrectionStep(sign, correction_ratio_step, -1);
break;
case 1:
case 3:
// Sinus phase [PI / 2, PI] => [1.00, 0.00]
// Sinus phase [PI, 3 * PI / 2] => [0.00, -1.00]
// lfo1 is too slow
updateCorrectionStep(sign, correction_ratio_step, +1);
break;
default:
FAIL() << "Issue on phase: " << phase;
break;
}
break;
}
}
if(correction_ratio_step < 1e-9) FAIL() << "correction_ratio_step became too small. maxOK = " << maxOK << " with best_correction = " << best_correction;
phase += phase_increment;
if(phase > 2.0f * PI) phase -= 2.0f * PI;
}
--nbTrials;
}
if(nbTrials > -2)
std::cout << "Correct correction ratio = " << correction_ratio << " with maxDiff = " << maxDiff << std::endl;
else
std::cout << "No matching correction ratio" << std::endl;
std::cout << "maxOK = " << maxOK << " with best_correction = " << best_correction << std::endl;;
// std::stringstream ssFst;
// std::stringstream ssSin;
// for(size_t i = 0; i < NB; ++i)
// {
// ssFst << lfo1.process() << (i == (NB - 1) ? "" : ", ");
// ssSin << sin(2.0f * PI * freq * i / SAMPLING_FREQUENCY + init_phase) << (i == (NB - 1) ? "" : ", ");
// }
// std::ofstream _fst(getResultFile(full_test_name + ".fst.data", true));
// _fst << ssFst.str();
// _fst.close();
// std::ofstream _sin(getResultFile(full_test_name + ".sin.data", true));
// _sin << ssSin.str();
// _sin.close();
// std::ofstream out(getResultFile(full_test_name + ".data.m", true));
// out << "# m file to tune FastLFO component" << std::endl << std::endl;
// out << "# Parameters:" << std::endl
// << "# + frequency: " << freq << "Hz" << std::endl
// << "# + # samples: " << NB << std::endl << std::endl;
// out << "time = 0 : " << (NB - 1) << ";" << std::endl;
// out << "fst_lfo = load(\"-ascii\", \"" << full_test_name << ".fst.data\");" << std::endl;
// out << "sin_lfo = load(\"-ascii\", \"" << full_test_name << ".sin.data\");" << std::endl;
// out << std::endl << std::endl;
// out << "plot(time, fst_lfo, '-', time, sin_lfo, '-');" << std::endl;
// out << "title('LFO tuning');" << std::endl;
// out << "legend('FastLFODebugger', 'Sinus');" << std::endl;
// out.close();
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

@ -177,8 +177,6 @@ TEST(LevelTuning, Delay)
fx.setLeftDelayTime(0.15f);
fx.setLeftDelayTime(0.2f);
fx.setFeedback(0.35f);
fx.setFlutterRate(0.0f);
fx.setFlutterAmount(0.0f);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
float32_t sumIn = 0.0f;

@ -12,6 +12,8 @@
#define MAX_SVF_SAMPLES 10000000
#define MAX_NB_ERRORS 100
void testFastLFOPrecision(std::string file, float32_t freq, float32_t init_phase);
TEST(FXComponent, LFO)
{
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info();
@ -141,10 +143,10 @@ void testFastLFOPrecision(std::string file, float32_t freq, float32_t init_phase
lfo1.setFrequency(freq);
lfo2.setFrequency(freq);
std::string file0 = std::string("data/") + file + ".ComplexLFO." + std::to_string(freq) + "Hz-" + std::to_string(init_phase_deg) + ".data";
std::string file1 = std::string("data/") + file + ".FastLFO." + std::to_string(freq) + "Hz-" + std::to_string(init_phase_deg) + ".data";
std::string file2 = std::string("data/") + file + ".FastLFO2." + std::to_string(freq) + "Hz-" + std::to_string(init_phase_deg) + ".data";
std::string file3 = std::string(file + ".") + std::to_string(freq) + "Hz-" + std::to_string(init_phase_deg) + ".data.m";
std::string file0 = string("data/") + file + ".ComplexLFO." + std::to_string(freq) + "Hz-" + std::to_string(init_phase_deg) + ".data";
std::string file1 = string("data/") + file + ".FastLFO." + std::to_string(freq) + "Hz-" + std::to_string(init_phase_deg) + ".data";
std::string file2 = string("data/") + file + ".FastLFO2." + std::to_string(freq) + "Hz-" + std::to_string(init_phase_deg) + ".data";
std::string file3 = string(file + ".") + std::to_string(freq) + "Hz-" + std::to_string(init_phase_deg) + ".data.m";
std::ofstream _lfo0(getResultFile(file0, true));
std::ofstream _lfo1(getResultFile(file1, true));
@ -180,7 +182,7 @@ void testFastLFOPrecision(std::string file, float32_t freq, float32_t init_phase
_m << "fast_lfo = load(\"-ascii\", \"" << file1 << "\");" << std::endl;
_m << "fast_lfo2 = load(\"-ascii\", \"" << file2 << "\");" << std::endl;
_m << "plot(time, cplx_lfo, '-b', 'LineWidth', 6, time, fast_lfo, '-r', 'LineWidth', 4, time, fast_lfo2, '-o', 'LineWidth', 4);" << std::endl;
_m << "title('LFO tuning @ " << freq << "Hz / " << init_phase_deg << "<EFBFBD>');" << std::endl;
_m << "title('LFO tuning @ " << freq << "Hz / " << init_phase_deg << "°');" << std::endl;
_m << "legend('ComplexLFO', 'FastLFO');" << std::endl;
_m.close();

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

@ -0,0 +1,565 @@
#include <gtest/gtest.h>
#include <cmath>
#include "test_fx_helper.h"
#include "wave.h"
#include "../mixing_console.hpp"
#define NB_MIXER_CHANNELS 8
class MixingConsoleScenarioTest : public testing::TestWithParam<MixerOutput> {};
typedef MixingConsole<NB_MIXER_CHANNELS> Mixer;
void setupMixingConsoleFX(Mixer* mixer);
void setupMixingConsoleFX(Mixer* mixer, int scenarioId, size_t channel = 0);
TEST_P(MixingConsoleScenarioTest, MixerOutputTest)
{
MixerOutput v = this->GetParam();
std::string str = toString(v);
MixerOutput mo = toIndex(str.c_str());
ASSERT_EQ(v, mo);
}
INSTANTIATE_TEST_SUITE_P(MixerOutputTest, MixingConsoleScenarioTest, testing::Range(MixerOutput::OutputStart, MixerOutput::kFXCount));
void setupMixingConsoleFX(Mixer* mixer)
{
mixer->setChannelLevel(0, 1.0f);
mixer->setPan(0, 0.5f);
mixer->getTube()->setMute(false);
mixer->getTube()->setOverdrive(0.85f);
mixer->getChorus()->setMute(false);
mixer->getChorus()->setRate(0.4f);
mixer->getChorus()->setDepth(0.7f);
mixer->getFlanger()->setMute(false);
mixer->getFlanger()->setRate(0.03f);
mixer->getFlanger()->setDepth(0.75f);
mixer->getFlanger()->setFeedback(0.7f);
mixer->getOrbitone()->setMute(false);
mixer->getOrbitone()->setRate(0.4f);
mixer->getOrbitone()->setDepth(0.8f);
mixer->getPhaser()->setMute(false);
mixer->getPhaser()->setRate(0.1f);
mixer->getPhaser()->setDepth(1.0f);
mixer->getPhaser()->setFeedback(0.7f);
mixer->getPhaser()->setNbStages(12);
mixer->getDelay()->setMute(false);
mixer->getDelay()->setLeftDelayTime(0.15f);
mixer->getDelay()->setLeftDelayTime(0.20f);
mixer->getDelay()->setFeedback(0.7f);
mixer->getPlateReverb()->setMute(false);
mixer->getPlateReverb()->set_bypass(false);
mixer->getPlateReverb()->size(0.7f);
mixer->getPlateReverb()->hidamp(0.5f);
mixer->getPlateReverb()->lodamp(0.5f);
mixer->getPlateReverb()->lowpass(0.3f);
mixer->getPlateReverb()->diffusion(0.65f);
mixer->getPlateReverb()->level(1.0f);
mixer->getReverberator()->setMute(false);
mixer->getReverberator()->setInputGain(0.65f);
mixer->getReverberator()->setTime(0.89f);
mixer->getReverberator()->setDiffusion(0.75f);
mixer->getReverberator()->setLP(0.8f);
}
#define ACTIVE_FX(activity, scenarioId, fx) activity[MixerOutput::fx] = ((scenarioId & (1 << MixerOutput::fx)) == (1 << MixerOutput::fx))
void setupMixingConsoleFX(Mixer* mixer, int scenarioId, size_t channel)
{
mixer->setChannelLevel(channel, 1.0f);
mixer->setPan(channel, 0.5f);
bool fxActivity[MixerOutput::kFXCount - 1];
ACTIVE_FX(fxActivity, scenarioId, FX_Tube);
ACTIVE_FX(fxActivity, scenarioId, FX_Chorus);
ACTIVE_FX(fxActivity, scenarioId, FX_Flanger);
ACTIVE_FX(fxActivity, scenarioId, FX_Orbitone);
ACTIVE_FX(fxActivity, scenarioId, FX_Phaser);
ACTIVE_FX(fxActivity, scenarioId, FX_Delay);
ACTIVE_FX(fxActivity, scenarioId, FX_PlateReverb);
ACTIVE_FX(fxActivity, scenarioId, FX_Reverberator);
size_t nbActiveFX = 0;
MixerOutput previousActivatedFX = MixerOutput::MainOutput;
for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i)
{
if(fxActivity[i])
{
nbActiveFX++;
if(nbActiveFX == 1)
{
mixer->setSendLevel(channel, static_cast<MixerOutput>(i), 1.0f);
}
else
{
mixer->setFXSendLevel(previousActivatedFX, static_cast<MixerOutput>(i), 1.0f);
}
previousActivatedFX = static_cast<MixerOutput>(i);
}
}
if(previousActivatedFX == MixerOutput::MainOutput)
{
mixer->setSendLevel(channel, MixerOutput::MainOutput, 1.0f);
}
else
{
mixer->setSendLevel(channel, MixerOutput::MainOutput, 0.25f);
mixer->setFXSendLevel(previousActivatedFX, MixerOutput::MainOutput, 0.75f);
}
}
TEST(MixingConsole, ZeroSamplesTest)
{
static const size_t length = 4;
Mixer mixer(SAMPLING_FREQUENCY, length);
ASSERT_EQ(0, FULL_INSPECT((&mixer), true));
mixer.reset();
ASSERT_EQ(0, FULL_INSPECT((&mixer), true));
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));
float32_t samples[] = {0.0f, 0.0f, 0.0f, 0.0f};
mixer.setInputSampleBuffer(0, samples);
mixer.preProcessInputSampleBuffer(0, 4);
ASSERT_EQ(0, FULL_INSPECT((&mixer), true));
mixer.setSendLevel(0, MixerOutput::MainOutput, 1.0f);
ASSERT_EQ(0, FULL_INSPECT((&mixer), true));
float32_t outL[4];
float32_t outR[4];
mixer.process(outL, outR);
ASSERT_EQ(0, FULL_INSPECT((&mixer), true));
mixer.setSendLevel(0, MixerOutput::FX_Tube, 1.0f);
mixer.setSendLevel(0, MixerOutput::FX_Delay, 1.0f);
mixer.setSendLevel(0, MixerOutput::FX_PlateReverb, 1.0f);
mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Orbitone, 1.0f);
mixer.setFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::MainOutput, 0.5f);
mixer.setFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_PlateReverb, 1.0f);
mixer.setFXSendLevel(MixerOutput::FX_Delay, MixerOutput::MainOutput, 0.5f);
mixer.setFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::MainOutput, 0.5f);
ASSERT_EQ(0, FULL_INSPECT((&mixer), true));
}
TEST(MixingConsole, DryProcessing)
{
static const float32_t epsilon = 1e-4;
static const size_t length = 2;
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);
mixer.setSendLevel(0, MixerOutput::FX_Flanger, 0.0f);
mixer.setSendLevel(0, MixerOutput::FX_Orbitone, 0.0f);
mixer.setSendLevel(0, MixerOutput::FX_Phaser, 0.0f);
mixer.setSendLevel(0, MixerOutput::FX_Delay, 0.0f);
mixer.setSendLevel(0, MixerOutput::FX_PlateReverb, 0.0f);
mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 0.0f);
for(size_t i = MixerOutput::OutputStart; i < (MixerOutput::kFXCount - 1); ++i)
{
mixer.setFXSendLevel(static_cast<MixerOutput>(i), MixerOutput::MainOutput, 0.0f);
}
mixer.setSendLevel(0, MixerOutput::MainOutput, 1.0f);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
float32_t in[length] = {0.1, 0.2};
float32_t out[StereoChannels::kNumChannels][length];
for(size_t i = 0; i < StereoChannels::kNumChannels; ++i) memset(out[i], 0, length * sizeof(float32_t));
mixer.setInputSampleBuffer(0, in);
mixer.preProcessInputSampleBuffer(0, 2);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
mixer.process(
out[StereoChannels::Left ],
out[StereoChannels::Right]
);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
EXPECT_FLOAT_EQ(out[StereoChannels::Left ][0], out[StereoChannels::Right][0]);
EXPECT_FLOAT_EQ(out[StereoChannels::Left ][1], out[StereoChannels::Right][1]);
EXPECT_NEAR(out[StereoChannels::Left ][0], sqrt(2.0f) / 20.0f, epsilon);
EXPECT_NEAR(out[StereoChannels::Left ][1], sqrt(2.0f) / 10.0f, epsilon);
}
TEST(MixingConsole, ReverberatorProcessing)
{
static const float32_t epsilon = 1e-7;
static const size_t length = 2;
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.setFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 1.0f);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
float32_t in[length] = {0.1, 0.2};
float32_t out1[StereoChannels::kNumChannels][length];
for(size_t i = 0; i < StereoChannels::kNumChannels; ++i) memset(out1[i], 0, length * sizeof(float32_t));
mixer.setInputSampleBuffer(0, in);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
mixer.process(
out1[StereoChannels::Left ],
out1[StereoChannels::Right]
);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
mixer.reset();
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
float32_t out2[StereoChannels::kNumChannels][length];
mixer.setInputSampleBuffer(0, in);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
mixer.process(
out2[StereoChannels::Left ],
out2[StereoChannels::Right]
);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
EXPECT_NEAR(out1[StereoChannels::Left ][0], out2[StereoChannels::Left ][0], epsilon);
EXPECT_NEAR(out1[StereoChannels::Right][0], out2[StereoChannels::Right][0], epsilon);
EXPECT_NEAR(out1[StereoChannels::Left ][1], out2[StereoChannels::Left ][1], epsilon);
EXPECT_NEAR(out1[StereoChannels::Right][1], out2[StereoChannels::Right][1], epsilon);
}
TEST(MixingConsole, ReverberatorNoiseProcessing)
{
static const size_t length = 1024;
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.setFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 1.0f);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
float32_t in[length];
for(size_t i = 0; i < length; ++i) in[i] = getRandomValue();
float32_t out[StereoChannels::kNumChannels][length];
for(size_t i = 0; i < StereoChannels::kNumChannels; ++i) memset(out[i], 0, length * sizeof(float32_t));
mixer.setInputSampleBuffer(0, in);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
mixer.process(
out[StereoChannels::Left ],
out[StereoChannels::Right]
);
ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
}
TEST(MixingConsole, StandardUsageProcessingByInjection)
{
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
Mixer mixer(SAMPLING_FREQUENCY, size);
setupMixingConsoleFX(&mixer);
mixer.getTube()->setOverdrive(0.15f);
mixer.setSendLevel(0, MixerOutput::FX_Tube, 1.0f);
mixer.setSendLevel(0, MixerOutput::FX_Phaser, 1.0f);
// mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, 1.0f);
// mixer.setSendLevel(0, MixerOutput::FX_Chorus, 1.0f);
// mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f);
mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Chorus, 1.0f);
mixer.setFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Reverberator, 1.0f);
mixer.setFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Delay, 1.0f);
mixer.setSendLevel(0, MixerOutput::MainOutput, 0.25f);
mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, 0.1f);
mixer.setFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::MainOutput, 0.15f);
mixer.setFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.3f);
mixer.setFXSendLevel(MixerOutput::FX_Delay, MixerOutput::MainOutput, 0.3f);
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);
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.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, 1.0f);
// mixer.setSendLevel(0, MixerOutput::FX_Chorus, 1.0f);
// mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f);
mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Chorus, 1.0f);
mixer.setFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Reverberator, 1.0f);
mixer.setFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Delay, 1.0f);
mixer.setSendLevel(0, MixerOutput::MainOutput, 0.25f);
mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, 0.1f);
mixer.setFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::MainOutput, 0.15f);
mixer.setFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.3f);
mixer.setFXSendLevel(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(MixingConsole, StandardUsageProcessingAllMixerChannels)
{
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[NB_MIXER_CHANNELS][MAX_BUFFER_SIZE];
for(size_t i = 0; i < NB_MIXER_CHANNELS; ++i)
{
memset(channelBuffer[i], 0, MAX_BUFFER_SIZE * sizeof(float32_t));
mixer.setInputSampleBuffer(i, channelBuffer[i]);
}
setupMixingConsoleFX(&mixer);
mixer.getTube()->setOverdrive(0.15f);
mixer.setSendLevel(0, MixerOutput::FX_Tube, 1.0f);
mixer.setSendLevel(0, MixerOutput::FX_Phaser, 1.0f);
// mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, 1.0f);
// mixer.setSendLevel(0, MixerOutput::FX_Chorus, 1.0f);
// mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f);
mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Chorus, 1.0f);
mixer.setFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Reverberator, 1.0f);
mixer.setFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Delay, 1.0f);
mixer.setSendLevel(0, MixerOutput::MainOutput, 0.25f);
mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, 0.1f);
mixer.setFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::MainOutput, 0.15f);
mixer.setFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.3f);
mixer.setFXSendLevel(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[0], inS, nb * sizeof(float32_t));
for(size_t i = 0; i < mixer.getChannelNumber(); ++i)
{
mixer.preProcessInputSampleBuffer(i, nb);
}
mixer.process(outS[StereoChannels::Left ], outS[StereoChannels::Right]);
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(MixingConsole, StandardUsageProcessingAllMixerChannels2)
{
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[NB_MIXER_CHANNELS][MAX_BUFFER_SIZE];
for(size_t i = 0; i < NB_MIXER_CHANNELS; ++i)
{
memset(channelBuffer[i], 0, MAX_BUFFER_SIZE * sizeof(float32_t));
mixer.setInputSampleBuffer(i, channelBuffer[i]);
}
setupMixingConsoleFX(&mixer);
for(size_t i = 0; i < NB_MIXER_CHANNELS; ++i)
{
mixer.setSendLevel(i, static_cast<MixerOutput>(i), 1.0f);
mixer.setFXSendLevel(static_cast<MixerOutput>(i), MixerOutput::MainOutput, 0.5f);
mixer.setSendLevel(i, MixerOutput::MainOutput, 0.5f);
}
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;
for(size_t i = 0; i < mixer.getChannelNumber(); ++i)
{
memcpy(channelBuffer[i], inS, nb * sizeof(float32_t));
mixer.preProcessInputSampleBuffer(i, nb);
}
mixer.process(outS[StereoChannels::Left ], outS[StereoChannels::Right]);
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)
{
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[NB_MIXER_CHANNELS][MAX_BUFFER_SIZE];
for(size_t i = 0; i < NB_MIXER_CHANNELS; ++i)
{
memset(channelBuffer[i], 0, MAX_BUFFER_SIZE * sizeof(float32_t));
mixer.setInputSampleBuffer(i, channelBuffer[i]);
}
setupMixingConsoleFX(&mixer);
int scenarioId = this->GetParam();
setupMixingConsoleFX((&mixer), scenarioId);
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;
for(size_t i = 0; i < mixer.getChannelNumber(); ++i)
{
memcpy(channelBuffer[i], inS, nb * sizeof(float32_t));
mixer.preProcessInputSampleBuffer(i, nb);
}
mixer.process(outS[StereoChannels::Left ], outS[StereoChannels::Right]);
inS += nb;
outS[StereoChannels::Left ] += nb;
outS[StereoChannels::Right] += nb;
s -= nb;
}
std::string filename = "";
for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i)
{
int k = 1 << i;
if((scenarioId & k) == 0)
{
continue;
}
if(filename.size() > 0)
{
filename += ", ";
}
filename += toString(static_cast<MixerOutput>(i));
}
saveWaveFile(getResultFile(full_test_name + " mixing-console[ " + filename + " ].wav", true), outSamples[0], outSamples[1], size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
INSTANTIATE_TEST_SUITE_P(MixingConsole, FXScenarioTest, testing::Range(0, 1 << (MixerOutput::kFXCount - 1)));

@ -0,0 +1,187 @@
#include "test_fx_helper.h"
#include "../mixing_console.hpp"
typedef MixingConsole<1> Mixer;
TEST(MixingConsole, ShortBuffer)
{
static const float32_t SINPI_4 = std::sqrt(2.0f) / 2.0f;
static const float32_t epsilon = 1e-4;
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info();
std::string full_test_name = test_info->test_case_name();
full_test_name += ".";
full_test_name += test_info->name();
const size_t size = 10;
Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size);
mixer->reset();
mixer->setChannelLevel(0, 1.0f);
mixer->setPan(0, 0.5f);
mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f);
float32_t inSamples[size];
for(size_t s = 0; s < size; ++s) inSamples[s] = getRandomValue();
float32_t outSamples[2][size];
memset(outSamples[0], 0, size * sizeof(float32_t));
memset(outSamples[1], 0, size * sizeof(float32_t));
mixer->setInputSampleBuffer(0, inSamples);
mixer->preProcessInputSampleBuffer(0, size);
ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.setInputSampleBuffer";
mixer->process(outSamples[0], outSamples[1]);
ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.process";
for(size_t s = 0; s < size; ++s)
{
EXPECT_NEAR(outSamples[0][s], outSamples[1][s], epsilon);
EXPECT_NEAR(outSamples[0][s], inSamples[s] * SINPI_4, epsilon);
}
delete mixer;
}
TEST(MixingConsole, ReverberatorShortBuffer)
{
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info();
std::string full_test_name = test_info->test_case_name();
full_test_name += ".";
full_test_name += test_info->name();
const size_t size = 10;
Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size);
mixer->reset();
mixer->setChannelLevel(0, 1.0f);
mixer->setPan(0, 0.5f);
mixer->getReverberator()->setInputGain(0.35f);
mixer->getReverberator()->setTime(0.69f);
mixer->getReverberator()->setDiffusion(0.7f);
mixer->getReverberator()->setLP(0.8f);
mixer->setSendLevel(0, MixerOutput::MainOutput, 0.4f);
mixer->setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f);
mixer->setFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.6f);
float32_t inSamples[size];
for(size_t s = 0; s < size; ++s) inSamples[s] = getRandomValue();
float32_t outSamples[2][size];
memset(outSamples[0], 0, size * sizeof(float32_t));
memset(outSamples[1], 0, size * sizeof(float32_t));
mixer->setInputSampleBuffer(0, inSamples);
mixer->preProcessInputSampleBuffer(0, size);
ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.setInputSampleBuffer";
mixer->process(outSamples[0], outSamples[1]);
ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.process";
delete mixer;
}
TEST(MixingConsole, DrySamplesBoundariesTest)
{
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info();
std::string full_test_name = test_info->test_case_name();
full_test_name += ".";
full_test_name += test_info->name();
size_t size;
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size);
Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size);
mixer->reset();
FULL_INSPECT2(mixer, true, "Mixer.reset");
mixer->setChannelLevel(0, 1.0f);
mixer->setPan(0, 0.5f);
mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f);
mixer->setInputSampleBuffer(0, inSamples[0]);
mixer->preProcessInputSampleBuffer(0, size);
float32_t** outSamples = new float32_t*[2];
outSamples[0] = new float32_t[size];
outSamples[1] = new float32_t[size];
memset(outSamples[0], 0, size * sizeof(float32_t));
memset(outSamples[1], 0, size * sizeof(float32_t));
mixer->process(outSamples[0], outSamples[1]);
size_t nb_errors = 0;
for(size_t i = 0; i < size; ++i)
{
nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[0][i], -1.0f, 1.0f, true);
nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[1][i], -1.0f, 1.0f, true);
}
ASSERT_EQ(0, nb_errors) << full_test_name << ".outputSampleTest";
delete[] inSamples[0];
delete[] inSamples[1];
delete[] inSamples;
delete[] outSamples[0];
delete[] outSamples[1];
delete[] outSamples;
delete mixer;
}
TEST(MixingConsole, ReverberatorSamplesBoundariesTest)
{
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info();
std::string full_test_name = test_info->test_case_name();
full_test_name += ".";
full_test_name += test_info->name();
size_t size;
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size);
float32_t** outSamples = new float32_t*[2];
outSamples[0] = new float32_t[size];
outSamples[1] = new float32_t[size];
memset(outSamples[0], 0, size * sizeof(float32_t));
memset(outSamples[1], 0, size * sizeof(float32_t));
Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size);
mixer->reset();
mixer->setChannelLevel(0, 1.0f);
mixer->setPan(0, 0.5f);
mixer->setSendLevel(0, MixerOutput::MainOutput, 0.4f);
mixer->setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f);
mixer->setFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.6f);
mixer->getReverberator()->setMute(false);
mixer->getReverberator()->setInputGain(0.35);
mixer->getReverberator()->setTime(0.65);
mixer->getReverberator()->setDiffusion(0.8);
mixer->getReverberator()->setLP(0.7f);
mixer->setInputSampleBuffer(0, inSamples[0]);
mixer->preProcessInputSampleBuffer(0, size);
mixer->process(outSamples[0], outSamples[1]);
ASSERT_EQ(0, FULL_INSPECT2(mixer, true, full_test_name + "Mixer.process")) << full_test_name << " Mixer.process";
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamples[0], outSamples[1], size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16);
size_t nb_errors = 0;
for(size_t i = 0; i < size; ++i)
{
nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[0][i], -1.0f, 1.0f, true);
nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[1][i], -1.0f, 1.0f, true);
}
ASSERT_EQ(0, nb_errors) << full_test_name << ".outputSampleTest";
delete[] inSamples[0];
delete[] inSamples[1];
delete[] inSamples;
delete[] outSamples[0];
delete[] outSamples[1];
delete[] outSamples;
delete mixer;
}

@ -7,13 +7,15 @@ using namespace std;
#define MAX_SVF_SAMPLES 10000000
#define MAX_NB_ERRORS 100
void setupRack(FXRack* rack, int scenario);
void setupRack(FXRack* rack, int scenario)
{
rack->setWetLevel(1.0f);
rack->getTube()->setEnable(Active(scenario, FXSwitch::FX__Tube));
rack->getTube()->setWetLevel(0.25f);
rack->getTube()->setOverdrive(0.25f);
rack->getTube()->setOverdrive(0.5f);
rack->getChorus()->setEnable(Active(scenario, FXSwitch::FX__Chorus));
rack->getChorus()->setWetLevel(0.5f);
@ -43,8 +45,6 @@ void setupRack(FXRack* rack, int scenario)
rack->getDelay()->setLeftDelayTime(0.05f);
rack->getDelay()->setLeftDelayTime(0.07f);
rack->getDelay()->setFeedback(0.35f);
rack->getDelay()->setFlutterRate(0.0f);
rack->getDelay()->setFlutterAmount(0.0f);
rack->getReverberator()->setEnable(Active(scenario, FXSwitch::FX__Reverberator));
rack->getReverberator()->setWetLevel(0.5f);

@ -0,0 +1,182 @@
#include <gtest/gtest.h>
#include "test_fx_helper.h"
#include "../fx_reverberator.h"
TEST(FXShimmer, TransientSilence)
{
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info();
std::string full_test_name = test_info->test_case_name();
full_test_name += ".";
full_test_name += test_info->name();
const size_t size = static_cast<size_t>(SAMPLING_FREQUENCY);
float32_t* inSamples = new float32_t[size];
memset(inSamples, 0, size * sizeof(float32_t));
float32_t* outSamplesL = new float32_t[size];
float32_t* outSamplesR = new float32_t[size];
memset(outSamplesL, 0, size * sizeof(float32_t));
memset(outSamplesR, 0, size * sizeof(float32_t));
Reverberator* shimmer = new Reverberator(SAMPLING_FREQUENCY);
shimmer->setInputGain(0.55f);
shimmer->setTime(0.75f);
shimmer->setDiffusion(0.8f);
shimmer->setLP(0.7f);
shimmer->reset();
for(size_t i = 0; i < size; ++i)
{
shimmer->processSample(
inSamples[i],
inSamples[i],
outSamplesL[i],
outSamplesR[i]
);
}
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16);
delete shimmer;
delete[] inSamples;
delete[] outSamplesL;
delete[] outSamplesR;
}
TEST(FXShimmer, TransientSilenceWithDirac)
{
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info();
std::string full_test_name = test_info->test_case_name();
full_test_name += ".";
full_test_name += test_info->name();
const size_t size = 4 * static_cast<size_t>(SAMPLING_FREQUENCY);
float32_t* inSamples = new float32_t[size];
memset(inSamples, 0, size * sizeof(float32_t));
inSamples[0] = 1.0f;
float32_t* outSamplesL = new float32_t[size];
float32_t* outSamplesR = new float32_t[size];
memset(outSamplesL, 0, size * sizeof(float32_t));
memset(outSamplesR, 0, size * sizeof(float32_t));
Reverberator* shimmer = new Reverberator(SAMPLING_FREQUENCY);
shimmer->setInputGain(0.55f);
shimmer->setTime(0.75f);
shimmer->setDiffusion(0.8f);
shimmer->setLP(0.7f);
shimmer->reset();
for(size_t i = 0; i < size; ++i)
{
shimmer->processSample(
inSamples[i],
inSamples[i],
outSamplesL[i],
outSamplesR[i]
);
}
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16);
delete shimmer;
delete[] inSamples;
delete[] outSamplesL;
delete[] outSamplesR;
}
TEST(FXShimmer, TransientNoise)
{
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info();
std::string full_test_name = test_info->test_case_name();
full_test_name += ".";
full_test_name += test_info->name();
const size_t size = static_cast<size_t>(SAMPLING_FREQUENCY);
float32_t* inSamples = new float32_t[size];
for(size_t i = 0; i < size; ++i) inSamples[i] = getRandomValue();
float32_t* outSamplesL = new float32_t[size];
float32_t* outSamplesR = new float32_t[size];
memset(outSamplesL, 0, size * sizeof(float32_t));
memset(outSamplesR, 0, size * sizeof(float32_t));
Reverberator* shimmer = new Reverberator(SAMPLING_FREQUENCY);
shimmer->setInputGain(0.55f);
shimmer->setTime(0.75f);
shimmer->setDiffusion(0.8f);
shimmer->setLP(0.7f);
shimmer->reset();
for(size_t i = 0; i < size; ++i)
{
shimmer->processSample(
inSamples[i],
inSamples[i],
outSamplesL[i],
outSamplesR[i]
);
}
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16);
delete shimmer;
delete[] inSamples;
delete[] outSamplesL;
delete[] outSamplesR;
}
TEST(FXShimmer, TransientMusic)
{
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info();
std::string full_test_name = test_info->test_case_name();
full_test_name += ".";
full_test_name += test_info->name();
size_t size;
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size);
float32_t* outSamplesL = new float32_t[size];
float32_t* outSamplesR = new float32_t[size];
memset(outSamplesL, 0, size * sizeof(float32_t));
memset(outSamplesR, 0, size * sizeof(float32_t));
Reverberator* shimmer = new Reverberator(SAMPLING_FREQUENCY);
shimmer->setInputGain(0.55f);
shimmer->setTime(0.75f);
shimmer->setDiffusion(0.8f);
shimmer->setLP(0.7f);
shimmer->reset();
for(size_t i = 0; i < size; ++i)
{
shimmer->processSample(
inSamples[0][i],
inSamples[1][i],
outSamplesL[i],
outSamplesR[i]
);
}
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16);
delete shimmer;
delete[] inSamples[0];
delete[] inSamples[1];
delete[] inSamples;
delete[] outSamplesL;
delete[] outSamplesR;
}

@ -1,5 +1,6 @@
#include "test_fx_helper.h"
#include "../fx_dry.h"
#include "../fx_tube.h"
#include "../fx_chorus.h"
#include "../fx_flanger.h"
@ -7,7 +8,20 @@
#include "../fx_phaser.h"
#include "../fx_delay.h"
#include "../effect_platervbstereo.h"
#include "../fx_diffuser.h"
#include "../fx_pitch_shifter.h"
#include "../fx_reverberator.h"
#include "../fx_shimmer_reverb.h"
TEST(UnitFXTuning, Dry)
{
Dry fx(SAMPLING_FREQUENCY);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx);
SAVE_AUDIO_RESULTS(full_test_name, outSamples, size);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
TEST(UnitFXTuning, Tube)
{
@ -77,8 +91,6 @@ TEST(UnitFXTuning, Delay)
fx.setLeftDelayTime(0.25f);
fx.setLeftDelayTime(0.40f);
fx.setFeedback(0.55f);
fx.setFlutterRate(0.01f);
fx.setFlutterAmount(0.05f);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx);
@ -103,6 +115,28 @@ TEST(UnitFXTuning, PlateReverb)
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
TEST(UnitFXTuning, Diffuser)
{
Diffuser fx(SAMPLING_FREQUENCY);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx);
SAVE_AUDIO_RESULTS(full_test_name, outSamples, size);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
TEST(UnitFXTuning, PitchShifter)
{
PitchShifter fx(SAMPLING_FREQUENCY);
fx.setSize(0.5f);
fx.setTranspose(12.0f);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx);
SAVE_AUDIO_RESULTS(full_test_name, outSamples, size);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
TEST(UnitFXTuning, Reverberator)
{
Reverberator fx(SAMPLING_FREQUENCY);
@ -116,3 +150,20 @@ TEST(UnitFXTuning, Reverberator)
SAVE_AUDIO_RESULTS(full_test_name, outSamples, size);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
TEST(UnitFXTuning, ShimmerReverb)
{
const float32_t amount = 0.6f;
ShimmerReverb fx(SAMPLING_FREQUENCY);
fx.setInputGain(0.2f);
fx.setReverbAmount(amount);
fx.setDiffusion(0.7f);
fx.setFeedback(0.8f);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx);
SAVE_AUDIO_RESULTS(full_test_name, outSamples, size);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}

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

File diff suppressed because it is too large Load Diff

@ -85,6 +85,7 @@ private:
static void EditOPParameter (CUIMenu *pUIMenu, TMenuEvent Event);
static void SavePerformance (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditTGParameter2 (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditTGParameter3 (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditTGParameterModulation (CUIMenu *pUIMenu, TMenuEvent Event);
static void PerformanceMenu (CUIMenu *pUIMenu, TMenuEvent Event);
static void SavePerformanceNewFile (CUIMenu *pUIMenu, TMenuEvent Event);
@ -139,18 +140,37 @@ private:
static const TMenuItem s_MainMenu[];
static const TMenuItem s_TGMenu[];
static const TMenuItem s_EffectsMenu[];
#ifdef ARM_ALLOW_MULTI_CORE
static const TMenuItem s_ReverbMenu[];
#if defined(MIXING_CONSOLE_ENABLE)
static const TMenuItem s_TGFXMenu[];
static const TMenuItem s_FXTube[];
static const TMenuItem s_FXTubeLevels[];
static const TMenuItem s_FXTubeSend[];
static const TMenuItem s_FXChorus[];
static const TMenuItem s_FXChorusLevels[];
static const TMenuItem s_FXChorusSend[];
static const TMenuItem s_FXFlanger[];
static const TMenuItem s_FXFlangerLevels[];
static const TMenuItem s_FXFlangerSend[];
static const TMenuItem s_FXOrbitone[];
static const TMenuItem s_FXOrbitoneLevels[];
static const TMenuItem s_FXOrbitoneSend[];
static const TMenuItem s_FXPhaser[];
static const TMenuItem s_FXPhaserLevels[];
static const TMenuItem s_FXPhaserSend[];
static const TMenuItem s_FXDelay[];
static const TMenuItem s_FXDelayLevels[];
static const TMenuItem s_FXDelaySend[];
#endif
#ifdef FXRACK_ENABLE
static const TMenuItem s_FXChainMenu[];
static const TMenuItem s_FXChainTube[];
static const TMenuItem s_FXChainChorus[];
static const TMenuItem s_FXChainFlanger[];
static const TMenuItem s_FXChainOrbitone[];
static const TMenuItem s_FXChainPhaser[];
static const TMenuItem s_FXChainDelay[];
static const TMenuItem s_FXChainShimmerReverb[];
#if defined(PLATE_REVERB_ENABLE) || defined(MIXING_CONSOLE_ENABLE)
static const TMenuItem s_FXPlateReverb[];
#endif
#if defined(MIXING_CONSOLE_ENABLE)
static const TMenuItem s_FXPlateReverbLevels[];
static const TMenuItem s_FXPlateReverbSend[];
static const TMenuItem s_FXReverberator[];
static const TMenuItem s_FXReverberatorLevels[];
static const TMenuItem s_FXReverberatorSend[];
static const TMenuItem s_FXMainOutputLevels[];
#endif
static const TMenuItem s_EditVoiceMenu[];
static const TMenuItem s_OperatorMenu[];

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

@ -0,0 +1,470 @@
# Mixing Console & Audio Effect
On multi-core devices, MiniDexed can now be equiped with a complex and versatile mixing console that is composed of a multi-FX processor.
After the volume and pan controls, it is possible to set the send level of each Tone Generator to the individual Audio Effect and Main output. Below is the TG channel strip:
<img src="mixing-console--TG-Channel.png" alt="Mixing Console - TG Channel Strip" width="10%" style="background-color: #fff">
Each FX has the possibility to return to any other Audio FX unit as well as Main Output.
The diagram below shows the synopsis of this Mixing Console.
<img src="mixing-console-overview.png" alt="Mixing Console - TG Channel Strip" width="100%" style="background-color: #fff">
## The Audio FX
The following audio FX are available:
* **Tube:** an amplifier that mimmic the response of valve based amplifier.
<img src="Tube--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff">
* ***Enable [Off - On]:*** Enable / disable the FX unit.
* ***Overdrive [0 - 99]:*** the overdrive amount the more the overdrive the less linear is the amplification. Below is the amplifier response based on different value of the overdrive: ![Tube - Overdrive response](Tube--overdrive-response.png)
<br>
<br>
* **Chorus:** this effect is an audio signal processing technique that creates a richer, thicker sound by duplicating the original signal and slightly delaying and modulating the duplicated signal in pitch and time. The modulated and delayed signals are then mixed back together with the original signal, creating a sound that seems to be coming from multiple sources or voices.
The result of a chorus effect is a distinctive "shimmering" or "swirling" sound, similar to the sound of a choir or a group of instruments playing together. Chorus effects are commonly used in music production to add depth and complexity to a sound, particularly for guitars, keyboards, and vocals. They are also used in some audio equipment, such as guitar pedals and synthesizers, to produce a chorus effect in real-time.
This implementation is a standard Chorus FX that is based on 4 LFO:
| LFO | Min. freq. | Max. freq. | Phase |
|-----|------------|------------|-------|
| 1 | 0.0 Hz | 0.25 Hz | 0° |
| 2 | 0.0 Hz | 0.25 Hz | 90° |
| 3 | 0.0 Hz | 0.35 Hz | 0° |
| 4 | 0.0 Hz | 0.35 Hz | 90° |
<img src="Chorus--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff">
* ***Enable [Off - On]:*** Enable / disable the FX unit.
* ***Rate [0 - 99]:*** modulate the LFO rate between thei minimum frequency (Rate = 0) and their maximum frequency (Rate = 99).
* ***Depth [0 - 99]:*** the level of modulation of the FX. The higher the value the more prononced the audio processing is.
<br>
<br>
* **Flanger:** this effect is an audio signal processing technique that creates a sweeping, swirling sound by mixing a delayed copy of an audio signal with the original signal. The delay time of the copied signal is varied over time, creating a series of peaks and troughs in the frequency spectrum of the combined signal.
The resulting sound is similar to a chorus effect, but with a more pronounced "swooshing" or "jet engine" effect, as the peaks and troughs of the frequency spectrum sweep up and down in a cyclic pattern. Flangers are often used in music production to create an "otherworldly" or psychedelic effect, particularly on guitars and keyboards.
The name "flanger" comes from the original method of creating the effect, which involved using two tape recorders to play back the same signal simultaneously, while a finger was used to slightly slow down or speed up the flange (rim) of one of the reels, causing the delayed signal to vary in time. Today, flanger effects are typically created digitally, using specialized signal processing algorithms in software or hardware devices such as guitar pedals or studio processors.
This implementation is based on 2 LFO:
| LFO | Min. freq. | Max. freq. | Phase |
|-----|------------|------------|-------|
| 1 | 0.1 Hz | 5.0 Hz | 0° |
| 2 | 0.1 Hz | 5.0 Hz | 90° |
<img src="Flanger--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff">
* ***Enable [Off - On]:*** Enable / disable the FX unit.
* ***Rate [0 - 99]:*** modulate the LFO rate between thei minimum frequency (Rate = 0) and their maximum frequency (Rate = 99).
* ***Depth [0 - 99]:*** the level of modulation of the FX. The higher the value the more prononced the audio processing is.
* ***Feedback [0 - 99]:*** the amount of of processed signal that is re-injected at the audio effect input.
<br>
<br>
* ***Orbitone:*** this is an ensemble effect that is an audio signal processing technique that creates the impression of multiple instruments or voices playing together. It is similar to a chorus effect in that it involves duplicating and modulating an audio signal, but with some differences in the way it is applied.
In an ensemble effect, the duplicated signals are modulated with a low-frequency oscillator (LFO) that varies the pitch and amplitude of the signals over time. The modulated signals are then mixed with the original signal, creating a sound that is thicker and richer, as if several instruments or voices were playing in unison.
The resulting sound can be used to add depth and character to a musical performance, particularly for string, brass, or vocal sounds. Ensemble effects are often used in music production to create a sense of warmth, depth, and movement in a mix.
This implementation is based on 4 LFO:
| LFO | Min. freq. | Max. freq. | Phase |
|-----|------------|------------|-------|
| 1 | 0.0 Hz | 1.0 Hz | 0° |
| 2 | 0.0 Hz | 1.0 Hz | 120° |
| 3 | 0.0 Hz | 1.0 Hz | 240° |
| 4 | 0.0 Hz | 8.8 Hz | 0° |
| 5 | 0.0 Hz | 8.8 Hz | 120° |
| 6 | 0.0 Hz | 8.8 Hz | 240° |
<img src="Orbitone--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff">
* ***Enable [Off - On]:*** Enable / disable the FX unit.
* ***Rate [0 - 99]:*** modulate the LFO rate between thei minimum frequency (Rate = 0) and their maximum frequency (Rate = 99).
* ***Depth [0 - 99]:*** the level of modulation of the FX. The higher the value the more prononced the audio processing is.
* ***Feedback [0 - 99]:*** the amount of of processed signal that is re-injected at the audio effect input.
<br>
<br>
* ***Phaser:*** a phaser or phase shifter effect is an audio signal processing technique that creates a sweeping, "whooshing" sound by altering the phase relationship between two identical signals. This is achieved by splitting the audio signal into two parts, delaying one of them slightly, and then combining them again. The delay time is varied over time, causing certain frequencies in the audio signal to be boosted and others to be attenuated.
The resulting sound is characterized by a series of peaks and troughs in the frequency spectrum, which move up and down in a sweeping pattern. The phaser effect is often used in music production to create a sense of movement and space in a mix, and is particularly effective on guitars, keyboards, and other melodic instruments.
This implementation is based on 2 LFO:
| LFO | Min. freq. | Max. freq. | Phase |
|-----|------------|------------|-------|
| 1 | 0.0 Hz | 2.5 Hz | 0° |
| 2 | 0.0 Hz | 2.5 Hz | 90° |
<img src="Phaser--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff">
* ***Enable [Off - On]:*** Enable / disable the FX unit.
* ***Rate [0 - 99]:*** modulate the LFO rate between thei minimum frequency (Rate = 0) and their maximum frequency (Rate = 99).
* ***Depth [0 - 99]:*** the level of modulation of the FX. The higher the value the more prononced the audio processing is.
* ***Feedback [0 - 99]:*** the amount of of processed signal that is re-injected at the audio effect input.
* ***Nb Stages [2 - 24]:*** The number of state variable filter stages that the audio signal will traverse.
<br>
<br>
* **Delay:** the is an audio signal processing technique that creates a repeated, delayed version of an original sound. It does this by temporarily storing a copy of the original audio signal in a buffer, and then playing it back at a later time.
The delay time can be set to vary from a few milliseconds to 1 second, and the repeated sound can be manipulated in various ways to create a range of different effects.
Delay effects are used in a wide variety of musical genres to add texture, depth, and interest to a mix. They can be used on almost any sound source, including vocals, guitars, keyboards, drums and other melodic instruments.
The implemention of this delay accept negative values for both left and right delay. Negative values will echo sound reversed.
<img src="Delay--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff">
* ***Enable [Off - On]:*** Enable / disable the FX unit.
* **Left Delay [-99 - 99]:** the left delay from 0 to 2 seconds.
* **Right Delay [-99 - 99]:** the left delay from 0 to 2 seconds.
* ***Feedback [0 - 99]:*** the amount of of processed signal that is re-injected at the audio effect input.
<br>
<br>
* **Plate Reverb:** A plate reverb effect is an audio signal processing technique that simulates the sound of a large, metallic plate vibrating in response to an audio signal. It is one of the most iconic and widely used types of artificial reverberation in music production.
The plate reverb effect is created by passing an audio signal through a transducer that causes a large metal plate to vibrate in response. The vibrations of the plate are then picked up by one or more microphones and mixed back with the original signal. The sound of the plate reverb is characterized by a smooth decay and a diffuse, ambient quality that can add warmth and depth to a mix.
Plate reverb effects are often used in music production to add space, depth, and character to a mix. They are particularly effective on vocals, drums, and other percussive sounds, and can be used to create a sense of space and distance in a recording. The effect can be adjusted to achieve varying degrees of intensity, decay time, and other parameters.
<img src="PlateReverb--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff">
* ***Enable [Off - On]:*** Enable / disable the FX unit.
* ***Size [0 - 99]:*** the size of the simulated metallic plate hence of the room.
* ***High Damping [0 - 99]:*** the amount of high-frequency attenuation or absorption applied to the plate's vibrations. It is a key parameter that can have a significant impact on the overall sound of the plate reverb effect.
* ***Low Damping [0 - 99]:*** the amount of low-frequency attenuation or absorption applied to the plate's vibrations. It is a key parameter that can have a significant impact on the overall sound of the plate reverb effect.
* ***Low Pass [0 - 99]:*** the low pass parameter of a plate reverb refers to a filter that is applied to the reverb signal to attenuate or cut off frequencies above a certain threshold.
A low pass filter can be used to create a darker, more mellow sound by reducing or removing high-frequency content from the reverb effect. This can be useful when trying to create a more vintage or retro sound, or when trying to soften harsh or bright sounds in a mix.
* ***Diffusion [0 - 99]:*** the diffusion parameter of a plate reverb refers to the degree to which the reflections in the reverb decay are dispersed or spread out in time and space. It determines how "dense" or "sparse" the reverb effect sounds and how the individual reflections blend together.
A higher diffusion parameter means that the reflections in the reverb decay will be more widely dispersed, resulting in a more "washed out" or diffuse sound. This can be useful when trying to create a sense of space or depth in a mix, or when trying to create a more ambient or atmospheric effect.
Conversely, a lower diffusion parameter means that the reflections in the reverb decay will be more closely spaced, resulting in a more defined and focused sound. This can be useful when trying to create a more natural-sounding reverb effect, or when trying to highlight the details of a particular instrument or sound in a mix.
* ***Level [0 - 99]:*** the level parameter of a plate reverb refers to the overall level or amplitude of the reverb effect. It determines how loud the reverb signal is in relation to the dry or unprocessed signal.
<br>
<br>
* **Reverberator:** the reverberator effect, commonly known as reverb, is an audio processing effect that simulates the sound reflections and acoustic environment of a physical space.
When a sound is produced in a physical space, it travels through the air and reflects off the walls, floors, and other surfaces in the environment. These reflections create a sense of space and depth, and contribute to the character and quality of the sound.
A reverberator effect works by simulating these reflections, and can be used to create a wide range of sonic environments, from small and intimate spaces like a bedroom or bathroom, to large and expansive spaces like a concert hall or cathedral.
Reverberator can be applied to individual tracks or to a mix, and can be adjusted in various ways, such as changing the decay time or "size" of the simulated space, adjusting the frequency content of the reverb, and adjusting the level or balance of the reverb effect in relation to the dry or unprocessed signal.
Reverberator is a common effect used in many genres of music, as well as in film, television, and other forms of audio production. It can be used to create a sense of realism, depth, and immersion, or to add a sense of ambiance, texture, or mood to a recording or production.
This implementation pushes the reverberation to reach almost the shimmer effect.
<img src="Reverberator--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff">
* ***Enable [Off - On]:*** Enable / disable the FX unit.
* **Gain [0 - 99]:** the gain parameter of the reverberator refers to the overall level or amplitude of the reverberator effect. It determines how loud the reverberator signal is in relation to the dry or unprocessed signal.
* **Time [0 - 99]:** the time determines the size of the simulated reverberating space.
* **Diffusion [0 - 99]:** the diffusion parameter of a reverberator refers to the degree to which the reflections in the reverb decay are dispersed or spread out in time and space. It determines how "dense" or "sparse" the reverberator effect sounds and how the individual reflections blend together.
A higher diffusion parameter means that the reflections in the reverb decay will be more widely dispersed, resulting in a more "washed out" or diffuse sound. This can be useful when trying to create a sense of space or depth in a mix, or when trying to create a more ambient or atmospheric effect.
Conversely, a lower diffusion parameter means that the reflections in the reverberator decay will be more closely spaced, resulting in a more defined and focused sound. This can be useful when trying to create a more natural-sounding reverberator effect, or when trying to highlight the details of a particular instrument or sound in a mix.
* **Low Pass [0 - 99]:** the low pass parameter of a plate reverb refers to a filter that is applied to the reverb signal to attenuate or cut off frequencies above a certain threshold.
A low pass filter can be used to create a darker, more mellow sound by reducing or removing high-frequency content from the reverb effect. This can be useful when trying to create a more vintage or retro sound, or when trying to soften harsh or bright sounds in a mix.
## The Menu structure
* *MiniDexed*
* *TG1*
* *Voice*
* *Bank*
* *Volume*
* *Pan*
* **FX-Send**
* **> Tub** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Tube FX Unit.
* **> ChR** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Chorus FX Unit.
* **> Flg** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Flanger FX Unit.
* **> Orb** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Orbitone FX Unit.
* **> PhR** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Phaser FX Unit.
* **> Dly** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Delay FX Unit.
* **> Plt** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Plate Reverb FX Unit.
* **> Rev** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Reverberator FX Unit.
* **> Main** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Main output hence dry.
* *Detune*
* *Cutoff*
* *Resonance*
* *Pitch Bend*
* *Portamento*
* *Poly/Mono*
* *Modulation*
* *Channel*
* *Edit Voice:*
* ...
* *TG2*
* ... *similar to TG1 sub-menu* ...
* *TG3*
* ... *similar to TG1 sub-menu* ...
* *TG4*
* ... *similar to TG1 sub-menu* ...
* *TG5*
* ... *similar to TG1 sub-menu* ...
* *TG6*
* ... *similar to TG1 sub-menu* ...
* *TG7*
* ... *similar to TG1 sub-menu* ...
* *TG8*
* ... *similar to TG1 sub-menu* ...
* **Effects**
* Compress *[on - off]*: Enable / disable the FX unit for all Tone Generators.
* **Tube**
* **Enable** *[on - off]*: Enable / disable the FX unit.
* **Overdrv** *[0 - 99]*: The overdrive amount of the FX unit.
* **Levels**
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> Tub]
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> Tub]
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> Tub]
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> Tub]
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> Tub]
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> Tub]
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> Tub]
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> Tub]
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> Tub]
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> Tub]
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> Tub]
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> Tub]
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> Tub]
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> Tub]
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> Tub]
* **FX-Send**
* **> ChR** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Chorus FX unit.
* **> Flg** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Flanger FX unit.
* **> Orb** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Orbitone FX unit.
* **> PhR** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Phaser FX unit.
* **> Dly** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Delay FX unit.
* **> Plt** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Plate Reverb FX unit.
* **> Rev** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Reverberator FX unit.
* **> Main** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Main output.
* **Chorus**
* **Enable** *[on - off]*: Enable / disable the FX unit.
* **Rate** *[0 - 99]*: The speed of the LFO of the FX unit.
* **Depth** *[0 - 99]*: The amount of effect that is applied to the input signal.
* **Levels**
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> ChR]
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> ChR]
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> ChR]
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> ChR]
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> ChR]
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> ChR]
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> ChR]
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> ChR]
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> ChR]
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> ChR]
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> ChR]
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> ChR]
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> ChR]
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> ChR]
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> ChR]
* **FX-Send**
* **> Tub** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Tube FX unit.
* **> Flg** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Flanger FX unit.
* **> Orb** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Orbitone FX unit.
* **> PhR** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Phaser FX unit.
* **> Dly** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Delay FX unit.
* **> Plt** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Plate Reverb FX unit.
* **> Rev** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Reverberator FX unit.
* **> Main** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Main output.
* **FlangR**
* **Enable** *[on - off]*: Enable / disable the FX unit.
* **Rate** *[0 - 99]*: The speed of the LFO of the FX unit.
* **Depth** *[0 - 99]*: The amount of effect that is applied to the input signal.
* **Feedbck** *[0 - 99]*: The amount of processed signal that will be reinjected at the input.
* **Levels**
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> Flg]
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> Flg]
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> Flg]
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> Flg]
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> Flg]
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> Flg]
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> Flg]
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> Flg]
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> Flg]
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> Flg]
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> Flg]
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> Flg]
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> Flg]
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> Flg]
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> Flg]
* **FX-Send**
* **> Tub** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Tube FX unit.
* **> ChR** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Chorus FX unit.
* **> Orb** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Orbitone FX unit.
* **> PhR** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Phaser FX unit.
* **> Dly** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Delay FX unit.
* **> Plt** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Plate Reverb FX unit.
* **> Rev** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Reverberator FX unit.
* **> Main** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Main output.
* **Orb**
* **Enable** *[on - off]*: Enable / disable the FX unit.
* **Rate** *[0 - 99]*: The speed of the LFO of the FX unit.
* **Depth** *[0 - 99]*: The amount of effect that is applied to the input signal.
* **Levels**
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> Orb]
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> Orb]
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> Orb]
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> Orb]
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> Orb]
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> Orb]
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> Orb]
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> Orb]
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> Orb]
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> Orb]
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> Orb]
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> Orb]
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> Orb]
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> Orb]
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> Orb]
* **FX-Send**
* **> Tub** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Tube FX unit.
* **> ChR** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Chorus FX unit.
* **> Flg** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Flanger FX unit.
* **> PhR** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Phaser FX unit.
* **> Dly** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Delay FX unit.
* **> Plt** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Plate Reverb FX unit.
* **> Rev** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Reverberator FX unit.
* **> Main** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Main output.
* **PhasR**
* **Enable** *[on - off]*: Enable / disable the FX unit.
* **Rate** *[0 - 99]*: The speed of the LFO of the FX unit.
* **Depth** *[0 - 99]*: The amount of effect that is applied to the input signal.
* **Feedbck** *[0 - 99]*: The amount of processed signal that will be reinjected at the input.
* **Stages** *[2 - 24]*: The number of processing phasing stages.
* **Levels**
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> PhR]
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> PhR]
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> PhR]
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> PhR]
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> PhR]
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> PhR]
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> PhR]
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> PhR]
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> PhR]
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> PhR]
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> PhR]
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> PhR]
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> PhR]
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> PhR]
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> PhR]
* **FX-Send**
* **> Tub** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Tube FX unit.
* **> ChR** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Chorus FX unit.
* **> Flg** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Flanger FX unit.
* **> Orb** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Orbitone FX unit.
* **> Dly** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Delay FX unit.
* **> Plt** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Plate Reverb FX unit.
* **> Rev** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Reverberator FX unit.
* **> Main** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Main output.
* **Delay**
* **Enable** *[on - off]*: Enable / disable the FX unit.
* **L Delay** *[-99 - 99]*: The delay time for the Left channel. Negtive values play the echoed part of the processed sound reversed.
* **R Delay** *[-99 - 99]*: The delay time for the Right channel. Negtive values play the echoed part of the processed sound reversed.
* **Feedbck** *[0 - 99]*: The amount of processed signal that will be reinjected at the input.
* **Levels**
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> Dly]
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> Dly]
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> Dly]
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> Dly]
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> Dly]
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> Dly]
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> Dly]
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> Dly]
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> Dly]
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> Dly]
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> Dly]
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> Dly]
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> Dly]
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> Dly]
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> Dly]
* **FX-Send**
* **> Tub** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Tube FX unit.
* **> ChR** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Chorus FX unit.
* **> Flg** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Flanger FX unit.
* **> Orb** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Orbitone FX unit.
* **> PhR** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Phaser FX unit.
* **> Plt** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Plate Reverb FX unit.
* **> Rev** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Reverberator FX unit.
* **> Main** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Main output.
* **Plt Rvb**
* **Enable** *[on - off]*: Enable / disable the FX unit.
* **Size** *[0 - 99]*: The size of the plate that simulate the size of the reverberating room.
* **High damp** *[0 - 99]*: The amount of high-frequency attenuation.
* **Low damp** *[0 - 99]*: The amount of low-frequency attenuation.
* **Low pass** *[0 - 99]*: The low pass cutoff frequency.
* **Diffusion** *[0 - 99]*: The degree to which the reflections in the reverb decay are dispersed or spread out in time and space.
* **Level** *[0 - 99]*: The overall level or amplitude of the reverb effect.
* **Levels**
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> Plt]
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> Plt]
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> Plt]
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> Plt]
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> Plt]
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> Plt]
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> Plt]
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> Plt]
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> Plt]
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> Plt]
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> Plt]
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> Plt]
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> Plt]
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> Plt]
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> Plt]
* **FX-Send**
* **> Tub** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Tube FX unit.
* **> ChR** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Chorus FX unit.
* **> Flg** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Flanger FX unit.
* **> Orb** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Orbitone FX unit.
* **> PhR** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Phaser FX unit.
* **> Dly** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Delay FX unit.
* **> Rev** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Reverberator FX unit.
* **> Main** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Main output.
* **Rvbrtor**
* **Enable** *[on - off]*: Enable / disable the FX unit.
* **Gain** *[0 - 99]*: The overall level or amplitude of the FX.
* **Time** *[0 - 99]*: The time determines the size of the simulated reverberating space.
* **Diffusion** *[0 - 99]*: The degree to which the reflections in the reverb decay are dispersed or spread out in time and space.
* **Low pass** *[0 - 99]*: The low pass cutoff frequency.
* **Levels**
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> Rev]
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> Rev]
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> Rev]
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> Rev]
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> Rev]
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> Rev]
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> Rev]
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> Rev]
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> Rev]
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> Rev]
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> Rev]
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> Rev]
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> Rev]
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> Rev]
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> Rev]
* **FX-Send**
* **> Tub** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Tube FX unit.
* **> ChR** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Chorus FX unit.
* **> Flg** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Flanger FX unit.
* **> Orb** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Orbitone FX unit.
* **> PhR** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Phaser FX unit.
* **> Dly** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Delay FX unit.
* **> Plt** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Plate Reverb FX unit.
* **> Main** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Main output.
* **MainOut**
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> Main]
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> Main]
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> Main]
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> Main]
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> Main]
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> Main]
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> Main]
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> Main]
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> Main]
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> Main]
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> Main]
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> Main]
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> Main]
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> Main]
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> Main]
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> Main]
* *Performance*
* *Load*
* ...
* *Save*
* ...
* *Delete*
* ...

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 KiB

Loading…
Cancel
Save