/////////////////////////////////////////////////// // // Master Clock Module // VCV Module // // Strum 2017 // /////////////////////////////////////////////////// #include "mental.hpp" #include "dsp/digital.hpp" #include #include namespace rack_plugin_mental { struct LFOGenerator { float phase = 0.0; float pw = 0.5; float freq = 1.0; void setFreq(float freq_to_set) { freq = freq_to_set; } void step(float dt) { float deltaPhase = fminf(freq * dt, 0.5); phase += deltaPhase; if (phase >= 1.0) phase -= 1.0; } float sqr() { float sqr = phase < pw ? 1.0 : -1.0; return sqr; } }; struct MentalMasterClock : Module { enum ParamIds { TEMPO_PARAM, TIMESIGTOP_PARAM, TIMESIGBOTTOM_PARAM, RESET_BUTTON, RUN_SWITCH, NUM_PARAMS }; enum InputIds { NUM_INPUTS }; enum OutputIds { BEAT_OUT, EIGHTHS_OUT, SIXTEENTHS_OUT, BAR_OUT, NUM_OUTPUTS }; enum LightIds { RESET_LED, RUN_LED, NUM_LIGHTS }; LFOGenerator clock; SchmittTrigger eighths_trig; SchmittTrigger quarters_trig; SchmittTrigger bars_trig; SchmittTrigger run_button_trig; bool running = true; int eighths_count = 0; int quarters_count = 0; int bars_count = 0; int tempo, time_sig_top, time_sig_bottom = 0; float frequency = 2.0; int quarters_count_limit = 4; int eighths_count_limit = 2; int bars_count_limit = 16; MentalMasterClock(); void step() override; json_t *toJson() override { json_t *rootJ = json_object(); json_t *button_statesJ = json_array(); json_t *button_stateJ = json_integer((int)running); json_array_append_new(button_statesJ, button_stateJ); json_object_set_new(rootJ, "run", button_statesJ); return rootJ; } void fromJson(json_t *rootJ) override { json_t *button_statesJ = json_object_get(rootJ, "run"); if (button_statesJ) { json_t *button_stateJ = json_array_get(button_statesJ,0); if (button_stateJ) running = !!json_integer_value(button_stateJ); } } }; ///////////////////////////////////////////////////////////////////////// MentalMasterClock::MentalMasterClock() { params.resize(NUM_PARAMS); inputs.resize(NUM_INPUTS); outputs.resize(NUM_OUTPUTS); lights.resize(NUM_LIGHTS); } void MentalMasterClock::step() { if (run_button_trig.process(params[RUN_SWITCH].value)) { running = !running; } lights[RUN_LED].value = running ? 1.0 : 0.0; tempo = std::round(params[TEMPO_PARAM].value); time_sig_top = std::round(params[TIMESIGTOP_PARAM].value); time_sig_bottom = std::round(params[TIMESIGBOTTOM_PARAM].value); time_sig_bottom = std::pow(2,time_sig_bottom+1); frequency = tempo/60.0; if (params[RESET_BUTTON].value > 0.0) { eighths_count = 0; quarters_count = 0; bars_count = 0; lights[RESET_LED].value = 1.0; } else lights[RESET_LED].value = 0.0; if (!running) { eighths_count = 0; quarters_count = 0; bars_count = 0; outputs[BAR_OUT].value = 0.0; outputs[BEAT_OUT].value = 0.0; outputs[EIGHTHS_OUT].value = 0.0; outputs[SIXTEENTHS_OUT].value = 0.0; } else { if (time_sig_top == time_sig_bottom) { clock.setFreq(frequency*4); quarters_count_limit = 4; eighths_count_limit = 2; bars_count_limit = 16; } else { if (time_sig_bottom == 4) { quarters_count_limit = 4; eighths_count_limit = 2; bars_count_limit = time_sig_top * 4; clock.setFreq(frequency*4); } if (time_sig_bottom == 8) { quarters_count_limit = 4; eighths_count_limit = 2; bars_count_limit = time_sig_top * 2; clock.setFreq(frequency*4); if ((time_sig_top % 3) == 0) { quarters_count_limit = 6; eighths_count_limit = 2; bars_count_limit = (time_sig_top/3) * 6; clock.setFreq(frequency*6); } } } clock.step(1.0 / engineGetSampleRate()); outputs[SIXTEENTHS_OUT].value = 5.0 * clock.sqr(); if (eighths_trig.process(clock.sqr()) && eighths_count <= eighths_count_limit) eighths_count++; if (eighths_count >= eighths_count_limit) { eighths_count = 0; } if (eighths_count == 0) outputs[EIGHTHS_OUT].value = 5.0; else outputs[EIGHTHS_OUT].value = 0.0; if (quarters_trig.process(clock.sqr()) && quarters_count <= quarters_count_limit) quarters_count++; if (quarters_count >= quarters_count_limit) { quarters_count = 0; } if (quarters_count == 0) outputs[BEAT_OUT].value = 5.0; else outputs[BEAT_OUT].value = 0.0; if (bars_trig.process(clock.sqr()) && bars_count <= bars_count_limit) bars_count++; if (bars_count >= bars_count_limit) { bars_count = 0; } if (bars_count == 0) outputs[BAR_OUT].value = 5.0; else outputs[BAR_OUT].value = 0.0; } } //////////////////////////////////// struct NumberDisplayWidget2 : TransparentWidget { int *value; std::shared_ptr font; NumberDisplayWidget2() { font = Font::load(assetPlugin(plugin, "res/Segment7Standard.ttf")); }; void draw(NVGcontext *vg) override { // Background NVGcolor backgroundColor = nvgRGB(0x00, 0x00, 0x00); NVGcolor StrokeColor = nvgRGB(0x00, 0x47, 0x7e); nvgBeginPath(vg); nvgRoundedRect(vg, -1.0, -1.0, box.size.x+2, box.size.y+2, 4.0); nvgFillColor(vg, StrokeColor); nvgFill(vg); nvgBeginPath(vg); nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 4.0); nvgFillColor(vg, backgroundColor); nvgFill(vg); // text nvgFontSize(vg, 18); nvgFontFaceId(vg, font->handle); nvgTextLetterSpacing(vg, 2.5); std::stringstream to_display; to_display << std::setw(3) << *value; Vec textPos = Vec(6.0f, 17.0f); NVGcolor textColor = nvgRGB(0x00, 0x47, 0x7e); nvgFillColor(vg, textColor); nvgText(vg, textPos.x, textPos.y, to_display.str().c_str(), NULL); } }; ////////////////////////////////// struct MentalMasterClockWidget : ModuleWidget { MentalMasterClockWidget(MentalMasterClock *module); }; MentalMasterClockWidget::MentalMasterClockWidget(MentalMasterClock *module) : ModuleWidget(module) { setPanel(SVG::load(assetPlugin(plugin, "res/MentalMasterClock.svg"))); addParam(ParamWidget::create(Vec(2, 20), module, MentalMasterClock::TEMPO_PARAM, 40.0, 250.0, 120.0)); addParam(ParamWidget::create(Vec(2, 50), module, MentalMasterClock::TIMESIGTOP_PARAM,2.0, 15.0, 4.0)); addParam(ParamWidget::create(Vec(2, 80), module, MentalMasterClock::TIMESIGBOTTOM_PARAM,0.0, 3.0, 1.0)); addOutput(Port::create(Vec(90, 110), Port::OUTPUT, module, MentalMasterClock::BEAT_OUT)); addOutput(Port::create(Vec(90, 140), Port::OUTPUT, module, MentalMasterClock::BAR_OUT)); addOutput(Port::create(Vec(90, 170), Port::OUTPUT, module, MentalMasterClock::EIGHTHS_OUT)); addOutput(Port::create(Vec(90, 200), Port::OUTPUT, module, MentalMasterClock::SIXTEENTHS_OUT)); /* addParam(ParamWidget::create(Vec(5, 50+group_offset*i), module, MentalMasterClock::STEP_SWITCH + i, 0.0, 1.0, 0.0)); addChild(ModuleLightWidget::create>(Vec(10, 55+group_offset*i), &module->button_leds[0][i])); addParam(ParamWidget::create(Vec(5, 75+group_offset*i), module, MentalMasterClock::BI_SWITCH + i, 0.0, 1.0, 0.0)); addChild(ModuleLightWidget::create>(Vec(10, 80+group_offset*i), &module->button_leds[2][i])); */ //addChild(ModuleLightWidget::create>(Vec(33, 125), module, MentalClockDivider::LIGHTS)); addParam(ParamWidget::create(Vec(5, 140), module, MentalMasterClock::RESET_BUTTON, 0.0, 1.0, 0.0)); addChild(ModuleLightWidget::create>(Vec(10, 145), module, MentalMasterClock::RESET_LED)); addParam(ParamWidget::create(Vec(5, 110), module, MentalMasterClock::RUN_SWITCH, 0.0, 1.0, 0.0)); addChild(ModuleLightWidget::create>(Vec(10, 115), module, MentalMasterClock::RUN_LED)); NumberDisplayWidget2 *display = new NumberDisplayWidget2(); display->box.pos = Vec(35,20); display->box.size = Vec(50, 20); display->value = &module->tempo; addChild(display); NumberDisplayWidget2 *display2 = new NumberDisplayWidget2(); display2->box.pos = Vec(35,50); display2->box.size = Vec(50, 20); display2->value = &module->time_sig_top; addChild(display2); NumberDisplayWidget2 *display3 = new NumberDisplayWidget2(); display3->box.pos = Vec(35,80); display3->box.size = Vec(50, 20); display3->value = &module->time_sig_bottom; addChild(display3); } } // namespace rack_plugin_mental using namespace rack_plugin_mental; RACK_PLUGIN_MODEL_INIT(mental, MentalMasterClock) { Model *modelMentalMasterClock = Model::create("mental", "MentalMasterClock", "Master Clock", CLOCK_TAG); return modelMentalMasterClock; }