diff --git a/AS.JPG b/AS.JPG index 722d607..e59d602 100644 Binary files a/AS.JPG and b/AS.JPG differ diff --git a/Makefile b/Makefile index 2c320e1..5360faa 100755 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ RACK_DIR ?= ../.. SLUG = AS -VERSION = 0.6.9 +VERSION = 0.6.10 FLAGS += SOURCES += $(wildcard src/*.cpp freeverb/*.cpp) diff --git a/README.md b/README.md index b69c547..360ac30 100755 --- a/README.md +++ b/README.md @@ -96,11 +96,15 @@ V 0.5.5 Extendend the freq range 1 octave below. V 0.5.7 Module size reduced to 4HP +V 0.6.10 Added a base frequency switch to change from A (original and default) to C (to match the current VCV standard). + ### TinySine VCV tutorial module. Mods: graphics, proper sine wave. V 0.5.7 Module size reduced to 4HP +V 0.6.10 Added a base frequency switch to change from A (original and default) to C (to match the current VCV standard). + ### TriLFO Fundamental LFO module. Mods:graphics, controls stripped to the basics but you get 3 LFOS on the same space. @@ -189,6 +193,11 @@ CV to Trigger module. Feed a midi signal to the CV inputs and it will output one V 0.6.7: First relase of this module. +### Zero Crossing CV 2 T +Zero crossing CV to Trigger module. Each time a CV input hits 0v it will generate a trigger signal on the respective output. User request. + +V 0.6.10: First relase of this module. + ### Delay Plus Fundamental Delay module. Mods: graphics, digital display to show delay time in MS , wet signal send & return, bypass switch. diff --git a/res/SawOSC.svg b/res/SawOSC.svg index 64996fd..2a55b2c 100644 --- a/res/SawOSC.svg +++ b/res/SawOSC.svg @@ -25,20 +25,20 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="3.1789474" - inkscape:cx="40.066216" - inkscape:cy="89.33775" + inkscape:zoom="2.2623377" + inkscape:cx="-17.5" + inkscape:cy="96.680827" inkscape:document-units="mm" - inkscape:current-layer="layer2" + inkscape:current-layer="layer1" showgrid="false" units="px" inkscape:snap-bbox="true" inkscape:snap-page="true" inkscape:bbox-nodes="true" inkscape:snap-bbox-edge-midpoints="true" - inkscape:window-width="1004" - inkscape:window-height="1393" - inkscape:window-x="541" + inkscape:window-width="1419" + inkscape:window-height="1367" + inkscape:window-x="-86" inkscape:window-y="0" inkscape:window-maximized="0" /> + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + + + + + + + + + + + + + + diff --git a/res/SineOSC.svg b/res/SineOSC.svg index a946d0c..abdf2fa 100644 --- a/res/SineOSC.svg +++ b/res/SineOSC.svg @@ -26,7 +26,7 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="3.1789474" - inkscape:cx="123.11257" + inkscape:cx="65.231776" inkscape:cy="227.74834" inkscape:document-units="mm" inkscape:current-layer="layer1" @@ -38,8 +38,8 @@ inkscape:snap-bbox-edge-midpoints="true" inkscape:window-width="1943" inkscape:window-height="1385" - inkscape:window-x="272" - inkscape:window-y="31" + inkscape:window-x="480" + inkscape:window-y="24" inkscape:window-maximized="0" /> @@ -318,5 +318,73 @@ inkscape:connector-curvature="0" /> + + + + + + + + + + + + + + diff --git a/res/ZeroCV2T.svg b/res/ZeroCV2T.svg new file mode 100644 index 0000000..9f92503 --- /dev/null +++ b/res/ZeroCV2T.svg @@ -0,0 +1,549 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/AS.cpp b/src/AS.cpp index a8dcf91..75703f7 100755 --- a/src/AS.cpp +++ b/src/AS.cpp @@ -34,6 +34,7 @@ void init(rack::Plugin *p) { p->addModel(modelTriggersMKIII); p->addModel(modelBPMCalc); p->addModel(modelCv2T); + p->addModel(modelZeroCV2T); p->addModel(modelReScale); //EFFECTS diff --git a/src/AS.hpp b/src/AS.hpp index ab03c09..c14b731 100755 --- a/src/AS.hpp +++ b/src/AS.hpp @@ -36,6 +36,7 @@ extern Model *modelFlow; extern Model *modelSignalDelay; extern Model *modelBPMCalc; extern Model *modelCv2T; +extern Model *modelZeroCV2T; extern Model *modelReScale; extern Model *modelDelayPlusFx; diff --git a/src/ReScale.cpp b/src/ReScale.cpp index 4d4ff78..06fafea 100644 --- a/src/ReScale.cpp +++ b/src/ReScale.cpp @@ -131,4 +131,4 @@ ReScaleWidget::ReScaleWidget(ReScale *module) : ModuleWidget(module) { } -Model *modelReScale = Model::create("AS", "ReScale", "Voltage Converter", UTILITY_TAG); \ No newline at end of file +Model *modelReScale = Model::create("AS", "ReScale", "ReScale - Voltage Converter", UTILITY_TAG); \ No newline at end of file diff --git a/src/SawOSC.cpp b/src/SawOSC.cpp index 1463fc8..740fa71 100755 --- a/src/SawOSC.cpp +++ b/src/SawOSC.cpp @@ -9,6 +9,7 @@ struct SawOsc : Module { enum ParamIds { PITCH_PARAM, + BASE_PARAM, PW_PARAM, NUM_PARAMS }; @@ -28,6 +29,8 @@ struct SawOsc : Module { float phase = 0.0f; float blinkPhase = 0.0f; + float freq = 0.0f; + int base_freq = 0; SawOsc() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} void step() override; @@ -37,10 +40,18 @@ void SawOsc::step() { // Implement a simple sine oscillator float deltaTime = 1.0f / engineGetSampleRate(); // Compute the frequency from the pitch parameter and input + base_freq = params[BASE_PARAM].value; float pitch = params[PITCH_PARAM].value; pitch += inputs[PITCH_INPUT].value; pitch = clamp(pitch, -4.0f, 4.0f); - float freq = 440.0f * powf(2.0f, pitch); + + if(base_freq==1){ + //Note A4 + freq = 440.0f * powf(2.0f, pitch); + }else{ + // Note C4 + freq = 261.626f * powf(2.0f, pitch); + } // Accumulate the phase phase += freq * deltaTime; @@ -91,12 +102,17 @@ SawOscWidget::SawOscWidget(SawOsc *module) : ModuleWidget(module) { //LIGHT addChild(ModuleLightWidget::create>(Vec(22-15, 57), module, SawOsc::FREQ_LIGHT)); //PARAMS - //addParam(ParamWidget::create(Vec(26, 60), module, SawOsc::PITCH_PARAM, -3.0, 3.0, 0.0)); - addParam(ParamWidget::create(Vec(26-15, 60), module, SawOsc::PITCH_PARAM, -4.0, 4.0, 0.0)); + //addParam(ParamWidget::create(Vec(26, 60), module, SawOsc::PITCH_PARAM, -3.0f, 3.0f, 0.0f)); + addParam(ParamWidget::create(Vec(26-15, 60), module, SawOsc::PITCH_PARAM, -3.0f, 3.0f, 0.0f)); +//addParam(ParamWidget::create(Vec(26-15, 60), module, SawOsc::PITCH_PARAM, -4.75f, 4.75f, -0.75f)); + //addParam(ParamWidget::create(Vec(26, 125), module, SawOsc::PW_PARAM, -4.0, 5.0, -4.0)); - addParam(ParamWidget::create(Vec(26-15, 125), module, SawOsc::PW_PARAM, -4.2, 5.0, -4.2)); + addParam(ParamWidget::create(Vec(26-15, 120), module, SawOsc::PW_PARAM, -4.2f, 5.0f, -4.2f)); + + //BASE FREQ SWITCH + addParam(ParamWidget::create(Vec(18, 220), module, SawOsc::BASE_PARAM, 0.0f, 1.0f, 1.0f)); //INPUTS - addInput(Port::create(Vec(33-15, 200), Port::INPUT, module, SawOsc::PW_INPUT)); + addInput(Port::create(Vec(33-15, 180), Port::INPUT, module, SawOsc::PW_INPUT)); addInput(Port::create(Vec(33-15, 260), Port::INPUT, module, SawOsc::PITCH_INPUT)); //OUTPUTS addOutput(Port::create(Vec(33-15, 310), Port::OUTPUT, module, SawOsc::OSC_OUTPUT)); diff --git a/src/SineOSC.cpp b/src/SineOSC.cpp index 96cf1e0..920cda5 100755 --- a/src/SineOSC.cpp +++ b/src/SineOSC.cpp @@ -9,6 +9,7 @@ struct SineOsc : Module { enum ParamIds { FREQ_PARAM, + BASE_PARAM, NUM_PARAMS }; enum InputIds { @@ -27,6 +28,8 @@ struct SineOsc : Module { float phase = 0.0f; float blinkPhase = 0.0f; + float freq = 0.0f; + int base_freq = 0; SineOsc() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} void step() override; @@ -36,10 +39,18 @@ struct SineOsc : Module { void SineOsc::step() { // Implement a simple sine oscillator // Compute the frequency from the pitch parameter and input + base_freq = params[BASE_PARAM].value; float pitch = params[FREQ_PARAM].value; pitch += inputs[FREQ_CV].value; pitch = clamp(pitch, -4.0f, 4.0f); - float freq = 440.0f * powf(2.0f, pitch); + + if(base_freq==1){ + //Note A4 + freq = 440.0f * powf(2.0f, pitch); + }else{ + // Note C4 + freq = 261.626f * powf(2.0f, pitch); + } // Accumulate the phase phase += freq / engineGetSampleRate(); if (phase >= 1.0f) @@ -51,6 +62,7 @@ void SineOsc::step() { //float sine = sinf(2 * M_PI * phase)+ sinf(2 * M_PI * phase * 2)*5; //mod,like this it gives a unipolar saw-ish wave //float sine = sinf(2.0 * M_PI * (phase * 0.125)) * 5.0; + outputs[OSC_OUTPUT].value = sine; lights[FREQ_LIGHT].value = (outputs[OSC_OUTPUT].value > 0.0f) ? 1.0f : 0.0f; @@ -74,7 +86,12 @@ SineOscWidget::SineOscWidget(SineOsc *module) : ModuleWidget(module) { //LIGHT addChild(ModuleLightWidget::create>(Vec(22-15, 57), module, SineOsc::FREQ_LIGHT)); //PARAMS + //addParam(ParamWidget::create(Vec(26-15, 60), module, SineOsc::FREQ_PARAM, -3.75f, 3.75f, -0.75f)); + //addParam(ParamWidget::create(Vec(26-15, 60), module, SineOsc::FREQ_PARAM, -3.0f, 2.999934f, -0.000066f)); addParam(ParamWidget::create(Vec(26-15, 60), module, SineOsc::FREQ_PARAM, -3.0f, 3.0f, 0.0f)); + + //BASE FREQ SWITCH + addParam(ParamWidget::create(Vec(18, 220), module, SineOsc::BASE_PARAM, 0.0f, 1.0f, 1.0f)); //INPUTS addInput(Port::create(Vec(33-15, 260), Port::INPUT, module, SineOsc::FREQ_CV)); //OUTPUTS diff --git a/src/ZeroCV2T.cpp b/src/ZeroCV2T.cpp new file mode 100755 index 0000000..fe35054 --- /dev/null +++ b/src/ZeroCV2T.cpp @@ -0,0 +1,234 @@ +//************************************************************************************** +//CV to Trigger convenrter module for VCV Rack by Alfredo Santamaria - AS - https://github.com/AScustomWorks/AS +// +// +//************************************************************************************** +#include "AS.hpp" +#include "dsp/digital.hpp" + +struct ZeroCV2T : Module { + enum ParamIds { + TRIG_SWITCH_1, + TRIG_SWITCH_2, + TRIG_SWITCH_3, + TRIG_SWITCH_4, + NUM_PARAMS + }; + enum InputIds { + CV_IN_1, + CV_IN_2, + CV_IN_3, + CV_IN_4, + NUM_INPUTS + }; + enum OutputIds { + TRIG_OUT_1, + TRIG_OUT_2, + TRIG_OUT_3, + TRIG_OUT_4, + NUM_OUTPUTS + }; + enum LightIds { + TRIG_LED_1, + TRIG_LED_2, + TRIG_LED_3, + TRIG_LED_4, + NUM_LIGHTS + }; + + SchmittTrigger trig_1, trig_2, trig_3, trig_4; + + PulseGenerator trigPulse1, trigPulse2, trigPulse3, trigPulse4; + bool trig_pulse_1 = false; + bool trig_pulse_2 = false; + bool trig_pulse_3 = false; + bool trig_pulse_4 = false; + + float trigger_length = 0.0001f; + + const float lightLambda = 0.075f; + float trigLight1 = 0.0f; + float trigLight2 = 0.0f; + float trigLight3 = 0.0f; + float trigLight4 = 0.0f; + + bool cv_1_engaged = false; + bool cv_2_engaged = false; + bool cv_3_engaged = false; + bool cv_4_engaged = false; + + float current_cv_1_volts = 0.0f; + float current_cv_2_volts = 0.0f; + float current_cv_3_volts = 0.0f; + float current_cv_4_volts = 0.0f; + float trigger_treshold = 0.0005f; + + ZeroCV2T() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} + + void step() override; +}; + + +void ZeroCV2T::step() { + + //CV TRIG 1 + if ( trig_1.process( params[TRIG_SWITCH_1].value ) ) { + trigLight1 = 1.0; + trigPulse1.trigger( trigger_length ); + } + current_cv_1_volts = inputs[CV_IN_1].value; + + if ( fabs( current_cv_1_volts ) < trigger_treshold ){ + if(!cv_1_engaged){ + cv_1_engaged = true; + trigLight1 = 1.0; + trigPulse1.trigger( trigger_length ); + // send trigger + } + } else { + if ( fabs( current_cv_1_volts ) > trigger_treshold ) { + // reenable trigger + cv_1_engaged = false; + } + } + + trigLight1 -= trigLight1 / lightLambda / engineGetSampleRate(); + lights[TRIG_LED_1].value = trigLight1; + trig_pulse_1 = trigPulse1.process( 1.0 / engineGetSampleRate() ); + outputs[TRIG_OUT_1].value = ( trig_pulse_1 ? 10.0f : 0.0f ); + + //CV 2 TRIG 2 + if ( trig_2.process( params[TRIG_SWITCH_2].value ) ) { + trigLight2 = 1.0; + trigPulse2.trigger( trigger_length ); + } + current_cv_2_volts = inputs[CV_IN_2].value; + + if ( fabs( current_cv_2_volts ) < trigger_treshold ){ + if(!cv_2_engaged){ + cv_2_engaged = true; + trigLight2 = 1.0; + trigPulse2.trigger( trigger_length ); + // send trigger + } + } else { + if ( fabs( current_cv_2_volts ) > trigger_treshold ) { + // reenable trigger + cv_2_engaged = false; + } + } + + trigLight2 -= trigLight2 / lightLambda / engineGetSampleRate(); + lights[TRIG_LED_2].value = trigLight2; + trig_pulse_2 = trigPulse2.process( 1.0 / engineGetSampleRate() ); + outputs[TRIG_OUT_2].value = ( trig_pulse_2 ? 10.0f : 0.0f ); + + + //CV 2 TRIG 3 + if ( trig_3.process( params[TRIG_SWITCH_3].value ) ) { + trigLight3 = 1.0; + trigPulse3.trigger( trigger_length ); + } + current_cv_3_volts = inputs[CV_IN_3].value; + + if ( fabs( current_cv_3_volts ) < trigger_treshold ){ + if(!cv_3_engaged){ + cv_3_engaged = true; + trigLight3 = 1.0; + trigPulse3.trigger( trigger_length ); + // send trigger + } + } else { + if ( fabs( current_cv_3_volts ) > trigger_treshold ) { + // reenable trigger + cv_3_engaged = false; + } + } + + trigLight3 -= trigLight3 / lightLambda / engineGetSampleRate(); + lights[TRIG_LED_3].value = trigLight3; + trig_pulse_3 = trigPulse3.process( 1.0 / engineGetSampleRate() ); + outputs[TRIG_OUT_3].value = ( trig_pulse_3 ? 10.0f : 0.0f ); + + //CV 2 TRIG 4 + if ( trig_4.process( params[TRIG_SWITCH_4].value ) ) { + trigLight4 = 1.0; + trigPulse4.trigger( trigger_length ); + } + current_cv_4_volts = inputs[CV_IN_4].value; + + if ( fabs( current_cv_4_volts ) < trigger_treshold ){ + if(!cv_4_engaged){ + cv_4_engaged = true; + trigLight4 = 1.0; + trigPulse4.trigger( trigger_length ); + // send trigger + } + } else { + if ( fabs( current_cv_4_volts ) > trigger_treshold ) { + // reenable trigger + cv_4_engaged = false; + } + } + + trigLight4 -= trigLight4 / lightLambda / engineGetSampleRate(); + lights[TRIG_LED_4].value = trigLight4; + trig_pulse_4 = trigPulse4.process( 1.0 / engineGetSampleRate() ); + outputs[TRIG_OUT_4].value = ( trig_pulse_4 ? 10.0f : 0.0f ); + + +} + +struct ZeroCV2TWidget : ModuleWidget +{ + ZeroCV2TWidget(ZeroCV2T *module); +}; + + +ZeroCV2TWidget::ZeroCV2TWidget(ZeroCV2T *module) : ModuleWidget(module) { + + setPanel(SVG::load(assetPlugin(plugin, "res/ZeroCV2T.svg"))); + + //SCREWS - SPECIAL SPACING FOR RACK WIDTH*4 + addChild(Widget::create(Vec(0, 0))); + addChild(Widget::create(Vec(box.size.x - RACK_GRID_WIDTH, 0))); + addChild(Widget::create(Vec(0, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(Widget::create(Vec(box.size.x - RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + +const int gp_offset = 75; + //CV 2 TRIG 1 + //SWITCH & LED + addParam(ParamWidget::create(Vec(6, 101), module, ZeroCV2T::TRIG_SWITCH_1 , 0.0f, 1.0f, 0.0f)); + addChild(ModuleLightWidget::create>(Vec(6+2.2, 103.2), module, ZeroCV2T::TRIG_LED_1)); + //INPUTS + addInput(Port::create(Vec(18,60), Port::INPUT, module, ZeroCV2T::CV_IN_1)); + //OUTPUTS + addOutput(Port::create(Vec(32, 100), Port::OUTPUT, module, ZeroCV2T::TRIG_OUT_1)); + //CV 2 TRIG 2 + //SWITCH & LED + addParam(ParamWidget::create(Vec(6, 101+gp_offset*1), module, ZeroCV2T::TRIG_SWITCH_2 , 0.0f, 1.0f, 0.0f)); + addChild(ModuleLightWidget::create>(Vec(6+2.2, 103.2+gp_offset*1), module, ZeroCV2T::TRIG_LED_2)); + //INPUTS + addInput(Port::create(Vec(18,60+gp_offset*1), Port::INPUT, module, ZeroCV2T::CV_IN_2)); + //OUTPUTS + addOutput(Port::create(Vec(32, 100+gp_offset*1), Port::OUTPUT, module, ZeroCV2T::TRIG_OUT_2)); + //CV 2 TRIG 3 + //SWITCH & LED + addParam(ParamWidget::create(Vec(6, 101+gp_offset*2), module, ZeroCV2T::TRIG_SWITCH_3 , 0.0f, 1.0f, 0.0f)); + addChild(ModuleLightWidget::create>(Vec(6+2.2, 103.2+gp_offset*2), module, ZeroCV2T::TRIG_LED_3)); + //INPUTS + addInput(Port::create(Vec(18,60+gp_offset*2), Port::INPUT, module, ZeroCV2T::CV_IN_3)); + //OUTPUTS + addOutput(Port::create(Vec(32, 100+gp_offset*2), Port::OUTPUT, module, ZeroCV2T::TRIG_OUT_3)); + //CV 2 TRIG 4 + //SWITCH & LED + addParam(ParamWidget::create(Vec(6, 101+gp_offset*3), module, ZeroCV2T::TRIG_SWITCH_4 , 0.0f, 1.0f, 0.0f)); + addChild(ModuleLightWidget::create>(Vec(6+2.2, 103.2+gp_offset*3), module, ZeroCV2T::TRIG_LED_4)); + //INPUTS + addInput(Port::create(Vec(18,60+gp_offset*3), Port::INPUT, module, ZeroCV2T::CV_IN_4)); + //OUTPUTS + addOutput(Port::create(Vec(32, 100+gp_offset*3), Port::OUTPUT, module, ZeroCV2T::TRIG_OUT_4)); + +} + +Model *modelZeroCV2T = Model::create("AS", "ZeroCV2T", "Zero Crossing CV to Trigger Switch", SWITCH_TAG); \ No newline at end of file