//*********************************************************************************************** //Impromptu Modular: Modules for VCV Rack by Marc Boulé // //Based on code from the Fundamental and AudibleInstruments plugins by Andrew Belt //and graphics from the Component Library by Wes Milholen //See ./LICENSE.txt for all licenses //See ./res/fonts/ for font licenses //*********************************************************************************************** #include "ImpromptuModular.hpp" RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, BlankPanel); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, Foundry); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, FourView); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, Tact); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, Tact1); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, TwelveKey); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, Clocked); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, MidiFile); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, PhraseSeq16); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, PhraseSeq32); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, GateSeq64); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, WriteSeq32); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, WriteSeq64); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, BigButtonSeq); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, BigButtonSeq2); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, SemiModularSynth); RACK_PLUGIN_INIT(ImpromptuModular) { RACK_PLUGIN_INIT_ID(); RACK_PLUGIN_INIT_VERSION("0.6.16"); RACK_PLUGIN_INIT_WEBSITE("https://github.com/MarcBoule/ImpromptuModular"); RACK_PLUGIN_INIT_MANUAL("https://github.com/MarcBoule/ImpromptuModular"); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, BlankPanel); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, Foundry); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, FourView); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, Tact); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, Tact1); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, TwelveKey); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, Clocked); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, MidiFile); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, PhraseSeq16); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, PhraseSeq32); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, GateSeq64); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, WriteSeq32); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, WriteSeq64); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, BigButtonSeq); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, BigButtonSeq2); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, SemiModularSynth); } void IMBigPushButtonWithRClick::onMouseDown(EventMouseDown &e) { if (e.button == 1) {// if right button (see events.hpp) maxValue = 2.0f; // Simulate MomentarySwitch::onDragStart() since not called for right clicks: setValue(maxValue); EventAction eAction; onAction(eAction); } else maxValue = 1.0f; //ParamWidget::onMouseDown(e);// don't want the reset() that is called in ParamWidget::onMouseDown(), so implement rest of that function here: e.consumed = true; e.target = this; } void IMBigPushButtonWithRClick::onMouseUp(EventMouseUp &e) { if (e.button == 1) {// if right button (see events.hpp) // Simulate MomentarySwitch::onDragEnd() since not called for right clicks: setValue(minValue); } ParamWidget::onMouseUp(e); } LEDBezelBig::LEDBezelBig() { float ratio = 2.13f; addFrame(SVG::load(assetGlobal("res/ComponentLibrary/LEDBezel.svg"))); sw->box.size = sw->box.size.mult(ratio); box.size = sw->box.size; tw = new TransformWidget(); removeChild(sw); tw->addChild(sw); addChild(tw); tw->box.size = sw->box.size; tw->scale(Vec(ratio, ratio)); } void InvisibleKeySmall::onMouseDown(EventMouseDown &e) { if (e.button == 1) {// if right button (see events.hpp) maxValue = 2.0f; // Simulate MomentarySwitch::onDragStart() since not called for right clicks: setValue(maxValue); EventAction eAction; onAction(eAction); } else maxValue = 1.0f; //ParamWidget::onMouseDown(e);// don't want the reset() that is called in ParamWidget::onMouseDown(), so implement rest of that function here: e.consumed = true; e.target = this; } void InvisibleKeySmall::onMouseUp(EventMouseUp &e) { if (e.button == 1) {// if right button (see events.hpp) // Simulate MomentarySwitch::onDragEnd() since not called for right clicks: setValue(minValue); } ParamWidget::onMouseUp(e); } void LEDButtonWithRClick::onMouseDown(EventMouseDown &e) { if (e.button == 1) {// if right button (see events.hpp) maxValue = 2.0f; // Simulate MomentarySwitch::onDragStart() since not called for right clicks: setValue(maxValue); EventAction eAction; onAction(eAction); } else maxValue = 1.0f; //ParamWidget::onMouseDown(e);// don't want the reset() that is called in ParamWidget::onMouseDown(), so implement rest of that function here: e.consumed = true; e.target = this; } void LEDButtonWithRClick::onMouseUp(EventMouseUp &e) { if (e.button == 1) {// if right button (see events.hpp) // Simulate MomentarySwitch::onDragEnd() since not called for right clicks: setValue(minValue); } ParamWidget::onMouseUp(e); } ScrewSilverRandomRot::ScrewSilverRandomRot() { float angle0_90 = randomUniform()*M_PI/2.0f; //float angle0_90 = randomUniform() > 0.5f ? M_PI/4.0f : 0.0f;// for testing tw = new TransformWidget(); addChild(tw); sw = new SVGWidget(); tw->addChild(sw); //sw->setSVG(SVG::load(assetPlugin(plugin, "res/Screw0.svg"))); sw->setSVG(SVG::load(assetGlobal("res/ComponentLibrary/ScrewSilver.svg"))); sc = new ScrewCircle(angle0_90); sc->box.size = sw->box.size; tw->addChild(sc); box.size = sw->box.size; tw->box.size = sw->box.size; tw->identity(); // Rotate SVG Vec center = sw->box.getCenter(); tw->translate(center); tw->rotate(angle0_90); tw->translate(center.neg()); } ScrewHole::ScrewHole(Vec posGiven) { box.size = Vec(16, 7); box.pos = Vec(posGiven.x, posGiven.y + 4);// nudgeX for realism, 0 = no nudge } void ScrewHole::draw(NVGcontext *vg) { NVGcolor backgroundColor = nvgRGB(0x10, 0x10, 0x10); NVGcolor borderColor = nvgRGB(0x20, 0x20, 0x20); nvgBeginPath(vg); nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 2.5f); nvgFillColor(vg, backgroundColor); nvgFill(vg); nvgStrokeWidth(vg, 1.0); nvgStrokeColor(vg, borderColor); nvgStroke(vg); } NVGcolor prepareDisplay(NVGcontext *vg, Rect *box, int fontSize) { NVGcolor backgroundColor = nvgRGB(0x38, 0x38, 0x38); NVGcolor borderColor = nvgRGB(0x10, 0x10, 0x10); nvgBeginPath(vg); nvgRoundedRect(vg, 0.0, 0.0, box->size.x, box->size.y, 5.0); nvgFillColor(vg, backgroundColor); nvgFill(vg); nvgStrokeWidth(vg, 1.0); nvgStrokeColor(vg, borderColor); nvgStroke(vg); nvgFontSize(vg, fontSize); NVGcolor textColor = nvgRGB(0xaf, 0xd2, 0x2c); return textColor; } void printNote(float cvVal, char* text, bool sharp) {// text must be at least 4 chars long (three displayed chars plus end of string) static const char noteLettersSharp[12] = {'C', 'C', 'D', 'D', 'E', 'F', 'F', 'G', 'G', 'A', 'A', 'B'}; static const char noteLettersFlat [12] = {'C', 'D', 'D', 'E', 'E', 'F', 'G', 'G', 'A', 'A', 'B', 'B'}; static const char isBlackKey [12] = { 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0 }; float cvValOffset = cvVal + 10.0f;// to properly handle negative note voltages int indexNote = clamp( (int)((cvValOffset-floor(cvValOffset)) * 12.0f + 0.5f), 0, 11); // note letter text[0] = sharp ? noteLettersSharp[indexNote] : noteLettersFlat[indexNote]; // octave number int octave = (int) roundf(floorf(cvVal)+4.0f); if (octave < 0 || octave > 9) text[1] = (octave > 9) ? ':' : '_'; else text[1] = (char) ( 0x30 + octave); // sharp/flat text[2] = ' '; if (isBlackKey[indexNote] == 1) text[2] = (sharp ? '\"' : 'b' ); text[3] = 0; } int moveIndex(int index, int indexNext, int numSteps) { if (indexNext < 0) index = numSteps - 1; else { if (indexNext - index >= 0) { // if moving right or same place if (indexNext >= numSteps) index = 0; else index = indexNext; } else { // moving left if (indexNext >= numSteps) index = numSteps - 1; else index = indexNext; } } return index; }