diff --git a/.gitignore b/.gitignore
index 6303c464..6d2b104b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,3 +45,8 @@ sdcard
# Editor related files
*.swp
*.swo
+
+.vscode/
+src/test/bin/
+src/test/objects/
+src/test/results/
\ No newline at end of file
diff --git a/src/CFileDevice.hpp b/src/CFileDevice.hpp
new file mode 100644
index 00000000..c5958096
--- /dev/null
+++ b/src/CFileDevice.hpp
@@ -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 .
+
+//
+// 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
+#include
+#include
+#include
+#include
+#include
+
+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;
+};
diff --git a/src/Makefile b/src/Makefile
index 540ae684..b9f244b8 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -6,10 +6,38 @@ CIRCLE_STDLIB_DIR = ../circle-stdlib
SYNTH_DEXED_DIR = ../Synth_Dexed/src
CMSIS_DIR = ../CMSIS_5/CMSIS
-OBJS = main.o kernel.o minidexed.o config.o userinterface.o uimenu.o \
- mididevice.o midikeyboard.o serialmididevice.o pckeyboard.o \
- sysexfileloader.o performanceconfig.o perftimer.o \
- effect_compressor.o effect_platervbstereo.o uibuttons.o midipin.o
+OBJS := main.o
+OBJS += kernel.o
+OBJS += minidexed.o
+OBJS += config.o
+OBJS += userinterface.o
+OBJS += uimenu.o
+OBJS += mididevice.o
+OBJS += midikeyboard.o
+OBJS += serialmididevice.o
+OBJS += pckeyboard.o
+OBJS += sysexfileloader.o
+OBJS += performanceconfig.o
+OBJS += perftimer.o
+OBJS += effect_compressor.o
+OBJS += effect_platervbstereo.o
+OBJS += fx.o
+OBJS += fx_components.o
+OBJS += fx_svf.o
+OBJS += fx_tube.o
+OBJS += fx_chorus.o
+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_shimmer_reverb.o
+OBJS += fx_dry.o
+OBJS += uibuttons.o
+OBJS += midipin.o
OPTIMIZE = -O3
diff --git a/src/debug.hpp b/src/debug.hpp
new file mode 100644
index 00000000..24a3adca
--- /dev/null
+++ b/src/debug.hpp
@@ -0,0 +1,117 @@
+#pragma once
+
+#if defined(DEBUG)
+
+#include
+#include
+#include
+#include
+
+typedef size_t (*ValueInpector)(const std::string& tag, const float32_t valueToTest, const float32_t _min, float32_t _max, bool outDebugInfo);
+
+inline size_t nanValueInspector(const std::string& tag, const float32_t valueToTest, const float32_t _min, float32_t _max, bool outDebugInfo)
+{
+ if(std::isnan(valueToTest))
+ {
+ if(outDebugInfo)
+ {
+ std::cerr << tag << ": nan" << std::endl;
+ }
+ return 1u;
+ }
+
+ return 0u;
+}
+
+inline size_t normalizedValueInspector(const std::string& tag, const float32_t valueToTest, const float32_t _min, float32_t _max, bool outDebugInfo)
+{
+ if(valueToTest > _max || valueToTest < _min)
+ {
+ if(outDebugInfo)
+ {
+ std::cerr << tag << ": out of bounds - value: " << valueToTest << " - boundaries: [" << _min << ", " << _max << "]" << std::endl;
+ }
+ return 1u;
+ }
+
+ return 0u;
+}
+
+inline size_t fullInspector(const std::string& tag, const float32_t valueToTest, const float32_t _min = -1.0f, float32_t _max = 1.0f, bool outDebugInfo = true)
+{
+ if(std::isnan(valueToTest))
+ {
+ if(outDebugInfo)
+ {
+ std::cerr << tag << ": nan" << std::endl;
+ }
+ return 1u;
+ }
+ else if(valueToTest > _max || valueToTest < _min)
+ {
+ if(outDebugInfo)
+ {
+ std::cerr << tag << ": out of bounds - value: " << valueToTest << " - boundaries: [" << _min << ", " << _max << "]" << std::endl;
+ }
+ return 1u;
+ }
+
+ return 0u;
+}
+
+#define SS_RESET(ss, prec, align) ss.str(""); ss.precision(prec); ss << align; ss << std::fixed
+#define SS_SPACE(ss, spc, nb, align, sep) ss.fill(spc); ss.width(nb); ss << "" << sep
+#define SS__TEXT(ss, spc, nb, align, sep, txt) ss << align; ss.fill(spc); ss.width(nb); ss << txt << sep
+
+class ValueInpectorDebugger
+{
+public:
+ ValueInpectorDebugger() {}
+ virtual ~ValueInpectorDebugger() {}
+
+ virtual void dump(std::ostream& out, bool deepInspection = true, const std::string& tag = "") const = 0;
+ virtual size_t inspect(ValueInpector inspector, bool deepInspection = true, const std::string& tag = "") const = 0;
+};
+
+#define INSPECTABLE(clazz) clazz : public ValueInpectorDebugger
+#define IMPLEMENT_DUMP(code) \
+public:\
+ __attribute__((noinline)) virtual void dump(std::ostream& out, bool deepInspection = true, const std::string& tag = "") const override\
+ {\
+ code\
+ }
+
+#define DUMP(clazz, out) clazz->dump(out, true, "")
+#define DUMP2(clazz, out, tag) clazz->dump(out, true, tag)
+#define FAST_DUMP(clazz, out, tag) clazz->dump(out, false, "")
+#define FAST_DUMP2(clazz, out, tag) clazz->dump(out, false, tag)
+
+#define IMPLEMENT_INSPECT(code) \
+public:\
+ __attribute__((noinline)) virtual size_t inspect(ValueInpector inspector, bool deepInspection = true, const std::string& tag = "") const override\
+ {\
+ code\
+ }
+
+#define INSPECT(obj, inspector) obj->inspect(inspector, true)
+#define INSPECT2(obj, inspector, deepInspection) obj->inspect(inspector, deepInspection)
+#define FULL_INSPECT(obj, deepInspection) obj->inspect(fullInspector, deepInspection)
+#define FULL_INSPECT2(obj, deepInspection, tag) obj->inspect(fullInspector, deepInspection, tag)
+
+#else
+
+#define INSPECTABLE(clazz) clazz
+#define IMPLEMENT_DUMP(code)
+#define IMPLEMENT_INSPECT(code)
+
+#define DUMP(clazz, out)
+#define DUMP2(clazz, out, tag)
+#define FAST_DUMP(clazz, out, tag)
+#define FAST_DUMP2(clazz, out, tag)
+
+#define INSPECT(obj, inspector)
+#define INSPECT2(obj, inspector, deepInspection)
+#define FULL_INSPECT(obj, deepInspection)
+#define FULL_INSPECT2(obj, deepInspection, tag)
+
+#endif
\ No newline at end of file
diff --git a/src/effect_mixer.hpp b/src/effect_mixer.hpp
index 44184ab5..88dac62f 100644
--- a/src/effect_mixer.hpp
+++ b/src/effect_mixer.hpp
@@ -33,15 +33,16 @@ template class AudioMixer
delete [] sumbufL;
}
- void doAddMix(uint8_t channel, float32_t* in)
+ void doAddMix(uint8_t channel, float32_t* in)
{
- float32_t tmp[buffer_length];
-
assert(in);
- if(multiplier[channel]!=UNITY_GAIN)
+ float32_t tmp[buffer_length];
+ bool isScaled = (multiplier[channel]!=UNITY_GAIN);
+
+ if(isScaled)
arm_scale_f32(in,multiplier[channel],tmp,buffer_length);
- arm_add_f32(sumbufL, tmp, sumbufL, buffer_length);
+ arm_add_f32(sumbufL, (isScaled ? tmp : in), sumbufL, buffer_length);
}
void gain(uint8_t channel, float32_t gain)
@@ -119,37 +120,36 @@ template class AudioStereoMixer : public AudioMixer
void doAddMix(uint8_t channel, float32_t* in)
{
- float32_t tmp[buffer_length];
-
assert(in);
+ float32_t tmp[buffer_length];
+
// left
- arm_scale_f32(in, panorama[channel][0], tmp, buffer_length);
- if(multiplier[channel]!=UNITY_GAIN)
- arm_scale_f32(tmp,multiplier[channel],tmp,buffer_length);
+ arm_scale_f32(in, panorama[channel][0] * multiplier[channel], tmp, buffer_length);
arm_add_f32(sumbufL, tmp, sumbufL, buffer_length);
+
// right
- arm_scale_f32(in, panorama[channel][1], tmp, buffer_length);
- if(multiplier[channel]!=UNITY_GAIN)
- arm_scale_f32(tmp,multiplier[channel],tmp,buffer_length);
+ arm_scale_f32(in, panorama[channel][1] * multiplier[channel], tmp, buffer_length);
arm_add_f32(sumbufR, tmp, sumbufR, buffer_length);
}
void doAddMix(uint8_t channel, float32_t* inL, float32_t* inR)
{
- float32_t tmp[buffer_length];
-
assert(inL);
assert(inR);
+ float32_t tmp[buffer_length];
+ bool isScaled = (multiplier[channel]!=UNITY_GAIN);
+
// left
- if(multiplier[channel]!=UNITY_GAIN)
+ if(isScaled)
arm_scale_f32(inL,multiplier[channel],tmp,buffer_length);
- arm_add_f32(sumbufL, tmp, sumbufL, buffer_length);
+ arm_add_f32(sumbufL, (isScaled ? tmp : inL), sumbufL, buffer_length);
+
// right
- if(multiplier[channel]!=UNITY_GAIN)
+ if(isScaled)
arm_scale_f32(inR,multiplier[channel],tmp,buffer_length);
- arm_add_f32(sumbufR, tmp, sumbufR, buffer_length);
+ arm_add_f32(sumbufR, (isScaled ? tmp : inR), sumbufR, buffer_length);
}
void getMix(float32_t* bufferL, float32_t* bufferR)
diff --git a/src/effect_platervbstereo.cpp b/src/effect_platervbstereo.cpp
index ce2af1e4..7aa046a6 100644
--- a/src/effect_platervbstereo.cpp
+++ b/src/effect_platervbstereo.cpp
@@ -83,11 +83,11 @@ const int16_t AudioWaveformSine[257] = {
-4808, -4011, -3212, -2410, -1608, -804, 0
};
-AudioEffectPlateReverb::AudioEffectPlateReverb(float32_t samplerate)
+AudioEffectPlateReverb::AudioEffectPlateReverb(float32_t samplerate) :
+ FXElement(samplerate, 2.54f),
+ input_attn(0.5f),
+ in_allp_k(INP_ALLP_COEFF)
{
- input_attn = 0.5f;
- in_allp_k = INP_ALLP_COEFF;
-
memset(in_allp1_bufL, 0, sizeof(in_allp1_bufL));
memset(in_allp2_bufL, 0, sizeof(in_allp2_bufL));
memset(in_allp3_bufL, 0, sizeof(in_allp3_bufL));
@@ -97,6 +97,8 @@ AudioEffectPlateReverb::AudioEffectPlateReverb(float32_t samplerate)
in_allp3_idxL = 0;
in_allp4_idxL = 0;
+ in_allp_out_L = 0.0f;
+
memset(in_allp1_bufR, 0, sizeof(in_allp1_bufR));
memset(in_allp2_bufR, 0, sizeof(in_allp2_bufR));
memset(in_allp3_bufR, 0, sizeof(in_allp3_bufR));
@@ -148,6 +150,9 @@ AudioEffectPlateReverb::AudioEffectPlateReverb(float32_t samplerate)
master_lowpass_l = 0.0f;
master_lowpass_r = 0.0f;
+ rv_time_k = 0.0f;
+ rv_time_scaler = 0.0f;
+
lfo1_phase_acc = 0;
lfo1_adder = (UINT32_MAX + 1)/(samplerate * LFO1_FREQ_HZ);
lfo2_phase_acc = 0;
@@ -156,9 +161,33 @@ AudioEffectPlateReverb::AudioEffectPlateReverb(float32_t samplerate)
reverb_level = 0.0f;
}
+AudioEffectPlateReverb::~AudioEffectPlateReverb()
+{
+}
+
// #define sat16(n, rshift) signed_saturate_rshift((n), 16, (rshift))
-void AudioEffectPlateReverb::doReverb(const float32_t* inblockL, const float32_t* inblockR, float32_t* rvbblockL, float32_t* rvbblockR, uint16_t len)
+void AudioEffectPlateReverb::reset()
+{
+ memset(in_allp1_bufL, 0, sizeof(in_allp1_bufL));
+ memset(in_allp2_bufL, 0, sizeof(in_allp2_bufL));
+ memset(in_allp3_bufL, 0, sizeof(in_allp3_bufL));
+ memset(in_allp4_bufL, 0, sizeof(in_allp4_bufL));
+ memset(in_allp1_bufR, 0, sizeof(in_allp1_bufR));
+ memset(in_allp2_bufR, 0, sizeof(in_allp2_bufR));
+ memset(in_allp3_bufR, 0, sizeof(in_allp3_bufR));
+ memset(in_allp4_bufR, 0, sizeof(in_allp4_bufR));
+ memset(lp_allp1_buf, 0, sizeof(lp_allp1_buf));
+ memset(lp_allp2_buf, 0, sizeof(lp_allp2_buf));
+ memset(lp_allp3_buf, 0, sizeof(lp_allp3_buf));
+ memset(lp_allp4_buf, 0, sizeof(lp_allp4_buf));
+ memset(lp_dly1_buf, 0, sizeof(lp_dly1_buf));
+ memset(lp_dly2_buf, 0, sizeof(lp_dly2_buf));
+ memset(lp_dly3_buf, 0, sizeof(lp_dly3_buf));
+ memset(lp_dly4_buf, 0, sizeof(lp_dly4_buf));
+}
+
+void AudioEffectPlateReverb::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{
float32_t input, acc, temp1, temp2;
uint16_t temp16;
@@ -169,6 +198,259 @@ void AudioEffectPlateReverb::doReverb(const float32_t* inblockL, const float32_t
int32_t y0, y1;
int64_t y;
uint32_t idx;
+
+ rv_time = rv_time_k;
+
+ // do the LFOs
+ lfo1_phase_acc += lfo1_adder;
+ idx = lfo1_phase_acc >> 24; // 8bit lookup table address
+ y0 = AudioWaveformSine[idx];
+ y1 = AudioWaveformSine[idx+1];
+ idx = lfo1_phase_acc & 0x00FFFFFF; // lower 24 bit = fractional part
+ y = (int64_t)y0 * (0x00FFFFFF - idx);
+ y += (int64_t)y1 * idx;
+ lfo1_out_sin = (int32_t) (y >> (32-8)); // 16bit output
+ idx = ((lfo1_phase_acc >> 24)+64) & 0xFF;
+ y0 = AudioWaveformSine[idx];
+ y1 = AudioWaveformSine[idx + 1];
+ y = (int64_t)y0 * (0x00FFFFFF - idx);
+ y += (int64_t)y1 * idx;
+ lfo1_out_cos = (int32_t) (y >> (32-8)); // 16bit output
+
+ lfo2_phase_acc += lfo2_adder;
+ idx = lfo2_phase_acc >> 24; // 8bit lookup table address
+ y0 = AudioWaveformSine[idx];
+ y1 = AudioWaveformSine[idx+1];
+ idx = lfo2_phase_acc & 0x00FFFFFF; // lower 24 bit = fractional part
+ y = (int64_t)y0 * (0x00FFFFFF - idx);
+ y += (int64_t)y1 * idx;
+ lfo2_out_sin = (int32_t) (y >> (32-8)); //32-8->output 16bit,
+ idx = ((lfo2_phase_acc >> 24)+64) & 0xFF;
+ y0 = AudioWaveformSine[idx];
+ y1 = AudioWaveformSine[idx + 1];
+ y = (int64_t)y0 * (0x00FFFFFF - idx);
+ y += (int64_t)y1 * idx;
+ lfo2_out_cos = (int32_t) (y >> (32-8)); // 16bit output
+
+ input = inL * input_attn;
+
+ // chained input allpasses, channel L
+ acc = in_allp1_bufL[in_allp1_idxL] + input * in_allp_k;
+ in_allp1_bufL[in_allp1_idxL] = input - in_allp_k * acc;
+ input = acc;
+ if (++in_allp1_idxL >= sizeof(in_allp1_bufL)/sizeof(float32_t)) in_allp1_idxL = 0;
+
+ acc = in_allp2_bufL[in_allp2_idxL] + input * in_allp_k;
+ in_allp2_bufL[in_allp2_idxL] = input - in_allp_k * acc;
+ input = acc;
+ if (++in_allp2_idxL >= sizeof(in_allp2_bufL)/sizeof(float32_t)) in_allp2_idxL = 0;
+
+ acc = in_allp3_bufL[in_allp3_idxL] + input * in_allp_k;
+ in_allp3_bufL[in_allp3_idxL] = input - in_allp_k * acc;
+ input = acc;
+ if (++in_allp3_idxL >= sizeof(in_allp3_bufL)/sizeof(float32_t)) in_allp3_idxL = 0;
+
+ acc = in_allp4_bufL[in_allp4_idxL] + input * in_allp_k;
+ in_allp4_bufL[in_allp4_idxL] = input - in_allp_k * acc;
+ in_allp_out_L = acc;
+ if (++in_allp4_idxL >= sizeof(in_allp4_bufL)/sizeof(float32_t)) in_allp4_idxL = 0;
+
+ input = inR * input_attn;
+
+ // chained input allpasses, channel R
+ acc = in_allp1_bufR[in_allp1_idxR] + input * in_allp_k;
+ in_allp1_bufR[in_allp1_idxR] = input - in_allp_k * acc;
+ input = acc;
+ if (++in_allp1_idxR >= sizeof(in_allp1_bufR)/sizeof(float32_t)) in_allp1_idxR = 0;
+
+ acc = in_allp2_bufR[in_allp2_idxR] + input * in_allp_k;
+ in_allp2_bufR[in_allp2_idxR] = input - in_allp_k * acc;
+ input = acc;
+ if (++in_allp2_idxR >= sizeof(in_allp2_bufR)/sizeof(float32_t)) in_allp2_idxR = 0;
+
+ acc = in_allp3_bufR[in_allp3_idxR] + input * in_allp_k;
+ in_allp3_bufR[in_allp3_idxR] = input - in_allp_k * acc;
+ input = acc;
+ if (++in_allp3_idxR >= sizeof(in_allp3_bufR)/sizeof(float32_t)) in_allp3_idxR = 0;
+
+ acc = in_allp4_bufR[in_allp4_idxR] + input * in_allp_k;
+ in_allp4_bufR[in_allp4_idxR] = input - in_allp_k * acc;
+ in_allp_out_R = acc;
+ if (++in_allp4_idxR >= sizeof(in_allp4_bufR)/sizeof(float32_t)) in_allp4_idxR = 0;
+
+ // input allpases done, start loop allpases
+ input = lp_allp_out + in_allp_out_R;
+ acc = lp_allp1_buf[lp_allp1_idx] + input * loop_allp_k; // input is the lp allpass chain output
+ lp_allp1_buf[lp_allp1_idx] = input - loop_allp_k * acc;
+ input = acc;
+ if (++lp_allp1_idx >= sizeof(lp_allp1_buf)/sizeof(float32_t))lp_allp1_idx = 0;
+
+ acc = lp_dly1_buf[lp_dly1_idx]; // read the end of the delay
+ lp_dly1_buf[lp_dly1_idx] = input; // write new sample
+ input = acc;
+ if (++lp_dly1_idx >= sizeof(lp_dly1_buf)/sizeof(float32_t)) lp_dly1_idx = 0; // update index
+
+ // hi/lo shelving filter
+ temp1 = input - lpf1;
+ lpf1 += temp1 * lp_lowpass_f;
+ temp2 = input - lpf1;
+ temp1 = lpf1 - hpf1;
+ hpf1 += temp1 * lp_hipass_f;
+ acc = lpf1 + temp2*lp_hidamp_k + hpf1*lp_lodamp_k;
+ acc = acc * rv_time * rv_time_scaler; // scale by the reveb time
+
+ input = acc + in_allp_out_L;
+
+ acc = lp_allp2_buf[lp_allp2_idx] + input * loop_allp_k;
+ lp_allp2_buf[lp_allp2_idx] = input - loop_allp_k * acc;
+ input = acc;
+ if (++lp_allp2_idx >= sizeof(lp_allp2_buf)/sizeof(float32_t)) lp_allp2_idx = 0;
+ acc = lp_dly2_buf[lp_dly2_idx]; // read the end of the delay
+ lp_dly2_buf[lp_dly2_idx] = input; // write new sample
+ input = acc;
+ if (++lp_dly2_idx >= sizeof(lp_dly2_buf)/sizeof(float32_t)) lp_dly2_idx = 0; // update index
+ // hi/lo shelving filter
+ temp1 = input - lpf2;
+ lpf2 += temp1 * lp_lowpass_f;
+ temp2 = input - lpf2;
+ temp1 = lpf2 - hpf2;
+ hpf2 += temp1 * lp_hipass_f;
+ acc = lpf2 + temp2*lp_hidamp_k + hpf2*lp_lodamp_k;
+ acc = acc * rv_time * rv_time_scaler;
+
+ input = acc + in_allp_out_R;
+
+ acc = lp_allp3_buf[lp_allp3_idx] + input * loop_allp_k;
+ lp_allp3_buf[lp_allp3_idx] = input - loop_allp_k * acc;
+ input = acc;
+ if (++lp_allp3_idx >= sizeof(lp_allp3_buf)/sizeof(float32_t)) lp_allp3_idx = 0;
+ acc = lp_dly3_buf[lp_dly3_idx]; // read the end of the delay
+ lp_dly3_buf[lp_dly3_idx] = input; // write new sample
+ input = acc;
+ if (++lp_dly3_idx >= sizeof(lp_dly3_buf)/sizeof(float32_t)) lp_dly3_idx = 0; // update index
+ // hi/lo shelving filter
+ temp1 = input - lpf3;
+ lpf3 += temp1 * lp_lowpass_f;
+ temp2 = input - lpf3;
+ temp1 = lpf3 - hpf3;
+ hpf3 += temp1 * lp_hipass_f;
+ acc = lpf3 + temp2*lp_hidamp_k + hpf3*lp_lodamp_k;
+ acc = acc * rv_time * rv_time_scaler;
+
+ input = acc + in_allp_out_L;
+
+ acc = lp_allp4_buf[lp_allp4_idx] + input * loop_allp_k;
+ lp_allp4_buf[lp_allp4_idx] = input - loop_allp_k * acc;
+ input = acc;
+ if (++lp_allp4_idx >= sizeof(lp_allp4_buf)/sizeof(float32_t)) lp_allp4_idx = 0;
+ acc = lp_dly4_buf[lp_dly4_idx]; // read the end of the delay
+ lp_dly4_buf[lp_dly4_idx] = input; // write new sample
+ input = acc;
+ if (++lp_dly4_idx >= sizeof(lp_dly4_buf)/sizeof(float32_t)) lp_dly4_idx= 0; // update index
+ // hi/lo shelving filter
+ temp1 = input - lpf4;
+ lpf4 += temp1 * lp_lowpass_f;
+ temp2 = input - lpf4;
+ temp1 = lpf4 - hpf4;
+ hpf4 += temp1 * lp_hipass_f;
+ acc = lpf4 + temp2*lp_hidamp_k + hpf4*lp_lodamp_k;
+ acc = acc * rv_time * rv_time_scaler;
+
+ lp_allp_out = acc;
+
+ // channel L:
+#ifdef TAP1_MODULATED
+ temp16 = (lp_dly1_idx + lp_dly1_offset_L + (lfo1_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly1_buf)/sizeof(float32_t));
+ temp1 = lp_dly1_buf[temp16++]; // sample now
+ if (temp16 >= sizeof(lp_dly1_buf)/sizeof(float32_t)) temp16 = 0;
+ temp2 = lp_dly1_buf[temp16]; // sample next
+ input = (float32_t)(lfo1_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
+ acc = (temp1*(1.0f-input) + temp2*input)* 0.8f;
+#else
+ temp16 = (lp_dly1_idx + lp_dly1_offset_L) % (sizeof(lp_dly1_buf)/sizeof(float32_t));
+ acc = lp_dly1_buf[temp16]* 0.8f;
+#endif
+
+
+#ifdef TAP2_MODULATED
+ temp16 = (lp_dly2_idx + lp_dly2_offset_L + (lfo1_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly2_buf)/sizeof(float32_t));
+ temp1 = lp_dly2_buf[temp16++];
+ if (temp16 >= sizeof(lp_dly2_buf)/sizeof(float32_t)) temp16 = 0;
+ temp2 = lp_dly2_buf[temp16];
+ input = (float32_t)(lfo1_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
+ acc += (temp1*(1.0f-input) + temp2*input)* 0.7f;
+#else
+ temp16 = (lp_dly2_idx + lp_dly2_offset_L) % (sizeof(lp_dly2_buf)/sizeof(float32_t));
+ acc += (temp1*(1.0f-input) + temp2*input)* 0.6f;
+#endif
+
+ temp16 = (lp_dly3_idx + lp_dly3_offset_L + (lfo2_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly3_buf)/sizeof(float32_t));
+ temp1 = lp_dly3_buf[temp16++];
+ if (temp16 >= sizeof(lp_dly3_buf)/sizeof(float32_t)) temp16 = 0;
+ temp2 = lp_dly3_buf[temp16];
+ input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
+ acc += (temp1*(1.0f-input) + temp2*input)* 0.6f;
+
+ temp16 = (lp_dly4_idx + lp_dly4_offset_L + (lfo2_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly4_buf)/sizeof(float32_t));
+ temp1 = lp_dly4_buf[temp16++];
+ if (temp16 >= sizeof(lp_dly4_buf)/sizeof(float32_t)) temp16 = 0;
+ temp2 = lp_dly4_buf[temp16];
+ input = (float32_t)(lfo2_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
+ acc += (temp1*(1.0f-input) + temp2*input)* 0.5f;
+
+ // Master lowpass filter
+ temp1 = acc - master_lowpass_l;
+ master_lowpass_l += temp1 * master_lowpass_f;
+
+ outL = master_lowpass_l * this->OutputLevelCorrector;
+
+ // Channel R
+#ifdef TAP1_MODULATED
+ temp16 = (lp_dly1_idx + lp_dly1_offset_R + (lfo2_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly1_buf)/sizeof(float32_t));
+ temp1 = lp_dly1_buf[temp16++]; // sample now
+ if (temp16 >= sizeof(lp_dly1_buf)/sizeof(float32_t)) temp16 = 0;
+ temp2 = lp_dly1_buf[temp16]; // sample next
+ input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
+
+ acc = (temp1*(1.0f-input) + temp2*input)* 0.8f;
+ #else
+ temp16 = (lp_dly1_idx + lp_dly1_offset_R) % (sizeof(lp_dly1_buf)/sizeof(float32_t));
+ acc = lp_dly1_buf[temp16] * 0.8f;
+ #endif
+#ifdef TAP2_MODULATED
+ temp16 = (lp_dly2_idx + lp_dly2_offset_R + (lfo1_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly2_buf)/sizeof(float32_t));
+ temp1 = lp_dly2_buf[temp16++];
+ if (temp16 >= sizeof(lp_dly2_buf)/sizeof(float32_t)) temp16 = 0;
+ temp2 = lp_dly2_buf[temp16];
+ input = (float32_t)(lfo1_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
+ acc += (temp1*(1.0f-input) + temp2*input)* 0.7f;
+#else
+ temp16 = (lp_dly2_idx + lp_dly2_offset_R) % (sizeof(lp_dly2_buf)/sizeof(float32_t));
+ acc += (temp1*(1.0f-input) + temp2*input)* 0.7f;
+#endif
+ temp16 = (lp_dly3_idx + lp_dly3_offset_R + (lfo2_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly3_buf)/sizeof(float32_t));
+ temp1 = lp_dly3_buf[temp16++];
+ if (temp16 >= sizeof(lp_dly3_buf)/sizeof(float32_t)) temp16 = 0;
+ temp2 = lp_dly3_buf[temp16];
+ input = (float32_t)(lfo2_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
+ acc += (temp1*(1.0f-input) + temp2*input)* 0.6f;
+
+ temp16 = (lp_dly4_idx + lp_dly4_offset_R + (lfo1_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly4_buf)/sizeof(float32_t));
+ temp1 = lp_dly4_buf[temp16++];
+ if (temp16 >= sizeof(lp_dly4_buf)/sizeof(float32_t)) temp16 = 0;
+ temp2 = lp_dly4_buf[temp16];
+ input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
+ acc += (temp1*(1.0f-input) + temp2*input)* 0.5f;
+
+ // Master lowpass filter
+ temp1 = acc - master_lowpass_r;
+ master_lowpass_r += temp1 * master_lowpass_f;
+
+ outR = master_lowpass_r * this->OutputLevelCorrector;
+}
+
+void AudioEffectPlateReverb::doReverb(const float32_t* inblockL, const float32_t* inblockR, float32_t* rvbblockL, float32_t* rvbblockR, uint16_t len)
+{
static bool cleanup_done = false;
// handle bypass, 1st call will clean the buffers to avoid continuing the previous reverb tail
@@ -176,23 +458,7 @@ void AudioEffectPlateReverb::doReverb(const float32_t* inblockL, const float32_t
{
if (!cleanup_done)
{
- memset(in_allp1_bufL, 0, sizeof(in_allp1_bufL));
- memset(in_allp2_bufL, 0, sizeof(in_allp2_bufL));
- memset(in_allp3_bufL, 0, sizeof(in_allp3_bufL));
- memset(in_allp4_bufL, 0, sizeof(in_allp4_bufL));
- memset(in_allp1_bufR, 0, sizeof(in_allp1_bufR));
- memset(in_allp2_bufR, 0, sizeof(in_allp2_bufR));
- memset(in_allp3_bufR, 0, sizeof(in_allp3_bufR));
- memset(in_allp4_bufR, 0, sizeof(in_allp4_bufR));
- memset(lp_allp1_buf, 0, sizeof(lp_allp1_buf));
- memset(lp_allp2_buf, 0, sizeof(lp_allp2_buf));
- memset(lp_allp3_buf, 0, sizeof(lp_allp3_buf));
- memset(lp_allp4_buf, 0, sizeof(lp_allp4_buf));
- memset(lp_dly1_buf, 0, sizeof(lp_dly1_buf));
- memset(lp_dly2_buf, 0, sizeof(lp_dly2_buf));
- memset(lp_dly3_buf, 0, sizeof(lp_dly3_buf));
- memset(lp_dly4_buf, 0, sizeof(lp_dly4_buf));
-
+ this->reset();
cleanup_done = true;
}
@@ -200,255 +466,8 @@ void AudioEffectPlateReverb::doReverb(const float32_t* inblockL, const float32_t
}
cleanup_done = false;
- rv_time = rv_time_k;
-
for (uint16_t i=0; i < len; i++)
{
- // do the LFOs
- lfo1_phase_acc += lfo1_adder;
- idx = lfo1_phase_acc >> 24; // 8bit lookup table address
- y0 = AudioWaveformSine[idx];
- y1 = AudioWaveformSine[idx+1];
- idx = lfo1_phase_acc & 0x00FFFFFF; // lower 24 bit = fractional part
- y = (int64_t)y0 * (0x00FFFFFF - idx);
- y += (int64_t)y1 * idx;
- lfo1_out_sin = (int32_t) (y >> (32-8)); // 16bit output
- idx = ((lfo1_phase_acc >> 24)+64) & 0xFF;
- y0 = AudioWaveformSine[idx];
- y1 = AudioWaveformSine[idx + 1];
- y = (int64_t)y0 * (0x00FFFFFF - idx);
- y += (int64_t)y1 * idx;
- lfo1_out_cos = (int32_t) (y >> (32-8)); // 16bit output
-
- lfo2_phase_acc += lfo2_adder;
- idx = lfo2_phase_acc >> 24; // 8bit lookup table address
- y0 = AudioWaveformSine[idx];
- y1 = AudioWaveformSine[idx+1];
- idx = lfo2_phase_acc & 0x00FFFFFF; // lower 24 bit = fractional part
- y = (int64_t)y0 * (0x00FFFFFF - idx);
- y += (int64_t)y1 * idx;
- lfo2_out_sin = (int32_t) (y >> (32-8)); //32-8->output 16bit,
- idx = ((lfo2_phase_acc >> 24)+64) & 0xFF;
- y0 = AudioWaveformSine[idx];
- y1 = AudioWaveformSine[idx + 1];
- y = (int64_t)y0 * (0x00FFFFFF - idx);
- y += (int64_t)y1 * idx;
- lfo2_out_cos = (int32_t) (y >> (32-8)); // 16bit output
-
- input = inblockL[i] * input_attn;
-
- // chained input allpasses, channel L
- acc = in_allp1_bufL[in_allp1_idxL] + input * in_allp_k;
- in_allp1_bufL[in_allp1_idxL] = input - in_allp_k * acc;
- input = acc;
- if (++in_allp1_idxL >= sizeof(in_allp1_bufL)/sizeof(float32_t)) in_allp1_idxL = 0;
-
- acc = in_allp2_bufL[in_allp2_idxL] + input * in_allp_k;
- in_allp2_bufL[in_allp2_idxL] = input - in_allp_k * acc;
- input = acc;
- if (++in_allp2_idxL >= sizeof(in_allp2_bufL)/sizeof(float32_t)) in_allp2_idxL = 0;
-
- acc = in_allp3_bufL[in_allp3_idxL] + input * in_allp_k;
- in_allp3_bufL[in_allp3_idxL] = input - in_allp_k * acc;
- input = acc;
- if (++in_allp3_idxL >= sizeof(in_allp3_bufL)/sizeof(float32_t)) in_allp3_idxL = 0;
-
- acc = in_allp4_bufL[in_allp4_idxL] + input * in_allp_k;
- in_allp4_bufL[in_allp4_idxL] = input - in_allp_k * acc;
- in_allp_out_L = acc;
- if (++in_allp4_idxL >= sizeof(in_allp4_bufL)/sizeof(float32_t)) in_allp4_idxL = 0;
-
- input = inblockR[i] * input_attn;
-
- // chained input allpasses, channel R
- acc = in_allp1_bufR[in_allp1_idxR] + input * in_allp_k;
- in_allp1_bufR[in_allp1_idxR] = input - in_allp_k * acc;
- input = acc;
- if (++in_allp1_idxR >= sizeof(in_allp1_bufR)/sizeof(float32_t)) in_allp1_idxR = 0;
-
- acc = in_allp2_bufR[in_allp2_idxR] + input * in_allp_k;
- in_allp2_bufR[in_allp2_idxR] = input - in_allp_k * acc;
- input = acc;
- if (++in_allp2_idxR >= sizeof(in_allp2_bufR)/sizeof(float32_t)) in_allp2_idxR = 0;
-
- acc = in_allp3_bufR[in_allp3_idxR] + input * in_allp_k;
- in_allp3_bufR[in_allp3_idxR] = input - in_allp_k * acc;
- input = acc;
- if (++in_allp3_idxR >= sizeof(in_allp3_bufR)/sizeof(float32_t)) in_allp3_idxR = 0;
-
- acc = in_allp4_bufR[in_allp4_idxR] + input * in_allp_k;
- in_allp4_bufR[in_allp4_idxR] = input - in_allp_k * acc;
- in_allp_out_R = acc;
- if (++in_allp4_idxR >= sizeof(in_allp4_bufR)/sizeof(float32_t)) in_allp4_idxR = 0;
-
- // input allpases done, start loop allpases
- input = lp_allp_out + in_allp_out_R;
- acc = lp_allp1_buf[lp_allp1_idx] + input * loop_allp_k; // input is the lp allpass chain output
- lp_allp1_buf[lp_allp1_idx] = input - loop_allp_k * acc;
- input = acc;
- if (++lp_allp1_idx >= sizeof(lp_allp1_buf)/sizeof(float32_t)) lp_allp1_idx = 0;
-
- acc = lp_dly1_buf[lp_dly1_idx]; // read the end of the delay
- lp_dly1_buf[lp_dly1_idx] = input; // write new sample
- input = acc;
- if (++lp_dly1_idx >= sizeof(lp_dly1_buf)/sizeof(float32_t)) lp_dly1_idx = 0; // update index
-
- // hi/lo shelving filter
- temp1 = input - lpf1;
- lpf1 += temp1 * lp_lowpass_f;
- temp2 = input - lpf1;
- temp1 = lpf1 - hpf1;
- hpf1 += temp1 * lp_hipass_f;
- acc = lpf1 + temp2*lp_hidamp_k + hpf1*lp_lodamp_k;
- acc = acc * rv_time * rv_time_scaler; // scale by the reveb time
-
- input = acc + in_allp_out_L;
-
- acc = lp_allp2_buf[lp_allp2_idx] + input * loop_allp_k;
- lp_allp2_buf[lp_allp2_idx] = input - loop_allp_k * acc;
- input = acc;
- if (++lp_allp2_idx >= sizeof(lp_allp2_buf)/sizeof(float32_t)) lp_allp2_idx = 0;
- acc = lp_dly2_buf[lp_dly2_idx]; // read the end of the delay
- lp_dly2_buf[lp_dly2_idx] = input; // write new sample
- input = acc;
- if (++lp_dly2_idx >= sizeof(lp_dly2_buf)/sizeof(float32_t)) lp_dly2_idx = 0; // update index
- // hi/lo shelving filter
- temp1 = input - lpf2;
- lpf2 += temp1 * lp_lowpass_f;
- temp2 = input - lpf2;
- temp1 = lpf2 - hpf2;
- hpf2 += temp1 * lp_hipass_f;
- acc = lpf2 + temp2*lp_hidamp_k + hpf2*lp_lodamp_k;
- acc = acc * rv_time * rv_time_scaler;
-
- input = acc + in_allp_out_R;
-
- acc = lp_allp3_buf[lp_allp3_idx] + input * loop_allp_k;
- lp_allp3_buf[lp_allp3_idx] = input - loop_allp_k * acc;
- input = acc;
- if (++lp_allp3_idx >= sizeof(lp_allp3_buf)/sizeof(float32_t)) lp_allp3_idx = 0;
- acc = lp_dly3_buf[lp_dly3_idx]; // read the end of the delay
- lp_dly3_buf[lp_dly3_idx] = input; // write new sample
- input = acc;
- if (++lp_dly3_idx >= sizeof(lp_dly3_buf)/sizeof(float32_t)) lp_dly3_idx = 0; // update index
- // hi/lo shelving filter
- temp1 = input - lpf3;
- lpf3 += temp1 * lp_lowpass_f;
- temp2 = input - lpf3;
- temp1 = lpf3 - hpf3;
- hpf3 += temp1 * lp_hipass_f;
- acc = lpf3 + temp2*lp_hidamp_k + hpf3*lp_lodamp_k;
- acc = acc * rv_time * rv_time_scaler;
-
- input = acc + in_allp_out_L;
-
- acc = lp_allp4_buf[lp_allp4_idx] + input * loop_allp_k;
- lp_allp4_buf[lp_allp4_idx] = input - loop_allp_k * acc;
- input = acc;
- if (++lp_allp4_idx >= sizeof(lp_allp4_buf)/sizeof(float32_t)) lp_allp4_idx = 0;
- acc = lp_dly4_buf[lp_dly4_idx]; // read the end of the delay
- lp_dly4_buf[lp_dly4_idx] = input; // write new sample
- input = acc;
- if (++lp_dly4_idx >= sizeof(lp_dly4_buf)/sizeof(float32_t)) lp_dly4_idx= 0; // update index
- // hi/lo shelving filter
- temp1 = input - lpf4;
- lpf4 += temp1 * lp_lowpass_f;
- temp2 = input - lpf4;
- temp1 = lpf4 - hpf4;
- hpf4 += temp1 * lp_hipass_f;
- acc = lpf4 + temp2*lp_hidamp_k + hpf4*lp_lodamp_k;
- acc = acc * rv_time * rv_time_scaler;
-
- lp_allp_out = acc;
-
- // channel L:
-#ifdef TAP1_MODULATED
- temp16 = (lp_dly1_idx + lp_dly1_offset_L + (lfo1_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly1_buf)/sizeof(float32_t));
- temp1 = lp_dly1_buf[temp16++]; // sample now
- if (temp16 >= sizeof(lp_dly1_buf)/sizeof(float32_t)) temp16 = 0;
- temp2 = lp_dly1_buf[temp16]; // sample next
- input = (float32_t)(lfo1_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
- acc = (temp1*(1.0f-input) + temp2*input)* 0.8f;
-#else
- temp16 = (lp_dly1_idx + lp_dly1_offset_L) % (sizeof(lp_dly1_buf)/sizeof(float32_t));
- acc = lp_dly1_buf[temp16]* 0.8f;
-#endif
-
-
-#ifdef TAP2_MODULATED
- temp16 = (lp_dly2_idx + lp_dly2_offset_L + (lfo1_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly2_buf)/sizeof(float32_t));
- temp1 = lp_dly2_buf[temp16++];
- if (temp16 >= sizeof(lp_dly2_buf)/sizeof(float32_t)) temp16 = 0;
- temp2 = lp_dly2_buf[temp16];
- input = (float32_t)(lfo1_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
- acc += (temp1*(1.0f-input) + temp2*input)* 0.7f;
-#else
- temp16 = (lp_dly2_idx + lp_dly2_offset_L) % (sizeof(lp_dly2_buf)/sizeof(float32_t));
- acc += (temp1*(1.0f-input) + temp2*input)* 0.6f;
-#endif
-
- temp16 = (lp_dly3_idx + lp_dly3_offset_L + (lfo2_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly3_buf)/sizeof(float32_t));
- temp1 = lp_dly3_buf[temp16++];
- if (temp16 >= sizeof(lp_dly3_buf)/sizeof(float32_t)) temp16 = 0;
- temp2 = lp_dly3_buf[temp16];
- input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
- acc += (temp1*(1.0f-input) + temp2*input)* 0.6f;
-
- temp16 = (lp_dly4_idx + lp_dly4_offset_L + (lfo2_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly4_buf)/sizeof(float32_t));
- temp1 = lp_dly4_buf[temp16++];
- if (temp16 >= sizeof(lp_dly4_buf)/sizeof(float32_t)) temp16 = 0;
- temp2 = lp_dly4_buf[temp16];
- input = (float32_t)(lfo2_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
- acc += (temp1*(1.0f-input) + temp2*input)* 0.5f;
-
- // Master lowpass filter
- temp1 = acc - master_lowpass_l;
- master_lowpass_l += temp1 * master_lowpass_f;
-
- rvbblockL[i] = master_lowpass_l;
-
- // Channel R
- #ifdef TAP1_MODULATED
- temp16 = (lp_dly1_idx + lp_dly1_offset_R + (lfo2_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly1_buf)/sizeof(float32_t));
- temp1 = lp_dly1_buf[temp16++]; // sample now
- if (temp16 >= sizeof(lp_dly1_buf)/sizeof(float32_t)) temp16 = 0;
- temp2 = lp_dly1_buf[temp16]; // sample next
- input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
-
- acc = (temp1*(1.0f-input) + temp2*input)* 0.8f;
- #else
- temp16 = (lp_dly1_idx + lp_dly1_offset_R) % (sizeof(lp_dly1_buf)/sizeof(float32_t));
- acc = lp_dly1_buf[temp16] * 0.8f;
- #endif
-#ifdef TAP2_MODULATED
- temp16 = (lp_dly2_idx + lp_dly2_offset_R + (lfo1_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly2_buf)/sizeof(float32_t));
- temp1 = lp_dly2_buf[temp16++];
- if (temp16 >= sizeof(lp_dly2_buf)/sizeof(float32_t)) temp16 = 0;
- temp2 = lp_dly2_buf[temp16];
- input = (float32_t)(lfo1_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
- acc += (temp1*(1.0f-input) + temp2*input)* 0.7f;
-#else
- temp16 = (lp_dly2_idx + lp_dly2_offset_R) % (sizeof(lp_dly2_buf)/sizeof(float32_t));
- acc += (temp1*(1.0f-input) + temp2*input)* 0.7f;
-#endif
- temp16 = (lp_dly3_idx + lp_dly3_offset_R + (lfo2_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly3_buf)/sizeof(float32_t));
- temp1 = lp_dly3_buf[temp16++];
- if (temp16 >= sizeof(lp_dly3_buf)/sizeof(float32_t)) temp16 = 0;
- temp2 = lp_dly3_buf[temp16];
- input = (float32_t)(lfo2_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
- acc += (temp1*(1.0f-input) + temp2*input)* 0.6f;
-
- temp16 = (lp_dly4_idx + lp_dly4_offset_R + (lfo1_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly4_buf)/sizeof(float32_t));
- temp1 = lp_dly4_buf[temp16++];
- if (temp16 >= sizeof(lp_dly4_buf)/sizeof(float32_t)) temp16 = 0;
- temp2 = lp_dly4_buf[temp16];
- input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k
- acc += (temp1*(1.0f-input) + temp2*input)* 0.5f;
-
- // Master lowpass filter
- temp1 = acc - master_lowpass_r;
- master_lowpass_r += temp1 * master_lowpass_f;
-
- rvbblockR[i] = master_lowpass_r;
+ this->processSample(inblockL[i], inblockR[i], rvbblockL[i], rvbblockR[i]);
}
}
diff --git a/src/effect_platervbstereo.h b/src/effect_platervbstereo.h
index 23538c46..9d249266 100644
--- a/src/effect_platervbstereo.h
+++ b/src/effect_platervbstereo.h
@@ -44,9 +44,7 @@
#ifndef _EFFECT_PLATERVBSTEREO_H
#define _EFFECT_PLATERVBSTEREO_H
-#include
-#include
-#include "common.h"
+#include "fx_components.h"
/***
* Loop delay modulation: comment/uncomment to switch sin/cos
@@ -56,11 +54,18 @@
//#define TAP1_MODULATED
#define TAP2_MODULATED
-class AudioEffectPlateReverb
+class AudioEffectPlateReverb : public FXElement
{
+ DISALLOW_COPY_AND_ASSIGN(AudioEffectPlateReverb);
+
public:
AudioEffectPlateReverb(float32_t samplerate);
- void doReverb(const float32_t* inblockL, const float32_t* inblockR, float32_t* rvbblockL, float32_t* rvbblockR,uint16_t len);
+ virtual ~AudioEffectPlateReverb();
+
+ virtual void reset() override;
+ virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
+
+ void doReverb(const float32_t* inblockL, const float32_t* inblockR, float32_t* rvbblockL, float32_t* rvbblockR, uint16_t len);
void size(float n)
{
@@ -191,6 +196,9 @@ class AudioEffectPlateReverb
uint32_t lfo2_phase_acc; // LFO 2
uint32_t lfo2_adder;
+
+ IMPLEMENT_DUMP()
+ IMPLEMENT_INSPECT(return 0u;)
};
#endif // _EFFECT_PLATEREV_H
diff --git a/src/extra_features.h b/src/extra_features.h
new file mode 100644
index 00000000..f3ee5088
--- /dev/null
+++ b/src/extra_features.h
@@ -0,0 +1,75 @@
+// 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 .
+
+//
+// extra_features.h
+//
+// Header file that centralizes MACROS to enable / disable extra features
+// Author: Vincent Gauché
+//
+#pragma once
+
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
+
+#if defined(ARM_ALLOW_MULTI_CORE)
+
+ #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
+
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+inline long long int getElapseTime(std::string marker = "")
+{
+ static std::unordered_map marker_times;
+
+ auto current_time = std::chrono::high_resolution_clock::now();
+ auto it = marker_times.find(marker);
+ if (it != marker_times.end())
+ {
+ auto duration = std::chrono::duration_cast(current_time - it->second);
+ marker_times.erase(it);
+ return duration.count();
+ }
+ else
+ {
+ marker_times[marker] = current_time;
+ return 0;
+ }
+}
+
+#define LAP_TIME(marker) getElapseTime(marker)
+#define LOG_LAP_TIME(marker) { auto __d = getElapseTime(marker); if(__d > 0) std::cout << "Execution time for " << marker << ": " << __d << std::endl; }
+
+#define DEBUG_VALUE(lbl, idx, v) std::cout << lbl << " " << idx << ": " << v << std::endl
+
+#else
+
+#define LAP_TIME(marker)
+#define LOG_LAP_TIME(marker)
+
+#endif
diff --git a/src/fx.cpp b/src/fx.cpp
new file mode 100644
index 00000000..7652e9e4
--- /dev/null
+++ b/src/fx.cpp
@@ -0,0 +1,45 @@
+#include "fx.h"
+
+FXBase::FXBase(float32_t sampling_rate) :
+ SamplingRate(sampling_rate)
+{
+}
+
+FXBase::~FXBase()
+{
+}
+
+float32_t FXBase::getSamplingRate() const
+{
+ return this->SamplingRate;
+}
+
+FXElement::FXElement(float32_t sampling_rate, float32_t output_level_corrector) :
+ FXBase(sampling_rate),
+ OutputLevelCorrector(output_level_corrector),
+ bypass_fx_process_(false)
+{
+}
+
+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)
+{
+}
+
+FX::~FX()
+{
+}
diff --git a/src/fx.h b/src/fx.h
new file mode 100644
index 00000000..27a0f50e
--- /dev/null
+++ b/src/fx.h
@@ -0,0 +1,83 @@
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//
+// fx.h
+//
+// Base classes for Stereo audio effects proposed in the context of the MiniDexed project
+// Author: Vincent Gauché
+//
+#pragma once
+
+#include
+#include
+#include "common.h"
+
+#include "debug.hpp"
+#include "fx_base.h"
+
+#define MAKE_INTEGRAL_FRACTIONAL(x) \
+ size_t x ## _integral = static_cast(x); \
+ float32_t x ## _fractional = x - static_cast(x ## _integral)
+
+class INSPECTABLE(FXBase)
+{
+ DISALLOW_COPY_AND_ASSIGN(FXBase);
+
+protected:
+ FXBase(float32_t sampling_rate);
+
+public:
+ virtual ~FXBase();
+
+ float32_t getSamplingRate() const;
+
+ virtual void reset() = 0;
+
+private:
+ const float32_t SamplingRate;
+};
+
+class FXElement : public FXBase
+{
+ DISALLOW_COPY_AND_ASSIGN(FXElement);
+
+protected:
+ FXElement(float32_t sampling_rate, float32_t output_level_corrector = 1.0f);
+
+ const float32_t OutputLevelCorrector;
+
+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
+{
+ DISALLOW_COPY_AND_ASSIGN(FX);
+
+protected:
+ FX(float32_t sampling_rate);
+
+public:
+ virtual ~FX();
+
+ virtual void process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) = 0;
+};
diff --git a/src/fx_base.h b/src/fx_base.h
new file mode 100644
index 00000000..9ae019ba
--- /dev/null
+++ b/src/fx_base.h
@@ -0,0 +1,30 @@
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//
+// fx_base.h
+//
+// Base header file for the FX section of the MiniDexed project
+// Author: Vincent Gauché
+//
+
+#pragma once
+
+#include "extra_features.h"
+
+enum StereoChannels
+{
+ Left = 0,
+ Right,
+ kNumChannels
+};
diff --git a/src/fx_chorus.cpp b/src/fx_chorus.cpp
new file mode 100644
index 00000000..a9f66eaa
--- /dev/null
+++ b/src/fx_chorus.cpp
@@ -0,0 +1,100 @@
+#include "fx_chorus.h"
+
+#include
+
+#define LFO1_MAX_FREQ 0.25f
+#define LFO2_MAX_FREQ 0.35f
+
+Chorus::Chorus(float32_t sampling_rate) :
+ FXElement(sampling_rate, 1.1035f),
+ engine_(sampling_rate, 0.0f),
+ rate_(0.0f),
+ depth_(0.0f),
+ fullscale_depth_(0.0f),
+ feedback_(0.0f)
+{
+ this->lfo_[LFOIndex::Sin1] = new LFO(sampling_rate, 0.0f, LFO1_MAX_FREQ, 0.0f, false);
+ this->lfo_[LFOIndex::Cos1] = new LFO(sampling_rate, 0.0f, LFO1_MAX_FREQ, Constants::MPI_2, false);
+ this->lfo_[LFOIndex::Sin2] = new LFO(sampling_rate, 0.0f, LFO2_MAX_FREQ, 0.0f, false);
+ this->lfo_[LFOIndex::Cos2] = new LFO(sampling_rate, 0.0f, LFO2_MAX_FREQ, Constants::MPI_2, false);
+
+ this->setRate(0.1f);
+ this->setDepth(0.15f);
+}
+
+Chorus::~Chorus()
+{
+ for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ delete this->lfo_[i];
+ }
+}
+
+void Chorus::reset()
+{
+ this->engine_.reset();
+ for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ this->lfo_[i]->reset();
+ }
+}
+
+void Chorus::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
+{
+ typedef Engine::Reserve<2047> Memory;
+ static Engine::DelayLine line;
+ Engine::Context c;
+
+ this->engine_.start(&c);
+
+ // Update LFO.
+ float32_t sin_1 = this->lfo_[LFOIndex::Sin1]->process();
+ float32_t cos_1 = this->lfo_[LFOIndex::Cos1]->process();
+ float32_t sin_2 = this->lfo_[LFOIndex::Sin2]->process();
+ float32_t cos_2 = this->lfo_[LFOIndex::Cos2]->process();
+
+ float32_t wet;
+
+ // Sum L & R channel to send to chorus line.
+ c.read(inL + inR, 0.5f);
+ c.writeAndLoad(line, 0.0f);
+
+ c.interpolate(line, sin_1 * this->fullscale_depth_ + 1200, 0.5f);
+ c.interpolate(line, sin_2 * this->fullscale_depth_ + 800, 0.5f);
+ c.writeAndLoad(wet, 0.0f);
+ outL = wet * this->OutputLevelCorrector;
+
+ c.interpolate(line, cos_1 * this->fullscale_depth_ + 800, 0.5f);
+ c.interpolate(line, cos_2 * this->fullscale_depth_ + 1200, 0.5f);
+ c.writeAndLoad(wet, 0.0f);
+ outR = wet * this->OutputLevelCorrector;
+}
+
+void Chorus::setRate(float32_t rate)
+{
+ this->rate_ = constrain(rate, 0.0f, 1.0f);
+ for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ this->lfo_[i]->setNormalizedFrequency(this->rate_);
+ }
+}
+
+float32_t Chorus::getRate() const
+{
+ return this->depth_;
+}
+
+void Chorus::setDepth(float32_t depth)
+{
+ depth = constrain(depth, 0.0f, 1.0f);
+ if(this->depth_ != depth)
+ {
+ this->depth_ = depth;
+ this->fullscale_depth_ = this->depth_ * CHORUS_FULLSCALE_DEPTH_RATIO;
+ }
+}
+
+float32_t Chorus::getDepth() const
+{
+ return this->depth_;
+}
diff --git a/src/fx_chorus.h b/src/fx_chorus.h
new file mode 100644
index 00000000..2186e645
--- /dev/null
+++ b/src/fx_chorus.h
@@ -0,0 +1,127 @@
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//
+// fx_chorus.h
+//
+// 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
+
+#include "fx_components.h"
+#include "fx_engine.hpp"
+
+#define CHORUS_FULLSCALE_DEPTH_RATIO 1536.0f
+
+class Chorus : public FXElement
+{
+ DISALLOW_COPY_AND_ASSIGN(Chorus);
+
+public:
+ enum LFOIndex
+ {
+ Sin1 = 0,
+ Sin2,
+ Cos1,
+ Cos2,
+ kLFOCount
+ };
+
+ Chorus(float32_t sampling_rate);
+ virtual ~Chorus();
+
+ virtual void reset() override;
+ virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
+
+ void setDepth(float32_t depth);
+ float32_t getDepth() const;
+
+ void setRate(float32_t rate);
+ float32_t getRate() const;
+
+private:
+ typedef FxEngine<2048, Format::FORMAT_FLOAT32, false> Engine;
+ Engine engine_;
+
+ float32_t rate_; // Normalized frequency for the 2 LFOs frequencies (0.0 - 10.0)
+ float32_t depth_; // Depth of the chorus in milliseconds (0.0 - 10.0)
+ float32_t fullscale_depth_; // Equivalent to depth_ but in the range of (0.0 - 384.0)
+ float32_t feedback_; // Feedback level of the chorus (0.0 - 1.0)
+
+ LFO* lfo_[LFOIndex::kLFOCount];
+
+ IMPLEMENT_DUMP(
+ const size_t space = 16;
+ const size_t precision = 5;
+
+ std::stringstream ss;
+
+ out << "START " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+
+ if(deepInspection)
+ {
+ this->engine_.dump(out, deepInspection, tag + ".engine_");
+ }
+
+ SS_RESET(ss, precision, std::left);
+ SS__TEXT(ss, ' ', space, std::left, '|', "rate_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "depth_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "fullscale_depth_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "feedback_");
+ 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, '+');
+ out << "\t" << ss.str() << std::endl;
+
+ SS_RESET(ss, precision, std::left);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->rate_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->depth_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->fullscale_depth_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->feedback_);
+ out << "\t" << ss.str() << std::endl;
+
+ if(deepInspection)
+ {
+ for(size_t i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ this->lfo_[i]->dump(out, deepInspection, tag + ".lfo_[ " + std::to_string(i) + " ]");
+ }
+ }
+
+ 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_");
+ for(size_t i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ nb_errors += this->lfo_[i]->inspect(inspector, deepInspection, tag + ".lfo_[ " + std::to_string(i) + " ]");
+ }
+ }
+ nb_errors += inspector(tag + ".rate_", this->rate_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".depth_", this->depth_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".fullscale_depth_", this->fullscale_depth_, 0.0f, CHORUS_FULLSCALE_DEPTH_RATIO, deepInspection);
+
+ return nb_errors;
+ )
+};
diff --git a/src/fx_components.cpp b/src/fx_components.cpp
new file mode 100644
index 00000000..04ba3b96
--- /dev/null
+++ b/src/fx_components.cpp
@@ -0,0 +1,825 @@
+#include "fx_components.h"
+
+#include
+
+///////////////////////////////
+// Constants implemlentation //
+///////////////////////////////
+const float32_t Constants::M_PI_POW_2 = PI * PI;
+const float32_t Constants::M_PI_POW_3 = Constants::M_PI_POW_2 * PI;
+const float32_t Constants::M_PI_POW_5 = Constants::M_PI_POW_2 * Constants::M_PI_POW_3;
+const float32_t Constants::M_PI_POW_7 = Constants::M_PI_POW_2 * Constants::M_PI_POW_5;
+const float32_t Constants::M_PI_POW_9 = Constants::M_PI_POW_2 * Constants::M_PI_POW_7;
+const float32_t Constants::M_PI_POW_11 = Constants::M_PI_POW_2 * Constants::M_PI_POW_9;
+
+const float32_t Constants::M2PI = 2.0f * PI;
+const float32_t Constants::MPI_2 = PI / 2.0f;
+const float32_t Constants::MPI_3 = PI / 3.0f;
+const float32_t Constants::MPI_4 = PI / 4.0f;
+const float32_t Constants::M1_PI = 1.0f / PI;
+
+
+/////////////////////////////
+// FastLFO implemlentation //
+/////////////////////////////
+FastLFO::FastLFO(float32_t sampling_rate, float32_t min_frequency, float32_t max_frequency, float32_t initial_phase, bool centered) :
+ FXBase(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),
+ current_(0.0f)
+{
+ assert(this->min_frequency_ <= this->max_frequency_);
+ assert(this->max_frequency_ < sampling_rate / 2.0f);
+
+ this->setFrequency(this->min_frequency_);
+}
+
+FastLFO::~FastLFO()
+{
+}
+
+void FastLFO::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 ? 10 : 100);
+ this->unitary_frequency_ *= static_cast(this->nb_sub_increment_);
+
+ this->updateCoefficient();
+ }
+}
+
+float32_t FastLFO::getNormalizedFrequency() const
+{
+ return this->normalized_frequency_;
+}
+
+void FastLFO::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 ? 10 : (frequency < 0.1f ? 1000 : 100));
+ this->unitary_frequency_ *= static_cast(this->nb_sub_increment_);
+
+ this->updateCoefficient();
+ }
+}
+
+float32_t FastLFO::getFrequency() const
+{
+ return this->frequency_;
+}
+
+void FastLFO::updateCoefficient()
+{
+ this->iir_coefficient_ = 2.0f * cos(Constants::M2PI * this->unitary_frequency_);
+ this->initial_amplitude_ = this->iir_coefficient_ * 0.25f;
+
+ this->reset();
+}
+
+void FastLFO::reset()
+{
+ static const float32_t epsilon = 1e-3;
+
+ 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 = Constants::M2PI * this->unitary_frequency_ / static_cast(this->nb_sub_increment_);
+ float32_t p = Constants::MPI_2;
+ float32_t oldP = 1000.0f;
+ float32_t t_p = this->InitialPhase;
+ const float32_t target = sin(this->InitialPhase);
+ if(t_p < p)
+ {
+ p -= Constants::M2PI;
+ }
+ float32_t tuning = -3.0f;
+ while(p < t_p || abs(tuning - target) > epsilon)
+ {
+ oldP = p;
+ tuning = this->process();
+ p += p_i;
+ if(oldP == p)
+ {
+ return;
+ }
+ }
+}
+
+float32_t FastLFO::process()
+{
+ float32_t temp = this->y0_;
+ float32_t current = temp + 0.5f;
+ if(this->centered_)
+ {
+ current = current * 2.0f - 1.0f;
+ }
+
+ this->sub_increment_++;
+ if(this->sub_increment_ >= this->nb_sub_increment_)
+ {
+ this->sub_increment_ = 0;
+ this->y0_ = this->iir_coefficient_ * this->y0_ - this->y1_;
+ this->y1_ = temp;
+ this->current_ = current;
+ return current;
+ }
+
+ return mapfloat(this->sub_increment_, 0, this->nb_sub_increment_, this->current_, current);
+}
+
+float32_t FastLFO::current() const
+{
+ return this->current_;
+}
+
+
+/////////////////////////////
+// FastLFO2 implemlentation //
+/////////////////////////////
+FastLFO2::FastLFO2(float32_t sampling_rate, float32_t min_frequency, float32_t max_frequency, float32_t initial_phase, bool centered) :
+ FXBase(sampling_rate),
+ InitialPhase(initial_phase),
+ min_frequency_(min_frequency),
+ max_frequency_(max_frequency),
+ centered_(centered),
+ frequency_(0.0f),
+ normalized_frequency_(0.0f),
+ phase_(initial_phase),
+ phase_increment_(0.0f),
+ current_(0.0f)
+{
+ assert(this->min_frequency_ <= this->max_frequency_);
+ assert(this->max_frequency_ < sampling_rate / 2.0f);
+
+ this->setFrequency(this->min_frequency_);
+}
+
+FastLFO2::~FastLFO2()
+{
+}
+
+void FastLFO2::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->phase_increment_ = Constants::M2PI * frequency / this->getSamplingRate();
+ }
+}
+
+float32_t FastLFO2::getNormalizedFrequency() const
+{
+ return this->normalized_frequency_;
+}
+
+void FastLFO2::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->phase_increment_ = Constants::M2PI * frequency / this->getSamplingRate();
+ }
+}
+
+float32_t FastLFO2::getFrequency() const
+{
+ return this->frequency_;
+}
+
+void FastLFO2::reset()
+{
+ this->phase_ = this->InitialPhase;
+}
+
+float32_t FastLFO2::process()
+{
+ static const float32_t K = 5.0f * Constants::M_PI_POW_2;
+
+ float32_t x = this->phase_;
+ float32_t f = 4.0f;
+ if(x > PI)
+ {
+ x -= PI;
+ f = -4.0f;
+ }
+
+ float32_t tmp = 4.0f * x * (PI - x);
+ this->current_ = f * tmp / (K - tmp);
+
+ if(!this->centered_)
+ {
+ this->current_ = this->current_ * 0.5f + 0.5f;
+ }
+
+ this->phase_ += this->phase_increment_;
+ if(this->phase_ > Constants::M2PI)
+ {
+ this->phase_ -= Constants::M2PI;
+ }
+
+ return this->current_;
+}
+
+float32_t FastLFO2::current() const
+{
+ return this->current_;
+}
+
+
+////////////////////////////////////////////////
+// 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(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()
+{
+ static bool initialized = false;
+
+ if(!initialized)
+ {
+ float32_t phase_increment = Constants::M2PI / static_cast(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 initialized;
+}
+
+float32_t InterpolatedSineOscillator::CenteredDataPoints[InterpolatedSineOscillator::DataPointSize + 1];
+float32_t InterpolatedSineOscillator::UpliftDataPoints[InterpolatedSineOscillator::DataPointSize + 1];
+
+const float32_t InterpolatedSineOscillator::DeltaTime = Constants::M2PI / static_cast(InterpolatedSineOscillator::DataPointSize);
+
+InterpolatedSineOscillator::InterpolatedSineOscillator(float32_t sampling_rate, float32_t min_frequency, float32_t max_frequency, float32_t initial_phase, bool centered) :
+ FXBase(sampling_rate),
+ InitialPhase(initial_phase),
+ min_frequency_(min_frequency),
+ max_frequency_(max_frequency),
+ centered_(centered),
+ frequency_(0.0f),
+ normalized_frequency_(-1.0f),
+ phase_index_(initial_phase / InterpolatedSineOscillator::DeltaTime),
+ phase_index_increment_(0.0f),
+ current_sample_(0.0f)
+{
+ static bool initialized = false;
+ if(!initialized)
+ {
+ initialized = InterpolatedSineOscillator::ClassInitializer();
+ }
+
+ assert(this->min_frequency_ <= this->max_frequency_);
+ assert(this->max_frequency_ < sampling_rate / 2.0f);
+
+ this->setFrequency(this->min_frequency_);
+}
+
+InterpolatedSineOscillator::~InterpolatedSineOscillator()
+{
+}
+
+void InterpolatedSineOscillator::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->phase_index_increment_ = static_cast(InterpolatedSineOscillator::DataPointSize) * this->frequency_ / this->getSamplingRate();
+ }
+}
+
+float32_t InterpolatedSineOscillator::getNormalizedFrequency() const
+{
+ return this->normalized_frequency_;
+}
+
+void InterpolatedSineOscillator::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->phase_index_increment_ = static_cast(InterpolatedSineOscillator::DataPointSize) * this->frequency_ / this->getSamplingRate();
+ }
+}
+
+float32_t InterpolatedSineOscillator::getFrequency() const
+{
+ return this->frequency_;
+}
+
+void InterpolatedSineOscillator::reset()
+{
+ this->phase_index_ = this->InitialPhase / InterpolatedSineOscillator::DeltaTime;
+ this->current_sample_ = 0.0f;
+}
+
+float32_t InterpolatedSineOscillator::process()
+{
+ float32_t* dataPoints = this->centered_ ? InterpolatedSineOscillator::CenteredDataPoints : InterpolatedSineOscillator::UpliftDataPoints;
+
+ float32_t out = 0.0f;
+
+ float32_t findex = this->phase_index_;
+ size_t index1 = static_cast(findex);
+ size_t index2 = index1 + 1;
+
+ float32_t f1 = dataPoints[index1];
+ float32_t f2 = dataPoints[index2];
+ float32_t r = findex - index1;
+
+ out = f1 + (f2 - f1) * r * InterpolatedSineOscillator::DeltaTime;
+
+ this->phase_index_ += this->phase_index_increment_;
+ if(this->phase_index_ > InterpolatedSineOscillator::DataPointSize)
+ {
+ this->phase_index_ -= InterpolatedSineOscillator::DataPointSize;
+ }
+
+ return this->current_sample_ = out;
+}
+
+float32_t InterpolatedSineOscillator::current() const
+{
+ return this->current_sample_;
+}
+
+
+////////////////////////////////
+// ComplexLFO implemlentation //
+////////////////////////////////
+ComplexLFO::ComplexLFO(float32_t sampling_rate, float32_t min_frequency, float32_t max_frequency, float32_t initial_phase, bool centered) :
+ FXBase(sampling_rate),
+ InitialPhase(initial_phase),
+ min_frequency_(min_frequency),
+ max_frequency_(max_frequency),
+ centered_(centered),
+ normalized_frequency_(-1.0f),
+ frequency_(0.0f),
+ phase_(initial_phase),
+ phase_increment_(0.0f),
+ current_sample_(0.0f),
+ new_phase_(true),
+ rnd_generator_(rnd_device_()),
+ rnd_distribution_(-1.0f, 1.0f)
+{
+ assert(this->min_frequency_ <= this->max_frequency_);
+ assert(this->max_frequency_ < sampling_rate / 2.0f);
+
+ this->setWaveform(Waveform::Sine);
+ this->setFrequency(this->min_frequency_);
+}
+
+ComplexLFO::~ComplexLFO()
+{
+}
+
+void ComplexLFO::setWaveform(Waveform waveform)
+{
+ this->waveform_ = waveform;
+}
+
+ComplexLFO::Waveform ComplexLFO::getWaveform() const
+{
+ return this->waveform_;
+}
+
+void ComplexLFO::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->phase_increment_ = Constants::M2PI * this->frequency_ / this->getSamplingRate();
+ }
+}
+
+float32_t ComplexLFO::getNormalizedFrequency() const
+{
+ return this->normalized_frequency_;
+}
+
+void ComplexLFO::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->phase_increment_ = Constants::M2PI * this->frequency_ / this->getSamplingRate();
+ }
+}
+
+float32_t ComplexLFO::getFrequency() const
+{
+ return this->frequency_;
+}
+
+void ComplexLFO::reset()
+{
+ this->phase_ = this->InitialPhase;
+ this->current_sample_ = 0.0f;
+}
+
+float32_t ComplexLFO::process()
+{
+ float32_t out = 0.0f;
+ switch(this->waveform_)
+ {
+ case Waveform::Sine:
+ out = arm_sin_f32(this->phase_);
+ break;
+ case Waveform::Saw:
+ out = Constants::M1_PI * this->phase_ - 1.0f;
+ break;
+ case Waveform::Square:
+ out = this->phase_ < PI ? 1.0 : -1.0;
+ break;
+ case Waveform::SH:
+ if(this->new_phase_)
+ {
+ out = this->rnd_distribution_(this->rnd_generator_);
+ }
+ else
+ {
+ out = this->current_sample_;
+ }
+ break;
+ case Waveform::Noise:
+ out = this->rnd_distribution_(this->rnd_generator_);
+ break;
+ }
+
+ if(!this->centered_)
+ {
+ out = out * 0.5f + 0.5f;
+ }
+
+ this->current_sample_ = out;
+
+ this->phase_ += this->phase_increment_;
+ if(this->phase_ >= Constants::M2PI)
+ {
+ this->phase_ -= Constants::M2PI;
+ this->new_phase_ = true;
+ }
+ else
+ {
+ this->new_phase_ = false;
+ }
+
+ return out;
+}
+
+float32_t ComplexLFO::current() const
+{
+ return this->current_sample_;
+}
+
+
+////////////////////////////////////
+// JitterGenerator implementation //
+////////////////////////////////////
+JitterGenerator::JitterGenerator(float32_t sampling_rate) :
+ FXBase(sampling_rate),
+ rnd_generator_(rnd_device_()),
+ rnd_distribution_(-1.0f, 1.0f),
+ speed_(-1.0f),
+ magnitude_(-1.0f),
+ phase_(0.0f),
+ phase_increment_(0.0f)
+{
+ this->setSpeed(1.0f);
+ this->setMagnitude(0.1f);
+}
+
+JitterGenerator::~JitterGenerator()
+{
+}
+
+void JitterGenerator::setSpeed(float32_t speed)
+{
+ static const float32_t max_frequency = 0.45f * this->getSamplingRate();
+
+ speed = constrain(speed, 0.0f, max_frequency);
+ if(this->speed_ != speed)
+ {
+ this->speed_ = speed;
+ this->phase_increment_ = Constants::M2PI * this->speed_ / this->getSamplingRate();
+ }
+}
+
+float32_t JitterGenerator::getSpeed() const
+{
+ return this->speed_;
+}
+
+void JitterGenerator::setMagnitude(float32_t magnitude)
+{
+ this->magnitude_ = constrain(magnitude, 0.0f, 1.0f);
+}
+
+float32_t JitterGenerator::getMagnitude() const
+{
+ return this->magnitude_;
+}
+
+void JitterGenerator::reset()
+{
+ this->phase_ = 0.0f;
+}
+
+float32_t JitterGenerator::process()
+{
+ 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)
+ {
+ this->phase_ -= Constants::M2PI;
+ }
+
+ return out;
+}
+
+
+//////////////////////////////////////////
+// PerlinNoiseGenerator implemlentation //
+//////////////////////////////////////////
+#define MAX_FREQUENCY_PERLIN_NOISE_GENERATOR 0.5f
+
+const float32_t PerlinNoiseGenerator::Gradients[] =
+{
+ -1.0f, +1.0f,
+ -1.0f, -1.0f,
+ +1.0f, -1.0f,
+ +1.0f, +1.0f
+};
+
+PerlinNoiseGenerator::PerlinNoiseGenerator(float32_t sampling_rate, float32_t rate) :
+ FXBase(sampling_rate),
+ rate_(0.0f),
+ phase_(0.0f),
+ phase_increment_(0.0f),
+ current_(0.0f)
+{
+ this->setRate(rate);
+
+ this->reset();
+}
+
+PerlinNoiseGenerator::~PerlinNoiseGenerator()
+{
+}
+
+void PerlinNoiseGenerator::setRate(float32_t rate)
+{
+ rate = constrain(rate, 0.0f, 1.0f);
+ if(rate != this->rate_)
+ {
+ this->rate_ = rate;
+ this->phase_increment_ = Constants::M2PI * rate / this->getSamplingRate();
+ }
+}
+
+float32_t PerlinNoiseGenerator::getRate() const
+{
+ return this->rate_;
+}
+
+float32_t PerlinNoiseGenerator::current() const
+{
+ return this->current_;
+}
+
+void PerlinNoiseGenerator::reset()
+{
+ this->phase_ = 0.0f;
+ this->current_ = 0.0f;
+}
+
+float32_t PerlinNoiseGenerator::process()
+{
+ if(this->rate_ == 0.0f)
+ {
+ return this->current_ = 0.0f;
+ }
+
+ this->current_ = PerlinNoiseGenerator::perlin(this->phase_);
+ this->phase_ += this->phase_increment_;
+ if(this->phase_ >= Constants::M2PI)
+ {
+ this->phase_ -= Constants::M2PI;
+ }
+
+ return this->current_;
+}
+
+int PerlinNoiseGenerator::hash(int x)
+{
+ x = ((x << 13) ^ x);
+ return (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff;
+}
+
+float32_t PerlinNoiseGenerator::interpolate(float32_t a, float32_t b, float32_t x)
+{
+ float32_t ft = x * PI;
+ float32_t f = (1.0f - arm_cos_f32(ft)) * 0.5;
+ return a * (1.0f - f) + b * f;
+}
+
+float32_t PerlinNoiseGenerator::perlin(float32_t x)
+{
+ // Find the unit square that contains x
+ int squareX = (int)x;
+
+ // Find the relative x of x within that square
+ double relX = x - squareX;
+
+ // Calculate the hashes for the square's four corners
+ int h1 = PerlinNoiseGenerator::hash(squareX);
+ int h2 = PerlinNoiseGenerator::hash(squareX + 1);
+
+ // Calculate the gradients for each corner
+ double grad1 = PerlinNoiseGenerator::Gradients[h1 & 3];
+ double grad2 = PerlinNoiseGenerator::Gradients[h2 & 3];
+
+ // Calculate the dot products between the gradient vectors and the distance vectors
+ double dot1 = grad1 * relX;
+ double dot2 = grad2 * (relX - 1);
+
+ // Interpolate the dot products and return the final noise value
+ return PerlinNoiseGenerator::interpolate(dot1, dot2, relX);
+}
+
+//////////////////////////////////
+// softSaturate implemlentation //
+//////////////////////////////////
+float32_t softSaturator1(float32_t in, float32_t threshold)
+{
+ float32_t x = std::abs(in);
+ float32_t y = 0.0f;
+ if(x < threshold)
+ {
+ y = x;
+ }
+ else if(x > threshold)
+ {
+ y = threshold + (x - threshold) / (1.0f + std::pow((x - threshold) / (1.0f - threshold), 2.0f));
+ }
+ else if(x > 1.0f)
+ {
+ y = (threshold + 1.0f) / 2.0f;
+ }
+
+ float32_t g = 2.0f / (1.0f + threshold);
+ y *= g;
+
+ return (in < 0.0f) ? -y : y;
+}
+
+float32_t softSaturator2(float32_t input, float32_t saturation)
+{
+ const static float kTubeCurve = 4.0f;
+ const static float kTubeBias = 0.5f;
+
+ float absInput = std::abs(input);
+ float output = 0.0f;
+ if(absInput > kTubeBias)
+ {
+ output = (kTubeCurve + saturation) * (absInput - kTubeBias) / (1.0f - kTubeBias);
+ }
+ else
+ {
+ output = (kTubeCurve + saturation) * absInput / (1.0f + kTubeCurve * absInput);
+ }
+
+ // Clip the output if overdrive is set to 1
+ // output = std::min(1.0f, output);
+ if(output > 1.0f)
+ {
+ output = 1.0f;
+ }
+ else
+ {
+ output -= output * output * output / 3.0f;
+ }
+
+ if(input < 0.0f)
+ {
+ output = -output;
+ }
+
+ return output;
+}
+
+float32_t softSaturator3(float32_t input, float32_t overdrive)
+{
+ const float32_t w = (1.0f + overdrive) * Constants::MPI_4;
+ return constrain(std::tan(w * input), -1.0f, 1.0f);
+}
+
+float32_t softSaturator4(float32_t input, float32_t saturator_factor)
+{
+ float32_t x = input * saturator_factor;
+ float32_t abs_x = std::abs(x);
+ float32_t sat_x = std::log(1.0f + abs_x) / std::log(1.0f + saturator_factor);
+ return x > 0 ? sat_x : -sat_x;
+}
+
+float32_t waveFolder(float32_t input, float32_t bias)
+{
+ bias = 0.5 + (2.0f - bias) / 4.0f;
+ float32_t output = std::abs(input) / bias;
+
+ if(output > 1.0f)
+ {
+ output = 2.0f - output;
+ }
+
+ if(input < 0.0f)
+ {
+ output = -output;
+ }
+
+ return output;
+}
\ No newline at end of file
diff --git a/src/fx_components.h b/src/fx_components.h
new file mode 100644
index 00000000..45276e1a
--- /dev/null
+++ b/src/fx_components.h
@@ -0,0 +1,568 @@
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//
+// fx_components.h
+//
+// Several tools and components used in the implemlentation of FX
+// Quthor: Vincent Gauché
+//
+#pragma once
+
+#include "fx.h"
+
+#include
+#include
+#include
+
+#define LFO_MIN_FREQUENCY 0.01f
+#define LFO_MAX_FREQUENCY 10.0f
+
+struct Constants
+{
+ const static float32_t M_PI_POW_2; // PI^2
+ const static float32_t M_PI_POW_3; // PI^3
+ const static float32_t M_PI_POW_5; // PI^5
+ const static float32_t M_PI_POW_7; // PI^7
+ const static float32_t M_PI_POW_9; // PI^9
+ const static float32_t M_PI_POW_11; // PI^11
+
+ const static float32_t M2PI; // 2 * PI
+ const static float32_t MPI_2; // PI / 2
+ const static float32_t MPI_3; // PI / 3
+ const static float32_t MPI_4; // PI / 4
+ const static float32_t M1_PI; // 1 / PI
+};
+
+
+class FastLFO : public FXBase
+{
+ DISALLOW_COPY_AND_ASSIGN(FastLFO);
+
+public:
+ FastLFO(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 ~FastLFO();
+
+ void setNormalizedFrequency(float32_t normalized_frequency);
+ float32_t getNormalizedFrequency() const;
+
+ void setFrequency(float32_t frequency);
+ float32_t getFrequency() const;
+
+ virtual void reset() override;
+ float32_t process();
+ float32_t current() const;
+
+private:
+ void updateCoefficient();
+
+ 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_;
+
+ IMPLEMENT_DUMP(
+ const size_t space = 21;
+ const size_t precision = 5;
+
+ 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, '|', "InitialPhase");
+ SS__TEXT(ss, ' ', space, std::left, '|', "frequency_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "normalized_frequency_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "unitary_frequency_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "y0_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "y1_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "iir_coefficient_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "initial_amplitude_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "current_");
+ 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, '+');
+ 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->InitialPhase);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->frequency_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->normalized_frequency_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->unitary_frequency_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->y0_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->y1_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->iir_coefficient_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->initial_amplitude_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->current_);
+
+ out << "\t" << ss.str() << std::endl;
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0u;
+
+ nb_errors += inspector(tag + ".InitialPhase", this->InitialPhase, 0.0f, Constants::M2PI, deepInspection);
+ nb_errors += inspector(tag + ".frequency_", this->frequency_, this->min_frequency_, this->max_frequency_, deepInspection);
+ nb_errors += inspector(tag + ".normalized_frequency_", this->normalized_frequency_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".unitary_frequency_", this->unitary_frequency_, this->min_frequency_ / this->getSamplingRate(), this->max_frequency_ / this->getSamplingRate(), deepInspection);
+ nb_errors += inspector(tag + ".current_", this->current_, -1.0f, 1.0f, deepInspection);
+
+ return nb_errors;
+ )
+};
+
+
+class FastLFO2 : public FXBase
+{
+ DISALLOW_COPY_AND_ASSIGN(FastLFO2);
+
+public:
+ FastLFO2(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 ~FastLFO2();
+
+ void setNormalizedFrequency(float32_t normalized_frequency);
+ float32_t getNormalizedFrequency() const;
+
+ void setFrequency(float32_t frequency);
+ float32_t getFrequency() const;
+
+ virtual void reset() override;
+ float32_t process();
+ float32_t current() const;
+
+private:
+ 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 phase_;
+ float32_t phase_increment_;
+ float32_t current_;
+
+ IMPLEMENT_DUMP(
+ const size_t space = 21;
+ const size_t precision = 5;
+
+ 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, '|', "InitialPhase");
+ SS__TEXT(ss, ' ', space, std::left, '|', "frequency_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "normalized_frequency_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "phase_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "phase_increment_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "current_");
+ 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->InitialPhase);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->frequency_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->normalized_frequency_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->phase_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->phase_increment_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->current_);
+
+ out << "\t" << ss.str() << std::endl;
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0u;
+
+ nb_errors += inspector(tag + ".InitialPhase", this->InitialPhase, 0.0f, Constants::M2PI, deepInspection);
+ nb_errors += inspector(tag + ".frequency_", this->frequency_, this->min_frequency_, this->max_frequency_, deepInspection);
+ nb_errors += inspector(tag + ".normalized_frequency_", this->normalized_frequency_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".phase_", this->phase_, 0.0f, Constants::M2PI, deepInspection);
+ nb_errors += inspector(tag + ".phase_", this->phase_increment_, 0.0f, Constants::M2PI, deepInspection);
+ nb_errors += inspector(tag + ".current_", this->current_, -1.0f, 1.0f, deepInspection);
+
+ return nb_errors;
+ )
+};
+
+
+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();
+
+ void setNormalizedFrequency(float32_t normalized_frequency);
+ float32_t getNormalizedFrequency() const;
+
+ void setFrequency(float32_t frequency);
+ float32_t getFrequency() const;
+
+ virtual void reset() override;
+ float32_t process();
+ float32_t current() const;
+
+private:
+ static bool ClassInitializer();
+ static const size_t DataPointSize = 176400;
+ static const float32_t DeltaTime;
+ static float32_t CenteredDataPoints[];
+ static float32_t UpliftDataPoints[];
+
+ 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 phase_index_;
+ float32_t phase_index_increment_;
+ float32_t current_sample_;
+
+ IMPLEMENT_DUMP(
+ const size_t space = 22;
+ const size_t precision = 5;
+
+ 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, '|', "InitialPhase");
+ SS__TEXT(ss, ' ', space, std::left, '|', "normalized_frequency_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "frequency_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "phase_index_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "phase_index_increment_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "current_sample_");
+ 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->InitialPhase);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->normalized_frequency_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->frequency_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->phase_index_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->phase_index_increment_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->current_sample_);
+ out << "\t" << ss.str() << std::endl;
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0u;
+
+ nb_errors += inspector(tag + ".InitialPhase", this->InitialPhase, 0.0f, Constants::M2PI, deepInspection);
+ nb_errors += inspector(tag + ".normalized_frequency_", this->normalized_frequency_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".frequency_", this->frequency_, this->min_frequency_, this->max_frequency_, deepInspection);
+ nb_errors += inspector(tag + ".phase_index_", this->phase_index_, 0.0f, static_cast(InterpolatedSineOscillator::DataPointSize), deepInspection);
+ nb_errors += inspector(tag + ".current_sample_", this->current_sample_, -1.0f, 1.0f, deepInspection);
+
+ return nb_errors;
+ )
+};
+
+class ComplexLFO : public FXBase
+{
+ DISALLOW_COPY_AND_ASSIGN(ComplexLFO);
+
+public:
+ typedef enum {
+ Sine,
+ Saw,
+ Square,
+ SH,
+ Noise
+ } Waveform;
+
+ ComplexLFO(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 ~ComplexLFO();
+
+ void setWaveform(Waveform waveform);
+ Waveform getWaveform() const;
+
+ void setNormalizedFrequency(float32_t normalized_frequency);
+ float32_t getNormalizedFrequency() const;
+
+ void setFrequency(float32_t frequency);
+ float32_t getFrequency() const;
+
+ virtual void reset() override;
+ float32_t process();
+ float32_t current() const;
+
+private:
+ const float32_t InitialPhase;
+ const float32_t min_frequency_;
+ const float32_t max_frequency_;
+ const bool centered_;
+ Waveform waveform_;
+ float32_t normalized_frequency_;
+ float32_t frequency_;
+ float32_t phase_;
+ float32_t phase_increment_;
+ float32_t current_sample_;
+ bool new_phase_;
+ std::random_device rnd_device_;
+ std::mt19937 rnd_generator_;
+ std::uniform_real_distribution rnd_distribution_;
+
+ IMPLEMENT_DUMP(
+ const size_t space = 21;
+ const size_t precision = 5;
+
+ 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, '|', "InitialPhase");
+ SS__TEXT(ss, ' ', space, std::left, '|', "normalized_frequency_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "frequency_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "phase_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "phase_increment_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "current_sample_");
+ 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->InitialPhase);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->normalized_frequency_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->frequency_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->phase_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->phase_increment_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->current_sample_);
+ out << "\t" << ss.str() << std::endl;
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0u;
+
+ nb_errors += inspector(tag + ".InitialPhase", this->InitialPhase, 0.0f, Constants::M2PI, deepInspection);
+ nb_errors += inspector(tag + ".normalized_frequency_", this->normalized_frequency_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".frequency_", this->frequency_, this->min_frequency_, this->max_frequency_, deepInspection);
+ nb_errors += inspector(tag + ".phase_", this->phase_, 0.0f, Constants::M2PI, deepInspection);
+ nb_errors += inspector(tag + ".phase_increment_", this->phase_increment_, 0.0f, Constants::M2PI, deepInspection);
+ nb_errors += inspector(tag + ".current_sample_", this->current_sample_, -1.0f, 1.0f, deepInspection);
+
+ return nb_errors;
+ )
+};
+
+
+typedef FastLFO2 LFO;
+
+
+class JitterGenerator : public FXBase
+{
+ DISALLOW_COPY_AND_ASSIGN(JitterGenerator);
+
+public:
+ JitterGenerator(float32_t sampling_rate);
+ virtual ~JitterGenerator();
+
+ void setSpeed(float32_t speed);
+ float32_t getSpeed() const;
+
+ void setMagnitude(float32_t magnitude);
+ float32_t getMagnitude() const;
+
+ virtual void reset() override;
+ float32_t process();
+
+private:
+ std::random_device rnd_device_;
+ std::mt19937 rnd_generator_;
+ std::uniform_real_distribution rnd_distribution_;
+ float32_t speed_;
+ float32_t magnitude_;
+ float32_t phase_;
+ float32_t phase_increment_;
+
+ IMPLEMENT_DUMP(
+ const size_t space = 16;
+ const size_t precision = 5;
+
+ 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, '|', "speed_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "magnitude_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "phase_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "phase_increment_");
+ 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, '+');
+ out << "\t" << ss.str() << std::endl;
+
+ SS_RESET(ss, precision, std::left);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->speed_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->magnitude_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->phase_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->phase_increment_);
+ out << "\t" << ss.str() << std::endl;
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0u;
+
+ nb_errors += inspector(tag + ".speed_", this->speed_, 0.0f, 0.45f * this->getSamplingRate(), deepInspection);
+ nb_errors += inspector(tag + ".magnitude_", this->magnitude_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".phase_", this->phase_, 0.0f, Constants::M2PI, deepInspection);
+ nb_errors += inspector(tag + ".phase_increment_", this->phase_increment_, 0.0f, 0.45f * Constants::M2PI, deepInspection);
+
+ return nb_errors;
+ )
+};
+
+
+class PerlinNoiseGenerator : public FXBase
+{
+ DISALLOW_COPY_AND_ASSIGN(PerlinNoiseGenerator);
+
+public:
+ PerlinNoiseGenerator(float32_t sampling_rate, float32_t rate = 0.2f);
+ virtual ~PerlinNoiseGenerator();
+
+ void setRate(float32_t rate);
+ float32_t getRate() const;
+
+ float32_t current() const;
+
+ virtual void reset() override;
+ float32_t process();
+
+private:
+ static int hash(int x);
+ static float32_t interpolate(float32_t a, float32_t b, float32_t x);
+ static float32_t perlin(float32_t x);
+
+ float32_t rate_;
+ float32_t phase_;
+ float32_t phase_increment_;
+ float32_t current_;
+
+ static const float32_t Gradients[];
+
+ IMPLEMENT_DUMP(
+ const size_t space = 16;
+ const size_t precision = 5;
+
+ 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, '|', "rate_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "phase_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "phase_increment_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "current_");
+ 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, '+');
+ out << "\t" << ss.str() << std::endl;
+
+ SS_RESET(ss, precision, std::left);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->rate_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->phase_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->phase_increment_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->current_);
+ out << "\t" << ss.str() << std::endl;
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0u;
+
+ nb_errors += inspector(tag + ".rate_", this->rate_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".phase_", this->phase_, 0.0f, Constants::M2PI, deepInspection);
+ nb_errors += inspector(tag + ".phase_increment_", this->phase_increment_, 0.0f, Constants::M2PI / this->getSamplingRate(), deepInspection);
+ nb_errors += inspector(tag + ".current_", this->current_, -1.0f, 1.0f, deepInspection);
+
+ return nb_errors;
+ )
+};
+
+float32_t softSaturator1(float32_t in, float32_t threshold);
+float32_t softSaturator2(float32_t in, float32_t saturation);
+float32_t softSaturator3(float32_t in, float32_t saturation);
+float32_t softSaturator4(float32_t in, float32_t saturation);
+
+float32_t waveFolder(float32_t input, float32_t bias);
diff --git a/src/fx_delay.cpp b/src/fx_delay.cpp
new file mode 100644
index 00000000..6ddaacba
--- /dev/null
+++ b/src/fx_delay.cpp
@@ -0,0 +1,157 @@
+#include "fx_delay.h"
+
+#include
+
+#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
+
+Delay::LowHighPassFilter::LowHighPassFilter(float32_t sampling_rate) :
+ FXElement(sampling_rate),
+ lpf_(sampling_rate, StateVariableFilter::FilterMode::LPF, LPF_CUTOFF_REF),
+ hpf_(sampling_rate, StateVariableFilter::FilterMode::HPF, HPF_CUTOFF_REF),
+ ratio_(1.0f)
+{
+ this->setCutoffChangeRatio(0.0f);
+ this->lpf_.setGainDB(0.82f);
+ this->hpf_.setGainDB(0.82f);
+}
+
+Delay::LowHighPassFilter::~LowHighPassFilter()
+{
+}
+
+void Delay::LowHighPassFilter::setCutoffChangeRatio(float32_t ratio)
+{
+ static const float32_t weight = 4.0f;
+
+ ratio = constrain(ratio, -1.0f, 1.0f);
+ if(ratio != this->ratio_)
+ {
+ this->ratio_ = ratio;
+ ratio /= 10.0f;
+ this->lpf_.setCutoff(LPF_CUTOFF_REF * (1.0f - ratio / weight));
+ this->hpf_.setCutoff(HPF_CUTOFF_REF * (1.0f + ratio * weight));
+ }
+}
+
+void Delay::LowHighPassFilter::reset()
+{
+ this->lpf_.reset();
+ this->hpf_.reset();
+}
+
+void Delay::LowHighPassFilter::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
+{
+ this->lpf_.processSample(inL, inR, outL, outR);
+ this->hpf_.processSample(outL, outR, outL, outR);
+}
+
+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),
+ 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];
+
+ this->setLeftDelayTime(default_delay_time);
+ this->setRightDelayTime(default_delay_time);
+ this->setFeedback(default_feedback_level);
+
+ this->reset();
+}
+
+Delay::~Delay()
+{
+ delete[] this->buffer_L_;
+ delete[] this->buffer_R_;
+}
+
+void Delay::reset()
+{
+ memset(this->buffer_L_, 0, this->MaxSampleDelayTime * sizeof(float32_t));
+ memset(this->buffer_R_, 0, this->MaxSampleDelayTime * sizeof(float32_t));
+ this->write_pos_L_ = 0;
+ this->write_pos_R_ = 0;
+ this->filter_.reset();
+}
+
+void Delay::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
+{
+ // 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 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(this->MaxSampleDelayTime + signL * this->write_pos_L_ - delay_time_L) % this->MaxSampleDelayTime;
+ unsigned read_pos_R = static_cast(this->MaxSampleDelayTime + signR * this->write_pos_R_ - delay_time_R) % this->MaxSampleDelayTime;
+
+
+ // Read from delay buffers and apply feedback
+ this->filter_.processSample(
+ this->buffer_L_[read_pos_L],
+ this->buffer_R_[read_pos_R],
+ outL,
+ outR
+ );
+
+ this->buffer_L_[this->write_pos_L_] += outL * this->getFeedback();
+ this->buffer_R_[this->write_pos_R_] += outR * this->getFeedback();
+
+ // Increment read positions
+ ++ this->write_pos_L_;
+ if(this->write_pos_L_ >= this->MaxSampleDelayTime)
+ {
+ this->write_pos_L_ -= this->MaxSampleDelayTime;
+ }
+ ++ this->write_pos_R_;
+ if(this->write_pos_R_ >= this->MaxSampleDelayTime)
+ {
+ this->write_pos_R_ -= this->MaxSampleDelayTime;
+ }
+
+ outL *= this->OutputLevelCorrector;
+ outR *= this->OutputLevelCorrector;
+}
+
+void Delay::setLeftDelayTime(float32_t delay_time)
+{
+ this->delay_time_L_ = constrain(delay_time, -1.0f, 1.0f);
+}
+
+float32_t Delay::getLeftDelayTime() const
+{
+ return this->delay_time_L_;
+}
+
+void Delay::setRightDelayTime(float32_t delay_time)
+{
+ this->delay_time_R_ = constrain(delay_time, -1.0f, 1.0f);
+}
+
+float32_t Delay::getRightDelayTime() const
+{
+ return this->delay_time_R_;
+}
+
+void Delay::setFeedback(float32_t feedback)
+{
+ this->feedback_ = constrain(feedback, 0.0, 1.0);
+}
+
+float32_t Delay::getFeedback() const
+{
+ return this->feedback_;
+}
diff --git a/src/fx_delay.h b/src/fx_delay.h
new file mode 100644
index 00000000..30e063ea
--- /dev/null
+++ b/src/fx_delay.h
@@ -0,0 +1,204 @@
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+//
+// fx_tape_delay.h
+//
+// Stereo Delay proposed in the context of the MiniDexed project
+// Author: Vincent Gauché
+//
+#pragma once
+
+#include "fx_components.h"
+#include "fx_svf.h"
+
+class Delay : public FXElement
+{
+ DISALLOW_COPY_AND_ASSIGN(Delay);
+
+ class LowHighPassFilter : public FXElement
+ {
+ DISALLOW_COPY_AND_ASSIGN(LowHighPassFilter);
+
+ public:
+ LowHighPassFilter(float32_t sampling_rate);
+ virtual ~LowHighPassFilter();
+
+ virtual void reset() override;
+ virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
+
+ void setCutoffChangeRatio(float32_t ratio);
+
+ private:
+ StateVariableFilter lpf_;
+ StateVariableFilter hpf_;
+ float32_t ratio_;
+
+ IMPLEMENT_DUMP(
+ const size_t space = 10;
+ const size_t precision = 5;
+
+ 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, '|', "ratio_");
+ out << "\t" << ss.str() << std::endl;
+
+ SS_RESET(ss, precision, 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->ratio_);
+ out << "\t" << ss.str() << std::endl;
+
+ if(deepInspection)
+ {
+ out << "\t" << std::endl;
+ this->lpf_.dump(out, deepInspection, tag + ".lpf_");
+ this->hpf_.dump(out, deepInspection, tag + ".hpf_");
+ }
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0u;
+
+ if(deepInspection)
+ {
+ nb_errors += this->lpf_.inspect(inspector, deepInspection, tag + ".lpf_");
+ nb_errors += this->hpf_.inspect(inspector, deepInspection, tag + ".hpf_");
+ }
+ nb_errors += inspector(tag + ".ratio_", this->ratio_, -1.0f, 1.0f, deepInspection);
+
+ return nb_errors;
+ )
+ };
+
+public:
+ Delay(const float32_t sampling_rate, float32_t default_delay_time = 0.25f, float32_t default_flutter_level = 1.0f, float32_t default_wet_level = 0.5f);
+ virtual ~Delay();
+
+ virtual void reset() override;
+ virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
+
+ void setLeftDelayTime(float32_t delay_time);
+ float32_t getLeftDelayTime() const;
+
+ void setRightDelayTime(float32_t delay_time);
+ float32_t getRightDelayTime() const;
+
+ void setFeedback(float32_t feedback);
+ float32_t getFeedback() const;
+
+private:
+ const size_t MaxSampleDelayTime;
+ 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)
+
+ LowHighPassFilter filter_;
+
+ IMPLEMENT_DUMP(
+ const size_t space = 18;
+ const size_t precision = 5;
+
+ 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, '|', "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_");
+ 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;
+
+ SS_RESET(ss, precision, std::left);
+ 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_);
+ out << "\t" << ss.str() << std::endl;
+
+ if(deepInspection)
+ {
+ out << "Flanger internal delay lines:" << std::endl;
+
+ SS_RESET(ss, precision, std::left);
+ SS__TEXT(ss, ' ', space, std::left, '|', "index");
+ SS__TEXT(ss, ' ', space, std::left, '|', "buffer_L_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "buffer_R_");
+ 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, '+');
+ out << "\t" << ss.str() << std::endl;
+
+ for(size_t i = 0; i < this->MaxSampleDelayTime; ++i)
+ {
+ SS_RESET(ss, precision, std::left);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", i);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->buffer_L_[i]);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->buffer_R_[i]);
+ out << "\t" << ss.str() << std::endl;
+ }
+
+ this->filter_.dump(out, deepInspection, tag + ".filter_");
+ }
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0;
+ nb_errors += inspector(tag + ".write_pos_L_", static_cast(this->write_pos_L_), 0.0f, static_cast(this->MaxSampleDelayTime), deepInspection);
+ nb_errors += inspector(tag + ".write_pos_R_", static_cast(this->write_pos_R_), 0.0f, static_cast(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);
+
+ if(deepInspection)
+ {
+ for(size_t i = 0; i < this->MaxSampleDelayTime; ++i)
+ {
+ nb_errors += inspector(tag + ".buffer_L_[ " + std::to_string(i) + " ]", this->buffer_L_[i], -1.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".buffer_R_[ " + std::to_string(i) + " ]", this->buffer_R_[i], -1.0f, 1.0f, deepInspection);
+ }
+
+ nb_errors += this->filter_.inspect(inspector, deepInspection, tag + ".filter_");
+ }
+
+ return nb_errors;
+ )
+};
diff --git a/src/fx_diffuser.cpp b/src/fx_diffuser.cpp
new file mode 100644
index 00000000..936f2d3c
--- /dev/null
+++ b/src/fx_diffuser.cpp
@@ -0,0 +1,71 @@
+#include "fx_diffuser.h"
+
+#include
+#include
+
+#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 apl1;
+ Engine::DelayLine apl2;
+ Engine::DelayLine apl3;
+ Engine::DelayLine apl4;
+ Engine::DelayLine apr1;
+ Engine::DelayLine apr2;
+ Engine::DelayLine apr3;
+ Engine::DelayLine 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;
+}
diff --git a/src/fx_diffuser.h b/src/fx_diffuser.h
new file mode 100644
index 00000000..2fe1d48d
--- /dev/null
+++ b/src/fx_diffuser.h
@@ -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 .
+//
+
+//
+// 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 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;
+ )
+};
diff --git a/src/fx_dry.cpp b/src/fx_dry.cpp
new file mode 100644
index 00000000..a4c29314
--- /dev/null
+++ b/src/fx_dry.cpp
@@ -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;
+}
diff --git a/src/fx_dry.h b/src/fx_dry.h
new file mode 100644
index 00000000..52cd5526
--- /dev/null
+++ b/src/fx_dry.h
@@ -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 .
+
+//
+// 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;
+ )
+};
\ No newline at end of file
diff --git a/src/fx_engine.hpp b/src/fx_engine.hpp
new file mode 100644
index 00000000..239969f7
--- /dev/null
+++ b/src/fx_engine.hpp
@@ -0,0 +1,515 @@
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//
+// fx_engine.h
+//
+// FX Engine used in some of the Mutable Instruments Eurorack modules.
+// This version is ported for the MiniDexed project and brings some additional optimization to avoid unecessary multiplications.
+// Ported by: Vincent Gauché
+//
+
+#pragma once
+
+#include
+#include
+#include
+
+#include "fx_components.h"
+
+enum Format
+{
+ FORMAT_12_BIT,
+ FORMAT_16_BIT,
+ FORMAT_32_BIT,
+ FORMAT_FLOAT32
+};
+
+template
+struct DataType
+{
+};
+
+inline int16_t clip16(int32_t x)
+{
+ if(x > INT16_MAX)
+ {
+ return INT16_MAX;
+ }
+
+ if(x < INT16_MIN)
+ {
+ return INT16_MIN;
+ }
+
+ return static_cast(x);
+}
+
+template <>
+struct DataType
+{
+ typedef uint16_t T;
+
+ static inline float32_t decompress(T value)
+ {
+ return static_cast(static_cast(value)) / 4096.0f;
+ }
+
+ static inline T compress(float32_t value)
+ {
+ return clip16(static_cast(value * 4096.0f));
+ }
+};
+
+template <>
+struct DataType
+{
+ typedef uint32_t T;
+
+ static inline float32_t decompress(T value)
+ {
+ return static_cast(static_cast(value)) / 65536.0f;
+ }
+
+ static inline T compress(float32_t value)
+ {
+ return clip16(static_cast(value * 65536.0f));
+ }
+};
+
+template <>
+struct DataType
+{
+ typedef uint32_t T;
+
+ static inline float32_t decompress(T value)
+ {
+ return static_cast(static_cast(value)) / static_cast(UINT32_MAX);
+ }
+
+ static inline T compress(float32_t value)
+ {
+ return value * static_cast(INT32_MAX);
+ }
+};
+
+template <>
+struct DataType
+{
+ typedef float32_t T;
+
+ static inline float32_t decompress(T value)
+ {
+ return value;
+ }
+
+ static inline T compress(float32_t value)
+ {
+ return constrain(value, -1.0f, 1.0f);
+ }
+};
+
+template <
+ size_t size,
+ Format format,
+ bool enable_lfo = true>
+class FxEngine : public FXBase
+{
+ DISALLOW_COPY_AND_ASSIGN(FxEngine);
+
+public:
+ typedef typename DataType::T T;
+
+ enum LFOIndex
+ {
+ LFO_1 = 0,
+ LFO_2,
+ kLFOCount
+ };
+
+ FxEngine(float32_t sampling_rate, float32_t max_lfo_frequency = 20.0f) :
+ FXBase(sampling_rate),
+ write_ptr_(0)
+ {
+ this->buffer_ = new T[size];
+ for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i) this->lfo_[i] = enable_lfo ? new LFO(sampling_rate, 0.0f, max_lfo_frequency, 0.0f, false) : nullptr;
+ this->clear();
+ }
+
+ ~FxEngine()
+ {
+ delete[] this->buffer_;
+ if(enable_lfo)
+ {
+ for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i) delete this->lfo_[i];
+ }
+ }
+
+ void clear()
+ {
+ memset(this->buffer_, 0, size * sizeof(T));
+ this->write_ptr_ = 0;
+ }
+
+ virtual void reset() override
+ {
+ this->clear();
+ if(enable_lfo)
+ {
+ for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i) this->lfo_[i]->reset();
+ }
+ }
+
+ struct Empty
+ {
+ };
+
+ template
+ struct Reserve
+ {
+ typedef T Tail;
+ enum
+ {
+ length = _length
+ };
+ };
+
+ template
+ struct DelayLine
+ {
+ enum
+ {
+ length = DelayLine::length,
+ base = DelayLine::base + DelayLine::length + 1
+ };
+ };
+
+ template
+ struct DelayLine
+ {
+ enum
+ {
+ length = Memory::length,
+ base = 0
+ };
+ };
+
+ class Context
+ {
+ DISALLOW_COPY_AND_ASSIGN(Context);
+ friend class FxEngine;
+
+ public:
+ Context() :
+ accumulator_(0.0f),
+ previous_read_(0.0f),
+ buffer_(nullptr),
+ write_ptr_(0)
+ {
+ memset(this->lfo_value_, 0, LFOIndex::kLFOCount * sizeof(float32_t));
+ }
+
+ ~Context()
+ {
+ }
+
+ inline void load(float32_t value)
+ {
+ this->accumulator_ = value;
+ }
+
+ inline void read(float32_t value)
+ {
+ this->accumulator_ += value;
+ }
+
+ inline void read(float32_t value, float32_t scale)
+ {
+ this->accumulator_ += value * scale;
+ }
+
+ inline void write(float32_t& value)
+ {
+ value = this->accumulator_;
+ }
+
+ inline void write(float32_t& value, float32_t scale)
+ {
+ value = this->accumulator_;
+ this->accumulator_ *= scale;
+ }
+
+ inline void writeAndLoad(float32_t& value, float32_t newValue)
+ {
+ value = this->accumulator_;
+ this->load(newValue);
+ }
+
+ template
+ inline void directWrite(float32_t value, D& d)
+ {
+ this->load(value);
+ this->writeAndLoad(d, 0, 0.0f);
+ }
+
+ template
+ inline void write(D& d, int32_t offset)
+ {
+ assert((D::base + D::length) <= size);
+
+ T w = DataType::compress(this->accumulator_);
+ if(offset == -1)
+ {
+ this->buffer_[(this->write_ptr_ + D::base + D::length - 1) & MASK] = w;
+ }
+ else
+ {
+ this->buffer_[(this->write_ptr_ + D::base + offset) & MASK] = w;
+ }
+ }
+
+ template
+ inline void write(D& d, int32_t offset, float32_t scale)
+ {
+ this->write(d, offset);
+ this->accumulator_ *= scale;
+ }
+
+ template
+ inline void writeAndLoad(D& d, int32_t offset, float32_t newValue)
+ {
+ this->write(d, offset);
+ this->load(newValue);
+ }
+
+ template
+ inline void write(D& d, float32_t scale)
+ {
+ this->write(d, 0, scale);
+ }
+
+ template
+ inline void writeAndLoad(D& d, float32_t newValue)
+ {
+ this->writeAndLoad(d, 0, newValue);
+ }
+
+ template
+ inline void writeAllPass(D& d, int32_t offset, float32_t scale)
+ {
+ this->write(d, offset, scale);
+ this->accumulator_ += this->previous_read_;
+ }
+
+ template
+ inline void writeAllPass(D& d, float32_t scale)
+ {
+ this->writeAllPass(d, 0, scale);
+ }
+
+ template
+ inline void read(D& d, int32_t offset, float32_t scale)
+ {
+ assert((D::base + D::length) <= size);
+
+ T r;
+ if(offset == -1)
+ {
+ r = this->buffer_[(this->write_ptr_ + D::base + D::length - 1) & MASK];
+ }
+ else
+ {
+ r = this->buffer_[(this->write_ptr_ + D::base + offset) & MASK];
+ }
+ float32_t r_f = DataType::decompress(r);
+ this->previous_read_ = r_f;
+ this->accumulator_ += r_f * scale;
+ }
+
+ template
+ inline void read(D& d, float32_t scale)
+ {
+ this->read(d, 0, scale);
+ }
+
+ inline void lp(float32_t& state, float32_t coefficient)
+ {
+ state += coefficient * (this->accumulator_ - state);
+ this->accumulator_ = state;
+ }
+
+ inline void hp(float32_t& state, float32_t coefficient)
+ {
+ state += coefficient * (this->accumulator_ - state);
+ this->accumulator_ -= state;
+ }
+
+ template
+ inline void interpolate(D& d, float32_t offset, float32_t scale)
+ {
+ assert((D::base + D::length) <= size);
+
+ MAKE_INTEGRAL_FRACTIONAL(offset);
+
+ int32_t index = this->write_ptr_ + offset_integral + D::base;
+ float32_t a = DataType::decompress(this->buffer_[index & MASK]);
+ float32_t b = DataType::decompress(this->buffer_[(index + 1) & MASK]);
+ float32_t x = a + (b - a) * offset_fractional;
+
+ this->previous_read_ = x;
+ this->accumulator_ += x * scale;
+ }
+
+ template
+ inline void interpolate(D& d, float32_t offset, LFOIndex index, float32_t amplitude, float32_t scale)
+ {
+ assert(index < LFOIndex::kLFOCount);
+
+ this->interpolate(d, offset + amplitude * (this->lfo_value_[index] * 0.5f + 0.5f), scale);
+ }
+
+ private:
+ float32_t accumulator_;
+ float32_t previous_read_;
+ float32_t lfo_value_[LFOIndex::kLFOCount];
+ T* buffer_;
+ int32_t write_ptr_;
+ };
+
+ inline void setLFOFrequency(LFOIndex index, float32_t frequency)
+ {
+ assert(index < LFOIndex::kLFOCount);
+ if(enable_lfo)
+ {
+ this->lfo_[index]->setFrequency(frequency);
+ }
+ }
+
+ inline void setLFONormalizedFrequency(LFOIndex index, float32_t normalized_frequency)
+ {
+ assert(index < LFOIndex::kLFOCount);
+ if(enable_lfo)
+ {
+ this->lfo_[index]->setNormalizedFrequency(normalized_frequency);
+ }
+ }
+
+ inline void start(Context* c)
+ {
+ --this->write_ptr_;
+ if(this->write_ptr_ < 0)
+ {
+ this->write_ptr_ += size;
+ }
+ c->accumulator_ = 0.0f;
+ c->previous_read_ = 0.0f;
+ c->buffer_ = this->buffer_;
+ c->write_ptr_ = this->write_ptr_;
+ if(enable_lfo)
+ {
+ for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ c->lfo_value_[i] = this->lfo_[i]->process();
+ }
+ }
+ }
+
+private:
+ enum
+ {
+ MASK = size - 1
+ };
+
+ T* buffer_;
+ int32_t write_ptr_;
+
+ LFO* lfo_[LFOIndex::kLFOCount];
+
+ IMPLEMENT_DUMP(
+ const size_t space = 10;
+ const size_t precision = 5;
+
+ 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, '|', "write_ptr_");
+ out << "\t" << ss.str() << std::endl;
+
+ SS_RESET(ss, precision, 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->write_ptr_);
+ out << "\t" << ss.str() << std::endl;
+
+ if(deepInspection)
+ {
+ out << "FXEngine internal buffer:" << std::endl;
+
+ SS_RESET(ss, precision, std::left);
+ SS__TEXT(ss, ' ', space, std::left, '|', "index");
+ SS__TEXT(ss, ' ', space, std::left, '|', "buffer_");
+ out << "\t" << ss.str() << std::endl;
+
+ SS_RESET(ss, precision, 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 < size; ++i)
+ {
+ SS_RESET(ss, precision, std::left);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", i);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->buffer_[i]);
+ out << "\t" << ss.str() << std::endl;
+ }
+
+ if(enable_lfo)
+ {
+ for(size_t i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ this->lfo_[i]->dump(out, deepInspection, tag + ".lfo_[ " + std::to_string(i) + " ]");
+ }
+ }
+ }
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0u;
+
+ nb_errors += inspector(tag + ".write_ptr_", static_cast(this->write_ptr_), 0.0f, static_cast(size), deepInspection);
+ if(deepInspection)
+ {
+ for(size_t i = 0; i < size; ++i)
+ {
+ nb_errors += inspector(tag + ".buffer[ " + std::to_string(i) + " ]", this->buffer_[i], -1.0f, 1.0f, deepInspection);
+ }
+
+ if(enable_lfo)
+ {
+ for(size_t i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ this->lfo_[i]->inspect(inspector, deepInspection, tag + ".lfo_[ " + std::to_string(i) + " ]");
+ }
+ }
+ }
+
+ return nb_errors;
+
+ )
+};
diff --git a/src/fx_flanger.cpp b/src/fx_flanger.cpp
new file mode 100644
index 00000000..09081380
--- /dev/null
+++ b/src/fx_flanger.cpp
@@ -0,0 +1,145 @@
+#include "fx_flanger.h"
+
+Flanger::Flanger(float32_t sampling_rate, float32_t rate, float32_t depth, float32_t feedback) :
+ FXElement(sampling_rate, 0.928f),
+ MaxDelayLineSize(static_cast(MAX_FLANGER_DELAY * sampling_rate)),
+ write_index_(0)
+{
+ this->delay_lineL_ = new float32_t[this->MaxDelayLineSize];
+ this->delay_lineR_ = new float32_t[this->MaxDelayLineSize];
+
+ this->lfo_[LFOIndex::LFO_L] = new LFO(sampling_rate, 0.1f, 5.0f, 0.0f, false);
+ this->lfo_[LFOIndex::LFO_R] = new LFO(sampling_rate, 0.1f, 5.0f, Constants::MPI_2, false);
+
+ this->setRate(rate);
+ this->setDepth(depth);
+ this->setFeedback(feedback);
+
+ this->reset();
+}
+
+Flanger::~Flanger()
+{
+ delete[] this->delay_lineL_;
+ delete[] this->delay_lineR_;
+
+ for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ delete this->lfo_[i];
+ }
+}
+
+inline float32_t linearIterpolationnterp(float32_t inX, float32_t inY, float32_t inPhase)
+{
+ return (1.0f - inPhase) * inX + inPhase * inY;
+}
+
+void Flanger::reset()
+{
+ memset(this->delay_lineL_, 0, this->MaxDelayLineSize * sizeof(float32_t));
+ memset(this->delay_lineR_, 0, this->MaxDelayLineSize * sizeof(float32_t));
+ memset(this->feedback_samples_, 0, StereoChannels::kNumChannels * sizeof(float32_t));
+ this->write_index_ = 0;
+
+ for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ this->lfo_[i]->reset();
+ }
+}
+
+void Flanger::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
+{
+ // Write sample and any feedback into delay buffers
+ this->delay_lineL_[this->write_index_] = inL + this->feedback_samples_[StereoChannels::Left ];
+ this->delay_lineR_[this->write_index_] = inR + this->feedback_samples_[StereoChannels::Right];
+
+ ++this->write_index_;
+ if(this->write_index_ >= this->MaxDelayLineSize)
+ {
+ this->write_index_ -= this->MaxDelayLineSize;
+ }
+
+ // Configure LFO for effect processing
+ float32_t lfo_l = this->lfo_[LFOIndex::LFO_L]->process() * this->depth_;
+ float32_t lfo_r = this->lfo_[LFOIndex::LFO_R]->process() * this->depth_;
+
+ // Map LFO range to millisecond range according to Chorus or Flanger effect
+ float32_t lfoMappedL = mapfloat(lfo_l, -1.0f, 1.0f, 0.001f, 0.005f);
+ float32_t lfoMappedR = mapfloat(lfo_r, -1.0f, 1.0f, 0.001f, 0.005f);
+
+ // Calculate delay lengths in samples
+ float32_t delayTimeSamplesL = this->getSamplingRate() * lfoMappedL;
+ float32_t delayTimeSamplesR = this->getSamplingRate() * lfoMappedR;
+
+ // Calculate read head positions
+ float32_t delayReadHeadL = this->write_index_ - delayTimeSamplesL;
+ if(delayReadHeadL < 0.0f)
+ {
+ delayReadHeadL += this->MaxDelayLineSize;
+ }
+ float32_t delayReadHeadR = this->write_index_ - delayTimeSamplesR;
+ if(delayReadHeadR < 0.0f)
+ {
+ delayReadHeadR += this->MaxDelayLineSize;
+ }
+
+ // Calculate linear interpolation point for left channel
+ int32_t currentL = static_cast(delayReadHeadL);
+ int32_t nextL = currentL + 1;
+ float32_t fractionL = delayReadHeadL - currentL;
+ if(nextL >= static_cast(this->MaxDelayLineSize))
+ {
+ nextL -= this->MaxDelayLineSize;
+ }
+
+ // Calculate linear interpolation point for right channel
+ int32_t currentR = static_cast(delayReadHeadR);
+ int32_t nextR = currentR + 1;
+ float32_t fractionR = delayReadHeadR - currentR;
+ if(nextR >= static_cast(this->MaxDelayLineSize))
+ {
+ nextR -= this->MaxDelayLineSize;
+ }
+
+ // Interpolate and read from delay buffer
+ float32_t delay_sample_l = linearIterpolationnterp(this->delay_lineL_[currentL], this->delay_lineL_[nextL], fractionL);
+ float32_t delay_sample_r = linearIterpolationnterp(this->delay_lineR_[currentR], this->delay_lineR_[nextR], fractionR);
+
+ // Store delayed samples as feedback
+ this->feedback_samples_[StereoChannels::Left ] = delay_sample_l * this->feedback_;
+ this->feedback_samples_[StereoChannels::Right] = delay_sample_r * this->feedback_;
+
+ outL = delay_sample_l * this->OutputLevelCorrector;
+ outR = delay_sample_r * this->OutputLevelCorrector;
+}
+
+void Flanger::setRate(float32_t rate)
+{
+ this->lfo_[LFOIndex::LFO_L]->setNormalizedFrequency(rate);
+ this->lfo_[LFOIndex::LFO_R]->setNormalizedFrequency(rate);
+}
+
+float32_t Flanger::getRate() const
+{
+ return this->lfo_[LFOIndex::LFO_L]->getNormalizedFrequency();
+}
+
+void Flanger::setDepth(float32_t depth)
+{
+ this->depth_ = constrain(depth, 0.0f, 1.0f);
+}
+
+float32_t Flanger::getDepth() const
+{
+ return this->depth_;
+}
+
+void Flanger::setFeedback(float32_t feedback)
+{
+ this->feedback_ = constrain(feedback, 0.0f, 0.97f);
+}
+
+float32_t Flanger::getFeedback() const
+{
+ return this->feedback_;
+}
diff --git a/src/fx_flanger.h b/src/fx_flanger.h
new file mode 100644
index 00000000..89c7513e
--- /dev/null
+++ b/src/fx_flanger.h
@@ -0,0 +1,155 @@
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//
+// fx_flanger.h
+//
+// Stereo Flanger audio effects proposed in the context of the MiniDexed project
+// Author: Vincent Gauché
+//
+#pragma once
+
+#include "fx_components.h"
+
+#define MAX_FLANGER_DELAY 2.0f
+
+class Flanger : public FXElement
+{
+ DISALLOW_COPY_AND_ASSIGN(Flanger);
+
+public:
+ enum LFOIndex
+ {
+ LFO_L = 0,
+ LFO_R,
+ kLFOCount
+ };
+
+ Flanger(float32_t sampling_rate, float32_t rate = 0.5f, float32_t depth = 0.5f, float32_t feedback = 0.0f);
+ virtual ~Flanger();
+
+ virtual void reset() override;
+ virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
+
+ void setRate(float32_t rate);
+ float32_t getRate() const;
+
+ void setDepth(float32_t depth);
+ float32_t getDepth() const;
+
+ void setFeedback(float32_t feedback);
+ float32_t getFeedback() const;
+
+private:
+ const unsigned MaxDelayLineSize;
+ float32_t* delay_lineL_;
+ float32_t* delay_lineR_;
+ unsigned write_index_;
+ float32_t feedback_samples_[StereoChannels::kNumChannels];
+
+ LFO* lfo_[LFOIndex::kLFOCount];
+ float32_t depth_; // Depth of the flanger effect in milliseconds (0.0 - 10.0)
+ float32_t feedback_; // Amount of feedback to apply to the delay line
+
+ IMPLEMENT_DUMP(
+ const size_t space = 22;
+ const size_t precision = 5;
+
+ 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, '|', "write_index_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "feedback_samples_[ L ]");
+ SS__TEXT(ss, ' ', space, std::left, '|', "feedback_samples_[ R ]");
+ SS__TEXT(ss, ' ', space, std::left, '|', "depth_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "feedback_");
+ 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;
+
+ SS_RESET(ss, precision, std::left);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->write_index_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->feedback_samples_[StereoChannels::Left ]);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->feedback_samples_[StereoChannels::Right]);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->depth_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->feedback_);
+ out << "\t" << ss.str() << std::endl;
+
+ if(deepInspection)
+ {
+ out << "Flanger internal delay lines:" << std::endl;
+
+ SS_RESET(ss, precision, std::left);
+ SS__TEXT(ss, ' ', space, std::left, '|', "index");
+ SS__TEXT(ss, ' ', space, std::left, '|', "delay_lineL_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "delay_lineR_");
+ 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, '+');
+ out << "\t" << ss.str() << std::endl;
+
+ for(size_t i = 0; i < this->MaxDelayLineSize; ++i)
+ {
+ SS_RESET(ss, precision, std::left);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", i);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->delay_lineL_[i]);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->delay_lineR_[i]);
+ out << "\t" << ss.str() << std::endl;
+ }
+
+ for(size_t i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ this->lfo_[i]->dump(out, deepInspection, tag + ".lfo_[ " + std::to_string(i) + " ]");
+ }
+ }
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0u;
+
+ nb_errors += inspector(tag + ".write_index_", static_cast(this->write_index_), 0.0, static_cast(this->MaxDelayLineSize), deepInspection);
+ nb_errors += inspector(tag + ".feedback_samples_[ L ]", this->feedback_samples_[StereoChannels::Left ], -1.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".feedback_samples_[ R ]", this->feedback_samples_[StereoChannels::Right], -1.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".depth_", this->depth_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".feedback_", this->feedback_, 0.0f, 0.97f, deepInspection);
+
+ if(deepInspection)
+ {
+ for(size_t i = 0; i < this->MaxDelayLineSize; ++i)
+ {
+ nb_errors += inspector(tag + ".delay_lineL_[ " + std::to_string(i) + " ]", this->delay_lineL_[i], -1.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".delay_lineR_[ " + std::to_string(i) + " ]", this->delay_lineR_[i], -1.0f, 1.0f, deepInspection);
+ }
+
+ for(size_t i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ nb_errors += this->lfo_[i]->inspect(inspector, deepInspection, tag + ".lfo_[ " + std::to_string(i) + " ]");
+ }
+ }
+
+ return nb_errors;
+ )
+};
diff --git a/src/fx_orbitone.cpp b/src/fx_orbitone.cpp
new file mode 100644
index 00000000..764b46d8
--- /dev/null
+++ b/src/fx_orbitone.cpp
@@ -0,0 +1,116 @@
+#include "fx_orbitone.h"
+
+#define LFO_SLOW_MAX_FREQUENCY 1.0f
+#define LFO_FAST_MAX_FREQUENCY 8.8f
+
+Orbitone::Orbitone(float32_t sampling_rate, float32_t rate, float32_t depth) :
+ FXElement(sampling_rate, 1.4426f),
+ engine_(sampling_rate, 0.0f),
+ depth_(0.0f),
+ fullscale_depth_(0.0f)
+{
+ this->lfo_[LFOIndex::Slow0 ] = new LFO(sampling_rate, 0.0f, LFO_SLOW_MAX_FREQUENCY, 0.0f, false);
+ this->lfo_[LFOIndex::Slow120] = new LFO(sampling_rate, 0.0f, LFO_SLOW_MAX_FREQUENCY, 2.0f * PI / 3.0, false);
+ this->lfo_[LFOIndex::Slow240] = new LFO(sampling_rate, 0.0f, LFO_SLOW_MAX_FREQUENCY, 4.0f * PI / 3.0, false);
+
+ this->lfo_[LFOIndex::Fast0 ] = new LFO(sampling_rate, 0.0f, LFO_FAST_MAX_FREQUENCY, 0.0f, false);
+ this->lfo_[LFOIndex::Fast120] = new LFO(sampling_rate, 0.0f, LFO_FAST_MAX_FREQUENCY, 2.0f * PI / 3.0, false);
+ this->lfo_[LFOIndex::Fast240] = new LFO(sampling_rate, 0.0f, LFO_FAST_MAX_FREQUENCY, 4.0f * PI / 3.0, false);
+
+ for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ this->lfo_[i]->setNormalizedFrequency(rate);
+ }
+
+ this->setDepth(depth);
+}
+
+Orbitone::~Orbitone()
+{
+ for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ delete this->lfo_[i];
+ }
+}
+
+void Orbitone::reset()
+{
+ this->engine_.reset();
+ for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ this->lfo_[i]->reset();
+ }
+}
+
+void Orbitone::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
+{
+ typedef Engine::Reserve<2047, Engine::Reserve<2047> > Memory;
+ Engine::DelayLine line_l;
+ Engine::DelayLine line_r;
+ Engine::Context c;
+
+ this->engine_.start(&c);
+
+ float32_t slow_0 = this->lfo_[LFOIndex::Slow0 ]->process();
+ float32_t slow_120 = this->lfo_[LFOIndex::Slow120]->process();
+ float32_t slow_240 = this->lfo_[LFOIndex::Slow240]->process();
+
+ float32_t fast_0 = this->lfo_[LFOIndex::Fast0 ]->process();
+ float32_t fast_120 = this->lfo_[LFOIndex::Fast120]->process();
+ float32_t fast_240 = this->lfo_[LFOIndex::Fast240]->process();
+
+ float32_t a = this->fullscale_depth_ * 1.0f;
+ float32_t b = this->fullscale_depth_ * 0.1f;
+
+ float32_t mod_1 = slow_0 * a + fast_0 * b;
+ float32_t mod_2 = slow_120 * a + fast_120 * b;
+ float32_t mod_3 = slow_240 * a + fast_240 * b;
+
+ float32_t wet = 0.0f;
+
+ c.directWrite(inL, line_l);
+ 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);
+ c.writeAndLoad(wet, 0.0f);
+ outR = wet * this->OutputLevelCorrector;
+}
+
+void Orbitone::setRate(float32_t rate)
+{
+ rate = constrain(rate, 0.0f, 1.0f);
+ if(this->lfo_[LFOIndex::Slow0]->getNormalizedFrequency() != rate)
+ {
+ for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ this->lfo_[i]->setNormalizedFrequency(rate);
+ }
+ }
+}
+
+float32_t Orbitone::getRate() const
+{
+ return this->lfo_[LFOIndex::Slow0]->getNormalizedFrequency();
+}
+
+void Orbitone::setDepth(float32_t depth)
+{
+ depth = constrain(depth, 0.0f, 1.0f);
+ if(this->depth_ != depth)
+ {
+ this->depth_ = depth;
+ this->fullscale_depth_ = this->depth_ * ORBITONE_FULLSCALE_DEPTH_RATIO;
+ }
+}
+
+float32_t Orbitone::getDepth() const
+{
+ return this->depth_;
+}
diff --git a/src/fx_orbitone.h b/src/fx_orbitone.h
new file mode 100644
index 00000000..f8e81e94
--- /dev/null
+++ b/src/fx_orbitone.h
@@ -0,0 +1,119 @@
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//
+// fx_orbitone.h
+//
+// 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
+
+#include "fx_components.h"
+#include "fx_engine.hpp"
+
+#define ORBITONE_FULLSCALE_DEPTH_RATIO 256.0f
+
+class Orbitone : public FXElement
+{
+ DISALLOW_COPY_AND_ASSIGN(Orbitone);
+
+public:
+ enum LFOIndex
+ {
+ Slow0 = 0,
+ Slow120,
+ Slow240,
+ Fast0,
+ Fast120,
+ Fast240,
+ kLFOCount
+ };
+
+ Orbitone(float32_t sampling_rate, float32_t rate = 0.5f, float32_t depth = 0.5f);
+ virtual ~Orbitone();
+
+ virtual void reset() override;
+ virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
+
+ void setRate(float32_t rate);
+ float32_t getRate() const;
+
+ void setDepth(float32_t depth);
+ float32_t getDepth() const;
+
+private:
+ typedef FxEngine<4096, Format::FORMAT_FLOAT32, false> Engine;
+ Engine engine_;
+
+ float32_t depth_;
+ float32_t fullscale_depth_;
+
+ LFO* lfo_[LFOIndex::kLFOCount];
+
+ IMPLEMENT_DUMP(
+ const size_t space = 16;
+ const size_t precision = 5;
+
+ 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, '|', "depth_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "fullscale_depth_");
+ out << "\t" << ss.str() << std::endl;
+
+ SS_RESET(ss, precision, 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->depth_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->fullscale_depth_);
+ out << "\t" << ss.str() << std::endl;
+
+ if(deepInspection)
+ {
+ this->engine_.dump(out, deepInspection, tag + ".engine_");
+
+ for(size_t i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ this->lfo_[i]->dump(out, deepInspection, tag + ".lfo_[ " + std::to_string(i) + " ]");
+ }
+ }
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0u;
+
+ nb_errors += inspector(tag + ".depth_", this->depth_, -1.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".fullscale_depth_", this->fullscale_depth_, 0.0f, ORBITONE_FULLSCALE_DEPTH_RATIO, deepInspection);
+
+ if(deepInspection)
+ {
+ this->engine_.inspect(inspector, deepInspection, tag + ".engine_");
+
+ for(size_t i = 0; i < LFOIndex::kLFOCount; ++i)
+ {
+ this->lfo_[i]->inspect(inspector, deepInspection, tag + ".lfo_[ " + std::to_string(i) + " ]");
+ }
+ }
+
+ return nb_errors;
+ )
+};
diff --git a/src/fx_phaser.cpp b/src/fx_phaser.cpp
new file mode 100644
index 00000000..ba4f3757
--- /dev/null
+++ b/src/fx_phaser.cpp
@@ -0,0 +1,152 @@
+#include "fx_phaser.h"
+
+Phaser::AllpassDelay::AllpassDelay() :
+ FXElement(0.0f)
+{
+ this->reset();
+}
+
+Phaser::AllpassDelay::~AllpassDelay()
+{
+}
+
+void Phaser::AllpassDelay::reset()
+{
+ memset(this->a1_, 0, StereoChannels::kNumChannels * sizeof(float32_t));
+ memset(this->z_, 0, StereoChannels::kNumChannels * sizeof(float32_t));
+}
+
+void Phaser::AllpassDelay::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
+{
+ outL = inL * -this->a1_[StereoChannels::Left ] + this->z_[StereoChannels::Left ];
+ this->z_[StereoChannels::Left ] = outL * this->a1_[StereoChannels::Left ] + inL;
+
+ outR = inR * -this->a1_[StereoChannels::Right] + this->z_[StereoChannels::Right];
+ this->z_[StereoChannels::Right] = outR * this->a1_[StereoChannels::Right] + inR;
+}
+
+void Phaser::AllpassDelay::setDelay(float32_t delayL, float32_t delayR)
+{
+ this->a1_[StereoChannels::Left ] = (1.0f - delayL) / (1.0f + delayL);
+ this->a1_[StereoChannels::Right] = (1.0f - delayR) / (1.0f + delayR);
+}
+
+
+Phaser::Phaser(float32_t sampling_rate, float32_t rate, float32_t depth, float32_t feedback, unsigned nb_stages) :
+ FXElement(sampling_rate, 1.3804f),
+ depth_(0.0f),
+ gain_(1.0f),
+ feedback_(0.0f),
+ dmin_(0.0f),
+ dmax_(0.0f)
+{
+ this->lfo_[StereoChannels::Left ] = new LFO(sampling_rate, 0.0f, 2.5f, 0.0f, false);
+ this->lfo_[StereoChannels::Right] = new LFO(sampling_rate, 0.0f, 2.5f, Constants::MPI_2, false);
+
+ this->setRate(rate);
+ this->setDepth(depth);
+ this->setFeedback(feedback);
+ this->setNbStages(nb_stages);
+ this->setFrequencyRange(440.0f, 1600.0f);
+
+ this->reset();
+}
+
+Phaser::~Phaser()
+{
+ delete this->lfo_[StereoChannels::Left ];
+ delete this->lfo_[StereoChannels::Right];
+}
+
+void Phaser::reset()
+{
+ memset(this->z_, 0, StereoChannels::kNumChannels * sizeof(float32_t));
+
+ for(unsigned i = 0; i < MAX_NB_PHASES; ++i)
+ {
+ this->stages_[i].reset();
+ }
+ this->lfo_[StereoChannels::Left ]->reset();
+ this->lfo_[StereoChannels::Right]->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_) * 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];
+ for(unsigned i = 0; i < this->nb_stages_; ++i)
+ {
+ this->stages_[i].setDelay(dL, dR);
+ this->stages_[i].processSample(sampleL, sampleR, sampleL, sampleR);
+ }
+ this->z_[StereoChannels::Left ] = sampleL;
+ this->z_[StereoChannels::Right] = sampleR;
+
+ outL = inL + this->z_[StereoChannels::Left ] * this->depth_;
+ outR = inR + this->z_[StereoChannels::Right] * this->depth_;
+
+ outL *= this->gain_;
+ outR *= this->gain_;
+}
+
+void Phaser::setFrequencyRange(float32_t min_frequency, float32_t max_frequency)
+{
+ this->dmin_ = 2.0f * std::min(min_frequency, max_frequency) / this->getSamplingRate();
+ this->dmax_ = 2.0f * std::max(min_frequency, max_frequency) / this->getSamplingRate();
+}
+
+void Phaser::setRate(float32_t rate)
+{
+ rate = constrain(rate, 0.0f, 1.0f);
+ this->lfo_[StereoChannels::Left ]->setNormalizedFrequency(rate);
+ this->lfo_[StereoChannels::Right]->setNormalizedFrequency(rate);
+}
+
+float32_t Phaser::getRate() const
+{
+ return this->lfo_[StereoChannels::Left]->getNormalizedFrequency();
+}
+
+void Phaser::setDepth(float32_t depth)
+{
+ depth = constrain(depth, 0.0f, 1.0f);
+ this->depth_ = depth;
+ this->gain_ = this->OutputLevelCorrector / (1.0f + depth);
+}
+
+float32_t Phaser::getDepth() const
+{
+ return this->depth_;
+}
+
+void Phaser::setFeedback(float32_t feedback)
+{
+ feedback = constrain(feedback, 0.0f, 0.97f);
+ this->feedback_ = feedback;
+}
+
+float32_t Phaser::getFeedback() const
+{
+ return this->feedback_;
+}
+
+void Phaser::setNbStages(unsigned nb_stages)
+{
+ if(nb_stages < 2)
+ {
+ nb_stages = 2;
+ }
+ else if(nb_stages > MAX_NB_PHASES)
+ {
+ nb_stages = MAX_NB_PHASES;
+ }
+ this->nb_stages_ = nb_stages;
+}
+
+unsigned Phaser::getNbStages() const
+{
+ return this->nb_stages_;
+}
diff --git a/src/fx_phaser.h b/src/fx_phaser.h
new file mode 100644
index 00000000..6ff605bc
--- /dev/null
+++ b/src/fx_phaser.h
@@ -0,0 +1,177 @@
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//
+// fx_phaser.h
+//
+// Stereo Phaser audio effects proposed in the context of the MiniDexed project
+// Author: Vincent Gauché
+//
+#pragma once
+
+#include "fx_components.h"
+
+#define MAX_NB_PHASES 24
+
+class Phaser : public FXElement
+{
+ DISALLOW_COPY_AND_ASSIGN(Phaser);
+
+public:
+ class AllpassDelay : public FXElement
+ {
+ DISALLOW_COPY_AND_ASSIGN(AllpassDelay);
+
+ public:
+ AllpassDelay();
+ virtual ~AllpassDelay();
+
+ virtual void reset() override;
+ virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
+
+ void setDelay(float32_t delayL, float32_t delayR);
+
+ private:
+ float32_t a1_[StereoChannels::kNumChannels];
+ float32_t z_[StereoChannels::kNumChannels];
+
+ IMPLEMENT_DUMP(
+ const size_t space = 10;
+ 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, '|', "a1_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "z_[ L ]");
+ SS__TEXT(ss, ' ', space, std::left, '|', "z_[ R ]");
+ 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, '+');
+ out << "\t" << ss.str() << std::endl;
+
+ SS_RESET(ss, precision, std::left);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->a1_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->z_[StereoChannels::Left ]);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->z_[StereoChannels::Right]);
+ out << "\t" << ss.str() << std::endl;
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ return 0u;
+ )
+ };
+
+ Phaser(float32_t sampling_rate, float32_t rate = 0.5f, float32_t depth = 1.0f, float32_t feedback = 0.7f, unsigned nb_stages = 12);
+ virtual ~Phaser();
+
+ virtual void reset() override;
+ virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
+
+ void setFrequencyRange(float32_t min_frequency, float32_t max_frequency);
+
+ void setRate(float32_t rate);
+ float32_t getRate() const;
+
+ void setDepth(float32_t depth);
+ float32_t getDepth() const;
+
+ void setFeedback(float32_t depth);
+ float32_t getFeedback() const;
+
+ void setNbStages(unsigned nb_stages);
+ unsigned getNbStages() const;
+
+private:
+ LFO* lfo_[StereoChannels::kNumChannels];
+ float32_t depth_;
+ float32_t gain_;
+ float32_t feedback_;
+ float32_t dmin_;
+ float32_t dmax_;
+ unsigned nb_stages_;
+ AllpassDelay stages_[MAX_NB_PHASES];
+ float32_t z_[StereoChannels::kNumChannels];
+
+ 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, '|', "depth_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "feedback_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "dmin_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "dmax_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "nb_stages_");
+ 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;
+
+ SS_RESET(ss, precision, std::left);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->depth_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->feedback_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->dmin_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->dmax_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->nb_stages_);
+ out << "\t" << ss.str() << std::endl;
+
+ if(deepInspection)
+ {
+ this->lfo_[StereoChannels::Left ]->dump(out, deepInspection, tag + ".lfo_[ L ]");
+ this->lfo_[StereoChannels::Right]->dump(out, deepInspection, tag + ".lfo_[ R ]");
+ for(unsigned i = 0; i < MAX_NB_PHASES; ++i)
+ {
+ this->stages_[i].dump(out, deepInspection, tag + ".stages_[ " + std::to_string(i) + " ]");
+ }
+ }
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0u;
+
+ nb_errors += inspector(tag + ".depth_", this->depth_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".feedback_", this->feedback_, 0.0f, 0.97f, deepInspection);
+ nb_errors += inspector(tag + ".nb_stages_", static_cast(this->nb_stages_), 0.0f, static_cast(MAX_NB_PHASES), deepInspection);
+
+ if(deepInspection)
+ {
+ nb_errors += this->lfo_[StereoChannels::Left ]->inspect(inspector, deepInspection, tag + ".lfo_[ L ]");
+ nb_errors += this->lfo_[StereoChannels::Right]->inspect(inspector, deepInspection, tag + ".lfo_[ R ]");
+ for(unsigned i = 0; i < MAX_NB_PHASES; ++i)
+ {
+ nb_errors += this->stages_[i].inspect(inspector, deepInspection, tag + ".stages_[ " + std::to_string(i) + " ]");
+ }
+ }
+
+ return nb_errors;
+ )
+};
\ No newline at end of file
diff --git a/src/fx_pitch_shifter.cpp b/src/fx_pitch_shifter.cpp
new file mode 100644
index 00000000..5bb053ad
--- /dev/null
+++ b/src/fx_pitch_shifter.cpp
@@ -0,0 +1,104 @@
+#include "fx_pitch_shifter.h"
+#include "fx_shimmer_helper.h"
+
+#include
+#include
+
+#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 left;
+ Engine::DelayLine 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_;
+}
\ No newline at end of file
diff --git a/src/fx_pitch_shifter.h b/src/fx_pitch_shifter.h
new file mode 100644
index 00000000..f7fdeec0
--- /dev/null
+++ b/src/fx_pitch_shifter.h
@@ -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 .
+//
+
+//
+// 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 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;
+ )
+};
diff --git a/src/fx_rack.cpp b/src/fx_rack.cpp
new file mode 100644
index 00000000..a4e2bc17
--- /dev/null
+++ b/src/fx_rack.cpp
@@ -0,0 +1,153 @@
+#include "fx_rack.h"
+
+#include
+
+FXRack::FXRack(float32_t sampling_rate, bool enable, float32_t wet) :
+ FX(sampling_rate),
+ FXElement(sampling_rate),
+ enable_(enable),
+ wet_level_(wet),
+ fx_chain_()
+{
+ this->fxTube_ = new FXUnit(sampling_rate);
+ this->fxChorus_ = new FXUnit(sampling_rate);
+ this->fxFlanger_ = new FXUnit(sampling_rate);
+ this->fxOrbitone_ = new FXUnit(sampling_rate);
+ this->fxPhaser_ = new FXUnit(sampling_rate);
+ this->fxDelay_ = new FXUnit(sampling_rate);
+ this->fxReverberator_ = new FXUnit(sampling_rate);
+
+ this->registerFX(this->fxTube_);
+ this->registerFX(this->fxChorus_);
+ this->registerFX(this->fxFlanger_);
+ this->registerFX(this->fxOrbitone_);
+ this->registerFX(this->fxPhaser_);
+ this->registerFX(this->fxDelay_);
+ this->registerFX(this->fxReverberator_);
+}
+
+FXRack::~FXRack()
+{
+ this->fx_chain_.clear();
+
+ delete this->fxTube_;
+ delete this->fxChorus_;
+ delete this->fxFlanger_;
+ delete this->fxOrbitone_;
+ delete this->fxPhaser_;
+ delete this->fxDelay_;
+ delete this->fxReverberator_;
+}
+
+inline void FXRack::reset()
+{
+ for(FXElement* fx : this->fx_chain_)
+ {
+ fx->reset();
+ }
+}
+
+inline void FXRack::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
+{
+ for(FXElement* fx : this->fx_chain_)
+ {
+ fx->processSample(inL, inR, outL, outR);
+
+ inL = outL;
+ inR = outR;
+ }
+}
+
+void FXRack::process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples)
+{
+ for(unsigned i = 0; i < nSamples; ++i)
+ {
+ float32_t sampleInL = *left_input;
+ float32_t sampleInR = *right_input;
+ float32_t sampleOutL = 0.0f;
+ float32_t sampleOutR = 0.0f;
+
+ if(this->isEnable())
+ {
+ this->processSample(sampleInL, sampleInR, sampleOutL, sampleOutR);
+
+ float32_t dryLevel = 1.0f - this->getWetLevel();
+ *left_output = this->getWetLevel() * sampleOutL + dryLevel * (*left_input);
+ *right_output = this->getWetLevel() * sampleOutR + dryLevel * (*right_input);
+ }
+ else
+ {
+ *left_output = sampleInL;
+ *right_output = sampleInR;
+ }
+
+ // Move inputs by 1 sample
+ ++left_input;
+ ++right_input;
+
+ // Move outputs by 1 sample
+ ++left_output;
+ ++right_output;
+ }
+}
+
+void FXRack::setEnable(bool enable)
+{
+ this->enable_ = enable;
+}
+
+bool FXRack::isEnable() const
+{
+ return this->enable_;
+}
+
+void FXRack::setWetLevel(float32_t wet_level)
+{
+ this->wet_level_ = constrain(wet_level, 0.0f, 1.0f);
+}
+
+float32_t FXRack::getWetLevel() const
+{
+ return this->wet_level_;
+}
+
+void FXRack::registerFX(FXElement* fx)
+{
+ assert(fx);
+ this->fx_chain_.push_back(fx);
+}
+
+FXUnit* FXRack::getTube()
+{
+ return this->fxTube_;
+}
+
+FXUnit* FXRack::getChorus()
+{
+ return this->fxChorus_;
+}
+
+FXUnit* FXRack::getFlanger()
+{
+ return this->fxFlanger_;
+}
+
+FXUnit* FXRack::getOrbitone()
+{
+ return this->fxOrbitone_;
+}
+
+FXUnit* FXRack::getPhaser()
+{
+ return this->fxPhaser_;
+}
+
+FXUnit* FXRack::getDelay()
+{
+ return this->fxDelay_;
+}
+
+FXUnit* FXRack::getReverberator()
+{
+ return this->fxReverberator_;
+}
diff --git a/src/fx_rack.h b/src/fx_rack.h
new file mode 100644
index 00000000..0c61d722
--- /dev/null
+++ b/src/fx_rack.h
@@ -0,0 +1,133 @@
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//
+// fx_rack.h
+//
+// Rack of audio effects proposed in the context of the MiniDexed project
+// Author: Vincent Gauché
+//
+#pragma once
+
+#include "fx.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 "fx_reverberator.h"
+#include "fx_unit.hpp"
+
+#include
+
+typedef std::vector FXChain;
+
+class FXRack : virtual public FX, virtual public FXElement
+{
+ DISALLOW_COPY_AND_ASSIGN(FXRack);
+
+public:
+ FXRack(float32_t sampling_rate, bool enable = true, float32_t wet = 1.0f);
+ virtual ~FXRack();
+
+ virtual void reset() override;
+ virtual inline void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
+ virtual void process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) override;
+
+ void setEnable(bool enable = true);
+ bool isEnable() const;
+
+ void setWetLevel(float32_t wet_level);
+ float32_t getWetLevel() const;
+
+ FXUnit* getTube();
+ FXUnit* getChorus();
+ FXUnit* getFlanger();
+ FXUnit* getOrbitone();
+ FXUnit* getPhaser();
+ FXUnit* getDelay();
+ FXUnit* getReverberator();
+
+private:
+ void registerFX(FXElement* fx);
+
+ bool enable_;
+ float32_t wet_level_;
+
+ FXChain fx_chain_;
+ FXUnit* fxTube_;
+ FXUnit* fxChorus_;
+ FXUnit* fxFlanger_;
+ FXUnit* fxOrbitone_;
+ FXUnit* fxPhaser_;
+ FXUnit* fxDelay_;
+ FXUnit* fxReverberator_;
+
+ IMPLEMENT_DUMP(
+ const size_t space = 10;
+ const size_t precision = 5;
+
+ 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, '|', "enable_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "wet_level_");
+ out << "\t" << ss.str() << std::endl;
+
+ SS_RESET(ss, precision, 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->enable_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->wet_level_);
+ out << "\t" << ss.str() << std::endl;
+
+ if(deepInspection)
+ {
+ this->fxTube_->dump(out, deepInspection, tag + ".fxTube_");
+ this->fxChorus_->dump(out, deepInspection, tag + ".fxChorus_");
+ this->fxFlanger_->dump(out, deepInspection, tag + ".fxFlanger_");
+ this->fxOrbitone_->dump(out, deepInspection, tag + ".fxOrbitone_");
+ this->fxPhaser_->dump(out, deepInspection, tag + ".fxPhaser_");
+ this->fxDelay_->dump(out, deepInspection, tag + ".fxDelay_");
+ this->fxReverberator_->dump(out, deepInspection, tag + ".fxReverberator_");
+ }
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0;
+
+ nb_errors += inspector(tag + ".enable_", this->enable_, -1.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".wet_level_", this->wet_level_, -1.0f, 1.0f, deepInspection);
+
+ if(deepInspection)
+ {
+ nb_errors += this->fxTube_->inspect(inspector, deepInspection, tag + ".fxTube_");
+ nb_errors += this->fxChorus_->inspect(inspector, deepInspection, tag + ".fxChorus_");
+ nb_errors += this->fxFlanger_->inspect(inspector, deepInspection, tag + ".fxFlanger_");
+ nb_errors += this->fxOrbitone_->inspect(inspector, deepInspection, tag + ".fxOrbitone_");
+ nb_errors += this->fxPhaser_->inspect(inspector, deepInspection, tag + ".fxPhaser_");
+ nb_errors += this->fxDelay_->inspect(inspector, deepInspection, tag + ".fxDelay_");
+ nb_errors += this->fxReverberator_->inspect(inspector, deepInspection, tag + ".fxReverberator_");
+ }
+
+ return nb_errors;
+ )
+};
\ No newline at end of file
diff --git a/src/fx_reverberator.cpp b/src/fx_reverberator.cpp
new file mode 100644
index 00000000..139970f3
--- /dev/null
+++ b/src/fx_reverberator.cpp
@@ -0,0 +1,160 @@
+#include "fx_reverberator.h"
+
+#define TAIL , -1
+
+Reverberator::Reverberator(float32_t sampling_rate) :
+ FXElement(sampling_rate),
+ engine_(sampling_rate),
+ input_gain_(-1.0f),
+ reverb_time_(0.0f),
+ diffusion_(-1.0f),
+ lp_(-1.0f),
+ lp_decay_1_(0.0f),
+ lp_decay_2_(0.0f)
+{
+ this->engine_.setLFOFrequency(Engine::LFOIndex::LFO_1, 0.5f);
+ this->engine_.setLFOFrequency(Engine::LFOIndex::LFO_2, 0.3f);
+
+ this->setInputGain(1.0f);
+ this->setTime(0.7f);
+ this->setDiffusion(0.625f);
+ this->setLP(0.7f);
+
+ this->reset();
+}
+
+Reverberator::~Reverberator()
+{
+}
+
+void Reverberator::reset()
+{
+ this->engine_.reset();
+ this->lp_decay_1_ = 0.0f;
+ this->lp_decay_2_ = 0.0f;
+}
+
+void Reverberator::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
+{
+ // This is the Griesinger topology described in the Dattorro paper
+ // (4 AP diffusers on the input, then a loop of 2x 2AP+1Delay).
+ // Modulation is applied in the loop of the first diffuser AP for additional
+ // smearing; and to the two long delays for a slow shimmer/chorus effect.
+ typedef Engine::Reserve< 113,
+ Engine::Reserve< 162,
+ Engine::Reserve< 241,
+ Engine::Reserve< 399,
+ Engine::Reserve<1653,
+ Engine::Reserve<2038,
+ Engine::Reserve<3411,
+ Engine::Reserve<1913,
+ Engine::Reserve<1663,
+ Engine::Reserve<4782> > > > > > > > > > Memory;
+ Engine::DelayLine ap1;
+ Engine::DelayLine ap2;
+ Engine::DelayLine ap3;
+ Engine::DelayLine ap4;
+ Engine::DelayLine dap1a;
+ Engine::DelayLine dap1b;
+ Engine::DelayLine del1;
+ Engine::DelayLine dap2a;
+ Engine::DelayLine dap2b;
+ Engine::DelayLine del2;
+ Engine::Context c;
+
+ const float32_t kap = this->diffusion_;
+ const float32_t klp = this->lp_;
+ const float32_t krt = this->reverb_time_;
+ const float32_t gain = this->input_gain_;
+
+ float32_t lp_1 = this->lp_decay_1_;
+ float32_t lp_2 = this->lp_decay_2_;
+
+ float32_t wet = 0.0f;
+ float32_t apout = 0.0f;
+ engine_.start(&c);
+
+ // Smear AP1 inside the loop.
+ c.interpolate(ap1, 10.0f, Engine::LFOIndex::LFO_1, 60.0f, 1.0f);
+ c.writeAndLoad(ap1, 100, 0.0f);
+ c.read(inL + inR, gain);
+
+ // Diffuse through 4 allpasses.
+ c.read(ap1 TAIL, kap);
+ c.writeAllPass(ap1, -kap);
+ c.read(ap2 TAIL, kap);
+ c.writeAllPass(ap2, -kap);
+ c.read(ap3 TAIL, kap);
+ c.writeAllPass(ap3, -kap);
+ c.read(ap4 TAIL, kap);
+ c.writeAllPass(ap4, -kap);
+ c.write(apout);
+
+ // Main reverb loop.
+ c.load(apout);
+ c.interpolate(del2, 4680.0f, Engine::LFOIndex::LFO_2, 100.0f, krt);
+ c.lp(lp_1, klp);
+ c.read(dap1a TAIL, -kap);
+ c.writeAllPass(dap1a, kap);
+ c.read(dap1b TAIL, kap);
+ c.writeAllPass(dap1b, -kap);
+ c.write(del1, 2.0f);
+ c.writeAndLoad(wet, 0.0f);
+
+ outL = wet;
+
+ c.load(apout);
+ c.read(del1 TAIL, krt);
+ c.lp(lp_2, klp);
+ c.read(dap2a TAIL, kap);
+ c.writeAllPass(dap2a, -kap);
+ c.read(dap2b TAIL, -kap);
+ c.writeAllPass(dap2b, kap);
+ c.write(del2, 2.0f);
+ c.writeAndLoad(wet, 0.0f);
+
+ outR = wet;
+
+ this->lp_decay_1_ = lp_1;
+ this->lp_decay_2_ = lp_2;
+}
+
+void Reverberator::setInputGain(float32_t gain)
+{
+ this->input_gain_ = constrain(gain, 0.0f, 1.0f);
+}
+
+float32_t Reverberator::getInputGain() const
+{
+ return this->input_gain_;
+}
+
+void Reverberator::setTime(float32_t time)
+{
+ this->reverb_time_ = constrain(time, 0.0f, 1.0f);
+}
+
+float32_t Reverberator::getTime() const
+{
+ return this->reverb_time_;
+}
+
+void Reverberator::setDiffusion(float32_t diffusion)
+{
+ this->diffusion_ = constrain(diffusion, 0.0f, 1.0f);
+}
+
+float32_t Reverberator::getDiffusion() const
+{
+ return this->diffusion_;
+}
+
+void Reverberator::setLP(float32_t lp)
+{
+ this->lp_ = constrain(lp, 0.0f, 1.0f);
+}
+
+float32_t Reverberator::getLP() const
+{
+ return this->lp_;
+}
diff --git a/src/fx_reverberator.h b/src/fx_reverberator.h
new file mode 100644
index 00000000..ef79883d
--- /dev/null
+++ b/src/fx_reverberator.h
@@ -0,0 +1,124 @@
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+//
+// fx_reverberator.h
+//
+// 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
+
+#include "fx_components.h"
+#include "fx_engine.hpp"
+
+#define REVERBERATOR_BUFFER_SIZE 16384
+
+class Reverberator : public FXElement
+{
+ DISALLOW_COPY_AND_ASSIGN(Reverberator);
+
+public:
+ Reverberator(float32_t sampling_rate);
+ virtual ~Reverberator();
+
+ virtual void reset() override;
+ virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
+
+ void setInputGain(float32_t gain);
+ float32_t getInputGain() const;
+
+ void setTime(float32_t time);
+ float32_t getTime() const;
+
+ void setDiffusion(float32_t diffusion);
+ float32_t getDiffusion() const;
+
+ void setLP(float32_t lp);
+ float32_t getLP() const;
+
+private:
+ typedef FxEngine Engine;
+ Engine engine_;
+
+ float32_t input_gain_;
+ float32_t reverb_time_;
+ float32_t diffusion_;
+ float32_t lp_;
+
+ float32_t lp_decay_1_;
+ float32_t lp_decay_2_;
+
+ 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, '|', "input_gain_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "reverb_time_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "diffusion_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "lp_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "lp_decay_1_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "lp_decay_2_");
+ 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->input_gain_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->reverb_time_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->diffusion_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->lp_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->lp_decay_1_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->lp_decay_2_);
+ 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 + ".input_gain_", this->input_gain_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".reverb_time_", this->reverb_time_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".diffusion_", this->diffusion_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".lp_", this->lp_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".lp_decay_1_", this->lp_decay_1_, -1.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".lp_decay_2_", this->lp_decay_2_, -1.0f, 1.0f, deepInspection);
+
+ if(deepInspection)
+ {
+ nb_errors += this->engine_.inspect(inspector, deepInspection, tag + ".engine_");
+ }
+
+ return nb_errors;
+ )
+};
diff --git a/src/fx_shimmer_helper.cpp b/src/fx_shimmer_helper.cpp
new file mode 100644
index 00000000..bed1fad0
--- /dev/null
+++ b/src/fx_shimmer_helper.cpp
@@ -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,
+};
diff --git a/src/fx_shimmer_helper.h b/src/fx_shimmer_helper.h
new file mode 100644
index 00000000..582131c8
--- /dev/null
+++ b/src/fx_shimmer_helper.h
@@ -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 .
+
+//
+// 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(pitch_fractional * 256.0f)];
+}
diff --git a/src/fx_shimmer_reverb.cpp b/src/fx_shimmer_reverb.cpp
new file mode 100644
index 00000000..cc9ce023
--- /dev/null
+++ b/src/fx_shimmer_reverb.cpp
@@ -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(this->lp_cutoff_, this->lpq_);
+ this->hp_filter_.setFQ(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();
+}
diff --git a/src/fx_shimmer_reverb.h b/src/fx_shimmer_reverb.h
new file mode 100644
index 00000000..115ed85a
--- /dev/null
+++ b/src/fx_shimmer_reverb.h
@@ -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 .
+//
+
+//
+// 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;
+ )
+};
diff --git a/src/fx_svf.cpp b/src/fx_svf.cpp
new file mode 100644
index 00000000..976cad26
--- /dev/null
+++ b/src/fx_svf.cpp
@@ -0,0 +1,153 @@
+#include "fx_svf.h"
+
+#include
+
+StateVariableFilter::StateVariableFilter(float32_t sampling_rate, FilterMode mode, float32_t cutoff) :
+ FXElement(sampling_rate),
+ mode_(mode),
+ gain_(-1.0f),
+ cutoff_(cutoff),
+ resonance_(0.0f)
+{
+ this->setCutoff(cutoff);
+ this->setResonance(0.0f);
+ this->setGainDB(0.0f);
+
+ this->reset();
+}
+
+StateVariableFilter::~StateVariableFilter()
+{
+}
+
+void StateVariableFilter::setFilterMode(FilterMode mode)
+{
+ if(this->mode_ != mode)
+ {
+ this->mode_ = mode;
+ this->updateCoefficients();
+ }
+}
+
+void StateVariableFilter::setCutoff(float32_t cutoff)
+{
+ static const float32_t max_frequency = 0.45f * this->getSamplingRate();
+
+ cutoff = constrain(cutoff, 1.0f, max_frequency);
+ if(this->cutoff_ != cutoff)
+ {
+ this->cutoff_ = cutoff;
+ this->updateCoefficients();
+ }
+}
+
+void StateVariableFilter::setResonance(float32_t resonance)
+{
+ resonance = constrain(resonance, 0.005f, 1.0f);
+ if(this->resonance_ != resonance)
+ {
+ this->resonance_ = resonance;
+ this->updateCoefficients();
+ }
+}
+
+void StateVariableFilter::setGainDB(float32_t gainDB)
+{
+ gainDB = constrain(gainDB, -1.0f, 1.0f);
+ if(this->gain_ != gainDB)
+ {
+ this->gain_ = gainDB;
+ this->g_ = std::pow(10.0f, 1.2f * this->gain_);
+ this->updateCoefficients();
+ }
+}
+
+void StateVariableFilter::updateCoefficients()
+{
+ // Compute the filter coefficients based on the current parameter values
+ this->w_ = 2.0f * std::tan(PI * this->cutoff_ / this->getSamplingRate());
+ this->a_ = this->w_ / this->resonance_;
+ this->b_ = this->w_ * this->w_;
+ float32_t a_b = this->a_ + this->b_;
+ this->c1_ = a_b / (1.0f + 0.5f * this->a_ + 0.25f * this->b_);
+ this->c2_ = this->b_ / a_b;
+
+ switch(this->mode_)
+ {
+ case FilterMode::LPF:
+ this->d1_ = 0.0f;
+ this->d0_ = 0.25f * this->c1_ * this->c2_;
+ break;
+
+ case FilterMode::HPF:
+ this->d1_ = 0.0f;
+ this->d0_ = 1.0f - 0.5f * this->c1_ + 0.25f * this->c1_ * this->c2_;
+ break;
+
+ case FilterMode::BPF:
+ this->d1_ = 1.0f - this->c2_;
+ this->d0_ = this->d1_ * this->c1_ * 0.5f;
+ break;
+ }
+
+ this->reset();
+}
+
+void StateVariableFilter::reset()
+{
+ memset(this->z1_, 0, StereoChannels::kNumChannels * sizeof(float32_t));
+ memset(this->z2_, 0, StereoChannels::kNumChannels * sizeof(float32_t));
+}
+
+void StateVariableFilter::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
+{
+ const float32_t gain = this->g_;
+
+ switch(this->mode_)
+ {
+ case FilterMode::LPF:
+ {
+ const float32_t x = inL - this->z1_[StereoChannels::Left] - this->z2_[StereoChannels::Left] + 1e-20f;
+ this->z2_[StereoChannels::Left] += this->c2_ * this->z1_[StereoChannels::Left];
+ outL = gain * (this->d0_ * x + this->z2_[StereoChannels::Left]);
+ this->z1_[StereoChannels::Left] += this->c1_ * x;
+ }
+ {
+ const float32_t x = inR - this->z1_[StereoChannels::Right] - this->z2_[StereoChannels::Right] + 1e-20f;
+ this->z2_[StereoChannels::Right] += this->c2_ * this->z1_[StereoChannels::Right];
+ outR = gain * (this->d0_ * x + this->z2_[StereoChannels::Right]);
+ this->z1_[StereoChannels::Right] += this->c1_ * x;
+ }
+ break;
+
+ case FilterMode::HPF:
+ {
+ const float32_t x = inL - this->z1_[StereoChannels::Left] - this->z2_[StereoChannels::Left] + 1e-20f;
+ outL = gain * this->d0_ * x;
+ this->z2_[StereoChannels::Left] += this->c2_ * this->z1_[StereoChannels::Left];
+ this->z1_[StereoChannels::Left] += this->c1_ * x;
+ }
+ {
+ const float32_t x = inR - this->z1_[StereoChannels::Right] - this->z2_[StereoChannels::Right] + 1e-20f;
+ outR = gain * this->d0_ * x;
+ this->z2_[StereoChannels::Right] += this->c2_ * this->z1_[StereoChannels::Right];
+ this->z1_[StereoChannels::Right] += this->c1_ * x;
+ }
+ break;
+
+ case FilterMode::BPF:
+ {
+ const float32_t x = inL - this->z1_[StereoChannels::Left] - this->z2_[StereoChannels::Left] + 1e-20f;
+ outL = gain * (this->d0_ * x) + this->d1_ * this->z1_[StereoChannels::Left];
+ this->z2_[StereoChannels::Left] += this->c2_ * this->z1_[StereoChannels::Left];
+ this->z1_[StereoChannels::Left] += this->c1_ * x;
+ }
+ {
+ const float32_t x = inR - this->z1_[StereoChannels::Right] - this->z2_[StereoChannels::Right] + 1e-20f;
+ outL = gain * (this->d0_ * x) + this->d1_ * this->z1_[StereoChannels::Right];
+ this->z2_[StereoChannels::Right] += this->c2_ * this->z1_[StereoChannels::Right];
+ this->z1_[StereoChannels::Right] += this->c1_ * x;
+ }
+ break;
+ }
+}
diff --git a/src/fx_svf.h b/src/fx_svf.h
new file mode 100644
index 00000000..361dc6e1
--- /dev/null
+++ b/src/fx_svf.h
@@ -0,0 +1,363 @@
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+//
+// fx_svf.h
+//
+// State Variable Filter used in Tape Delay
+// Author: Vincent Gauché
+//
+#pragma once
+
+#include "fx.h"
+#include "fx_components.h"
+
+class StateVariableFilter : public FXElement
+{
+ DISALLOW_COPY_AND_ASSIGN(StateVariableFilter);
+
+public:
+ enum FilterMode
+ {
+ LPF, // Low pass filter
+ HPF, // High pass filter
+ BPF // Band pass filter
+ };
+
+ StateVariableFilter(float32_t sampling_rate, FilterMode mode, float32_t cutoff);
+ virtual ~StateVariableFilter();
+
+ void setFilterMode(FilterMode mode);
+ void setGainDB(float32_t gainDB);
+ void setCutoff(float32_t cutoff);
+ void setResonance(float32_t resonance);
+
+ virtual void reset() override;
+ virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
+
+private:
+ void updateCoefficients();
+
+ FilterMode mode_;
+ float32_t gain_;
+ float32_t cutoff_;
+ float32_t resonance_;
+ float32_t g_;
+ float32_t w_;
+ float32_t a_;
+ float32_t b_;
+ float32_t c1_;
+ float32_t c2_;
+ float32_t d0_;
+ float32_t d1_;
+ float32_t z1_[StereoChannels::kNumChannels];
+ float32_t z2_[StereoChannels::kNumChannels];
+
+ 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, '|', "mode_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "gain_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "cutoff_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "resonance_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "g_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "w_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "a_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "b_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "c1_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "c2_");
+ 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, '+');
+ 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->mode_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->gain_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->cutoff_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->resonance_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->g_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->w_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->a_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->b_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->c1_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->c2_);
+ out << "\t" << ss.str() << std::endl;
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0u;
+
+ nb_errors += inspector(tag + "gain_", this->gain_, -1.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + "cutoff_", this->cutoff_, 1.0f, this->getSamplingRate() / 2.0f, deepInspection);
+ nb_errors += inspector(tag + "resonance_", this->resonance_, 0.005f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + "g_", this->g_, 0.0f, 16.0f, deepInspection);
+ nb_errors += inspector(tag + "w_", this->w_, 0.0f, 13.0f, deepInspection);
+ nb_errors += inspector(tag + "a_", this->a_, 0.0f, 2526.0f, deepInspection);
+ nb_errors += inspector(tag + "b_", this->b_, 0.0f, 160.0f, deepInspection);
+ nb_errors += inspector(tag + "c1_", this->c1_, 0.0f, 2.06f, deepInspection);
+ nb_errors += inspector(tag + "c2_", this->c2_, 0.0f, 0.06f, deepInspection);
+
+ return nb_errors;
+ )
+};
+
+class SVF : public FXElement
+{
+ DISALLOW_COPY_AND_ASSIGN(SVF);
+
+public:
+ enum FrequencyApproximation
+ {
+ FrequencyExact,
+ FrequencyAccurate,
+ FrequencyFast,
+ FrequencyDirty
+ };
+
+ enum FilterMode
+ {
+ SVF_LP,
+ SVF_BP,
+ SVF_BP_NORMALIZED,
+ SVF_HP
+ };
+
+ SVF(float32_t sampling_frequency, FilterMode mode = FilterMode::SVF_LP) :
+ FXElement(sampling_frequency),
+ Mode(mode),
+ g_(0.0f),
+ r_(0.0f),
+ h_(0.0f)
+ {
+ this->reset();
+ }
+
+ virtual ~SVF()
+ {
+ }
+
+ inline virtual void reset() override
+ {
+ memset(this->state1_, 0, StereoChannels::kNumChannels * sizeof(float32_t));
+ memset(this->state2_, 0, StereoChannels::kNumChannels * sizeof(float32_t));
+ }
+
+ virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override
+ {
+ float32_t hp, bp, lp;
+
+ {
+ hp = (inL - this->r_ * this->state1_[StereoChannels::Left ] - this->g_ * this->state1_[StereoChannels::Left ] - this->state2_[StereoChannels::Left ]) * this->h_;
+ bp = this->g_ * hp + this->state1_[StereoChannels::Left ];
+ this->state1_[StereoChannels::Left ] = this->g_ * hp + bp;
+ lp = this->g_ * bp + this->state2_[StereoChannels::Left ];
+ this->state2_[StereoChannels::Left ] = this->g_ * bp + lp;
+
+ switch(this->Mode)
+ {
+ case FilterMode::SVF_LP:
+ outL = lp;
+ break;
+
+ case FilterMode::SVF_BP:
+ outL = bp;
+ break;
+
+ case FilterMode::SVF_BP_NORMALIZED:
+ outL = bp * this->r_;
+ break;
+
+ case FilterMode::SVF_HP:
+ outL = hp;
+ break;
+ }
+ }
+
+ {
+ hp = (inR - this->r_ * this->state1_[StereoChannels::Right] - this->g_ * this->state1_[StereoChannels::Right] - this->state2_[StereoChannels::Right]) * this->h_;
+ bp = this->g_ * hp + this->state1_[StereoChannels::Right];
+ this->state1_[StereoChannels::Right] = this->g_ * hp + bp;
+ lp = this->g_ * bp + this->state2_[StereoChannels::Right];
+ this->state2_[StereoChannels::Right] = this->g_ * bp + lp;
+
+ switch(this->Mode)
+ {
+ case FilterMode::SVF_LP:
+ outR = lp;
+ break;
+
+ case FilterMode::SVF_BP:
+ outR = bp;
+ break;
+
+ case FilterMode::SVF_BP_NORMALIZED:
+ outR = bp * this->r_;
+ break;
+
+ case FilterMode::SVF_HP:
+ outR = hp;
+ break;
+ }
+ }
+ }
+
+ inline void setGRH(float32_t g, float32_t r, float32_t h)
+ {
+ this->g_ = g;
+ this->r_ = r;
+ this->h_ = h;
+ }
+
+ inline void setGR(float32_t g, float32_t r)
+ {
+ this->g_ = g;
+ this->r_ = r;
+ this->h_ = 1.0f / (1.0f + this->r_ * this->g_ * this->g_ * this->g_);
+ }
+
+ template
+ inline void setFQ(float32_t frequency, float32_t resonance)
+ {
+ this->g_ = SVF::tan(frequency);
+ this->r_ = 1.0f / resonance;
+ this->h_ = 1.0f / (1.0f + this->r_ * this->g_ * this->g_ * this->g_);
+ }
+
+private:
+ template
+ static inline float32_t tan(float32_t f)
+ {
+ switch(approximation)
+ {
+ case FrequencyApproximation::FrequencyExact:
+ {
+ // Clip coefficient to about 100.
+ f = constrain(f, 0.0f, 0.497f);
+ return ::tan(PI * f);
+ }
+
+ case FrequencyApproximation::FrequencyDirty:
+ {
+ // Optimized for frequencies below 8kHz.
+ const float32_t a = 3.736e-01 * Constants::M_PI_POW_3;
+ return f * (PI + a * f * f);
+ }
+
+ case FrequencyApproximation::FrequencyFast:
+ {
+ // The usual tangent approximation uses 3.1755e-01 and 2.033e-01, but
+ // the coefficients used here are optimized to minimize error for the
+ // 16Hz to 16kHz range, with a sample rate of 48kHz.
+ const float a = 3.260e-01 * Constants::M_PI_POW_3;
+ const float b = 1.823e-01 * Constants::M_PI_POW_5;
+ float f2 = f * f;
+ return f * (PI + f2 * (a + b * f2));
+ }
+
+ case FrequencyApproximation::FrequencyAccurate:
+ {
+ // These coefficients don't need to be tweaked for the audio range.
+ const float a = 3.333314036e-01 * Constants::M_PI_POW_3;
+ const float b = 1.333923995e-01 * Constants::M_PI_POW_5;
+ const float c = 5.33740603e-02 * Constants::M_PI_POW_7;
+ const float d = 2.900525e-03 * Constants::M_PI_POW_9;
+ const float e = 9.5168091e-03 * Constants::M_PI_POW_11;
+ float f2 = f * f;
+ return f * (PI + f2 * (a + f2 * (b + f2 * (c + f2 * (d + f2 * e)))));
+ }
+ }
+ }
+
+ const FilterMode Mode;
+ float32_t g_;
+ float32_t r_;
+ float32_t h_;
+
+ float32_t state1_[StereoChannels::kNumChannels];
+ float32_t state2_[StereoChannels::kNumChannels];
+
+ 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, '|', "g_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "r_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "h_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "state1_[ L ]");
+ SS__TEXT(ss, ' ', space, std::left, '|', "state1_[ R ]");
+ SS__TEXT(ss, ' ', space, std::left, '|', "state2_[ L ]");
+ SS__TEXT(ss, ' ', space, std::left, '|', "state2_[ R ]");
+ 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, '+');
+ 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->g_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->r_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->r_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->state1_[StereoChannels::Left ]);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->state1_[StereoChannels::Right]);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->state2_[StereoChannels::Left ]);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->state2_[StereoChannels::Right]);
+ out << "\t" << ss.str() << std::endl;
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0u;
+
+ nb_errors += inspector(tag + ".g_", this->g_, 0.0f, 106.11f, deepInspection);
+ nb_errors += inspector(tag + ".r_", this->r_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".h_", this->h_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".state1_[ L ]", this->state1_[StereoChannels::Left ], -1.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".state1_[ R ]", this->state1_[StereoChannels::Right], -1.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".state2_[ L ]", this->state2_[StereoChannels::Left ], -1.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".state2_[ R ]", this->state2_[StereoChannels::Right], -1.0f, 1.0f, deepInspection);
+
+ return nb_errors;
+ )
+};
diff --git a/src/fx_tube.cpp b/src/fx_tube.cpp
new file mode 100644
index 00000000..2b171d95
--- /dev/null
+++ b/src/fx_tube.cpp
@@ -0,0 +1,60 @@
+#include "fx_tube.h"
+
+#include
+
+Tube::Tube(float32_t samplingRate) :
+ FXElement(samplingRate),
+ overdrive_(1.0f),
+ saturator_factor_(1.0f),
+ gain_factor_(1.0f)
+{
+ this->setOverdrive(0.0f);
+}
+
+Tube::~Tube()
+{
+}
+
+void Tube::reset()
+{
+ // nothing to be done
+}
+
+void Tube::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
+{
+ if(inL == 0.0f)
+ {
+ outL = 0.0f;
+ }
+ else
+ {
+ outL = std::tanh(this->saturator_factor_ * inL) * this->gain_factor_;
+ }
+
+ 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 = 3.0f;
+
+ overdrive = constrain(overdrive, 0.0f, 1.0f);
+ if(this->overdrive_ != overdrive)
+ {
+ this->overdrive_ = overdrive;
+ this->saturator_factor_ = 1.0 + N * overdrive;
+ this->gain_factor_ = this->OutputLevelCorrector / std::tanh(this->saturator_factor_);
+ }
+}
+
+float32_t Tube::getOverdrive() const
+{
+ return this->overdrive_;
+}
diff --git a/src/fx_tube.h b/src/fx_tube.h
new file mode 100644
index 00000000..99a163dd
--- /dev/null
+++ b/src/fx_tube.h
@@ -0,0 +1,81 @@
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//
+// fx_tube.h
+//
+// Stereo Tube overdrive audio effects proposed in the context of the MiniDexed project
+// Author: Vincent Gauché
+//
+#pragma once
+
+#include "fx.h"
+
+class Tube : public FXElement
+{
+ DISALLOW_COPY_AND_ASSIGN(Tube);
+
+public:
+ Tube(float32_t sampling_rate);
+ virtual ~Tube();
+
+ virtual void reset() override;
+ virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
+
+ void setOverdrive(float32_t overdrive);
+ float32_t getOverdrive() const;
+
+private:
+ float32_t overdrive_;
+ float32_t saturator_factor_;
+ float32_t gain_factor_;
+
+ IMPLEMENT_DUMP(
+ const size_t space = 17;
+ const size_t precision = 5;
+
+ 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, '|', "overdrive_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "saturator_factor_");
+ SS__TEXT(ss, ' ', space, std::left, '|', "gain_factor_");
+ 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, '+');
+ out << "\t" << ss.str() << std::endl;
+
+ SS_RESET(ss, precision, std::left);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->overdrive_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->saturator_factor_);
+ SS__TEXT(ss, ' ', space - 1, std::right, " |", this->gain_factor_);
+ out << "\t" << ss.str() << std::endl;
+
+ out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
+ )
+
+ IMPLEMENT_INSPECT(
+ size_t nb_errors = 0;
+
+ nb_errors += inspector(tag + ".overdrive_", this->overdrive_, 0.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".saturator_factor_", this->saturator_factor_, 1.0f, 201.0f, deepInspection);
+ nb_errors += inspector(tag + ".gain_factor_", this->gain_factor_, 0.0f, 4.0f, deepInspection);
+
+ return nb_errors;
+ )
+};
diff --git a/src/fx_unit.hpp b/src/fx_unit.hpp
new file mode 100644
index 00000000..344cdc79
--- /dev/null
+++ b/src/fx_unit.hpp
@@ -0,0 +1,116 @@
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//
+// fx_unit.h
+//
+// Unit of FX that handle enable and wet parameters
+// Author: Vincent Gauché
+//
+#pragma once
+
+#include "fx_components.h"
+
+class FXUnitModule
+{
+ DISALLOW_COPY_AND_ASSIGN(FXUnitModule);
+
+public:
+ FXUnitModule(bool enable = true, float32_t wet_level = 0.5f)
+ {
+ this->setEnable(enable);
+ this->setWetLevel(wet_level);
+ }
+
+ virtual ~FXUnitModule()
+ {
+ }
+
+ virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) = 0;
+
+ void setEnable(bool enable = true)
+ {
+ this->enable_ = enable;
+ }
+
+ inline bool isEnable() const
+ {
+ return this->enable_;
+ }
+
+ void setWetLevel(float32_t wet_level)
+ {
+ this->wet_level_ = constrain(wet_level, 0.0f, 1.0f);
+ }
+
+ inline float32_t getWetLevel() const
+ {
+ return this->wet_level_;
+ }
+
+protected:
+ bool enable_;
+ float32_t wet_level_; // How much the signal is affected by the inner FX (0.0 - 1.0)
+};
+
+template
+class FXUnit : public virtual FXUnitModule, public virtual _FXElement
+{
+ DISALLOW_COPY_AND_ASSIGN(FXUnit);
+
+public:
+ FXUnit(float32_t sampling_rate, bool enable = true, float32_t wet_level = 0.5f) :
+ FXUnitModule(),
+ _FXElement(sampling_rate),
+ is_reset_(false)
+ {
+ this->setEnable(enable);
+ this->setWetLevel(wet_level);
+ }
+
+ virtual ~FXUnit()
+ {
+ }
+
+ void reset() override
+ {
+ if(!this->is_reset_)
+ {
+ _FXElement::reset();
+ this->is_reset_ = true;
+ }
+ }
+
+ void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override
+ {
+ if(!this->isEnable() || this->getWetLevel() == 0.0f)
+ {
+ this->reset();
+
+ outL = inL;
+ outR = inR;
+ }
+ else
+ {
+ this->is_reset_ = false;
+ _FXElement::processSample(inL, inR, outL, outR);
+
+ float32_t dry = 1.0f - this->getWetLevel();
+ outL = this->getWetLevel() * outL + dry * inL;
+ outR = this->getWetLevel() * outR + dry * inR;
+ }
+ }
+
+private:
+ bool is_reset_;
+};
\ No newline at end of file
diff --git a/src/fx_unit2.hpp b/src/fx_unit2.hpp
new file mode 100644
index 00000000..e26213c0
--- /dev/null
+++ b/src/fx_unit2.hpp
@@ -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 .
+
+//
+// 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
+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_;
+};
diff --git a/src/mididevice.cpp b/src/mididevice.cpp
index e1f983ca..371a070a 100644
--- a/src/mididevice.cpp
+++ b/src/mididevice.cpp
@@ -31,39 +31,45 @@
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
+#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
+
+#if defined(MIXING_CONSOLE_ENABLE)
+#define MIDI_CC_ORBITONE_LEVEL 92 // added with mixing console
+#define MIDI_CC_CHORUS_LEVEL 93 // added with mixing console
+#define MIDI_CC_PHASER_LEVEL 95 // added with mixing console
+#endif
+
+#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++)
{
@@ -304,9 +310,25 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
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)
{
diff --git a/src/minidexed.cpp b/src/minidexed.cpp
index fafeff81..fdc2ae27 100644
--- a/src/minidexed.cpp
+++ b/src/minidexed.cpp
@@ -30,9 +30,13 @@
LOGMODULE ("minidexed");
-CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
- CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster, FATFS *pFileSystem)
-:
+CMiniDexed::CMiniDexed (
+ CConfig *pConfig,
+ CInterruptSystem *pInterrupt,
+ CGPIOManager *pGPIOManager,
+ CI2CMaster *pI2CMaster,
+ FATFS *pFileSystem
+) :
#ifdef ARM_ALLOW_MULTI_CORE
CMultiCoreSupport (CMemorySystem::Get ()),
#endif
@@ -47,8 +51,9 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
#ifdef ARM_ALLOW_MULTI_CORE
m_nActiveTGsLog2 (0),
#endif
- m_GetChunkTimer ("GetChunk",
- 1000000U * pConfig->GetChunkSize ()/2 / pConfig->GetSampleRate ()),
+
+
+ m_GetChunkTimer ("GetChunk", 1000000U * pConfig->GetChunkSize ()/2 / pConfig->GetSampleRate ()),
m_bProfileEnabled (m_pConfig->GetProfileEnabled ()),
m_bSavePerformance (false),
m_bSavePerformanceNewFile (false),
@@ -79,16 +84,29 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
m_nNoteLimitHigh[i] = 127;
m_nNoteShift[i] = 0;
- m_nModulationWheelRange[i]=99;
- m_nModulationWheelTarget[i]=7;
- m_nFootControlRange[i]=99;
- m_nFootControlTarget[i]=0;
- m_nBreathControlRange[i]=99;
- m_nBreathControlTarget[i]=0;
- m_nAftertouchRange[i]=99;
- m_nAftertouchTarget[i]=0;
-
+
+
+
+
+
+
+
+
+
+ m_nModulationWheelRange[i] = 99;
+ m_nModulationWheelTarget[i] = 7;
+ m_nFootControlRange[i] = 99;
+ m_nFootControlTarget[i] = 0;
+ m_nBreathControlRange[i] = 99;
+ m_nBreathControlTarget[i] = 0;
+ m_nAftertouchRange[i] = 99;
+ m_nAftertouchTarget[i] = 0;
+
+#if defined(MIXING_CONSOLE_ENABLE)
+ memset(this->m_nTGSendLevel[i], 0, MixerOutput::kFXCount * sizeof(unsigned));
+#elif defined(PLATE_REVERB_ENABLE)
m_nReverbSend[i] = 0;
+#endif
m_uchOPMask[i] = 0b111111; // All operators on
m_pTG[i] = new CDexedAdapter (CConfig::MaxNotes, pConfig->GetSampleRate ());
@@ -97,6 +115,17 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
m_pTG[i]->activate ();
}
+#if defined(MIXING_CONSOLE_ENABLE)
+ for(size_t i = MixerOutput::FX_Tube; i < (MixerOutput::kFXCount - 1); ++i)
+ {
+ memset(this->m_nFXSendLevel[i], 0, MixerOutput::kFXCount * sizeof(unsigned));
+ }
+
+ this->m_nTGSendLevel[0][MixerOutput::MainOutput] = 99;
+ this->m_nTGSendLevel[0][MixerOutput::FX_PlateReverb] = 99;
+ this->m_nFXSendLevel[MixerOutput::FX_PlateReverb][MixerOutput::MainOutput] = 99;
+#endif
+
for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++)
{
m_pMIDIKeyboard[i] = new CMIDIKeyboard (this, pConfig, &m_UI, i);
@@ -139,7 +168,69 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
}
#endif
- setMasterVolume(1.0);
+ this->setMasterVolume(1.0);
+
+#if defined(MIXING_CONSOLE_ENABLE)
+ this->mixing_console_ = new Mixer(static_cast(pConfig->GetSampleRate()), CConfig::MaxChunkSize, this->m_bChannelsSwapped);
+ for (uint8_t i = 0; i < CConfig::ToneGenerators; i++)
+ {
+ memset(this->m_OutputLevel[i], 0, CConfig::MaxChunkSize * sizeof(float32_t));
+ this->mixing_console_->setInputSampleBuffer(i, this->m_OutputLevel[i]);
+ }
+
+ // Tube parameters
+ this->SetParameter(TParameter::ParameterFXTubeEnable, 1);
+ this->SetParameter(TParameter::ParameterFXTubeOverdrive, 25);
+
+ // Chorus parameters
+ this->SetParameter(TParameter::ParameterFXChorusEnable, 1);
+ this->SetParameter(TParameter::ParameterFXChorusRate, 50);
+ this->SetParameter(TParameter::ParameterFXChorusDepth, 50);
+
+ // Flanger parameters
+ this->SetParameter(TParameter::ParameterFXFlangerEnable, 1);
+ this->SetParameter(TParameter::ParameterFXFlangerRate, 3);
+ this->SetParameter(TParameter::ParameterFXFlangerDepth, 75);
+ this->SetParameter(TParameter::ParameterFXFlangerFeedback, 50);
+
+ // Orbitone parameters
+ this->SetParameter(TParameter::ParameterFXOrbitoneEnable, 1);
+ this->SetParameter(TParameter::ParameterFXOrbitoneRate, 40);
+ this->SetParameter(TParameter::ParameterFXOrbitoneDepth, 50);
+
+ // Phaser parameters
+ this->SetParameter(TParameter::ParameterFXPhaserEnable, 1);
+ this->SetParameter(TParameter::ParameterFXPhaserRate, 5);
+ this->SetParameter(TParameter::ParameterFXPhaserDepth, 99);
+ this->SetParameter(TParameter::ParameterFXPhaserFeedback, 50);
+ this->SetParameter(TParameter::ParameterFXPhaserNbStages, 12);
+
+ // Delay parameters
+ this->SetParameter(TParameter::ParameterFXDelayEnable, 1);
+ this->SetParameter(TParameter::ParameterFXDelayLeftDelayTime, 15);
+ this->SetParameter(TParameter::ParameterFXDelayRightDelayTime, 22);
+ this->SetParameter(TParameter::ParameterFXDelayFeedback, 35);
+
+ // AudioEffectPlateReverb parameters
+ this->SetParameter(TParameter::ParameterReverbEnable, 1);
+ this->SetParameter(TParameter::ParameterReverbSize, 70);
+ this->SetParameter(TParameter::ParameterReverbHighDamp, 50);
+ this->SetParameter(TParameter::ParameterReverbLowDamp, 50);
+ this->SetParameter(TParameter::ParameterReverbLowPass, 30);
+ this->SetParameter(TParameter::ParameterReverbDiffusion, 65);
+ this->SetParameter(TParameter::ParameterReverbLevel, 99);
+
+ // Reverberator parameters
+ this->SetParameter(TParameter::ParameterFXReverberatorEnable, 1);
+ this->SetParameter(TParameter::ParameterFXReverberatorInputGain, 99);
+ this->SetParameter(TParameter::ParameterFXReverberatorTime, 80);
+ this->SetParameter(TParameter::ParameterFXReverberatorDiffusion, 80);
+ this->SetParameter(TParameter::ParameterFXReverberatorLP, 70);
+
+ // Bypass
+ this->SetParameter(TParameter::ParameterFXBypass, 0);
+
+#elif defined(PLATE_REVERB_ENABLE)
// BEGIN setup tg_mixer
tg_mixer = new AudioStereoMixer(pConfig->GetChunkSize()/2);
@@ -148,16 +239,18 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
// BEGIN setup reverb
reverb_send_mixer = new AudioStereoMixer(pConfig->GetChunkSize()/2);
reverb = new AudioEffectPlateReverb(pConfig->GetSampleRate());
- SetParameter (ParameterReverbEnable, 1);
- SetParameter (ParameterReverbSize, 70);
- SetParameter (ParameterReverbHighDamp, 50);
- SetParameter (ParameterReverbLowDamp, 50);
- SetParameter (ParameterReverbLowPass, 30);
- SetParameter (ParameterReverbDiffusion, 65);
- SetParameter (ParameterReverbLevel, 99);
+ SetParameter (TParameter::ParameterReverbEnable, 1);
+ SetParameter (TParameter::ParameterReverbSize, 70);
+ SetParameter (TParameter::ParameterReverbHighDamp, 50);
+ SetParameter (TParameter::ParameterReverbLowDamp, 50);
+ SetParameter (TParameter::ParameterReverbLowPass, 30);
+ SetParameter (TParameter::ParameterReverbDiffusion, 65);
+ SetParameter (TParameter::ParameterReverbLevel, 99);
// END setup reverb
- SetParameter (ParameterCompressorEnable, 1);
+#endif
+
+ this->SetParameter (TParameter::ParameterCompressorEnable, 1);
};
bool CMiniDexed::Initialize (void)
@@ -195,10 +288,21 @@ bool CMiniDexed::Initialize (void)
m_pTG[i]->setBCController (99, 1, 0);
m_pTG[i]->setATController (99, 1, 0);
+#if defined(MIXING_CONSOLE_ENABLE)
+ this->mixing_console_->reset();
+ this->mixing_console_->setPan(i, this->m_nPan[i] / 127.0f);
+
+ this->mixing_console_->setSendLevel(i, MixerOutput::FX_PlateReverb, this->m_nTGSendLevel[i][MixerOutput::FX_PlateReverb] / 99.0f);
+ this->mixing_console_->setSendLevel(i, MixerOutput::MainOutput, this->m_nTGSendLevel[i][MixerOutput::MainOutput] / 99.0f);
+ this->mixing_console_->setFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::MainOutput, this->m_nFXSendLevel[MixerOutput::FX_PlateReverb][MixerOutput::FX_PlateReverb] / 99.0f);
+
+#elif defined(PLATE_REVERB_ENABLE)
+
tg_mixer->pan(i,mapfloat(m_nPan[i],0,127,0.0f,1.0f));
tg_mixer->gain(i,1.0f);
reverb_send_mixer->pan(i,mapfloat(m_nPan[i],0,127,0.0f,1.0f));
reverb_send_mixer->gain(i,mapfloat(m_nReverbSend[i],0,99,0.0f,1.0f));
+#endif
}
if (m_PerformanceConfig.Load ())
@@ -209,7 +313,7 @@ bool CMiniDexed::Initialize (void)
{
SetMIDIChannel (CMIDIDevice::OmniMode, 0);
}
-
+
// load performances file list, and attempt to create the performance folder
if (!m_PerformanceConfig.ListPerformances())
{
@@ -224,24 +328,26 @@ bool CMiniDexed::Initialize (void)
return false;
}
-#ifndef ARM_ALLOW_MULTI_CORE
- m_pSoundDevice->SetWriteFormat (SoundFormatSigned16, 1); // 16-bit Mono
-#else
+#if defined(ARM_ALLOW_MULTI_CORE)
+
+
m_pSoundDevice->SetWriteFormat (SoundFormatSigned16, 2); // 16-bit Stereo
+#else
+ m_pSoundDevice->SetWriteFormat (SoundFormatSigned16, 1); // 16-bit Mono
#endif
m_nQueueSizeFrames = m_pSoundDevice->GetQueueSizeFrames ();
m_pSoundDevice->Start ();
-#ifdef ARM_ALLOW_MULTI_CORE
+#if defined(ARM_ALLOW_MULTI_CORE)
// start secondary cores
if (!CMultiCoreSupport::Initialize ())
{
return false;
}
#endif
-
+
return true;
}
@@ -354,6 +460,10 @@ void CMiniDexed::Run (unsigned nCore)
{
assert (m_pTG[nTG]);
m_pTG[nTG]->getSamples (m_OutputLevel[nTG],m_nFramesToProcess);
+
+#if defined(MIXING_CONSOLE_ENABLE)
+ this->mixing_console_->preProcessInputSampleBuffer(nTG, this->m_nFramesToProcess);
+#endif
}
}
}
@@ -468,6 +578,10 @@ void CMiniDexed::SetVolume (unsigned nVolume, unsigned nTG)
assert (m_pTG[nTG]);
m_pTG[nTG]->setGain (nVolume / 127.0f);
+#if defined(MIXING_CONSOLE_ENABLE)
+ this->mixing_console_->setChannelLevel(nTG, nVolume == 0 ? 0.0f : 1.0f);
+#endif
+
m_UI.ParameterChanged ();
}
@@ -477,13 +591,52 @@ void CMiniDexed::SetPan (unsigned nPan, unsigned nTG)
assert (nTG < CConfig::ToneGenerators);
m_nPan[nTG] = nPan;
-
+
+#if defined(MIXING_CONSOLE_ENABLE)
+ this->mixing_console_->setPan(nTG, nPan / 127.0f);
+
+#elif defined(PLATE_REVERB_ENABLE)
+
tg_mixer->pan(nTG,mapfloat(nPan,0,127,0.0f,1.0f));
reverb_send_mixer->pan(nTG,mapfloat(nPan,0,127,0.0f,1.0f));
+#endif
m_UI.ParameterChanged ();
}
+#if defined(MIXING_CONSOLE_ENABLE)
+
+unsigned CMiniDexed::getMixingConsoleSendLevel(unsigned nTG, MixerOutput fx) const
+{
+ assert (nTG < CConfig::ToneGenerators);
+ return this->m_nTGSendLevel[nTG][fx];
+}
+
+void CMiniDexed::setMixingConsoleSendLevel(unsigned nTG, MixerOutput fx, unsigned nFXSend)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ nFXSend = constrain((int)nFXSend, 0, 99);
+
+ this->m_nTGSendLevel[nTG][fx] = nFXSend;
+ this->mixing_console_->setSendLevel(nTG, fx, nFXSend / 99.0f);
+
+ this->m_UI.ParameterChanged();
+}
+
+void CMiniDexed::setMixingConsoleFXSendLevel(MixerOutput fromFX, MixerOutput toFX, unsigned nFXSend)
+{
+ assert(fromFX < (MixerOutput::kFXCount - 1));
+ assert(toFX < MixerOutput::kFXCount);
+ if(fromFX != toFX)
+ {
+ nFXSend = constrain((int)nFXSend, 0, 99);
+ this->m_nFXSendLevel[fromFX][toFX] = nFXSend;
+ this->mixing_console_->setFXSendLevel(fromFX, toFX, nFXSend / 99.0f);
+ }
+}
+
+#elif defined(PLATE_REVERB_ENABLE)
+
void CMiniDexed::SetReverbSend (unsigned nReverbSend, unsigned nTG)
{
nReverbSend=constrain((int)nReverbSend,0,99);
@@ -496,6 +649,8 @@ void CMiniDexed::SetReverbSend (unsigned nReverbSend, unsigned nTG)
m_UI.ParameterChanged ();
}
+#endif
+
void CMiniDexed::SetMasterTune (int nMasterTune, unsigned nTG)
{
nMasterTune=constrain((int)nMasterTune,-99,99);
@@ -690,70 +845,689 @@ void CMiniDexed::ControllersRefresh (unsigned nTG)
void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
{
- assert (reverb);
+
- assert (Parameter < ParameterUnknown);
+ assert(Parameter < TParameter::ParameterUnknown);
+
m_nParameter[Parameter] = nValue;
switch (Parameter)
{
- case ParameterCompressorEnable:
- for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
+ case TParameter::ParameterCompressorEnable:
+ for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; ++nTG)
{
- assert (m_pTG[nTG]);
+ assert(m_pTG[nTG]);
m_pTG[nTG]->setCompressor (!!nValue);
}
break;
- case ParameterReverbEnable:
+#if defined(MIXING_CONSOLE_ENABLE)
+
+ // Tube parameters
+ case TParameter::ParameterFXTubeEnable:
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getTube()->setMute(!!!nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXTubeOverdrive:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getTube()->setOverdrive(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+
+ // Chorus parameters
+ case TParameter::ParameterFXChorusEnable:
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getChorus()->setMute(!!!nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXChorusRate:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getChorus()->setRate(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXChorusDepth:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getChorus()->setDepth(nValue / 9.9f);
+ this->m_FXSpinLock.Release();
+ break;
+
+ // Flanger parameters
+ case TParameter::ParameterFXFlangerEnable:
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getFlanger()->setMute(!!!nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXFlangerRate:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getFlanger()->setRate(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXFlangerDepth:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getFlanger()->setDepth(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXFlangerFeedback:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getFlanger()->setFeedback(mapfloat(nValue, 0, 99, 0.0f, 0.97f));
+ this->m_FXSpinLock.Release();
+ break;
+
+ // Orbitone parameters
+ case TParameter::ParameterFXOrbitoneEnable:
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getOrbitone()->setMute(!!!nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXOrbitoneRate:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getOrbitone()->setRate(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXOrbitoneDepth:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getOrbitone()->setDepth(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+
+ // Phaser parameters
+ case TParameter::ParameterFXPhaserEnable:
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getPhaser()->setMute(!!!nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPhaserRate:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getPhaser()->setRate(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPhaserDepth:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getPhaser()->setDepth(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPhaserFeedback:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getPhaser()->setFeedback(mapfloat(nValue, 0, 99, 0.0f, 0.97f));
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPhaserNbStages:
+ nValue = constrain((int)nValue, 2, MAX_NB_PHASES);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getPhaser()->setNbStages(nValue);
+ this->m_FXSpinLock.Release();
+ break;
+
+ // Delay parameters
+ case TParameter::ParameterFXDelayEnable:
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getDelay()->setMute(!!!nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXDelayLeftDelayTime:
+ nValue = constrain((int)nValue, -99, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getDelay()->setLeftDelayTime(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXDelayRightDelayTime:
+ nValue = constrain((int)nValue, -99, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getDelay()->setRightDelayTime(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXDelayFeedback:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getDelay()->setFeedback(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+
+ // AudioEffectPlateReverb parameters
+ case TParameter::ParameterReverbEnable:
+ this->m_FXSpinLock.Acquire ();
+ this->mixing_console_->getPlateReverb()->set_bypass (!!!nValue);
+ this->m_FXSpinLock.Release ();
+ break;
+ case TParameter::ParameterReverbSize:
+ nValue=constrain((int)nValue,0,99);
+ this->m_FXSpinLock.Acquire ();
+ this->mixing_console_->getPlateReverb()->size (nValue / 99.0f);
+ this->m_FXSpinLock.Release ();
+ break;
+ case TParameter::ParameterReverbHighDamp:
+ nValue=constrain((int)nValue,0,99);
+ this->m_FXSpinLock.Acquire ();
+ this->mixing_console_->getPlateReverb()->hidamp (nValue / 99.0f);
+ this->m_FXSpinLock.Release ();
+ break;
+ case TParameter::ParameterReverbLowDamp:
+ nValue=constrain((int)nValue,0,99);
+ this->m_FXSpinLock.Acquire ();
+ this->mixing_console_->getPlateReverb()->lodamp (nValue / 99.0f);
+ this->m_FXSpinLock.Release ();
+ break;
+ case TParameter::ParameterReverbLowPass:
+ nValue=constrain((int)nValue,0,99);
+ this->m_FXSpinLock.Acquire ();
+ this->mixing_console_->getPlateReverb()->lowpass (nValue / 99.0f);
+ this->m_FXSpinLock.Release ();
+ break;
+ case TParameter::ParameterReverbDiffusion:
+ nValue=constrain((int)nValue,0,99);
+ this->m_FXSpinLock.Acquire ();
+ this->mixing_console_->getPlateReverb()->diffusion (nValue / 99.0f);
+ this->m_FXSpinLock.Release ();
+ break;
+ case TParameter::ParameterReverbLevel:
+ nValue=constrain((int)nValue,0,99);
+ this->m_FXSpinLock.Acquire ();
+ this->mixing_console_->getPlateReverb()->level (nValue / 99.0f);
+ this->m_FXSpinLock.Release ();
+ break;
+
+ // Reverberator parameters
+ case TParameter::ParameterFXReverberatorEnable:
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getReverberator()->setMute(!!!nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXReverberatorInputGain:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getReverberator()->setInputGain(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXReverberatorTime:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getReverberator()->setTime(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXReverberatorDiffusion:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getReverberator()->setDiffusion(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXReverberatorLP:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->getReverberator()->setLP(nValue / 99.0f);
+ this->m_FXSpinLock.Release();
+ break;
+
+ // Tube Send parameters
+ case TParameter::ParameterFXTube_ChorusSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Chorus, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXTube_FlangerSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Flanger, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXTube_OrbitoneSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Orbitone, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXTube_PhaserSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Phaser, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXTube_DelaySend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Delay, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXTube_PlateReverbSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_PlateReverb, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXTube_ReverberatorSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Reverberator, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXTube_MainOutput:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+
+ // Chorus Send parameters
+ case TParameter::ParameterFXChorus_TubeSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Tube, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXChorus_FlangerSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Flanger, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXChorus_OrbitoneSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Orbitone, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXChorus_PhaserSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Phaser, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXChorus_DelaySend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Delay, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXChorus_PlateReverbSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_PlateReverb, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXChorus_ReverberatorSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Reverberator, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXChorus_MainOutput:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::MainOutput, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+
+ // Flanger Send parameters
+ case TParameter::ParameterFXFlanger_TubeSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Tube, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXFlanger_ChorusSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Chorus, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXFlanger_OrbitoneSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Orbitone, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXFlanger_PhaserSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Phaser, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXFlanger_DelaySend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Delay, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXFlanger_PlateReverbSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_PlateReverb, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXFlanger_ReverberatorSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Reverberator, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXFlanger_MainOutput:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::MainOutput, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+
+ // Orbitone Send parameters
+ case TParameter::ParameterFXOrbitone_TubeSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Tube, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXOrbitone_ChorusSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Chorus, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXOrbitone_FlangerSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Flanger, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXOrbitone_PhaserSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Phaser, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXOrbitone_DelaySend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Delay, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXOrbitone_PlateReverbSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_PlateReverb, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXOrbitone_ReverberatorSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Reverberator, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXOrbitone_MainOutput:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::MainOutput, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+
+ // Phaser Send parameters
+ case TParameter::ParameterFXPhaser_TubeSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Tube, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPhaser_ChorusSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Chorus, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPhaser_FlangerSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Flanger, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPhaser_OrbitoneSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Orbitone, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPhaser_DelaySend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Delay, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPhaser_PlateReverbSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_PlateReverb, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPhaser_ReverberatorSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Reverberator, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPhaser_MainOutput:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::MainOutput, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+
+ // Delay Send parameters
+ case TParameter::ParameterFXDelay_TubeSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Tube, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXDelay_ChorusSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Chorus, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXDelay_FlangerSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Flanger, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXDelay_OrbitoneSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Orbitone, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXDelay_PhaserSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Phaser, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXDelay_PlateReverbSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_PlateReverb, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXDelay_ReverberatorSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Reverberator, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXDelay_MainOutput:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Delay, MixerOutput::MainOutput, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+
+ // Reverb Send parameters
+ case TParameter::ParameterFXPlateReverb_TubeSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Tube, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPlateReverb_ChorusSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Chorus, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPlateReverb_FlangerSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Flanger, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPlateReverb_OrbitoneSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Orbitone, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPlateReverb_PhaserSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Phaser, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPlateReverb_DelaySend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Delay, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPlateReverb_ReverberatorSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Reverberator, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXPlateReverb_MainOutput:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::MainOutput, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+
+ // Reverberator Send parameters
+ case TParameter::ParameterFXReverberator_TubeSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Tube, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXReverberator_ChorusSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Chorus, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXReverberator_FlangerSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Flanger, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXReverberator_OrbitoneSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Orbitone, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXReverberator_PhaserSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Phaser, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXReverberator_DelaySend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Delay, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXReverberator_PlateReverbSend:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_PlateReverb, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+ case TParameter::ParameterFXReverberator_MainOutput:
+ nValue = constrain((int)nValue, 0, 99);
+ this->m_FXSpinLock.Acquire();
+ this->setMixingConsoleFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, nValue);
+ this->m_FXSpinLock.Release();
+ break;
+
+ case TParameter::ParameterFXBypass:
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->bypass(!!nValue);
+ this->m_FXSpinLock.Release();
+ break;
+
+#elif defined(PLATE_REVERB_ENABLE)
+
+ case TParameter::ParameterReverbEnable:
nValue=constrain((int)nValue,0,1);
- m_ReverbSpinLock.Acquire ();
+ m_FXSpinLock.Acquire ();
reverb->set_bypass (!nValue);
- m_ReverbSpinLock.Release ();
+ m_FXSpinLock.Release ();
break;
- case ParameterReverbSize:
+ case TParameter::ParameterReverbSize:
nValue=constrain((int)nValue,0,99);
- m_ReverbSpinLock.Acquire ();
+ m_FXSpinLock.Acquire ();
reverb->size (nValue / 99.0f);
- m_ReverbSpinLock.Release ();
+ m_FXSpinLock.Release ();
break;
- case ParameterReverbHighDamp:
+ case TParameter::ParameterReverbHighDamp:
nValue=constrain((int)nValue,0,99);
- m_ReverbSpinLock.Acquire ();
+ m_FXSpinLock.Acquire ();
reverb->hidamp (nValue / 99.0f);
- m_ReverbSpinLock.Release ();
+ m_FXSpinLock.Release ();
break;
- case ParameterReverbLowDamp:
+ case TParameter::ParameterReverbLowDamp:
nValue=constrain((int)nValue,0,99);
- m_ReverbSpinLock.Acquire ();
+ m_FXSpinLock.Acquire ();
reverb->lodamp (nValue / 99.0f);
- m_ReverbSpinLock.Release ();
+ m_FXSpinLock.Release ();
break;
- case ParameterReverbLowPass:
+ case TParameter::ParameterReverbLowPass:
nValue=constrain((int)nValue,0,99);
- m_ReverbSpinLock.Acquire ();
+ m_FXSpinLock.Acquire ();
reverb->lowpass (nValue / 99.0f);
- m_ReverbSpinLock.Release ();
+ m_FXSpinLock.Release ();
break;
- case ParameterReverbDiffusion:
+ case TParameter::ParameterReverbDiffusion:
nValue=constrain((int)nValue,0,99);
- m_ReverbSpinLock.Acquire ();
+ m_FXSpinLock.Acquire ();
reverb->diffusion (nValue / 99.0f);
- m_ReverbSpinLock.Release ();
+ m_FXSpinLock.Release ();
break;
- case ParameterReverbLevel:
+ case TParameter::ParameterReverbLevel:
nValue=constrain((int)nValue,0,99);
- m_ReverbSpinLock.Acquire ();
+ m_FXSpinLock.Acquire ();
reverb->level (nValue / 99.0f);
- m_ReverbSpinLock.Release ();
+ m_FXSpinLock.Release ();
break;
+#endif
+
default:
assert (0);
break;
@@ -762,7 +1536,8 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
int CMiniDexed::GetParameter (TParameter Parameter)
{
- assert (Parameter < ParameterUnknown);
+ assert(Parameter < TParameter::ParameterUnknown);
+
return m_nParameter[Parameter];
}
@@ -772,53 +1547,67 @@ void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nT
switch (Parameter)
{
- case TGParameterVoiceBank: BankSelect (nValue, nTG); break;
- case TGParameterVoiceBankMSB: BankSelectMSB (nValue, nTG); break;
- case TGParameterVoiceBankLSB: BankSelectLSB (nValue, nTG); break;
- case TGParameterProgram: ProgramChange (nValue, nTG); break;
- case TGParameterVolume: SetVolume (nValue, nTG); break;
- case TGParameterPan: SetPan (nValue, nTG); break;
- case TGParameterMasterTune: SetMasterTune (nValue, nTG); break;
- case TGParameterCutoff: SetCutoff (nValue, nTG); break;
- case TGParameterResonance: SetResonance (nValue, nTG); break;
- case TGParameterPitchBendRange: setPitchbendRange (nValue, nTG); break;
- case TGParameterPitchBendStep: setPitchbendStep (nValue, nTG); break;
- case TGParameterPortamentoMode: setPortamentoMode (nValue, nTG); break;
- case TGParameterPortamentoGlissando: setPortamentoGlissando (nValue, nTG); break;
- case TGParameterPortamentoTime: setPortamentoTime (nValue, nTG); break;
- case TGParameterMonoMode: setMonoMode (nValue , nTG); break;
+ case TTGParameter::TGParameterVoiceBank: this->BankSelect (nValue, nTG); break;
+ case TTGParameter::TGParameterVoiceBankMSB: this->BankSelectMSB (nValue, nTG); break;
+ case TTGParameter::TGParameterVoiceBankLSB: this->BankSelectLSB (nValue, nTG); break;
+ case TTGParameter::TGParameterProgram: this->ProgramChange (nValue, nTG); break;
+ case TTGParameter::TGParameterVolume: this->SetVolume (nValue, nTG); break;
+ case TTGParameter::TGParameterPan: this->SetPan (nValue, nTG); break;
+ case TTGParameter::TGParameterMasterTune: this->SetMasterTune (nValue, nTG); break;
+ case TTGParameter::TGParameterCutoff: this->SetCutoff (nValue, nTG); break;
+ case TTGParameter::TGParameterResonance: this->SetResonance (nValue, nTG); break;
+ case TTGParameter::TGParameterPitchBendRange: this->setPitchbendRange (nValue, nTG); break;
+ case TTGParameter::TGParameterPitchBendStep: this->setPitchbendStep (nValue, nTG); break;
+ case TTGParameter::TGParameterPortamentoMode: this->setPortamentoMode (nValue, nTG); break;
+ case TTGParameter::TGParameterPortamentoGlissando: this->setPortamentoGlissando (nValue, nTG); break;
+ case TTGParameter::TGParameterPortamentoTime: this->setPortamentoTime (nValue, nTG); break;
+ case TTGParameter::TGParameterMonoMode: this->setMonoMode (nValue , nTG); break;
- case TGParameterMWRange: setModController(0, 0, nValue, nTG); break;
- case TGParameterMWPitch: setModController(0, 1, nValue, nTG); break;
- case TGParameterMWAmplitude: setModController(0, 2, nValue, nTG); break;
- case TGParameterMWEGBias: setModController(0, 3, nValue, nTG); break;
+ case TTGParameter::TGParameterMWRange: this->setModController(0, 0, nValue, nTG); break;
+ case TTGParameter::TGParameterMWPitch: this->setModController(0, 1, nValue, nTG); break;
+ case TTGParameter::TGParameterMWAmplitude: this->setModController(0, 2, nValue, nTG); break;
+ case TTGParameter::TGParameterMWEGBias: this->setModController(0, 3, nValue, nTG); break;
- case TGParameterFCRange: setModController(1, 0, nValue, nTG); break;
- case TGParameterFCPitch: setModController(1, 1, nValue, nTG); break;
- case TGParameterFCAmplitude: setModController(1, 2, nValue, nTG); break;
- case TGParameterFCEGBias: setModController(1, 3, nValue, nTG); break;
+ case TTGParameter::TGParameterFCRange: this->setModController(1, 0, nValue, nTG); break;
+ case TTGParameter::TGParameterFCPitch: this->setModController(1, 1, nValue, nTG); break;
+ case TTGParameter::TGParameterFCAmplitude: this->setModController(1, 2, nValue, nTG); break;
+ case TTGParameter::TGParameterFCEGBias: this->setModController(1, 3, nValue, nTG); break;
- case TGParameterBCRange: setModController(2, 0, nValue, nTG); break;
- case TGParameterBCPitch: setModController(2, 1, nValue, nTG); break;
- case TGParameterBCAmplitude: setModController(2, 2, nValue, nTG); break;
- case TGParameterBCEGBias: setModController(2, 3, nValue, nTG); break;
+ case TTGParameter::TGParameterBCRange: this->setModController(2, 0, nValue, nTG); break;
+ case TTGParameter::TGParameterBCPitch: this->setModController(2, 1, nValue, nTG); break;
+ case TTGParameter::TGParameterBCAmplitude: this->setModController(2, 2, nValue, nTG); break;
+ case TTGParameter::TGParameterBCEGBias: this->setModController(2, 3, nValue, nTG); break;
- case TGParameterATRange: setModController(3, 0, nValue, nTG); break;
- case TGParameterATPitch: setModController(3, 1, nValue, nTG); break;
- case TGParameterATAmplitude: setModController(3, 2, nValue, nTG); break;
- case TGParameterATEGBias: setModController(3, 3, nValue, nTG); break;
+ case TTGParameter::TGParameterATRange: this->setModController(3, 0, nValue, nTG); break;
+ case TTGParameter::TGParameterATPitch: this->setModController(3, 1, nValue, nTG); break;
+ case TTGParameter::TGParameterATAmplitude: this->setModController(3, 2, nValue, nTG); break;
+ case TTGParameter::TGParameterATEGBias: this->setModController(3, 3, nValue, nTG); break;
- case TGParameterMIDIChannel:
+ case TTGParameter::TGParameterMIDIChannel:
assert (0 <= nValue && nValue <= 255);
SetMIDIChannel ((uint8_t) nValue, nTG);
break;
- case TGParameterReverbSend: SetReverbSend (nValue, nTG); break;
+#if defined(MIXING_CONSOLE_ENABLE)
+ case TTGParameter::TGParameterMixingSendFXTube: this->setMixingConsoleSendLevel(nTG, MixerOutput::FX_Tube, nValue); break;
+ case TTGParameter::TGParameterMixingSendFXChorus: this->setMixingConsoleSendLevel(nTG, MixerOutput::FX_Chorus, nValue); break;
+ case TTGParameter::TGParameterMixingSendFXFlanger: this->setMixingConsoleSendLevel(nTG, MixerOutput::FX_Flanger, nValue); break;
+ case TTGParameter::TGParameterMixingSendFXOrbitone: this->setMixingConsoleSendLevel(nTG, MixerOutput::FX_Orbitone, nValue); break;
+ case TTGParameter::TGParameterMixingSendFXPhaser: this->setMixingConsoleSendLevel(nTG, MixerOutput::FX_Phaser, nValue); break;
+ case TTGParameter::TGParameterMixingSendFXDelay: this->setMixingConsoleSendLevel(nTG, MixerOutput::FX_Delay, nValue); break;
+ case TTGParameter::TGParameterMixingSendFXPlateReverb: this->setMixingConsoleSendLevel(nTG, MixerOutput::FX_PlateReverb, nValue); break;
+ case TTGParameter::TGParameterMixingSendFXReverberator: this->setMixingConsoleSendLevel(nTG, MixerOutput::FX_Reverberator, nValue); break;
+ case TTGParameter::TGParameterMixingSendFXMainOutput: this->setMixingConsoleSendLevel(nTG, MixerOutput::MainOutput, nValue); break;
+#elif defined(PLATE_REVERB_ENABLE)
+ case TTGParameter::TGParameterReverbSend: this->SetReverbSend (nValue, nTG); break;
+#endif // MIXING_CONSOLE_ENABLE
default:
assert (0);
break;
}
+
+ this->m_UI.ParameterChanged();
}
int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG)
@@ -827,45 +1616,56 @@ int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG)
switch (Parameter)
{
- case TGParameterVoiceBank: return m_nVoiceBankID[nTG];
- case TGParameterVoiceBankMSB: return m_nVoiceBankID[nTG] >> 7;
- case TGParameterVoiceBankLSB: return m_nVoiceBankID[nTG] & 0x7F;
- case TGParameterProgram: return m_nProgram[nTG];
- case TGParameterVolume: return m_nVolume[nTG];
- case TGParameterPan: return m_nPan[nTG];
- case TGParameterMasterTune: return m_nMasterTune[nTG];
- case TGParameterCutoff: return m_nCutoff[nTG];
- case TGParameterResonance: return m_nResonance[nTG];
- case TGParameterMIDIChannel: return m_nMIDIChannel[nTG];
- case TGParameterReverbSend: return m_nReverbSend[nTG];
- case TGParameterPitchBendRange: return m_nPitchBendRange[nTG];
- case TGParameterPitchBendStep: return m_nPitchBendStep[nTG];
- case TGParameterPortamentoMode: return m_nPortamentoMode[nTG];
- case TGParameterPortamentoGlissando: return m_nPortamentoGlissando[nTG];
- case TGParameterPortamentoTime: return m_nPortamentoTime[nTG];
- case TGParameterMonoMode: return m_bMonoMode[nTG] ? 1 : 0;
-
- case TGParameterMWRange: return getModController(0, 0, nTG);
- case TGParameterMWPitch: return getModController(0, 1, nTG);
- case TGParameterMWAmplitude: return getModController(0, 2, nTG);
- case TGParameterMWEGBias: return getModController(0, 3, nTG);
-
- case TGParameterFCRange: return getModController(1, 0, nTG);
- case TGParameterFCPitch: return getModController(1, 1, nTG);
- case TGParameterFCAmplitude: return getModController(1, 2, nTG);
- case TGParameterFCEGBias: return getModController(1, 3, nTG);
+ case TTGParameter::TGParameterVoiceBank: return m_nVoiceBankID[nTG];
+ case TTGParameter::TGParameterVoiceBankMSB: return m_nVoiceBankID[nTG] >> 7;
+ case TTGParameter::TGParameterVoiceBankLSB: return m_nVoiceBankID[nTG] & 0x7F;
+ case TTGParameter::TGParameterProgram: return m_nProgram[nTG];
+ case TTGParameter::TGParameterVolume: return m_nVolume[nTG];
+ case TTGParameter::TGParameterPan: return m_nPan[nTG];
+ case TTGParameter::TGParameterMasterTune: return m_nMasterTune[nTG];
+ case TTGParameter::TGParameterCutoff: return m_nCutoff[nTG];
+ case TTGParameter::TGParameterResonance: return m_nResonance[nTG];
+ case TTGParameter::TGParameterMIDIChannel: return m_nMIDIChannel[nTG];
+#if defined(MIXING_CONSOLE_ENABLE)
+ case TTGParameter::TGParameterMixingSendFXTube: return this->getMixingConsoleSendLevel(nTG, MixerOutput::FX_Tube);
+ case TTGParameter::TGParameterMixingSendFXChorus: return this->getMixingConsoleSendLevel(nTG, MixerOutput::FX_Chorus);
+ case TTGParameter::TGParameterMixingSendFXFlanger: return this->getMixingConsoleSendLevel(nTG, MixerOutput::FX_Flanger);
+ case TTGParameter::TGParameterMixingSendFXOrbitone: return this->getMixingConsoleSendLevel(nTG, MixerOutput::FX_Orbitone);
+ case TTGParameter::TGParameterMixingSendFXPhaser: return this->getMixingConsoleSendLevel(nTG, MixerOutput::FX_Phaser);
+ case TTGParameter::TGParameterMixingSendFXDelay: return this->getMixingConsoleSendLevel(nTG, MixerOutput::FX_Delay);
+ case TTGParameter::TGParameterMixingSendFXPlateReverb: return this->getMixingConsoleSendLevel(nTG, MixerOutput::FX_PlateReverb);
+ case TTGParameter::TGParameterMixingSendFXReverberator: return this->getMixingConsoleSendLevel(nTG, MixerOutput::FX_Reverberator);
+ case TTGParameter::TGParameterMixingSendFXMainOutput: return this->getMixingConsoleSendLevel(nTG, MixerOutput::MainOutput);
+#elif defined(PLATE_REVERB_ENABLE)
+ case TTGParameter::TGParameterReverbSend: return m_nReverbSend[nTG];
+#endif
+ case TTGParameter::TGParameterPitchBendRange: return m_nPitchBendRange[nTG];
+ case TTGParameter::TGParameterPitchBendStep: return m_nPitchBendStep[nTG];
+ case TTGParameter::TGParameterPortamentoMode: return m_nPortamentoMode[nTG];
+ case TTGParameter::TGParameterPortamentoGlissando: return m_nPortamentoGlissando[nTG];
+ case TTGParameter::TGParameterPortamentoTime: return m_nPortamentoTime[nTG];
+ case TTGParameter::TGParameterMonoMode: return m_bMonoMode[nTG] ? 1 : 0;
- case TGParameterBCRange: return getModController(2, 0, nTG);
- case TGParameterBCPitch: return getModController(2, 1, nTG);
- case TGParameterBCAmplitude: return getModController(2, 2, nTG);
- case TGParameterBCEGBias: return getModController(2, 3, nTG);
+ case TTGParameter::TGParameterMWRange: return getModController(0, 0, nTG);
+ case TTGParameter::TGParameterMWPitch: return getModController(0, 1, nTG);
+ case TTGParameter::TGParameterMWAmplitude: return getModController(0, 2, nTG);
+ case TTGParameter::TGParameterMWEGBias: return getModController(0, 3, nTG);
- case TGParameterATRange: return getModController(3, 0, nTG);
- case TGParameterATPitch: return getModController(3, 1, nTG);
- case TGParameterATAmplitude: return getModController(3, 2, nTG);
- case TGParameterATEGBias: return getModController(3, 3, nTG);
+ case TTGParameter::TGParameterFCRange: return getModController(1, 0, nTG);
+ case TTGParameter::TGParameterFCPitch: return getModController(1, 1, nTG);
+ case TTGParameter::TGParameterFCAmplitude: return getModController(1, 2, nTG);
+ case TTGParameter::TGParameterFCEGBias: return getModController(1, 3, nTG);
+ case TTGParameter::TGParameterBCRange: return getModController(2, 0, nTG);
+ case TTGParameter::TGParameterBCPitch: return getModController(2, 1, nTG);
+ case TTGParameter::TGParameterBCAmplitude: return getModController(2, 2, nTG);
+ case TTGParameter::TGParameterBCEGBias: return getModController(2, 3, nTG);
+ case TTGParameter::TGParameterATRange: return getModController(3, 0, nTG);
+ case TTGParameter::TGParameterATPitch: return getModController(3, 1, nTG);
+ case TTGParameter::TGParameterATAmplitude: return getModController(3, 2, nTG);
+ case TTGParameter::TGParameterATEGBias: return getModController(3, 3, nTG);
+
default:
assert (0);
return 0;
@@ -1003,6 +1803,10 @@ void CMiniDexed::ProcessSound (void)
{
assert (m_pTG[i]);
m_pTG[i]->getSamples (m_OutputLevel[i], nFrames);
+
+#if defined(MIXING_CONSOLE_ENABLE)
+ this->mixing_console_->preProcessInputSampleBuffer(i, nFrames);
+#endif
}
// wait for cores 2 and 3 to complete their work
@@ -1016,17 +1820,40 @@ void CMiniDexed::ProcessSound (void)
//
// Audio signal path after tone generators starts here
- //
+ int16_t tmp_int[nFrames * 2];
- assert (CConfig::ToneGenerators == 8);
+#if defined(MIXING_CONSOLE_ENABLE)
+ // BEGIN mixing
+ if(this->nMasterVolume > 0.0f)
+ {
+ // temp buffering and channel indexing
+ float32_t interlacedSampleBuffer[nFrames << 1];
+
+ this->m_FXSpinLock.Acquire();
+ this->mixing_console_->process(interlacedSampleBuffer);
+ this->m_FXSpinLock.Release();
+
+ if(this->nMasterVolume < 1.0f)
+ {
+ arm_scale_f32(interlacedSampleBuffer, this->nMasterVolume, interlacedSampleBuffer, nFrames << 1);
+ }
+
+ // Convert float array (left, right) to single int16 array (left/right)
+ arm_float_to_q15(interlacedSampleBuffer, tmp_int, nFrames << 1);
+ }
+ else // this->nMasterVolume == 0.0f
+ {
+ arm_fill_q15(0, tmp_int, nFrames << 1);
+ }
+
+#elif defined(PLATE_REVERB_ENABLE)
uint8_t indexL=0, indexR=1;
// BEGIN TG mixing
float32_t tmp_float[nFrames*2];
- int16_t tmp_int[nFrames*2];
- if(nMasterVolume > 0.0)
+ if(nMasterVolume > 0.0f)
{
for (uint8_t i = 0; i < CConfig::ToneGenerators; i++)
{
@@ -1053,9 +1880,9 @@ void CMiniDexed::ProcessSound (void)
arm_fill_f32(0.0f, ReverbSendBuffer[indexR], nFrames);
arm_fill_f32(0.0f, ReverbSendBuffer[indexL], nFrames);
- m_ReverbSpinLock.Acquire ();
+ m_FXSpinLock.Acquire ();
- reverb_send_mixer->getMix(ReverbSendBuffer[indexL], ReverbSendBuffer[indexR]);
+ reverb_send_mixer->getMix(ReverbSendBuffer[indexL], ReverbSendBuffer[indexR]);
reverb->doReverb(ReverbSendBuffer[indexL],ReverbSendBuffer[indexR],ReverbBuffer[indexL], ReverbBuffer[indexR],nFrames);
// scale down and add left reverb buffer by reverb level
@@ -1065,7 +1892,7 @@ void CMiniDexed::ProcessSound (void)
arm_scale_f32(ReverbBuffer[indexR], reverb->get_level(), ReverbBuffer[indexR], nFrames);
arm_add_f32(SampleBuffer[indexR], ReverbBuffer[indexR], SampleBuffer[indexR], nFrames);
- m_ReverbSpinLock.Release ();
+ m_FXSpinLock.Release ();
}
// END adding reverb
@@ -1094,15 +1921,16 @@ void CMiniDexed::ProcessSound (void)
}
else
arm_fill_q15(0, tmp_int, nFrames * 2);
+#endif
- if (m_pSoundDevice->Write (tmp_int, sizeof(tmp_int)) != (int) sizeof(tmp_int))
+ if(this->m_pSoundDevice->Write(tmp_int, sizeof(tmp_int)) != (int)sizeof(tmp_int))
{
LOGERR ("Sound data dropped");
}
- if (m_bProfileEnabled)
+ if(this->m_bProfileEnabled)
{
- m_GetChunkTimer.Stop ();
+ this->m_GetChunkTimer.Stop ();
}
}
}
@@ -1150,18 +1978,139 @@ bool CMiniDexed::DoSavePerformance (void)
m_PerformanceConfig.SetBreathControlTarget (m_nBreathControlTarget[nTG], nTG);
m_PerformanceConfig.SetAftertouchRange (m_nAftertouchRange[nTG], nTG);
m_PerformanceConfig.SetAftertouchTarget (m_nAftertouchTarget[nTG], nTG);
-
+
+#if defined(MIXING_CONSOLE_ENABLE)
+ for(size_t fx = 0; fx < MixerOutput::kFXCount; ++fx)
+ {
+ this->m_PerformanceConfig.SetTGSendLevel(nTG, static_cast(fx), this->m_nTGSendLevel[nTG][fx]);
+ }
+#endif
+
+#if defined(PLATE_REVERB_ENABLE)
m_PerformanceConfig.SetReverbSend (m_nReverbSend[nTG], nTG);
+#endif
}
- m_PerformanceConfig.SetCompressorEnable (!!m_nParameter[ParameterCompressorEnable]);
- m_PerformanceConfig.SetReverbEnable (!!m_nParameter[ParameterReverbEnable]);
- m_PerformanceConfig.SetReverbSize (m_nParameter[ParameterReverbSize]);
- m_PerformanceConfig.SetReverbHighDamp (m_nParameter[ParameterReverbHighDamp]);
- m_PerformanceConfig.SetReverbLowDamp (m_nParameter[ParameterReverbLowDamp]);
- m_PerformanceConfig.SetReverbLowPass (m_nParameter[ParameterReverbLowPass]);
- m_PerformanceConfig.SetReverbDiffusion (m_nParameter[ParameterReverbDiffusion]);
- m_PerformanceConfig.SetReverbLevel (m_nParameter[ParameterReverbLevel]);
+ m_PerformanceConfig.SetCompressorEnable (!!m_nParameter[TParameter::ParameterCompressorEnable]);
+#if defined(MIXING_CONSOLE_ENABLE) || defined(PLATE_REVERB_ENABLE)
+ m_PerformanceConfig.SetReverbEnable (!!m_nParameter[TParameter::ParameterReverbEnable]);
+ m_PerformanceConfig.SetReverbSize (m_nParameter[TParameter::ParameterReverbSize]);
+ m_PerformanceConfig.SetReverbHighDamp (m_nParameter[TParameter::ParameterReverbHighDamp]);
+ m_PerformanceConfig.SetReverbLowDamp (m_nParameter[TParameter::ParameterReverbLowDamp]);
+ m_PerformanceConfig.SetReverbLowPass (m_nParameter[TParameter::ParameterReverbLowPass]);
+ m_PerformanceConfig.SetReverbDiffusion (m_nParameter[TParameter::ParameterReverbDiffusion]);
+ m_PerformanceConfig.SetReverbLevel (m_nParameter[TParameter::ParameterReverbLevel]);
+#endif
+
+#ifdef MIXING_CONSOLE_ENABLE
+ this->m_PerformanceConfig.SetFXTubeEnable(!!this->m_nParameter[TParameter::ParameterFXTubeEnable]);
+ this->m_PerformanceConfig.SetFXTubeOverdrive(this->m_nParameter[TParameter::ParameterFXTubeOverdrive]);
+
+ this->m_PerformanceConfig.SetFXChorusEnable(!!this->m_nParameter[TParameter::ParameterFXChorusEnable]);
+ this->m_PerformanceConfig.SetFXChorusRate(this->m_nParameter[TParameter::ParameterFXChorusRate]);
+ this->m_PerformanceConfig.SetFXChorusDepth(this->m_nParameter[TParameter::ParameterFXChorusDepth]);
+
+ this->m_PerformanceConfig.SetFXFlangerEnable(!!this->m_nParameter[TParameter::ParameterFXFlangerEnable]);
+ this->m_PerformanceConfig.SetFXFlangerRate(this->m_nParameter[TParameter::ParameterFXFlangerRate]);
+ this->m_PerformanceConfig.SetFXFlangerDepth(this->m_nParameter[TParameter::ParameterFXFlangerDepth]);
+ this->m_PerformanceConfig.SetFXFlangerFeedback(this->m_nParameter[TParameter::ParameterFXFlangerFeedback]);
+
+ this->m_PerformanceConfig.SetFXOrbitoneEnable(!!this->m_nParameter[TParameter::ParameterFXOrbitoneEnable]);
+ this->m_PerformanceConfig.SetFXOrbitoneRate(this->m_nParameter[TParameter::ParameterFXOrbitoneRate]);
+ this->m_PerformanceConfig.SetFXOrbitoneDepth(this->m_nParameter[TParameter::ParameterFXOrbitoneDepth]);
+
+ this->m_PerformanceConfig.SetFXPhaserEnable(!!this->m_nParameter[TParameter::ParameterFXPhaserEnable]);
+ this->m_PerformanceConfig.SetFXPhaserRate(this->m_nParameter[TParameter::ParameterFXPhaserRate]);
+ this->m_PerformanceConfig.SetFXPhaserDepth(this->m_nParameter[TParameter::ParameterFXPhaserDepth]);
+ this->m_PerformanceConfig.SetFXPhaserFeedback(this->m_nParameter[TParameter::ParameterFXPhaserFeedback]);
+ this->m_PerformanceConfig.SetFXPhaserNbStages(this->m_nParameter[TParameter::ParameterFXPhaserNbStages]);
+
+ this->m_PerformanceConfig.SetFXDelayEnable(!!this->m_nParameter[TParameter::ParameterFXDelayEnable]);
+ this->m_PerformanceConfig.SetFXDelayLeftDelayTime(this->m_nParameter[TParameter::ParameterFXDelayLeftDelayTime]);
+ this->m_PerformanceConfig.SetFXDelayRightDelayTime(this->m_nParameter[TParameter::ParameterFXDelayRightDelayTime]);
+ this->m_PerformanceConfig.SetFXDelayFeedback(this->m_nParameter[TParameter::ParameterFXDelayFeedback]);
+
+ this->m_PerformanceConfig.SetFXReverberatorEnable(!!this->m_nParameter[TParameter::ParameterFXReverberatorEnable]);
+ this->m_PerformanceConfig.SetFXReverberatorInputGain(this->m_nParameter[TParameter::ParameterFXReverberatorInputGain]);
+ this->m_PerformanceConfig.SetFXReverberatorTime(this->m_nParameter[TParameter::ParameterFXReverberatorTime]);
+ this->m_PerformanceConfig.SetFXReverberatorDiffusion(this->m_nParameter[TParameter::ParameterFXReverberatorDiffusion]);
+ this->m_PerformanceConfig.SetFXReverberatorLP(this->m_nParameter[TParameter::ParameterFXReverberatorLP]);
+
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Chorus, this->m_nParameter[TParameter::ParameterFXTube_ChorusSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Flanger, this->m_nParameter[TParameter::ParameterFXTube_FlangerSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Orbitone, this->m_nParameter[TParameter::ParameterFXTube_OrbitoneSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Phaser, this->m_nParameter[TParameter::ParameterFXTube_PhaserSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Delay, this->m_nParameter[TParameter::ParameterFXTube_DelaySend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_PlateReverb, this->m_nParameter[TParameter::ParameterFXTube_PlateReverbSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Reverberator, this->m_nParameter[TParameter::ParameterFXTube_ReverberatorSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, this->m_nParameter[TParameter::ParameterFXTube_MainOutput]);
+
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Tube, this->m_nParameter[TParameter::ParameterFXChorus_TubeSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Flanger, this->m_nParameter[TParameter::ParameterFXChorus_FlangerSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Orbitone, this->m_nParameter[TParameter::ParameterFXChorus_OrbitoneSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Phaser, this->m_nParameter[TParameter::ParameterFXChorus_PhaserSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Delay, this->m_nParameter[TParameter::ParameterFXChorus_DelaySend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_PlateReverb, this->m_nParameter[TParameter::ParameterFXChorus_PlateReverbSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Reverberator, this->m_nParameter[TParameter::ParameterFXChorus_ReverberatorSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::MainOutput, this->m_nParameter[TParameter::ParameterFXChorus_MainOutput]);
+
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Tube, this->m_nParameter[TParameter::ParameterFXFlanger_TubeSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Chorus, this->m_nParameter[TParameter::ParameterFXFlanger_ChorusSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Orbitone, this->m_nParameter[TParameter::ParameterFXFlanger_OrbitoneSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Phaser, this->m_nParameter[TParameter::ParameterFXFlanger_PhaserSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Delay, this->m_nParameter[TParameter::ParameterFXFlanger_DelaySend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_PlateReverb, this->m_nParameter[TParameter::ParameterFXFlanger_PlateReverbSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Reverberator, this->m_nParameter[TParameter::ParameterFXFlanger_ReverberatorSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::MainOutput, this->m_nParameter[TParameter::ParameterFXFlanger_MainOutput]);
+
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Tube, this->m_nParameter[TParameter::ParameterFXOrbitone_TubeSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Chorus, this->m_nParameter[TParameter::ParameterFXOrbitone_ChorusSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Flanger, this->m_nParameter[TParameter::ParameterFXOrbitone_FlangerSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Phaser, this->m_nParameter[TParameter::ParameterFXOrbitone_PhaserSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Delay, this->m_nParameter[TParameter::ParameterFXOrbitone_DelaySend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_PlateReverb, this->m_nParameter[TParameter::ParameterFXOrbitone_PlateReverbSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Reverberator, this->m_nParameter[TParameter::ParameterFXOrbitone_ReverberatorSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::MainOutput, this->m_nParameter[TParameter::ParameterFXOrbitone_MainOutput]);
+
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Tube, this->m_nParameter[TParameter::ParameterFXPhaser_TubeSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Chorus, this->m_nParameter[TParameter::ParameterFXPhaser_ChorusSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Flanger, this->m_nParameter[TParameter::ParameterFXPhaser_FlangerSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Orbitone, this->m_nParameter[TParameter::ParameterFXPhaser_OrbitoneSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Delay, this->m_nParameter[TParameter::ParameterFXPhaser_DelaySend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_PlateReverb, this->m_nParameter[TParameter::ParameterFXPhaser_PlateReverbSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Reverberator, this->m_nParameter[TParameter::ParameterFXPhaser_ReverberatorSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::MainOutput, this->m_nParameter[TParameter::ParameterFXPhaser_MainOutput]);
+
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Tube, this->m_nParameter[TParameter::ParameterFXDelay_TubeSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Chorus, this->m_nParameter[TParameter::ParameterFXDelay_ChorusSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Flanger, this->m_nParameter[TParameter::ParameterFXDelay_FlangerSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Orbitone, this->m_nParameter[TParameter::ParameterFXDelay_OrbitoneSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Phaser, this->m_nParameter[TParameter::ParameterFXDelay_PhaserSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_PlateReverb, this->m_nParameter[TParameter::ParameterFXDelay_PlateReverbSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Reverberator, this->m_nParameter[TParameter::ParameterFXDelay_ReverberatorSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::MainOutput, this->m_nParameter[TParameter::ParameterFXDelay_MainOutput]);
+
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Tube, this->m_nParameter[TParameter::ParameterFXPlateReverb_TubeSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Chorus, this->m_nParameter[TParameter::ParameterFXPlateReverb_ChorusSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Flanger, this->m_nParameter[TParameter::ParameterFXPlateReverb_FlangerSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Orbitone, this->m_nParameter[TParameter::ParameterFXPlateReverb_OrbitoneSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Phaser, this->m_nParameter[TParameter::ParameterFXPlateReverb_PhaserSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Delay, this->m_nParameter[TParameter::ParameterFXPlateReverb_DelaySend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Reverberator, this->m_nParameter[TParameter::ParameterFXPlateReverb_ReverberatorSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::MainOutput, this->m_nParameter[TParameter::ParameterFXPlateReverb_MainOutput]);
+
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Tube, this->m_nParameter[TParameter::ParameterFXReverberator_TubeSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Chorus, this->m_nParameter[TParameter::ParameterFXReverberator_ChorusSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Flanger, this->m_nParameter[TParameter::ParameterFXReverberator_FlangerSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Orbitone, this->m_nParameter[TParameter::ParameterFXReverberator_OrbitoneSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Phaser, this->m_nParameter[TParameter::ParameterFXReverberator_PhaserSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Delay, this->m_nParameter[TParameter::ParameterFXReverberator_DelaySend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_PlateReverb, this->m_nParameter[TParameter::ParameterFXReverberator_PlateReverbSend]);
+ this->m_PerformanceConfig.SetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, this->m_nParameter[TParameter::ParameterFXReverberator_MainOutput]);
+
+ this->m_PerformanceConfig.SetFXBypass(this->mixing_console_->bypass());
+
+#endif
if(m_bSaveAsDeault)
{
@@ -1408,12 +2357,12 @@ void CMiniDexed::getSysExVoiceDump(uint8_t* dest, uint8_t nTG)
void CMiniDexed::setMasterVolume (float32_t vol)
{
- if(vol < 0.0)
- vol = 0.0;
- else if(vol > 1.0)
- vol = 1.0;
+ this->nMasterVolume = constrain(vol, 0.0f, 1.0f);
+
+
+
- nMasterVolume=vol;
+
}
std::string CMiniDexed::GetPerformanceFileName(unsigned nID)
@@ -1502,55 +2451,179 @@ bool CMiniDexed::DoSavePerformanceNewFile (void)
void CMiniDexed::LoadPerformanceParameters(void)
{
for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
+ {
+
+ BankSelect (m_PerformanceConfig.GetBankNumber (nTG), nTG);
+ ProgramChange (m_PerformanceConfig.GetVoiceNumber (nTG), nTG);
+ SetMIDIChannel (m_PerformanceConfig.GetMIDIChannel (nTG), nTG);
+ SetVolume (m_PerformanceConfig.GetVolume (nTG), nTG);
+ SetPan (m_PerformanceConfig.GetPan (nTG), nTG);
+ SetMasterTune (m_PerformanceConfig.GetDetune (nTG), nTG);
+ SetCutoff (m_PerformanceConfig.GetCutoff (nTG), nTG);
+ SetResonance (m_PerformanceConfig.GetResonance (nTG), nTG);
+ setPitchbendRange (m_PerformanceConfig.GetPitchBendRange (nTG), nTG);
+ setPitchbendStep (m_PerformanceConfig.GetPitchBendStep (nTG), nTG);
+ setPortamentoMode (m_PerformanceConfig.GetPortamentoMode (nTG), nTG);
+ setPortamentoGlissando (m_PerformanceConfig.GetPortamentoGlissando (nTG), nTG);
+ setPortamentoTime (m_PerformanceConfig.GetPortamentoTime (nTG), nTG);
+
+ m_nNoteLimitLow[nTG] = m_PerformanceConfig.GetNoteLimitLow (nTG);
+ m_nNoteLimitHigh[nTG] = m_PerformanceConfig.GetNoteLimitHigh (nTG);
+ m_nNoteShift[nTG] = m_PerformanceConfig.GetNoteShift (nTG);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ if(m_PerformanceConfig.VoiceDataFilled(nTG))
{
+ uint8_t* tVoiceData = m_PerformanceConfig.GetVoiceDataFromTxt(nTG);
+ m_pTG[nTG]->loadVoiceParameters(tVoiceData);
+ }
+ setMonoMode(m_PerformanceConfig.GetMonoMode(nTG) ? 1 : 0, nTG);
- BankSelect (m_PerformanceConfig.GetBankNumber (nTG), nTG);
- ProgramChange (m_PerformanceConfig.GetVoiceNumber (nTG), nTG);
- SetMIDIChannel (m_PerformanceConfig.GetMIDIChannel (nTG), nTG);
- SetVolume (m_PerformanceConfig.GetVolume (nTG), nTG);
- SetPan (m_PerformanceConfig.GetPan (nTG), nTG);
- SetMasterTune (m_PerformanceConfig.GetDetune (nTG), nTG);
- SetCutoff (m_PerformanceConfig.GetCutoff (nTG), nTG);
- SetResonance (m_PerformanceConfig.GetResonance (nTG), nTG);
- setPitchbendRange (m_PerformanceConfig.GetPitchBendRange (nTG), nTG);
- setPitchbendStep (m_PerformanceConfig.GetPitchBendStep (nTG), nTG);
- setPortamentoMode (m_PerformanceConfig.GetPortamentoMode (nTG), nTG);
- setPortamentoGlissando (m_PerformanceConfig.GetPortamentoGlissando (nTG), nTG);
- setPortamentoTime (m_PerformanceConfig.GetPortamentoTime (nTG), nTG);
-
- m_nNoteLimitLow[nTG] = m_PerformanceConfig.GetNoteLimitLow (nTG);
- m_nNoteLimitHigh[nTG] = m_PerformanceConfig.GetNoteLimitHigh (nTG);
- m_nNoteShift[nTG] = m_PerformanceConfig.GetNoteShift (nTG);
-
- if(m_PerformanceConfig.VoiceDataFilled(nTG))
- {
- uint8_t* tVoiceData = m_PerformanceConfig.GetVoiceDataFromTxt(nTG);
- m_pTG[nTG]->loadVoiceParameters(tVoiceData);
- }
- setMonoMode(m_PerformanceConfig.GetMonoMode(nTG) ? 1 : 0, nTG);
- SetReverbSend (m_PerformanceConfig.GetReverbSend (nTG), nTG);
-
- setModWheelRange (m_PerformanceConfig.GetModulationWheelRange (nTG), nTG);
- setModWheelTarget (m_PerformanceConfig.GetModulationWheelTarget (nTG), nTG);
- setFootControllerRange (m_PerformanceConfig.GetFootControlRange (nTG), nTG);
- setFootControllerTarget (m_PerformanceConfig.GetFootControlTarget (nTG), nTG);
- setBreathControllerRange (m_PerformanceConfig.GetBreathControlRange (nTG), nTG);
- setBreathControllerTarget (m_PerformanceConfig.GetBreathControlTarget (nTG), nTG);
- setAftertouchRange (m_PerformanceConfig.GetAftertouchRange (nTG), nTG);
- setAftertouchTarget (m_PerformanceConfig.GetAftertouchTarget (nTG), nTG);
-
-
+ this->SetParameter(TParameter::ParameterCompressorEnable, this->m_PerformanceConfig.GetCompressorEnable());
+#if defined(MIXING_CONSOLE_ENABLE)
+ for(size_t fx = 0; fx < MixerOutput::kFXCount; ++fx)
+ {
+ this->setMixingConsoleSendLevel(nTG, static_cast(fx), this->m_PerformanceConfig.GetTGSendLevel(nTG, static_cast(fx)));
}
+#elif defined(PLATE_REVERB_ENABLE)
+ SetReverbSend (m_PerformanceConfig.GetReverbSend (nTG), nTG);
+#endif
- // Effects
- SetParameter (ParameterCompressorEnable, m_PerformanceConfig.GetCompressorEnable () ? 1 : 0);
- SetParameter (ParameterReverbEnable, m_PerformanceConfig.GetReverbEnable () ? 1 : 0);
- SetParameter (ParameterReverbSize, m_PerformanceConfig.GetReverbSize ());
- SetParameter (ParameterReverbHighDamp, m_PerformanceConfig.GetReverbHighDamp ());
- SetParameter (ParameterReverbLowDamp, m_PerformanceConfig.GetReverbLowDamp ());
- SetParameter (ParameterReverbLowPass, m_PerformanceConfig.GetReverbLowPass ());
- SetParameter (ParameterReverbDiffusion, m_PerformanceConfig.GetReverbDiffusion ());
- SetParameter (ParameterReverbLevel, m_PerformanceConfig.GetReverbLevel ());
+ setModWheelRange (m_PerformanceConfig.GetModulationWheelRange (nTG), nTG);
+ setModWheelTarget (m_PerformanceConfig.GetModulationWheelTarget (nTG), nTG);
+ setFootControllerRange (m_PerformanceConfig.GetFootControlRange (nTG), nTG);
+ setFootControllerTarget (m_PerformanceConfig.GetFootControlTarget (nTG), nTG);
+ setBreathControllerRange (m_PerformanceConfig.GetBreathControlRange (nTG), nTG);
+ setBreathControllerTarget (m_PerformanceConfig.GetBreathControlTarget (nTG), nTG);
+ setAftertouchRange (m_PerformanceConfig.GetAftertouchRange (nTG), nTG);
+ setAftertouchTarget (m_PerformanceConfig.GetAftertouchTarget (nTG), nTG);
+ }
+
+#ifdef MIXING_CONSOLE_ENABLE
+ this->SetParameter(TParameter::ParameterFXTubeEnable, this->m_PerformanceConfig.GetFXTubeEnable());
+ this->SetParameter(TParameter::ParameterFXTubeOverdrive, this->m_PerformanceConfig.GetFXTubeOverdrive());
+
+ this->SetParameter(TParameter::ParameterFXChorusEnable, this->m_PerformanceConfig.GetFXChorusEnable());
+ this->SetParameter(TParameter::ParameterFXChorusRate, this->m_PerformanceConfig.GetFXChorusRate());
+ this->SetParameter(TParameter::ParameterFXChorusDepth, this->m_PerformanceConfig.GetFXChorusDepth());
+
+ this->SetParameter(TParameter::ParameterFXFlangerEnable, this->m_PerformanceConfig.GetFXFlangerEnable());
+ this->SetParameter(TParameter::ParameterFXFlangerRate, this->m_PerformanceConfig.GetFXFlangerRate());
+ this->SetParameter(TParameter::ParameterFXFlangerDepth, this->m_PerformanceConfig.GetFXFlangerDepth());
+ this->SetParameter(TParameter::ParameterFXFlangerFeedback, this->m_PerformanceConfig.GetFXFlangerFeedback());
+
+ this->SetParameter(TParameter::ParameterFXOrbitoneEnable, this->m_PerformanceConfig.GetFXOrbitoneEnable());
+ this->SetParameter(TParameter::ParameterFXOrbitoneRate, this->m_PerformanceConfig.GetFXOrbitoneRate());
+ this->SetParameter(TParameter::ParameterFXOrbitoneDepth, this->m_PerformanceConfig.GetFXOrbitoneDepth());
+
+ this->SetParameter(TParameter::ParameterFXPhaserEnable, this->m_PerformanceConfig.GetFXPhaserEnable());
+ this->SetParameter(TParameter::ParameterFXPhaserRate, this->m_PerformanceConfig.GetFXPhaserRate());
+ this->SetParameter(TParameter::ParameterFXPhaserDepth, this->m_PerformanceConfig.GetFXPhaserDepth());
+ this->SetParameter(TParameter::ParameterFXPhaserFeedback, this->m_PerformanceConfig.GetFXPhaserFeedback());
+ this->SetParameter(TParameter::ParameterFXPhaserNbStages, this->m_PerformanceConfig.GetFXPhaserNbStages());
+
+ this->SetParameter(TParameter::ParameterFXDelayEnable, this->m_PerformanceConfig.GetFXDelayEnable());
+ this->SetParameter(TParameter::ParameterFXDelayLeftDelayTime, this->m_PerformanceConfig.GetFXDelayLeftDelayTime());
+ this->SetParameter(TParameter::ParameterFXDelayRightDelayTime, this->m_PerformanceConfig.GetFXDelayRightDelayTime());
+ this->SetParameter(TParameter::ParameterFXDelayFeedback, this->m_PerformanceConfig.GetFXDelayFeedback());
+
+ this->SetParameter(TParameter::ParameterFXReverberatorEnable, this->m_PerformanceConfig.GetFXReverberatorEnable());
+ this->SetParameter(TParameter::ParameterFXReverberatorInputGain, this->m_PerformanceConfig.GetFXReverberatorInputGain());
+ this->SetParameter(TParameter::ParameterFXReverberatorTime, this->m_PerformanceConfig.GetFXReverberatorTime());
+ this->SetParameter(TParameter::ParameterFXReverberatorDiffusion, this->m_PerformanceConfig.GetFXReverberatorDiffusion());
+ this->SetParameter(TParameter::ParameterFXReverberatorLP, this->m_PerformanceConfig.GetFXReverberatorLP());
+
+ this->SetParameter(TParameter::ParameterFXTube_ChorusSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Chorus));
+ this->SetParameter(TParameter::ParameterFXTube_FlangerSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Flanger));
+ this->SetParameter(TParameter::ParameterFXTube_OrbitoneSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Orbitone));
+ this->SetParameter(TParameter::ParameterFXTube_PhaserSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Phaser));
+ this->SetParameter(TParameter::ParameterFXTube_DelaySend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Delay));
+ this->SetParameter(TParameter::ParameterFXTube_PlateReverbSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_PlateReverb));
+ this->SetParameter(TParameter::ParameterFXTube_ReverberatorSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Reverberator));
+ this->SetParameter(TParameter::ParameterFXTube_MainOutput, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput));
+
+ this->SetParameter(TParameter::ParameterFXChorus_TubeSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Tube));
+ this->SetParameter(TParameter::ParameterFXChorus_FlangerSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Flanger));
+ this->SetParameter(TParameter::ParameterFXChorus_OrbitoneSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Orbitone));
+ this->SetParameter(TParameter::ParameterFXChorus_PhaserSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Phaser));
+ this->SetParameter(TParameter::ParameterFXChorus_DelaySend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Delay));
+ this->SetParameter(TParameter::ParameterFXChorus_PlateReverbSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_PlateReverb));
+ this->SetParameter(TParameter::ParameterFXChorus_ReverberatorSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Reverberator));
+ this->SetParameter(TParameter::ParameterFXChorus_MainOutput, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::MainOutput));
+
+ this->SetParameter(TParameter::ParameterFXFlanger_TubeSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Tube));
+ this->SetParameter(TParameter::ParameterFXFlanger_ChorusSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Chorus));
+ this->SetParameter(TParameter::ParameterFXFlanger_OrbitoneSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Orbitone));
+ this->SetParameter(TParameter::ParameterFXFlanger_PhaserSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Phaser));
+ this->SetParameter(TParameter::ParameterFXFlanger_DelaySend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Delay));
+ this->SetParameter(TParameter::ParameterFXFlanger_PlateReverbSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_PlateReverb));
+ this->SetParameter(TParameter::ParameterFXFlanger_ReverberatorSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::FX_Reverberator));
+ this->SetParameter(TParameter::ParameterFXFlanger_MainOutput, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Flanger, MixerOutput::MainOutput));
+
+ this->SetParameter(TParameter::ParameterFXOrbitone_TubeSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Tube));
+ this->SetParameter(TParameter::ParameterFXOrbitone_ChorusSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Chorus));
+ this->SetParameter(TParameter::ParameterFXOrbitone_FlangerSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Flanger));
+ this->SetParameter(TParameter::ParameterFXOrbitone_PhaserSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Phaser));
+ this->SetParameter(TParameter::ParameterFXOrbitone_DelaySend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Delay));
+ this->SetParameter(TParameter::ParameterFXOrbitone_PlateReverbSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_PlateReverb));
+ this->SetParameter(TParameter::ParameterFXOrbitone_ReverberatorSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_Reverberator));
+ this->SetParameter(TParameter::ParameterFXOrbitone_MainOutput, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::MainOutput));
+
+ this->SetParameter(TParameter::ParameterFXPhaser_TubeSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Tube));
+ this->SetParameter(TParameter::ParameterFXPhaser_ChorusSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Chorus));
+ this->SetParameter(TParameter::ParameterFXPhaser_FlangerSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Flanger));
+ this->SetParameter(TParameter::ParameterFXPhaser_OrbitoneSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Orbitone));
+ this->SetParameter(TParameter::ParameterFXPhaser_DelaySend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Delay));
+ this->SetParameter(TParameter::ParameterFXPhaser_PlateReverbSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_PlateReverb));
+ this->SetParameter(TParameter::ParameterFXPhaser_ReverberatorSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Reverberator));
+ this->SetParameter(TParameter::ParameterFXPhaser_MainOutput, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::MainOutput));
+
+ this->SetParameter(TParameter::ParameterFXDelay_TubeSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Tube));
+ this->SetParameter(TParameter::ParameterFXDelay_ChorusSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Chorus));
+ this->SetParameter(TParameter::ParameterFXDelay_FlangerSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Flanger));
+ this->SetParameter(TParameter::ParameterFXDelay_OrbitoneSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Orbitone));
+ this->SetParameter(TParameter::ParameterFXDelay_PhaserSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Phaser));
+ this->SetParameter(TParameter::ParameterFXDelay_PlateReverbSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_PlateReverb));
+ this->SetParameter(TParameter::ParameterFXDelay_ReverberatorSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::FX_Reverberator));
+ this->SetParameter(TParameter::ParameterFXDelay_MainOutput, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Delay, MixerOutput::MainOutput));
+
+ this->SetParameter(TParameter::ParameterFXPlateReverb_TubeSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Tube));
+ this->SetParameter(TParameter::ParameterFXPlateReverb_ChorusSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Chorus));
+ this->SetParameter(TParameter::ParameterFXPlateReverb_FlangerSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Flanger));
+ this->SetParameter(TParameter::ParameterFXPlateReverb_OrbitoneSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Orbitone));
+ this->SetParameter(TParameter::ParameterFXPlateReverb_PhaserSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Phaser));
+ this->SetParameter(TParameter::ParameterFXPlateReverb_DelaySend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Delay));
+ this->SetParameter(TParameter::ParameterFXPlateReverb_ReverberatorSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::FX_Reverberator));
+ this->SetParameter(TParameter::ParameterFXPlateReverb_MainOutput, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::MainOutput));
+
+ this->SetParameter(TParameter::ParameterFXReverberator_TubeSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Tube));
+ this->SetParameter(TParameter::ParameterFXReverberator_ChorusSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Chorus));
+ this->SetParameter(TParameter::ParameterFXReverberator_FlangerSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Flanger));
+ this->SetParameter(TParameter::ParameterFXReverberator_OrbitoneSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Orbitone));
+ this->SetParameter(TParameter::ParameterFXReverberator_PhaserSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Phaser));
+ this->SetParameter(TParameter::ParameterFXReverberator_DelaySend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_Delay));
+ this->SetParameter(TParameter::ParameterFXReverberator_PlateReverbSend, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::FX_PlateReverb));
+ this->SetParameter(TParameter::ParameterFXReverberator_MainOutput, this->m_PerformanceConfig.GetFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput));
+
+ this->mixing_console_->bypass(this->m_PerformanceConfig.IsFXBypass());
+#endif
}
std::string CMiniDexed::GetNewPerformanceDefaultName(void)
@@ -1732,5 +2805,4 @@ unsigned CMiniDexed::getModController (unsigned controller, unsigned parameter,
return 0;
break;
}
-
}
diff --git a/src/minidexed.h b/src/minidexed.h
index e49e7dea..ed6e84d1 100644
--- a/src/minidexed.h
+++ b/src/minidexed.h
@@ -20,6 +20,7 @@
#ifndef _minidexed_h
#define _minidexed_h
+#include "extra_features.h"
#include "dexedadapter.h"
#include "config.h"
#include "userinterface.h"
@@ -44,14 +45,25 @@
#include "effect_platervbstereo.h"
#include "effect_compressor.h"
+#if defined(MIXING_CONSOLE_ENABLE)
+#include "mixing_console.hpp"
+
+typedef MixingConsole Mixer;
+#endif
+
class CMiniDexed
#ifdef ARM_ALLOW_MULTI_CORE
: public CMultiCoreSupport
#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);
@@ -88,7 +100,13 @@ class CMiniDexed
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);
@@ -130,6 +148,9 @@ class CMiniDexed
enum TParameter
{
ParameterCompressorEnable,
+
+ #if defined(PLATE_REVERB_ENABLE) || defined(MIXING_CONSOLE_ENABLE)
+ // Plate Reverb parameters
ParameterReverbEnable,
ParameterReverbSize,
ParameterReverbHighDamp,
@@ -137,6 +158,137 @@ class CMiniDexed
ParameterReverbLowPass,
ParameterReverbDiffusion,
ParameterReverbLevel,
+ #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
};
@@ -162,7 +314,9 @@ class CMiniDexed
TGParameterCutoff,
TGParameterResonance,
TGParameterMIDIChannel,
+#if defined(PLATE_REVERB_ENABLE)
TGParameterReverbSend,
+#endif
TGParameterPitchBendRange,
TGParameterPitchBendStep,
TGParameterPortamentoMode,
@@ -190,6 +344,18 @@ class CMiniDexed
TGParameterATAmplitude,
TGParameterATEGBias,
+#if defined(MIXING_CONSOLE_ENABLE)
+ TGParameterMixingSendFXTube,
+ TGParameterMixingSendFXChorus,
+ TGParameterMixingSendFXFlanger,
+ TGParameterMixingSendFXOrbitone,
+ TGParameterMixingSendFXPhaser,
+ TGParameterMixingSendFXDelay,
+ TGParameterMixingSendFXPlateReverb,
+ TGParameterMixingSendFXReverberator,
+ TGParameterMixingSendFXMainOutput,
+#endif // MIXING_CONSOLE_ENABLE
+
TGParameterUnknown
};
@@ -261,8 +427,13 @@ class CMiniDexed
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];
@@ -291,11 +462,16 @@ class CMiniDexed
CPerformanceTimer m_GetChunkTimer;
bool m_bProfileEnabled;
+#if defined(MIXING_CONSOLE_ENABLE)
+ Mixer* mixing_console_;
+#elif defined(PLATE_REVERB_ENABLE)
AudioEffectPlateReverb* reverb;
AudioStereoMixer* tg_mixer;
AudioStereoMixer* reverb_send_mixer;
+#endif
+
+ CSpinLock m_FXSpinLock;
- CSpinLock m_ReverbSpinLock;
bool m_bSavePerformance;
bool m_bSavePerformanceNewFile;
diff --git a/src/mixing_console.hpp b/src/mixing_console.hpp
new file mode 100644
index 00000000..af679fc0
--- /dev/null
+++ b/src/mixing_console.hpp
@@ -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 .
+//
+
+// 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
+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* getTube();
+ inline FXUnit2* getChorus();
+ inline FXUnit2* getFlanger();
+ inline FXUnit2* getOrbitone();
+ inline FXUnit2* getPhaser();
+ inline FXUnit2* getDelay();
+ inline FXUnit2* getPlateReverb();
+ inline FXUnit2* getReverberator();
+ inline FXUnit2* 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_;
+ FXUnit2* chorus_;
+ FXUnit2* flanger_;
+ FXUnit2* orbitone_;
+ FXUnit2* phaser_;
+ FXUnit2* delay_;
+ FXUnit2* plate_reverb_;
+ FXUnit2* reverberator_;
+ FXUnit2* 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(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(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(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(i - nb_inputs)) + " ]", this->input_samples_[StereoChannels::Left ][i], -1.0f, 1.0f, deepInspection);
+ nb_errors += inspector(tag + ".input[ R ][ input " + toString(static_cast(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
+float32_t MixingConsole::weighted_sum(const float32_t* data, const float32_t* weights, size_t size)
+{
+ float32_t res = arm_weighted_sum_f32(data, weights, size);
+
+ return std::isnan(res) ? 0.0f : res;
+}
+
+template
+MixingConsole::MixingConsole(float32_t sampling_rate, size_t buffer_size, 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(sampling_rate);
+ this->fx_[MixerOutput::FX_Chorus] = this->chorus_ = new FXUnit2(sampling_rate);
+ this->fx_[MixerOutput::FX_Flanger] = this->flanger_ = new FXUnit2(sampling_rate);
+ this->fx_[MixerOutput::FX_Orbitone] = this->orbitone_ = new FXUnit2(sampling_rate);
+ this->fx_[MixerOutput::FX_Phaser] = this->phaser_ = new FXUnit2(sampling_rate);
+ this->fx_[MixerOutput::FX_Delay] = this->delay_ = new FXUnit2(sampling_rate);
+ this->fx_[MixerOutput::FX_PlateReverb] = this->plate_reverb_ = new FXUnit2(sampling_rate);
+ this->fx_[MixerOutput::FX_Reverberator] = this->reverberator_ = new FXUnit2(sampling_rate);
+ this->fx_[MixerOutput::MainOutput] = this->dry_ = new FXUnit2(sampling_rate);
+
+ this->bypass(false);
+
+ this->init();
+}
+
+template
+MixingConsole::~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
+void MixingConsole::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
+bool MixingConsole::bypass() const
+{
+ return this->bypass_;
+}
+
+template
+size_t MixingConsole::getChannelNumber() const
+{
+ return nb_inputs;
+}
+
+// Send section
+template
+void MixingConsole::setChannelLevel(size_t in, float32_t lvl)
+{
+ assert(in < nb_inputs);
+
+ lvl = constrain(lvl, 0.0f, 1.0f);
+ if(lvl == this->channel_level_[in]) return;
+
+ this->channel_level_[in] = lvl;
+ this->updatePan(in);
+}
+
+template
+void MixingConsole::setPan(size_t in, float32_t pan)
+{
+ 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
+void MixingConsole::swapStereoImage(bool swap)
+{
+ this->swap_stereo_image_ = swap;
+}
+
+template
+void MixingConsole::setSendLevel(size_t in, MixerOutput fx, float32_t lvl)
+{
+ assert(in < nb_inputs);
+ assert(fx < MixerOutput::kFXCount);
+
+ this->setLevel(in, fx, lvl);
+}
+
+template
+void MixingConsole::setInputSample(size_t in, float32_t sampleL, float32_t sampleR)
+{
+ assert(in < nb_inputs);
+
+ this->setSample(in, sampleL, sampleR);
+}
+
+template
+void MixingConsole::setInputSampleBuffer(size_t in, float32_t* samples)
+{
+ assert(in < nb_inputs);
+
+ this->tg_input_sample_buffer_[in] = samples;
+}
+
+// Return section
+template
+void MixingConsole::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
+void MixingConsole::setReturnSample(MixerOutput ret, float32_t sampleL, float32_t sampleR)
+{
+ assert(ret < (MixerOutput::kFXCount - 1));
+
+ this->setSample(nb_inputs + ret, sampleL, sampleR);
+}
+
+// Get FX
+template
+FXElement* MixingConsole::getFX(size_t fx)
+{
+ assert(fx < MixerOutput::kFXCount);
+ return this->fx_[fx];
+}
+
+template
+FXUnit2* MixingConsole::getTube()
+{
+ return this->tube_;
+}
+
+template
+FXUnit2* MixingConsole::getChorus()
+{
+ return this->chorus_;
+}
+
+template
+FXUnit2