//*********************************************************************************************** // //Reverb Stereo module for VCV Rack by Alfredo Santamaria - AS - https://github.com/AScustomWorks/AS // //Based on code from ML_Modules by martin-lueders https://github.com/martin-lueders/ML_modules //And code from Freeverb by Jezar at Dreampoint - http://www.dreampoint.co.uk // //*********************************************************************************************** #include "AS.hpp" #include "dsp/digital.hpp" #include "../freeverb/revmodel.hpp" struct ReverbStereoFx : Module{ enum ParamIds { DECAY_PARAM, DAMP_PARAM, BLEND_PARAM, BYPASS_SWITCH, NUM_PARAMS }; enum InputIds { SIGNAL_INPUT_L, SIGNAL_INPUT_R, DECAY_CV_INPUT, DAMP_CV_INPUT, BLEND_CV_INPUT, BYPASS_CV_INPUT, NUM_INPUTS }; enum OutputIds { SIGNAL_OUTPUT_L, SIGNAL_OUTPUT_R, NUM_OUTPUTS }; enum LightIds { DECAY_LIGHT, DAMP_LIGHT, BLEND_LIGHT, BYPASS_LED, NUM_LIGHTS }; revmodel reverb; float roomsize, damp; SchmittTrigger bypass_button_trig; SchmittTrigger bypass_cv_trig; bool fx_bypass = false; float input_signal_L = 0.0f; float input_signal_R = 0.0f; float mix_value = 0.0f; float outL = 0.0f; float outR = 0.0f; float fade_in_fx = 0.0f; float fade_in_dry = 0.0f; float fade_out_fx = 1.0f; float fade_out_dry = 1.0f; const float fade_speed = 0.001f; ReverbStereoFx() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { float gSampleRate = engineGetSampleRate(); reverb.init(gSampleRate); } void step() override; void onSampleRateChange() override; json_t *toJson()override { json_t *rootJm = json_object(); json_t *statesJ = json_array(); json_t *bypassJ = json_boolean(fx_bypass); json_array_append_new(statesJ, bypassJ); json_object_set_new(rootJm, "as_FxBypass", statesJ); return rootJm; } void fromJson(json_t *rootJm)override { json_t *statesJ = json_object_get(rootJm, "as_FxBypass"); json_t *bypassJ = json_array_get(statesJ, 0); fx_bypass = !!json_boolean_value(bypassJ); } void resetFades(){ fade_in_fx = 0.0f; fade_in_dry = 0.0f; fade_out_fx = 1.0f; fade_out_dry = 1.0f; } }; void ReverbStereoFx::onSampleRateChange() { float gSampleRate = engineGetSampleRate(); reverb.init(gSampleRate); reverb.setdamp(damp); reverb.setroomsize(roomsize); reverb.setwidth(1.0f); }; void ReverbStereoFx::step() { if (bypass_button_trig.process(params[BYPASS_SWITCH].value) || bypass_cv_trig.process(inputs[BYPASS_CV_INPUT].value) ){ fx_bypass = !fx_bypass; resetFades(); } lights[BYPASS_LED].value = fx_bypass ? 1.0f : 0.0f; float wetL, wetR; wetL = wetR = 0.0f; float old_roomsize = roomsize; float old_damp = damp; input_signal_L = clamp(inputs[SIGNAL_INPUT_L].value,-10.0f,10.0f); if(!inputs[SIGNAL_INPUT_R].active){ input_signal_R = input_signal_L; }else{ input_signal_R = clamp(inputs[SIGNAL_INPUT_R].value,-10.0f,10.0f); } roomsize = clamp(params[DECAY_PARAM].value + inputs[DECAY_CV_INPUT].value / 10.0f, 0.0f, 0.88f); damp = clamp(params[DAMP_PARAM].value + inputs[DAMP_CV_INPUT].value / 10.0f, 0.0f, 1.0f); if( old_damp != damp ) reverb.setdamp(damp); if( old_roomsize != roomsize) reverb.setroomsize(roomsize); reverb.process(input_signal_L + input_signal_R, wetL, wetR); /* //original mix method, changed to work better when used with a mixer FX loop float outL = input_signal_L + wetL * clamp(params[BLEND_PARAM].value + inputs[BLEND_CV_INPUT].value / 10.0f, 0.0f, 1.0f); float outR = input_signal_R + wetR * clamp(params[BLEND_PARAM].value + inputs[BLEND_CV_INPUT].value / 10.0f, 0.0f, 1.0f); */ mix_value = clamp(params[BLEND_PARAM].value + inputs[BLEND_CV_INPUT].value / 10.0f, 0.0f, 1.0f); outL = crossfade(input_signal_L, wetL, mix_value); outR = crossfade(input_signal_R, wetR, mix_value); //check bypass switch status if (fx_bypass){ fade_in_dry += fade_speed; if ( fade_in_dry > 1.0f ) { fade_in_dry = 1.0f; } fade_out_fx -= fade_speed; if ( fade_out_fx < 0.0f ) { fade_out_fx = 0.0f; } outputs[SIGNAL_OUTPUT_L].value = ( input_signal_L * fade_in_dry ) + ( outL * fade_out_fx ); outputs[SIGNAL_OUTPUT_R].value = ( input_signal_R * fade_in_dry ) + ( outR * fade_out_fx ); }else{ fade_in_fx += fade_speed; if ( fade_in_fx > 1.0f ) { fade_in_fx = 1.0f; } fade_out_dry -= fade_speed; if ( fade_out_dry < 0.0f ) { fade_out_dry = 0.0f; } outputs[SIGNAL_OUTPUT_L].value = ( input_signal_L * fade_out_dry ) + ( outL * fade_in_fx ); outputs[SIGNAL_OUTPUT_R].value = ( input_signal_R * fade_out_dry ) + ( outR * fade_in_fx ); } lights[DECAY_LIGHT].value = clamp(params[DECAY_PARAM].value + inputs[DECAY_CV_INPUT].value / 10.0f, 0.0f, 1.0f); lights[DAMP_LIGHT].value = clamp(params[DAMP_PARAM].value + inputs[DAMP_CV_INPUT].value / 10.0f, 0.0f, 1.0f); lights[BLEND_LIGHT].value = clamp(params[BLEND_PARAM].value + inputs[BLEND_CV_INPUT].value / 10.0f, 0.0f, 1.0f); } struct ReverbStereoFxWidget : ModuleWidget { ReverbStereoFxWidget(ReverbStereoFx *module); }; ReverbStereoFxWidget::ReverbStereoFxWidget(ReverbStereoFx *module) : ModuleWidget(module) { setPanel(SVG::load(assetPlugin(plugin, "res/ReverbStereo.svg"))); //SCREWS addChild(Widget::create(Vec(RACK_GRID_WIDTH, 0))); addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); addChild(Widget::create(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); //KNOBS addParam(ParamWidget::create(Vec(43, 60), module, ReverbStereoFx::DECAY_PARAM, 0.0f, 0.95f, 0.475f)); addParam(ParamWidget::create(Vec(43, 125), module, ReverbStereoFx::DAMP_PARAM, 0.0f, 1.0f, 0.5f)); addParam(ParamWidget::create(Vec(43, 190), module, ReverbStereoFx::BLEND_PARAM, 0.0f, 1.0f, 0.5f)); //LIGHTS addChild(ModuleLightWidget::create>(Vec(39, 57), module, ReverbStereoFx::DECAY_LIGHT)); addChild(ModuleLightWidget::create>(Vec(39, 122), module, ReverbStereoFx::DAMP_LIGHT)); addChild(ModuleLightWidget::create>(Vec(39, 187), module, ReverbStereoFx::BLEND_LIGHT)); //BYPASS SWITCH addParam(ParamWidget::create(Vec(55, 260), module, ReverbStereoFx::BYPASS_SWITCH , 0.0f, 1.0f, 0.0f)); addChild(ModuleLightWidget::create>(Vec(57.2, 262), module, ReverbStereoFx::BYPASS_LED)); /* //INS/OUTS addInput(Port::create(Vec(10, 310), Port::INPUT, module, ReverbStereoFx::SIGNAL_INPUT_L)); addOutput(Port::create(Vec(55, 310), Port::OUTPUT, module, ReverbStereoFx::SIGNAL_OUTPUT_L)); */ //INPUTS addInput(Port::create(Vec(15, 300), Port::INPUT, module, ReverbStereoFx::SIGNAL_INPUT_L)); addInput(Port::create(Vec(15, 330), Port::INPUT, module, ReverbStereoFx::SIGNAL_INPUT_R)); //OUTPUTS addOutput(Port::create(Vec(50, 300), Port::OUTPUT, module, ReverbStereoFx::SIGNAL_OUTPUT_L)); addOutput(Port::create(Vec(50, 330), Port::OUTPUT, module, ReverbStereoFx::SIGNAL_OUTPUT_R)); //CV INPUTS addInput(Port::create(Vec(10, 67), Port::INPUT, module, ReverbStereoFx::DECAY_CV_INPUT)); addInput(Port::create(Vec(10, 132), Port::INPUT, module, ReverbStereoFx::DAMP_CV_INPUT)); addInput(Port::create(Vec(10, 197), Port::INPUT, module, ReverbStereoFx::BLEND_CV_INPUT)); //BYPASS CV INPUT addInput(Port::create(Vec(10, 259), Port::INPUT, module, ReverbStereoFx::BYPASS_CV_INPUT)); } RACK_PLUGIN_MODEL_INIT(AS, ReverbStereoFx) { Model *modelReverbStereoFx = Model::create("AS", "ReverbStereoFx", "Reverb Stereo FX", REVERB_TAG, EFFECT_TAG); return modelReverbStereoFx; }