|  | #include "mscHack.hpp"
//#include "mscHack_Controls.hpp"
#include "dsp/digital.hpp"
//#include "CLog.h"
namespace rack_plugin_mscHack {
#define PATTERNS 16
#define STEPS    16
#define CHANNELS 3
#define LIGHT_LAMBDA ( 0.065f )
#define CHANNEL_OFF_H 91
//-----------------------------------------------------
// Module Definition
//
//-----------------------------------------------------
struct Seq_3x16x16 : Module 
{
	enum ParamIds 
    {
        PARAM_RAND,
        PARAM_CPY_NEXT          = PARAM_RAND + CHANNELS,
		PARAM_RUN               = PARAM_CPY_NEXT + CHANNELS,
		PARAM_RESET,
		PARAM_SLIDERS,
		PARAM_STEPS             = PARAM_SLIDERS  + ( CHANNELS * STEPS ),
		PARAM_PATTERNS          = PARAM_STEPS    + ( CHANNELS * STEPS ),
        PARAM_STEP_NUM          = PARAM_PATTERNS + ( CHANNELS * PATTERNS ),
        PARAM_GLOBAL_PAT        = PARAM_STEP_NUM + ( STEPS ),
        nPARAMS                 = PARAM_GLOBAL_PAT + ( PATTERNS )
    };
	enum InputIds 
    {
		INPUT_EXT_CLOCK,
		INPUT_RESET,
        INPUT_GLOBAL_PAT_CHANGE,
        INPUT_PAT_CHANGE,
        nINPUTS = INPUT_PAT_CHANGE + CHANNELS
	};
	enum OutputIds 
    {
		OUT_GATES,
        OUT_CV      = OUT_GATES + CHANNELS,
        nOUTPUTS    = OUT_CV + CHANNELS
	};
	enum LightIds 
    {
		LIGHT_RUN,
        LIGHT_RESET,
        LIGHT_STEP_NUM,
        LIGHT_STEP          = LIGHT_STEP_NUM + ( STEPS ),
        LIGHT_PAT           = LIGHT_STEP + ( CHANNELS * STEPS ),
        LIGHT_COPY          = LIGHT_PAT + ( CHANNELS * PATTERNS ),
        LIGHT_RAND          = LIGHT_COPY + CHANNELS,
        LIGHT_GLOBAL_PAT    = LIGHT_RAND + CHANNELS,
        nLIGHTS             = LIGHT_GLOBAL_PAT + PATTERNS
	};
	enum GateMode 
    {
		TRIGGER,
		RETRIGGER,
		CONTINUOUS,
	};
    CLog            lg;
    bool            m_bInitJson = false;
	GateMode        m_GateMode = TRIGGER;
	PulseGenerator  m_PulseStep;
	bool            m_bRunning = true;
	float           m_fPhase = 0.0;
    // Inputs
	SchmittTrigger  m_SchTrigClock;
	SchmittTrigger  m_SchTrigRun;
	SchmittTrigger  m_SchTrigReset;
    SchmittTrigger  m_SchTrigPatChange[ CHANNELS ];
    // random pattern button
    SchmittTrigger  m_SchTrigRandPat;
    // copy next buttons
    SchmittTrigger  m_SchTrigCopyNext;
    // number of steps
    int             m_nSteps = STEPS;    
    SchmittTrigger  m_SchTrigStepNumbers[ STEPS ];
    
