#include "mscHack.hpp" //#include "mscHack_Controls.hpp" #include "dsp/digital.hpp" //#include "CLog.h" namespace rack_plugin_mscHack { #define nCHANNELS 6 #define nSTEPS 32 #define nPROG 16 typedef struct { bool bPending; int prog; }PHRASE_CHANGE_STRUCT; //----------------------------------------------------- // Module Definition // //----------------------------------------------------- struct SEQ_6x32x16 : Module { #define NO_COPY -1 enum ParamIds { PARAM_CPY_NEXT, PARAM_RAND = PARAM_CPY_NEXT + nCHANNELS, PARAM_PAUSE = PARAM_RAND + nCHANNELS, PARAM_BILEVEL = PARAM_PAUSE + nCHANNELS, PARAM_LVL1_KNOB = PARAM_BILEVEL + nCHANNELS, PARAM_LVL2_KNOB = PARAM_LVL1_KNOB + nCHANNELS, PARAM_LVL3_KNOB = PARAM_LVL2_KNOB + nCHANNELS, PARAM_LVL4_KNOB = PARAM_LVL3_KNOB + nCHANNELS, PARAM_LVL5_KNOB = PARAM_LVL4_KNOB + nCHANNELS, nPARAMS = PARAM_LVL5_KNOB + nCHANNELS }; enum InputIds { IN_GLOBAL_CLK_RESET, IN_GLOBAL_PAT_CHANGE, IN_CLK, IN_PAT_TRIG = IN_CLK + nCHANNELS, IN_GLOBAL_TRIG_MUTE = IN_PAT_TRIG + nCHANNELS, nINPUTS }; enum OutputIds { OUT_TRIG, OUT_LEVEL = OUT_TRIG + nCHANNELS, OUT_BEAT1 = OUT_LEVEL + nCHANNELS, nOUTPUTS = OUT_BEAT1 + nCHANNELS }; bool m_bInitialized = false; CLog lg; bool m_bPauseState[ nCHANNELS ] = {}; bool m_bBiLevelState[ nCHANNELS ] = {}; SinglePatternClocked32 *m_pPatternDisplay[ nCHANNELS ] = {}; int m_Pattern[ nCHANNELS ][ nPROG ][ nSTEPS ]; int m_MaxPat[ nCHANNELS ][ nPROG ] = {}; PatternSelectStrip *m_pProgramDisplay[ nCHANNELS ] = {}; int m_CurrentProg[ nCHANNELS ] = {}; int m_MaxProg[ nCHANNELS ] = {}; PHRASE_CHANGE_STRUCT m_ProgPending[ nCHANNELS ] = {}; SchmittTrigger m_SchTrigClock[ nCHANNELS ]; SchmittTrigger m_SchTrigProg[ nCHANNELS ]; SchmittTrigger m_SchTrigGlobalClkReset; SchmittTrigger m_SchTrigGlobalProg; bool m_bTrig[ nCHANNELS ] = {}; PulseGenerator m_gatePulse[ nCHANNELS ]; PulseGenerator m_gateBeatPulse[ nCHANNELS ]; // swing //int m_SwingLen[ nCHANNELS ] = {0}; //int m_SwingCount[ nCHANNELS ] = {0}; int m_ClockTick[ nCHANNELS ] = {0}; int m_CopySrc = NO_COPY; // trig mute bool m_bTrigMute = false; MyLEDButton *m_pButtonTrigMute = NULL; // buttons MyLEDButton *m_pButtonPause[ nCHANNELS ] = {}; MyLEDButton *m_pButtonCopy[ nCHANNELS ] = {}; MyLEDButton *m_pButtonBiLevel[ nCHANNELS ] = {}; MyLEDButton *m_pButtonRand[ nCHANNELS ] = {}; MyLEDButton *m_pButtonAutoPat[ nCHANNELS ] = {}; MyLEDButton *m_pButtonHoldCV[ nCHANNELS ] = {}; MyLEDButton *m_pButtonClear[ nCHANNELS ] = {}; bool m_bAutoPatChange[ nCHANNELS ] = {}; bool m_bHoldCVState[ nCHANNELS ] = {}; float m_fCVRanges[ 3 ] = { 15.0f, 10.0f, 5.0f}; int m_RangeSelect = 0; char m_strRange[ 10 ] = {0}; float flast[ nCHANNELS ]; // Contructor SEQ_6x32x16() : Module(nPARAMS, nINPUTS, nOUTPUTS, 0){} // Overrides void step() override; json_t* toJson() override; void fromJson(json_t *rootJ) override; void onRandomize() override; void onReset() override; void JsonParams( bool bTo, json_t *root); void CpyNext( int ch ); void Rand( int ch ); void ChangeProg( int ch, int prog, bool force ); void SetPendingProg( int ch, int prog ); void Copy( int kb, bool bOn ); }; //----------------------------------------------------- // MyLEDButton_TrigMute //----------------------------------------------------- void MyLEDButton_TrigMute( void *pClass, int id, bool bOn ) { SEQ_6x32x16 *mymodule; mymodule = (SEQ_6x32x16*)pClass; mymodule->m_bTrigMute = bOn; } //----------------------------------------------------- // MyLEDButton_AutoPat //----------------------------------------------------- void MyLEDButton_AutoPat( void *pClass, int id, bool bOn ) { SEQ_6x32x16 *mymodule; mymodule = (SEQ_6x32x16*)pClass; mymodule->m_bAutoPatChange[ id ] = bOn; } //----------------------------------------------------- // MyLEDButton_Pause //----------------------------------------------------- void MyLEDButton_Pause( void *pClass, int id, bool bOn ) { SEQ_6x32x16 *mymodule; mymodule = (SEQ_6x32x16*)pClass; mymodule->m_bPauseState[ id ] = bOn; } //----------------------------------------------------- // MyLEDButton_HoldCV //----------------------------------------------------- void MyLEDButton_HoldCV( void *pClass, int id, bool bOn ) { SEQ_6x32x16 *mymodule; mymodule = (SEQ_6x32x16*)pClass; mymodule->m_bHoldCVState[ id ] = bOn; } //----------------------------------------------------- // MyLEDButton_BiLevel //----------------------------------------------------- void MyLEDButton_BiLevel( void *pClass, int id, bool bOn ) { SEQ_6x32x16 *mymodule; mymodule = (SEQ_6x32x16*)pClass; mymodule->m_bBiLevelState[ id ] = bOn; } //----------------------------------------------------- // MyLEDButton_CpyNxt //----------------------------------------------------- void MyLEDButton_CpyNxt( void *pClass, int id, bool bOn ) { SEQ_6x32x16 *mymodule; mymodule = (SEQ_6x32x16*)pClass; mymodule->Copy( id, bOn ); } //----------------------------------------------------- // MyLEDButton_Rand //----------------------------------------------------- void MyLEDButton_Rand( void *pClass, int id, bool bOn ) { SEQ_6x32x16 *mymodule; mymodule = (SEQ_6x32x16*)pClass; mymodule->Rand( id ); } //----------------------------------------------------- // MyLEDButton_Rand //----------------------------------------------------- void MyLEDButton_Clear( void *pClass, int id, bool bOn ) { SEQ_6x32x16 *mymodule; mymodule = (SEQ_6x32x16*)pClass; for( int i = 0; i < nSTEPS; i++ ) { mymodule->m_Pattern[ id ][ mymodule->m_CurrentProg[ id ] ][ i ] = 0; } mymodule->m_pPatternDisplay[ id ]->SetPatAll( mymodule->m_Pattern[ id ][ mymodule->m_CurrentProg[ id ] ] ); } //----------------------------------------------------- // Procedure: PatternChangeCallback // //----------------------------------------------------- void SEQ_6x32x16_PatternChangeCallback ( void *pClass, int ch, int pat, int level, int maxpat ) { SEQ_6x32x16 *mymodule = (SEQ_6x32x16 *)pClass; if( !mymodule || !mymodule->m_bInitialized ) return; mymodule->m_MaxPat[ ch ][ mymodule->m_CurrentProg[ ch ] ] = maxpat; mymodule->m_Pattern[ ch ][ mymodule->m_CurrentProg[ ch ] ] [ pat ] = level; } //----------------------------------------------------- // Procedure: ProgramChangeCallback // //----------------------------------------------------- void SEQ_6x32x16_ProgramChangeCallback ( void *pClass, int ch, int pat, int max ) { SEQ_6x32x16 *mymodule = (SEQ_6x32x16 *)pClass; if( !mymodule || !mymodule->m_bInitialized ) return; if( mymodule->m_MaxProg[ ch ] != max ) { mymodule->m_MaxProg[ ch ] = max; } else if( mymodule->m_CurrentProg[ ch ] == pat && mymodule->m_bPauseState[ ch ] && mymodule->m_CopySrc != NO_COPY ) { mymodule->ChangeProg( ch, pat, true ); } else if( mymodule->m_CurrentProg[ ch ] != pat ) { if( !mymodule->m_bPauseState[ ch ] && mymodule->inputs[ SEQ_6x32x16::IN_CLK + ch ].active ) mymodule->SetPendingProg( ch, pat ); else mymodule->ChangeProg( ch, pat, false ); } } //----------------------------------------------------- // Procedure: Widget // //----------------------------------------------------- struct SEQ_6x32x16_Widget : ModuleWidget { Menu *createContextMenu() override; SEQ_6x32x16_Widget( SEQ_6x32x16 *module ); }; SEQ_6x32x16_Widget::SEQ_6x32x16_Widget( SEQ_6x32x16 *module ) : ModuleWidget(module) { int x, y, x2, y2; ParamWidget *pWidget = NULL; box.size = Vec( 15*41, 380); { SVGPanel *panel = new SVGPanel(); panel->box.size = box.size; panel->setBackground(SVG::load(assetPlugin(plugin, "res/SEQ_6x32x16.svg"))); addChild(panel); } //module->lg.Open("SEQ_6x32x16.txt"); x = 7; y = 22; // global inputs addInput(Port::create( Vec( 204, 357 ), Port::INPUT, module, SEQ_6x32x16::IN_GLOBAL_CLK_RESET ) ); addInput(Port::create( Vec( 90, 357 ), Port::INPUT, module, SEQ_6x32x16::IN_GLOBAL_PAT_CHANGE ) ); // trig mute module->m_pButtonTrigMute = new MyLEDButton( 491, 3, 15, 15, 13.0, DWRGB( 180, 180, 180 ), DWRGB( 255, 0, 0 ), MyLEDButton::TYPE_SWITCH, 0, module, MyLEDButton_TrigMute ); addChild( module->m_pButtonTrigMute ); addInput(Port::create( Vec( 466, 1 ), Port::INPUT, module, SEQ_6x32x16::IN_GLOBAL_TRIG_MUTE ) ); for( int ch = 0; ch < nCHANNELS; ch++ ) { // inputs addInput(Port::create( Vec( x + 6, y + 7 ), Port::INPUT, module, SEQ_6x32x16::IN_CLK + ch ) ); addInput(Port::create( Vec( x + 64, y + 31 ), Port::INPUT, module, SEQ_6x32x16::IN_PAT_TRIG + ch ) ); // pattern display module->m_pPatternDisplay[ ch ] = new SinglePatternClocked32( x + 39, y + 2, 13, 13, 5, 2, 7, DWRGB( 255, 128, 64 ), DWRGB( 18, 9, 0 ), DWRGB( 180, 75, 180 ), DWRGB( 80, 45, 80 ), nSTEPS, ch, module, SEQ_6x32x16_PatternChangeCallback ); addChild( module->m_pPatternDisplay[ ch ] ); // program display module->m_pProgramDisplay[ ch ] = new PatternSelectStrip( x + 106, y + 31, 9, 7, DWRGB( 180, 180, 0 ), DWRGB( 90, 90, 64 ), DWRGB( 0, 180, 200 ), DWRGB( 0, 90, 90 ), nPROG, ch, module, SEQ_6x32x16_ProgramChangeCallback ); addChild( module->m_pProgramDisplay[ ch ] ); // add knobs y2 = y + 34; //pWidget = ParamWidget::create( Vec( x + 374, y2 ), module, SEQ_6x32x16::PARAM_SWING_KNOB + ch, 0.0, 0.6, 0.0 ); // removed for now if( pWidget ) { addParam( pWidget ); pWidget->visible = false; } x2 = x + 447; addParam(ParamWidget::create( Vec( x2, y2 ), module, SEQ_6x32x16::PARAM_LVL1_KNOB + ch, 0.0, 1.0, 0.25 ) ); x2 += 19; addParam(ParamWidget::create( Vec( x2, y2 ), module, SEQ_6x32x16::PARAM_LVL2_KNOB + ch, 0.0, 1.0, 0.33 ) ); x2 += 19; addParam(ParamWidget::create( Vec( x2, y2 ), module, SEQ_6x32x16::PARAM_LVL3_KNOB + ch, 0.0, 1.0, 0.50 ) ); x2 += 19; addParam(ParamWidget::create( Vec( x2, y2 ), module, SEQ_6x32x16::PARAM_LVL4_KNOB + ch, 0.0, 1.0, 0.75 ) ); x2 += 19; addParam(ParamWidget::create( Vec( x2, y2 ), module, SEQ_6x32x16::PARAM_LVL5_KNOB + ch, 0.0, 1.0, 0.9 ) ); // add buttons module->m_pButtonAutoPat[ ch ] = new MyLEDButton( x + 55, y + 35, 9, 9, 6.0, DWRGB( 180, 180, 180 ), DWRGB( 0, 255, 0 ), MyLEDButton::TYPE_SWITCH, ch, module, MyLEDButton_AutoPat ); addChild( module->m_pButtonAutoPat[ ch ] ); module->m_pButtonPause[ ch ] = new MyLEDButton( x + 26, y + 10, 11, 11, 8.0, DWRGB( 180, 180, 180 ), DWRGB( 255, 0, 0 ), MyLEDButton::TYPE_SWITCH, ch, module, MyLEDButton_Pause ); addChild( module->m_pButtonPause[ ch ] ); y2 = y + 33; module->m_pButtonCopy[ ch ] = new MyLEDButton( x + 290, y2, 11, 11, 8.0, DWRGB( 180, 180, 180 ), DWRGB( 0, 244, 244 ), MyLEDButton::TYPE_SWITCH, ch, module, MyLEDButton_CpyNxt ); addChild( module->m_pButtonCopy[ ch ] ); module->m_pButtonRand[ ch ] = new MyLEDButton( x + 315, y2, 11, 11, 8.0, DWRGB( 180, 180, 180 ), DWRGB( 0, 244, 244 ), MyLEDButton::TYPE_MOMENTARY, ch, module, MyLEDButton_Rand ); addChild( module->m_pButtonRand[ ch ] ); module->m_pButtonClear[ ch ] = new MyLEDButton( x + 340, y2, 11, 11, 8.0, DWRGB( 180, 180, 180 ), DWRGB( 0, 244, 244 ), MyLEDButton::TYPE_MOMENTARY, ch, module, MyLEDButton_Clear ); addChild( module->m_pButtonClear[ ch ] ); module->m_pButtonHoldCV[ ch ] = new MyLEDButton( x + 405, y2, 11, 11, 8.0, DWRGB( 180, 180, 180 ), DWRGB( 0, 244, 244 ), MyLEDButton::TYPE_SWITCH, ch, module, MyLEDButton_HoldCV ); addChild( module->m_pButtonHoldCV[ ch ] ); module->m_pButtonBiLevel[ ch ] = new MyLEDButton( x + 425, y2, 11, 11, 8.0, DWRGB( 180, 180, 180 ), DWRGB( 0, 244, 244 ), MyLEDButton::TYPE_SWITCH, ch, module, MyLEDButton_BiLevel ); addChild( module->m_pButtonBiLevel[ ch ] ); // add outputs addOutput(Port::create( Vec( x + 580, y + 7 ), Port::OUTPUT, module, SEQ_6x32x16::OUT_TRIG + ch ) ); addOutput(Port::create( Vec( x + 544, y + 33 ), Port::OUTPUT, module, SEQ_6x32x16::OUT_LEVEL + ch ) ); addOutput(Port::create( Vec( x + 37, y + 31 ), Port::OUTPUT, module, SEQ_6x32x16::OUT_BEAT1 + ch ) ); y += 56; } 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_bInitialized = true; module->onReset(); } //----------------------------------------------------- // Procedure: JsonParams // //----------------------------------------------------- void SEQ_6x32x16::JsonParams( bool bTo, json_t *root) { JsonDataBool ( bTo, "m_bPauseState", root, &m_bPauseState[ 0 ], nCHANNELS ); JsonDataBool ( bTo, "m_bBiLevelState", root, &m_bBiLevelState[ 0 ], nCHANNELS ); JsonDataInt ( bTo, "m_Pattern", root, &m_Pattern[ 0 ][ 0 ][ 0 ], nCHANNELS * nSTEPS * nPROG ); JsonDataInt ( bTo, "m_MaxPat", root, &m_MaxPat[ 0 ][ 0 ], nCHANNELS * nPROG ); JsonDataInt ( bTo, "m_CurrentProg", root, &m_CurrentProg[ 0 ], nCHANNELS ); JsonDataInt ( bTo, "m_MaxProg", root, &m_MaxProg[ 0 ], nCHANNELS ); JsonDataBool ( bTo, "m_bAutoPatChange", root, &m_bAutoPatChange[ 0 ], nCHANNELS ); JsonDataBool ( bTo, "m_bHoldCVState", root, &m_bHoldCVState[ 0 ], nCHANNELS ); JsonDataInt ( bTo, "m_RangeSelect", root, &m_RangeSelect, 1 ); JsonDataBool ( bTo, "m_bTrigMute", root, &m_bTrigMute, 1 ); } //----------------------------------------------------- // Procedure: toJson // //----------------------------------------------------- json_t *SEQ_6x32x16::toJson() { json_t *root = json_object(); if( !root ) return NULL; JsonParams( TOJSON, root ); return root; } //----------------------------------------------------- // Procedure: fromJson // //----------------------------------------------------- void SEQ_6x32x16::fromJson( json_t *root ) { JsonParams( FROMJSON, root ); for( int ch = 0; ch < nCHANNELS; ch++ ) { m_pButtonAutoPat[ ch ]->Set( m_bAutoPatChange[ ch ] ); m_pButtonPause[ ch ]->Set( m_bPauseState[ ch ] ); m_pButtonBiLevel[ ch ]->Set( m_bBiLevelState[ ch ] ); m_pButtonHoldCV[ ch ]->Set( m_bHoldCVState[ ch ] ); m_pPatternDisplay[ ch ]->SetPatAll( m_Pattern[ ch ][ m_CurrentProg[ ch ] ] ); m_pPatternDisplay[ ch ]->SetMax( m_MaxPat[ ch ][ m_CurrentProg[ ch ] ] ); m_pProgramDisplay[ ch ]->SetPat( m_CurrentProg[ ch ], false ); m_pProgramDisplay[ ch ]->SetMax( m_MaxProg[ ch ] ); } if( m_bTrigMute ) m_pButtonTrigMute->Set( m_bTrigMute ); sprintf( m_strRange, "%.1fV", m_fCVRanges[ m_RangeSelect ] ); } //----------------------------------------------------- // Procedure: reset // //----------------------------------------------------- void SEQ_6x32x16::onReset() { if( !m_bInitialized ) return; for( int ch = 0; ch < nCHANNELS; ch++ ) { m_pButtonPause[ ch ]->Set( false ); m_pButtonBiLevel[ ch ]->Set( false ); } memset( m_bPauseState, 0, sizeof(m_bPauseState) ); memset( m_bBiLevelState, 0, sizeof(m_bBiLevelState) ); memset( m_Pattern, 0, sizeof(m_Pattern) ); memset( m_CurrentProg, 0, sizeof(m_CurrentProg) ); for( int ch = 0; ch < nCHANNELS; ch++ ) { for( int prog = 0; prog < nCHANNELS; prog++ ) m_MaxPat[ ch ][ prog ] = nSTEPS - 1; m_MaxProg[ ch ] = nPROG - 1; m_pPatternDisplay[ ch ]->SetPatAll( m_Pattern[ ch ][ 0 ] ); m_pPatternDisplay[ ch ]->SetMax( m_MaxPat[ ch ][ 0 ] ); m_pProgramDisplay[ ch ]->SetPat( 0, false ); m_pProgramDisplay[ ch ]->SetMax( m_MaxProg[ ch ] ); m_pPatternDisplay[ ch ]->SetPatAll( m_Pattern[ ch ][ m_CurrentProg[ ch ] ] ); ChangeProg( ch, 0, true ); } } //----------------------------------------------------- // Procedure: randomize // //----------------------------------------------------- void SEQ_6x32x16::onRandomize() { for( int ch = 0; ch < nCHANNELS; ch++ ) { for( int p = 0; p < nPROG; p++ ) { for( int i = 0; i < nSTEPS; i++ ) { m_Pattern[ ch ][ p ][ i ] = (int)(randomUniform() * 5.0 ); } } m_pPatternDisplay[ ch ]->SetPatAll( m_Pattern[ ch ][ m_CurrentProg[ ch ] ] ); } } //----------------------------------------------------- // Procedure: Rand // //----------------------------------------------------- void SEQ_6x32x16::Rand( int ch ) { for( int i = 0; i < nSTEPS; i++ ) { if( i <= m_MaxPat[ ch ][ m_CurrentProg[ ch ] ] && randomUniform() > 0.5f ) m_Pattern[ ch ][ m_CurrentProg[ ch ] ][ i ] = (int)(randomUniform() * 5.0 ); else m_Pattern[ ch ][ m_CurrentProg[ ch ] ][ i ] = 0; } m_pPatternDisplay[ ch ]->SetPatAll( m_Pattern[ ch ][ m_CurrentProg[ ch ] ] ); } //----------------------------------------------------- // Procedure: CpyNext // //----------------------------------------------------- void SEQ_6x32x16::CpyNext( int ch ) { int next; next = m_CurrentProg[ ch ] + 1; if( next >= nPROG ) return; memcpy( m_Pattern[ ch ][ next ], m_Pattern[ ch ][ m_CurrentProg[ ch ] ], sizeof(int) * nSTEPS ); if(m_bPauseState[ ch ] || !inputs[ SEQ_6x32x16::IN_CLK + ch ].active ) ChangeProg( ch, next, false ); } //----------------------------------------------------- // Procedure: Copy // //----------------------------------------------------- void SEQ_6x32x16::Copy( int ch, bool bOn ) { if( !m_bPauseState[ ch ] || !bOn || m_CopySrc != NO_COPY ) { if( m_CopySrc != NO_COPY ) m_pButtonCopy[ m_CopySrc ]->Set( false ); m_CopySrc = NO_COPY; m_pButtonCopy[ ch ]->Set( false ); } else if( bOn ) { m_CopySrc = ch; } } //----------------------------------------------------- // Procedure: ChangProg // //----------------------------------------------------- void SEQ_6x32x16::ChangeProg( int ch, int prog, bool bforce ) { if( ch < 0 || ch >= nCHANNELS ) return; if( !bforce && prog == m_CurrentProg[ ch ] ) return; if( prog < 0 ) prog = nPROG - 1; else if( prog >= nPROG ) prog = 0; if( m_CopySrc != NO_COPY ) { if( m_bPauseState[ ch ] ) { memcpy( m_Pattern[ ch ][ prog ], m_Pattern[ m_CopySrc ][ m_CurrentProg[ m_CopySrc ] ], sizeof(int) * nSTEPS ); m_pButtonCopy[ m_CopySrc ]->Set( false ); m_MaxPat[ ch ][ prog ] = m_MaxPat[ m_CopySrc ][ m_CurrentProg[ m_CopySrc ] ]; m_CopySrc = NO_COPY; } } m_CurrentProg[ ch ] = prog; m_pPatternDisplay[ ch ]->SetPatAll( m_Pattern[ ch ][ prog ] ); m_pPatternDisplay[ ch ]->SetMax( m_MaxPat[ ch ][ prog ] ); m_pProgramDisplay[ ch ]->SetPat( prog, false ); } //----------------------------------------------------- // Procedure: SetPendingProg // //----------------------------------------------------- void SEQ_6x32x16::SetPendingProg( int ch, int progIn ) { int prog; if( progIn < 0 || progIn >= nPROG ) prog = ( m_CurrentProg[ ch ] + 1 ) & 0xF; else prog = progIn; if( prog > m_MaxProg[ ch ] ) prog = 0; m_ProgPending[ ch ].bPending = true; m_ProgPending[ ch ].prog = prog; m_pProgramDisplay[ ch ]->SetPat( m_CurrentProg[ ch ], false ); m_pProgramDisplay[ ch ]->SetPat( prog, true ); } //----------------------------------------------------- // Procedure: step // //----------------------------------------------------- void SEQ_6x32x16::step() { int ch, level, useclock; bool bClk, bClkTrig, bClockAtZero = false, bGlobalClk = false, bGlobalProg = false, bTrigOut, bPatTrig[ nCHANNELS ] = {}; float fout = 0.0; if( !m_bInitialized ) return; if( inputs[ IN_GLOBAL_TRIG_MUTE ].active ) { if( inputs[ IN_GLOBAL_TRIG_MUTE ].value >= 0.00001 ) { m_bTrigMute = true; m_pButtonTrigMute->Set( true ); } else if( inputs[ IN_GLOBAL_TRIG_MUTE ].value < 0.00001 ) { m_bTrigMute = false; m_pButtonTrigMute->Set( false ); } } if( inputs[ IN_GLOBAL_CLK_RESET ].active ) bGlobalClk = m_SchTrigGlobalClkReset.process( inputs[ IN_GLOBAL_CLK_RESET ].value ); if( inputs[ IN_GLOBAL_PAT_CHANGE ].active ) bGlobalProg= m_SchTrigGlobalProg.process( inputs[ IN_GLOBAL_PAT_CHANGE ].value ); // get trigs for( ch = 0; ch < nCHANNELS; ch++ ) bPatTrig[ ch ] = m_SchTrigClock[ ch ].process( inputs[ IN_CLK + ch ].normalize( 0.0f ) ); for( ch = 0; ch < nCHANNELS; ch++ ) { bClkTrig = false; bClk = false; bTrigOut = false; bClockAtZero = false; useclock = ch; // if no keyboard clock active then use kb 0's clock if( !inputs[ IN_CLK + ch ].active && inputs[ IN_CLK + 0 ].active ) useclock = 0; if( inputs[ IN_CLK + useclock ].active ) { // time the clock tick if( bGlobalClk ) { m_pPatternDisplay[ ch ]->ClockReset(); bClkTrig = true; bClockAtZero = true; bClk = true; } // clock if( bPatTrig[ useclock ] && !bGlobalClk ) bClkTrig = true; bClk = bClkTrig; // clock the pattern if( !m_bPauseState[ ch ] ) { if( bGlobalProg || m_SchTrigProg[ ch ].process( inputs[ IN_PAT_TRIG + ch ].value ) ) SetPendingProg( ch, -1 ); // clock in if( bClk ) { if( !bGlobalClk ) bClockAtZero = m_pPatternDisplay[ ch ]->ClockInc(); bTrigOut = ( m_Pattern[ ch ][ m_CurrentProg[ ch ] ][ m_pPatternDisplay[ ch ]->m_PatClk ] ); } } } // no clock input else { // resolve any left over phrase triggers if( m_ProgPending[ ch ].bPending ) { m_ProgPending[ ch ].bPending = false; ChangeProg( ch, m_ProgPending[ ch ].prog, true ); } continue; } // auto inc pattern if( m_bAutoPatChange[ ch ] && bClockAtZero ) { SetPendingProg( ch, -1 ); m_ProgPending[ ch ].bPending = false; ChangeProg( ch, m_ProgPending[ ch ].prog, true ); bTrigOut = ( m_Pattern[ ch ][ m_CurrentProg[ ch ] ][ m_pPatternDisplay[ ch ]->m_PatClk ] ); } // resolve pattern change else if( m_ProgPending[ ch ].bPending && bClockAtZero ) { m_ProgPending[ ch ].bPending = false; ChangeProg( ch, m_ProgPending[ ch ].prog, false ); bTrigOut = ( m_Pattern[ ch ][ m_CurrentProg[ ch ] ][ m_pPatternDisplay[ ch ]->m_PatClk ] ); } // trigger the beat 0 output if( bClockAtZero ) m_gateBeatPulse[ ch ].trigger(1e-3); outputs[ OUT_BEAT1 + ch ].value = m_gateBeatPulse[ ch ].process( 1.0 / engineGetSampleRate() ) ? CV_MAX : 0.0; // trigger out if( bTrigOut ) { m_gatePulse[ ch ].trigger(1e-3); } if( !m_bTrigMute ) outputs[ OUT_TRIG + ch ].value = m_gatePulse[ ch ].process( 1.0 / engineGetSampleRate() ) ? CV_MAX : 0.0; else outputs[ OUT_TRIG + ch ].value = 0.0f; level = m_Pattern[ ch ][ m_CurrentProg[ ch ] ][ m_pPatternDisplay[ ch ]->m_PatClk ]; // get actual level from knobs switch( level ) { case 1: fout = params[ PARAM_LVL1_KNOB + ch ].value; break; case 2: fout = params[ PARAM_LVL2_KNOB + ch ].value; break; case 3: fout = params[ PARAM_LVL3_KNOB + ch ].value; break; case 4: fout = params[ PARAM_LVL4_KNOB + ch ].value; break; case 5: fout = params[ PARAM_LVL5_KNOB + ch ].value; break; default: if( m_bHoldCVState[ ch ] ) continue; else fout = 0.0f; } // bidirectional convert to -1.0 to 1.0 if( m_bBiLevelState[ ch ] ) fout = ( fout * 2 ) - 1.0; if( m_bTrigMute ) outputs[ OUT_LEVEL + ch ].value = flast[ ch ]; else { flast[ ch ] = m_fCVRanges[ m_RangeSelect ] * fout; outputs[ OUT_LEVEL + ch ].value = flast[ ch ]; } } } //----------------------------------------------------- // Procedure: SEQ_6x32x16_CVRange // //----------------------------------------------------- struct SEQ_6x32x16_CVRange : MenuItem { SEQ_6x32x16 *module; void onAction(EventAction &e) override { module->m_RangeSelect++; if( module->m_RangeSelect > 2 ) module->m_RangeSelect = 0; sprintf( module->m_strRange, "%.1fV", module->m_fCVRanges[ module->m_RangeSelect ] ); } void step() override { rightText = module->m_strRange; } }; //----------------------------------------------------- // Procedure: createContextMenu // //----------------------------------------------------- Menu *SEQ_6x32x16_Widget::createContextMenu() { Menu *menu = ModuleWidget::createContextMenu(); SEQ_6x32x16 *mod = dynamic_cast(module); assert(mod); menu->addChild(construct()); menu->addChild(construct(&MenuLabel::text, "---- CV Output Level ----")); menu->addChild(construct( &MenuItem::text, "VRange (15, 10, 5):", &SEQ_6x32x16_CVRange::module, mod ) ); return menu; } } // namespace rack_plugin_mscHack using namespace rack_plugin_mscHack; RACK_PLUGIN_MODEL_INIT(mscHack, SEQ_6x32x16) { Model *modelSEQ_6x32x16 = Model::create( "mscHack", "Seq_6ch_32step", "SEQ 6 x 32", SEQUENCER_TAG, MULTIPLE_TAG ); return modelSEQ_6x32x16; }