#include "mscHack.hpp" #include "dsp/digital.hpp" namespace rack_plugin_mscHack { //----------------------------------------------------- // General Definition //----------------------------------------------------- #define nCHANNELS 2 //----------------------------------------------------- // filter //----------------------------------------------------- enum FILTER_TYPES { FILTER_OFF, FILTER_LP, FILTER_HP, FILTER_BP, FILTER_NT }; typedef struct { int type; float basef, q, f, qmod, fmod; float lp1, bp1; }FILTER_STRUCT; //----------------------------------------------------- // Morph oscillator //----------------------------------------------------- #define nMORPH_WAVES 2 enum MOD_TYPES { MOD_LEVEL, MOD_REZ, nMODS }; #define nBITS 2048 typedef struct { int state; int baud; float finc; float fcount; float fout; int bits[ nBITS ]; }DIGITAL_OSC_STRUCT; #define OSC2_NOTES 5 //----------------------------------------------------- // Module Definition // //----------------------------------------------------- struct Alienz : Module { enum ParamIds { PARAM_SPEED, nPARAMS }; enum InputIds { IN_RANDTRIG, IN_GATE, nINPUTS }; enum OutputIds { OUT, nOUTPUTS }; enum LightIds { nLIGHTS }; enum FADE_STATE { FADE_IDLE, FADE_OUT, FADE_IN, }; bool m_bInitialized = false; CLog lg; // Contructor Alienz() : Module(nPARAMS, nINPUTS, nOUTPUTS, nLIGHTS){} Label *m_pTextLabel = NULL; Label *m_pTextLabel2 = NULL; // osc DIGITAL_OSC_STRUCT m_bitosc = {}; EnvelopeData m_osc2; float m_osc2notes[ OSC2_NOTES ]; float m_osc2freq; // modulation envelopes EnvelopeData m_mod[ nCHANNELS ][ nMODS ] = {}; float m_fval[ nCHANNELS ][ nMODS ] = {}; float m_finc[ nCHANNELS ][ nMODS ] = {}; FILTER_STRUCT m_filter[ nCHANNELS ]={}; // random seed SchmittTrigger m_SchmitTrigRand; MyLEDButton *m_pButtonSeed[ 32 ] = {}; MyLEDButton *m_pButtonRand = NULL; int m_Seed = 0; int m_FadeState = FADE_IN; float m_fFade = 0.0f; float speeds[ 9 ] = { 0.1f, 0.25f, 0.50f, 0.75f, 1.0f, 1.5f, 2.0f, 4.0f, 8.0f }; //----------------------------------------------------- // MySpeed_Knob //----------------------------------------------------- struct MySpeed_Knob : Knob_Yellow3_20_Snap { Alienz *mymodule; char strVal[ 10 ] = {}; void onChange( EventChange &e ) override { mymodule = (Alienz*)module; if( mymodule ) { if( !mymodule->m_bInitialized ) return; sprintf( strVal, "x%.2f", mymodule->speeds[ (int)value ] ); mymodule->m_pTextLabel2->text = strVal; } RoundKnob::onChange( e ); } }; void putx( int x ); void putf( float fval ); int getseed( void ); void putseed( int seed ); void ChangeSeedPending( int seed ); void BuildWave( int ch ); void BuildDrone( void ); void RandWave( EnvelopeData *pEnv, float min=0.0f, float max= 1.0f ); void RandPresetWaveAdjust( EnvelopeData *pEnv ); // audio void ChangeFilterCutoff( int ch, float f ); void processFilter( int ch, float *pIn ); void processReverb( float In, float *pL, float *pR ); // 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; }; //----------------------------------------------------- // Alienz_SeedButton //----------------------------------------------------- void Alienz_SeedButton( void *pClass, int id, bool bOn ) { Alienz *mymodule; mymodule = (Alienz*)pClass; mymodule->ChangeSeedPending( mymodule->getseed() ); } //----------------------------------------------------- // Alienz_RandButton //----------------------------------------------------- void Alienz_RandButton( void *pClass, int id, bool bOn ) { Alienz *mymodule; mymodule = (Alienz*)pClass; mymodule->ChangeSeedPending( (int)randomu32() ); } //----------------------------------------------------- // Procedure: Widget // //----------------------------------------------------- struct Alienz_Widget : ModuleWidget { Alienz_Widget( Alienz *module ); }; Alienz_Widget::Alienz_Widget( Alienz *module ) : ModuleWidget(module) { int i, x, y; box.size = Vec( 15*5, 380 ); { SVGPanel *panel = new SVGPanel(); panel->box.size = box.size; panel->setBackground(SVG::load(assetPlugin(plugin, "res/Alienz.svg"))); addChild(panel); } addInput(Port::create( Vec( 10, 20 ), Port::INPUT, module, Alienz::IN_GATE ) ); addInput(Port::create( Vec( 10, 241 ), Port::INPUT, module, Alienz::IN_RANDTRIG ) ); // random button module->m_pButtonRand = new MyLEDButton( 40, 238, 25, 25, 20.0, DWRGB( 180, 180, 180 ), DWRGB( 255, 0, 0 ), MyLEDButton::TYPE_MOMENTARY, 0, module, Alienz_RandButton ); addChild( module->m_pButtonRand ); addOutput(Port::create( Vec( 48, 20 ), Port::OUTPUT, module, Alienz::OUT ) ); y = 95; x = 9; //module->lg.Open("c://users//mark//documents//rack//Alienz.txt"); for( i = 31; i >=0; i-- ) { module->m_pButtonSeed[ i ] = new MyLEDButton( x, y, 11, 11, 8.0, DWRGB( 180, 180, 180 ), DWRGB( 255, 255, 0 ), MyLEDButton::TYPE_SWITCH, i, module, Alienz_SeedButton ); addChild( module->m_pButtonSeed[ i ] ); if( i % 4 == 0 ) { y += 15; x = 9; } else { x += 15; } } addParam(ParamWidget::create( Vec( 10, 280 ), module, Alienz::PARAM_SPEED, 0.0, 8.0, 4.0 ) ); module->m_pTextLabel2 = new Label(); module->m_pTextLabel2->box.pos = Vec( 30, 280 ); module->m_pTextLabel2->text = "x1.00"; addChild( module->m_pTextLabel2 ); module->m_pTextLabel = new Label(); module->m_pTextLabel->box.pos = Vec( 0, 213 ); module->m_pTextLabel->text = "----"; addChild( module->m_pTextLabel ); addChild(Widget::create(Vec(30, 0))); addChild(Widget::create(Vec(30, 365))); module->putseed( (int)randomu32() ); module->BuildDrone(); } //----------------------------------------------------- // Procedure: JsonParams // //----------------------------------------------------- void Alienz::JsonParams( bool bTo, json_t *root) { JsonDataInt( bTo, "m_Seed", root, &m_Seed, 1 ); } //----------------------------------------------------- // Procedure: toJson // //----------------------------------------------------- json_t *Alienz::toJson() { json_t *root = json_object(); if( !root ) return NULL; JsonParams( TOJSON, root ); return root; } //----------------------------------------------------- // Procedure: fromJson // //----------------------------------------------------- void Alienz::fromJson( json_t *root ) { //char strVal[ 10 ] = {}; JsonParams( FROMJSON, root ); ChangeSeedPending( m_Seed ); //sprintf( strVal, "x%.2f", speeds[ (int)params[ PARAM_SPEED ].value ] ); //m_pTextLabel2->text = strVal; } //----------------------------------------------------- // Procedure: onCreate // //----------------------------------------------------- void Alienz::onCreate() { } //----------------------------------------------------- // Procedure: onDelete // //----------------------------------------------------- void Alienz::onDelete() { } //----------------------------------------------------- // Procedure: onReset // //----------------------------------------------------- void Alienz::onReset() { } //----------------------------------------------------- // Procedure: onRandomize // //----------------------------------------------------- void Alienz::onRandomize() { ChangeSeedPending( (int)randomu32() ); } //----------------------------------------------------- // Procedure: getseed // //----------------------------------------------------- int Alienz::getseed( void ) { int seed = 0, shift= 1;; for( int i = 0; i < 32; i++ ) { if( m_pButtonSeed[ i ]->m_bOn ) seed |= shift; shift<<=1; } return seed; } //----------------------------------------------------- // Procedure: putseed // //----------------------------------------------------- void Alienz::putseed( int seed ) { m_Seed = seed; init_rand( seed ); putx( seed ); for( int i = 0; i < 32; i++ ) { m_pButtonSeed[ i ]->Set( (bool)(seed & 1) ); seed>>=1; } } //----------------------------------------------------- // Procedure: ChangeSeedPending // //----------------------------------------------------- void Alienz::ChangeSeedPending( int seed ) { m_FadeState = FADE_OUT; putseed( seed ); } //----------------------------------------------------- // Procedure: RandWave //----------------------------------------------------- void Alienz::RandWave( EnvelopeData *pEnv, float min, float max ) { int i; for( i = 0; i < ENVELOPE_HANDLES - 1; i++ ) pEnv->setVal( i, frand_mm( min, max ) ); pEnv->setVal( i, pEnv->m_HandleVal[ 0 ] ); } //----------------------------------------------------- // Procedure: RandPresetWaveAdjust //----------------------------------------------------- void Alienz::RandPresetWaveAdjust( EnvelopeData *pEnv ) { int i; float fval; if( frand_perc( 25.0f ) ) { RandWave( pEnv, 0.0f, 1.0f ); } else { //pEnv->Preset( (int)frand_mm( 2.0f, 7.25f) ); pEnv->Preset( EnvelopeData::PRESET_SIN ); for( i = 0; i < ENVELOPE_HANDLES - 1; i++ ) { fval = clamp( pEnv->m_HandleVal[ i ] + frand_mm( -0.01f, 0.01f ), -1.0f, 1.0f ); pEnv->setVal( i, fval ); } } } //----------------------------------------------------- // Procedure: BuildWave // //----------------------------------------------------- void Alienz::BuildWave( int ch ) { // modulation waveforms m_mod[ ch ][ MOD_LEVEL ].Init( EnvelopeData::MODE_LOOP, EnvelopeData::RANGE_0to1, false, 1.0f ); m_finc[ ch ][ MOD_LEVEL ] = 1.0f / frand_mm( 14.5f, 38.0f ); if( ch == 0 ) RandWave( &m_mod[ ch ][ MOD_LEVEL ], 0.8f, 0.9f ); else RandWave( &m_mod[ ch ][ MOD_LEVEL ], 0.1f, 0.4f ); m_mod[ ch ][ MOD_REZ ].Init( EnvelopeData::MODE_LOOP, EnvelopeData::RANGE_0to1, false, 1.0f ); m_finc[ ch ][ MOD_REZ ] = 1.0f / frand_mm( 14.5f, 38.0f ); RandWave( &m_mod[ ch ][ MOD_REZ ], 0.9f, 1.0f ); } //----------------------------------------------------- // Procedure: BuildDrone // //----------------------------------------------------- int bauds[ 4 ] = { 2400, 4800, 9600, 19200 }; void Alienz::BuildDrone( void ) { int ch, i; int byte; int bit; init_rand( m_Seed ); for( ch = 0; ch < nCHANNELS; ch++ ) { BuildWave( ch ); ChangeFilterCutoff( ch, frand_mm( 0.1, 0.5 ) ); } //set up osc m_bitosc.baud = bauds[ srand() & 3 ]; m_bitosc.finc = engineGetSampleRate() / frand_mm( 100.0f, 400.0f ); m_bitosc.fcount = 0.0f; m_bitosc.fout = 0.0f; for( i= 0; i < nBITS; i+=8 ) { byte = (int)frand_mm( (float)0x20, (float)0x80 ); for( bit = 0; bit < 8; bit++ ) { m_bitosc.bits[ i + bit ] = (byte >> bit) & 1; } } // osc 2 m_osc2.Init( EnvelopeData::MODE_LOOP, EnvelopeData::RANGE_n1to1, false, 1.0f ); m_osc2.Preset( EnvelopeData::PRESET_TRI_FULL ); for( i = 0; i < OSC2_NOTES; i++ ) m_osc2notes[ i ] = frand_mm( 3.0f, 6.0f ); m_osc2freq = engineGetSampleRate() / frand_mm( 60.0f, 90.0f ); m_bInitialized = true; } //----------------------------------------------------- // Procedure: putf // //----------------------------------------------------- void Alienz::putf( float fval ) { char strVal[ 10 ] = {}; sprintf( strVal, "%.3f", fval ); m_pTextLabel->text = strVal; } //----------------------------------------------------- // Procedure: putf // //----------------------------------------------------- void Alienz::putx( int x ) { char strVal[ 10 ] = {}; sprintf( strVal, "%.8X", x ); m_pTextLabel->text = strVal; } //----------------------------------------------------- // Procedure: ChangeFilterCutoff // //----------------------------------------------------- void Alienz::ChangeFilterCutoff( int ch, float f ) { float fx, fx2, fx3, fx5, fx7, cutfreq; FILTER_STRUCT *pf; pf = &m_filter[ ch ]; cutfreq = f; // 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; pf->f = 2.0 * (fx - (fx3 * 0.16666666666666666666666666666667) + (fx5 * 0.0083333333333333333333333333333333) - (fx7 * 0.0001984126984126984126984126984127)); } //----------------------------------------------------- // Procedure: Filter // //----------------------------------------------------- #define MULTI (0.33333333333333333333333333333333f) void Alienz::processFilter( int ch, float *pIn ) { float rez, hp1; float input, lowpass, bandpass, highpass; FILTER_STRUCT *pf; rez = 1.0 - m_fval[ ch ][ MOD_REZ ]; pf = &m_filter[ ch ]; input = *pIn; input = input + 0.000000001; pf->lp1 = pf->lp1 + pf->f * pf->bp1; hp1 = input - pf->lp1 - rez * pf->bp1; pf->bp1 = pf->f * hp1 + pf->bp1; lowpass = pf->lp1; highpass = hp1; bandpass = pf->bp1; pf->lp1 = pf->lp1 + pf->f * pf->bp1; hp1 = input - pf->lp1 - rez * pf->bp1; pf->bp1 = pf->f * hp1 + pf->bp1; lowpass = lowpass + pf->lp1; highpass = highpass + hp1; bandpass = bandpass + pf->bp1; input = input - 0.000000001; pf->lp1 = pf->lp1 + pf->f * pf->bp1; hp1 = input - pf->lp1 - rez * pf->bp1; pf->bp1 = pf->f * hp1 + pf->bp1; lowpass = (lowpass + pf->lp1) * MULTI; highpass = (highpass + hp1) * MULTI; bandpass = (bandpass + pf->bp1) * MULTI; /*switch( pf->type ) { case FILTER_LP: out = lowpass; break; case FILTER_HP: out = highpass; break; case FILTER_BP: out = bandpass; break; case FILTER_NT: out = lowpass + highpass; break; default: return; }*/ *pIn = lowpass; } //----------------------------------------------------- // Procedure: step // //----------------------------------------------------- void Alienz::step() { float In = 0.0f, fout; int i, ch; static int bcount = 0, note = 0, fcount = 0; if( !m_bInitialized ) return; // randomize trigger if( m_SchmitTrigRand.process( inputs[ IN_RANDTRIG ].normalize( 0.0f ) ) ) { m_pButtonRand->Set( true ); ChangeSeedPending( (int)randomu32() ); } switch( m_FadeState ) { case FADE_OUT: m_fFade -= 0.0005f; if( m_fFade <= 0.0f ) { m_fFade = 0.0f; BuildDrone(); m_FadeState = FADE_IN; } break; case FADE_IN: m_fFade += 0.0005f; if( m_fFade >= 1.0f ) { m_fFade = 1.0f; m_FadeState = FADE_IDLE; } break; case FADE_IDLE: default: break; } if( inputs[ IN_GATE ].active ) { if( inputs[ IN_GATE ].value < 0.000001 ) { outputs[ OUT ].value= 0.0f; return; } } // process oscillators for( ch = 0; ch < nCHANNELS; ch++ ) { // process modulation waves for( i = 0; i < nMODS; i++ ) { m_mod[ ch ][ i ].m_Clock.syncInc = m_finc[ ch ][ i ] * speeds[ (int)params[ PARAM_SPEED ].value ]; m_fval[ ch ][ i ] = m_mod[ ch ][ i ].procStep( false, false ); } } m_bitosc.fcount -= 1.0f; // change if( m_bitosc.fcount <= 0.0f ) { m_bitosc.fcount += m_bitosc.finc; if( m_bitosc.state == 2 ) m_bitosc.state = m_bitosc.bits[ ( bcount++ ) & (nBITS - 1) ]; else m_bitosc.state = 2; switch( m_bitosc.state ) { case 0: m_bitosc.fout = -0.7f; break; case 1: m_bitosc.fout = 0.7f; break; case 2: m_bitosc.fout = 0.0f; break; } } In = m_bitosc.fout; processFilter( 0, &In ); // osc2 fcount -= 1.0f; // change if( fcount <= 0.0f ) { fcount += m_osc2freq; if( ++note >= OSC2_NOTES ) note = 0; m_osc2.m_Clock.syncInc = 32.7032f * clamp( powf( 2.0f, m_osc2notes[ note ] ), 0.0f, 4186.01f ); } fout = m_osc2.procStep( false, false ); processFilter( 1, &fout ); fout *= AUDIO_MAX * m_fval[ 1 ][ MOD_LEVEL ]; fout += In * m_fval[ 0 ][ MOD_LEVEL ] * AUDIO_MAX; outputs[ OUT ].value = fout * m_fFade; } } // namespace rack_plugin_mscHack using namespace rack_plugin_mscHack; RACK_PLUGIN_MODEL_INIT(mscHack, Alienz) { Model *modelAlienz = Model::create( "mscHack", "Alienz", "Alienz module", OSCILLATOR_TAG, MULTIPLE_TAG ); return modelAlienz; }