computerscare-vcv-modules

ComputerScare modules for VCV Rack
Log | Files | Refs

ComputerscareTolyPools-v2.cpp (8718B)


      1 #include "Computerscare.hpp"
      2 #include "dtpulse.hpp"
      3 
      4 struct ComputerscareTolyPoolsV2;
      5 
      6 /*
      7 Input:
      8 
      9 first rotate
     10 knob, CV
     11 
     12 numChannels select (auto)
     13 knob,cv
     14 
     15 
     16 input:
     17 0123456789abcdef
     18 
     19 want:
     20 3456
     21 
     22 rotate 4,clip 4
     23 
     24 */
     25 
     26 
     27 struct ComputerscareTolyPoolsV2 : Module {
     28 	int counter = 83910;
     29 	int numOutputChannelsControlValue = 0;
     30 	int numOutputChannels = 1;
     31 	int rotation = 0;
     32 
     33 	int knobRotation=0;
     34 	int numChannelsKnob = 0;
     35 
     36 	int numInputChannels = 1;
     37 
     38 	int rotationModeEnum=0;
     39 	int rotationBase = 16;
     40 
     41 	ComputerscareSVGPanel* panelRef;
     42 	enum ParamIds {
     43 		ROTATE_KNOB,
     44 		NUM_CHANNELS_KNOB,
     45 		AUTO_CHANNELS_SWITCH,
     46 		NUM_PARAMS
     47 
     48 	};
     49 	enum InputIds {
     50 		POLY_INPUT,
     51 		ROTATE_CV,
     52 		NUM_CHANNELS_CV,
     53 		NUM_INPUTS
     54 	};
     55 	enum OutputIds {
     56 		POLY_OUTPUT,
     57 		NUM_CHANNELS_OUTPUT,
     58 		NUM_OUTPUTS
     59 	};
     60 	enum LightIds {
     61 		NUM_LIGHTS
     62 	};
     63 
     64 
     65 	ComputerscareTolyPoolsV2()  {
     66 
     67 		config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
     68 
     69 		configParam(ROTATE_KNOB, -16.f, 16.f, 0.f, "Rotation Offset", " channels");
     70 
     71 		configParam<AutoParamQuantity>(NUM_CHANNELS_KNOB, 0.f, 16.f, 0.f, "Number of Output Channels Offset");
     72 
     73 		configInput(POLY_INPUT, "Main");
     74 		configInput(ROTATE_CV, "Rotation CV");
     75 		configInput(NUM_CHANNELS_CV, "Number of Channels CV");
     76 
     77 		configOutput(POLY_OUTPUT, "Main");
     78 		configOutput(NUM_CHANNELS_OUTPUT, "Number of Input Channels");
     79 	}
     80 	void process(const ProcessArgs &args) override {
     81 		counter++;
     82 		
     83 		int cvRotation = 0;
     84 		int cvOutputChannels = 0;
     85 
     86 		int finalPositiveRotation = 0;
     87 
     88 		bool inputIsConnected = inputs[POLY_INPUT].isConnected();
     89 
     90 		if (counter > 982) {
     91 			counter = 0;
     92 			numChannelsKnob = params[NUM_CHANNELS_KNOB].getValue();
     93 			knobRotation = (int) round(params[ROTATE_KNOB].getValue());
     94 		}
     95 		
     96 		if(inputIsConnected) {
     97 			numInputChannels = inputs[POLY_INPUT].getChannels();
     98 		} else {
     99 			numInputChannels = 0;
    100 		}
    101 
    102 		if (inputs[NUM_CHANNELS_CV].isConnected()) {
    103 			cvOutputChannels = (int) round(inputs[NUM_CHANNELS_CV].getVoltage(0)*1.6f);
    104 		}
    105 		if (inputs[ROTATE_CV].isConnected()) {
    106 			cvRotation = (int) round(inputs[ROTATE_CV].getVoltage(0) * 1.6f);
    107 		}
    108 
    109 		rotation = knobRotation+cvRotation;
    110 
    111 		numOutputChannelsControlValue = math::clamp(cvOutputChannels+numChannelsKnob,0,16);
    112 
    113 
    114 		if(numOutputChannelsControlValue == 0) {
    115 			numOutputChannels = inputIsConnected ? numInputChannels : 1;
    116 		} else {
    117 			numOutputChannels = numOutputChannelsControlValue;
    118 		}
    119 
    120 		outputs[POLY_OUTPUT].setChannels(numOutputChannels);
    121 		outputs[NUM_CHANNELS_OUTPUT].setVoltage(mapChannelCountToVoltage(numInputChannels));
    122 
    123 
    124 
    125 		if(rotationModeEnum == 0) {
    126 			rotationBase = inputIsConnected ? numInputChannels : 16; // so when unconnected, the rotation knob illustrates the normal range
    127 		} else if(rotationModeEnum == 1) {
    128 			rotationBase = std::max(numOutputChannels,numInputChannels);
    129 		} else if(rotationModeEnum == 2) {
    130 			rotationBase = 16;
    131 		}
    132 
    133 		if(rotation > 0) {
    134 			finalPositiveRotation = rotation % rotationBase;
    135 		} else if(rotation < 0) {
    136 			/*
    137 				 eg: rotationBase=16, rotation = -21
    138 				 positiveRotation = 16-(21 % 16) = 16 - (5) = +11
    139 			*/
    140 			finalPositiveRotation = rotationBase - ((-rotation) % rotationBase);
    141 		}
    142 
    143 
    144 		if(inputs[POLY_INPUT].isConnected() && outputs[POLY_OUTPUT].isConnected()) {
    145 			for (int i = 0; i < numOutputChannels; i++) {
    146 				outputs[POLY_OUTPUT].setVoltage(inputs[POLY_INPUT].getVoltage((i + finalPositiveRotation) % rotationBase), i);
    147 			}
    148 		} else {
    149 			for (int i = 0; i < numOutputChannels; i++) {
    150 				outputs[POLY_OUTPUT].setVoltage(0.f, i);
    151 			}
    152 		}
    153 	}
    154 
    155 	json_t *dataToJson() override {
    156 		json_t *rootJ = json_object();
    157 		json_object_set_new(rootJ, "rotationModeEnum", json_integer(rotationModeEnum));
    158 		return rootJ;
    159 	}
    160 
    161 	void dataFromJson(json_t *rootJ) override {
    162 		json_t *rotationModeJ = json_object_get(rootJ, "rotationModeEnum");
    163 
    164 		if (rotationModeJ) {
    165 			rotationModeEnum = json_integer_value(rotationModeJ);
    166 		}
    167 	}
    168 
    169 };
    170 struct PoolsSmallDisplayV2 : SmallLetterDisplay
    171 {
    172 	ComputerscareTolyPoolsV2 *module;
    173 	int ch;
    174 	int type = 0;
    175 	PoolsSmallDisplayV2(int someType)
    176 	{
    177 		type = someType;
    178 		SmallLetterDisplay();
    179 	};
    180 	void draw(const DrawArgs &args)
    181 	{
    182 		//this->setNumDivisionsString();
    183 		if (module)
    184 		{
    185 
    186 			if (type == 0) {
    187 				if(module->numOutputChannelsControlValue == 0) {
    188 					value = "A"; //Automatic - output channels match input channels
    189 				} else {
    190 					value = std::to_string(module->numOutputChannelsControlValue);
    191 				}
    192 				
    193 			}
    194 			else if (type == 1) {
    195 
    196 				//keep the displayed knob value between -15 and +15
    197 				int rotationDisplay = 0;
    198 				if(module->rotation > 0) {
    199 					rotationDisplay = module->rotation % module->rotationBase;
    200 				} else if(module->rotation < 0) {
    201 					rotationDisplay = -1*( (-1* module->rotation) % module->rotationBase);
    202 				}
    203 
    204 				value = std::to_string(rotationDisplay);
    205 			}
    206 			else if (type == 2) {
    207 				value = std::to_string(module->numInputChannels);
    208 			}
    209 
    210 		}
    211 		else {
    212 			value = std::to_string((random::u32() % 16) + 1);
    213 		}
    214 		SmallLetterDisplay::draw(args);
    215 	}
    216 
    217 };
    218 
    219 struct ComputerscareTolyPoolsWidgetV2 : ModuleWidget {
    220 	ComputerscareTolyPoolsWidgetV2(ComputerscareTolyPoolsV2 *module) {
    221 
    222 		setModule(module);
    223 		box.size = Vec(4 * 15, 380);
    224 		{
    225 			ComputerscareSVGPanel *panel = new ComputerscareSVGPanel();
    226 			panel->box.size = box.size;
    227 			panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscareTolyPoolsPanel.svg")));
    228 
    229 			addChild(panel);
    230 
    231 		}
    232 
    233 
    234 		addInput(createInput<InPort>(Vec(1	, 50), module, ComputerscareTolyPoolsV2::POLY_INPUT));
    235 		poolsSmallDisplay = new PoolsSmallDisplayV2(2);
    236 		poolsSmallDisplay->box.size = Vec(14, 20);
    237 		poolsSmallDisplay->box.pos = Vec(-3 , 80);
    238 		poolsSmallDisplay->fontSize = 22;
    239 		poolsSmallDisplay->textAlign = 18;
    240 		poolsSmallDisplay->breakRowWidth = 20;
    241 		poolsSmallDisplay->module = module;
    242 		addChild(poolsSmallDisplay);
    243 
    244 
    245 		addLabeledKnob("Num Output Channels", 10, 156, module, ComputerscareTolyPoolsV2::NUM_CHANNELS_KNOB, -14, -24, 0);
    246 		addInput(createInput<InPort>(Vec(10, 186), module, ComputerscareTolyPoolsV2::NUM_CHANNELS_CV));
    247 
    248 		addLabeledKnob("Rotation", 10, 256, module, ComputerscareTolyPoolsV2::ROTATE_KNOB, -13, -5, 1);
    249 		addInput(createInput<InPort>(Vec(10, 286), module, ComputerscareTolyPoolsV2::ROTATE_CV));
    250 
    251 
    252 		addOutput(createOutput<OutPort>(Vec(28, 30), module, ComputerscareTolyPoolsV2::POLY_OUTPUT));
    253 
    254 		addOutput(createOutput<PointingUpPentagonPort>(Vec(31, 76), module, ComputerscareTolyPoolsV2::NUM_CHANNELS_OUTPUT));
    255 	}
    256 	void addLabeledKnob(std::string label, int x, int y, ComputerscareTolyPoolsV2 *module, int index, float labelDx, float labelDy, int type) {
    257 
    258 		poolsSmallDisplay = new PoolsSmallDisplayV2(type);
    259 		poolsSmallDisplay->box.size = Vec(30, 20);
    260 		poolsSmallDisplay->box.pos = Vec(x - 7.5 , y + 1.f);
    261 		poolsSmallDisplay->fontSize = 22;
    262 		poolsSmallDisplay->textAlign = 18;
    263 		poolsSmallDisplay->textColor = COLOR_COMPUTERSCARE_LIGHT_GREEN;
    264 		poolsSmallDisplay->breakRowWidth = 30;
    265 		poolsSmallDisplay->module = module;
    266 
    267 
    268 		outputChannelLabel = new SmallLetterDisplay();
    269 		outputChannelLabel->box.size = Vec(5, 5);
    270 		outputChannelLabel->box.pos = Vec(x + labelDx, y - 12 + labelDy);
    271 		outputChannelLabel->fontSize = 15;
    272 		outputChannelLabel->textAlign = 1;
    273 		outputChannelLabel->breakRowWidth = 55;
    274 
    275 		outputChannelLabel->value = label;
    276 
    277 		addParam(createParam<MediumDotSnapKnob>(Vec(x, y), module, index));
    278 		addChild(poolsSmallDisplay);
    279 
    280 	}
    281 
    282 	void appendContextMenu(Menu *menu) override;
    283 
    284 
    285 	PoolsSmallDisplayV2* poolsSmallDisplay;
    286 	SmallLetterDisplay* outputChannelLabel;
    287 };
    288 struct PoolsModeItem : MenuItem {
    289 	ComputerscareTolyPoolsV2 *pools;
    290 	int modeEnum;
    291 	void onAction(const event::Action &e) override {
    292 		pools->rotationModeEnum = modeEnum;
    293 	}
    294 	void step() override {
    295 		rightText = CHECKMARK(pools->rotationModeEnum == modeEnum);
    296 		MenuItem::step();
    297 	}
    298 };
    299 
    300 	void ComputerscareTolyPoolsWidgetV2::appendContextMenu(Menu* menu) {
    301 		ComputerscareTolyPoolsV2* pools = dynamic_cast<ComputerscareTolyPoolsV2*>(this->module);
    302 		menu->addChild(construct<MenuLabel>(&MenuLabel::text, ""));
    303 
    304 		menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Rotation Mode"));
    305 		menu->addChild(construct<PoolsModeItem>(&MenuItem::text, "Repeat Input Channels", &PoolsModeItem::pools, pools, &PoolsModeItem::modeEnum, 0));
    306 		menu->addChild(construct<PoolsModeItem>(&MenuItem::text, "Rotate Through Maximum of Output, Input Channels", &PoolsModeItem::pools, pools, &PoolsModeItem::modeEnum, 1));
    307 		menu->addChild(construct<PoolsModeItem>(&MenuItem::text, "Rotate Through 16 Channels (Legacy)", &PoolsModeItem::pools, pools, &PoolsModeItem::modeEnum, 2));
    308 	
    309 	}
    310 
    311 
    312 Model *modelComputerscareTolyPoolsV2 = createModel<ComputerscareTolyPoolsV2, ComputerscareTolyPoolsWidgetV2>("computerscare-toly-pools-v2");