computerscare-vcv-modules

ComputerScare modules for VCV Rack
Log | Files | Refs

ComputerscareBolyPuttons.cpp (10638B)


      1 #include "Computerscare.hpp"
      2 
      3 struct ComputerscareBolyPuttons;
      4 
      5 const int numToggles = 16;
      6 
      7 struct ComputerscareBolyPuttons : ComputerscarePolyModule {
      8 	int outputRangeEnum = 0;
      9 	bool momentary = false;
     10 	bool radioMode = false;
     11 	float outputRanges[6][2];
     12 	float previousToggle[16] = {0.f};
     13 	rack::dsp::SchmittTrigger momentaryTriggers[16];
     14 	rack::dsp::PulseGenerator pulseGen[16];
     15 
     16 	ComputerscareSVGPanel* panelRef;
     17 	enum ParamIds {
     18 		TOGGLE,
     19 		POLY_CHANNELS = TOGGLE + numToggles,
     20 		MOMENTARY_MODE,
     21 		NUM_PARAMS
     22 
     23 	};
     24 	enum InputIds {
     25 		CHANNEL_INPUT,
     26 		A_INPUT,
     27 		B_INPUT,
     28 		NUM_INPUTS
     29 	};
     30 	enum OutputIds {
     31 		POLY_OUTPUT,
     32 		NUM_OUTPUTS
     33 	};
     34 	enum LightIds {
     35 		NUM_LIGHTS
     36 	};
     37 
     38 	ComputerscareBolyPuttons()  {
     39 
     40 		config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
     41 
     42 		for (int i = 0; i < numToggles; i++) {
     43 			configSwitch(TOGGLE + i, 0.f, 1.f, 0.f, "Channel " + std::to_string(i + 1), {"A", "B"});
     44 		}
     45 		configParam<AutoParamQuantity>(POLY_CHANNELS, 0.f, 16.f, 16.f, "Poly Channels");
     46 
     47 		getParamQuantity(POLY_CHANNELS)->randomizeEnabled = false;
     48 		getParamQuantity(POLY_CHANNELS)->resetEnabled = false;
     49 
     50 		configInput(A_INPUT, "A (Button Up)");
     51 		configInput(B_INPUT, "B (Button Down)");
     52 		configOutput(POLY_OUTPUT, "Main");
     53 
     54 		outputRanges[0][0] = 0.f;
     55 		outputRanges[0][1] = 10.f;
     56 		outputRanges[1][0] = -5.f;
     57 		outputRanges[1][1] = 5.f;
     58 		outputRanges[2][0] = 0.f;
     59 		outputRanges[2][1] = 5.f;
     60 		outputRanges[3][0] = 0.f;
     61 		outputRanges[3][1] = 1.f;
     62 		outputRanges[4][0] = -1.f;
     63 		outputRanges[4][1] = 1.f;
     64 		outputRanges[5][0] = -10.f;
     65 		outputRanges[5][1] = 10.f;
     66 	}
     67 	void switchOffAllButtonsButOne(int index) {
     68 		for (int i = 0; i < numToggles; i++) {
     69 			if (i != index) {
     70 				params[TOGGLE + i].setValue(0.f);
     71 			}
     72 		}
     73 	}
     74 	void checkForParamChanges() {
     75 		int changeIndex = -1;
     76 		float val;
     77 		for (int i = 0; i < numToggles; i++) {
     78 			val = params[TOGGLE + i].getValue();
     79 			if (val == 1.f && previousToggle[i] != val) {
     80 				changeIndex = i;
     81 			}
     82 			previousToggle[i] = val;
     83 		}
     84 		if (changeIndex > -1) {
     85 			switchOffAllButtonsButOne(changeIndex);
     86 		}
     87 	}
     88 	void legacyJSON(json_t *rootJ) {
     89 		json_t *outputRangeEnumJ = json_object_get(rootJ, "outputRange");
     90 		if (outputRangeEnumJ) { outputRangeEnum = json_integer_value(outputRangeEnumJ); }
     91 		json_t *radioModeJ = json_object_get(rootJ, "radioMode");
     92 		if (radioModeJ) { radioMode = json_is_true(radioModeJ); }
     93 		json_t *momentaryModeJ = json_object_get(rootJ, "momentaryMode");
     94 		if (momentaryModeJ) { momentary = json_is_true(momentaryModeJ); }
     95 	}
     96 	void dataFromJson(json_t *rootJ) override {
     97 		legacyJSON(rootJ);
     98 	}
     99 	json_t *dataToJson() override
    100 	{
    101 		json_t *rootJ = json_object();
    102 		json_object_set_new(rootJ, "outputRange", json_integer(outputRangeEnum));
    103 		json_object_set_new(rootJ, "radioMode", json_boolean(radioMode));
    104 		json_object_set_new(rootJ, "momentaryMode", json_boolean(momentary));
    105 		return rootJ;
    106 	}
    107 
    108 	void onRandomize() override {
    109 		if (momentary) {
    110 		} else {
    111 			if (radioMode) {
    112 				int rIndex = floor(random::uniform() * 16);
    113 				switchOffAllButtonsButOne(rIndex);
    114 				params[TOGGLE + rIndex].setValue(1.f);
    115 			}
    116 			else {
    117 				for (int i = 0; i < numToggles; i++) {
    118 					params[TOGGLE + i].setValue(random::uniform() < 0.5 ? 0.f : 1.f);
    119 				}
    120 			}
    121 		}
    122 	}
    123 	void checkPoly() override {
    124 		int aChannels = inputs[A_INPUT].getChannels();
    125 		int bChannels = inputs[B_INPUT].getChannels();
    126 		int knobSetting = params[POLY_CHANNELS].getValue();
    127 		if (knobSetting == 0) {
    128 			polyChannels = (aChannels <= 1 && bChannels <= 1) ? 16 : std::max(aChannels, bChannels);
    129 		}
    130 		else {
    131 			polyChannels = knobSetting;
    132 		}
    133 		outputs[POLY_OUTPUT].setChannels(polyChannels);
    134 	}
    135 	void toggleMomentary() {
    136 		momentary = !momentary;
    137 		if (momentary) {
    138 			switchOffAllButtonsButOne(-1);
    139 		}
    140 	}
    141 	void toggleRadio() {
    142 		radioMode = !radioMode;
    143 		if (radioMode) {
    144 			switchOffAllButtonsButOne(-1);
    145 		}
    146 	}
    147 	void process(const ProcessArgs &args) override {
    148 		ComputerscarePolyModule::checkCounter();
    149 
    150 		float rangeMin = outputRanges[outputRangeEnum][0];
    151 		float rangeMax = outputRanges[outputRangeEnum][1];
    152 		int numAChannels = inputs[A_INPUT].getChannels();
    153 		int numBChannels = inputs[B_INPUT].getChannels();
    154 
    155 		float min;
    156 		float max;
    157 		float spread;
    158 
    159 
    160 		if (radioMode && !momentary) {
    161 			checkForParamChanges();
    162 		}
    163 
    164 		if (numAChannels == 1) {
    165 			min = inputs[A_INPUT].getVoltage(0);
    166 		}
    167 		if (numBChannels == 1) {
    168 			max = inputs[B_INPUT].getVoltage(0);
    169 		}
    170 		for (int i = 0; i < polyChannels; i++) {
    171 			if (numAChannels != 1) {
    172 				min = i < numAChannels ? inputs[A_INPUT].getVoltage(i) : rangeMin;
    173 			}
    174 			if (numBChannels != 1) {
    175 				max = i < numBChannels ? inputs[B_INPUT].getVoltage(i) : rangeMax;
    176 			}
    177 			spread = max - min;
    178 			outputs[POLY_OUTPUT].setVoltage(params[TOGGLE + i].getValue()*spread + min, i);
    179 		}
    180 	}
    181 
    182 };
    183 
    184 struct DisableableParamWidget : SmallIsoButton {
    185 	ComputerscareBolyPuttons *module;
    186 	SmallLetterDisplay *smallLetterDisplay;
    187 	int channel;
    188 	Vec labelOffset = Vec(0, 0);
    189 	bool pressed = false;
    190 
    191 
    192 	DisableableParamWidget() {
    193 		smallLetterDisplay = new SmallLetterDisplay();
    194 		smallLetterDisplay->box.size = Vec(5, 10);
    195 		smallLetterDisplay->fontSize = 17;
    196 		smallLetterDisplay->value = "";
    197 		smallLetterDisplay->textAlign = 1;
    198 		smallLetterDisplay->box.pos = box.pos;
    199 
    200 		addChild(smallLetterDisplay);
    201 		SmallIsoButton();
    202 	}
    203 	void step() override {
    204 		if (module) {
    205 			disabled = channel > module->polyChannels - 1;
    206 			momentary = module->momentary;
    207 			pressed = module->params[channel].getValue() == 1.f;
    208 		}
    209 		else {
    210 			disabled = false;
    211 		}
    212 		SmallIsoButton::step();
    213 	}
    214 	void draw(const DrawArgs &ctx) override {
    215 		labelOffset = Vec(pressed ? 3.f : -4.f, pressed ? 7.f : 2.f);
    216 		smallLetterDisplay->value = std::to_string(channel + 1);
    217 
    218 		smallLetterDisplay->textOffset = labelOffset;
    219 		SmallIsoButton::draw(ctx);
    220 	}
    221 };
    222 
    223 struct ComputerscareBolyPuttonsWidget : ModuleWidget {
    224 	ComputerscareBolyPuttonsWidget(ComputerscareBolyPuttons *module) {
    225 
    226 		setModule(module);
    227 		box.size = Vec(4 * 15, 380);
    228 		{
    229 			ComputerscareSVGPanel *panel = new ComputerscareSVGPanel();
    230 			panel->box.size = box.size;
    231 			panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscareBolyPuttonsPanel.svg")));
    232 			addChild(panel);
    233 
    234 		}
    235 
    236 		channelWidget = new PolyOutputChannelsWidget(Vec(22, 23), module, ComputerscareBolyPuttons::POLY_CHANNELS);
    237 		addChild(channelWidget);
    238 
    239 		float xx;
    240 		float yy;
    241 		float dx;
    242 		float dy;
    243 		for (int i = 0; i < numToggles; i++) {
    244 			xx = 5.2f + 27.3 * (i - i % 8) / 8;
    245 			yy = 92 + 33.5 * (i % 8) + 14.3 * (i - i % 8) / 8;
    246 			dx = 4 - (i - i % 8) * 0.9;
    247 			dy  = 19;
    248 			addLabeledButton(std::to_string(i + 1), xx, yy, module, i, dx, dy);
    249 		}
    250 
    251 		addInput(createInput<InPort>(Vec(9, 58), module, ComputerscareBolyPuttons::A_INPUT));
    252 		addInput(createInput<PointingUpPentagonPort>(Vec(33, 55), module, ComputerscareBolyPuttons::B_INPUT));
    253 
    254 		addOutput(createOutput<PointingUpPentagonPort>(Vec(1, 24), module, ComputerscareBolyPuttons::POLY_OUTPUT));
    255 		bolyPuttons = module;
    256 	}
    257 	void addLabeledButton(std::string label, int x, int y, ComputerscareBolyPuttons *module, int index, float labelDx, float labelDy) {
    258 
    259 		DisableableParamWidget* button =  createParam<DisableableParamWidget>(Vec(x, y), module, ComputerscareBolyPuttons::TOGGLE + index);
    260 
    261 		button->module = module;
    262 		button->channel = index;
    263 		addParam(button);
    264 	}
    265 
    266 	/*void fromJson(json_t *rootJ) override
    267 	{
    268 		ModuleWidget::fromJson(rootJ);
    269 		bolyPuttons->legacyJSON(rootJ);
    270 	}*/
    271 	void appendContextMenu(Menu *menu) override;
    272 
    273 	DisableableParamWidget* button;
    274 	PolyOutputChannelsWidget* channelWidget;
    275 	ComputerscareBolyPuttons *bolyPuttons;
    276 	SmallLetterDisplay* smallLetterDisplay;
    277 };
    278 struct OutputRangeItem : MenuItem {
    279 	ComputerscareBolyPuttons *bolyPuttons;
    280 	int outputRangeEnum;
    281 	void onAction(const event::Action &e) override {
    282 		bolyPuttons->outputRangeEnum = outputRangeEnum;
    283 	}
    284 	void step() override {
    285 		rightText = CHECKMARK(bolyPuttons->outputRangeEnum == outputRangeEnum);
    286 		MenuItem::step();
    287 	}
    288 };
    289 struct RadioModeMenuItem: MenuItem {
    290 	ComputerscareBolyPuttons *bolyPuttons;
    291 	RadioModeMenuItem() {
    292 
    293 	}
    294 	void onAction(const event::Action &e) override {
    295 		bolyPuttons->toggleRadio();
    296 	}
    297 	void step() override {
    298 		rightText = bolyPuttons->radioMode ? "✔" : "";
    299 		MenuItem::step();
    300 	}
    301 };
    302 struct MomentaryModeMenuItem: MenuItem {
    303 	ComputerscareBolyPuttons *bolyPuttons;
    304 	MomentaryModeMenuItem() {
    305 
    306 	}
    307 	void onAction(const event::Action &e) override {
    308 		bolyPuttons->toggleMomentary();
    309 	}
    310 	void step() override {
    311 		rightText = bolyPuttons->momentary ? "✔" : "";
    312 		MenuItem::step();
    313 	}
    314 };
    315 
    316 
    317 void ComputerscareBolyPuttonsWidget::appendContextMenu(Menu *menu)
    318 {
    319 	ComputerscareBolyPuttons *bolyPuttons = dynamic_cast<ComputerscareBolyPuttons *>(this->module);
    320 
    321 	menu->addChild(construct<MenuLabel>(&MenuLabel::text, ""));
    322 	menu->addChild(construct<MenuLabel>(&MenuLabel::text, "How The Buttons Work"));
    323 	RadioModeMenuItem *radioMode = new RadioModeMenuItem();
    324 	radioMode->text = "Exclusive Mode (like radio buttons: only can be pressed at a time)";
    325 	radioMode->bolyPuttons = bolyPuttons;
    326 	menu->addChild(radioMode);
    327 
    328 
    329 	menu->addChild(construct<MenuLabel>(&MenuLabel::text, ""));
    330 	MomentaryModeMenuItem *momentaryMode = new MomentaryModeMenuItem();
    331 	momentaryMode->text = "Momentary (gate output while button is held)";
    332 	momentaryMode->bolyPuttons = bolyPuttons;
    333 	menu->addChild(momentaryMode);
    334 
    335 	menu->addChild(construct<MenuLabel>(&MenuLabel::text, ""));
    336 
    337 	menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Off / On Values (A ... B)"));
    338 	menu->addChild(construct<OutputRangeItem>(&MenuItem::text, "  0v ... +10v", &OutputRangeItem::bolyPuttons, bolyPuttons, &OutputRangeItem::outputRangeEnum, 0));
    339 	menu->addChild(construct<OutputRangeItem>(&MenuItem::text, " -5v ...  +5v", &OutputRangeItem::bolyPuttons, bolyPuttons, &OutputRangeItem::outputRangeEnum, 1));
    340 	menu->addChild(construct<OutputRangeItem>(&MenuItem::text, "  0v ...  +5v", &OutputRangeItem::bolyPuttons, bolyPuttons, &OutputRangeItem::outputRangeEnum, 2));
    341 	menu->addChild(construct<OutputRangeItem>(&MenuItem::text, "  0v ...  +1v", &OutputRangeItem::bolyPuttons, bolyPuttons, &OutputRangeItem::outputRangeEnum, 3));
    342 	menu->addChild(construct<OutputRangeItem>(&MenuItem::text, " -1v ...  +1v", &OutputRangeItem::bolyPuttons, bolyPuttons, &OutputRangeItem::outputRangeEnum, 4));
    343 	menu->addChild(construct<OutputRangeItem>(&MenuItem::text, "-10v ... +10v", &OutputRangeItem::bolyPuttons, bolyPuttons, &OutputRangeItem::outputRangeEnum, 5));
    344 
    345 
    346 
    347 
    348 }
    349 Model *modelComputerscareBolyPuttons = createModel<ComputerscareBolyPuttons, ComputerscareBolyPuttonsWidget>("computerscare-bolyputtons");