diff --git a/components/ctagSoundProcessor/CMakeLists.txt b/components/ctagSoundProcessor/CMakeLists.txt index 837418b5..e4206fb7 100644 --- a/components/ctagSoundProcessor/CMakeLists.txt +++ b/components/ctagSoundProcessor/CMakeLists.txt @@ -41,6 +41,8 @@ file(GLOB SRCS_FILES polypad/*.cpp filters/*.hpp filters/*.cpp + fx/*.hpp + fx/*.cpp tesselode/*.h tesselode/*.cpp airwindows/*.hpp diff --git a/components/ctagSoundProcessor/ctagSoundProcessorMSxxNoise.cpp b/components/ctagSoundProcessor/ctagSoundProcessorMSxxNoise.cpp new file mode 100644 index 00000000..8ba17fc5 --- /dev/null +++ b/components/ctagSoundProcessor/ctagSoundProcessorMSxxNoise.cpp @@ -0,0 +1,219 @@ +#include "ctagSoundProcessorMSxxNoise.hpp" +using namespace CTAG::SP; + +// --- Trigger/Gate values --- +#define GATE_HIGH_NEW 2 +#define GATE_HIGH 1 +#define GATE_LOW 0 + +#define MAX_CUTOFF_FREQ 14000.f +#define MIN_LFO_SPEED 0.05f +#define MAX_LFO_SPEED 20.f + +// --- Additional macros for oscillator and GUI-parameter processing --- +#define MK_TRIG_PAR(outname, inname) int outname = process_param_trig(data, trig_##inname, inname, e_##inname); + +// --- Process trigger signals and keep their state internally --- +inline int ctagSoundProcessorMSxxNoise::process_param_trig(const ProcessData &data, int trig_myparm, int my_parm, int enum_trigger_id, int gate_type = 0 ) +{ + int trig_status = 0; + + if(trig_myparm != -1) // Trigger given via CV/Gate or button? + { + trig_status = (data.trig[trig_myparm]==0); // HIGH is 0, so we negate for boolean logic + if( gate_type == 1 ) + return(trig_status); + + if(trig_status) // Statuschange from HIGH to LOW or LOW to HIGH? Startup-Status for prev_trig_state is -1, so first change is always new + { + if( low_reached[enum_trigger_id] ) // We had a trigger low before the new trigger high + { + if (prev_trig_state[enum_trigger_id] == GATE_LOW || gate_type==2 ) // Toggle or AD EG Trigger... + { + prev_trig_state[enum_trigger_id] = GATE_HIGH; // Remember status for next round + low_reached[enum_trigger_id] = false; + return (GATE_HIGH_NEW); // New trigger + } + else // previous status was high! + { + prev_trig_state[enum_trigger_id] = GATE_LOW; // Remember status for next round + low_reached[enum_trigger_id] = false; + return (GATE_LOW); // New trigger + } + } + } + else + low_reached[enum_trigger_id] = true; + } + else // We may have a trigger set by activating the button via the GUI + { + if (my_parm != prev_trig_state[enum_trigger_id]) // Statuschange from HIGH to LOW or LOW to HIGH? + { + prev_trig_state[enum_trigger_id] = my_parm; // Remember status + if(my_parm != 0) // LOW if 0 + return (GATE_HIGH_NEW); // New trigger + else + return (GATE_LOW); // Trigger released + } + } + return(prev_trig_state[enum_trigger_id]); // No change (1 for active, 0 for inactive) +} + + +void ctagSoundProcessorMSxxNoise::Process(const ProcessData &data) +{ + // === Process all parameters from the GUI === + // --- Soundsources --- + MK_TRIG_PAR(t_ExternalActive, ExternalActive); + MK_FLT_PAR_ABS(f_NoiseExternalBalance, NoiseExternalBalance, 4095.f, 1.f); + MK_TRIG_PAR(t_PinkActive, PinkActive); + MK_TRIG_PAR(t_WhiteActive, WhiteActive); + MK_FLT_PAR_ABS(f_PinkWhiteBalance, PinkWhiteBalance, 4095.f, 1.f); + MK_FLT_PAR_ABS(f_Volume, Volume, 4095.f, 4.f); // We use a high value to have headroom, so lower the volume normally + + // --- Soundmodifiers --- + MK_TRIG_PAR(t_FilterBypass, FilterBypass); + MK_TRIG_PAR(t_SaturationActive, SaturationActive); + MK_FLT_PAR_ABS_MIN_MAX(f_Saturation, Saturation, 4095.f, 0.899f, 8.99f); + MK_FLT_PAR_ABS(f_Cutoff, Cutoff, 4095.f, MAX_CUTOFF_FREQ); + MK_FLT_PAR_ABS_MIN_MAX(f_Resonance, Resonance, 4095.f, 0.0002f, 1.99f); + MK_TRIG_PAR(t_MGactive, MGactive); + MK_TRIG_PAR(t_MGisSquare, MGisSquare); + MK_FLT_PAR_ABS_MIN_MAX(f_MGspeed, MGspeed, 4095.f, MIN_LFO_SPEED, MAX_LFO_SPEED); + MK_FLT_PAR_ABS(f_MGamnt, MGamnt, 4095.f, MAX_CUTOFF_FREQ); + MK_TRIG_PAR(t_PhaserBypass, PhaserBypass); + MK_FLT_PAR_ABS_MIN_MAX(f_PhaserDryWet, PhaserDryWet, 4095.f, 0.f, 100.f); + + // --- Phaser settings --- + MK_TRIG_PAR(t_PhaserPreset, PhaserPreset); + MK_FLT_PAR_ABS(f_PhaserColor, PhaserColor, 4095.f, 1.f); + MK_FLT_PAR_ABS_MIN_MAX(f_PhaserLFOfrequency, PhaserLFOfrequency, 4095.f, 0.00999999978f,5.f); + MK_FLT_PAR_ABS_MIN_MAX(f_PhaserFeedbackDepth, PhaserFeedbackDepth, 4095.f, 0.f, 99.f ); + MK_FLT_PAR_ABS_MIN_MAX(f_PhaserFeedbackBassCut, PhaserFeedbackBassCut, 4095.f, 10.f, 5000.f); + + // === Precalculate values for DSP-processing === + // --- Precalculate internal/external sound and noise-mix --- + float f_internal_sound = t_ExternalActive ? 1.f-f_NoiseExternalBalance : 1.f; + float f_external_sound = t_ExternalActive ? f_NoiseExternalBalance : 0.f; + float f_pinkAmnt = t_PinkActive ? (1.f-f_PinkWhiteBalance)*f_internal_sound : 0.f; + float f_whiteAmnt = t_WhiteActive ? f_PinkWhiteBalance*f_internal_sound : 0.f; + + // --- LFO modulation of the filter cutoff --- + if(!t_MGactive) + f_MGamnt = 0.f; + mg.SetFrequency(f_MGspeed); + float mg_wave = mg.Process(); + if(t_MGisSquare) + mg_wave = (mg_wave >= 0.f) ? 1.f : -1.f; + f_Cutoff += mg_wave*f_MGamnt; + CONSTRAIN( f_Cutoff, 0.f, MAX_CUTOFF_FREQ ); + + // --- Apply filter-parameters --- + if(!t_SaturationActive) + f_Saturation = 0.f; // Switch off saturator! + wpKorg35.SetSaturation(f_Saturation); + wpKorg35.SetCutoff(f_Cutoff); + wpKorg35.SetResonance(f_Resonance); + + // --- Phaser --- + if( t_PhaserPreset == GATE_HIGH_NEW ) + phaser.Init(); // Set to preset-defaults + + if(!t_PhaserPreset) // Use individual settings instead + { + phaser.SetDryWet(f_PhaserDryWet); + phaser.SetColor(f_PhaserColor); + phaser.SetLFOfrequency(f_PhaserLFOfrequency); + phaser.SetFeedbackDepth(f_PhaserFeedbackDepth); + phaser.SetFeedbackBassCut(f_PhaserFeedbackBassCut); + } + // === Main DSP loops (depending on Bypass-settings) === + if(!t_FilterBypass && !t_PhaserBypass) + { + for (int i = 0; i < bufSz; i++) + data.buf[i*2+processCh] = phaser.Process(wpKorg35.Process(data.buf[i*2+processCh]*f_external_sound + pNoise.Process()*f_pinkAmnt + wNoise.Process()*f_whiteAmnt))*f_Volume; + } + else if(!t_FilterBypass && t_PhaserBypass) + { + for (int i = 0; i < bufSz; i++) + data.buf[i*2+processCh] = wpKorg35.Process(data.buf[i*2+processCh]*f_external_sound + pNoise.Process()*f_pinkAmnt + wNoise.Process()*f_whiteAmnt)*f_Volume; + } + else if(t_FilterBypass && !t_PhaserBypass) + { + for (int i = 0; i < bufSz; i++) + data.buf[i*2+processCh] = phaser.Process(data.buf[i*2+processCh]*f_external_sound + pNoise.Process()*f_pinkAmnt + wNoise.Process()*f_whiteAmnt)*f_Volume; + } + else if(t_FilterBypass && t_PhaserBypass) + { + for (int i = 0; i < bufSz; i++) + data.buf[i*2+processCh] = (data.buf[i*2+processCh]*f_external_sound + pNoise.Process()*f_pinkAmnt + wNoise.Process()*f_whiteAmnt)*f_Volume; + } +} + +ctagSoundProcessorMSxxNoise::ctagSoundProcessorMSxxNoise() +{ + // construct internal data model + knowYourself(); + model = std::make_unique(id, isStereo); + LoadPreset(0); + + // --- Init LFO --- + mg.SetSampleRate(44100.f / bufSz); // Please note: because the LFO is applied already outside the DSP-loop we reduce it's frequency in a manner to fit + mg.SetFrequency(1.f); +} + +ctagSoundProcessorMSxxNoise::~ctagSoundProcessorMSxxNoise() +{ +} + +void ctagSoundProcessorMSxxNoise::knowYourself(){ + // autogenerated code here + // sectionCpp0 + pMapPar.emplace("ExternalActive", [&](const int val){ ExternalActive = val;}); + pMapTrig.emplace("ExternalActive", [&](const int val){ trig_ExternalActive = val;}); + pMapPar.emplace("NoiseExternalBalance", [&](const int val){ NoiseExternalBalance = val;}); + pMapCv.emplace("NoiseExternalBalance", [&](const int val){ cv_NoiseExternalBalance = val;}); + pMapPar.emplace("PinkActive", [&](const int val){ PinkActive = val;}); + pMapTrig.emplace("PinkActive", [&](const int val){ trig_PinkActive = val;}); + pMapPar.emplace("WhiteActive", [&](const int val){ WhiteActive = val;}); + pMapTrig.emplace("WhiteActive", [&](const int val){ trig_WhiteActive = val;}); + pMapPar.emplace("PinkWhiteBalance", [&](const int val){ PinkWhiteBalance = val;}); + pMapCv.emplace("PinkWhiteBalance", [&](const int val){ cv_PinkWhiteBalance = val;}); + pMapPar.emplace("Volume", [&](const int val){ Volume = val;}); + pMapCv.emplace("Volume", [&](const int val){ cv_Volume = val;}); + pMapPar.emplace("FilterBypass", [&](const int val){ FilterBypass = val;}); + pMapTrig.emplace("FilterBypass", [&](const int val){ trig_FilterBypass = val;}); + pMapPar.emplace("SaturationActive", [&](const int val){ SaturationActive = val;}); + pMapTrig.emplace("SaturationActive", [&](const int val){ trig_SaturationActive = val;}); + pMapPar.emplace("Saturation", [&](const int val){ Saturation = val;}); + pMapCv.emplace("Saturation", [&](const int val){ cv_Saturation = val;}); + pMapPar.emplace("Cutoff", [&](const int val){ Cutoff = val;}); + pMapCv.emplace("Cutoff", [&](const int val){ cv_Cutoff = val;}); + pMapPar.emplace("Resonance", [&](const int val){ Resonance = val;}); + pMapCv.emplace("Resonance", [&](const int val){ cv_Resonance = val;}); + pMapPar.emplace("MGactive", [&](const int val){ MGactive = val;}); + pMapTrig.emplace("MGactive", [&](const int val){ trig_MGactive = val;}); + pMapPar.emplace("MGisSquare", [&](const int val){ MGisSquare = val;}); + pMapTrig.emplace("MGisSquare", [&](const int val){ trig_MGisSquare = val;}); + pMapPar.emplace("MGspeed", [&](const int val){ MGspeed = val;}); + pMapCv.emplace("MGspeed", [&](const int val){ cv_MGspeed = val;}); + pMapPar.emplace("MGamnt", [&](const int val){ MGamnt = val;}); + pMapCv.emplace("MGamnt", [&](const int val){ cv_MGamnt = val;}); + pMapPar.emplace("PhaserBypass", [&](const int val){ PhaserBypass = val;}); + pMapTrig.emplace("PhaserBypass", [&](const int val){ trig_PhaserBypass = val;}); + pMapPar.emplace("PhaserDryWet", [&](const int val){ PhaserDryWet = val;}); + pMapCv.emplace("PhaserDryWet", [&](const int val){ cv_PhaserDryWet = val;}); + pMapPar.emplace("PhaserPreset", [&](const int val){ PhaserPreset = val;}); + pMapTrig.emplace("PhaserPreset", [&](const int val){ trig_PhaserPreset = val;}); + pMapPar.emplace("PhaserColor", [&](const int val){ PhaserColor = val;}); + pMapCv.emplace("PhaserColor", [&](const int val){ cv_PhaserColor = val;}); + pMapPar.emplace("PhaserLFOfrequency", [&](const int val){ PhaserLFOfrequency = val;}); + pMapCv.emplace("PhaserLFOfrequency", [&](const int val){ cv_PhaserLFOfrequency = val;}); + pMapPar.emplace("PhaserFeedbackDepth", [&](const int val){ PhaserFeedbackDepth = val;}); + pMapCv.emplace("PhaserFeedbackDepth", [&](const int val){ cv_PhaserFeedbackDepth = val;}); + pMapPar.emplace("PhaserFeedbackBassCut", [&](const int val){ PhaserFeedbackBassCut = val;}); + pMapCv.emplace("PhaserFeedbackBassCut", [&](const int val){ cv_PhaserFeedbackBassCut = val;}); + isStereo = false; + id = "MSxxNoise"; + // sectionCpp0 +} \ No newline at end of file diff --git a/components/ctagSoundProcessor/ctagSoundProcessorMSxxNoise.hpp b/components/ctagSoundProcessor/ctagSoundProcessorMSxxNoise.hpp new file mode 100644 index 00000000..f1d0295f --- /dev/null +++ b/components/ctagSoundProcessor/ctagSoundProcessorMSxxNoise.hpp @@ -0,0 +1,77 @@ +#include +#include "ctagSoundProcessor.hpp" + +#include "helpers/ctagWNoiseGen.hpp" +#include "helpers/ctagPNoiseGen.hpp" +#include "helpers/ctagSineSource.hpp" + +#include "filters/ctagWPkorg35.hpp" +#include "fx/ctagPebble.hpp" +#include "../mutable/eurorack/stmlib/stmlib.h" // for CONSTRAIN Macro... + +using namespace CTAG::SP::HELPERS; + +namespace CTAG +{ + namespace SP + { + class ctagSoundProcessorMSxxNoise : public ctagSoundProcessor + { + public: + virtual void Process(const ProcessData &) override; + ctagSoundProcessorMSxxNoise(); + virtual ~ctagSoundProcessorMSxxNoise(); + + private: + virtual void knowYourself() override; + + // --- Remember status of triggers / buttons --- + inline int process_param_trig( const ProcessData &data, int trig_myparm, int my_parm, int prev_trig_state_id, int gate_type ); // rescale incoming data to bool + enum trig_states + { + e_PinkActive, e_WhiteActive, e_MGisSquare, e_SaturationActive, e_MGactive, e_FilterBypass, e_PhaserPreset, e_PhaserBypass, + e_ExternalActive, e_Switch_1, e_Switch_2, e_VibratoMode, e_Invert, e_MSxxNoise_options_max + }; + int prev_trig_state[e_MSxxNoise_options_max] = {0}; // Initialize _all_ entries with "low value" + bool low_reached[e_MSxxNoise_options_max] = {false}; // We need this for look for toggle-events + + // --- White Noise --- + ctagWNoiseGen wNoise; + ctagPNoiseGen pNoise; + + // --- Filter --- + ctagWPkorg35 wpKorg35; + ctagSineSource mg; + + // --- Phaser --- + ctagPebble phaser; + + // private attributes could go here + // autogenerated code here + // sectionHpp + atomic ExternalActive, trig_ExternalActive; + atomic NoiseExternalBalance, cv_NoiseExternalBalance; + atomic PinkActive, trig_PinkActive; + atomic WhiteActive, trig_WhiteActive; + atomic PinkWhiteBalance, cv_PinkWhiteBalance; + atomic Volume, cv_Volume; + atomic FilterBypass, trig_FilterBypass; + atomic SaturationActive, trig_SaturationActive; + atomic Saturation, cv_Saturation; + atomic Cutoff, cv_Cutoff; + atomic Resonance, cv_Resonance; + atomic MGactive, trig_MGactive; + atomic MGisSquare, trig_MGisSquare; + atomic MGspeed, cv_MGspeed; + atomic MGamnt, cv_MGamnt; + atomic PhaserBypass, trig_PhaserBypass; + atomic PhaserDryWet, cv_PhaserDryWet; + atomic PhaserPreset, trig_PhaserPreset; + atomic PhaserColor, cv_PhaserColor; + atomic PhaserLFOfrequency, cv_PhaserLFOfrequency; + atomic PhaserFeedbackDepth, cv_PhaserFeedbackDepth; + atomic PhaserFeedbackBassCut, cv_PhaserFeedbackBassCut; + // sectionHpp + }; + } +} \ No newline at end of file diff --git a/components/ctagSoundProcessor/filters/ctagWPkorg35.cpp b/components/ctagSoundProcessor/filters/ctagWPkorg35.cpp new file mode 100644 index 00000000..0df885d3 --- /dev/null +++ b/components/ctagSoundProcessor/filters/ctagWPkorg35.cpp @@ -0,0 +1,160 @@ +/*************** +CTAG TBD >>to be determined<< is an open source eurorack synthesizer module. + +A project conceived within the Creative Technologies Arbeitsgruppe of +Kiel University of Applied Sciences: https://www.creative-technologies.de + +(c) 2020 by Robert Manzke. All rights reserved. + +The CTAG TBD software is licensed under the GNU General Public License +(GPL 3.0), available here: https://www.gnu.org/licenses/gpl-3.0.txt + +The CTAG TBD hardware design is released under the Creative Commons +Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0). +Details here: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +CTAG TBD is provided "as is" without any express or implied warranties. + +License and copyright details for specific submodules are included in their +respective component folders / files if different from this license. +***************/ + +#include +#include "ctagWPkorg35.hpp" +#include "../helpers/ctagFastMath.hpp" + +using namespace CTAG::SP::HELPERS; + +void ctagWPkorg35::Init() +{ + fs_ = 44100.f; + alpha = 0.f; + pcutoff = cutoff_ = 1000.f; + pres = resonance_ = 1.f; + + /* reset memory for filters */ + lpf1_z = 0.f; + lpf2_z = 0.f; + hpf_z = 0.f; + + /* initialize LPF1 */ + lpf1_a = 1.f; + lpf1_z = 0.f; + + /* initialize LPF2 */ + lpf2_a = 1.f; + lpf2_b = 1.f; + lpf2_z = 0.f; + + saturation_ = 0.f; + + /* update filters, prewarp for BZT */ + wd = 2*M_PI*cutoff_; + T = 1.f/fs_; + wa = (2.f/T)*fasttanh(wd*T/2.f); + g = wa*T/2.f; + /* the feedforward coeff in the VA One Pole */ + G = g/(1.f + g); + + /* set alphas */ + lpf1_a = G; + lpf2_a = G; + hpf_a = G; + + /* set betas */ + lpf2_b = (resonance_ - resonance_*G)/(1.f + g); + hpf_b = -1.f/(1.f + g); + + alpha = 1.f/(1.f - resonance_*G + resonance_*G*G); +} + +void ctagWPkorg35::SetSaturation(float saturation) +{ + saturation_ = saturation; +} + +void ctagWPkorg35::SetCutoff(float cutoff) +{ + cutoff_ = cutoff; +} + +void ctagWPkorg35::SetResonance(float resonance) +{ + if( resonance <= 0 ) + resonance_ = 0.0002f; // Low value for a 10bit range from 0 to 1 + else + resonance_ = resonance; +} + +void ctagWPkorg35::SetSampleRate(float sr) +{ + fs_ = sr; +} + +float ctagWPkorg35::Process(float in) +{ + /* initialize variables */ + y1 = 0.f; + S35 = 0.f; + u = 0.f; + y = 0.f; + vn = 0.f; + + if(pcutoff != cutoff_ || pres != resonance_ || psaturation != saturation_ ) // Reinit filter if external values have changed! + { + /* prewarp for BZT */ + wd = 2*M_PI*cutoff_; + T = 1.f/fs_; + wa = (2.f/T)*fasttanh(wd*T/2.f); + g = wa*T/2.f; + /* the feedforward coeff in the VA One Pole */ + G = g/(1.f + g); + + /* set alphas */ + lpf1_a = G; + lpf2_a = G; + hpf_a = G; + + /* set betas */ + lpf2_b = (resonance_ - resonance_*G)/(1.f + g); + hpf_b = -1.f/(1.f + g); + + alpha = 1.f/(1.f - resonance_*G + resonance_*G*G); + } + + /* process input through LPF1 */ + vn = (in - lpf1_z) * lpf1_a; + y1 = vn + lpf1_z; + lpf1_z = y1 + vn; + + /* form feedback value */ + S35 = (hpf_z * hpf_b) + (lpf2_z * lpf2_b); + + /* Calculate u */ + u = alpha * (y1 + S35); + + /* Naive NLP */ + if(saturation_ > 0.f) + u = fasttanh(saturation_ * u); + + /* Feed it to LPF2 */ + vn = (u - lpf2_z) * lpf2_a; + y = (vn + lpf2_z); + lpf2_z = y + vn; + y *= resonance_; + + /* Feed y to HPF2 */ + vn = (y - hpf_z) * hpf_a; + hpf_z = vn + (vn + hpf_z); + + /* Auto-normalize */ + if(resonance_ > 0.f) + y *= 1.0 / resonance_; + + pcutoff = cutoff_; + pres = resonance_; + psaturation = saturation_; + + return(y); +} + diff --git a/components/ctagSoundProcessor/filters/ctagWPkorg35.hpp b/components/ctagSoundProcessor/filters/ctagWPkorg35.hpp new file mode 100644 index 00000000..44f35e04 --- /dev/null +++ b/components/ctagSoundProcessor/filters/ctagWPkorg35.hpp @@ -0,0 +1,99 @@ +/*************** +CTAG TBD >>to be determined<< is an open source eurorack synthesizer module. + +A project conceived within the Creative Technologies Arbeitsgruppe of +Kiel University of Applied Sciences: https://www.creative-technologies.de + +(c) 2020,2021 by Robert Manzke. All rights reserved. + +The CTAG TBD software is licensed under the GNU General Public License +(GPL 3.0), available here: https://www.gnu.org/licenses/gpl-3.0.txt + +The CTAG TBD hardware design is released under the Creative Commons +Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0). +Details here: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +CTAG TBD is provided "as is" without any express or implied warranties. + +License and copyright details for specific submodules are included in their +respective component folders / files if different from this license. +***************/ + +/* +Zero Delay Feedback Filters +Based on code by Will Pirkle, presented in: +http://www.willpirkle.com/Downloads/AN-4VirtualAnalogFilters.2.0.pdf +http://www.willpirkle.com/Downloads/AN-5Korg35_V3.pdf +http://www.willpirkle.com/Downloads/AN-6DiodeLadderFilter.pdf +http://www.willpirkle.com/Downloads/AN-7Korg35HPF_V2.pdf +and in his book "Designing software synthesizer plug-ins in C++ : for +RackAFX, VST3, and Audio Units" +ZDF using Trapezoidal integrator by Vadim Zavalishin, presented in "The Art +of VA Filter Design" (https://www.native-instruments.com/fileadmin/ni_media/ +downloads/pdf/VAFilterDesign_1.1.1.pdf) +Adapted by M. Brüssel from a Soundpipe version which is based off of an implemenation of the Korg35 filter by Will +Pirkle. The Soundpipe version had been ported from the CCRMA chugin by the same name. + +Recommended values: +SetSaturation(); 0-8.99, off when 0 +SetCutoff(); 0-12000 +SetResonance(); 0.0002-1.99, filter is closed when 0! +*/ + +#pragma once + +#include "ctagFilterBase.hpp" + +namespace CTAG::SP::HELPERS +{ + class ctagWPkorg35 : public ctagFilterBase + { + public: + ctagWPkorg35() {Init();} + + void SetSaturation(float saturation); + virtual void SetCutoff(float cutoff) override; + virtual void SetResonance(float resonance) override; + virtual void SetSampleRate(float sr) override; + virtual float Process(float in) override; + virtual void Init() override; + + private: + /* LPF1 */ + float lpf1_a; + float lpf1_z; + + /* LPF2 */ + float lpf2_a; + float lpf2_b; + float lpf2_z; + + /* HPF */ + float hpf_a; + float hpf_b; + float hpf_z; + + float alpha; + float saturation_; + + /* variables for reuse */ + float y1 = 0.f; + float S35 = 0.f; + float u = 0.f; + float y = 0.f; + float vn = 0.f; + + float wd = 0.f; + float T = 0.f; + float wa = 0.f; + float g = 0.f; + float G = 0.f; + + float pcutoff; // Previous cutoff + float pres; // Previous resonance + float psaturation; // Previous saturation + }; +} + + + diff --git a/components/ctagSoundProcessor/fx/ctagPebble.cpp b/components/ctagSoundProcessor/fx/ctagPebble.cpp new file mode 100644 index 00000000..44b9fe4c --- /dev/null +++ b/components/ctagSoundProcessor/fx/ctagPebble.cpp @@ -0,0 +1,193 @@ +/*************** +CTAG TBD >>to be determined<< is an open source eurorack synthesizer module. + +A project conceived within the Creative Technologies Arbeitsgruppe of +Kiel University of Applied Sciences: https://www.creative-technologies.de + +(c) 2020,2021 by Robert Manzke. All rights reserved. + +The CTAG TBD software is licensed under the GNU General Public License +(GPL 3.0), available here: https://www.gnu.org/licenses/gpl-3.0.txt + +The CTAG TBD hardware design is released under the Creative Commons +Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0). +Details here: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +CTAG TBD is provided "as is" without any express or implied warranties. + +License and copyright details for specific submodules are included in their +respective component folders / files if different from this license. +***************/ +/* +Phaser effect, based on the Faust-Code for a "Electro Harmonix Small Stone" type of phaser by Jean Pierre Cimalando at https://github.com/jpcima/stone-phaser +Référence : + Kiiski, R., Esqueda, F., & Välimäki, V. (2016). + Time-variant gray-box modeling of a phaser pedal. + In 19th International Conference on Digital Audio Effects (DAFx-16). +Adapted by M. Brüssel from the C++ code derived from the "stone_phaser.dsp" there via https://faustide.grame.fr +*/ + +#include +#include +#include "helpers/ctagFastMath.hpp" +#include "fx/ctagPebble.hpp" + +using namespace CTAG::SP::HELPERS; + +void ctagPebble::Init() // For convenience Init() already will be called by the constructor +{ + // === Faust: classInit() --- + // --- Faust: instanceInitmydspSIG0() --- + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) + { + iRec14[l9] = 0; + } + // --- fillmydspSIG0() --- + for (int i = 0; (i < sizeof(ftbl0mydspSIG0)/sizeof(float)); i = (i + 1)) + { + iRec14[0] = (iRec14[1] + 1); + float fTemp3 = (0.0078125f * float((iRec14[0] + -1))); + float fTemp4 = float(int(fTemp3)); + float fTemp5 = (fTemp3 - fTemp4); + ftbl0mydspSIG0[i] = (1.0f - (1.02564108f * std::sin((2.69344211f * ((fTemp5 < 0.5f) ? fTemp5 : (fTemp4 + (1.0f - fTemp3))))))); + iRec14[1] = iRec14[0]; + } + // === Faust: instanceInit() --- + // --- Faust: instanceConstants(int sample_rate) --- + fSampleRate = 44100.f; // Default, use SetSampleRate() to change! + float fConst0 = std::min(192000.0f, std::max(1.0f, float(fSampleRate))); + fConst1 = std::exp((0.0f - (10.0f / fConst0))); + fConst2 = (1.0f - fConst1); + fConst3 = std::exp((0.0f - (207.345108f / fConst0))); + fConst4 = (0.5f * (fConst3 + 1.0f)); + fConst5 = (0.0f - fConst4); + fConst6 = (0.00999999978f * fConst2); + fConst7 = (1.0f / fConst0); + fConst8 = (2764.60156f / fConst0); + + // --- Faust: instanceResetUserInterface() --- + fBypass = FAUSTFLOAT(0.0f); // inactive/active (0/1) + fDryWet = FAUSTFLOAT(50.0f); // 0...100 + fColor = FAUSTFLOAT(1.0f); // 0...1 + fFeedbackDepth = FAUSTFLOAT(75.0f); // 0...99 + fFeedbackBassCut = FAUSTFLOAT(500.0f); // 10...5000 + fLFOfrequency = FAUSTFLOAT(0.20000000000000001f); // 0.00999999978...5 + + // --- Faust: instanceClear() --- + for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { + fRec0[l0] = 0.0f; + } + for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { + fRec1[l1] = 0.0f; + } + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec7[l2] = 0.0f; + } + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fRec9[l3] = 0.0f; + } + for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { + fRec8[l4] = 0.0f; + } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fRec11[l5] = 0.0f; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fRec10[l6] = 0.0f; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec12[l7] = 0.0f; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec13[l8] = 0.0f; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fRec16[l10] = 0.0f; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fRec15[l11] = 0.0f; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fRec6[l12] = 0.0f; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec5[l13] = 0.0f; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fRec4[l14] = 0.0f; + } + for (int l15 = 0; (l15 < 2); l15 = (l15 + 1)) { + fRec3[l15] = 0.0f; + } + for (int l16 = 0; (l16 < 2); l16 = (l16 + 1)) { + fRec2[l16] = 0.0f; + } + for (int l17 = 0; (l17 < 2); l17 = (l17 + 1)) { + fRec17[l17] = 0.0f; + } +} + +float ctagPebble::Process(float in) // Mono +{ +float f_result = 0.f; + + // --- Faust: compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) --- + float fSlow0 = (fConst2 * float((1 - (float(fBypass) > 0.5f)))); + float fSlow1 = (0.0157079641f * float(fDryWet)); + float fSlow2 = (fConst2 * fastcos(fSlow1)); + int iSlow3 = int(float(fColor)); + float fSlow4 = (fConst6 * float(fFeedbackDepth)); + float fSlow5 = (fConst2 * float(fFeedbackBassCut)); + float fSlow6 = (fConst2 * (iSlow3 ? 39.4868202f : 62.3695068f)); + float fSlow7 = (fConst2 * (iSlow3 ? 96.8631363f : 114.232643f)); + float fSlow8 = (fConst2 * float(fLFOfrequency)); + float fSlow9 = (fConst2 * fastsin(fSlow1)); + + fRec0[0] = (fSlow0 + (fConst1 * fRec0[1])); + fBypassMeterSymbol = FAUSTFLOAT(fRec0[0]); + float fTemp0 = float(in); + fRec1[0] = (fSlow2 + (fConst1 * fRec1[1])); + fRec7[0] = (fTemp0 + (fConst3 * fRec7[1])); + fRec9[0] = (fSlow4 + (fConst1 * fRec9[1])); + fRec8[0] = ((fConst1 * fRec8[1]) + (fConst2 * (iSlow3 ? fRec9[0] : (0.100000001f * fRec9[0])))); + fRec11[0] = (fSlow5 + (fConst1 * fRec11[1])); + float fTemp1 = fasterexp((fConst7 * (0.0f - (6.28318548f * fRec11[0])))); + fRec10[0] = (fRec2[1] + (fRec10[1] * fTemp1)); + float fTemp2 = (fTemp1 + 1.0f); + fRec12[0] = ((fConst1 * fRec12[1]) + fSlow6); + fRec13[0] = ((fConst1 * fRec13[1]) + fSlow7); + fRec16[0] = (fSlow8 + (fConst1 * fRec16[1])); + float fTemp6 = (fRec15[1] + (fConst7 * fRec16[0])); + fRec15[0] = (fTemp6 - std::floor(fTemp6)); + float fTemp7 = (128.0f * fRec15[0]); + int iTemp8 = int(fTemp7); + float fTemp9 = ftbl0mydspSIG0[iTemp8]; + float fTemp10 = ((fConst8 * fasterpow2((0.0833333358f * ((fRec12[0] + ((fRec13[0] - fRec12[0]) * (fTemp9 + ((fTemp7 - float(iTemp8)) * (ftbl0mydspSIG0[((iTemp8 + 1) % 128)] - fTemp9))))) + -69.0f)))) + -1.0f); + fRec6[0] = (((fConst5 * fRec7[1]) + ((fRec8[0] * ((0.5f * (fRec10[0] * fTemp2)) + (fRec10[1] * (0.0f - (0.5f * fTemp2))))) + (fConst4 * fRec7[0]))) - (fRec6[1] * fTemp10)); + fRec5[0] = (fRec6[1] + (fTemp10 * (fRec6[0] - fRec5[1]))); + fRec4[0] = (fRec5[1] + (fTemp10 * (fRec5[0] - fRec4[1]))); + fRec3[0] = (fRec4[1] + (fTemp10 * (fRec4[0] - fRec3[1]))); + fRec2[0] = (fRec3[1] + (fRec3[0] * fTemp10)); + fRec17[0] = (fSlow9 + (fConst1 * fRec17[1])); + f_result = FAUSTFLOAT(((((fTemp0 * fRec1[0]) + (fRec2[0] * fRec17[0])) * fRec0[0]) + (fTemp0 * (1.0f - fRec0[0])))); + fRec0[1] = fRec0[0]; + fRec1[1] = fRec1[0]; + fRec7[1] = fRec7[0]; + fRec9[1] = fRec9[0]; + fRec8[1] = fRec8[0]; + fRec11[1] = fRec11[0]; + fRec10[1] = fRec10[0]; + fRec12[1] = fRec12[0]; + fRec13[1] = fRec13[0]; + fRec16[1] = fRec16[0]; + fRec15[1] = fRec15[0]; + fRec6[1] = fRec6[0]; + fRec5[1] = fRec5[0]; + fRec4[1] = fRec4[0]; + fRec3[1] = fRec3[0]; + fRec2[1] = fRec2[0]; + fRec17[1] = fRec17[0]; + + return(f_result); +} + diff --git a/components/ctagSoundProcessor/fx/ctagPebble.hpp b/components/ctagSoundProcessor/fx/ctagPebble.hpp new file mode 100644 index 00000000..20507ae0 --- /dev/null +++ b/components/ctagSoundProcessor/fx/ctagPebble.hpp @@ -0,0 +1,85 @@ +/*************** +CTAG TBD >>to be determined<< is an open source eurorack synthesizer module. + +A project conceived within the Creative Technologies Arbeitsgruppe of +Kiel University of Applied Sciences: https://www.creative-technologies.de + +(c) 2020,2021 by Robert Manzke. All rights reserved. + +The CTAG TBD software is licensed under the GNU General Public License +(GPL 3.0), available here: https://www.gnu.org/licenses/gpl-3.0.txt + +The CTAG TBD hardware design is released under the Creative Commons +Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0). +Details here: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +CTAG TBD is provided "as is" without any express or implied warranties. + +License and copyright details for specific submodules are included in their +respective component folders / files if different from this license. +***************/ +/* +Phaser effect, based on the Faust-Code for a "Electro Harmonix Small Stone" type of phaser by Jean Pierre Cimalando at https://github.com/jpcima/stone-phaser +Référence : + Kiiski, R., Esqueda, F., & Välimäki, V. (2016). + Time-variant gray-box modeling of a phaser pedal. + In 19th International Conference on Digital Audio Effects (DAFx-16). +Adapted by M. Brüssel from the C++ code derived from the "stone_phaser.dsp" there via https://faustide.grame.fr +*/ + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +class ctagPebble +{ + public: + ctagPebble() {Init();} + void Init(); + float Process(float in); // Mono + inline void SetSampleRate(float sampleRate) { fSampleRate = sampleRate; } + inline void SetDryWet(float dryWet) { fDryWet = dryWet; } + inline void SetColor(float color) { fColor = color; } + inline void SetBypass(bool bypass) { fBypass = (float)bypass; } + inline void SetFeedbackDepth(float feedbackDepth) { fFeedbackDepth = feedbackDepth; } + inline void SetFeedbackBassCut(float feedbackBassCut) { fFeedbackBassCut = feedbackBassCut; } + inline void SetLFOfrequency(float lfoFrequency) { fLFOfrequency = lfoFrequency; } + + private: + int iRec14[2]; // Faust class variable, used only during initialistation + float ftbl0mydspSIG0[128]; // Internal table + + int fSampleRate; + float fConst1; + float fConst2; + FAUSTFLOAT fBypass; // inactive/active (0/1) + float fRec0[2]; + FAUSTFLOAT fBypassMeterSymbol; // Unused for now + FAUSTFLOAT fDryWet; // 0...100 + float fRec1[2]; + float fConst3; + float fConst4; + float fConst5; + float fRec7[2]; + FAUSTFLOAT fColor; // 0...1 + float fConst6; + FAUSTFLOAT fFeedbackDepth; // 0...99 + float fRec9[2]; + float fRec8[2]; + float fConst7; + FAUSTFLOAT fFeedbackBassCut; // 10...5000 + float fRec11[2]; + float fRec10[2]; + float fConst8; + float fRec12[2]; + float fRec13[2]; + FAUSTFLOAT fLFOfrequency; // 0.00999999978...5 + float fRec16[2]; + float fRec15[2]; + float fRec6[2]; + float fRec5[2]; + float fRec4[2]; + float fRec3[2]; + float fRec2[2]; + float fRec17[2]; +}; diff --git a/components/ctagSoundProcessor/fx/ctagSPphaser.cpp b/components/ctagSoundProcessor/fx/ctagSPphaser.cpp new file mode 100644 index 00000000..d02b9ee4 --- /dev/null +++ b/components/ctagSoundProcessor/fx/ctagSPphaser.cpp @@ -0,0 +1,225 @@ +#include +#include +#include "helpers/ctagFastMath.hpp" +#include "fx/ctagSPphaser.hpp" + +#define phaser_max(a,b) ((a < b) ? b : a) +#define phaser_min(a,b) ((a < b) ? a : b) +#define phaser_faustpower2_f(value) (value * value) +#define phaser_faustpower3_f(value) (value * value * value) +#define phaser_faustpower4_f(value) (value * value * value * value) + +/*************** +CTAG TBD >>to be determined<< is an open source eurorack synthesizer module. + +A project conceived within the Creative Technologies Arbeitsgruppe of +Kiel University of Applied Sciences: https://www.creative-technologies.de + +(c) 2020,2021 by Robert Manzke. All rights reserved. + +The CTAG TBD software is licensed under the GNU General Public License +(GPL 3.0), available here: https://www.gnu.org/licenses/gpl-3.0.txt + +The CTAG TBD hardware design is released under the Creative Commons +Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0). +Details here: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +CTAG TBD is provided "as is" without any express or implied warranties. + +License and copyright details for specific submodules are included in their +respective component folders / files if different from this license. +***************/ +/* +Phaser effect, based on the soundpipe port by Paul Bachelor generated from Faust code taken from the Guitarix project, as to be found here: +https://github.com/PaulBatchelor/Soundpipe +https://paulbatchelor.github.io/res/soundpipe/docs/phaser.html +Adapted by M. Brüssel from the Soundpipe version. +*/ +using namespace CTAG::SP::HELPERS; + +void ctagSPphaser::Init() // For convenience Init() already will be called by the constructor +{ + fSamplingFreq_ = 44100.f; // Default is 44.1 kHz, use SetSampleRate() afterwards to change - + level_ = (float) 0.; + for (int i0 = 0; (i0 < 2); i0 = (i0 + 1)) + iVec0[i0] = 0; + + vibratoMode_ = (float) 0.; + depth_ = (float) 1.; + iConst0 = phaser_min(192000, phaser_max(1, fSamplingFreq_)); + fConst1 = (1.f / (float) iConst0); + notchWidth_ = (float) 1000.; + notchFreq_ = (float) 1.5; + minNotch1Freq_ = (float) 100.; + maxNotch1Freq_ = (float) 800.; + fConst2 = (0.10472f / (float) iConst0); + lfobpm_ = (float) 30.; + + for (int i1 = 0; (i1 < 2); i1 = (i1 + 1)) + fRec5[i1] = 0.f; + for (int i2 = 0; (i2 < 2); i2 = (i2 + 1)) + fRec6[i2] = 0.f; + feedbackGain_ = (float) 0.; + for (int i3 = 0; (i3 < 3); i3 = (i3 + 1)) + fRec4[i3] = 0.f; + for (int i4 = 0; (i4 < 3); i4 = (i4 + 1)) + fRec3[i4] = 0.f; + for (int i5 = 0; (i5 < 3); i5 = (i5 + 1)) + fRec2[i5] = 0.f; + for (int i6 = 0; (i6 < 3); i6 = (i6 + 1)) + fRec1[i6] = 0.f; + for (int i7 = 0; (i7 < 2); i7 = (i7 + 1)) + fRec0[i7] = 0.f; + for (int i8 = 0; (i8 < 3); i8 = (i8 + 1)) + fRec11[i8] = 0.f; + for (int i9 = 0; (i9 < 3); i9 = (i9 + 1)) + fRec10[i9] = 0.f; + for (int i10 = 0; (i10 < 3); i10 = (i10 + 1)) + fRec9[i10] = 0.f; + for (int i11 = 0; (i11 < 3); i11 = (i11 + 1)) + fRec8[i11] = 0.f; + for (int i12 = 0; (i12 < 2); i12 = (i12 + 1)) + fRec7[i12] = 0.f; +} + +void ctagSPphaser::Process(float* in_l, float* in_r ,float* out_l,float* out_r) // Stereo +{ + float* input0 = in_l; + float* input1 = in_r; + float* output0 = out_l; + float* output1 = out_r; + float fSlow0 = fasterpow10(0.05f * (float)level_); + float fSlow1 = (0.5f * ((int)(float) vibratoMode_?2.f:(float)depth_)); + float fSlow2 = (1.f - fSlow1); + float fSlow3 = fasterexp((fConst1 * (0.f - (3.14159f * (float)notchWidth_)))); + float fSlow4 = phaser_faustpower2_f(fSlow3); + float fSlow5 = (0.f - (2.f * fSlow3)); + float fSlow6 = (float)notchFreq_; + float fSlow7 = (fConst1 * fSlow6); + float fSlow8 = (float)minNotch1Freq_; + float fSlow9 = (6.28319f * fSlow8); + float fSlow10 = (0.5f * ((6.28319f * phaser_max(fSlow8, (float)maxNotch1Freq_)) - fSlow9)); + float fSlow11 = (fConst2 * (float)lfobpm_); + float fSlow12 = fastsin(fSlow11); + float fSlow13 = fastcos(fSlow11); + float fSlow14 = (0.f - fSlow12); + float fSlow15 = (float)feedbackGain_; + float fSlow16 = (fConst1 * phaser_faustpower2_f(fSlow6)); + float fSlow17 = (fConst1 * phaser_faustpower3_f(fSlow6)); + float fSlow18 = (fConst1 * phaser_faustpower4_f(fSlow6)); + float fSlow19 = ((int)(float)invert_?(0.f - fSlow1):fSlow1); + + iVec0[0] = 1; + float fTemp0 = (float)(*input0); + fRec5[0] = ((fSlow12 * fRec6[1]) + (fSlow13 * fRec5[1])); + fRec6[0] = ((1.f + ((fSlow13 * fRec6[1]) + (fSlow14 * fRec5[1]))) - (float)iVec0[1]); + float fTemp1 = ((fSlow10 * (1.f - fRec5[0])) + fSlow9); + float fTemp2 = (fRec4[1] * fastcos((fSlow7 * fTemp1))); + fRec4[0] = (0.f - (((fSlow5 * fTemp2) + (fSlow4 * fRec4[2])) - ((fSlow0 * fTemp0) + (fSlow15 * fRec0[1])))); + float fTemp3 = (fRec3[1] * fastcos((fSlow16 * fTemp1))); + fRec3[0] = ((fSlow5 * (fTemp2 - fTemp3)) + (fRec4[2] + (fSlow4 * (fRec4[0] - fRec3[2])))); + float fTemp4 = (fRec2[1] * fastcos((fSlow17 * fTemp1))); + fRec2[0] = ((fSlow5 * (fTemp3 - fTemp4)) + (fRec3[2] + (fSlow4 * (fRec3[0] - fRec2[2])))); + float fTemp5 = (fRec1[1] * fastcos((fSlow18 * fTemp1))); + fRec1[0] = ((fSlow5 * (fTemp4 - fTemp5)) + (fRec2[2] + (fSlow4 * (fRec2[0] - fRec1[2])))); + fRec0[0] = ((fSlow4 * fRec1[0]) + ((fSlow5 * fTemp5) + fRec1[2])); + *output0 = (float)((fSlow0 * (fSlow2 * fTemp0)) + (fRec0[0] * fSlow19)); + float fTemp6 = (float)(*input1); + float fTemp7 = ((fSlow10 * (1.f - fRec6[0])) + fSlow9); + float fTemp8 = (fRec11[1] * fastcos((fSlow7 * fTemp7))); + fRec11[0] = (0.f - (((fSlow5 * fTemp8) + (fSlow4 * fRec11[2])) - ((fSlow0 * fTemp6) + (fSlow15 * fRec7[1])))); + float fTemp9 = (fRec10[1] * fastcos((fSlow16 * fTemp7))); + fRec10[0] = ((fSlow5 * (fTemp8 - fTemp9)) + (fRec11[2] + (fSlow4 * (fRec11[0] - fRec10[2])))); + float fTemp10 = (fRec9[1] * fastcos((fSlow17 * fTemp7))); + fRec9[0] = ((fSlow5 * (fTemp9 - fTemp10)) + (fRec10[2] + (fSlow4 * (fRec10[0] - fRec9[2])))); + float fTemp11 = (fRec8[1] * fastcos((fSlow18 * fTemp7))); + fRec8[0] = ((fSlow5 * (fTemp10 - fTemp11)) + (fRec9[2] + (fSlow4 * (fRec9[0] - fRec8[2])))); + fRec7[0] = ((fSlow4 * fRec8[0]) + ((fSlow5 * fTemp11) + fRec8[2])); + *output1 = (float)((fSlow0 * (fSlow2 * fTemp6)) + (fRec7[0] * fSlow19)); + iVec0[1] = iVec0[0]; + fRec5[1] = fRec5[0]; + fRec6[1] = fRec6[0]; + fRec4[2] = fRec4[1]; + fRec4[1] = fRec4[0]; + fRec3[2] = fRec3[1]; + fRec3[1] = fRec3[0]; + fRec2[2] = fRec2[1]; + fRec2[1] = fRec2[0]; + fRec1[2] = fRec1[1]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + fRec11[2] = fRec11[1]; + fRec11[1] = fRec11[0]; + fRec10[2] = fRec10[1]; + fRec10[1] = fRec10[0]; + fRec9[2] = fRec9[1]; + fRec9[1] = fRec9[0]; + fRec8[2] = fRec8[1]; + fRec8[1] = fRec8[0]; + fRec7[1] = fRec7[0]; +} + +float ctagSPphaser::Process(float in) // Mono +{ +float result = 0.f; + + float fSlow0 = fasterpow10(0.05f * (float)level_); + float fSlow1 = (0.5f * ((int)(float) vibratoMode_?2.f:(float)depth_)); + float fSlow2 = (1.f - fSlow1); + float fSlow3 = fasterexp((fConst1 * (0.f - (3.14159f * (float)notchWidth_)))); + float fSlow4 = phaser_faustpower2_f(fSlow3); + float fSlow5 = (0.f - (2.f * fSlow3)); + float fSlow6 = (float)notchFreq_; + float fSlow7 = (fConst1 * fSlow6); + float fSlow8 = (float)minNotch1Freq_; + float fSlow9 = (6.28319f * fSlow8); + float fSlow10 = (0.5f * ((6.28319f * phaser_max(fSlow8, (float)maxNotch1Freq_)) - fSlow9)); + float fSlow11 = (fConst2 * (float)lfobpm_); + float fSlow12 = fastsin(fSlow11); + float fSlow13 = fastcos(fSlow11); + float fSlow14 = (0.f - fSlow12); + float fSlow15 = (float)feedbackGain_; + float fSlow16 = (fConst1 * phaser_faustpower2_f(fSlow6)); + float fSlow17 = (fConst1 * phaser_faustpower3_f(fSlow6)); + float fSlow18 = (fConst1 * phaser_faustpower4_f(fSlow6)); + float fSlow19 = ((int)(float)invert_?(0.f - fSlow1):fSlow1); + + iVec0[0] = 1; + float fTemp0 = in; + fRec5[0] = ((fSlow12 * fRec6[1]) + (fSlow13 * fRec5[1])); + fRec6[0] = ((1.f + ((fSlow13 * fRec6[1]) + (fSlow14 * fRec5[1]))) - (float)iVec0[1]); + float fTemp1 = ((fSlow10 * (1.f - fRec5[0])) + fSlow9); + float fTemp2 = (fRec4[1] * fastcos((fSlow7 * fTemp1))); + fRec4[0] = (0.f - (((fSlow5 * fTemp2) + (fSlow4 * fRec4[2])) - ((fSlow0 * fTemp0) + (fSlow15 * fRec0[1])))); + float fTemp3 = (fRec3[1] * fastcos((fSlow16 * fTemp1))); + fRec3[0] = ((fSlow5 * (fTemp2 - fTemp3)) + (fRec4[2] + (fSlow4 * (fRec4[0] - fRec3[2])))); + float fTemp4 = (fRec2[1] * fastcos((fSlow17 * fTemp1))); + fRec2[0] = ((fSlow5 * (fTemp3 - fTemp4)) + (fRec3[2] + (fSlow4 * (fRec3[0] - fRec2[2])))); + float fTemp5 = (fRec1[1] * fastcos((fSlow18 * fTemp1))); + fRec1[0] = ((fSlow5 * (fTemp4 - fTemp5)) + (fRec2[2] + (fSlow4 * (fRec2[0] - fRec1[2])))); + fRec0[0] = ((fSlow4 * fRec1[0]) + ((fSlow5 * fTemp5) + fRec1[2])); + result = (float)((fSlow0 * (fSlow2 * fTemp0)) + (fRec0[0] * fSlow19)); + iVec0[1] = iVec0[0]; + fRec5[1] = fRec5[0]; + fRec6[1] = fRec6[0]; + fRec4[2] = fRec4[1]; + fRec4[1] = fRec4[0]; + fRec3[2] = fRec3[1]; + fRec3[1] = fRec3[0]; + fRec2[2] = fRec2[1]; + fRec2[1] = fRec2[0]; + fRec1[2] = fRec1[1]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + fRec11[2] = fRec11[1]; + fRec11[1] = fRec11[0]; + fRec10[2] = fRec10[1]; + fRec10[1] = fRec10[0]; + fRec9[2] = fRec9[1]; + fRec9[1] = fRec9[0]; + fRec8[2] = fRec8[1]; + fRec8[1] = fRec8[0]; + fRec7[1] = fRec7[0]; + return(result); +} + diff --git a/components/ctagSoundProcessor/fx/ctagSPphaser.hpp b/components/ctagSoundProcessor/fx/ctagSPphaser.hpp new file mode 100644 index 00000000..2027bb21 --- /dev/null +++ b/components/ctagSoundProcessor/fx/ctagSPphaser.hpp @@ -0,0 +1,77 @@ +/*************** +CTAG TBD >>to be determined<< is an open source eurorack synthesizer module. + +A project conceived within the Creative Technologies Arbeitsgruppe of +Kiel University of Applied Sciences: https://www.creative-technologies.de + +(c) 2020,2021 by Robert Manzke. All rights reserved. + +The CTAG TBD software is licensed under the GNU General Public License +(GPL 3.0), available here: https://www.gnu.org/licenses/gpl-3.0.txt + +The CTAG TBD hardware design is released under the Creative Commons +Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0). +Details here: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +CTAG TBD is provided "as is" without any express or implied warranties. + +License and copyright details for specific submodules are included in their +respective component folders / files if different from this license. +***************/ +/* +Phaser effect, based on the soundpipe port by Paul Bachelor generated from Faust code taken from the Guitarix project, as to be found here: +https://github.com/PaulBatchelor/Soundpipe +https://paulbatchelor.github.io/res/soundpipe/docs/phaser.html +Adapted by M. Brüssel from the Soundpipe version. +*/ + +class ctagSPphaser +{ + public: + ctagSPphaser() {Init();} + void Init(); + float Process(float in); // Mono + void Process(float* in_l, float* in_r ,float* out_l, float* out_r); // Stereo + + inline void SetSampleRate(float sr) { fSamplingFreq_ = sr; } + inline void SetMaxNotch1Freq(float maxNotch1Freq) { maxNotch1Freq_ = maxNotch1Freq; } + inline void SetMinNotch1Freq(float minNotch1Freq) { minNotch1Freq_ = minNotch1Freq; } + inline void SetNotchWidth(float notchWidth) { notchWidth_ = notchWidth; } + inline void SetNotchFreq(float notchFreq) { notchFreq_ = notchFreq; } + inline void SetVibratoMode(bool activate) { vibratoMode_ = (float)activate; } + inline void SetDepth(float depth) { depth_ = depth; } + inline void SetFeedbackGain(float feedbackGain) { feedbackGain_ = feedbackGain; } + inline void SetInvert(bool activate) { invert_ = (float)activate; } + inline void SetLevel(float level) { level_ = level; } + inline void SetLfoBpm(float lfobpm) { lfobpm_ = lfobpm; } + + private: + float fRec4[3]; + float fRec3[3]; + float fRec2[3]; + float fRec1[3]; + float fRec11[3]; + float fRec10[3]; + float fRec9[3]; + float fRec8[3]; + int iVec0[2]; + float fRec5[2]; + float fRec6[2]; + float fRec0[2]; + float fRec7[2]; + + float level_; + float vibratoMode_; + float depth_; + int fSamplingFreq_; + int iConst0; + float fConst1; + float notchWidth_; + float notchFreq_; + float minNotch1Freq_; + float maxNotch1Freq_; + float fConst2; + float lfobpm_; + float feedbackGain_; + float invert_; +}; diff --git a/spiffs_image/data/sp/mp-MSxxNoise.jsn b/spiffs_image/data/sp/mp-MSxxNoise.jsn new file mode 100644 index 00000000..ec4d06e4 --- /dev/null +++ b/spiffs_image/data/sp/mp-MSxxNoise.jsn @@ -0,0 +1 @@ +{"activePatch":1,"patches":[{"name":"WindyTimes","params":[{"id":"ExternalActive","current":0,"trig":-1},{"id":"NoiseExternalBalance","current":0,"cv":-1},{"id":"PinkActive","current":1,"trig":-1},{"id":"WhiteActive","current":1,"trig":-1},{"id":"PinkWhiteBalance","current":746,"cv":-1},{"id":"Volume","current":1335,"cv":-1},{"id":"FilterBypass","current":0,"trig":-1},{"id":"SaturationActive","current":1,"trig":-1},{"id":"Saturation","current":244,"cv":-1},{"id":"Cutoff","current":411,"cv":-1},{"id":"Resonance","current":2047,"cv":-1},{"id":"MGactive","current":1,"trig":-1},{"id":"MGisSquare","current":0,"trig":-1},{"id":"MGspeed","current":53,"cv":-1},{"id":"MGamnt","current":261,"cv":-1},{"id":"PhaserBypass","current":0,"trig":-1},{"id":"PhaserDryWet","current":2824,"cv":-1},{"id":"PhaserPreset","current":1,"trig":-1},{"id":"PhaserColor","current":2047,"cv":-1},{"id":"PhaserLFOfrequency","current":2047,"cv":-1},{"id":"PhaserFeedbackDepth","current":2047,"cv":-1},{"id":"PhaserFeedbackBassCut","current":2047,"cv":-1}]},{"name":"Copter","params":[{"id":"ExternalActive","current":0,"trig":-1},{"id":"NoiseExternalBalance","current":2047,"cv":-1},{"id":"PinkActive","current":1,"trig":-1},{"id":"WhiteActive","current":1,"trig":-1},{"id":"PinkWhiteBalance","current":2553,"cv":-1},{"id":"Volume","current":2047,"cv":-1},{"id":"FilterBypass","current":0,"trig":-1},{"id":"SaturationActive","current":1,"trig":-1},{"id":"Saturation","current":994,"cv":-1},{"id":"Cutoff","current":0,"cv":-1},{"id":"Resonance","current":936,"cv":-1},{"id":"MGactive","current":1,"trig":-1},{"id":"MGisSquare","current":1,"trig":-1},{"id":"MGspeed","current":1534,"cv":-1},{"id":"MGamnt","current":70,"cv":-1},{"id":"PhaserBypass","current":0,"trig":-1},{"id":"PhaserDryWet","current":2663,"cv":-1},{"id":"PhaserPreset","current":0,"trig":-1},{"id":"PhaserColor","current":3985,"cv":-1},{"id":"PhaserLFOfrequency","current":307,"cv":-1},{"id":"PhaserFeedbackDepth","current":3714,"cv":-1},{"id":"PhaserFeedbackBassCut","current":885,"cv":-1}]}]} \ No newline at end of file diff --git a/spiffs_image/data/sp/mui-MSxxNoise.jsn b/spiffs_image/data/sp/mui-MSxxNoise.jsn new file mode 100644 index 00000000..dd1e77a2 --- /dev/null +++ b/spiffs_image/data/sp/mui-MSxxNoise.jsn @@ -0,0 +1,42 @@ +{ + "id": "MSxxNoise", "isStereo": false, "name": "MSxxNoise", "hint": "White/Pink Noise+external in+Filter+LFO+Reverb", "params": + [ + { + "id": "Soundsources", "name": "Soundsources", "type": "group", "params": + [ + {"id":"ExternalActive","name":"External Input n/y","type":"bool"}, + {"id":"NoiseExternalBalance","name":"Noise <=> External Input","type":"int","min":0,"max":4095,"step":1}, + {"id":"PinkActive","name":"Pink Noise n/y","type":"bool"}, + {"id":"WhiteActive","name":"White Noise n/y","type":"bool"}, + {"id":"PinkWhiteBalance","name":"PinkNoise <=> WhiteNoise","type":"int","min":0,"max":4095,"step":1}, + {"id":"Volume","name":"Volume","type":"int","min":0,"max":4095,"step":1} + ] + }, + { + "id": "Soundmodifiers", "name": "Soundmodifiers", "type": "group", "params": + [ + {"id":"FilterBypass","name":"Filter Bypass n/y","type":"bool"}, + {"id":"SaturationActive","name":"Enable Filter Drive n/y","type":"bool"}, + {"id":"Saturation","name":"Filter Drive","type":"int","min":0,"max":4095,"step":1}, + {"id":"Cutoff","name":"Cutoff","type":"int","min":0,"max":4095,"step":1}, + {"id":"Resonance","name":"Resonance","type":"int","min":0,"max":4095,"step":1}, + {"id":"MGactive","name":"MG active n/y","type":"bool"}, + {"id":"MGisSquare","name":"MG Sinus <=> Square","type":"bool"}, + {"id":"MGspeed","name":"MG Speed","type":"int","min":0,"max":4095,"step":1}, + {"id":"MGamnt","name":"MG Amount","type":"int","min":0,"max":4095,"step":1}, + {"id":"PhaserBypass","name":"Phaser Bypass n/y","type":"bool"}, + {"id":"PhaserDryWet","name":"Phaser Dry <=> Wet","type":"int","min":0,"max":4095,"step":1} + ] + }, + { + "id": "Phaser", "name": "Phaser", "type": "group", "params": + [ + {"id":"PhaserPreset","name":"Disable Phaser Parameters and set to Preset n/y","type":"bool"}, + {"id":"PhaserColor","name":"Color","type":"int","min":0,"max":4095,"step":1}, + {"id":"PhaserLFOfrequency","name":"LFO Frequency","type":"int","min":0,"max":4095,"step":1}, + {"id":"PhaserFeedbackDepth","name":"Feedback Depth","type":"int","min":0,"max":4095,"step":1}, + {"id":"PhaserFeedbackBassCut","name":"Feedback Bass Cut","type":"int","min":0,"max":4095,"step":1} + ] + } + ] +} \ No newline at end of file