#include "mscHack.hpp" //#include "mscHack_Controls.hpp" #include "dsp/digital.hpp" //#include "CLog.h" namespace rack_plugin_mscHack { #define nDELAYS 8 #define nSTEPS 4 #define nBUFFLEN 0x80000 #define MAX_nWAVES 7 typedef struct { // track in clk bpm //SLIDING_AVG_STRUCT Avg; int tickcount; float ftickspersec; float fbpm; // track sync tick float fsynclen; float fsynccount; bool bClockReset; }MAIN_SYNC_CLOCK; typedef struct { float lp1[ 2 ] = {}, bp1[ 2 ] = {}; }FILTER_PARAM_STRUCT; //----------------------------------------------------- // Module Definition // //----------------------------------------------------- struct StepDelay : Module { enum ParamIds { PARAM_FILTER_MODE, PARAM_FILTERF, PARAM_FILTERQ, PARAM_LEVEL, PARAM_PAN = (PARAM_LEVEL + nSTEPS ), PARAM_FB = (PARAM_PAN + nSTEPS ), PARAM_DELAY = (PARAM_FB + nSTEPS ), PARAM_MIX = (PARAM_DELAY + nSTEPS ), nPARAMS }; enum InputIds { IN_CLOCK, IN_CLK_RESET, IN_AUDIOL, IN_AUDIOR, IN_FILTER_MOD, nINPUTS }; enum OutputIds { OUT_AUDIOL, OUT_AUDIOR, nOUTPUTS }; enum FILTER_TYPES { FILTER_OFF, FILTER_LP, FILTER_HP, FILTER_BP, FILTER_NT }; CLog lg; bool m_bInitialized = false; // Contructor StepDelay() : Module(nPARAMS, nINPUTS, nOUTPUTS, 0){} // clock SchmittTrigger m_SchTrigClk; MAIN_SYNC_CLOCK m_Clock; // global triggers SchmittTrigger m_SchTrigGlobalClkReset; bool m_GlobalClkResetPending = false; Label *m_pTextLabel[ nSTEPS ] = {}; MyLEDButtonStrip *m_pButtonLenMod[ nSTEPS ] = {}; int m_lenmod[ nSTEPS ] = {0}; FILTER_PARAM_STRUCT m_Filter = {}; float m_fCutoff; #define L 0 #define R 1 // buffers float m_fBuffer[ 2 ][ nBUFFLEN ] = {}; int m_BufferInOffset = 0; int m_BufferOutOffset[ nSTEPS ] = {0}; //----------------------------------------------------- // MyDelayKnob //----------------------------------------------------- struct MyDelayKnob : Knob_Yellow3_20_Snap { StepDelay *mymodule; void onChange( EventChange &e ) override { mymodule = (StepDelay*)module; if( mymodule ) { mymodule->CalcDelays(); } RoundKnob::onChange( e ); } }; // Overrides void step() override; void JsonParams( bool bTo, json_t *root); json_t* toJson() override; void fromJson(json_t *rootJ) override; void onRandomize() override; void onReset() override; void ChangeFilterCutoff( float cutfreq ); void Filter( float *InL, float *InR ); void CalcDelays( void ); void BuildWaves( void ); float GetWave( int type, float phase ); }; //----------------------------------------------------- // Procedure: LenModCallback //----------------------------------------------------- void LenModCallback( void *pClass, int id, int nbutton, bool bOn ) { StepDelay *mymodule; mymodule = (StepDelay*)pClass; if( mymodule ) { mymodule->m_lenmod[ id ] = nbutton; mymodule->CalcDelays(); } } //----------------------------------------------------- // Procedure: Widget // //----------------------------------------------------- struct StepDelay_Widget : ModuleWidget { StepDelay_Widget( StepDelay *module ); }; StepDelay_Widget::StepDelay_Widget( StepDelay *module ) : ModuleWidget(module) { int steps, x, y; box.size = Vec( 15*14, 380); { SVGPanel *panel = new SVGPanel(); panel->box.size = box.size; panel->setBackground(SVG::load(assetPlugin(plugin, "res/StepDelay.svg"))); addChild(panel); } //module->lg.Open("StepDelay.txt"); // Clock Inputs addInput(Port::create( Vec( 14, 23 ), Port::INPUT, module, StepDelay::IN_CLOCK ) ); // Audio Inputs addInput(Port::create( Vec( 14, 70 ), Port::INPUT, module, StepDelay::IN_AUDIOL ) ); addInput(Port::create( Vec( 40, 70 ), Port::INPUT, module, StepDelay::IN_AUDIOR ) ); // Audio Outputs addOutput(Port::create( Vec( 14, 109 ), Port::OUTPUT, module, StepDelay::OUT_AUDIOL ) ); addOutput(Port::create( Vec( 40, 109 ), Port::OUTPUT, module, StepDelay::OUT_AUDIOR ) ); // Mix addParam(ParamWidget::create( Vec( 11, 149 ), module, StepDelay::PARAM_MIX, 0.0, 1.0, 0.5 ) ); // Filter knobs addParam(ParamWidget::create( Vec( 93, 58 ), module, StepDelay::PARAM_FILTERF, 0.0, 1.0, 0.0 ) ); addParam(ParamWidget::create( Vec( 144, 77 ), module, StepDelay::PARAM_FILTERQ, 0.0, 1.0, 0.0 ) ); addParam(ParamWidget::create( Vec( 139, 57 ), module, StepDelay::PARAM_FILTER_MODE, 0.0, 4.0, 0.0 ) ); addInput(Port::create( Vec( 104, 103 ), Port::INPUT, module, StepDelay::IN_FILTER_MOD ) ); x = 93; y = 173; for( steps = 0; steps < nSTEPS; steps++ ) { y = 173; // step knobs addParam(ParamWidget::create( Vec( x , y ), module, StepDelay::PARAM_LEVEL + steps, 0.0, 1.0, 0.5 ) ); y += 28; addParam(ParamWidget::create( Vec( x , y ), module, StepDelay::PARAM_PAN + steps, -1.0, 1.0, 0.0 ) ); y += 28; addParam(ParamWidget::create( Vec( x , y ), module, StepDelay::PARAM_FB + steps, 0.0, 1.0, 0.0 ) ); y += 28; addParam(ParamWidget::create( Vec( x , y ), module, StepDelay::PARAM_DELAY + steps, 0.0, (float)(nDELAYS - 1), 0.0 ) ); module->m_pTextLabel[ steps ] = new Label(); module->m_pTextLabel[ steps ]->box.pos = Vec( x - 9, y + 18 ); module->m_pTextLabel[ steps ]->text = "Delay"; //module->m_pTextLabel[ steps ]-> addChild( module->m_pTextLabel[ steps ] ); y += 38; module->m_pButtonLenMod[ steps ] = new MyLEDButtonStrip( x + 4, y, 12, 12, 2, 10.0, 2, true, DWRGB( 180, 180, 180 ), DWRGB( 255, 255, 0 ), MyLEDButtonStrip::TYPE_EXCLUSIVE_WOFF, steps, module, LenModCallback ); addChild( module->m_pButtonLenMod[ steps ] ); x += 28; } addChild(Widget::create(Vec(15, 0))); addChild(Widget::create(Vec(box.size.x-30, 0))); addChild(Widget::create(Vec(15, 365))); addChild(Widget::create(Vec(box.size.x-30, 365))); module->m_Clock.fsynclen = 2.0 * 48.0; // default to 120bpm module->CalcDelays(); module->m_bInitialized = true; } //----------------------------------------------------- // Procedure: JsonParams // //----------------------------------------------------- void StepDelay::JsonParams( bool bTo, json_t *root) { JsonDataInt( bTo, "m_lenmod", root, (int*)m_lenmod, nSTEPS ); } //----------------------------------------------------- // Procedure: toJson // //----------------------------------------------------- json_t *StepDelay::toJson() { json_t *root = json_object(); if( !root ) return NULL; JsonParams( TOJSON, root ); return root; } //----------------------------------------------------- // Procedure: fromJson // //----------------------------------------------------- void StepDelay::fromJson( json_t *root ) { JsonParams( FROMJSON, root ); for( int i = 0; i < nSTEPS; i++ ) { m_pButtonLenMod[ i ]->Set( m_lenmod[ i ], true ); } CalcDelays(); } //----------------------------------------------------- // Procedure: reset // //----------------------------------------------------- void StepDelay::onReset() { } //----------------------------------------------------- // Procedure: randomize // //----------------------------------------------------- void StepDelay::onRandomize() { } //----------------------------------------------------- // Procedure: ChangeFilterCutoff // //----------------------------------------------------- void StepDelay::ChangeFilterCutoff( float cutfreq ) { float fx, fx2, fx3, fx5, fx7; // clamp at 1.0 and 20/samplerate cutfreq = fmax(cutfreq, 20 / engineGetSampleRate()); cutfreq = fmin(cutfreq, 1.0); // calculate eq rez freq fx = 3.141592 * (cutfreq * 0.026315789473684210526315789473684) * 2 * 3.141592; fx2 = fx*fx; fx3 = fx2*fx; fx5 = fx3*fx2; fx7 = fx5*fx2; m_fCutoff = 2.0 * (fx - (fx3 * 0.16666666666666666666666666666667) + (fx5 * 0.0083333333333333333333333333333333) - (fx7 * 0.0001984126984126984126984126984127)); } //----------------------------------------------------- // Procedure: Filter // //----------------------------------------------------- #define MULTI (0.33333333333333333333333333333333f) void StepDelay::Filter( float *InL, float *InR ) { FILTER_PARAM_STRUCT *p; float rez, hp1; float input[ 2 ], out[ 2 ] = {0}, lowpass, bandpass, highpass; if( (int)params[ PARAM_FILTER_MODE ].value == 0 ) return; ChangeFilterCutoff( clamp( params[ PARAM_FILTERF ].value + clamp( inputs[ IN_FILTER_MOD ].normalize( 0.0f ) / CV_MAX, -1.0f, 1.0f ), -1.0f, 1.0f ) ); p = &m_Filter; rez = 1.0 - params[ PARAM_FILTERQ ].value; input[ 0 ] = clamp( *InL / AUDIO_MAX, -1.0f, 1.0f ); input[ 1 ] = clamp( *InR / AUDIO_MAX, -1.0f, 1.0f ); // do left and right channels for( int i = 0; i < 2; i++ ) { input[ i ] = input[ i ] + 0.000000001; p->lp1[ i ] = p->lp1[ i ] + m_fCutoff * p->bp1[ i ]; hp1 = input[ i ] - p->lp1[ i ] - rez * p->bp1[ i ]; p->bp1[ i ] = m_fCutoff * hp1 + p->bp1[ i ]; lowpass = p->lp1[ i ]; highpass = hp1; bandpass = p->bp1[ i ]; p->lp1[ i ] = p->lp1[ i ] + m_fCutoff * p->bp1[ i ]; hp1 = input[ i ] - p->lp1[ i ] - rez * p->bp1[ i ]; p->bp1[ i ] = m_fCutoff * hp1 + p->bp1[ i ]; lowpass = lowpass + p->lp1[ i ]; highpass = highpass + hp1; bandpass = bandpass + p->bp1[ i ]; input[ i ] = input[ i ] - 0.000000001; p->lp1[ i ] = p->lp1[ i ] + m_fCutoff * p->bp1[ i ]; hp1 = input[ i ] - p->lp1[ i ] - rez * p->bp1[ i ]; p->bp1[ i ] = m_fCutoff * hp1 + p->bp1[ i ]; lowpass = (lowpass + p->lp1[ i ]) * MULTI; highpass = (highpass + hp1) * MULTI; bandpass = (bandpass + p->bp1[ i ]) * MULTI; switch( (int)params[ PARAM_FILTER_MODE ].value ) { case FILTER_LP: out[ i ] = lowpass; break; case FILTER_HP: out[ i ] = highpass; break; case FILTER_BP: out[ i ] = bandpass; break; case FILTER_NT: out[ i ] = lowpass + highpass; break; default: break; } } *InL = out[ 0 ] * AUDIO_MAX; *InR = out[ 1 ] * AUDIO_MAX; } //----------------------------------------------------- // Procedure: CalcDelays // //----------------------------------------------------- #define BASE_TICK 48 const float fdelaylen[ nDELAYS ] = { 0.0f, 8.0f, 4.0f, 2.0f, 1.0f, 0.5f, 0.25f, 0.125f }; const float delaymod[ 3 ] = { 1.0f, 1.5f, 0.3333f }; const char strDelay[ nDELAYS ][ 8 ] = { {" Off"}, {"2Bar"}, {"Bar"}, {" Half"}, {" 4th"}, {" 8th"}, {" 16th"}, {" 32nd"} }; void StepDelay::CalcDelays( void ) { int i, delay, delaysum = 0; for( i = 0; i < nSTEPS; i++ ) { delay = (int)params[ PARAM_DELAY + i ].value; if( m_pTextLabel[ i ] ) m_pTextLabel[ i ]->text = strDelay[ delay ]; if( delay ) { delaysum += (int)( m_Clock.fsynclen * ( fdelaylen[ delay ] * delaymod[ m_lenmod[ i ] ] ) ); m_BufferOutOffset[ i ] = ( m_BufferInOffset - delaysum ) & 0x7FFFF; } } } //----------------------------------------------------- // Procedure: step // //----------------------------------------------------- void StepDelay::step() { int i; bool bMono = true; float delayL, delayR, fFbL = 0.0f, fFbR = 0.0f, inLOrig = 0.0f, inROrig = 0.0f, inL = 0.0f, inR = 0.0f, outL = 0.0f, outR = 0.0f, inPan; if( !m_bInitialized ) return; // get audio inputs, Mono if L only if( inputs[ IN_AUDIOR ].active ) { // right channel connected, this is not mono bMono = false; inR = inputs[ IN_AUDIOR ].value; } if( inputs[ IN_AUDIOL ].active ) { inL = inputs[ IN_AUDIOL ].value; // mono, make R channel equal left if( bMono ) inR = inL; } inLOrig = inL; inROrig = inR; Filter( &inL, &inR ); m_Clock.tickcount++; // track clock period if( m_SchTrigClk.process( inputs[ IN_CLOCK ].normalize( 0.0f ) ) ) { //m_Clock.fsynclen = (float)( engineGetSampleRate() / (float)m_Clock.tickcount ) * 48.0; m_Clock.fsynclen = m_Clock.tickcount; CalcDelays(); m_Clock.tickcount = 0; } // add delays for( i = 0; i < nSTEPS; i++ ) { // add each activated delay if( params[ PARAM_DELAY + i ].value != 0 ) { // add delay delayL = m_fBuffer[ L ][ m_BufferOutOffset[ i ] ] * params[ PARAM_LEVEL + i ].value; delayR = m_fBuffer[ R ][ m_BufferOutOffset[ i ] ] * params[ PARAM_LEVEL + i ].value; m_BufferOutOffset[ i ] = ( m_BufferOutOffset[ i ] + 1 ) & 0x7FFFF; inPan = params[ PARAM_PAN + i ].value; if( inPan <= 0.0 ) delayR *= ( 1.0 + inPan ); else delayL *= ( 1.0 - inPan ); outL += delayL; outR += delayR; fFbL += delayL * params[ PARAM_FB + i ].value; fFbR += delayR * params[ PARAM_FB + i ].value; } } // main delay buffer m_fBuffer[ L ][ m_BufferInOffset ] = inL + fFbL; m_fBuffer[ R ][ m_BufferInOffset ] = inR + fFbR; m_BufferInOffset = ( m_BufferInOffset + 1 ) & 0x7FFFF; outputs[ OUT_AUDIOL ].value = ( outL * params[ PARAM_MIX ].value ) + ( inLOrig * ( 1.0f - params[ PARAM_MIX ].value ) ); outputs[ OUT_AUDIOR ].value = ( outR * params[ PARAM_MIX ].value ) + ( inROrig * ( 1.0f - params[ PARAM_MIX ].value ) ); } } // namespace rack_plugin_mscHack using namespace rack_plugin_mscHack; RACK_PLUGIN_MODEL_INIT(mscHack, StepDelay) { Model *modelStepDelay = Model::create( "mscHack", "StepDelay", "DELAY 4 Step Delay", DELAY_TAG, PANNING_TAG ); return modelStepDelay; }