#include "mscHack.hpp" namespace rack_plugin_mscHack { #define nCHANNELS 24 #define nINCHANNELS 16 #define nGROUPS 4 #define nAUX 4 #define nEQ 3 #define FADE_MULT (0.0005f) #define CUTOFF (0.025f) #define L 0 #define R 1 #define MUTE_FADE_STATE_IDLE 0 #define MUTE_FADE_STATE_INC 1 #define MUTE_FADE_STATE_DEC 2 //----------------------------------------------------- // Module Definition // //----------------------------------------------------- struct Mixer_16_4_4 : Module { enum ParamIds { PARAM_LEVEL_OUT, PARAM_CHLVL, PARAM_CHPAN = PARAM_CHLVL + nCHANNELS, PARAM_CHEQHI = PARAM_CHPAN + nCHANNELS, PARAM_CHEQMD = PARAM_CHEQHI + nCHANNELS, PARAM_CHEQLO = PARAM_CHEQMD + nCHANNELS, PARAM_CHAUX = PARAM_CHEQLO + nCHANNELS, PARAM_AUXLVL = PARAM_CHAUX + ( (nCHANNELS - 4) * nAUX ), nPARAMS = PARAM_AUXLVL + nAUX, }; enum InputIds { IN_LEFT, IN_RIGHT = IN_LEFT + nCHANNELS, IN_LEVELCV = IN_RIGHT + nCHANNELS, IN_PANCV = IN_LEVELCV + nINCHANNELS, IN_FADEX = IN_PANCV + nINCHANNELS, IN_FADEY, nINPUTS }; enum OutputIds { OUT_MAINL, OUT_MAINR, OUT_GRPL, OUT_GRPR = OUT_GRPL + nGROUPS, OUT_AUXL = OUT_GRPR + nGROUPS, OUT_AUXR = OUT_AUXL + nAUX, nOUTPUTS = OUT_AUXR + nAUX }; enum LightIds { nLIGHTS }; bool m_bInitialized = false; CLog lg; // Contructor Mixer_16_4_4() : Module(nPARAMS, nINPUTS, nOUTPUTS, nLIGHTS){} // mute/solo bool m_bMuteStates[ nCHANNELS ] = {}; float m_fMuteFade[ nCHANNELS ] = {}; int m_FadeState[ nCHANNELS ] = {MUTE_FADE_STATE_IDLE}; bool m_bSoloStates[ nCHANNELS ] = {}; bool m_bPreFader[ nINCHANNELS + nGROUPS ] = {}; // processing bool m_bMono[ nCHANNELS ]; // LED Meters LEDMeterWidget *m_pLEDMeterChannel[ nCHANNELS ][ 2 ] ={}; LEDMeterWidget *m_pLEDMeterMain[ 2 ] ={}; // EQ Rez float lp1[ nCHANNELS ][ 2 ] = {}, bp1[ nCHANNELS ][ 2 ] = {}; float m_hpIn[ nCHANNELS ]; float m_lpIn[ nCHANNELS ]; float m_mpIn[ nCHANNELS ]; float m_Freq; // buttons MyLEDButton *m_pButtonChannelMute[ nCHANNELS ] = {}; MyLEDButton *m_pButtonChannelSolo[ nCHANNELS - nAUX ] = {}; MyLEDButton *m_pButtonPreFader[ nCHANNELS ] = {}; // routing int m_iRouteGroup[ nINCHANNELS ] = {nGROUPS}; MyLEDButtonStrip *m_pMultiButtonRoute[ nINCHANNELS ] = {0}; // menu bool m_bGroupPreMute = true; bool m_bGainLevelx2 = false; bool m_bAuxIgnoreSolo = false; // 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 onCreate() override; void onDelete() override; void ProcessMuteSolo( int channel, bool bMute, bool bOn ); void ProcessEQ( int ch, float *pL, float *pR ); void SetControls( int ch ); //----------------------------------------------------- // MyEQHi_Knob //----------------------------------------------------- struct MyEQHi_Knob : Knob_Green1_15 { Mixer_16_4_4 *mymodule; int param; void onChange( EventChange &e ) override { mymodule = (Mixer_16_4_4*)module; if( mymodule ) { param = paramId - Mixer_16_4_4::PARAM_CHEQHI; mymodule->m_hpIn[ param ] = value; } RoundKnob::onChange( e ); } }; //----------------------------------------------------- // MyEQHi_Knob //----------------------------------------------------- struct MyEQMid_Knob : Knob_Green1_15 { Mixer_16_4_4 *mymodule; int param; void onChange( EventChange &e ) override { mymodule = (Mixer_16_4_4*)module; if( mymodule ) { param = paramId - Mixer_16_4_4::PARAM_CHEQMD; mymodule->m_mpIn[ param ] = value; } RoundKnob::onChange( e ); } }; //----------------------------------------------------- // MyEQHi_Knob //----------------------------------------------------- struct MyEQLo_Knob : Knob_Green1_15 { Mixer_16_4_4 *mymodule; int param; void onChange( EventChange &e ) override { mymodule = (Mixer_16_4_4*)module; if( mymodule ) { param = paramId - Mixer_16_4_4::PARAM_CHEQLO; mymodule->m_lpIn[ param ] = value; } RoundKnob::onChange( e ); } }; }; //----------------------------------------------------- // MyLEDButton_ChSolo //----------------------------------------------------- void Mixer_16_4_4_Button_ChSolo( void *pClass, int id, bool bOn ) { Mixer_16_4_4 *mymodule; mymodule = (Mixer_16_4_4*)pClass; mymodule->ProcessMuteSolo( id, false, bOn ); } //----------------------------------------------------- // MyLEDButton_ChMute //----------------------------------------------------- void Mixer_16_4_4_Button_ChMute( void *pClass, int id, bool bOn ) { Mixer_16_4_4 *mymodule; mymodule = (Mixer_16_4_4*)pClass; mymodule->ProcessMuteSolo( id, true, bOn ); } //----------------------------------------------------- // Button_ChPreFader //----------------------------------------------------- void Mixer_16_4_4_Button_ChPreFader( void *pClass, int id, bool bOn ) { Mixer_16_4_4 *mymodule; mymodule = (Mixer_16_4_4*)pClass; mymodule->m_bPreFader[ id ] = bOn; } //----------------------------------------------------- // Procedure: RouteCallback //----------------------------------------------------- void Mixer_16_4_4_RouteCallback( void *pClass, int id, int nbutton, bool bOn ) { Mixer_16_4_4 *mymodule; mymodule = (Mixer_16_4_4*)pClass; if( mymodule ) { mymodule->m_iRouteGroup[ id ] = nbutton; } } //----------------------------------------------------- // Procedure: Widget // //----------------------------------------------------- struct Mixer_16_4_4_Widget : ModuleWidget { Menu *createContextMenu() override; Mixer_16_4_4_Widget( Mixer_16_4_4 *module ); }; Mixer_16_4_4_Widget::Mixer_16_4_4_Widget( Mixer_16_4_4 *module ) : ModuleWidget(module) { Port *pPort; float fx, fx2, fx3, fx5, fx7; int ch, x, y, x2, y2; bool bGroup, bAux, bNormal; box.size = Vec( 15*45, 380); { SVGPanel *panel = new SVGPanel(); panel->box.size = box.size; panel->setBackground(SVG::load(assetPlugin(plugin, "res/Mixer_16_4_4.svg"))); addChild(panel); } //module->lg.Open("Mixer_16_4_4.txt"); x = 47; y = 13; for( ch = 0; ch < nCHANNELS; ch++ ) { bGroup = false; bAux = false; bNormal= false; // groups if( ch < nINCHANNELS ) bNormal = true; else if( ch >= nINCHANNELS && ch < ( nINCHANNELS + nGROUPS) ) bGroup = true; else bAux = true; x2 = x + 3; y2 = y + 8; // inputs pPort = Port::create( Vec( x2, y2 ), Port::INPUT, module, Mixer_16_4_4::IN_LEFT + ch ); addInput( pPort ); y2 += 23; if( bGroup ) pPort->visible = false; pPort = Port::create( Vec( x2, y2 ), Port::INPUT, module, Mixer_16_4_4::IN_RIGHT + ch ); addInput( pPort ); if( bGroup ) pPort->visible = false; x2 = x + 4; y2 += 20; // aux sends if( !bAux ) { addParam(ParamWidget::create( Vec( x2, y2 ), module, Mixer_16_4_4::PARAM_CHAUX + (ch * 4) + 0, 0.0, 1.0, 0.0 ) ); y2 += 17; addParam(ParamWidget::create( Vec( x2, y2 ), module, Mixer_16_4_4::PARAM_CHAUX + (ch * 4) + 1, 0.0, 1.0, 0.0 ) ); y2 += 17; addParam(ParamWidget::create( Vec( x2, y2 ), module, Mixer_16_4_4::PARAM_CHAUX + (ch * 4) + 2, 0.0, 1.0, 0.0 ) ); y2 += 17; addParam(ParamWidget::create( Vec( x2, y2 ), module, Mixer_16_4_4::PARAM_CHAUX + (ch * 4) + 3, 0.0, 1.0, 0.0 ) ); module->m_pButtonPreFader[ ch ] = new MyLEDButton( x2 - 3, y2 + 15, 7, 7, 5.0f, DWRGB( 180, 180, 180 ), DWRGB( 255, 255, 255 ), MyLEDButton::TYPE_SWITCH, ch, module, Mixer_16_4_4_Button_ChPreFader ); addChild( module->m_pButtonPreFader[ ch ] ); y2 += 24; } else { switch( ch ) { case 20: addParam(ParamWidget::create( Vec( x2, y2 ), module, Mixer_16_4_4::PARAM_AUXLVL + 0, 0.0, 1.0, 0.0 ) ); break; case 21: addParam(ParamWidget::create( Vec( x2, y2 ), module, Mixer_16_4_4::PARAM_AUXLVL + 1, 0.0, 1.0, 0.0 ) ); break; case 22: addParam(ParamWidget::create( Vec( x2, y2 ), module, Mixer_16_4_4::PARAM_AUXLVL + 2, 0.0, 1.0, 0.0 ) ); break; case 23: addParam(ParamWidget::create( Vec( x2, y2 ), module, Mixer_16_4_4::PARAM_AUXLVL + 3, 0.0, 1.0, 0.0 ) ); break; } addOutput(Port::create( Vec( x2 - 1, y2 + 22), Port::OUTPUT, module, Mixer_16_4_4::OUT_AUXL + (ch - (nCHANNELS - nAUX) ) ) ); addOutput(Port::create( Vec( x2 - 1, y2 + 47), Port::OUTPUT, module, Mixer_16_4_4::OUT_AUXR + (ch - (nCHANNELS - nAUX) ) ) ); y2 += 75; } // EQ addParam(ParamWidget::create( Vec( x2, y2 ), module, Mixer_16_4_4::PARAM_CHEQHI + ch, 0.0, 1.0, 0.5 ) ); y2 += 18; addParam(ParamWidget::create( Vec( x2, y2 ), module, Mixer_16_4_4::PARAM_CHEQMD + ch, 0.0, 1.0, 0.5 ) ); y2 += 18; addParam(ParamWidget::create( Vec( x2, y2 ), module, Mixer_16_4_4::PARAM_CHEQLO + ch, 0.0, 1.0, 0.5 ) ); y2 += 20; // CVs if( bNormal ) { addInput(Port::create( Vec( x2 - 1, y2 ), Port::INPUT, module, Mixer_16_4_4::IN_LEVELCV + ch ) ); y2 += 25; addInput(Port::create( Vec( x2 - 1, y2 ), Port::INPUT, module, Mixer_16_4_4::IN_PANCV + ch ) ); } // group outs else if( bGroup ) { addOutput(Port::create( Vec( x2 - 1, y2 ), Port::OUTPUT, module, Mixer_16_4_4::OUT_GRPL + (ch - nINCHANNELS) ) ); y2 += 25; addOutput(Port::create( Vec( x2 - 1, y2 ), Port::OUTPUT, module, Mixer_16_4_4::OUT_GRPR + (ch - nINCHANNELS) ) ); } else y2 += 25; y2 += 23; addParam(ParamWidget::create( Vec( x2, y2 ), module, Mixer_16_4_4::PARAM_CHPAN + ch, -1.0, 1.0, 0.0 ) ); y2 += 19; // mute/solo if( bNormal || bGroup ) { module->m_pButtonChannelMute[ ch ] = new MyLEDButton( x + 3, y2, 8, 8, 6.0f, DWRGB( 180, 180, 180 ), DWRGB( 255, 0, 0 ), MyLEDButton::TYPE_SWITCH, ch, module, Mixer_16_4_4_Button_ChMute ); addChild( module->m_pButtonChannelMute[ ch ] ); module->m_pButtonChannelSolo[ ch ] = new MyLEDButton( x + 12, y2, 8, 8, 6.0f, DWRGB( 180, 180, 180 ), DWRGB( 0, 255, 0 ), MyLEDButton::TYPE_SWITCH, ch, module, Mixer_16_4_4_Button_ChSolo ); addChild( module->m_pButtonChannelSolo[ ch ] ); } // mute only else { module->m_pButtonChannelMute[ ch ] = new MyLEDButton( x + 9, y2, 8, 8, 6.0f, DWRGB( 180, 180, 180 ), DWRGB( 255, 0, 0 ), MyLEDButton::TYPE_SWITCH, ch, module, Mixer_16_4_4_Button_ChMute ); addChild( module->m_pButtonChannelMute[ ch ] ); } // VUMeter module->m_pLEDMeterChannel[ ch ][ 0 ] = new LEDMeterWidget( x + 7, y + 260, 4, 1, 1, true ); addChild( module->m_pLEDMeterChannel[ ch ][ 0 ] ); module->m_pLEDMeterChannel[ ch ][ 1 ] = new LEDMeterWidget( x + 12, y + 260, 4, 1, 1, true ); addChild( module->m_pLEDMeterChannel[ ch ][ 1 ] ); x2 = x + 2; y2 = y + 290; // Group Route if( bNormal ) { module->m_pMultiButtonRoute[ ch ] = new MyLEDButtonStrip( x2, y2, 6, 6, 3, 4.0f, 5, true, DWRGB( 0, 128, 128 ), DWRGB( 0, 255, 255 ), MyLEDButtonStrip::TYPE_EXCLUSIVE, ch, module, Mixer_16_4_4_RouteCallback ); addChild( module->m_pMultiButtonRoute[ ch ] ); } // level slider addParam(ParamWidget::create( Vec( x + 10, y2 - 8 ), module, Mixer_16_4_4::PARAM_CHLVL + ch, 0.0, 1.0, 0.0 ) ); x += 23; } // output addParam(ParamWidget::create( Vec( 605, 256 ), module, Mixer_16_4_4::PARAM_LEVEL_OUT, 0.0, 1.0, 0.5 ) ); module->m_pLEDMeterMain[ 0 ] = new LEDMeterWidget( 615, 315, 5, 3, 2, true ); addChild( module->m_pLEDMeterMain[ 0 ] ); module->m_pLEDMeterMain[ 1 ] = new LEDMeterWidget( 615 + 7, 315, 5, 3, 2, true ); addChild( module->m_pLEDMeterMain[ 1 ] ); // main outputs addOutput(Port::create( Vec( 638, 319 ), Port::OUTPUT, module, Mixer_16_4_4::OUT_MAINL ) ); addOutput(Port::create( Vec( 638, 344 ), Port::OUTPUT, module, Mixer_16_4_4::OUT_MAINR ) ); // xfade inputs addInput(Port::create( Vec( 650, 203 ), Port::INPUT, module, Mixer_16_4_4::IN_FADEX ) ); addInput(Port::create( Vec( 650, 230 ), Port::INPUT, module, Mixer_16_4_4::IN_FADEY ) ); //screw 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))); // calculate eq rez freq fx = 3.141592 * (CUTOFF * 0.026315789473684210526315789473684) * 2 * 3.141592; fx2 = fx*fx; fx3 = fx2*fx; fx5 = fx3*fx2; fx7 = fx5*fx2; module->m_Freq = 2.0 * (fx - (fx3 * 0.16666666666666666666666666666667) + (fx5 * 0.0083333333333333333333333333333333) - (fx7 * 0.0001984126984126984126984126984127)); module->m_bInitialized = true; module->onReset(); } //----------------------------------------------------- // Procedure: JsonParams // //----------------------------------------------------- void Mixer_16_4_4::JsonParams( bool bTo, json_t *root) { JsonDataBool( bTo, "m_bMuteStates", root, m_bMuteStates, nCHANNELS ); JsonDataBool( bTo, "m_bSoloStates", root, m_bSoloStates, nCHANNELS ); JsonDataInt( bTo, "m_iRouteGroup", root, &m_iRouteGroup[ 0 ], nINCHANNELS ); JsonDataBool( bTo, "m_bGroupPreMute", root, &m_bGroupPreMute, 1 ); JsonDataBool( bTo, "m_bGainLevelx2", root, &m_bGainLevelx2, 1 ); JsonDataBool( bTo, "m_bPreFader", root, m_bPreFader, nINCHANNELS + nGROUPS ); JsonDataBool( bTo, "m_bAuxIgnoreSolo", root, &m_bAuxIgnoreSolo, 1 ); } //----------------------------------------------------- // Procedure: toJson // //----------------------------------------------------- json_t *Mixer_16_4_4::toJson() { json_t *root = json_object(); if( !root ) return NULL; JsonParams( TOJSON, root ); return root; } //----------------------------------------------------- // Procedure: fromJson // //----------------------------------------------------- void Mixer_16_4_4::fromJson( json_t *root ) { int ch; JsonParams( FROMJSON, root ); // anybody soloing? for( ch = 0; ch < nCHANNELS; ch++ ) { if( m_bMuteStates[ ch ] ) ProcessMuteSolo( ch, true, m_bMuteStates[ ch ] ); else if( m_bSoloStates[ ch ] ) ProcessMuteSolo( ch, false, m_bSoloStates[ ch ] ); SetControls( ch ); } } //----------------------------------------------------- // Procedure: SetControls // //----------------------------------------------------- void Mixer_16_4_4::SetControls( int ch ) { if( !m_bInitialized || ch >= nCHANNELS || ch < 0 ) return; //lg.f("Here\n"); if( m_pButtonChannelMute[ ch ] ) m_pButtonChannelMute[ ch ]->Set( m_bMuteStates[ ch ] ); if( m_pButtonChannelSolo[ ch ] ) m_pButtonChannelSolo[ ch ]->Set( m_bSoloStates[ ch ] ); if( ch < nINCHANNELS && m_pMultiButtonRoute[ ch ] ) m_pMultiButtonRoute[ ch ]->Set( m_iRouteGroup[ ch ], true ); if( ch < (nINCHANNELS + nGROUPS ) ) m_pButtonPreFader[ ch ]->Set( m_bPreFader[ ch ] ); } //----------------------------------------------------- // Procedure: onCreate // //----------------------------------------------------- void Mixer_16_4_4::onCreate() { } //----------------------------------------------------- // Procedure: onDelete // //----------------------------------------------------- void Mixer_16_4_4::onDelete() { } //----------------------------------------------------- // Procedure: onReset // //----------------------------------------------------- void Mixer_16_4_4::onReset() { int ch; if( !m_bInitialized ) return; for( ch = 0; ch < nCHANNELS; ch++ ) { m_FadeState[ ch ] = MUTE_FADE_STATE_IDLE; m_bMuteStates[ ch ] = false; m_bSoloStates[ ch ] = false; m_fMuteFade[ ch ] = 1.0f; if( ch < nINCHANNELS ) m_iRouteGroup[ ch ] = nGROUPS; SetControls( ch ); } } //----------------------------------------------------- // Procedure: onRandomize // //----------------------------------------------------- void Mixer_16_4_4::onRandomize() { } //----------------------------------------------------- // Procedure: ProcessEQ // //----------------------------------------------------- #define MULTI (0.33333333333333333333333333333333f) void Mixer_16_4_4::ProcessEQ( int ch, float *pL, float *pR ) { float rez, hp1; float input[ 2 ], out[ 2 ], lowpass, bandpass, highpass; input[ L ] = *pL / AUDIO_MAX; input[ R ] = *pR / AUDIO_MAX; rez = 1.00; // do left and right channels for( int i = 0; i < 2; i++ ) { input[ i ] = input[ i ] + 0.000000001; lp1[ ch ][ i ] = lp1[ ch ][ i ] + m_Freq * bp1[ ch ][ i ]; hp1 = input[ i ] - lp1[ ch ][ i ] - rez * bp1[ ch ][ i ]; bp1[ ch ][ i ] = m_Freq * hp1 + bp1[ ch ][ i ]; lowpass = lp1[ ch ][ i ]; highpass = hp1; bandpass = bp1[ ch ][ i ]; lp1[ ch ][ i ] = lp1[ ch ][ i ] + m_Freq * bp1[ ch ][ i ]; hp1 = input[ i ] - lp1[ ch ][ i ] - rez * bp1[ ch ][ i ]; bp1[ ch ][ i ] = m_Freq * hp1 + bp1[ ch ][ i ]; lowpass = lowpass + lp1[ ch ][ i ]; highpass = highpass + hp1; bandpass = bandpass + bp1[ ch ][ i ]; input[ i ] = input[ i ] - 0.000000001; lp1[ ch ][ i ] = lp1[ ch ][ i ] + m_Freq * bp1[ ch ][ i ]; hp1 = input[ i ] - lp1[ ch ][ i ] - rez * bp1[ ch ][ i ]; bp1[ ch ][ i ] = m_Freq * hp1 + bp1[ ch ][ i ]; lowpass = (lowpass + lp1[ ch ][ i ]) * MULTI; highpass = (highpass + hp1) * MULTI; bandpass = (bandpass + bp1[ ch ][ i ]) * MULTI; out[ i ] = ( highpass * m_hpIn[ ch ] ) + ( lowpass * m_lpIn[ ch ] ) + ( bandpass * m_mpIn[ ch ] ); } *pL = clamp( out[ L ] * AUDIO_MAX, -AUDIO_MAX, AUDIO_MAX ); *pR = clamp( out[ R ] * AUDIO_MAX, -AUDIO_MAX, AUDIO_MAX ); } //----------------------------------------------------- // Procedure: ProcessMuteSolo // //----------------------------------------------------- void Mixer_16_4_4::ProcessMuteSolo( int index, bool bMute, bool bOn ) { int i, j; bool bSoloing = false, bSoloGroup[ nGROUPS ] = {}, bSoloRoutedToGroup[ nGROUPS ] = {}; if( bMute ) { m_bMuteStates[ index ] = bOn; // turn solo off if( m_bSoloStates[ index ] ) { m_bSoloStates[ index ] = false; m_pButtonChannelSolo[ index ]->Set( false ); } // if mute is off then set volume if( m_bMuteStates[ index ] ) { m_pButtonChannelMute[ index ]->Set( true ); m_FadeState[ index ] = MUTE_FADE_STATE_DEC; } else { m_pButtonChannelMute[ index ]->Set( false ); m_FadeState[ index ] = MUTE_FADE_STATE_INC; } } else { m_bSoloStates[ index ] = bOn; // turn mute off if( m_bMuteStates[ index ] ) { m_bMuteStates[ index ] = false; m_pButtonChannelMute[ index ]->Set( false ); } // toggle solo if( !m_bSoloStates[ index ] ) { m_pButtonChannelSolo[ index ]->Set( false ); } else { m_pButtonChannelSolo[ index ]->Set( true ); } } // is a track soloing? for( i = 0; i < nCHANNELS - nAUX; i++ ) { if( m_bSoloStates[ i ] ) { bSoloing = true; if( i < nINCHANNELS ) { if( m_iRouteGroup[ i ] != nGROUPS ) bSoloRoutedToGroup[ m_iRouteGroup[ i ] ] = true; } else { bSoloGroup[ i - nINCHANNELS ] = true; } } } // somebody is soloing if( bSoloing ) { // first shut down volume of all not in solo for( i = 0; i < nCHANNELS; i++ ) { // no aux mute on solo if( i >= ( nCHANNELS - nAUX ) ) { if( m_bAuxIgnoreSolo && !m_bMuteStates[ i ] ) m_FadeState[ i ] = MUTE_FADE_STATE_INC; else m_FadeState[ i ] = MUTE_FADE_STATE_DEC; } else { if( !m_bSoloStates[ i ] ) m_FadeState[ i ] = MUTE_FADE_STATE_DEC; else m_FadeState[ i ] = MUTE_FADE_STATE_INC; } } // second, re-enable all groups that are being soloed from an input channel for( i = nINCHANNELS; i < (nINCHANNELS + nGROUPS); i++ ) { if( bSoloRoutedToGroup[ i - nINCHANNELS ] && !m_bMuteStates[ i ] ) m_FadeState[ i ] = MUTE_FADE_STATE_INC; } // third solo any input channels that are routed to a soloing group for( i = nINCHANNELS; i < (nINCHANNELS + nGROUPS); i++ ) { // if this group is soloing if( bSoloGroup[ i - nINCHANNELS ] ) { // enable output for each input channel routed to this group for( j = 0; j < nINCHANNELS; j++ ) { if( m_iRouteGroup[ j ] == ( i - nINCHANNELS ) && !m_bMuteStates[ j ] ) { m_FadeState[ j ] = MUTE_FADE_STATE_INC; } } } } } // nobody soloing and just turned solo off then enable all channels that aren't muted else //if( bSoloOff ) { // turn on everything except muted for( i = 0; i < nCHANNELS; i++ ) { // bring back if not muted if( !m_bMuteStates[ i ] ) { m_FadeState[ i ] = MUTE_FADE_STATE_INC; } else { m_FadeState[ i ] = MUTE_FADE_STATE_DEC; } } } } //----------------------------------------------------- // Procedure: step // //----------------------------------------------------- #define SNORMAL 0 #define SGROUP 1 #define SAUX 2 void Mixer_16_4_4::step() { int section = 0; int ch, aux, group = 0; float GroupMixL[ nGROUPS ] = {0}, GroupMixR[ nGROUPS ] = {0}, fMixOutL = 0.0f, fMixOutR = 0.0f, inL, inR, flevel, fx, fy, fade[ nGROUPS ]; float auxL[ nAUX ] = {}, auxR[ nAUX ] = {}; bool bChannelActive, bGroupActive[ nGROUPS ] = {false}; float pan, levelmult = 1.0; if( !m_bInitialized ) return; if( m_bGainLevelx2 ) levelmult = 1.5f; fade[ 0 ] = 1.0f; fade[ 1 ] = 1.0f; fade[ 2 ] = 1.0f; fade[ 3 ] = 1.0f; // calc XFADE if( inputs[ IN_FADEX ].active || inputs[ IN_FADEY ].active ) { fx = clamp( inputs[ IN_FADEX ].normalize( 5.0f ), 0.0f, 10.0f ) / 10.0f; fy = clamp( inputs[ IN_FADEY ].normalize( 5.0f ), 0.0f, 10.0f ) / 10.0f; if( fx > 0.5f || fy < 0.5 ) fade[ 0 ] = min( (1.0f - fx) * 2, fy * 2 ); if( fx < 0.5f || fy < 0.5 ) fade[ 1 ] = min( fx * 2, fy * 2 ); if( fx > 0.5f || fy > 0.5 ) fade[ 2 ] = min( (1.0f - fx) * 2, (1.0f - fy) * 2 ); if( fx < 0.5f || fy > 0.5 ) fade[ 3 ] = min( fx * 2, (1.0f - fy) * 2 ); } for( ch = 0; ch < nCHANNELS; ch++ ) { inL = 0.0f; inR = 0.0f; // normal(0), group(1) or aux channels(2) section if( ch == nINCHANNELS || ch == ( nINCHANNELS + nGROUPS ) ) section++; bChannelActive = false; // see if channel active if( section != SGROUP ) { if( inputs[ IN_LEFT + ch ].active || inputs[ IN_RIGHT + ch ].active ) bChannelActive = true; } else if( section == SGROUP ) { group = ch - nINCHANNELS; if( bGroupActive[ group ] ) bChannelActive = true; } if( bChannelActive ) { if( section == SNORMAL ) flevel = clamp( ( params[ PARAM_CHLVL + ch ].value * levelmult ) * ( inputs[ IN_LEVELCV + ch ].normalize( CV_MAX ) / CV_MAX ), 0.0f, levelmult ); else flevel = params[ PARAM_CHLVL + ch ].value * levelmult; if( section == SGROUP ) { // process xfade inL = GroupMixL[ group ]; inR = GroupMixR[ group ]; } else { // check right channel first for possible mono if( inputs[ IN_RIGHT + ch ].active ) { inR = inputs[ IN_RIGHT + ch ].value; m_bMono[ ch ] = false; } else m_bMono[ ch ] = true; // left channel if( inputs[ IN_LEFT + ch ].active ) { inL = inputs[ IN_LEFT + ch ].value; if( m_bMono[ ch ] ) inR = inL; } } if( m_FadeState[ ch ] == MUTE_FADE_STATE_DEC ) { m_fMuteFade[ ch ] -= FADE_MULT; if( m_fMuteFade[ ch ] <= 0.0 ) { m_fMuteFade[ ch ] = 0.0; m_FadeState[ ch ] = MUTE_FADE_STATE_IDLE; } } else if( m_FadeState[ ch ] == MUTE_FADE_STATE_INC ) { m_fMuteFade[ ch ] += FADE_MULT; if( m_fMuteFade[ ch ] >= 1.0 ) { m_fMuteFade[ ch ] = 1.0; m_FadeState[ ch ] = MUTE_FADE_STATE_IDLE; } } // attenuate for EQ ProcessEQ( ch, &inL, &inR ); inL *= 2.0f; inR *= 2.0f; if( section == SNORMAL ) pan = clamp( params[ PARAM_CHPAN + ch ].value + ( inputs[ IN_PANCV + ch ].normalize( 0.0 ) / CV_MAX ), -1.0f, 1.0f ); else pan = params[ PARAM_CHPAN + ch ].value; if( pan <= 0.0 ) inR *= ( 1.0 + pan ); else inL *= ( 1.0 - pan ); // put output to aux ( pre fader ) if( section != SAUX ) { if( m_bPreFader[ ch ] ) { for ( aux = 0; aux < nAUX; aux++ ) { auxL[ aux ] += inL * params[ PARAM_CHAUX + (ch * 4) + aux ].value; auxR[ aux ] += inR * params[ PARAM_CHAUX + (ch * 4) + aux ].value; } } } inL *= flevel; inR *= flevel; if( section == SGROUP ) { // process xfade inL *= fade[ group ]; inR *= fade[ group ]; } // mute comes before group outputs if( !m_bGroupPreMute ) { inL *= m_fMuteFade[ ch ]; inR *= m_fMuteFade[ ch ]; } // group output (pre mute) if( section == SGROUP ) { if( bGroupActive[ group ] ) { outputs[ OUT_GRPL + group ].value = clamp( inL, -AUDIO_MAX, AUDIO_MAX ); outputs[ OUT_GRPR + group ].value = clamp( inR, -AUDIO_MAX, AUDIO_MAX ); } else { outputs[ OUT_GRPL + group ].value = 0.0f; outputs[ OUT_GRPR + group ].value = 0.0f; } } // mute comes after group outputs if( m_bGroupPreMute ) { inL *= m_fMuteFade[ ch ]; inR *= m_fMuteFade[ ch ]; } // put output to aux ( post fader ) if( section != SAUX ) { if( !m_bPreFader[ ch ] ) { for ( aux = 0; aux < nAUX; aux++ ) { auxL[ aux ] += inL * params[ PARAM_CHAUX + (ch * 4) + aux ].value; auxR[ aux ] += inR * params[ PARAM_CHAUX + (ch * 4) + aux ].value; } } } // non normal input channels go directly to output if( section != SNORMAL ) { fMixOutL += inL; fMixOutR += inR; } else { // normal channel direct out if( m_iRouteGroup[ ch ] == nGROUPS ) { fMixOutL += inL; fMixOutR += inR; } // normal channel routed to group else { GroupMixL[ m_iRouteGroup[ ch ] ] += inL; GroupMixR[ m_iRouteGroup[ ch ] ] += inR; bGroupActive[ m_iRouteGroup[ ch ] ] = true; } } } if( m_pLEDMeterChannel[ ch ][ 0 ] ) m_pLEDMeterChannel[ ch ][ 0 ]->Process( inL / AUDIO_MAX ); if( m_pLEDMeterChannel[ ch ][ 1 ] ) m_pLEDMeterChannel[ ch ][ 1 ]->Process( inR / AUDIO_MAX); } // apply main level fMixOutL = clamp( fMixOutL * params[ PARAM_LEVEL_OUT ].value, -AUDIO_MAX, AUDIO_MAX ); fMixOutR = clamp( fMixOutR * params[ PARAM_LEVEL_OUT ].value, -AUDIO_MAX, AUDIO_MAX ); // update main VUMeters if( m_pLEDMeterMain[ 0 ] ) m_pLEDMeterMain[ 0 ]->Process( fMixOutL / AUDIO_MAX ); if( m_pLEDMeterMain[ 1 ] ) m_pLEDMeterMain[ 1 ]->Process( fMixOutR / AUDIO_MAX ); // put aux output for ( aux = 0; aux < nAUX; aux++ ) { outputs[ OUT_AUXL + aux ].value = clamp( auxL[ aux ] * params[ PARAM_AUXLVL + aux ].value, -AUDIO_MAX, AUDIO_MAX ); outputs[ OUT_AUXR + aux ].value = clamp( auxR[ aux ] * params[ PARAM_AUXLVL + aux ].value, -AUDIO_MAX, AUDIO_MAX ); } outputs[ OUT_MAINL ].value = fMixOutL; outputs[ OUT_MAINR ].value = fMixOutR; } //----------------------------------------------------- // Procedure: Group Pre-Mute Menu Item // //----------------------------------------------------- struct Mixer_16_4_4_GroupPreMute : MenuItem { Mixer_16_4_4 *module; void onAction(EventAction &e) override { module->m_bGroupPreMute = !module->m_bGroupPreMute; } void step() override { rightText = (module->m_bGroupPreMute) ? "✔" : ""; } }; //----------------------------------------------------- // Procedure: Level Gain x2 Menu Item // //----------------------------------------------------- struct Mixer_16_4_4_Gainx2 : MenuItem { Mixer_16_4_4 *module; void onAction(EventAction &e) override { module->m_bGainLevelx2 = !module->m_bGainLevelx2; } void step() override { rightText = (module->m_bGainLevelx2) ? "✔" : ""; } }; //----------------------------------------------------- // Procedure: AuxIgnoreSolo menu item // //----------------------------------------------------- struct Mixer_16_4_4_AuxIgnoreSolo : MenuItem { Mixer_16_4_4 *module; void onAction(EventAction &e) override { module->m_bAuxIgnoreSolo = !module->m_bAuxIgnoreSolo; // cause an update with a passive call module->ProcessMuteSolo( nINCHANNELS - nAUX, false, false ); } void step() override { rightText = (module->m_bAuxIgnoreSolo) ? "✔" : ""; } }; //----------------------------------------------------- // Procedure: createContextMenu // //----------------------------------------------------- Menu *Mixer_16_4_4_Widget::createContextMenu() { Menu *menu = ModuleWidget::createContextMenu(); Mixer_16_4_4 *mod = dynamic_cast(module); assert(mod); menu->addChild(construct()); menu->addChild(construct(&MenuLabel::text, "---- Group Outputs ----")); menu->addChild(construct( &MenuItem::text, "Pre-Mute", &Mixer_16_4_4_GroupPreMute::module, mod ) ); menu->addChild(construct(&MenuLabel::text, "---- Level Sliders ----")); menu->addChild(construct( &MenuItem::text, "Gain x1.5", &Mixer_16_4_4_Gainx2::module, mod ) ); menu->addChild(construct(&MenuLabel::text, "---- Aux Output ----")); menu->addChild(construct( &MenuItem::text, "Do Not Mute AUX when SOLOing", &Mixer_16_4_4_AuxIgnoreSolo::module, mod ) ); return menu; } } // namespace rack_plugin_mscHack using namespace rack_plugin_mscHack; RACK_PLUGIN_MODEL_INIT(mscHack, Mix_16_4_4) { Model *modelMix_16_4_4 = Model::create( "mscHack", "Mix_16_4_4", "MIXER 16ch, 4 groups, 4 aux", MIXER_TAG, EQUALIZER_TAG, PANNING_TAG, AMPLIFIER_TAG, MULTIPLE_TAG ); return modelMix_16_4_4; }