    // Level settings
    ParamWidget     *m_pLevelToggleParam2[ CHANNELS ][ STEPS ] = {};
    SchmittTrigger  m_SchTrigLevels[ CHANNELS ][ STEPS ];
    float           m_fLevels[ PATTERNS ][ CHANNELS ][ STEPS ] = {};
    // Global Pattern Select
    SchmittTrigger  m_SchTrigGlobalPatternSelect;
    int             m_GlobalSelect = 0;
    bool            m_bGlobalPatternChangePending = false;
    int             m_GlobalPendingLight = 0;
    // Pattern Select
    int             m_PatternSelect[ CHANNELS ] = {0};    
    SchmittTrigger  m_SchTrigPatternSelects[ CHANNELS ][ PATTERNS ];
    bool            m_bPatternPending = false;
    bool            m_bPatternChangePending[ CHANNELS ] = {0};
    int             m_PendingLight[ CHANNELS ] = {0};
    // Steps
    int             m_CurrentStep = 0;
    SchmittTrigger  m_SchTrigSteps[ CHANNELS ][ STEPS ];
    bool            m_bStepStates[ PATTERNS ][ CHANNELS ][ STEPS ] = {};    
    float           m_fLightStepLevels[ CHANNELS ][ STEPS ] = {};
    // Contructor
	Seq_3x16x16() : Module( nPARAMS, nINPUTS, nOUTPUTS, nLIGHTS ){} 
    // Overrides 
	void    step() override;
    json_t* toJson() override;
    void    fromJson(json_t *rootJ) override;
    void    onRandomize() override;
    void    onReset() override;
    void Randomize_Channel( int ch );
    void CopyNext( int ch );
    void ChangePattern( int ch, int index, bool bForceChange );
    void SetSteps( int nSteps );
    void SetGlobalPattern( int step );
};
//-----------------------------------------------------
// Procedure:   MySquareButton_CopyNext
//
//-----------------------------------------------------
struct MySquareButton_CopyNext : MySquareButton
{
    Seq_3x16x16 *mymodule;
    int param;
    void onChange( EventChange &e ) override 
    {
        mymodule = (Seq_3x16x16*)module;
        if( mymodule && value == 1.0 )
        {
            param = paramId - Seq_3x16x16::PARAM_CPY_NEXT;
            mymodule->CopyNext( param );
        }
		MomentarySwitch::onChange( e );
	}
};
//-----------------------------------------------------
// Procedure:   MySquareButton_Rand
//
//-----------------------------------------------------
struct MySquareButton_Rand : MySquareButton
{
    Seq_3x16x16 *mymodule;
    int param;
    void onChange( EventChange &e ) override 
    {
        mymodule = (Seq_3x16x16*)module;
        if( mymodule && value == 1.0 )
        {
            param = paramId - Seq_3x16x16::PARAM_RAND;
            mymodule->Randomize_Channel( param );
        }
		MomentarySwitch::onChange( e );
	}
};
//-----------------------------------------------------
// Procedure:   MySquareButton_GlobalPattern
//
//-----------------------------------------------------
struct MySquareButton_GlobalPattern : MySquareButton //SVGSwitch, MomentarySwitch
{
    Seq_3x16x16 *mymodule;
    int param;
    void onChange( EventChange &e ) override 
    {
        mymodule = (Seq_3x16x16*)module;
        if( mymodule && value == 1.0 )
        {
            param = paramId - Seq_3x16x16::PARAM_GLOBAL_PAT;
            if( mymodule->m_bRunning )
            {
                if( param != mymodule->m_GlobalSelect && !mymodule->m_bGlobalPatternChangePending )
                {
                    mymodule->m_bGlobalPatternChangePending = true;
                    mymodule->m_GlobalPendingLight = param;
                }
            }
            else
            {
                mymodule->SetGlobalPattern( param );
            }
        }
		MomentarySwitch::onChange( e );
	}
};
//-----------------------------------------------------
// Procedure:   MySquareButton_Pattern
//
//-----------------------------------------------------
struct MySquareButton_Pattern : MySquareButton //SVGSwitch, MomentarySwitch
{
    Seq_3x16x16 *mymodule;
    int ch, stp, param;
    void onChange( EventChange &e ) override 
    {
        mymodule = (Seq_3x16x16*)module;
        if( mymodule && value == 1.0 )
        {
            param = paramId - Seq_3x16x16::PARAM_PATTERNS;
            ch = param / STEPS;
            stp = param - (ch * STEPS);
            //mymodule->lg.f( (char*)"pat = %d, ch = %d, stp = %d\n", param, ch, stp );
            if( mymodule->m_bRunning )
            {
                if( stp != mymodule->m_PatternSelect[ ch ] && !mymodule->m_bPatternChangePending[ ch ] )
                {
                    mymodule->m_bPatternPending = true;
                    mymodule->m_bPatternChangePending[ ch ] = true;
                    mymodule->m_PendingLight[ ch ] = stp;
                }
            }
            else
            {
                mymodule->ChangePattern( ch, stp, false );
            }
        }
		MomentarySwitch::onChange( e );
	}
};
//-----------------------------------------------------
// Procedure:   MySquareButton_Step
//
//-----------------------------------------------------
struct MySquareButton_Step : MySquareButton //SVGSwitch, MomentarySwitch 
{
    Seq_3x16x16 *mymodule;
    int ch, stp, param;
    void onChange( EventChange &e ) override 
    {
        mymodule = (Seq_3x16x16*)module;
        if( mymodule && value == 1.0 )
        {
            param = paramId - Seq_3x16x16::PARAM_STEPS;
            ch = param / STEPS;
            stp = param - (ch * STEPS);
            //lg.f( (char*)"step = %d, ch = %d, stp = %d\n", param, ch, stp );
            mymodule->m_bStepStates[ mymodule->m_PatternSelect[ ch ] ][ ch ][ stp ] = !mymodule->m_bStepStates[ mymodule->m_PatternSelect[ ch ] ][ ch ][ stp ];
            mymodule->lights[ Seq_3x16x16::LIGHT_STEP + ( ch * STEPS ) + stp ].value = mymodule->m_bStepStates[ mymodule->m_PatternSelect[ ch ] ][ ch ][ stp ] ? 1.0f : 0.0f;
        }
		MomentarySwitch::onChange( e );
	}
};
//-----------------------------------------------------
// Procedure:   MySquareButton_Step
//
//-----------------------------------------------------
struct MySquareButton_StepNum : MySquareButton 
{
    Seq_3x16x16 *mymodule;
    int stp, i;
    void onChange( EventChange &e ) override 
    {
        mymodule = (Seq_3x16x16*)module;
        if( mymodule && value == 1.0 )
        {
            stp = paramId - Seq_3x16x16::PARAM_STEP_NUM;
            mymodule->SetSteps( stp + 1 );
        }
		MomentarySwitch::onChange( e );
	}
};
//-----------------------------------------------------
// Procedure:   MySlider_Levels
//
//-----------------------------------------------------
struct MySlider_Levels : MySlider_01
{
    Seq_3x16x16 *mymodule;
    int ch, stp, param;
    void onChange( EventChange &e ) override 
    {
        mymodule = (Seq_3x16x16*)module;
        if( mymodule )
        {
            param = paramId - Seq_3x16x16::PARAM_SLIDERS;
            ch = param / STEPS;
            stp = param - (ch * STEPS);
            //lg.f( "slider = %d, ch = %d, stp = %d, value = %.3f\n", param, ch, stp, value );
            mymodule->m_fLevels[ mymodule->m_PatternSelect[ ch ] ][ ch ][ stp ] = value;
        }
        SVGFader::onChange( e );
	}
};
//-----------------------------------------------------
// Procedure:   Widget
//
//-----------------------------------------------------
struct Seq_3x16x16_Widget : ModuleWidget {
	Seq_3x16x16_Widget( Seq_3x16x16 *module );
};
Seq_3x16x16_Widget::Seq_3x16x16_Widget( Seq_3x16x16 *module ) : ModuleWidget(module) 
{
    int i, ch, stp, x, y;
	box.size = Vec(15*23, 380);
	{
		SVGPanel *panel = new SVGPanel();
		panel->box.size = box.size;
		panel->setBackground(SVG::load(assetPlugin(plugin, "res/Seq_3x16x16.svg")));
		addChild(panel);
	}
    //module->lg.Open("Seq_3x16x16.txt");
	addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
	addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
	addChild(Widget::create<ScrewSilver>(Vec(15, 365))); 
	addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 365)));
    // clk/run button
	addParam(ParamWidget::create<LEDButton>(Vec( 78, 17 ), module, Seq_3x16x16::PARAM_RUN, 0.0, 1.0, 0.0 ) );
    addChild(ModuleLightWidget::create<SmallLight<GreenLight>>( Vec( 78 + 5, 17 + 5 ), module, Seq_3x16x16::LIGHT_RUN ) );
    addInput(Port::create<MyPortInSmall>( Vec( 47, 17 ), Port::INPUT, module, Seq_3x16x16::INPUT_EXT_CLOCK ) );
    // reset button
	addParam(ParamWidget::create<LEDButton>( Vec( 143, 17 ), module, Seq_3x16x16::PARAM_RESET, 0.0, 1.0, 0.0 ) );
    addChild(ModuleLightWidget::create<SmallLight<GreenLight>>( Vec( 143 + 5, 17 + 5 ), module, Seq_3x16x16::LIGHT_RESET ) );
    addInput(Port::create<MyPortInSmall>( Vec( 117, 17 ), Port::INPUT, module, Seq_3x16x16::INPUT_RESET ) );
    //----------------------------------------------------
    // Step Select buttons 
    y = 41;
    x = 50;
	for ( stp = 0; stp < STEPS; stp++ ) 
    {
        // step button
		addParam(ParamWidget::create<MySquareButton_StepNum>( Vec( x, y ), module, Seq_3x16x16::PARAM_STEP_NUM + stp, 0.0, 1.0, 0.0 ) );
        addChild(ModuleLightWidget::create<SmallLight<DarkRedValueLight>>( Vec( x + 2, y + 3 ), module, Seq_3x16x16::LIGHT_STEP_NUM + stp ) );
        if( ( stp & 0x3 ) == 0x3 )
            x += 20;
        else
            x += 15;
        module->lights[ Seq_3x16x16::LIGHT_STEP_NUM + stp ].value = 1.0;
    }
    //----------------------------------------------------
    // All the Channel buttons    
    // channel
	for ( ch = 0; ch < CHANNELS; ch++ ) 
    {
        x = 50;
        // step
	    for ( stp = 0; stp < STEPS; stp++ ) 
        {
            y = 64 + (ch * CHANNEL_OFF_H);
            // level button
		    module->m_pLevelToggleParam2[ ch ][ stp ] = ParamWidget::create<MySlider_Levels>( Vec( x, y ), module, Seq_3x16x16::PARAM_SLIDERS + ( ch * STEPS ) + stp , 0.0, 6.0, 0.0);
            addParam( module->m_pLevelToggleParam2[ ch ][ stp ] );
            y += 46;
            // step button
		    addParam(ParamWidget::create<MySquareButton_Step>( Vec( x, y ), module, Seq_3x16x16::PARAM_STEPS + ( ch * STEPS ) + stp, 0.0, 1.0, 0.0 ) );
            addChild(ModuleLightWidget::create<SmallLight<DarkGreenValueLight>>( Vec( x + 2, y + 3 ), module, Seq_3x16x16::LIGHT_STEP + ( ch * STEPS ) + stp ) );
            if( ( stp & 0x3 ) == 0x3 )
                x += 20;
            else
                x += 15;
        }
        x = 45;
        y = 130 + (ch * CHANNEL_OFF_H);
	    for ( i = 0; i < PATTERNS; i++ ) 
        {
            // pattern button
		    addParam(ParamWidget::create<MySquareButton_Pattern>( Vec( x, y ), module, Seq_3x16x16::PARAM_PATTERNS + ( ch * PATTERNS ) + i, 0.0, 1.0, 0.0 ) );
            addChild(ModuleLightWidget::create<SmallLight<OrangeValueLight>>( Vec( x + 2, y + 3 ), module, Seq_3x16x16::LIGHT_PAT + ( ch * PATTERNS ) + i ) );
            x += 13;
        }
        x = 260;
        y = 126 + (ch * CHANNEL_OFF_H);
        // copy next button
		addParam(ParamWidget::create<MySquareButton_CopyNext>( Vec( x, y ), module, Seq_3x16x16::PARAM_CPY_NEXT + ch, 0.0, 1.0, 0.0 ) );
        addChild(ModuleLightWidget::create<SmallLight<CyanValueLight>>( Vec( x + 2, y + 3 ), module, Seq_3x16x16::LIGHT_COPY + ch ) );
        x = 290;
        // random button
		addParam(ParamWidget::create<MySquareButton_Rand>( Vec( x, y ), module, Seq_3x16x16::PARAM_RAND + ch, 0.0, 1.0, 0.0 ) );
        addChild(ModuleLightWidget::create<SmallLight<CyanValueLight>>( Vec( x + 2, y + 3 ), module, Seq_3x16x16::LIGHT_RAND + ch ) );
        // pattern change input trigger
        addInput(Port::create<MyPortInSmall>( Vec( 17, 127 + (ch * CHANNEL_OFF_H) ), Port::INPUT, module, Seq_3x16x16::INPUT_PAT_CHANGE + ch ) );
        x = 310;
        y = 65 + (ch * CHANNEL_OFF_H);
        // outputs
        addOutput(Port::create<MyPortOutSmall>( Vec( x, y ), Port::OUTPUT, module, Seq_3x16x16::OUT_CV + ch ) );
        addOutput(Port::create<MyPortOutSmall>( Vec( x, y + 42 ), Port::OUTPUT, module, Seq_3x16x16::OUT_GATES + ch ) );
	}
    //----------------------------------------------------
    // Global patter select buttons  
    x = 45;
    y = 340;
	for ( stp = 0; stp < PATTERNS; stp++ ) 
    {
        // pattern button
		addParam(ParamWidget::create<MySquareButton_GlobalPattern>( Vec( x, y ), module, Seq_3x16x16::PARAM_GLOBAL_PAT + stp, 0.0, 1.0, 0.0 ) );
        addChild(ModuleLightWidget::create<SmallLight<OrangeValueLight>>( Vec( x + 2, y + 3 ), module, Seq_3x16x16::LIGHT_GLOBAL_PAT + stp ) );
        x += 13;
    }
    // pattern change input trigger
    addInput(Port::create<MyPortInSmall>( Vec( 17, 338 ), Port::INPUT, module, Seq_3x16x16::INPUT_GLOBAL_PAT_CHANGE ) );
    module->lights[ Seq_3x16x16::LIGHT_GLOBAL_PAT ].value = 1.0;
    module->m_GlobalSelect = 0;
    module->ChangePattern( 0, PATTERNS, false );
    module->ChangePattern( 1, PATTERNS, false );
    module->ChangePattern( 2, PATTERNS, false );
}
//-----------------------------------------------------
// Procedure:   
//
//-----------------------------------------------------
json_t *Seq_3x16x16::toJson() 
{
    bool *pgateState;
    float *pfloat;
	json_t *rootJ = json_object();
	// running
	json_object_set_new(rootJ, "running", json_boolean (m_bRunning));
	// steps
    pgateState = &m_bStepStates[ 0 ][ 0 ][ 0 ];
	json_t *gatesJ = json_array();
	for (int i = 0; i < ( PATTERNS * CHANNELS * STEPS ); i++)
    {
		json_t *gateJ = json_integer( (int) pgateState[ i ] );
		json_array_append_new( gatesJ, gateJ );
	}
	json_object_set_new( rootJ, "steps", gatesJ );
    // level settings
    pfloat = &m_fLevels[ 0 ][ 0 ][ 0 ];
	json_t *gatesJ_3 = json_array();
	for (int i = 0; i < ( PATTERNS * CHANNELS * STEPS ); i++) 
    {
		json_t *gateJ = json_real( pfloat[ i ] );
		json_array_append_new(gatesJ_3, gateJ);
	}
	json_object_set_new(rootJ, "levelsettings", gatesJ_3);
    // number of steps
	json_t *stepsJ = json_integer( m_nSteps );
	json_object_set_new(rootJ, "numberofsteps", stepsJ);
	// gateMode
	json_t *gateModeJ = json_integer((int) m_GateMode );
	json_object_set_new(rootJ, "gateMode", gateModeJ);
	return rootJ;
}
//-----------------------------------------------------
// Procedure:   fromJson
//
//-----------------------------------------------------
void Seq_3x16x16::fromJson(json_t *rootJ) 
{
    bool *pgateState;
    float *pfloat;
	// running
	json_t *runningJ = json_object_get(rootJ, "running");
	if (runningJ)
		m_bRunning = json_is_true(runningJ);
	// steps
    pgateState = &m_bStepStates[ 0 ][ 0 ][ 0 ];
	json_t *StepsJ = json_object_get(rootJ, "steps");
	if (StepsJ) 
    {
		for (int i = 0; i < ( PATTERNS * CHANNELS * STEPS ); i++)
        {
			json_t *gateJ = json_array_get(StepsJ, i);
			if (gateJ)
				pgateState[ i ] = json_integer_value( gateJ );
		}
	}
	// level settings
    pfloat = &m_fLevels[ 0 ][ 0 ][ 0 ];
	json_t *LvlSettingsJ = json_object_get(rootJ, "levelsettings");
    if (LvlSettingsJ) 
    {
		for (int i = 0; i < ( PATTERNS * CHANNELS * STEPS ); i++) 
        {
			json_t *gateJ = json_array_get(LvlSettingsJ, i);
			if (gateJ)
				pfloat[ i ] = json_real_value(gateJ);
            //lg.f("loading %.3f\n", pfloat[ i ] );
		}
	}
	// number of steps
	json_t *nStepsJ = json_object_get(rootJ, "numberofsteps");
	if (nStepsJ)
		m_nSteps = (GateMode)json_integer_value( nStepsJ );
	// gateMode
	json_t *gateModeJ = json_object_get(rootJ, "gateMode");
	if (gateModeJ)
		m_GateMode = (GateMode)json_integer_value(gateModeJ);
    SetSteps( m_nSteps );
    //lg.f("json read complete\n");
    m_bInitJson = true;
}
//-----------------------------------------------------
// Procedure:   reset
//
//-----------------------------------------------------
void Seq_3x16x16::onReset()
{
    //lg.f("init\n");
    memset( m_fLevels, 0, sizeof(m_fLevels) );
    memset( m_bStepStates, 0, sizeof(m_bStepStates) );
    for( int ch = 0; ch < CHANNELS; ch ++ )
    {
        for( int i = 0; i < STEPS; i ++ )
        {
            lights[ LIGHT_STEP + ( ch * STEPS ) + i ].value = 0.0;
            m_fLightStepLevels[ ch ][ i ] = 0.0;
        }
    }
    ChangePattern( 0, PATTERNS, false );
    ChangePattern( 1, PATTERNS, false );
    ChangePattern( 2, PATTERNS, false );
    SetSteps( 16 );
}
//-----------------------------------------------------
// Procedure:   randomize
//
//-----------------------------------------------------
float stepchance[ 16 ] = { 0.9, 0.5, 0.8, 0.4, 0.9, 0.5, 0.8, 0.4, 0.9, 0.5, 0.8, 0.4, 0.9, 0.5, 0.8, 0.4 }; 
void Seq_3x16x16::Randomize_Channel( int ch )
{
    int stp, pat;
    int octave;
    pat = m_PatternSelect[ ch ];
    octave = (int)( randomUniform() * 4.0 );
    for( stp = 0; stp < STEPS; stp++ )
    {
        m_fLevels[ pat ][ ch ][ stp ] = (float)octave + ( randomUniform() * 2.0 );
        m_bStepStates[ pat ][ ch ][ stp ] = ( randomUniform() < stepchance[ stp ] ) ? true : false;
    }
    ChangePattern( ch, pat, true );
}
//-----------------------------------------------------
// Procedure:   randomize
//
//-----------------------------------------------------
void Seq_3x16x16::onRandomize() 
{
    int ch, stp, pat;
    int octave;
    for( ch = 0; ch < CHANNELS; ch++ )
    {
        for( pat = 0; pat < PATTERNS; pat++ )
        {
            octave = (int)( randomUniform() * 4.0 );
            for( stp = 0; stp < STEPS; stp++ )
            {
                m_fLevels[ pat ][ ch ][ stp ] = (float)octave + ( randomUniform() * 2.0 );
                m_bStepStates[ pat ][ ch ][ stp ] = ( randomUniform() < stepchance[ stp ] ) ? true : false;
            }
        }
    }
    ChangePattern( 0, 0, true );
    ChangePattern( 1, 0, true );
    ChangePattern( 2, 0, true );
}
//-----------------------------------------------------
// Procedure:   CopyNext
//
//-----------------------------------------------------
void Seq_3x16x16::CopyNext( int ch )
{
    int stp, pat;
    pat = m_PatternSelect[ ch ];
    // don't copy from the last pattern
    if( pat == (PATTERNS - 1) )
        return;
    for( stp = 0; stp < STEPS; stp++ )
    {
        m_fLevels[ pat + 1 ][ ch ][ stp ] = m_fLevels[ pat ][ ch ][ stp ];
        m_bStepStates[ pat + 1 ][ ch ][ stp ] = m_bStepStates[ pat ][ ch ][ stp ];
    }
    ChangePattern( ch, pat + 1, true );
}
//-----------------------------------------------------
// Procedure:   ChangePattern
//
//-----------------------------------------------------
void Seq_3x16x16::ChangePattern( int ch, int index, bool bForceChange )
{
    int stp, i;
    if( !bForceChange && index == m_PatternSelect[ ch ] )
        return;
    if( index < 0 )
        index = PATTERNS - 1;
    else if( index >= PATTERNS )
        index = 0;
    m_PatternSelect[ ch ] = index;
    // update lights
    for( stp = 0; stp < STEPS; stp++ ) 
        lights[ LIGHT_STEP + ( ch * STEPS ) + stp ].value = m_bStepStates[ m_PatternSelect[ ch ] ][ ch ][ stp ] ? 1.0 : 0.0;
    for( i = 0; i < PATTERNS; i++ )
    {
        if( m_PatternSelect[ ch ] == i )
            lights[ LIGHT_PAT + ( ch * PATTERNS ) + i ].value = 1.0f; 
        else
            lights[ LIGHT_PAT + ( ch * PATTERNS ) + i ].value = 0.0f;
    }
    // step
	for( stp = 0; stp < STEPS; stp++ ) 
    {
        // level button
		m_pLevelToggleParam2[ ch ][ stp ]->setValue( m_fLevels[ m_PatternSelect[ ch ] ][ ch ][ stp ] );
    }
}
//-----------------------------------------------------
// Procedure:   SetSteps
//
//-----------------------------------------------------
void Seq_3x16x16::SetSteps( int nSteps )
{
    int i;
    if( nSteps < 1 || nSteps > STEPS )
        nSteps = STEPS;
    m_nSteps = nSteps;
	for ( i = 0; i < STEPS; i++ ) 
    {
        // level button
		if( i < nSteps )
            lights[ LIGHT_STEP_NUM + i ].value = 1.0f; 
        else
            lights[ LIGHT_STEP_NUM + i ].value = 0.0f;
    }
}
//-----------------------------------------------------
// Procedure:   SetGlobalPattern
//
//-----------------------------------------------------
void Seq_3x16x16::SetGlobalPattern( int step )
{
	for ( int i = 0; i < PATTERNS; i++ ) 
        lights[ LIGHT_GLOBAL_PAT + i ].value = 0.0f;
    lights[ LIGHT_GLOBAL_PAT + step ].value = 1.0;
    m_GlobalSelect = step;
    ChangePattern( 0, step, false );
    ChangePattern( 1, step, false );
    ChangePattern( 2, step, false );
}
//-----------------------------------------------------
// Procedure:   step
//
//-----------------------------------------------------
void Seq_3x16x16::step() 
{
    bool bNextStep = false, bPulse, bGatesOn, bChTrig;
    int ch, stp;
    float fout, fnote;
    int octave;
    static float fpendinglight = 0.0f;
    static bool bUp = true;
    // pending light value
    if( bUp )
    {
        fpendinglight += ( 1.0 / engineGetSampleRate() ) * 1.5 ;
        if( fpendinglight >= 0.5f )
        {
            fpendinglight = 0.5f;
            bUp = false;
        }
    }
    else
    {
        fpendinglight -= ( 1.0 / engineGetSampleRate() ) * 2;
        if( fpendinglight <= 0.0f )
        {
            fpendinglight = 0.0f;
            bUp = true;
        }
    }
	// Run
	if( m_SchTrigRun.process( params[ PARAM_RUN ].value ) )
		m_bRunning = !m_bRunning;
	lights[ LIGHT_RUN ].value = m_bRunning ? 1.0f : 0.0f;
	// Random and copy buttons
	for( ch = 0; ch < CHANNELS; ch++ ) 
    {
	    if( m_SchTrigRandPat.process( params[ PARAM_RAND + ch ].value ) ) 
            lights[ LIGHT_RAND + ch ].value = 1.0f;
	    if( m_SchTrigCopyNext.process( params[ PARAM_CPY_NEXT + ch ].value ) )
            lights[ LIGHT_COPY + ch ].value = 1.0f;
        lights[ LIGHT_RAND + ch ].value -= lights[ LIGHT_RAND + ch ].value / LIGHT_LAMBDA / engineGetSampleRate();
        lights[ LIGHT_COPY + ch ].value -= lights[ LIGHT_COPY + ch ].value / LIGHT_LAMBDA / engineGetSampleRate();
    }
	if( m_bRunning ) 
    {
		if( inputs[ INPUT_EXT_CLOCK ].active ) 
        {
			// External clock
			if( m_SchTrigClock.process( inputs[ INPUT_EXT_CLOCK ].value ) ) 
            {
				m_fPhase = 0.0f;
				bNextStep = true;
    		}
		}
	}
    else
    {
        // cancel any pending pattern changes
        if( m_bGlobalPatternChangePending )
        {
            m_bGlobalPatternChangePending = false;
            SetGlobalPattern( m_GlobalPendingLight );
        }
        m_bPatternPending = false;
        for( ch = 0; ch < CHANNELS; ch++ )
        {
	        // pat change trigger
	        if( m_bPatternChangePending[ ch ] )
            {
                m_bPatternChangePending[ ch ] = false;
                ChangePattern( ch, m_PendingLight[ ch ], false );
            }
        }
    }
	// Reset
	if( m_SchTrigReset.process( params[ PARAM_RESET ].value + inputs[ INPUT_RESET ].value ) ) 
    {
		m_fPhase = 0.0f;
		m_CurrentStep = m_nSteps;
		bNextStep = true;
        lights[ LIGHT_RESET ].value = 1.0f;
	}
	if( bNextStep ) 
    {
		m_CurrentStep += 1;
		if( m_CurrentStep >= m_nSteps ) 
			m_CurrentStep = 0;
        for( ch = 0; ch < CHANNELS; ch++ )
		    m_fLightStepLevels[ ch ][ m_CurrentStep ] = 1.0f;
		m_PulseStep.trigger( 1e-3 );
        // resolve any pending pattern changes
        if( m_CurrentStep == 0 )
        {
            if( m_bGlobalPatternChangePending )
            {
                m_bGlobalPatternChangePending = false;
                SetGlobalPattern( m_GlobalPendingLight );
                // cancel other changes on a global change
                m_bPatternChangePending[ 0 ] = false;
                m_bPatternChangePending[ 1 ] = false;
                m_bPatternChangePending[ 2 ] = false;
                m_bPatternPending = false;
            }
            for( ch = 0; ch < CHANNELS; ch++ )
            {
	            // pat change trigger
	            if( m_bPatternChangePending[ ch ] )
                {
                    m_bPatternChangePending[ ch ] = false;
                    ChangePattern( ch, m_PendingLight[ ch ], false );
                }
            }
        }
	}
    // global pattern pending change light blinky
    if( m_bGlobalPatternChangePending )
    {
        lights[ LIGHT_GLOBAL_PAT + m_GlobalPendingLight ].value = fpendinglight;
    }
    // channel pattern pending change light blinky
    if( m_bPatternPending )
    {
        m_bPatternPending = false;
        for( ch = 0; ch < CHANNELS; ch++ )
        {
	        // pat change trigger
	        if( m_bPatternChangePending[ ch ] )
            {
                m_bPatternPending = true;
                lights[ LIGHT_PAT + (ch * PATTERNS) + m_PendingLight[ ch ] ].value = fpendinglight;
            }
        }
    }
   	lights[ LIGHT_RESET ].value -= lights[ LIGHT_RESET ].value / LIGHT_LAMBDA / engineGetSampleRate();
	bPulse = m_PulseStep.process( 1.0 / engineGetSampleRate() );
	// Step buttons
	for( ch = 0; ch < CHANNELS; ch++ ) 
    {
        bChTrig = false;
	    for( stp = 0; stp < STEPS; stp++ ) 
        {
		    bGatesOn = ( m_bRunning && stp == m_CurrentStep && m_bStepStates[ m_PatternSelect[ ch ] ][ ch ][ stp ] );
		    if( m_GateMode == TRIGGER )
			    bGatesOn = bGatesOn && bPulse;
		    else if( m_GateMode == RETRIGGER )
			    bGatesOn = bGatesOn && !bPulse;
            if( bGatesOn )
                bChTrig = true;
		    
		    m_fLightStepLevels[ ch ][ stp ] -= m_fLightStepLevels[ ch ][ stp ] / LIGHT_LAMBDA / engineGetSampleRate();
            lights[ LIGHT_STEP + (ch * STEPS) + stp ].value = m_bStepStates[ m_PatternSelect[ ch ] ][ ch ][ stp ] ? 1.0 - m_fLightStepLevels[ ch ][ stp ] : m_fLightStepLevels[ ch ][ stp ];
	    }
        // trigger
        outputs[ OUT_GATES + ch ].value = bChTrig ? CV_MAX : 0.0;
    }
    // outputs
	for( ch = 0; ch < CHANNELS; ch++ ) 
    {
        // set CV
        if( m_bRunning && m_bStepStates[ m_PatternSelect[ ch ] ][ ch ][ m_CurrentStep ]  )
        {
            // quantizie note
            fout = params[ PARAM_SLIDERS + ( ch * STEPS ) + m_CurrentStep ].value;
            octave = round( fout );
            fnote   = round( ( fout - (float)octave ) * 12.0f );
            outputs[ OUT_CV + ch ].value = (float)octave + fnote/12.0f;
        }
    }
    if( m_bRunning )
    {
        // external pattern select change
        for( ch = 0; ch < CHANNELS; ch++ )
        {
	        // pat change trigger - ignore if already pending
	        if( m_SchTrigPatChange[ ch ].process( inputs[ INPUT_PAT_CHANGE + ch ].value ) && !m_bPatternChangePending[ ch ] ) 
            {
                m_bPatternPending = true;
                m_bPatternChangePending[ ch ] = true;
                m_PendingLight[ ch ] = ( m_PatternSelect[ ch ] + 1 ) & 0xF;
	        }
        }
	    // global pat change trigger - ignore if already pending
	    if( m_SchTrigGlobalPatternSelect.process( inputs[ INPUT_GLOBAL_PAT_CHANGE ].value ) && !m_bGlobalPatternChangePending ) 
        {
            m_bGlobalPatternChangePending = true;
            m_GlobalPendingLight = ( m_GlobalSelect + 1 ) & 0xF;
	    }
    }
}
} // namespace rack_plugin_mscHack
using namespace rack_plugin_mscHack;
RACK_PLUGIN_MODEL_INIT(mscHack, Seq_3x16x16) {
   Model *modelSeq_3x16x16 = Model::create<Seq_3x16x16, Seq_3x16x16_Widget>( "mscHack", "Seq_3ch_16step", "SEQ 3 x 16", SEQUENCER_TAG, MULTIPLE_TAG );
   return modelSeq_3x16x16;
}
 |