@ -0,0 +1,78 @@ |
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// CFileDevice.h
|
||||
//
|
||||
// Device implementation that writes into a file.
|
||||
// This device is a singleton dedicated to allow the Circle CLogger class to output log event into the file instead of the internal buffer.
|
||||
// Author: Vincent Gauché
|
||||
//
|
||||
|
||||
#pragma once |
||||
|
||||
#include "extra_features.h" |
||||
|
||||
#include <circle/device.h> |
||||
#include <fatfs/ff.h> |
||||
#include <fstream> |
||||
#include <iostream> |
||||
#include <sstream> |
||||
#include <cassert> |
||||
|
||||
class CFileDevice : public CDevice |
||||
{ |
||||
DISALLOW_COPY_AND_ASSIGN(CFileDevice); |
||||
|
||||
private: |
||||
CFileDevice() : |
||||
m_file(new FIL()) |
||||
{ |
||||
FRESULT res = f_open(this->m_file, "SD:/debuglog.txt", FA_WRITE | FA_CREATE_ALWAYS); |
||||
assert(res == FR_OK); |
||||
} |
||||
|
||||
public: |
||||
static void UseMeForLogger() |
||||
{ |
||||
CLogger::Get()->SetNewTarget(&CFileDevice::GetInstance()); |
||||
} |
||||
|
||||
static CFileDevice& GetInstance() |
||||
{ |
||||
static CFileDevice Instance; |
||||
|
||||
return Instance; |
||||
} |
||||
|
||||
~CFileDevice() |
||||
{ |
||||
f_sync(this->m_file); |
||||
f_close(this->m_file);
|
||||
} |
||||
|
||||
virtual int Write(const void* pBuffer, size_t nCount) override |
||||
{ |
||||
UINT nb = 0; |
||||
if(nCount > 0) |
||||
{ |
||||
f_write(this->m_file, pBuffer, nCount, &nb); |
||||
f_sync(this->m_file); |
||||
} |
||||
|
||||
return (int)nb; |
||||
} |
||||
|
||||
private: |
||||
FIL* m_file; |
||||
}; |
@ -0,0 +1,71 @@ |
||||
#include "fx_diffuser.h" |
||||
|
||||
#include <cmath> |
||||
#include <algorithm> |
||||
|
||||
#define TAIL , -1 |
||||
|
||||
Diffuser::Diffuser(float32_t sampling_frequency) :
|
||||
FXElement(sampling_frequency), |
||||
engine_(sampling_frequency) |
||||
{ |
||||
} |
||||
|
||||
Diffuser::~Diffuser() |
||||
{ |
||||
} |
||||
|
||||
void Diffuser::reset() |
||||
{ |
||||
this->engine_.reset(); |
||||
} |
||||
|
||||
void Diffuser::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) |
||||
{ |
||||
typedef Engine::Reserve<126, |
||||
Engine::Reserve<180, |
||||
Engine::Reserve<269, |
||||
Engine::Reserve<444, |
||||
Engine::Reserve<151, |
||||
Engine::Reserve<205, |
||||
Engine::Reserve<245, |
||||
Engine::Reserve<405> > > > > > > > Memory; |
||||
Engine::DelayLine<Memory, 0> apl1; |
||||
Engine::DelayLine<Memory, 1> apl2; |
||||
Engine::DelayLine<Memory, 2> apl3; |
||||
Engine::DelayLine<Memory, 3> apl4; |
||||
Engine::DelayLine<Memory, 4> apr1; |
||||
Engine::DelayLine<Memory, 5> apr2; |
||||
Engine::DelayLine<Memory, 6> apr3; |
||||
Engine::DelayLine<Memory, 7> apr4; |
||||
Engine::Context c; |
||||
|
||||
const float32_t kap = 0.625f; |
||||
float wet = 0.0f; |
||||
|
||||
engine_.start(&c); |
||||
|
||||
c.load(inL); |
||||
c.read(apl1 TAIL, kap); |
||||
c.writeAllPass(apl1, -kap); |
||||
c.read(apl2 TAIL, kap); |
||||
c.writeAllPass(apl2, -kap); |
||||
c.read(apl3 TAIL, kap); |
||||
c.writeAllPass(apl3, -kap); |
||||
c.read(apl4 TAIL, kap); |
||||
c.writeAllPass(apl4, -kap); |
||||
c.writeAndLoad(wet, 0.0f); |
||||
outL = wet; |
||||
|
||||
c.load(inR); |
||||
c.read(apr1 TAIL, kap); |
||||
c.writeAllPass(apr1, -kap); |
||||
c.read(apr2 TAIL, kap); |
||||
c.writeAllPass(apr2, -kap); |
||||
c.read(apr3 TAIL, kap); |
||||
c.writeAllPass(apr3, -kap); |
||||
c.read(apr4 TAIL, kap); |
||||
c.writeAllPass(apr4, -kap); |
||||
c.writeAndLoad(wet, 0.0f); |
||||
outR = wet; |
||||
} |
@ -0,0 +1,65 @@ |
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//
|
||||
// fx_shimmer_reverb3.h
|
||||
//
|
||||
// Stereo Diffuser proposed in the context of the MiniDexed project
|
||||
// It is adapted from the Diffuser that could be found on Cloud EuroRack module from Mutable Instrruments
|
||||
// Ported by: Vincent Gauché
|
||||
//
|
||||
#pragma once |
||||
|
||||
#include "fx_components.h" |
||||
#include "fx_engine.hpp" |
||||
|
||||
#define DIFFUSER_BUFFER_SIZE 2048 |
||||
|
||||
class Diffuser : public FXElement |
||||
{ |
||||
DISALLOW_COPY_AND_ASSIGN(Diffuser); |
||||
|
||||
public: |
||||
Diffuser(float32_t sampling_frequency); |
||||
virtual ~Diffuser(); |
||||
|
||||
virtual void reset() override; |
||||
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override; |
||||
|
||||
private: |
||||
typedef FxEngine<DIFFUSER_BUFFER_SIZE, Format::FORMAT_FLOAT32, false> Engine; |
||||
Engine engine_; |
||||
|
||||
IMPLEMENT_DUMP( |
||||
out << "START " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl; |
||||
|
||||
if(deepInspection) |
||||
{ |
||||
this->engine_.dump(out, deepInspection, tag + ".engine_"); |
||||
} |
||||
|
||||
out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl; |
||||
) |
||||
|
||||
IMPLEMENT_INSPECT( |
||||
size_t nb_errors = 0u; |
||||
|
||||
if(deepInspection) |
||||
{ |
||||
nb_errors += this->engine_.inspect(inspector, deepInspection, tag + ".engine_"); |
||||
} |
||||
|
||||
return nb_errors; |
||||
) |
||||
}; |
@ -0,0 +1,21 @@ |
||||
#include "fx_dry.h" |
||||
|
||||
Dry::Dry(float32_t samplingRate) : |
||||
FXElement(samplingRate) |
||||
{ |
||||
} |
||||
|
||||
Dry::~Dry() |
||||
{ |
||||
} |
||||
|
||||
void Dry::reset() |
||||
{ |
||||
// nothing to be done
|
||||
} |
||||
|
||||
void Dry::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) |
||||
{ |
||||
outL = inL; |
||||
outR = inR; |
||||
} |
@ -0,0 +1,43 @@ |
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// fx_dry.h
|
||||
//
|
||||
// An FX that does nothing but used to generalize the processing.
|
||||
// Author: Vincent Gauché
|
||||
//
|
||||
#pragma once |
||||
|
||||
#include "fx_components.h" |
||||
|
||||
class Dry : public FXElement |
||||
{ |
||||
DISALLOW_COPY_AND_ASSIGN(Dry); |
||||
|
||||
public: |
||||
Dry(float32_t sampling_rate); |
||||
virtual ~Dry(); |
||||
|
||||
virtual void reset() override; |
||||
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override; |
||||
|
||||
IMPLEMENT_DUMP( |
||||
out << "START " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl; |
||||
out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl; |
||||
) |
||||
|
||||
IMPLEMENT_INSPECT( |
||||
return 0u; |
||||
) |
||||
}; |
@ -0,0 +1,104 @@ |
||||
#include "fx_pitch_shifter.h" |
||||
#include "fx_shimmer_helper.h" |
||||
|
||||
#include <cmath> |
||||
#include <algorithm> |
||||
|
||||
#define ONE_POLE(out, in, coefficient) out += (coefficient) * ((in) - out); |
||||
|
||||
#define TAIL , -1 |
||||
|
||||
PitchShifter::PitchShifter(float32_t sampling_rate, float32_t transpose_boundary) :
|
||||
FXElement(sampling_rate), |
||||
engine_(sampling_rate), |
||||
TransposeBoundary(transpose_boundary), |
||||
phase_(0.0f), |
||||
transpose_(0.0f), |
||||
ratio_(0.0f), |
||||
size_(-1.0f), |
||||
sample_size_(0.0f) |
||||
{ |
||||
this->setTranspose(0.0f); |
||||
this->setSize(0.5f); |
||||
} |
||||
|
||||
PitchShifter::~PitchShifter() |
||||
{ |
||||
} |
||||
|
||||
void PitchShifter::reset() |
||||
{ |
||||
this->engine_.reset(); |
||||
} |
||||
|
||||
void PitchShifter::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) |
||||
{ |
||||
typedef Engine::Reserve<2047, Engine::Reserve<2047> > Memory; |
||||
Engine::DelayLine<Memory, 0> left; |
||||
Engine::DelayLine<Memory, 1> right; |
||||
Engine::Context c; |
||||
|
||||
this->engine_.start(&c); |
||||
|
||||
this->phase_ += (1.0f - this->ratio_) / this->sample_size_; |
||||
if(this->phase_ >= 1.0f) |
||||
{ |
||||
phase_ -= 1.0f; |
||||
} |
||||
if(this->phase_ <= 0.0f) |
||||
{ |
||||
this->phase_ += 1.0f; |
||||
} |
||||
|
||||
float tri = 2.0f * (this->phase_ >= 0.5f ? 1.0f - phase_ : phase_); |
||||
float phase = this->phase_ * this->sample_size_; |
||||
float half = phase + this->sample_size_ * 0.5f; |
||||
if(half >= this->sample_size_) |
||||
{ |
||||
half -= this->sample_size_; |
||||
} |
||||
|
||||
c.load(inL); |
||||
c.writeAndLoad(left, 0.0f); |
||||
c.interpolate(left, phase, tri); |
||||
c.interpolate(left, half, 1.0f - tri); |
||||
c.writeAndLoad(outL, 0.0f); |
||||
|
||||
c.load(inR); |
||||
c.writeAndLoad(right, 0.0f); |
||||
c.interpolate(right, phase, tri); |
||||
c.interpolate(right, half, 1.0f - tri); |
||||
c.writeAndLoad(outR, 0.0f); |
||||
} |
||||
|
||||
void PitchShifter::setTranspose(float32_t transpose) |
||||
{ |
||||
transpose = constrain(transpose, -this->TransposeBoundary, this->TransposeBoundary); |
||||
if(this->transpose_ != transpose) |
||||
{ |
||||
this->transpose_ = transpose; |
||||
this->ratio_ = semitoneToRatio(transpose); |
||||
} |
||||
} |
||||
|
||||
float32_t PitchShifter::getTranspose() const |
||||
{ |
||||
return this->transpose_; |
||||
} |
||||
|
||||
void PitchShifter::setSize(float32_t size) |
||||
{ |
||||
size = constrain(size, 0.0f, 1.0f); |
||||
if(size != this->size_) |
||||
{ |
||||
this->size_ = size; |
||||
|
||||
float32_t target_size = 128.0f + (2047.0f - 128.0f) * size * size * size; |
||||
ONE_POLE(this->sample_size_, target_size, 0.05f); |
||||
} |
||||
} |
||||
|
||||
float32_t PitchShifter::getSize() const |
||||
{ |
||||
return this->size_; |
||||
} |
@ -0,0 +1,115 @@ |
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//
|
||||
// fx_shimmer_reverb3.h
|
||||
//
|
||||
// Stereo Pitch Shifter proposed in the context of the MiniDexed project
|
||||
// It is adapted from the Pitch Shifter that could be found on Cloud EuroRack module from Mutable Instrruments
|
||||
// Ported by: Vincent Gauché
|
||||
//
|
||||
#pragma once |
||||
|
||||
#include "fx_components.h" |
||||
#include "fx_engine.hpp" |
||||
|
||||
#define PITCH_SHIFTER_BUFFER_SIZE 4096 |
||||
#define PITCH_SHIFTER_TRANSPOSE_BOUNDARY 36.0f |
||||
|
||||
class PitchShifter : public FXElement |
||||
{ |
||||
DISALLOW_COPY_AND_ASSIGN(PitchShifter); |
||||
|
||||
public: |
||||
PitchShifter(float32_t sampling_rate, float32_t transpose_boundary = PITCH_SHIFTER_TRANSPOSE_BOUNDARY); |
||||
virtual ~PitchShifter(); |
||||
|
||||
virtual void reset() override; |
||||
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override; |
||||
|
||||
void setTranspose(float32_t transpose); |
||||
float32_t getTranspose() const; |
||||
|
||||
void setSize(float32_t size); |
||||
float32_t getSize() const; |
||||
|
||||
private: |
||||
typedef FxEngine<PITCH_SHIFTER_BUFFER_SIZE, Format::FORMAT_FLOAT32, false> Engine; |
||||
Engine engine_; |
||||
|
||||
const float32_t TransposeBoundary; |
||||
float32_t phase_; |
||||
float32_t transpose_; |
||||
float32_t ratio_; |
||||
float32_t size_; |
||||
float32_t sample_size_; |
||||
|
||||
IMPLEMENT_DUMP( |
||||
const size_t space = 12; |
||||
const size_t precision = 6; |
||||
|
||||
std::stringstream ss; |
||||
|
||||
out << "START " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl; |
||||
|
||||
SS_RESET(ss, precision, std::left); |
||||
SS__TEXT(ss, ' ', space, std::left, '|', "phase_"); |
||||
SS__TEXT(ss, ' ', space, std::left, '|', "transpose_"); |
||||
SS__TEXT(ss, ' ', space, std::left, '|', "ratio_"); |
||||
SS__TEXT(ss, ' ', space, std::left, '|', "size_"); |
||||
SS__TEXT(ss, ' ', space, std::left, '|', "sample_size_"); |
||||
out << "\t" << ss.str() << std::endl; |
||||
|
||||
SS_RESET(ss, precision, std::left); |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
out << "\t" << ss.str() << std::endl; |
||||
|
||||
SS_RESET(ss, precision, std::left); |
||||
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->phase_); |
||||
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->transpose_); |
||||
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->ratio_); |
||||
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->size_); |
||||
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->sample_size_); |
||||
out << "\t" << ss.str() << std::endl; |
||||
|
||||
if(deepInspection) |
||||
{ |
||||
this->engine_.dump(out, deepInspection, tag + ".engine_"); |
||||
} |
||||
|
||||
out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl;
|
||||
) |
||||
|
||||
IMPLEMENT_INSPECT( |
||||
size_t nb_errors = 0u; |
||||
|
||||
nb_errors += inspector(tag + ".phase_", this->phase_, 0.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".transpose_", this->transpose_, -PITCH_SHIFTER_TRANSPOSE_BOUNDARY, PITCH_SHIFTER_TRANSPOSE_BOUNDARY, deepInspection); |
||||
nb_errors += inspector(tag + ".ratio_", this->ratio_, 0.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".size_", this->size_, 0.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".sample_size_", this->sample_size_, 0.0f, 1.0f, deepInspection); |
||||
|
||||
if(deepInspection) |
||||
{ |
||||
nb_errors += this->engine_.inspect(inspector, deepInspection, tag + ".engine_"); |
||||
} |
||||
|
||||
return nb_errors; |
||||
) |
||||
}; |
@ -0,0 +1,135 @@ |
||||
#include "fx_shimmer_helper.h" |
||||
|
||||
const float lut_pitch_ratio_high[] = { |
||||
6.151958251e-04, 6.517772725e-04, 6.905339660e-04, 7.315952524e-04, |
||||
7.750981699e-04, 8.211879055e-04, 8.700182794e-04, 9.217522585e-04, |
||||
9.765625000e-04, 1.034631928e-03, 1.096154344e-03, 1.161335073e-03, |
||||
1.230391650e-03, 1.303554545e-03, 1.381067932e-03, 1.463190505e-03, |
||||
1.550196340e-03, 1.642375811e-03, 1.740036559e-03, 1.843504517e-03, |
||||
1.953125000e-03, 2.069263856e-03, 2.192308688e-03, 2.322670146e-03, |
||||
2.460783301e-03, 2.607109090e-03, 2.762135864e-03, 2.926381010e-03, |
||||
3.100392680e-03, 3.284751622e-03, 3.480073118e-03, 3.687009034e-03, |
||||
3.906250000e-03, 4.138527712e-03, 4.384617376e-03, 4.645340293e-03, |
||||
4.921566601e-03, 5.214218180e-03, 5.524271728e-03, 5.852762019e-03, |
||||
6.200785359e-03, 6.569503244e-03, 6.960146235e-03, 7.374018068e-03, |
||||
7.812500000e-03, 8.277055425e-03, 8.769234752e-03, 9.290680586e-03, |
||||
9.843133202e-03, 1.042843636e-02, 1.104854346e-02, 1.170552404e-02, |
||||
1.240157072e-02, 1.313900649e-02, 1.392029247e-02, 1.474803614e-02, |
||||
1.562500000e-02, 1.655411085e-02, 1.753846950e-02, 1.858136117e-02, |
||||
1.968626640e-02, 2.085687272e-02, 2.209708691e-02, 2.341104808e-02, |
||||
2.480314144e-02, 2.627801298e-02, 2.784058494e-02, 2.949607227e-02, |
||||
3.125000000e-02, 3.310822170e-02, 3.507693901e-02, 3.716272234e-02, |
||||
3.937253281e-02, 4.171374544e-02, 4.419417382e-02, 4.682209615e-02, |
||||
4.960628287e-02, 5.255602595e-02, 5.568116988e-02, 5.899214454e-02, |
||||
6.250000000e-02, 6.621644340e-02, 7.015387802e-02, 7.432544469e-02, |
||||
7.874506562e-02, 8.342749089e-02, 8.838834765e-02, 9.364419230e-02, |
||||
9.921256575e-02, 1.051120519e-01, 1.113623398e-01, 1.179842891e-01, |
||||
1.250000000e-01, 1.324328868e-01, 1.403077560e-01, 1.486508894e-01, |
||||
1.574901312e-01, 1.668549818e-01, 1.767766953e-01, 1.872883846e-01, |
||||
1.984251315e-01, 2.102241038e-01, 2.227246795e-01, 2.359685782e-01, |
||||
2.500000000e-01, 2.648657736e-01, 2.806155121e-01, 2.973017788e-01, |
||||
3.149802625e-01, 3.337099635e-01, 3.535533906e-01, 3.745767692e-01, |
||||
3.968502630e-01, 4.204482076e-01, 4.454493591e-01, 4.719371563e-01, |
||||
5.000000000e-01, 5.297315472e-01, 5.612310242e-01, 5.946035575e-01, |
||||
6.299605249e-01, 6.674199271e-01, 7.071067812e-01, 7.491535384e-01, |
||||
7.937005260e-01, 8.408964153e-01, 8.908987181e-01, 9.438743127e-01, |
||||
1.000000000e+00, 1.059463094e+00, 1.122462048e+00, 1.189207115e+00, |
||||
1.259921050e+00, 1.334839854e+00, 1.414213562e+00, 1.498307077e+00, |
||||
1.587401052e+00, 1.681792831e+00, 1.781797436e+00, 1.887748625e+00, |
||||
2.000000000e+00, 2.118926189e+00, 2.244924097e+00, 2.378414230e+00, |
||||
2.519842100e+00, 2.669679708e+00, 2.828427125e+00, 2.996614154e+00, |
||||
3.174802104e+00, 3.363585661e+00, 3.563594873e+00, 3.775497251e+00, |
||||
4.000000000e+00, 4.237852377e+00, 4.489848193e+00, 4.756828460e+00, |
||||
5.039684200e+00, 5.339359417e+00, 5.656854249e+00, 5.993228308e+00, |
||||
6.349604208e+00, 6.727171322e+00, 7.127189745e+00, 7.550994501e+00, |
||||
8.000000000e+00, 8.475704755e+00, 8.979696386e+00, 9.513656920e+00, |
||||
1.007936840e+01, 1.067871883e+01, 1.131370850e+01, 1.198645662e+01, |
||||
1.269920842e+01, 1.345434264e+01, 1.425437949e+01, 1.510198900e+01, |
||||
1.600000000e+01, 1.695140951e+01, 1.795939277e+01, 1.902731384e+01, |
||||
2.015873680e+01, 2.135743767e+01, 2.262741700e+01, 2.397291323e+01, |
||||
2.539841683e+01, 2.690868529e+01, 2.850875898e+01, 3.020397801e+01, |
||||
3.200000000e+01, 3.390281902e+01, 3.591878555e+01, 3.805462768e+01, |
||||
4.031747360e+01, 4.271487533e+01, 4.525483400e+01, 4.794582646e+01, |
||||
5.079683366e+01, 5.381737058e+01, 5.701751796e+01, 6.040795601e+01, |
||||
6.400000000e+01, 6.780563804e+01, 7.183757109e+01, 7.610925536e+01, |
||||
8.063494719e+01, 8.542975067e+01, 9.050966799e+01, 9.589165292e+01, |
||||
1.015936673e+02, 1.076347412e+02, 1.140350359e+02, 1.208159120e+02, |
||||
1.280000000e+02, 1.356112761e+02, 1.436751422e+02, 1.522185107e+02, |
||||
1.612698944e+02, 1.708595013e+02, 1.810193360e+02, 1.917833058e+02, |
||||
2.031873347e+02, 2.152694823e+02, 2.280700718e+02, 2.416318240e+02, |
||||
2.560000000e+02, 2.712225522e+02, 2.873502844e+02, 3.044370214e+02, |
||||
3.225397888e+02, 3.417190027e+02, 3.620386720e+02, 3.835666117e+02, |
||||
4.063746693e+02, 4.305389646e+02, 4.561401437e+02, 4.832636481e+02, |
||||
5.120000000e+02, 5.424451043e+02, 5.747005687e+02, 6.088740429e+02, |
||||
6.450795775e+02, 6.834380053e+02, 7.240773439e+02, 7.671332234e+02, |
||||
8.127493386e+02, 8.610779292e+02, 9.122802874e+02, 9.665272962e+02, |
||||
1.024000000e+03, 1.084890209e+03, 1.149401137e+03, 1.217748086e+03, |
||||
1.290159155e+03, 1.366876011e+03, 1.448154688e+03, 1.534266447e+03, |
||||
}; |
||||
|
||||
const float lut_pitch_ratio_low[] = { |
||||
1.000000000e+00, 1.000225659e+00, 1.000451370e+00, 1.000677131e+00, |
||||
1.000902943e+00, 1.001128806e+00, 1.001354720e+00, 1.001580685e+00, |
||||
1.001806701e+00, 1.002032768e+00, 1.002258886e+00, 1.002485055e+00, |
||||
1.002711275e+00, 1.002937546e+00, 1.003163868e+00, 1.003390242e+00, |
||||
1.003616666e+00, 1.003843141e+00, 1.004069668e+00, 1.004296246e+00, |
||||
1.004522874e+00, 1.004749554e+00, 1.004976285e+00, 1.005203068e+00, |
||||
1.005429901e+00, 1.005656786e+00, 1.005883722e+00, 1.006110709e+00, |
||||
1.006337747e+00, 1.006564836e+00, 1.006791977e+00, 1.007019169e+00, |
||||
1.007246412e+00, 1.007473707e+00, 1.007701053e+00, 1.007928450e+00, |
||||
1.008155898e+00, 1.008383398e+00, 1.008610949e+00, 1.008838551e+00, |
||||
1.009066205e+00, 1.009293910e+00, 1.009521667e+00, 1.009749475e+00, |
||||
1.009977334e+00, 1.010205245e+00, 1.010433207e+00, 1.010661221e+00, |
||||
1.010889286e+00, 1.011117403e+00, 1.011345571e+00, 1.011573790e+00, |
||||
1.011802061e+00, 1.012030384e+00, 1.012258758e+00, 1.012487183e+00, |
||||
1.012715661e+00, 1.012944189e+00, 1.013172770e+00, 1.013401401e+00, |
||||
1.013630085e+00, 1.013858820e+00, 1.014087607e+00, 1.014316445e+00, |
||||
1.014545335e+00, 1.014774277e+00, 1.015003270e+00, 1.015232315e+00, |
||||
1.015461411e+00, 1.015690560e+00, 1.015919760e+00, 1.016149011e+00, |
||||
1.016378315e+00, 1.016607670e+00, 1.016837077e+00, 1.017066536e+00, |
||||
1.017296046e+00, 1.017525609e+00, 1.017755223e+00, 1.017984889e+00, |
||||
1.018214607e+00, 1.018444376e+00, 1.018674198e+00, 1.018904071e+00, |
||||
1.019133996e+00, 1.019363973e+00, 1.019594002e+00, 1.019824083e+00, |
||||
1.020054216e+00, 1.020284401e+00, 1.020514637e+00, 1.020744926e+00, |
||||
1.020975266e+00, 1.021205659e+00, 1.021436104e+00, 1.021666600e+00, |
||||
1.021897149e+00, 1.022127749e+00, 1.022358402e+00, 1.022589107e+00, |
||||
1.022819863e+00, 1.023050672e+00, 1.023281533e+00, 1.023512446e+00, |
||||
1.023743411e+00, 1.023974428e+00, 1.024205498e+00, 1.024436619e+00, |
||||
1.024667793e+00, 1.024899019e+00, 1.025130297e+00, 1.025361627e+00, |
||||
1.025593009e+00, 1.025824444e+00, 1.026055931e+00, 1.026287470e+00, |
||||
1.026519061e+00, 1.026750705e+00, 1.026982401e+00, 1.027214149e+00, |
||||
1.027445949e+00, 1.027677802e+00, 1.027909707e+00, 1.028141664e+00, |
||||
1.028373674e+00, 1.028605736e+00, 1.028837851e+00, 1.029070017e+00, |
||||
1.029302237e+00, 1.029534508e+00, 1.029766832e+00, 1.029999209e+00, |
||||
1.030231638e+00, 1.030464119e+00, 1.030696653e+00, 1.030929239e+00, |
||||
1.031161878e+00, 1.031394569e+00, 1.031627313e+00, 1.031860109e+00, |
||||
1.032092958e+00, 1.032325859e+00, 1.032558813e+00, 1.032791820e+00, |
||||
1.033024879e+00, 1.033257991e+00, 1.033491155e+00, 1.033724372e+00, |
||||
1.033957641e+00, 1.034190964e+00, 1.034424338e+00, 1.034657766e+00, |
||||
1.034891246e+00, 1.035124779e+00, 1.035358364e+00, 1.035592003e+00, |
||||
1.035825694e+00, 1.036059437e+00, 1.036293234e+00, 1.036527083e+00, |
||||
1.036760985e+00, 1.036994940e+00, 1.037228947e+00, 1.037463008e+00, |
||||
1.037697121e+00, 1.037931287e+00, 1.038165506e+00, 1.038399777e+00, |
||||
1.038634102e+00, 1.038868479e+00, 1.039102910e+00, 1.039337393e+00, |
||||
1.039571929e+00, 1.039806518e+00, 1.040041160e+00, 1.040275855e+00, |
||||
1.040510603e+00, 1.040745404e+00, 1.040980258e+00, 1.041215165e+00, |
||||
1.041450125e+00, 1.041685138e+00, 1.041920204e+00, 1.042155323e+00, |
||||
1.042390495e+00, 1.042625720e+00, 1.042860998e+00, 1.043096329e+00, |
||||
1.043331714e+00, 1.043567151e+00, 1.043802642e+00, 1.044038185e+00, |
||||
1.044273782e+00, 1.044509433e+00, 1.044745136e+00, 1.044980892e+00, |
||||
1.045216702e+00, 1.045452565e+00, 1.045688481e+00, 1.045924450e+00, |
||||
1.046160473e+00, 1.046396549e+00, 1.046632678e+00, 1.046868860e+00, |
||||
1.047105096e+00, 1.047341385e+00, 1.047577727e+00, 1.047814123e+00, |
||||
1.048050572e+00, 1.048287074e+00, 1.048523630e+00, 1.048760239e+00, |
||||
1.048996902e+00, 1.049233618e+00, 1.049470387e+00, 1.049707210e+00, |
||||
1.049944086e+00, 1.050181015e+00, 1.050417999e+00, 1.050655035e+00, |
||||
1.050892125e+00, 1.051129269e+00, 1.051366466e+00, 1.051603717e+00, |
||||
1.051841021e+00, 1.052078378e+00, 1.052315790e+00, 1.052553255e+00, |
||||
1.052790773e+00, 1.053028345e+00, 1.053265971e+00, 1.053503650e+00, |
||||
1.053741383e+00, 1.053979169e+00, 1.054217010e+00, 1.054454903e+00, |
||||
1.054692851e+00, 1.054930852e+00, 1.055168907e+00, 1.055407016e+00, |
||||
1.055645178e+00, 1.055883395e+00, 1.056121664e+00, 1.056359988e+00, |
||||
1.056598366e+00, 1.056836797e+00, 1.057075282e+00, 1.057313821e+00, |
||||
1.057552413e+00, 1.057791060e+00, 1.058029760e+00, 1.058268515e+00, |
||||
1.058507323e+00, 1.058746185e+00, 1.058985101e+00, 1.059224071e+00, |
||||
}; |
@ -0,0 +1,34 @@ |
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// fx_shimmer_helper.h
|
||||
//
|
||||
// Helper class for the FX Shimmer FX that is ported from Mutable Instruments
|
||||
// Ported by: Vincent Gauché
|
||||
//
|
||||
|
||||
#pragma once |
||||
|
||||
#include "fx.h" |
||||
|
||||
extern const float lut_pitch_ratio_high[257]; |
||||
extern const float lut_pitch_ratio_low[257]; |
||||
|
||||
inline float32_t semitoneToRatio(float32_t semitones) |
||||
{ |
||||
float32_t pitch = semitones + 128.0f; |
||||
MAKE_INTEGRAL_FRACTIONAL(pitch); |
||||
|
||||
return lut_pitch_ratio_high[pitch_integral] * lut_pitch_ratio_low[static_cast<int32_t>(pitch_fractional * 256.0f)]; |
||||
} |
@ -0,0 +1,169 @@ |
||||
#include "fx_shimmer_reverb.h" |
||||
|
||||
#define TAIL , -1 |
||||
|
||||
ShimmerReverb::ShimmerReverb(float32_t sampling_frequency) :
|
||||
FXElement(sampling_frequency, 1.2f), |
||||
pitch_shifter_(sampling_frequency, PITCH_SHIFTER_TRANSPOSE_BOUNDARY), |
||||
lp_filter_(sampling_frequency, SVF::FilterMode::SVF_LP), |
||||
hp_filter_(sampling_frequency, SVF::FilterMode::SVF_HP), |
||||
reverberator_(sampling_frequency), |
||||
texture_(0.0f), |
||||
lp_cutoff_(0.0f), |
||||
hp_cutoff_(0.0f), |
||||
lpq_(0.0f), |
||||
amount_(0.0f), |
||||
feedback_(0.0f), |
||||
cutoff_(0.0f) |
||||
{ |
||||
this->setInputGain(0.2f); |
||||
this->setDiffusion(0.7f); |
||||
this->setCutoff(1.0f); |
||||
|
||||
this->reset(); |
||||
} |
||||
|
||||
ShimmerReverb::~ShimmerReverb() |
||||
{ |
||||
} |
||||
|
||||
void ShimmerReverb::reset() |
||||
{ |
||||
this->pitch_shifter_.reset(); |
||||
this->lp_filter_.reset(); |
||||
this->hp_filter_.reset(); |
||||
this->reverberator_.reset(); |
||||
} |
||||
|
||||
void ShimmerReverb::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) |
||||
{ |
||||
this->pitch_shifter_.processSample(inL, inR, outL, outR); |
||||
this->lp_filter_.processSample(outL, outR, outL, outR); |
||||
this->hp_filter_.processSample(outL, outR, outL, outR); |
||||
this->reverberator_.processSample(outL, outR, outL, outR); |
||||
|
||||
outL *= this->OutputLevelCorrector; |
||||
outR *= this->OutputLevelCorrector; |
||||
} |
||||
|
||||
void ShimmerReverb::setInputGain(float32_t input_gain) |
||||
{ |
||||
this->reverberator_.setInputGain(input_gain); |
||||
} |
||||
|
||||
float32_t ShimmerReverb::getInputGain() const |
||||
{ |
||||
return this->reverberator_.getInputGain(); |
||||
} |
||||
|
||||
void ShimmerReverb::setDiffusion(float32_t diffusion) |
||||
{ |
||||
this->reverberator_.setDiffusion(diffusion); |
||||
} |
||||
|
||||
float32_t ShimmerReverb::getDiffusion() const |
||||
{ |
||||
return this->reverberator_.getDiffusion(); |
||||
} |
||||
|
||||
void ShimmerReverb::setTime(float32_t time) |
||||
{ |
||||
this->reverberator_.setTime(time); |
||||
} |
||||
|
||||
float32_t ShimmerReverb::getTime() const |
||||
{ |
||||
return this->reverberator_.getTime(); |
||||
} |
||||
|
||||
void ShimmerReverb::setReverbAmount(float32_t amount) |
||||
{ |
||||
amount = constrain(amount, 0.0f, 1.0f); |
||||
if(this->amount_ != amount) |
||||
{ |
||||
this->amount_ = amount; |
||||
this->updateReverberatorCoefficients(); |
||||
} |
||||
} |
||||
|
||||
void ShimmerReverb::setTexture(float32_t texture) |
||||
{ |
||||
texture = constrain(texture, 0.0f, 1.0f); |
||||
if(this->texture_ != texture) |
||||
{ |
||||
this->texture_ = texture; |
||||
this->updateFilterCoefficients(); |
||||
} |
||||
} |
||||
|
||||
float32_t ShimmerReverb::getTexture() const |
||||
{ |
||||
return this->texture_; |
||||
} |
||||
|
||||
void ShimmerReverb::setFeedback(float32_t feedback) |
||||
{ |
||||
feedback = constrain(feedback, 0.0f, 1.0f); |
||||
if(this->feedback_ != feedback) |
||||
{ |
||||
this->feedback_ = feedback; |
||||
this->updateFilterCoefficients(); |
||||
this->updateReverberatorCoefficients(); |
||||
} |
||||
} |
||||
|
||||
float32_t ShimmerReverb::getFeedback() const |
||||
{ |
||||
return this->feedback_; |
||||
} |
||||
|
||||
void ShimmerReverb::setCutoff(float32_t cutoff) |
||||
{ |
||||
cutoff = constrain(cutoff, 0.0f, 1.0f); |
||||
if(this->cutoff_ != cutoff) |
||||
{ |
||||
this->cutoff_ = cutoff; |
||||
this->updateFilterCoefficients(); |
||||
} |
||||
} |
||||
|
||||
void ShimmerReverb::updateFilterCoefficients() |
||||
{ |
||||
this->lp_cutoff_ = constrain(0.50f * semitoneToRatio((this->cutoff_ < 0.5f ? this->cutoff_ - 0.5f : 0.0f ) * 216.0f), 0.0f, 0.499f); |
||||
this->hp_cutoff_ = constrain(0.25f * semitoneToRatio((this->cutoff_ < 0.5f ? -0.5f : this->cutoff_ - 1.0f) * 216.0f), 0.0f, 0.499f); |
||||
this->lpq_ = 1.0f + 3.0f * (1.0f - this->feedback_) * (0.5f - this->lp_cutoff_); |
||||
|
||||
this->lp_filter_.setFQ<SVF::FrequencyApproximation::FrequencyFast>(this->lp_cutoff_, this->lpq_); |
||||
this->hp_filter_.setFQ<SVF::FrequencyApproximation::FrequencyFast>(this->hp_cutoff_, 1.0f); |
||||
|
||||
this->reverberator_.setLP(0.6f + 0.37f * this->feedback_); |
||||
} |
||||
|
||||
void ShimmerReverb::updateReverberatorCoefficients() |
||||
{ |
||||
float32_t reverb_amount = this->amount_ * 0.95f; |
||||
reverb_amount += this->feedback_ * (2.0f - this->feedback_); |
||||
reverb_amount = constrain(reverb_amount, 0.0f, 1.0f); |
||||
|
||||
this->setTime(0.35f + 0.63f * reverb_amount); |
||||
} |
||||
|
||||
void ShimmerReverb::setPitch(float32_t pitch) |
||||
{ |
||||
this->pitch_shifter_.setTranspose(pitch); |
||||
} |
||||
|
||||
float32_t ShimmerReverb::getPitch() const |
||||
{ |
||||
return this->pitch_shifter_.getTranspose(); |
||||
} |
||||
|
||||
void ShimmerReverb::setSize(float32_t size) |
||||
{ |
||||
this->pitch_shifter_.setSize(size); |
||||
} |
||||
|
||||
float32_t ShimmerReverb::getSize() const |
||||
{ |
||||
return this->pitch_shifter_.getSize(); |
||||
} |
@ -0,0 +1,111 @@ |
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//
|
||||
// fx_shimmer_reverb.h
|
||||
//
|
||||
// Stereo ShimmerReverb Reverb proposed in the context of the MiniDexed project
|
||||
// It is adapted from the ShimmerReverb Reverb that could be found on Cloud EuroRack module from Mutable Instrruments
|
||||
// Ported by: Vincent Gauché
|
||||
//
|
||||
#pragma once |
||||
|
||||
#include "fx_components.h" |
||||
#include "fx_svf.h" |
||||
#include "fx_shimmer_helper.h" |
||||
#include "fx_pitch_shifter.h" |
||||
#include "fx_reverberator.h" |
||||
|
||||
class ShimmerReverb : public FXElement |
||||
{ |
||||
DISALLOW_COPY_AND_ASSIGN(ShimmerReverb); |
||||
|
||||
public: |
||||
ShimmerReverb(float32_t sampling_rate); |
||||
virtual ~ShimmerReverb(); |
||||
|
||||
virtual void reset() override; |
||||
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override; |
||||
|
||||
void setInputGain(float32_t input_gain); |
||||
float32_t getInputGain() const; |
||||
|
||||
void setDiffusion(float32_t diffusion); |
||||
float32_t getDiffusion() const; |
||||
|
||||
void setTime(float32_t time); |
||||
float32_t getTime() const; |
||||
|
||||
void setReverbAmount(float32_t amount); |
||||
|
||||
void setTexture(float32_t texture); |
||||
float32_t getTexture() const; |
||||
|
||||
void setFeedback(float32_t feedback); |
||||
float32_t getFeedback() const; |
||||
|
||||
void setPitch(float32_t pitch); |
||||
float32_t getPitch() const; |
||||
|
||||
void setSize(float32_t size); |
||||
float32_t getSize() const; |
||||
|
||||
void setCutoff(float32_t cutoff); |
||||
float32_t getCutoff() const; |
||||
|
||||
private: |
||||
void updateFilterCoefficients(); |
||||
void updateReverberatorCoefficients(); |
||||
|
||||
PitchShifter pitch_shifter_; |
||||
SVF lp_filter_; |
||||
SVF hp_filter_; |
||||
Reverberator reverberator_; |
||||
|
||||
float32_t texture_; |
||||
float32_t lp_cutoff_; |
||||
float32_t hp_cutoff_; |
||||
float32_t lpq_; |
||||
float32_t amount_; |
||||
float32_t feedback_; |
||||
float32_t cutoff_; |
||||
|
||||
IMPLEMENT_DUMP( |
||||
out << "START " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl; |
||||
|
||||
out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl; |
||||
) |
||||
|
||||
IMPLEMENT_INSPECT( |
||||
size_t nb_errors = 0u; |
||||
|
||||
nb_errors += inspector(tag + ".texture_", this->texture_, 0.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".lp_cutoff_", this->lp_cutoff_, 0.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".hp_cutoff_", this->hp_cutoff_, 0.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".lpq_", this->lpq_, 0.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".amount_", this->amount_, 0.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".feedback_", this->feedback_, 0.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".cutoff_", this->cutoff_, 0.0f, 1.0f, deepInspection); |
||||
|
||||
if(deepInspection) |
||||
{ |
||||
nb_errors += this->pitch_shifter_.inspect(inspector, deepInspection, tag + ".pitch_shifter_"); |
||||
nb_errors += this->lp_filter_.inspect(inspector, deepInspection, tag + ".lp_filter_"); |
||||
nb_errors += this->hp_filter_.inspect(inspector, deepInspection, tag + ".hp_filter_"); |
||||
nb_errors += this->reverberator_.inspect(inspector, deepInspection, tag + ".reverberator_"); |
||||
} |
||||
|
||||
return nb_errors; |
||||
) |
||||
}; |
@ -0,0 +1,104 @@ |
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// fx_unit2.h
|
||||
//
|
||||
// Unit of FX that handle the mute parameter
|
||||
// Author: Vincent Gauché
|
||||
//
|
||||
#pragma once |
||||
|
||||
#include "fx_components.h" |
||||
|
||||
class FXUnitModule2 |
||||
{ |
||||
DISALLOW_COPY_AND_ASSIGN(FXUnitModule2); |
||||
|
||||
public: |
||||
FXUnitModule2(bool mute = false) |
||||
{ |
||||
this->setMute(mute); |
||||
} |
||||
|
||||
virtual ~FXUnitModule2() |
||||
{ |
||||
} |
||||
|
||||
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) = 0; |
||||
|
||||
inline void setMute(bool mute = false) |
||||
{ |
||||
this->mute_ = mute; |
||||
} |
||||
|
||||
inline bool isMute() const |
||||
{ |
||||
return this->mute_; |
||||
} |
||||
|
||||
protected: |
||||
bool mute_; |
||||
}; |
||||
|
||||
template<typename _FXElement> |
||||
class FXUnit2 : public virtual FXUnitModule2, public virtual _FXElement |
||||
{ |
||||
DISALLOW_COPY_AND_ASSIGN(FXUnit2); |
||||
|
||||
public: |
||||
FXUnit2(float32_t sampling_rate, bool mute = false) : |
||||
FXUnitModule2(mute), |
||||
_FXElement(sampling_rate), |
||||
is_reset_(false) |
||||
{ |
||||
this->setMute(mute); |
||||
} |
||||
|
||||
virtual ~FXUnit2() |
||||
{ |
||||
} |
||||
|
||||
inline void reset() override |
||||
{ |
||||
if(!this->is_reset_) |
||||
{ |
||||
_FXElement::reset(); |
||||
this->is_reset_ = true; |
||||
} |
||||
} |
||||
|
||||
inline void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override |
||||
{ |
||||
if(this->isMute()) |
||||
{ |
||||
this->reset(); |
||||
|
||||
outL = 0.0f; |
||||
outR = 0.0f; |
||||
} |
||||
else if(this->bypassFXProcess()) |
||||
{ |
||||
outL = inL; |
||||
outR = inR; |
||||
} |
||||
else |
||||
{ |
||||
this->is_reset_ = false; |
||||
_FXElement::processSample(inL, inR, outL, outR); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
bool is_reset_; |
||||
}; |
@ -0,0 +1,793 @@ |
||||
//
|
||||
// mixing_console.hpp
|
||||
//
|
||||
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
|
||||
// Author: Vincent Gauché
|
||||
// Copyright (C) 2022 The MiniDexed Team
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
// Implementation of the MixingConsole class defined in mixing_console.h
|
||||
#pragma once |
||||
|
||||
#include "mixing_console_constants.h" |
||||
#include "fx_tube.h" |
||||
#include "fx_chorus.h" |
||||
#include "fx_flanger.h" |
||||
#include "fx_orbitone.h" |
||||
#include "fx_phaser.h" |
||||
#include "fx_delay.h" |
||||
#include "effect_platervbstereo.h" |
||||
#include "fx_reverberator.h" |
||||
#include "fx_dry.h" |
||||
#include "fx_unit2.hpp" |
||||
|
||||
template<size_t nb_inputs> |
||||
class MixingConsole : public FXBase |
||||
{ |
||||
DISALLOW_COPY_AND_ASSIGN(MixingConsole); |
||||
|
||||
public: |
||||
MixingConsole(float32_t sampling_rate, size_t buffer_size, bool swapStereoImage = false); |
||||
~MixingConsole(); |
||||
|
||||
inline size_t getChannelNumber() const; |
||||
|
||||
inline void bypass(bool bypass); |
||||
inline bool bypass() const; |
||||
|
||||
// Send section
|
||||
inline void setChannelLevel(size_t in, float32_t lvl); |
||||
inline void setPan(size_t in, float32_t pan); |
||||
inline void swapStereoImage(bool swap); |
||||
inline void setSendLevel(size_t in, MixerOutput fx, float32_t lvl); |
||||
inline void setInputSample(size_t in, float32_t sampleL, float32_t sampleR); |
||||
inline void setInputSampleBuffer(size_t in, float32_t* samples); |
||||
|
||||
// Return section
|
||||
inline void setFXSendLevel(MixerOutput fromFX, MixerOutput toFX, float32_t lvl); |
||||
inline void setReturnSample(MixerOutput ret, float32_t sampleL, float32_t sampleR); |
||||
|
||||
// Get FX
|
||||
inline FXElement* getFX(size_t fx); |
||||
inline FXUnit2<Tube>* getTube(); |
||||
inline FXUnit2<Chorus>* getChorus(); |
||||
inline FXUnit2<Flanger>* getFlanger(); |
||||
inline FXUnit2<Orbitone>* getOrbitone(); |
||||
inline FXUnit2<Phaser>* getPhaser(); |
||||
inline FXUnit2<Delay>* getDelay(); |
||||
inline FXUnit2<AudioEffectPlateReverb>* getPlateReverb(); |
||||
inline FXUnit2<Reverberator>* getReverberator(); |
||||
inline FXUnit2<Dry>* getDry(); |
||||
|
||||
// Processing
|
||||
inline void init(); |
||||
inline void reset() override; |
||||
inline void preProcessInputSampleBuffer(size_t in, size_t nSamples); |
||||
inline void injectInputSamples(size_t in, float32_t* samplesL, float32_t* samplesR, size_t nSamples); |
||||
inline void processSample(float32_t& outL, float32_t& outR); |
||||
void process(float32_t* outL, float32_t* outR); |
||||
void process(float32_t* outLR); |
||||
|
||||
protected: |
||||
inline void updatePan(size_t in); |
||||
inline void setLevel(size_t in, MixerOutput fx, float32_t lvl); |
||||
inline void setSample(size_t in, float32_t sampleL, float32_t sampleR); |
||||
|
||||
private: |
||||
static inline float32_t weighted_sum(const float32_t* data, const float32_t* weights, size_t size); |
||||
|
||||
const size_t BufferSize; |
||||
|
||||
bool bypass_; |
||||
|
||||
float32_t channel_level_[nb_inputs]; |
||||
float32_t pan_[StereoChannels::kNumChannels + 1][nb_inputs]; |
||||
bool swap_stereo_image_; |
||||
float32_t* tg_input_sample_buffer_[nb_inputs]; |
||||
float32_t* input_sample_buffer_[StereoChannels::kNumChannels][nb_inputs]; |
||||
float32_t input_samples_[StereoChannels::kNumChannels][nb_inputs + MixerOutput::kFXCount - 1]; |
||||
float32_t levels_[MixerOutput::kFXCount][nb_inputs + MixerOutput::kFXCount - 1]; |
||||
volatile size_t m_nSamples; |
||||
|
||||
FXElement* fx_[MixerOutput::kFXCount]; |
||||
FXUnit2<Tube>* tube_; |
||||
FXUnit2<Chorus>* chorus_; |
||||
FXUnit2<Flanger>* flanger_; |
||||
FXUnit2<Orbitone>* orbitone_; |
||||
FXUnit2<Phaser>* phaser_; |
||||
FXUnit2<Delay>* delay_; |
||||
FXUnit2<AudioEffectPlateReverb>* plate_reverb_; |
||||
FXUnit2<Reverberator>* reverberator_; |
||||
FXUnit2<Dry>* dry_; |
||||
|
||||
IMPLEMENT_DUMP( |
||||
const size_t space = 9; |
||||
const size_t precision = 5; |
||||
|
||||
std::stringstream ss; |
||||
|
||||
out << "START " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl; |
||||
|
||||
out << "\t" << "Input levels & Pan:" << std::endl; |
||||
{ |
||||
SS_RESET(ss, precision, std::left); |
||||
SS_SPACE(ss, ' ', space, std::left, '|'); |
||||
SS__TEXT(ss, ' ', space, std::left, '|', "Level"); |
||||
SS__TEXT(ss, ' ', space, std::left, '|', "Pan L"); |
||||
SS__TEXT(ss, ' ', space, std::left, '|', "Pan R"); |
||||
SS__TEXT(ss, ' ', space, std::left, '|', "Pan"); |
||||
out << "\t" << ss.str() << std::endl; |
||||
|
||||
SS_RESET(ss, precision, std::left); |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
out << "\t" << ss.str() << std::endl; |
||||
|
||||
for(size_t i = 0; i < nb_inputs; ++i) |
||||
{ |
||||
std::stringstream s; |
||||
s << "* Input "; |
||||
s << (i + 1); |
||||
|
||||
SS_RESET(ss, precision, std::left); |
||||
SS__TEXT(ss, ' ', space, std::left, '|', s.str()); |
||||
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->channel_level_[i]); |
||||
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->pan_[StereoChannels::Left][i]); |
||||
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->pan_[StereoChannels::Right][i]); |
||||
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->pan_[StereoChannels::kNumChannels][i]); |
||||
|
||||
out << "\t" << ss.str() << std::endl; |
||||
} |
||||
} |
||||
out << std::endl; |
||||
|
||||
out << "\t" << "Mixing Console input samples:" << std::endl; |
||||
{ |
||||
SS_RESET(ss, precision, std::left); |
||||
SS_SPACE(ss, ' ', space, std::left, '|'); |
||||
for(size_t i = 0; i < nb_inputs; ++i) |
||||
{ |
||||
std::stringstream s; |
||||
s << "Input "; |
||||
s << (i + 1); |
||||
|
||||
SS__TEXT(ss, ' ', space, std::left, '|', s.str()); |
||||
} |
||||
for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i) |
||||
{ |
||||
std::string s = toString(static_cast<MixerOutput>(i)); |
||||
s.resize(space); |
||||
SS__TEXT(ss, ' ', space, std::left, '|', s.c_str()); |
||||
} |
||||
out << "\t" << ss.str() << std::endl; |
||||
|
||||
SS_RESET(ss, precision, std::left); |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
for(size_t i = 0; i < nb_inputs; ++i) |
||||
{ |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
} |
||||
for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i) |
||||
{ |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
} |
||||
out << "\t" << ss.str() << std::endl; |
||||
|
||||
const char* LR = "LR"; |
||||
for(size_t c = 0; c < StereoChannels::kNumChannels; ++c) |
||||
{ |
||||
std::stringstream s; |
||||
s << "* Input "; |
||||
s << LR[c]; |
||||
|
||||
SS_RESET(ss, precision, std::left); |
||||
SS__TEXT(ss, ' ', space, std::left, '|', s.str()); |
||||
for(size_t i = 0; i < (nb_inputs + MixerOutput::kFXCount - 1); ++i) |
||||
{ |
||||
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->input_samples_[c][i]); |
||||
} |
||||
out << "\t" << ss.str() << std::endl; |
||||
} |
||||
} |
||||
out << std::endl; |
||||
|
||||
out << "\t" << "Mixing Console levels:" << std::endl; |
||||
{ |
||||
SS_RESET(ss, precision, std::left); |
||||
SS_SPACE(ss, ' ', space, std::left, '|'); |
||||
for(size_t i = 0; i < nb_inputs; ++i) |
||||
{ |
||||
std::stringstream s; |
||||
s << "Input "; |
||||
s << (i + 1); |
||||
|
||||
SS__TEXT(ss, ' ', space, std::left, '|', s.str()); |
||||
} |
||||
for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i) |
||||
{ |
||||
std::string s = toString(static_cast<MixerOutput>(i)); |
||||
s.resize(space); |
||||
SS__TEXT(ss, ' ', space, std::left, '|', s.c_str()); |
||||
} |
||||
out << "\t" << ss.str() << std::endl; |
||||
|
||||
SS_RESET(ss, precision, std::left); |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
for(size_t i = 0; i < nb_inputs; ++i) |
||||
{ |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
} |
||||
for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i) |
||||
{ |
||||
SS_SPACE(ss, '-', space, std::left, '+'); |
||||
} |
||||
out << "\t" << ss.str() << std::endl; |
||||
|
||||
for(size_t c = 0; c < MixerOutput::kFXCount; ++c) |
||||
{ |
||||
SS_RESET(ss, precision, std::left); |
||||
std::string s = toString(static_cast<MixerOutput>(c)); |
||||
s.resize(space); |
||||
SS__TEXT(ss, ' ', space, std::left, '|', s.c_str()); |
||||
for(size_t i = 0; i < (nb_inputs + MixerOutput::kFXCount - 1); ++i) |
||||
{ |
||||
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->levels_[c][i]); |
||||
} |
||||
out << "\t" << ss.str() << std::endl; |
||||
} |
||||
} |
||||
out << std::endl; |
||||
|
||||
if(deepInspection) |
||||
{ |
||||
this->tube_->dump(out, deepInspection, tag + ".tube_"); |
||||
this->chorus_->dump(out, deepInspection, tag + ".chorus_"); |
||||
this->flanger_->dump(out, deepInspection, tag + ".flanger_"); |
||||
this->orbitone_->dump(out, deepInspection, tag + ".orbitone_"); |
||||
this->phaser_->dump(out, deepInspection, tag + ".phaser_"); |
||||
this->delay_->dump(out, deepInspection, tag + ".delay_"); |
||||
this->plate_reverb_->dump(out, deepInspection, tag + ".plate_reverb_"); |
||||
this->reverberator_->dump(out, deepInspection, tag + ".reverberator_"); |
||||
this->dry_->dump(out, deepInspection, tag + ".dry_"); |
||||
} |
||||
|
||||
out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl; |
||||
) |
||||
|
||||
IMPLEMENT_INSPECT( |
||||
size_t nb_errors = 0; |
||||
|
||||
for(size_t i = 0; i < nb_inputs; ++i) |
||||
{ |
||||
nb_errors += inspector(tag + ".level[ input #" + std::to_string(i) + " ]" , this->channel_level_[i], -1.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".pan[ L ][ input #" + std::to_string(i) + " ]", this->pan_[StereoChannels::Left][i], -1.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".pan[ R ][ input #" + std::to_string(i) + " ]", this->pan_[StereoChannels::Right][i], -1.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".pan[ input #" + std::to_string(i) + " ]", this->pan_[StereoChannels::kNumChannels][i], -1.0f, 1.0f, deepInspection); |
||||
} |
||||
|
||||
for(size_t i = 0; i < nb_inputs; ++i) |
||||
{ |
||||
nb_errors += inspector(tag + ".input[ L ][ input #" + std::to_string(i) + " ]", this->input_samples_[StereoChannels::Left ][i], -1.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".input[ R ][ input #" + std::to_string(i) + " ]", this->input_samples_[StereoChannels::Right][i], -1.0f, 1.0f, deepInspection); |
||||
} |
||||
|
||||
for(size_t i = nb_inputs; i < (nb_inputs + MixerOutput::kFXCount - 1); ++i) |
||||
{ |
||||
nb_errors += inspector(tag + ".input[ L ][ input " + toString(static_cast<MixerOutput>(i - nb_inputs)) + " ]", this->input_samples_[StereoChannels::Left ][i], -1.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".input[ R ][ input " + toString(static_cast<MixerOutput>(i - nb_inputs)) + " ]", this->input_samples_[StereoChannels::Right][i], -1.0f, 1.0f, deepInspection); |
||||
} |
||||
|
||||
for(size_t c = 0; c < MixerOutput::kFXCount; ++c) |
||||
{ |
||||
for(size_t i = 0; i < (nb_inputs + MixerOutput::kFXCount - 1); ++i) |
||||
{ |
||||
nb_errors += inspector(tag + ".levels[ " + std::to_string(c) + " ][ " + std::to_string(i) + " ]", this->levels_[c][i], -1.0f, 1.0f, deepInspection); |
||||
} |
||||
} |
||||
|
||||
if(deepInspection) |
||||
{ |
||||
for(size_t i = 0; i < nb_inputs; ++i) |
||||
{ |
||||
for(size_t k = 0; k < this->m_nSamples; ++k) |
||||
{ |
||||
nb_errors += inspector(tag + ".input_sample_buffer_[ L ][ " + std::to_string(i) + " ][ " + std::to_string(k) +" ] ", this->input_sample_buffer_[StereoChannels::Left ][i][k], -1.0f, 1.0f, deepInspection); |
||||
nb_errors += inspector(tag + ".input_sample_buffer_[ R ][ " + std::to_string(i) + " ][ " + std::to_string(k) +" ] ", this->input_sample_buffer_[StereoChannels::Right][i][k], -1.0f, 1.0f, deepInspection); |
||||
} |
||||
} |
||||
|
||||
nb_errors += this->tube_->inspect(inspector, deepInspection, tag + ".tube_"); |
||||
nb_errors += this->chorus_->inspect(inspector, deepInspection, tag + ".chorus_"); |
||||
nb_errors += this->flanger_->inspect(inspector, deepInspection, tag + ".flanger_"); |
||||
nb_errors += this->orbitone_->inspect(inspector, deepInspection, tag + ".orbitone_"); |
||||
nb_errors += this->phaser_->inspect(inspector, deepInspection, tag + ".phaser_"); |
||||
nb_errors += this->delay_->inspect(inspector, deepInspection, tag + ".delay_"); |
||||
nb_errors += this->plate_reverb_->inspect(inspector, deepInspection, tag + ".plate_reverb_"); |
||||
nb_errors += this->reverberator_->inspect(inspector, deepInspection, tag + ".reverberator_"); |
||||
nb_errors += this->dry_->inspect(inspector, deepInspection, tag + ".dry_"); |
||||
} |
||||
|
||||
return nb_errors; |
||||
) |
||||
}; |
||||
|
||||
template<size_t nb_inputs> |
||||
float32_t MixingConsole<nb_inputs>::weighted_sum(const float32_t* data, const float32_t* weights, size_t size) |
||||
{ |
||||
float32_t res = arm_weighted_sum_f32(data, weights, size); |
||||
|
||||
return std::isnan(res) ? 0.0f : res; |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
MixingConsole<nb_inputs>::MixingConsole(float32_t sampling_rate, size_t buffer_size, bool swapStereoImage) : |
||||
FXBase(sampling_rate), |
||||
BufferSize(buffer_size), |
||||
bypass_(true), |
||||
swap_stereo_image_(swapStereoImage), |
||||
m_nSamples(0) |
||||
{ |
||||
for(size_t i = 0; i < nb_inputs; ++i) |
||||
{ |
||||
this->tg_input_sample_buffer_[i] = nullptr; |
||||
this->input_sample_buffer_[StereoChannels::Left ][i] = new float32_t[this->BufferSize]; |
||||
this->input_sample_buffer_[StereoChannels::Right][i] = new float32_t[this->BufferSize]; |
||||
memset(this->input_sample_buffer_[StereoChannels::Left ][i], 0, sizeof(float32_t) * this->BufferSize); |
||||
memset(this->input_sample_buffer_[StereoChannels::Right][i], 0, sizeof(float32_t) * this->BufferSize); |
||||
} |
||||
|
||||
this->fx_[MixerOutput::FX_Tube] = this->tube_ = new FXUnit2<Tube>(sampling_rate); |
||||
this->fx_[MixerOutput::FX_Chorus] = this->chorus_ = new FXUnit2<Chorus>(sampling_rate); |
||||
this->fx_[MixerOutput::FX_Flanger] = this->flanger_ = new FXUnit2<Flanger>(sampling_rate); |
||||
this->fx_[MixerOutput::FX_Orbitone] = this->orbitone_ = new FXUnit2<Orbitone>(sampling_rate); |
||||
this->fx_[MixerOutput::FX_Phaser] = this->phaser_ = new FXUnit2<Phaser>(sampling_rate); |
||||
this->fx_[MixerOutput::FX_Delay] = this->delay_ = new FXUnit2<Delay>(sampling_rate); |
||||
this->fx_[MixerOutput::FX_PlateReverb] = this->plate_reverb_ = new FXUnit2<AudioEffectPlateReverb>(sampling_rate); |
||||
this->fx_[MixerOutput::FX_Reverberator] = this->reverberator_ = new FXUnit2<Reverberator>(sampling_rate); |
||||
this->fx_[MixerOutput::MainOutput] = this->dry_ = new FXUnit2<Dry>(sampling_rate); |
||||
|
||||
this->bypass(false); |
||||
|
||||
this->init(); |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
MixingConsole<nb_inputs>::~MixingConsole() |
||||
{ |
||||
for(size_t i = 0; i < MixerOutput::kFXCount; ++i) |
||||
{ |
||||
delete this->fx_[i]; |
||||
} |
||||
|
||||
for(size_t i = 0; i < nb_inputs; ++i) |
||||
{ |
||||
delete[] this->input_sample_buffer_[StereoChannels::Left ][i]; |
||||
delete[] this->input_sample_buffer_[StereoChannels::Right][i]; |
||||
|
||||
// The tg_input_sample_buffer_ buffers are not freed as MixingConsole is not the creator
|
||||
// They must be freed by the creator of the buffers
|
||||
this->tg_input_sample_buffer_[i] = nullptr; |
||||
} |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::bypass(bool bypass) |
||||
{ |
||||
if(this->bypass_ != bypass) |
||||
{ |
||||
this->bypass_ = bypass; |
||||
|
||||
for(size_t fx = MixerOutput::FX_Tube; fx < MixerOutput::kFXCount; ++fx) |
||||
{ |
||||
this->getFX(fx)->bypassFXProcess(bypass); |
||||
} |
||||
|
||||
if(!bypass) |
||||
{ |
||||
this->reset(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
bool MixingConsole<nb_inputs>::bypass() const |
||||
{ |
||||
return this->bypass_; |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
size_t MixingConsole<nb_inputs>::getChannelNumber() const |
||||
{ |
||||
return nb_inputs; |
||||
} |
||||
|
||||
// Send section
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::setChannelLevel(size_t in, float32_t lvl) |
||||
{ |
||||
assert(in < nb_inputs); |
||||
|
||||
lvl = constrain(lvl, 0.0f, 1.0f); |
||||
if(lvl == this->channel_level_[in]) return; |
||||
|
||||
this->channel_level_[in] = lvl; |
||||
this->updatePan(in); |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::setPan(size_t in, float32_t pan) |
||||
{ |
||||
assert(in < nb_inputs); |
||||
|
||||
pan = constrain(pan, 0.0f, 1.0f); |
||||
|
||||
if(pan == this->pan_[StereoChannels::kNumChannels][in]) return; |
||||
|
||||
this->pan_[StereoChannels::kNumChannels][in] = pan; |
||||
this->updatePan(in); |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::swapStereoImage(bool swap) |
||||
{ |
||||
this->swap_stereo_image_ = swap; |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::setSendLevel(size_t in, MixerOutput fx, float32_t lvl) |
||||
{ |
||||
assert(in < nb_inputs); |
||||
assert(fx < MixerOutput::kFXCount); |
||||
|
||||
this->setLevel(in, fx, lvl); |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::setInputSample(size_t in, float32_t sampleL, float32_t sampleR) |
||||
{ |
||||
assert(in < nb_inputs); |
||||
|
||||
this->setSample(in, sampleL, sampleR); |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::setInputSampleBuffer(size_t in, float32_t* samples) |
||||
{ |
||||
assert(in < nb_inputs); |
||||
|
||||
this->tg_input_sample_buffer_[in] = samples; |
||||
} |
||||
|
||||
// Return section
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::setFXSendLevel(MixerOutput fromFX, MixerOutput toFX, float32_t lvl) |
||||
{ |
||||
assert(fromFX < (MixerOutput::kFXCount - 1)); |
||||
assert(toFX < MixerOutput::kFXCount); |
||||
|
||||
if(fromFX == toFX) |
||||
{ |
||||
// An FX cannot feedback on itself
|
||||
return; |
||||
} |
||||
|
||||
this->setLevel(nb_inputs + fromFX, toFX, lvl); |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::setReturnSample(MixerOutput ret, float32_t sampleL, float32_t sampleR) |
||||
{ |
||||
assert(ret < (MixerOutput::kFXCount - 1)); |
||||
|
||||
this->setSample(nb_inputs + ret, sampleL, sampleR); |
||||
} |
||||
|
||||
// Get FX
|
||||
template<size_t nb_inputs> |
||||
FXElement* MixingConsole<nb_inputs>::getFX(size_t fx) |
||||
{ |
||||
assert(fx < MixerOutput::kFXCount); |
||||
return this->fx_[fx]; |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
FXUnit2<Tube>* MixingConsole<nb_inputs>::getTube() |
||||
{ |
||||
return this->tube_; |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
FXUnit2<Chorus>* MixingConsole<nb_inputs>::getChorus() |
||||
{ |
||||
return this->chorus_; |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
FXUnit2<Flanger>* MixingConsole<nb_inputs>::getFlanger() |
||||
{ |
||||
return this->flanger_; |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
FXUnit2<Orbitone>* MixingConsole<nb_inputs>::getOrbitone() |
||||
{ |
||||
return this->orbitone_; |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
FXUnit2<Phaser>* MixingConsole<nb_inputs>::getPhaser() |
||||
{ |
||||
return this->phaser_; |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
FXUnit2<Delay>* MixingConsole<nb_inputs>::getDelay() |
||||
{ |
||||
return this->delay_; |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
FXUnit2<AudioEffectPlateReverb>* MixingConsole<nb_inputs>::getPlateReverb() |
||||
{ |
||||
return this->plate_reverb_; |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
FXUnit2<Reverberator>* MixingConsole<nb_inputs>::getReverberator() |
||||
{ |
||||
return this->reverberator_; |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
FXUnit2<Dry>* MixingConsole<nb_inputs>::getDry() |
||||
{ |
||||
return this->dry_; |
||||
} |
||||
|
||||
// Processing
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::init() |
||||
{ |
||||
memset(this->channel_level_, 0, nb_inputs * sizeof(float32_t)); |
||||
for(size_t i = 0; i <= StereoChannels::kNumChannels; ++i) memset(this->pan_[i], 0, nb_inputs * sizeof(float32_t)); |
||||
|
||||
for(size_t i = 0; i < MixerOutput::kFXCount; ++i) |
||||
memset(this->levels_[i], 0, (nb_inputs + MixerOutput::kFXCount - 1) * sizeof(float32_t)); |
||||
|
||||
for(size_t i = 0; i < StereoChannels::kNumChannels; ++i)
|
||||
memset(this->input_samples_[i], 0, (nb_inputs + MixerOutput::kFXCount - 1) * sizeof(float32_t)); |
||||
|
||||
this->reset(); |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::reset() |
||||
{ |
||||
for(size_t i = 0; i < nb_inputs; ++i) |
||||
{ |
||||
memset(this->input_sample_buffer_[StereoChannels::Left ][i], 0, this->BufferSize * sizeof(float32_t)); |
||||
memset(this->input_sample_buffer_[StereoChannels::Right][i], 0, this->BufferSize * sizeof(float32_t)); |
||||
} |
||||
|
||||
for(size_t i = 0; i < MixerOutput::kFXCount; ++i) |
||||
{ |
||||
this->fx_[i]->reset(); |
||||
|
||||
if(i != MixerOutput::MainOutput) |
||||
{ |
||||
this->setReturnSample(static_cast<MixerOutput>(i), 0.0f, 0.0f); |
||||
} |
||||
} |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::injectInputSamples(size_t in, float32_t* samplesL, float32_t* samplesR, size_t nSamples) |
||||
{ |
||||
// Only used to input stereo samples
|
||||
assert(in < nb_inputs); |
||||
this->m_nSamples = std::min(nSamples, this->BufferSize); |
||||
if(samplesL != nullptr) |
||||
{ |
||||
memcpy(this->input_sample_buffer_[StereoChannels::Left ][in], samplesL, this->m_nSamples * sizeof(float32_t)); |
||||
} |
||||
else |
||||
{ |
||||
memset(this->input_sample_buffer_[StereoChannels::Left ][in], 0, this->m_nSamples * sizeof(float32_t)); |
||||
} |
||||
|
||||
if(samplesR != nullptr) |
||||
{ |
||||
memcpy(this->input_sample_buffer_[StereoChannels::Right][in], samplesR, this->m_nSamples * sizeof(float32_t)); |
||||
} |
||||
else |
||||
{ |
||||
memset(this->input_sample_buffer_[StereoChannels::Right][in], 0, this->m_nSamples * sizeof(float32_t)); |
||||
} |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::preProcessInputSampleBuffer(size_t in, size_t nSamples) |
||||
{ |
||||
assert(in < nb_inputs); |
||||
assert(nSamples <= this->BufferSize); |
||||
|
||||
float32_t* samples = this->tg_input_sample_buffer_[in]; |
||||
if(samples == nullptr) return; |
||||
|
||||
this->m_nSamples = nSamples; |
||||
if(nSamples > 0) |
||||
{ |
||||
if(this->pan_[StereoChannels::Left ][in] != 0.0f) |
||||
{ |
||||
arm_scale_f32(samples, this->pan_[StereoChannels::Left ][in], this->input_sample_buffer_[StereoChannels::Left ][in], nSamples); |
||||
} |
||||
else |
||||
{ |
||||
memset(this->input_sample_buffer_[StereoChannels::Left ][in], 0, nSamples * sizeof(float32_t)); |
||||
} |
||||
|
||||
if(this->pan_[StereoChannels::Right][in] != 0.0f) |
||||
{ |
||||
arm_scale_f32(samples, this->pan_[StereoChannels::Right][in], this->input_sample_buffer_[StereoChannels::Right][in], nSamples); |
||||
} |
||||
else |
||||
{ |
||||
memset(this->input_sample_buffer_[StereoChannels::Right][in], 0, nSamples * sizeof(float32_t)); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
memset(this->input_sample_buffer_[StereoChannels::Left ][in], 0, this->BufferSize * sizeof(float32_t)); |
||||
memset(this->input_sample_buffer_[StereoChannels::Right][in], 0, this->BufferSize * sizeof(float32_t)); |
||||
} |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::processSample(float32_t& outL, float32_t& outR) |
||||
{ |
||||
const size_t nBuffers = nb_inputs + MixerOutput::kFXCount - 1; |
||||
|
||||
float32_t fx_input_[StereoChannels::kNumChannels]; |
||||
float32_t fx_output_[StereoChannels::kNumChannels]; |
||||
for(size_t fxId = 0; fxId < MixerOutput::kFXCount; ++fxId) |
||||
{ |
||||
// Compute the samples that will feed the FX
|
||||
fx_input_[StereoChannels::Left ] = MixingConsole<nb_inputs>::weighted_sum(this->input_samples_[StereoChannels::Left ], this->levels_[fxId], nBuffers); |
||||
fx_input_[StereoChannels::Right] = MixingConsole<nb_inputs>::weighted_sum(this->input_samples_[StereoChannels::Right], this->levels_[fxId], nBuffers); |
||||
|
||||
// Process the FX
|
||||
this->fx_[fxId]->processSample( |
||||
fx_input_[StereoChannels::Left ], |
||||
fx_input_[StereoChannels::Right], |
||||
fx_output_[StereoChannels::Left ], |
||||
fx_output_[StereoChannels::Right] |
||||
); |
||||
|
||||
if(fxId != MixerOutput::MainOutput) |
||||
{ |
||||
// Feedback the processed samples except for the main output
|
||||
this->setReturnSample( |
||||
static_cast<MixerOutput>(fxId),
|
||||
fx_output_[StereoChannels::Left], |
||||
fx_output_[StereoChannels::Right] |
||||
); |
||||
} |
||||
else |
||||
{ |
||||
// Returns the main output sample
|
||||
outL = fx_output_[StereoChannels::Left]; |
||||
outR = fx_output_[StereoChannels::Right]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::process(float32_t* outL, float32_t* outR) |
||||
{ |
||||
size_t nSamples = this->m_nSamples; |
||||
for(size_t s = 0; s < nSamples; ++s) |
||||
{ |
||||
for(size_t in = 0; in < nb_inputs; ++in) |
||||
{ |
||||
this->setSample( |
||||
in,
|
||||
this->input_sample_buffer_[StereoChannels::Left ][in][s],
|
||||
this->input_sample_buffer_[StereoChannels::Right][in][s] |
||||
); |
||||
} |
||||
|
||||
if(this->swap_stereo_image_) |
||||
{ |
||||
this->processSample(*outR, *outL); |
||||
} |
||||
else |
||||
{ |
||||
this->processSample(*outL, *outR); |
||||
} |
||||
|
||||
++outL; |
||||
++outR; |
||||
} |
||||
|
||||
this->m_nSamples = 0; |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::process(float32_t* outLR) |
||||
{ |
||||
size_t nSamples = this->m_nSamples; |
||||
for(size_t s = 0; s < nSamples; ++s) |
||||
{ |
||||
for(size_t in = 0; in < nb_inputs; ++in) |
||||
{ |
||||
this->setSample( |
||||
in,
|
||||
this->input_sample_buffer_[StereoChannels::Left ][in][s],
|
||||
this->input_sample_buffer_[StereoChannels::Right][in][s] |
||||
); |
||||
} |
||||
|
||||
if(this->swap_stereo_image_) |
||||
{ |
||||
this->processSample(*(outLR + 1), *outLR); |
||||
} |
||||
else |
||||
{ |
||||
this->processSample(*outLR, *(outLR + 1)); |
||||
|
||||
} |
||||
|
||||
outLR += 2; |
||||
} |
||||
|
||||
this->m_nSamples = 0; |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::updatePan(size_t in) |
||||
{ |
||||
float32_t pan = this->pan_[StereoChannels::kNumChannels][in] * Constants::MPI_2; |
||||
if(this->channel_level_[in] != 0.0f) |
||||
{ |
||||
this->pan_[StereoChannels::Left ][in] = InterpolatedSineOscillator::Cos(pan) * this->channel_level_[in]; |
||||
this->pan_[StereoChannels::Right][in] = InterpolatedSineOscillator::Sin(pan) * this->channel_level_[in]; |
||||
} |
||||
else |
||||
{ |
||||
this->pan_[StereoChannels::Left ][in] =
|
||||
this->pan_[StereoChannels::Right][in] = 0.0f; |
||||
} |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::setLevel(size_t in, MixerOutput fx, float32_t lvl) |
||||
{ |
||||
assert(in < (nb_inputs + MixerOutput::kFXCount - 1)); |
||||
assert(fx < MixerOutput::kFXCount); |
||||
|
||||
this->levels_[fx][in] = constrain(lvl, 0.0f, 1.0f); |
||||
} |
||||
|
||||
template<size_t nb_inputs> |
||||
void MixingConsole<nb_inputs>::setSample(size_t in, float32_t sampleL, float32_t sampleR) |
||||
{ |
||||
assert(in < (nb_inputs + MixerOutput::kFXCount - 1)); |
||||
this->input_samples_[StereoChannels::Left ][in] = sampleL; |
||||
this->input_samples_[StereoChannels::Right][in] = sampleR; |
||||
} |
@ -0,0 +1,58 @@ |
||||
#pragma once |
||||
|
||||
#include "extra_features.h" |
||||
|
||||
#include <array> |
||||
#include <cstring> |
||||
#include <stdexcept> |
||||
|
||||
enum MixerOutput |
||||
{ |
||||
OutputStart = 0, |
||||
FX_Tube = 0, |
||||
FX_Chorus, |
||||
FX_Flanger, |
||||
FX_Orbitone, |
||||
FX_Phaser, |
||||
FX_Delay, |
||||
FX_PlateReverb, |
||||
FX_Reverberator, |
||||
MainOutput, |
||||
kFXCount |
||||
}; |
||||
|
||||
inline std::string toString(MixerOutput enum_val) |
||||
{ |
||||
static const std::array<const char*, MixerOutput::kFXCount> names |
||||
{
|
||||
"Tube", |
||||
"Chorus", |
||||
"Flanger", |
||||
"Orbitone", |
||||
"Phaser", |
||||
"Delay", |
||||
"PlateReverb", |
||||
"Reverberator", |
||||
"MainOutput" |
||||
}; |
||||
static_assert(names.size() == MixerOutput::kFXCount, "Enum MixerOutput and string array size mismatch"); |
||||
|
||||
return std::string(names[static_cast<size_t>(enum_val)]); |
||||
} |
||||
|
||||
#define TO_INDEX_CHECK(str, fx) if(std::strcmp(str, toString(fx).c_str()) == 0) return fx; |
||||
|
||||
inline MixerOutput toIndex(const char* str) |
||||
{ |
||||
TO_INDEX_CHECK(str, MixerOutput::FX_Tube); |
||||
TO_INDEX_CHECK(str, MixerOutput::FX_Chorus); |
||||
TO_INDEX_CHECK(str, MixerOutput::FX_Flanger); |
||||
TO_INDEX_CHECK(str, MixerOutput::FX_Orbitone); |
||||
TO_INDEX_CHECK(str, MixerOutput::FX_Phaser); |
||||
TO_INDEX_CHECK(str, MixerOutput::FX_Delay); |
||||
TO_INDEX_CHECK(str, MixerOutput::FX_PlateReverb); |
||||
TO_INDEX_CHECK(str, MixerOutput::FX_Reverberator); |
||||
TO_INDEX_CHECK(str, MixerOutput::MainOutput); |
||||
|
||||
throw std::invalid_argument("Invalid MixerOutput string"); |
||||
} |
@ -0,0 +1,390 @@ |
||||
#include "test_fx_helper.h" |
||||
|
||||
#include <fstream> |
||||
#include <sstream> |
||||
#include <iomanip> |
||||
|
||||
class FastLFODebugger |
||||
{ |
||||
public: |
||||
FastLFODebugger(float32_t sampling_rate, float32_t min_frequency, float32_t max_frequency, float32_t initial_phase, bool centered) : |
||||
SamplingFrequency(sampling_rate), |
||||
InitialPhase(initial_phase), |
||||
min_frequency_(min_frequency), |
||||
max_frequency_(max_frequency), |
||||
centered_(centered), |
||||
frequency_(0.0f), |
||||
nb_sub_increment_(1), |
||||
sub_increment_(0), |
||||
y0_(0.0f), |
||||
y1_(0.0f), |
||||
iir_coefficient_(0.0f), |
||||
initial_amplitude_(0.0f) |
||||
{ |
||||
assert(this->min_frequency_ <= this->max_frequency_); |
||||
assert(this->max_frequency_ < sampling_rate / 2.0f); |
||||
|
||||
this->setFrequency(this->min_frequency_); |
||||
} |
||||
|
||||
~FastLFODebugger() |
||||
{ |
||||
} |
||||
|
||||
inline float32_t getSamplingRate() const |
||||
{ |
||||
return this->SamplingFrequency; |
||||
} |
||||
|
||||
void setNormalizedFrequency(float32_t normalized_frequency) |
||||
{ |
||||
normalized_frequency = constrain(normalized_frequency, 0.0f, 1.0f); |
||||
if(this->normalized_frequency_ != normalized_frequency) |
||||
{ |
||||
float32_t frequency = mapfloat(normalized_frequency, 0.0f, 1.0f, this->min_frequency_, this->max_frequency_); |
||||
this->normalized_frequency_ = normalized_frequency; |
||||
this->frequency_ = frequency; |
||||
this->unitary_frequency_ = this->frequency_ / this->getSamplingRate(); |
||||
|
||||
this->nb_sub_increment_ = (frequency >= 3.0f ? 1 : 300); |
||||
this->unitary_frequency_ *= this->nb_sub_increment_; |
||||
|
||||
this->updateCoefficient(1.0f); |
||||
} |
||||
} |
||||
|
||||
float32_t getNormalizedFrequency() const |
||||
{ |
||||
return this->normalized_frequency_; |
||||
} |
||||
|
||||
void setFrequency(float32_t frequency) |
||||
{ |
||||
frequency = constrain(frequency, this->min_frequency_, this->max_frequency_); |
||||
if(this->frequency_ != frequency) |
||||
{ |
||||
float32_t normalized_frequency = mapfloat(frequency, this->min_frequency_, this->max_frequency_, 0.0f, 1.0f); |
||||
this->normalized_frequency_ = normalized_frequency; |
||||
this->frequency_ = frequency; |
||||
this->unitary_frequency_ = this->frequency_ / this->getSamplingRate(); |
||||
|
||||
this->nb_sub_increment_ = (frequency >= 3.0f ? 1 : 300); |
||||
this->unitary_frequency_ *= this->nb_sub_increment_; |
||||
|
||||
this->updateCoefficient(1.0f); |
||||
} |
||||
} |
||||
|
||||
float32_t getFrequency() const |
||||
{ |
||||
return this->frequency_; |
||||
} |
||||
|
||||
void updateCoefficient(float32_t correction_ratio) |
||||
{ |
||||
float32_t frequency = this->unitary_frequency_ * correction_ratio; |
||||
|
||||
float32_t sign = 16.0f; |
||||
frequency -= 0.25f; |
||||
if(frequency < 0.0f) |
||||
{ |
||||
frequency = -frequency; |
||||
} |
||||
else |
||||
{ |
||||
if(frequency > 0.5f) |
||||
{ |
||||
frequency -= 0.5f; |
||||
} |
||||
else |
||||
{ |
||||
sign = -16.0f; |
||||
} |
||||
} |
||||
|
||||
this->iir_coefficient_ = sign * frequency * (1.0f - 2.0f * frequency); |
||||
this->initial_amplitude_ = this->iir_coefficient_ * 0.25f; |
||||
|
||||
this->reset(correction_ratio); |
||||
} |
||||
|
||||
void reset(float32_t correction_ratio = 1.0f) |
||||
{ |
||||
// static const float32_t epsilon = 1e-7;
|
||||
|
||||
this->sub_increment_ = 0.0f; |
||||
|
||||
// computing cos(0) = sin(-PI/2)
|
||||
this->y1_ = this->initial_amplitude_; |
||||
this->y0_ = 0.5f; |
||||
|
||||
// if(this->unitary_frequency_ == 0.0f)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// float32_t p_i = 2.0f * PI * this->unitary_frequency_ * correction_ratio / static_cast<float32_t>(this->nb_sub_increment_);
|
||||
// float32_t p = PI / 2.0f;
|
||||
// float32_t t_p = this->InitialPhase;
|
||||
// if(t_p < p)
|
||||
// {
|
||||
// p -= 2.0f* PI;
|
||||
// }
|
||||
// float32_t current = 3.0f;
|
||||
// while(abs(current, sin(this->InitialPhase)) > epsilon)
|
||||
// {
|
||||
// std::cout << "phase: " << p << std::endl;
|
||||
// this->process();
|
||||
// p += p_i;
|
||||
// if(p > (6.0f * PI))
|
||||
// cout << "ERROR: FLO is not precise enough" <<
|
||||
// return;
|
||||
// }
|
||||
} |
||||
|
||||
float32_t process() |
||||
{ |
||||
float32_t temp = this->y0_; |
||||
float32_t current = temp + 0.5f; |
||||
if(this->centered_) |
||||
{ |
||||
current = current * 2.0f - 1.0f; |
||||
} |
||||
|
||||
if(this->sub_increment_ == 0) |
||||
{ |
||||
this->y0_ = this->iir_coefficient_ * this->y0_ - this->y1_; |
||||
this->y1_ = temp; |
||||
this->current_ = current; |
||||
return current; |
||||
} |
||||
|
||||
this->sub_increment_++; |
||||
if(this->sub_increment_ >= this->nb_sub_increment_) |
||||
{ |
||||
this->sub_increment_ = 0; |
||||
} |
||||
|
||||
return mapfloat(this->sub_increment_, 0, this->nb_sub_increment_, this->current_, current); |
||||
} |
||||
|
||||
float32_t current() const |
||||
{ |
||||
return this->current_; |
||||
} |
||||
|
||||
private: |
||||
const float32_t SamplingFrequency; |
||||
const float32_t InitialPhase; |
||||
const float32_t min_frequency_; |
||||
const float32_t max_frequency_; |
||||
const bool centered_; |
||||
float32_t frequency_; |
||||
float32_t normalized_frequency_; |
||||
float32_t unitary_frequency_; |
||||
size_t nb_sub_increment_; |
||||
size_t sub_increment_; |
||||
|
||||
float32_t y0_; |
||||
float32_t y1_; |
||||
float32_t iir_coefficient_; |
||||
float32_t initial_amplitude_; |
||||
float32_t current_; |
||||
}; |
||||
|
||||
void updateCorrectionStep(float32_t& sign, float32_t& correction_ratio_step, int direction) |
||||
{ |
||||
if(sign == direction) |
||||
{ |
||||
// does nothing
|
||||
} |
||||
else if(sign == -direction) |
||||
{ |
||||
sign = static_cast<float32_t>(direction); |
||||
correction_ratio_step /= 2.0f; |
||||
} |
||||
else |
||||
{ |
||||
sign = static_cast<float32_t>(direction); |
||||
} |
||||
|
||||
if(direction > 0) |
||||
{ |
||||
std::cout << "LFO is too slow - correction ratio step becomes: " << (sign * correction_ratio_step); |
||||
} |
||||
else if(direction < 0) |
||||
{ |
||||
std::cout << "LFO is too fast - correction ratio step becomes: " << (sign * correction_ratio_step); |
||||
} |
||||
} |
||||
|
||||
TEST(BetaTesta, FastLFO) |
||||
{ |
||||
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); |
||||
std::string full_test_name = test_info->test_case_name(); |
||||
full_test_name += "."; |
||||
full_test_name += test_info->name(); |
||||
|
||||
size_t NB = static_cast<size_t>(1.0f * SAMPLING_FREQUENCY); |
||||
const float32_t freq = 1.5f; |
||||
const float32_t init_phase = PI / 2.0f; |
||||
float32_t correction_ratio = 1.0f; |
||||
float32_t correction_ratio_step = 8.0f/ SAMPLING_FREQUENCY; |
||||
|
||||
FastLFODebugger lfo1(SAMPLING_FREQUENCY, freq, 440.0f, init_phase, true); |
||||
lfo1.setFrequency(freq); |
||||
|
||||
const float32_t epsilon = 1e-3; |
||||
|
||||
int nbTrials = 100000; |
||||
float32_t maxDiff; |
||||
float32_t sign = 0.0f; |
||||
float32_t phase; |
||||
float32_t phase_increment; |
||||
size_t maxOK = 0; |
||||
float32_t best_correction = correction_ratio; |
||||
while(nbTrials > 0) |
||||
{ |
||||
maxDiff = 0.0f; |
||||
phase = init_phase; |
||||
correction_ratio += sign * correction_ratio_step; |
||||
std::cout << std::setprecision(9) << std::fixed << " - Testing correction_ratio: " << correction_ratio << std::endl; |
||||
lfo1.updateCoefficient(correction_ratio); |
||||
phase_increment = freq / SAMPLING_FREQUENCY; |
||||
|
||||
for(size_t i = 0; i < NB; ++i) |
||||
{ |
||||
float32_t v1 = lfo1.process(); |
||||
float32_t v2 = sin(phase); |
||||
// std::cout << std::setprecision(9) << std::fixed << " + phase: " << phase << " // v1: " << v1 << " / v2: " << v2 << " => diff: " << (v2 - v1);
|
||||
|
||||
float32_t diff = abs(v1 - v2); |
||||
if(diff > maxDiff) maxDiff = diff; |
||||
|
||||
// std::cout << " - OK: " << ((diff < epsilon) ? "Yes" : "No") << std::endl;
|
||||
|
||||
if(diff > epsilon) |
||||
{ |
||||
if(maxOK < i) |
||||
{ |
||||
maxOK = i + 1; |
||||
best_correction = correction_ratio; |
||||
} |
||||
|
||||
int quater = 0; |
||||
if(phase > (PI / 2.0f)) ++quater; |
||||
if(phase > PI) ++quater; |
||||
if(phase > (3.0f * PI / 2.0f)) ++quater; |
||||
|
||||
if(v1 < v2) |
||||
{ |
||||
switch (quater) |
||||
{ |
||||
case 0: |
||||
case 4: |
||||
// Sinus phase [0, PI / 2] => [0.00, 1.00]
|
||||
// Sinus phase [3 * PI / 2, 2 * PI] => [-1.00, 0.00]
|
||||
// lfo1 is too slow
|
||||
updateCorrectionStep(sign, correction_ratio_step, +1); |
||||
break; |
||||
|
||||
case 1: |
||||
case 3: |
||||
// Sinus phase [PI / 2, PI] => [1.00, 0.00]
|
||||
// Sinus phase [PI, 3 * PI / 2] => [0.00, -1.00]
|
||||
// lfo1 is too fast
|
||||
updateCorrectionStep(sign, correction_ratio_step, -1); |
||||
break; |
||||
|
||||
default: |
||||
FAIL() << "Issue on phase: " << phase; |
||||
break; |
||||
} |
||||
break; |
||||
} |
||||
else |
||||
{ |
||||
switch (quater) |
||||
{ |
||||
case 0: |
||||
case 4: |
||||
// Sinus phase [0, PI / 2] => [0.00, 1.00]
|
||||
// Sinus phase [3 * PI / 2, 2 * PI] => [-1.00, 0.00]
|
||||
// lfo1 is too fast
|
||||
updateCorrectionStep(sign, correction_ratio_step, -1); |
||||
break; |
||||
|
||||
case 1: |
||||
case 3: |
||||
// Sinus phase [PI / 2, PI] => [1.00, 0.00]
|
||||
// Sinus phase [PI, 3 * PI / 2] => [0.00, -1.00]
|
||||
// lfo1 is too slow
|
||||
updateCorrectionStep(sign, correction_ratio_step, +1); |
||||
break; |
||||
|
||||
default: |
||||
FAIL() << "Issue on phase: " << phase; |
||||
break; |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if(correction_ratio_step < 1e-9) FAIL() << "correction_ratio_step became too small. maxOK = " << maxOK << " with best_correction = " << best_correction; |
||||
|
||||
phase += phase_increment; |
||||
if(phase > 2.0f * PI) phase -= 2.0f * PI; |
||||
} |
||||
|
||||
--nbTrials; |
||||
} |
||||
if(nbTrials > -2) |
||||
std::cout << "Correct correction ratio = " << correction_ratio << " with maxDiff = " << maxDiff << std::endl; |
||||
else |
||||
std::cout << "No matching correction ratio" << std::endl; |
||||
|
||||
std::cout << "maxOK = " << maxOK << " with best_correction = " << best_correction << std::endl;; |
||||
|
||||
|
||||
// std::stringstream ssFst;
|
||||
// std::stringstream ssSin;
|
||||
|
||||
// for(size_t i = 0; i < NB; ++i)
|
||||
// {
|
||||
// ssFst << lfo1.process() << (i == (NB - 1) ? "" : ", ");
|
||||
// ssSin << sin(2.0f * PI * freq * i / SAMPLING_FREQUENCY + init_phase) << (i == (NB - 1) ? "" : ", ");
|
||||
// }
|
||||
|
||||
// std::ofstream _fst(getResultFile(full_test_name + ".fst.data", true));
|
||||
// _fst << ssFst.str();
|
||||
// _fst.close();
|
||||
|
||||
// std::ofstream _sin(getResultFile(full_test_name + ".sin.data", true));
|
||||
// _sin << ssSin.str();
|
||||
// _sin.close();
|
||||
|
||||
|
||||
// std::ofstream out(getResultFile(full_test_name + ".data.m", true));
|
||||
// out << "# m file to tune FastLFO component" << std::endl << std::endl;
|
||||
// out << "# Parameters:" << std::endl
|
||||
// << "# + frequency: " << freq << "Hz" << std::endl
|
||||
// << "# + # samples: " << NB << std::endl << std::endl;
|
||||
|
||||
// out << "time = 0 : " << (NB - 1) << ";" << std::endl;
|
||||
// out << "fst_lfo = load(\"-ascii\", \"" << full_test_name << ".fst.data\");" << std::endl;
|
||||
// out << "sin_lfo = load(\"-ascii\", \"" << full_test_name << ".sin.data\");" << std::endl;
|
||||
|
||||
// out << std::endl << std::endl;
|
||||
|
||||
// out << "plot(time, fst_lfo, '-', time, sin_lfo, '-');" << std::endl;
|
||||
// out << "title('LFO tuning');" << std::endl;
|
||||
// out << "legend('FastLFODebugger', 'Sinus');" << std::endl;
|
||||
|
||||
// out.close();
|
||||
} |
||||
|
||||
int main(int argc, char **argv) |
||||
{ |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
@ -0,0 +1,565 @@ |
||||
#include <gtest/gtest.h> |
||||
#include <cmath> |
||||
|
||||
#include "test_fx_helper.h" |
||||
#include "wave.h" |
||||
|
||||
#include "../mixing_console.hpp" |
||||
|
||||
#define NB_MIXER_CHANNELS 8 |
||||
|
||||
class MixingConsoleScenarioTest : public testing::TestWithParam<MixerOutput> {}; |
||||
|
||||
typedef MixingConsole<NB_MIXER_CHANNELS> Mixer; |
||||
|
||||
void setupMixingConsoleFX(Mixer* mixer); |
||||
void setupMixingConsoleFX(Mixer* mixer, int scenarioId, size_t channel = 0); |
||||
|
||||
TEST_P(MixingConsoleScenarioTest, MixerOutputTest) |
||||
{ |
||||
MixerOutput v = this->GetParam(); |
||||
std::string str = toString(v); |
||||
MixerOutput mo = toIndex(str.c_str()); |
||||
ASSERT_EQ(v, mo); |
||||
} |
||||
|
||||
INSTANTIATE_TEST_SUITE_P(MixerOutputTest, MixingConsoleScenarioTest, testing::Range(MixerOutput::OutputStart, MixerOutput::kFXCount)); |
||||
|
||||
void setupMixingConsoleFX(Mixer* mixer) |
||||
{ |
||||
mixer->setChannelLevel(0, 1.0f); |
||||
mixer->setPan(0, 0.5f); |
||||
|
||||
mixer->getTube()->setMute(false); |
||||
mixer->getTube()->setOverdrive(0.85f); |
||||
|
||||
mixer->getChorus()->setMute(false); |
||||
mixer->getChorus()->setRate(0.4f); |
||||
mixer->getChorus()->setDepth(0.7f); |
||||
|
||||
mixer->getFlanger()->setMute(false); |
||||
mixer->getFlanger()->setRate(0.03f); |
||||
mixer->getFlanger()->setDepth(0.75f); |
||||
mixer->getFlanger()->setFeedback(0.7f); |
||||
|
||||
mixer->getOrbitone()->setMute(false); |
||||
mixer->getOrbitone()->setRate(0.4f); |
||||
mixer->getOrbitone()->setDepth(0.8f); |
||||
|
||||
mixer->getPhaser()->setMute(false); |
||||
mixer->getPhaser()->setRate(0.1f); |
||||
mixer->getPhaser()->setDepth(1.0f); |
||||
mixer->getPhaser()->setFeedback(0.7f); |
||||
mixer->getPhaser()->setNbStages(12); |
||||
|
||||
mixer->getDelay()->setMute(false); |
||||
mixer->getDelay()->setLeftDelayTime(0.15f); |
||||
mixer->getDelay()->setLeftDelayTime(0.20f); |
||||
mixer->getDelay()->setFeedback(0.7f); |
||||
|
||||
mixer->getPlateReverb()->setMute(false); |
||||
mixer->getPlateReverb()->set_bypass(false); |
||||
mixer->getPlateReverb()->size(0.7f); |
||||
mixer->getPlateReverb()->hidamp(0.5f); |
||||
mixer->getPlateReverb()->lodamp(0.5f); |
||||
mixer->getPlateReverb()->lowpass(0.3f); |
||||
mixer->getPlateReverb()->diffusion(0.65f); |
||||
mixer->getPlateReverb()->level(1.0f); |
||||
|
||||
mixer->getReverberator()->setMute(false); |
||||
mixer->getReverberator()->setInputGain(0.65f); |
||||
mixer->getReverberator()->setTime(0.89f); |
||||
mixer->getReverberator()->setDiffusion(0.75f); |
||||
mixer->getReverberator()->setLP(0.8f); |
||||
} |
||||
|
||||
#define ACTIVE_FX(activity, scenarioId, fx) activity[MixerOutput::fx] = ((scenarioId & (1 << MixerOutput::fx)) == (1 << MixerOutput::fx)) |
||||
|
||||
void setupMixingConsoleFX(Mixer* mixer, int scenarioId, size_t channel) |
||||
{ |
||||
mixer->setChannelLevel(channel, 1.0f); |
||||
mixer->setPan(channel, 0.5f); |
||||
|
||||
bool fxActivity[MixerOutput::kFXCount - 1]; |
||||
ACTIVE_FX(fxActivity, scenarioId, FX_Tube); |
||||
ACTIVE_FX(fxActivity, scenarioId, FX_Chorus); |
||||
ACTIVE_FX(fxActivity, scenarioId, FX_Flanger); |
||||
ACTIVE_FX(fxActivity, scenarioId, FX_Orbitone); |
||||
ACTIVE_FX(fxActivity, scenarioId, FX_Phaser); |
||||
ACTIVE_FX(fxActivity, scenarioId, FX_Delay); |
||||
ACTIVE_FX(fxActivity, scenarioId, FX_PlateReverb); |
||||
ACTIVE_FX(fxActivity, scenarioId, FX_Reverberator); |
||||
|
||||
size_t nbActiveFX = 0; |
||||
MixerOutput previousActivatedFX = MixerOutput::MainOutput; |
||||
|
||||
for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i) |
||||
{ |
||||
if(fxActivity[i]) |
||||
{ |
||||
nbActiveFX++;
|
||||
if(nbActiveFX == 1) |
||||
{ |
||||
mixer->setSendLevel(channel, static_cast<MixerOutput>(i), 1.0f); |
||||
} |
||||
else |
||||
{ |
||||
mixer->setFXSendLevel(previousActivatedFX, static_cast<MixerOutput>(i), 1.0f); |
||||
} |
||||
previousActivatedFX = static_cast<MixerOutput>(i); |
||||
} |
||||
} |
||||
|
||||
if(previousActivatedFX == MixerOutput::MainOutput) |
||||
{ |
||||
mixer->setSendLevel(channel, MixerOutput::MainOutput, 1.0f); |
||||
} |
||||
else |
||||
{ |
||||
mixer->setSendLevel(channel, MixerOutput::MainOutput, 0.25f); |
||||
mixer->setFXSendLevel(previousActivatedFX, MixerOutput::MainOutput, 0.75f); |
||||
} |
||||
} |
||||
|
||||
TEST(MixingConsole, ZeroSamplesTest) |
||||
{ |
||||
static const size_t length = 4; |
||||
|
||||
Mixer mixer(SAMPLING_FREQUENCY, length); |
||||
ASSERT_EQ(0, FULL_INSPECT((&mixer), true)); |
||||
|
||||
mixer.reset(); |
||||
ASSERT_EQ(0, FULL_INSPECT((&mixer), true)); |
||||
|
||||
setupMixingConsoleFX(&mixer); |
||||
ASSERT_EQ(0, FULL_INSPECT((&mixer), true)); |
||||
|
||||
mixer.setChannelLevel(0, 1.0f); |
||||
ASSERT_EQ(0, FULL_INSPECT((&mixer), true)); |
||||
|
||||
mixer.setPan(0, 0.5f); |
||||
ASSERT_EQ(0, FULL_INSPECT((&mixer), true)); |
||||
|
||||
float32_t samples[] = {0.0f, 0.0f, 0.0f, 0.0f}; |
||||
mixer.setInputSampleBuffer(0, samples); |
||||
mixer.preProcessInputSampleBuffer(0, 4); |
||||
ASSERT_EQ(0, FULL_INSPECT((&mixer), true)); |
||||
|
||||
mixer.setSendLevel(0, MixerOutput::MainOutput, 1.0f); |
||||
ASSERT_EQ(0, FULL_INSPECT((&mixer), true)); |
||||
|
||||
float32_t outL[4]; |
||||
float32_t outR[4]; |
||||
mixer.process(outL, outR); |
||||
ASSERT_EQ(0, FULL_INSPECT((&mixer), true)); |
||||
|
||||
mixer.setSendLevel(0, MixerOutput::FX_Tube, 1.0f); |
||||
mixer.setSendLevel(0, MixerOutput::FX_Delay, 1.0f); |
||||
mixer.setSendLevel(0, MixerOutput::FX_PlateReverb, 1.0f); |
||||
|
||||
mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Orbitone, 1.0f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::MainOutput, 0.5f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Orbitone, MixerOutput::FX_PlateReverb, 1.0f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Delay, MixerOutput::MainOutput, 0.5f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_PlateReverb, MixerOutput::MainOutput, 0.5f); |
||||
ASSERT_EQ(0, FULL_INSPECT((&mixer), true)); |
||||
} |
||||
|
||||
TEST(MixingConsole, DryProcessing) |
||||
{ |
||||
static const float32_t epsilon = 1e-4; |
||||
static const size_t length = 2; |
||||
|
||||
Mixer mixer(SAMPLING_FREQUENCY, length); |
||||
mixer.reset(); |
||||
mixer.setChannelLevel(0, 1.0f); |
||||
mixer.setPan(0, 0.5f); |
||||
|
||||
mixer.setSendLevel(0, MixerOutput::FX_Tube, 0.0f); |
||||
mixer.setSendLevel(0, MixerOutput::FX_Chorus, 0.0f); |
||||
mixer.setSendLevel(0, MixerOutput::FX_Flanger, 0.0f); |
||||
mixer.setSendLevel(0, MixerOutput::FX_Orbitone, 0.0f); |
||||
mixer.setSendLevel(0, MixerOutput::FX_Phaser, 0.0f); |
||||
mixer.setSendLevel(0, MixerOutput::FX_Delay, 0.0f); |
||||
mixer.setSendLevel(0, MixerOutput::FX_PlateReverb, 0.0f); |
||||
mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 0.0f); |
||||
|
||||
for(size_t i = MixerOutput::OutputStart; i < (MixerOutput::kFXCount - 1); ++i) |
||||
{ |
||||
mixer.setFXSendLevel(static_cast<MixerOutput>(i), MixerOutput::MainOutput, 0.0f); |
||||
} |
||||
|
||||
mixer.setSendLevel(0, MixerOutput::MainOutput, 1.0f); |
||||
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); |
||||
|
||||
float32_t in[length] = {0.1, 0.2}; |
||||
float32_t out[StereoChannels::kNumChannels][length]; |
||||
for(size_t i = 0; i < StereoChannels::kNumChannels; ++i) memset(out[i], 0, length * sizeof(float32_t)); |
||||
|
||||
mixer.setInputSampleBuffer(0, in); |
||||
mixer.preProcessInputSampleBuffer(0, 2); |
||||
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); |
||||
|
||||
mixer.process( |
||||
out[StereoChannels::Left ], |
||||
out[StereoChannels::Right] |
||||
); |
||||
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); |
||||
|
||||
EXPECT_FLOAT_EQ(out[StereoChannels::Left ][0], out[StereoChannels::Right][0]); |
||||
EXPECT_FLOAT_EQ(out[StereoChannels::Left ][1], out[StereoChannels::Right][1]); |
||||
EXPECT_NEAR(out[StereoChannels::Left ][0], sqrt(2.0f) / 20.0f, epsilon); |
||||
EXPECT_NEAR(out[StereoChannels::Left ][1], sqrt(2.0f) / 10.0f, epsilon); |
||||
} |
||||
|
||||
TEST(MixingConsole, ReverberatorProcessing) |
||||
{ |
||||
static const float32_t epsilon = 1e-7; |
||||
static const size_t length = 2; |
||||
|
||||
Mixer mixer(SAMPLING_FREQUENCY, length); |
||||
mixer.reset(); |
||||
mixer.setChannelLevel(0, 1.0f); |
||||
mixer.setPan(0, 0.5f); |
||||
|
||||
mixer.setSendLevel(0, MixerOutput::MainOutput, 0.0f); |
||||
mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 1.0f); |
||||
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); |
||||
|
||||
float32_t in[length] = {0.1, 0.2}; |
||||
float32_t out1[StereoChannels::kNumChannels][length]; |
||||
for(size_t i = 0; i < StereoChannels::kNumChannels; ++i) memset(out1[i], 0, length * sizeof(float32_t)); |
||||
|
||||
mixer.setInputSampleBuffer(0, in); |
||||
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); |
||||
|
||||
mixer.process( |
||||
out1[StereoChannels::Left ], |
||||
out1[StereoChannels::Right] |
||||
); |
||||
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); |
||||
|
||||
mixer.reset(); |
||||
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); |
||||
|
||||
float32_t out2[StereoChannels::kNumChannels][length]; |
||||
mixer.setInputSampleBuffer(0, in); |
||||
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); |
||||
|
||||
mixer.process( |
||||
out2[StereoChannels::Left ], |
||||
out2[StereoChannels::Right] |
||||
); |
||||
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); |
||||
|
||||
EXPECT_NEAR(out1[StereoChannels::Left ][0], out2[StereoChannels::Left ][0], epsilon); |
||||
EXPECT_NEAR(out1[StereoChannels::Right][0], out2[StereoChannels::Right][0], epsilon); |
||||
EXPECT_NEAR(out1[StereoChannels::Left ][1], out2[StereoChannels::Left ][1], epsilon); |
||||
EXPECT_NEAR(out1[StereoChannels::Right][1], out2[StereoChannels::Right][1], epsilon); |
||||
} |
||||
|
||||
TEST(MixingConsole, ReverberatorNoiseProcessing) |
||||
{ |
||||
static const size_t length = 1024; |
||||
|
||||
Mixer mixer(SAMPLING_FREQUENCY, length); |
||||
mixer.reset(); |
||||
mixer.setChannelLevel(0, 1.0f); |
||||
mixer.setPan(0, 0.5f); |
||||
|
||||
mixer.setSendLevel(0, MixerOutput::MainOutput, 0.0f); |
||||
mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 1.0f); |
||||
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); |
||||
|
||||
float32_t in[length]; |
||||
for(size_t i = 0; i < length; ++i) in[i] = getRandomValue(); |
||||
|
||||
float32_t out[StereoChannels::kNumChannels][length]; |
||||
for(size_t i = 0; i < StereoChannels::kNumChannels; ++i) memset(out[i], 0, length * sizeof(float32_t)); |
||||
|
||||
mixer.setInputSampleBuffer(0, in); |
||||
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); |
||||
|
||||
mixer.process( |
||||
out[StereoChannels::Left ], |
||||
out[StereoChannels::Right] |
||||
); |
||||
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); |
||||
} |
||||
|
||||
TEST(MixingConsole, StandardUsageProcessingByInjection) |
||||
{ |
||||
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); |
||||
|
||||
Mixer mixer(SAMPLING_FREQUENCY, size); |
||||
setupMixingConsoleFX(&mixer); |
||||
|
||||
mixer.getTube()->setOverdrive(0.15f); |
||||
|
||||
mixer.setSendLevel(0, MixerOutput::FX_Tube, 1.0f); |
||||
mixer.setSendLevel(0, MixerOutput::FX_Phaser, 1.0f); |
||||
// mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, 1.0f);
|
||||
// mixer.setSendLevel(0, MixerOutput::FX_Chorus, 1.0f);
|
||||
// mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f);
|
||||
mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Chorus, 1.0f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Reverberator, 1.0f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Delay, 1.0f); |
||||
|
||||
mixer.setSendLevel(0, MixerOutput::MainOutput, 0.25f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, 0.1f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::MainOutput, 0.15f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.3f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Delay, MixerOutput::MainOutput, 0.3f); |
||||
|
||||
mixer.injectInputSamples(0, inSamples[StereoChannels::Left], inSamples[StereoChannels::Right], size); |
||||
mixer.process(outSamples[0], outSamples[1]); |
||||
ASSERT_EQ(0, INSPECT((&mixer), fullInspector)); |
||||
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamples[0], outSamples[1], size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16); |
||||
|
||||
CLEANUP_AUDIO_TEST(inSamples, outSamples); |
||||
} |
||||
|
||||
TEST(MixingConsole, StandardUsageProcessing) |
||||
{ |
||||
static const size_t MAX_BUFFER_SIZE = 4096; |
||||
static const size_t BUFFER_SIZE = 256; |
||||
|
||||
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); |
||||
|
||||
Mixer mixer(SAMPLING_FREQUENCY, MAX_BUFFER_SIZE); |
||||
|
||||
float32_t channelBuffer[MAX_BUFFER_SIZE]; |
||||
memset(channelBuffer, 0, MAX_BUFFER_SIZE * sizeof(float32_t)); |
||||
mixer.setInputSampleBuffer(0, channelBuffer); |
||||
|
||||
setupMixingConsoleFX(&mixer); |
||||
|
||||
mixer.getTube()->setOverdrive(0.15f); |
||||
|
||||
mixer.setSendLevel(0, MixerOutput::FX_Tube, 1.0f); |
||||
mixer.setSendLevel(0, MixerOutput::FX_Phaser, 1.0f); |
||||
// mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, 1.0f);
|
||||
// mixer.setSendLevel(0, MixerOutput::FX_Chorus, 1.0f);
|
||||
// mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f);
|
||||
mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Chorus, 1.0f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Reverberator, 1.0f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Delay, 1.0f); |
||||
|
||||
mixer.setSendLevel(0, MixerOutput::MainOutput, 0.25f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, 0.1f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::MainOutput, 0.15f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.3f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Delay, MixerOutput::MainOutput, 0.3f); |
||||
|
||||
float32_t* inS = inSamples[StereoChannels::Left]; |
||||
float32_t* outS[StereoChannels::kNumChannels]; |
||||
outS[StereoChannels::Left ] = outSamples[StereoChannels::Left ]; |
||||
outS[StereoChannels::Right] = outSamples[StereoChannels::Right]; |
||||
size_t s = size; |
||||
|
||||
while(s > 0) |
||||
{ |
||||
size_t nb = (s > BUFFER_SIZE) ? BUFFER_SIZE : s; |
||||
|
||||
memcpy(channelBuffer, inS, nb * sizeof(float32_t)); |
||||
|
||||
mixer.preProcessInputSampleBuffer(0, nb); |
||||
mixer.process(outS[StereoChannels::Left ], outS[StereoChannels::Right]); |
||||
// ASSERT_EQ(0, INSPECT((&mixer), fullInspector));
|
||||
|
||||
inS += nb; |
||||
outS[StereoChannels::Left ] += nb; |
||||
outS[StereoChannels::Right] += nb; |
||||
s -= nb; |
||||
} |
||||
|
||||
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamples[0], outSamples[1], size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16); |
||||
|
||||
CLEANUP_AUDIO_TEST(inSamples, outSamples); |
||||
} |
||||
|
||||
TEST(MixingConsole, StandardUsageProcessingAllMixerChannels) |
||||
{ |
||||
static const size_t MAX_BUFFER_SIZE = 4096; |
||||
static const size_t BUFFER_SIZE = 256; |
||||
|
||||
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); |
||||
|
||||
Mixer mixer(SAMPLING_FREQUENCY, MAX_BUFFER_SIZE); |
||||
|
||||
float32_t channelBuffer[NB_MIXER_CHANNELS][MAX_BUFFER_SIZE]; |
||||
for(size_t i = 0; i < NB_MIXER_CHANNELS; ++i) |
||||
{ |
||||
memset(channelBuffer[i], 0, MAX_BUFFER_SIZE * sizeof(float32_t)); |
||||
mixer.setInputSampleBuffer(i, channelBuffer[i]); |
||||
} |
||||
|
||||
setupMixingConsoleFX(&mixer); |
||||
|
||||
mixer.getTube()->setOverdrive(0.15f); |
||||
|
||||
mixer.setSendLevel(0, MixerOutput::FX_Tube, 1.0f); |
||||
mixer.setSendLevel(0, MixerOutput::FX_Phaser, 1.0f); |
||||
// mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, 1.0f);
|
||||
// mixer.setSendLevel(0, MixerOutput::FX_Chorus, 1.0f);
|
||||
// mixer.setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f);
|
||||
mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::FX_Chorus, 1.0f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::FX_Reverberator, 1.0f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Phaser, MixerOutput::FX_Delay, 1.0f); |
||||
|
||||
mixer.setSendLevel(0, MixerOutput::MainOutput, 0.25f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Tube, MixerOutput::MainOutput, 0.1f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Chorus, MixerOutput::MainOutput, 0.15f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.3f); |
||||
mixer.setFXSendLevel(MixerOutput::FX_Delay, MixerOutput::MainOutput, 0.3f); |
||||
|
||||
float32_t* inS = inSamples[StereoChannels::Left]; |
||||
float32_t* outS[StereoChannels::kNumChannels]; |
||||
outS[StereoChannels::Left ] = outSamples[StereoChannels::Left ]; |
||||
outS[StereoChannels::Right] = outSamples[StereoChannels::Right]; |
||||
size_t s = size; |
||||
|
||||
while(s > 0) |
||||
{ |
||||
size_t nb = (s > BUFFER_SIZE) ? BUFFER_SIZE : s; |
||||
|
||||
memcpy(channelBuffer[0], inS, nb * sizeof(float32_t)); |
||||
|
||||
for(size_t i = 0; i < mixer.getChannelNumber(); ++i) |
||||
{ |
||||
mixer.preProcessInputSampleBuffer(i, nb); |
||||
} |
||||
mixer.process(outS[StereoChannels::Left ], outS[StereoChannels::Right]); |
||||
|
||||
inS += nb; |
||||
outS[StereoChannels::Left ] += nb; |
||||
outS[StereoChannels::Right] += nb; |
||||
s -= nb; |
||||
} |
||||
|
||||
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamples[0], outSamples[1], size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16); |
||||
|
||||
CLEANUP_AUDIO_TEST(inSamples, outSamples); |
||||
} |
||||
|
||||
TEST(MixingConsole, StandardUsageProcessingAllMixerChannels2) |
||||
{ |
||||
static const size_t MAX_BUFFER_SIZE = 4096; |
||||
static const size_t BUFFER_SIZE = 256; |
||||
|
||||
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); |
||||
|
||||
Mixer mixer(SAMPLING_FREQUENCY, MAX_BUFFER_SIZE); |
||||
|
||||
float32_t channelBuffer[NB_MIXER_CHANNELS][MAX_BUFFER_SIZE]; |
||||
for(size_t i = 0; i < NB_MIXER_CHANNELS; ++i) |
||||
{ |
||||
memset(channelBuffer[i], 0, MAX_BUFFER_SIZE * sizeof(float32_t)); |
||||
mixer.setInputSampleBuffer(i, channelBuffer[i]); |
||||
} |
||||
|
||||
setupMixingConsoleFX(&mixer); |
||||
|
||||
for(size_t i = 0; i < NB_MIXER_CHANNELS; ++i) |
||||
{ |
||||
mixer.setSendLevel(i, static_cast<MixerOutput>(i), 1.0f); |
||||
mixer.setFXSendLevel(static_cast<MixerOutput>(i), MixerOutput::MainOutput, 0.5f); |
||||
mixer.setSendLevel(i, MixerOutput::MainOutput, 0.5f); |
||||
} |
||||
|
||||
float32_t* inS = inSamples[StereoChannels::Left]; |
||||
float32_t* outS[StereoChannels::kNumChannels]; |
||||
outS[StereoChannels::Left ] = outSamples[StereoChannels::Left ]; |
||||
outS[StereoChannels::Right] = outSamples[StereoChannels::Right]; |
||||
size_t s = size; |
||||
|
||||
while(s > 0) |
||||
{ |
||||
size_t nb = (s > BUFFER_SIZE) ? BUFFER_SIZE : s; |
||||
|
||||
for(size_t i = 0; i < mixer.getChannelNumber(); ++i) |
||||
{ |
||||
memcpy(channelBuffer[i], inS, nb * sizeof(float32_t)); |
||||
mixer.preProcessInputSampleBuffer(i, nb); |
||||
} |
||||
mixer.process(outS[StereoChannels::Left ], outS[StereoChannels::Right]); |
||||
|
||||
inS += nb; |
||||
outS[StereoChannels::Left ] += nb; |
||||
outS[StereoChannels::Right] += nb; |
||||
s -= nb; |
||||
} |
||||
|
||||
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamples[0], outSamples[1], size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16); |
||||
|
||||
CLEANUP_AUDIO_TEST(inSamples, outSamples); |
||||
} |
||||
|
||||
TEST_P(FXScenarioTest, FXProcessingScenario) |
||||
{ |
||||
static const size_t MAX_BUFFER_SIZE = 4096; |
||||
static const size_t BUFFER_SIZE = 256; |
||||
|
||||
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name); |
||||
|
||||
Mixer mixer(SAMPLING_FREQUENCY, MAX_BUFFER_SIZE); |
||||
|
||||
float32_t channelBuffer[NB_MIXER_CHANNELS][MAX_BUFFER_SIZE]; |
||||
for(size_t i = 0; i < NB_MIXER_CHANNELS; ++i) |
||||
{ |
||||
memset(channelBuffer[i], 0, MAX_BUFFER_SIZE * sizeof(float32_t)); |
||||
mixer.setInputSampleBuffer(i, channelBuffer[i]); |
||||
} |
||||
|
||||
setupMixingConsoleFX(&mixer); |
||||
|
||||
int scenarioId = this->GetParam(); |
||||
setupMixingConsoleFX((&mixer), scenarioId); |
||||
|
||||
float32_t* inS = inSamples[StereoChannels::Left]; |
||||
float32_t* outS[StereoChannels::kNumChannels]; |
||||
outS[StereoChannels::Left ] = outSamples[StereoChannels::Left ]; |
||||
outS[StereoChannels::Right] = outSamples[StereoChannels::Right]; |
||||
size_t s = size; |
||||
|
||||
while(s > 0) |
||||
{ |
||||
size_t nb = (s > BUFFER_SIZE) ? BUFFER_SIZE : s; |
||||
|
||||
for(size_t i = 0; i < mixer.getChannelNumber(); ++i) |
||||
{ |
||||
memcpy(channelBuffer[i], inS, nb * sizeof(float32_t)); |
||||
mixer.preProcessInputSampleBuffer(i, nb); |
||||
} |
||||
mixer.process(outS[StereoChannels::Left ], outS[StereoChannels::Right]); |
||||
|
||||
inS += nb; |
||||
outS[StereoChannels::Left ] += nb; |
||||
outS[StereoChannels::Right] += nb; |
||||
s -= nb; |
||||
} |
||||
|
||||
std::string filename = ""; |
||||
for(size_t i = 0; i < (MixerOutput::kFXCount - 1); ++i) |
||||
{ |
||||
int k = 1 << i; |
||||
if((scenarioId & k) == 0) |
||||
{ |
||||
continue; |
||||
} |
||||
|
||||
if(filename.size() > 0) |
||||
{ |
||||
filename += ", "; |
||||
} |
||||
filename += toString(static_cast<MixerOutput>(i)); |
||||
} |
||||
|
||||
saveWaveFile(getResultFile(full_test_name + " mixing-console[ " + filename + " ].wav", true), outSamples[0], outSamples[1], size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16); |
||||
|
||||
CLEANUP_AUDIO_TEST(inSamples, outSamples); |
||||
} |
||||
|
||||
INSTANTIATE_TEST_SUITE_P(MixingConsole, FXScenarioTest, testing::Range(0, 1 << (MixerOutput::kFXCount - 1))); |
@ -0,0 +1,187 @@ |
||||
#include "test_fx_helper.h" |
||||
|
||||
#include "../mixing_console.hpp" |
||||
|
||||
typedef MixingConsole<1> Mixer; |
||||
|
||||
TEST(MixingConsole, ShortBuffer) |
||||
{ |
||||
static const float32_t SINPI_4 = std::sqrt(2.0f) / 2.0f; |
||||
static const float32_t epsilon = 1e-4; |
||||
|
||||
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); |
||||
std::string full_test_name = test_info->test_case_name(); |
||||
full_test_name += "."; |
||||
full_test_name += test_info->name(); |
||||
|
||||
const size_t size = 10; |
||||
Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); |
||||
mixer->reset(); |
||||
mixer->setChannelLevel(0, 1.0f); |
||||
mixer->setPan(0, 0.5f); |
||||
|
||||
mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f); |
||||
|
||||
float32_t inSamples[size]; |
||||
for(size_t s = 0; s < size; ++s) inSamples[s] = getRandomValue(); |
||||
float32_t outSamples[2][size]; |
||||
memset(outSamples[0], 0, size * sizeof(float32_t)); |
||||
memset(outSamples[1], 0, size * sizeof(float32_t)); |
||||
|
||||
mixer->setInputSampleBuffer(0, inSamples); |
||||
mixer->preProcessInputSampleBuffer(0, size); |
||||
ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.setInputSampleBuffer"; |
||||
|
||||
mixer->process(outSamples[0], outSamples[1]); |
||||
ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.process"; |
||||
for(size_t s = 0; s < size; ++s) |
||||
{ |
||||
EXPECT_NEAR(outSamples[0][s], outSamples[1][s], epsilon); |
||||
EXPECT_NEAR(outSamples[0][s], inSamples[s] * SINPI_4, epsilon); |
||||
} |
||||
|
||||
delete mixer; |
||||
} |
||||
|
||||
TEST(MixingConsole, ReverberatorShortBuffer) |
||||
{ |
||||
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); |
||||
std::string full_test_name = test_info->test_case_name(); |
||||
full_test_name += "."; |
||||
full_test_name += test_info->name(); |
||||
|
||||
const size_t size = 10; |
||||
Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); |
||||
mixer->reset(); |
||||
mixer->setChannelLevel(0, 1.0f); |
||||
mixer->setPan(0, 0.5f); |
||||
|
||||
mixer->getReverberator()->setInputGain(0.35f); |
||||
mixer->getReverberator()->setTime(0.69f); |
||||
mixer->getReverberator()->setDiffusion(0.7f); |
||||
mixer->getReverberator()->setLP(0.8f); |
||||
|
||||
mixer->setSendLevel(0, MixerOutput::MainOutput, 0.4f); |
||||
mixer->setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f); |
||||
mixer->setFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.6f); |
||||
|
||||
float32_t inSamples[size]; |
||||
for(size_t s = 0; s < size; ++s) inSamples[s] = getRandomValue(); |
||||
float32_t outSamples[2][size]; |
||||
memset(outSamples[0], 0, size * sizeof(float32_t)); |
||||
memset(outSamples[1], 0, size * sizeof(float32_t)); |
||||
|
||||
mixer->setInputSampleBuffer(0, inSamples); |
||||
mixer->preProcessInputSampleBuffer(0, size); |
||||
ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.setInputSampleBuffer"; |
||||
|
||||
mixer->process(outSamples[0], outSamples[1]); |
||||
ASSERT_EQ(0, FULL_INSPECT(mixer, true)) << full_test_name << " Mixer.process"; |
||||
|
||||
delete mixer; |
||||
} |
||||
|
||||
TEST(MixingConsole, DrySamplesBoundariesTest) |
||||
{ |
||||
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); |
||||
std::string full_test_name = test_info->test_case_name(); |
||||
full_test_name += "."; |
||||
full_test_name += test_info->name(); |
||||
|
||||
size_t size; |
||||
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size); |
||||
|
||||
Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); |
||||
mixer->reset(); |
||||
FULL_INSPECT2(mixer, true, "Mixer.reset"); |
||||
|
||||
mixer->setChannelLevel(0, 1.0f); |
||||
mixer->setPan(0, 0.5f); |
||||
mixer->setSendLevel(0, MixerOutput::MainOutput, 1.0f); |
||||
|
||||
mixer->setInputSampleBuffer(0, inSamples[0]); |
||||
mixer->preProcessInputSampleBuffer(0, size); |
||||
|
||||
float32_t** outSamples = new float32_t*[2]; |
||||
outSamples[0] = new float32_t[size]; |
||||
outSamples[1] = new float32_t[size]; |
||||
memset(outSamples[0], 0, size * sizeof(float32_t)); |
||||
memset(outSamples[1], 0, size * sizeof(float32_t)); |
||||
|
||||
mixer->process(outSamples[0], outSamples[1]); |
||||
|
||||
size_t nb_errors = 0; |
||||
for(size_t i = 0; i < size; ++i) |
||||
{ |
||||
nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[0][i], -1.0f, 1.0f, true); |
||||
nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[1][i], -1.0f, 1.0f, true); |
||||
} |
||||
ASSERT_EQ(0, nb_errors) << full_test_name << ".outputSampleTest"; |
||||
|
||||
delete[] inSamples[0]; |
||||
delete[] inSamples[1]; |
||||
delete[] inSamples; |
||||
|
||||
delete[] outSamples[0]; |
||||
delete[] outSamples[1]; |
||||
delete[] outSamples; |
||||
|
||||
delete mixer; |
||||
} |
||||
|
||||
TEST(MixingConsole, ReverberatorSamplesBoundariesTest) |
||||
{ |
||||
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); |
||||
std::string full_test_name = test_info->test_case_name(); |
||||
full_test_name += "."; |
||||
full_test_name += test_info->name(); |
||||
|
||||
size_t size; |
||||
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size); |
||||
|
||||
float32_t** outSamples = new float32_t*[2]; |
||||
outSamples[0] = new float32_t[size]; |
||||
outSamples[1] = new float32_t[size]; |
||||
memset(outSamples[0], 0, size * sizeof(float32_t)); |
||||
memset(outSamples[1], 0, size * sizeof(float32_t)); |
||||
|
||||
Mixer* mixer = new Mixer(SAMPLING_FREQUENCY, size); |
||||
mixer->reset(); |
||||
mixer->setChannelLevel(0, 1.0f); |
||||
mixer->setPan(0, 0.5f); |
||||
|
||||
mixer->setSendLevel(0, MixerOutput::MainOutput, 0.4f); |
||||
mixer->setSendLevel(0, MixerOutput::FX_Reverberator, 1.0f); |
||||
mixer->setFXSendLevel(MixerOutput::FX_Reverberator, MixerOutput::MainOutput, 0.6f); |
||||
|
||||
mixer->getReverberator()->setMute(false); |
||||
mixer->getReverberator()->setInputGain(0.35); |
||||
mixer->getReverberator()->setTime(0.65); |
||||
mixer->getReverberator()->setDiffusion(0.8); |
||||
mixer->getReverberator()->setLP(0.7f); |
||||
|
||||
mixer->setInputSampleBuffer(0, inSamples[0]); |
||||
mixer->preProcessInputSampleBuffer(0, size); |
||||
mixer->process(outSamples[0], outSamples[1]); |
||||
ASSERT_EQ(0, FULL_INSPECT2(mixer, true, full_test_name + "Mixer.process")) << full_test_name << " Mixer.process"; |
||||
|
||||
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamples[0], outSamples[1], size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16); |
||||
|
||||
size_t nb_errors = 0; |
||||
for(size_t i = 0; i < size; ++i) |
||||
{ |
||||
nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[0][i], -1.0f, 1.0f, true); |
||||
nb_errors += fullInspector(full_test_name + ".outputSampleTest", inSamples[1][i], -1.0f, 1.0f, true); |
||||
} |
||||
ASSERT_EQ(0, nb_errors) << full_test_name << ".outputSampleTest"; |
||||
|
||||
delete[] inSamples[0]; |
||||
delete[] inSamples[1]; |
||||
delete[] inSamples; |
||||
|
||||
delete[] outSamples[0]; |
||||
delete[] outSamples[1]; |
||||
delete[] outSamples; |
||||
|
||||
delete mixer; |
||||
} |
@ -0,0 +1,182 @@ |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include "test_fx_helper.h" |
||||
#include "../fx_reverberator.h" |
||||
|
||||
TEST(FXShimmer, TransientSilence) |
||||
{ |
||||
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); |
||||
std::string full_test_name = test_info->test_case_name(); |
||||
full_test_name += "."; |
||||
full_test_name += test_info->name(); |
||||
|
||||
const size_t size = static_cast<size_t>(SAMPLING_FREQUENCY); |
||||
float32_t* inSamples = new float32_t[size]; |
||||
memset(inSamples, 0, size * sizeof(float32_t)); |
||||
|
||||
float32_t* outSamplesL = new float32_t[size]; |
||||
float32_t* outSamplesR = new float32_t[size]; |
||||
memset(outSamplesL, 0, size * sizeof(float32_t)); |
||||
memset(outSamplesR, 0, size * sizeof(float32_t)); |
||||
|
||||
Reverberator* shimmer = new Reverberator(SAMPLING_FREQUENCY); |
||||
|
||||
shimmer->setInputGain(0.55f); |
||||
shimmer->setTime(0.75f); |
||||
shimmer->setDiffusion(0.8f); |
||||
shimmer->setLP(0.7f); |
||||
|
||||
shimmer->reset(); |
||||
for(size_t i = 0; i < size; ++i) |
||||
{ |
||||
shimmer->processSample( |
||||
inSamples[i],
|
||||
inSamples[i],
|
||||
outSamplesL[i],
|
||||
outSamplesR[i] |
||||
); |
||||
} |
||||
|
||||
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16); |
||||
|
||||
delete shimmer; |
||||
|
||||
delete[] inSamples; |
||||
|
||||
delete[] outSamplesL; |
||||
delete[] outSamplesR; |
||||
} |
||||
|
||||
TEST(FXShimmer, TransientSilenceWithDirac) |
||||
{ |
||||
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); |
||||
std::string full_test_name = test_info->test_case_name(); |
||||
full_test_name += "."; |
||||
full_test_name += test_info->name(); |
||||
|
||||
const size_t size = 4 * static_cast<size_t>(SAMPLING_FREQUENCY); |
||||
float32_t* inSamples = new float32_t[size]; |
||||
memset(inSamples, 0, size * sizeof(float32_t)); |
||||
inSamples[0] = 1.0f; |
||||
|
||||
float32_t* outSamplesL = new float32_t[size]; |
||||
float32_t* outSamplesR = new float32_t[size]; |
||||
memset(outSamplesL, 0, size * sizeof(float32_t)); |
||||
memset(outSamplesR, 0, size * sizeof(float32_t)); |
||||
|
||||
Reverberator* shimmer = new Reverberator(SAMPLING_FREQUENCY); |
||||
|
||||
shimmer->setInputGain(0.55f); |
||||
shimmer->setTime(0.75f); |
||||
shimmer->setDiffusion(0.8f); |
||||
shimmer->setLP(0.7f); |
||||
|
||||
shimmer->reset(); |
||||
for(size_t i = 0; i < size; ++i) |
||||
{ |
||||
shimmer->processSample( |
||||
inSamples[i],
|
||||
inSamples[i],
|
||||
outSamplesL[i],
|
||||
outSamplesR[i] |
||||
); |
||||
} |
||||
|
||||
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16); |
||||
|
||||
delete shimmer; |
||||
|
||||
delete[] inSamples; |
||||
|
||||
delete[] outSamplesL; |
||||
delete[] outSamplesR; |
||||
} |
||||
|
||||
TEST(FXShimmer, TransientNoise) |
||||
{ |
||||
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); |
||||
std::string full_test_name = test_info->test_case_name(); |
||||
full_test_name += "."; |
||||
full_test_name += test_info->name(); |
||||
|
||||
const size_t size = static_cast<size_t>(SAMPLING_FREQUENCY); |
||||
float32_t* inSamples = new float32_t[size]; |
||||
for(size_t i = 0; i < size; ++i) inSamples[i] = getRandomValue(); |
||||
|
||||
float32_t* outSamplesL = new float32_t[size]; |
||||
float32_t* outSamplesR = new float32_t[size]; |
||||
memset(outSamplesL, 0, size * sizeof(float32_t)); |
||||
memset(outSamplesR, 0, size * sizeof(float32_t)); |
||||
|
||||
Reverberator* shimmer = new Reverberator(SAMPLING_FREQUENCY); |
||||
|
||||
shimmer->setInputGain(0.55f); |
||||
shimmer->setTime(0.75f); |
||||
shimmer->setDiffusion(0.8f); |
||||
shimmer->setLP(0.7f); |
||||
|
||||
shimmer->reset(); |
||||
for(size_t i = 0; i < size; ++i) |
||||
{ |
||||
shimmer->processSample( |
||||
inSamples[i],
|
||||
inSamples[i],
|
||||
outSamplesL[i],
|
||||
outSamplesR[i] |
||||
); |
||||
} |
||||
|
||||
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16); |
||||
|
||||
delete shimmer; |
||||
|
||||
delete[] inSamples; |
||||
|
||||
delete[] outSamplesL; |
||||
delete[] outSamplesR; |
||||
} |
||||
|
||||
TEST(FXShimmer, TransientMusic) |
||||
{ |
||||
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); |
||||
std::string full_test_name = test_info->test_case_name(); |
||||
full_test_name += "."; |
||||
full_test_name += test_info->name(); |
||||
|
||||
size_t size; |
||||
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size); |
||||
|
||||
float32_t* outSamplesL = new float32_t[size]; |
||||
float32_t* outSamplesR = new float32_t[size]; |
||||
memset(outSamplesL, 0, size * sizeof(float32_t)); |
||||
memset(outSamplesR, 0, size * sizeof(float32_t)); |
||||
|
||||
Reverberator* shimmer = new Reverberator(SAMPLING_FREQUENCY); |
||||
|
||||
shimmer->setInputGain(0.55f); |
||||
shimmer->setTime(0.75f); |
||||
shimmer->setDiffusion(0.8f); |
||||
shimmer->setLP(0.7f); |
||||
|
||||
shimmer->reset(); |
||||
for(size_t i = 0; i < size; ++i) |
||||
{ |
||||
shimmer->processSample( |
||||
inSamples[0][i],
|
||||
inSamples[1][i],
|
||||
outSamplesL[i],
|
||||
outSamplesR[i] |
||||
); |
||||
} |
||||
|
||||
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16); |
||||
|
||||
delete shimmer; |
||||
|
||||
delete[] inSamples[0]; |
||||
delete[] inSamples[1]; |
||||
delete[] inSamples; |
||||
|
||||
delete[] outSamplesL; |
||||
delete[] outSamplesR; |
||||
} |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 202 KiB |
@ -0,0 +1,470 @@ |
||||
# Mixing Console & Audio Effect |
||||
|
||||
On multi-core devices, MiniDexed can now be equiped with a complex and versatile mixing console that is composed of a multi-FX processor. |
||||
|
||||
After the volume and pan controls, it is possible to set the send level of each Tone Generator to the individual Audio Effect and Main output. Below is the TG channel strip: |
||||
|
||||
<img src="mixing-console--TG-Channel.png" alt="Mixing Console - TG Channel Strip" width="10%" style="background-color: #fff"> |
||||
|
||||
Each FX has the possibility to return to any other Audio FX unit as well as Main Output. |
||||
|
||||
The diagram below shows the synopsis of this Mixing Console. |
||||
|
||||
<img src="mixing-console-overview.png" alt="Mixing Console - TG Channel Strip" width="100%" style="background-color: #fff"> |
||||
|
||||
## The Audio FX |
||||
|
||||
The following audio FX are available: |
||||
|
||||
* **Tube:** an amplifier that mimmic the response of valve based amplifier. |
||||
|
||||
<img src="Tube--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff"> |
||||
|
||||
* ***Enable [Off - On]:*** Enable / disable the FX unit. |
||||
* ***Overdrive [0 - 99]:*** the overdrive amount the more the overdrive the less linear is the amplification. Below is the amplifier response based on different value of the overdrive: ![Tube - Overdrive response](Tube--overdrive-response.png) |
||||
|
||||
<br> |
||||
<br> |
||||
|
||||
* **Chorus:** this effect is an audio signal processing technique that creates a richer, thicker sound by duplicating the original signal and slightly delaying and modulating the duplicated signal in pitch and time. The modulated and delayed signals are then mixed back together with the original signal, creating a sound that seems to be coming from multiple sources or voices. |
||||
The result of a chorus effect is a distinctive "shimmering" or "swirling" sound, similar to the sound of a choir or a group of instruments playing together. Chorus effects are commonly used in music production to add depth and complexity to a sound, particularly for guitars, keyboards, and vocals. They are also used in some audio equipment, such as guitar pedals and synthesizers, to produce a chorus effect in real-time. |
||||
This implementation is a standard Chorus FX that is based on 4 LFO: |
||||
| LFO | Min. freq. | Max. freq. | Phase | |
||||
|-----|------------|------------|-------| |
||||
| 1 | 0.0 Hz | 0.25 Hz | 0° | |
||||
| 2 | 0.0 Hz | 0.25 Hz | 90° | |
||||
| 3 | 0.0 Hz | 0.35 Hz | 0° | |
||||
| 4 | 0.0 Hz | 0.35 Hz | 90° | |
||||
|
||||
<img src="Chorus--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff"> |
||||
|
||||
* ***Enable [Off - On]:*** Enable / disable the FX unit. |
||||
* ***Rate [0 - 99]:*** modulate the LFO rate between thei minimum frequency (Rate = 0) and their maximum frequency (Rate = 99). |
||||
* ***Depth [0 - 99]:*** the level of modulation of the FX. The higher the value the more prononced the audio processing is. |
||||
|
||||
<br> |
||||
<br> |
||||
|
||||
* **Flanger:** this effect is an audio signal processing technique that creates a sweeping, swirling sound by mixing a delayed copy of an audio signal with the original signal. The delay time of the copied signal is varied over time, creating a series of peaks and troughs in the frequency spectrum of the combined signal. |
||||
The resulting sound is similar to a chorus effect, but with a more pronounced "swooshing" or "jet engine" effect, as the peaks and troughs of the frequency spectrum sweep up and down in a cyclic pattern. Flangers are often used in music production to create an "otherworldly" or psychedelic effect, particularly on guitars and keyboards. |
||||
The name "flanger" comes from the original method of creating the effect, which involved using two tape recorders to play back the same signal simultaneously, while a finger was used to slightly slow down or speed up the flange (rim) of one of the reels, causing the delayed signal to vary in time. Today, flanger effects are typically created digitally, using specialized signal processing algorithms in software or hardware devices such as guitar pedals or studio processors. |
||||
This implementation is based on 2 LFO: |
||||
| LFO | Min. freq. | Max. freq. | Phase | |
||||
|-----|------------|------------|-------| |
||||
| 1 | 0.1 Hz | 5.0 Hz | 0° | |
||||
| 2 | 0.1 Hz | 5.0 Hz | 90° | |
||||
|
||||
<img src="Flanger--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff"> |
||||
|
||||
* ***Enable [Off - On]:*** Enable / disable the FX unit. |
||||
* ***Rate [0 - 99]:*** modulate the LFO rate between thei minimum frequency (Rate = 0) and their maximum frequency (Rate = 99). |
||||
* ***Depth [0 - 99]:*** the level of modulation of the FX. The higher the value the more prononced the audio processing is. |
||||
* ***Feedback [0 - 99]:*** the amount of of processed signal that is re-injected at the audio effect input. |
||||
|
||||
<br> |
||||
<br> |
||||
|
||||
* ***Orbitone:*** this is an ensemble effect that is an audio signal processing technique that creates the impression of multiple instruments or voices playing together. It is similar to a chorus effect in that it involves duplicating and modulating an audio signal, but with some differences in the way it is applied. |
||||
In an ensemble effect, the duplicated signals are modulated with a low-frequency oscillator (LFO) that varies the pitch and amplitude of the signals over time. The modulated signals are then mixed with the original signal, creating a sound that is thicker and richer, as if several instruments or voices were playing in unison. |
||||
The resulting sound can be used to add depth and character to a musical performance, particularly for string, brass, or vocal sounds. Ensemble effects are often used in music production to create a sense of warmth, depth, and movement in a mix. |
||||
This implementation is based on 4 LFO: |
||||
| LFO | Min. freq. | Max. freq. | Phase | |
||||
|-----|------------|------------|-------| |
||||
| 1 | 0.0 Hz | 1.0 Hz | 0° | |
||||
| 2 | 0.0 Hz | 1.0 Hz | 120° | |
||||
| 3 | 0.0 Hz | 1.0 Hz | 240° | |
||||
| 4 | 0.0 Hz | 8.8 Hz | 0° | |
||||
| 5 | 0.0 Hz | 8.8 Hz | 120° | |
||||
| 6 | 0.0 Hz | 8.8 Hz | 240° | |
||||
|
||||
<img src="Orbitone--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff"> |
||||
|
||||
* ***Enable [Off - On]:*** Enable / disable the FX unit. |
||||
* ***Rate [0 - 99]:*** modulate the LFO rate between thei minimum frequency (Rate = 0) and their maximum frequency (Rate = 99). |
||||
* ***Depth [0 - 99]:*** the level of modulation of the FX. The higher the value the more prononced the audio processing is. |
||||
* ***Feedback [0 - 99]:*** the amount of of processed signal that is re-injected at the audio effect input. |
||||
|
||||
<br> |
||||
<br> |
||||
|
||||
* ***Phaser:*** a phaser or phase shifter effect is an audio signal processing technique that creates a sweeping, "whooshing" sound by altering the phase relationship between two identical signals. This is achieved by splitting the audio signal into two parts, delaying one of them slightly, and then combining them again. The delay time is varied over time, causing certain frequencies in the audio signal to be boosted and others to be attenuated. |
||||
The resulting sound is characterized by a series of peaks and troughs in the frequency spectrum, which move up and down in a sweeping pattern. The phaser effect is often used in music production to create a sense of movement and space in a mix, and is particularly effective on guitars, keyboards, and other melodic instruments. |
||||
This implementation is based on 2 LFO: |
||||
| LFO | Min. freq. | Max. freq. | Phase | |
||||
|-----|------------|------------|-------| |
||||
| 1 | 0.0 Hz | 2.5 Hz | 0° | |
||||
| 2 | 0.0 Hz | 2.5 Hz | 90° | |
||||
|
||||
<img src="Phaser--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff"> |
||||
|
||||
* ***Enable [Off - On]:*** Enable / disable the FX unit. |
||||
* ***Rate [0 - 99]:*** modulate the LFO rate between thei minimum frequency (Rate = 0) and their maximum frequency (Rate = 99). |
||||
* ***Depth [0 - 99]:*** the level of modulation of the FX. The higher the value the more prononced the audio processing is. |
||||
* ***Feedback [0 - 99]:*** the amount of of processed signal that is re-injected at the audio effect input. |
||||
* ***Nb Stages [2 - 24]:*** The number of state variable filter stages that the audio signal will traverse. |
||||
|
||||
<br> |
||||
<br> |
||||
|
||||
* **Delay:** the is an audio signal processing technique that creates a repeated, delayed version of an original sound. It does this by temporarily storing a copy of the original audio signal in a buffer, and then playing it back at a later time. |
||||
The delay time can be set to vary from a few milliseconds to 1 second, and the repeated sound can be manipulated in various ways to create a range of different effects. |
||||
Delay effects are used in a wide variety of musical genres to add texture, depth, and interest to a mix. They can be used on almost any sound source, including vocals, guitars, keyboards, drums and other melodic instruments. |
||||
The implemention of this delay accept negative values for both left and right delay. Negative values will echo sound reversed. |
||||
|
||||
<img src="Delay--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff"> |
||||
|
||||
* ***Enable [Off - On]:*** Enable / disable the FX unit. |
||||
* **Left Delay [-99 - 99]:** the left delay from 0 to 2 seconds. |
||||
* **Right Delay [-99 - 99]:** the left delay from 0 to 2 seconds. |
||||
* ***Feedback [0 - 99]:*** the amount of of processed signal that is re-injected at the audio effect input. |
||||
|
||||
<br> |
||||
<br> |
||||
|
||||
* **Plate Reverb:** A plate reverb effect is an audio signal processing technique that simulates the sound of a large, metallic plate vibrating in response to an audio signal. It is one of the most iconic and widely used types of artificial reverberation in music production. |
||||
The plate reverb effect is created by passing an audio signal through a transducer that causes a large metal plate to vibrate in response. The vibrations of the plate are then picked up by one or more microphones and mixed back with the original signal. The sound of the plate reverb is characterized by a smooth decay and a diffuse, ambient quality that can add warmth and depth to a mix. |
||||
Plate reverb effects are often used in music production to add space, depth, and character to a mix. They are particularly effective on vocals, drums, and other percussive sounds, and can be used to create a sense of space and distance in a recording. The effect can be adjusted to achieve varying degrees of intensity, decay time, and other parameters. |
||||
|
||||
<img src="PlateReverb--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff"> |
||||
|
||||
* ***Enable [Off - On]:*** Enable / disable the FX unit. |
||||
* ***Size [0 - 99]:*** the size of the simulated metallic plate hence of the room. |
||||
* ***High Damping [0 - 99]:*** the amount of high-frequency attenuation or absorption applied to the plate's vibrations. It is a key parameter that can have a significant impact on the overall sound of the plate reverb effect. |
||||
* ***Low Damping [0 - 99]:*** the amount of low-frequency attenuation or absorption applied to the plate's vibrations. It is a key parameter that can have a significant impact on the overall sound of the plate reverb effect. |
||||
* ***Low Pass [0 - 99]:*** the low pass parameter of a plate reverb refers to a filter that is applied to the reverb signal to attenuate or cut off frequencies above a certain threshold. |
||||
A low pass filter can be used to create a darker, more mellow sound by reducing or removing high-frequency content from the reverb effect. This can be useful when trying to create a more vintage or retro sound, or when trying to soften harsh or bright sounds in a mix. |
||||
* ***Diffusion [0 - 99]:*** the diffusion parameter of a plate reverb refers to the degree to which the reflections in the reverb decay are dispersed or spread out in time and space. It determines how "dense" or "sparse" the reverb effect sounds and how the individual reflections blend together. |
||||
A higher diffusion parameter means that the reflections in the reverb decay will be more widely dispersed, resulting in a more "washed out" or diffuse sound. This can be useful when trying to create a sense of space or depth in a mix, or when trying to create a more ambient or atmospheric effect. |
||||
Conversely, a lower diffusion parameter means that the reflections in the reverb decay will be more closely spaced, resulting in a more defined and focused sound. This can be useful when trying to create a more natural-sounding reverb effect, or when trying to highlight the details of a particular instrument or sound in a mix. |
||||
* ***Level [0 - 99]:*** the level parameter of a plate reverb refers to the overall level or amplitude of the reverb effect. It determines how loud the reverb signal is in relation to the dry or unprocessed signal. |
||||
|
||||
<br> |
||||
<br> |
||||
|
||||
* **Reverberator:** the reverberator effect, commonly known as reverb, is an audio processing effect that simulates the sound reflections and acoustic environment of a physical space. |
||||
When a sound is produced in a physical space, it travels through the air and reflects off the walls, floors, and other surfaces in the environment. These reflections create a sense of space and depth, and contribute to the character and quality of the sound. |
||||
A reverberator effect works by simulating these reflections, and can be used to create a wide range of sonic environments, from small and intimate spaces like a bedroom or bathroom, to large and expansive spaces like a concert hall or cathedral. |
||||
Reverberator can be applied to individual tracks or to a mix, and can be adjusted in various ways, such as changing the decay time or "size" of the simulated space, adjusting the frequency content of the reverb, and adjusting the level or balance of the reverb effect in relation to the dry or unprocessed signal. |
||||
Reverberator is a common effect used in many genres of music, as well as in film, television, and other forms of audio production. It can be used to create a sense of realism, depth, and immersion, or to add a sense of ambiance, texture, or mood to a recording or production. |
||||
This implementation pushes the reverberation to reach almost the shimmer effect. |
||||
|
||||
<img src="Reverberator--Channel.png" alt="FX Tube Channel Strip" width="10%" style="background-color: #fff"> |
||||
|
||||
* ***Enable [Off - On]:*** Enable / disable the FX unit. |
||||
* **Gain [0 - 99]:** the gain parameter of the reverberator refers to the overall level or amplitude of the reverberator effect. It determines how loud the reverberator signal is in relation to the dry or unprocessed signal. |
||||
* **Time [0 - 99]:** the time determines the size of the simulated reverberating space. |
||||
* **Diffusion [0 - 99]:** the diffusion parameter of a reverberator refers to the degree to which the reflections in the reverb decay are dispersed or spread out in time and space. It determines how "dense" or "sparse" the reverberator effect sounds and how the individual reflections blend together. |
||||
A higher diffusion parameter means that the reflections in the reverb decay will be more widely dispersed, resulting in a more "washed out" or diffuse sound. This can be useful when trying to create a sense of space or depth in a mix, or when trying to create a more ambient or atmospheric effect. |
||||
Conversely, a lower diffusion parameter means that the reflections in the reverberator decay will be more closely spaced, resulting in a more defined and focused sound. This can be useful when trying to create a more natural-sounding reverberator effect, or when trying to highlight the details of a particular instrument or sound in a mix. |
||||
* **Low Pass [0 - 99]:** the low pass parameter of a plate reverb refers to a filter that is applied to the reverb signal to attenuate or cut off frequencies above a certain threshold. |
||||
A low pass filter can be used to create a darker, more mellow sound by reducing or removing high-frequency content from the reverb effect. This can be useful when trying to create a more vintage or retro sound, or when trying to soften harsh or bright sounds in a mix. |
||||
|
||||
## The Menu structure |
||||
|
||||
* *MiniDexed* |
||||
* *TG1* |
||||
* *Voice* |
||||
* *Bank* |
||||
* *Volume* |
||||
* *Pan* |
||||
* **FX-Send** |
||||
* **> Tub** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Tube FX Unit. |
||||
* **> ChR** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Chorus FX Unit. |
||||
* **> Flg** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Flanger FX Unit. |
||||
* **> Orb** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Orbitone FX Unit. |
||||
* **> PhR** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Phaser FX Unit. |
||||
* **> Dly** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Delay FX Unit. |
||||
* **> Plt** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Plate Reverb FX Unit. |
||||
* **> Rev** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Reverberator FX Unit. |
||||
* **> Main** *[0 - 99]*: the amount of the Tone Generator signal that will be send to the Main output hence dry. |
||||
* *Detune* |
||||
* *Cutoff* |
||||
* *Resonance* |
||||
* *Pitch Bend* |
||||
* *Portamento* |
||||
* *Poly/Mono* |
||||
* *Modulation* |
||||
* *Channel* |
||||
* *Edit Voice:* |
||||
* ... |
||||
* *TG2* |
||||
* ... *similar to TG1 sub-menu* ... |
||||
* *TG3* |
||||
* ... *similar to TG1 sub-menu* ... |
||||
* *TG4* |
||||
* ... *similar to TG1 sub-menu* ... |
||||
* *TG5* |
||||
* ... *similar to TG1 sub-menu* ... |
||||
* *TG6* |
||||
* ... *similar to TG1 sub-menu* ... |
||||
* *TG7* |
||||
* ... *similar to TG1 sub-menu* ... |
||||
* *TG8* |
||||
* ... *similar to TG1 sub-menu* ... |
||||
* **Effects** |
||||
* Compress *[on - off]*: Enable / disable the FX unit for all Tone Generators. |
||||
* **Tube** |
||||
* **Enable** *[on - off]*: Enable / disable the FX unit. |
||||
* **Overdrv** *[0 - 99]*: The overdrive amount of the FX unit. |
||||
* **Levels** |
||||
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> Tub] |
||||
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> Tub] |
||||
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> Tub] |
||||
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> Tub] |
||||
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> Tub] |
||||
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> Tub] |
||||
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> Tub] |
||||
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> Tub] |
||||
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> Tub] |
||||
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> Tub] |
||||
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> Tub] |
||||
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> Tub] |
||||
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> Tub] |
||||
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> Tub] |
||||
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> Tub] |
||||
* **FX-Send** |
||||
* **> ChR** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Chorus FX unit. |
||||
* **> Flg** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Flanger FX unit. |
||||
* **> Orb** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Orbitone FX unit. |
||||
* **> PhR** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Phaser FX unit. |
||||
* **> Dly** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Delay FX unit. |
||||
* **> Plt** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Plate Reverb FX unit. |
||||
* **> Rev** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Reverberator FX unit. |
||||
* **> Main** *[0 - 99]*: The amount of signal processed by the Tube FX unit that will be sent to the Main output. |
||||
* **Chorus** |
||||
* **Enable** *[on - off]*: Enable / disable the FX unit. |
||||
* **Rate** *[0 - 99]*: The speed of the LFO of the FX unit. |
||||
* **Depth** *[0 - 99]*: The amount of effect that is applied to the input signal. |
||||
* **Levels** |
||||
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> ChR] |
||||
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> ChR] |
||||
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> ChR] |
||||
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> ChR] |
||||
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> ChR] |
||||
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> ChR] |
||||
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> ChR] |
||||
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> ChR] |
||||
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> ChR] |
||||
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> ChR] |
||||
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> ChR] |
||||
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> ChR] |
||||
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> ChR] |
||||
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> ChR] |
||||
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> ChR] |
||||
* **FX-Send** |
||||
* **> Tub** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Tube FX unit. |
||||
* **> Flg** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Flanger FX unit. |
||||
* **> Orb** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Orbitone FX unit. |
||||
* **> PhR** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Phaser FX unit. |
||||
* **> Dly** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Delay FX unit. |
||||
* **> Plt** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Plate Reverb FX unit. |
||||
* **> Rev** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Reverberator FX unit. |
||||
* **> Main** *[0 - 99]*: The amount of signal processed by the Chorus FX unit that will be sent to the Main output. |
||||
* **FlangR** |
||||
* **Enable** *[on - off]*: Enable / disable the FX unit. |
||||
* **Rate** *[0 - 99]*: The speed of the LFO of the FX unit. |
||||
* **Depth** *[0 - 99]*: The amount of effect that is applied to the input signal. |
||||
* **Feedbck** *[0 - 99]*: The amount of processed signal that will be reinjected at the input. |
||||
* **Levels** |
||||
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> Flg] |
||||
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> Flg] |
||||
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> Flg] |
||||
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> Flg] |
||||
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> Flg] |
||||
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> Flg] |
||||
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> Flg] |
||||
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> Flg] |
||||
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> Flg] |
||||
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> Flg] |
||||
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> Flg] |
||||
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> Flg] |
||||
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> Flg] |
||||
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> Flg] |
||||
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> Flg] |
||||
* **FX-Send** |
||||
* **> Tub** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Tube FX unit. |
||||
* **> ChR** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Chorus FX unit. |
||||
* **> Orb** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Orbitone FX unit. |
||||
* **> PhR** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Phaser FX unit. |
||||
* **> Dly** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Delay FX unit. |
||||
* **> Plt** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Plate Reverb FX unit. |
||||
* **> Rev** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Reverberator FX unit. |
||||
* **> Main** *[0 - 99]*: The amount of signal processed by the Flanger FX unit that will be sent to the Main output. |
||||
* **Orb** |
||||
* **Enable** *[on - off]*: Enable / disable the FX unit. |
||||
* **Rate** *[0 - 99]*: The speed of the LFO of the FX unit. |
||||
* **Depth** *[0 - 99]*: The amount of effect that is applied to the input signal. |
||||
* **Levels** |
||||
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> Orb] |
||||
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> Orb] |
||||
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> Orb] |
||||
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> Orb] |
||||
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> Orb] |
||||
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> Orb] |
||||
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> Orb] |
||||
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> Orb] |
||||
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> Orb] |
||||
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> Orb] |
||||
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> Orb] |
||||
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> Orb] |
||||
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> Orb] |
||||
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> Orb] |
||||
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> Orb] |
||||
* **FX-Send** |
||||
* **> Tub** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Tube FX unit. |
||||
* **> ChR** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Chorus FX unit. |
||||
* **> Flg** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Flanger FX unit. |
||||
* **> PhR** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Phaser FX unit. |
||||
* **> Dly** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Delay FX unit. |
||||
* **> Plt** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Plate Reverb FX unit. |
||||
* **> Rev** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Reverberator FX unit. |
||||
* **> Main** *[0 - 99]*: The amount of signal processed by the Orbitone FX unit that will be sent to the Main output. |
||||
* **PhasR** |
||||
* **Enable** *[on - off]*: Enable / disable the FX unit. |
||||
* **Rate** *[0 - 99]*: The speed of the LFO of the FX unit. |
||||
* **Depth** *[0 - 99]*: The amount of effect that is applied to the input signal. |
||||
* **Feedbck** *[0 - 99]*: The amount of processed signal that will be reinjected at the input. |
||||
* **Stages** *[2 - 24]*: The number of processing phasing stages. |
||||
* **Levels** |
||||
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> PhR] |
||||
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> PhR] |
||||
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> PhR] |
||||
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> PhR] |
||||
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> PhR] |
||||
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> PhR] |
||||
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> PhR] |
||||
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> PhR] |
||||
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> PhR] |
||||
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> PhR] |
||||
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> PhR] |
||||
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> PhR] |
||||
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> PhR] |
||||
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> PhR] |
||||
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> PhR] |
||||
* **FX-Send** |
||||
* **> Tub** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Tube FX unit. |
||||
* **> ChR** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Chorus FX unit. |
||||
* **> Flg** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Flanger FX unit. |
||||
* **> Orb** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Orbitone FX unit. |
||||
* **> Dly** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Delay FX unit. |
||||
* **> Plt** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Plate Reverb FX unit. |
||||
* **> Rev** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Reverberator FX unit. |
||||
* **> Main** *[0 - 99]*: The amount of signal processed by the Phaser FX unit that will be sent to the Main output. |
||||
* **Delay** |
||||
* **Enable** *[on - off]*: Enable / disable the FX unit. |
||||
* **L Delay** *[-99 - 99]*: The delay time for the Left channel. Negtive values play the echoed part of the processed sound reversed. |
||||
* **R Delay** *[-99 - 99]*: The delay time for the Right channel. Negtive values play the echoed part of the processed sound reversed. |
||||
* **Feedbck** *[0 - 99]*: The amount of processed signal that will be reinjected at the input. |
||||
* **Levels** |
||||
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> Dly] |
||||
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> Dly] |
||||
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> Dly] |
||||
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> Dly] |
||||
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> Dly] |
||||
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> Dly] |
||||
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> Dly] |
||||
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> Dly] |
||||
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> Dly] |
||||
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> Dly] |
||||
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> Dly] |
||||
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> Dly] |
||||
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> Dly] |
||||
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> Dly] |
||||
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> Dly] |
||||
* **FX-Send** |
||||
* **> Tub** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Tube FX unit. |
||||
* **> ChR** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Chorus FX unit. |
||||
* **> Flg** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Flanger FX unit. |
||||
* **> Orb** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Orbitone FX unit. |
||||
* **> PhR** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Phaser FX unit. |
||||
* **> Plt** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Plate Reverb FX unit. |
||||
* **> Rev** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Reverberator FX unit. |
||||
* **> Main** *[0 - 99]*: The amount of signal processed by the Delay FX unit that will be sent to the Main output. |
||||
* **Plt Rvb** |
||||
* **Enable** *[on - off]*: Enable / disable the FX unit. |
||||
* **Size** *[0 - 99]*: The size of the plate that simulate the size of the reverberating room. |
||||
* **High damp** *[0 - 99]*: The amount of high-frequency attenuation. |
||||
* **Low damp** *[0 - 99]*: The amount of low-frequency attenuation. |
||||
* **Low pass** *[0 - 99]*: The low pass cutoff frequency. |
||||
* **Diffusion** *[0 - 99]*: The degree to which the reflections in the reverb decay are dispersed or spread out in time and space. |
||||
* **Level** *[0 - 99]*: The overall level or amplitude of the reverb effect. |
||||
* **Levels** |
||||
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> Plt] |
||||
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> Plt] |
||||
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> Plt] |
||||
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> Plt] |
||||
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> Plt] |
||||
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> Plt] |
||||
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> Plt] |
||||
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> Plt] |
||||
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> Plt] |
||||
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> Plt] |
||||
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> Plt] |
||||
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> Plt] |
||||
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> Plt] |
||||
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> Plt] |
||||
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> Plt] |
||||
* **FX-Send** |
||||
* **> Tub** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Tube FX unit. |
||||
* **> ChR** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Chorus FX unit. |
||||
* **> Flg** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Flanger FX unit. |
||||
* **> Orb** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Orbitone FX unit. |
||||
* **> PhR** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Phaser FX unit. |
||||
* **> Dly** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Delay FX unit. |
||||
* **> Rev** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Reverberator FX unit. |
||||
* **> Main** *[0 - 99]*: The amount of signal processed by the Plate Reverb FX unit that will be sent to the Main output. |
||||
* **Rvbrtor** |
||||
* **Enable** *[on - off]*: Enable / disable the FX unit. |
||||
* **Gain** *[0 - 99]*: The overall level or amplitude of the FX. |
||||
* **Time** *[0 - 99]*: The time determines the size of the simulated reverberating space. |
||||
* **Diffusion** *[0 - 99]*: The degree to which the reflections in the reverb decay are dispersed or spread out in time and space. |
||||
* **Low pass** *[0 - 99]*: The low pass cutoff frequency. |
||||
* **Levels** |
||||
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> Rev] |
||||
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> Rev] |
||||
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> Rev] |
||||
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> Rev] |
||||
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> Rev] |
||||
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> Rev] |
||||
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> Rev] |
||||
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> Rev] |
||||
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> Rev] |
||||
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> Rev] |
||||
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> Rev] |
||||
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> Rev] |
||||
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> Rev] |
||||
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> Rev] |
||||
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> Rev] |
||||
* **FX-Send** |
||||
* **> Tub** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Tube FX unit. |
||||
* **> ChR** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Chorus FX unit. |
||||
* **> Flg** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Flanger FX unit. |
||||
* **> Orb** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Orbitone FX unit. |
||||
* **> PhR** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Phaser FX unit. |
||||
* **> Dly** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Delay FX unit. |
||||
* **> Plt** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Plate Reverb FX unit. |
||||
* **> Main** *[0 - 99]*: The amount of signal processed by the Reverberator FX unit that will be sent to the Main output. |
||||
* **MainOut** |
||||
* **TG1 >** *[0 - 99]* - shortcut to [TG1] >> [FX-Send] >> [> Main] |
||||
* **TG2 >** *[0 - 99]* - shortcut to [TG2] >> [FX-Send] >> [> Main] |
||||
* **TG3 >** *[0 - 99]* - shortcut to [TG3] >> [FX-Send] >> [> Main] |
||||
* **TG4 >** *[0 - 99]* - shortcut to [TG4] >> [FX-Send] >> [> Main] |
||||
* **TG5 >** *[0 - 99]* - shortcut to [TG5] >> [FX-Send] >> [> Main] |
||||
* **TG6 >** *[0 - 99]* - shortcut to [TG6] >> [FX-Send] >> [> Main] |
||||
* **TG7 >** *[0 - 99]* - shortcut to [TG7] >> [FX-Send] >> [> Main] |
||||
* **TG8 >** *[0 - 99]* - shortcut to [TG8] >> [FX-Send] >> [> Main] |
||||
* **Tub >** *[0 - 99]* - shortcut to [Effects] >> [Tube] >> [> Main] |
||||
* **ChR >** *[0 - 99]* - shortcut to [Effects] >> [Chorus] >> [> Main] |
||||
* **Flg >** *[0 - 99]* - shortcut to [Effects] >> [FlangR] >> [> Main] |
||||
* **Orb >** *[0 - 99]* - shortcut to [Effects] >> [Orb] >> [> Main] |
||||
* **PhR >** *[0 - 99]* - shortcut to [Effects] >> [PhasR] >> [> Main] |
||||
* **Dly >** *[0 - 99]* - shortcut to [Effects] >> [Delay] >> [> Main] |
||||
* **Plt >** *[0 - 99]* - shortcut to [Effects] >> [Plt Rvb] >> [> Main] |
||||
* **Rev >** *[0 - 99]* - shortcut to [Effects] >> [Rvbrtor] >> [> Main] |
||||
* *Performance* |
||||
* *Load* |
||||
* ... |
||||
* *Save* |
||||
* ... |
||||
* *Delete* |
||||
* ... |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 762 KiB |