module.in (10897B)
1 #include "plugin.hpp" 2 #include "GEN_RACK_DISPLAY_NAME.h" 3 #include <array> 4 5 using namespace GEN_RACK_DISPLAY_NAME; 6 7 8 /// Processing 9 10 struct GEN_RACK_NAME : Module { 11 CommonState *moduleState; 12 t_sample **inputBuffers; 13 t_sample **outputBuffers; 14 15 std::array<int, 10> validBufferSizes = { 1, 4, 16, 32, 64, 128, 256, 512, 1024, 2048 }; 16 int currentBufferSize = 256; 17 int currentSampleRate = 44100; 18 19 int numParams; 20 int numInputs; 21 int numOutputs; 22 23 int count = 0; 24 25 GEN_RACK_NAME() { 26 moduleState = (CommonState *)create(currentSampleRate, currentBufferSize); 27 reset(moduleState); 28 29 numParams = num_params(); 30 numInputs = num_inputs(); 31 numOutputs = num_outputs(); 32 33 // Initialize sample buffers 34 inputBuffers = new t_sample *[numInputs]; 35 for (int i = 0; i < numInputs; i++) { 36 inputBuffers[i] = new t_sample[currentBufferSize]; 37 } 38 39 outputBuffers = new t_sample *[numOutputs]; 40 for (int i = 0; i < numOutputs; i++) { 41 outputBuffers[i] = new t_sample[currentBufferSize]; 42 } 43 44 45 // Configure parameters 46 config(numParams, numInputs + numParams, numOutputs, 0); 47 for (int i = 0; i < numParams; i++) { 48 std::string name = std::string(getparametername(moduleState, i)); 49 std::string units = std::string(getparameterunits(moduleState, i)); 50 float min = 0.0; 51 float max = 1.0; 52 if (getparameterhasminmax(moduleState, i)) { 53 min = getparametermin(moduleState, i); 54 max = getparametermax(moduleState, i); 55 } 56 configParam(i, min, max, min, name, units); 57 } 58 } 59 60 61 ~GEN_RACK_NAME() { 62 destroy(moduleState); 63 deleteBuffers(); 64 } 65 66 void deleteBuffers() { 67 for (int i = 0; i < numInputs; i++) { 68 if (inputBuffers[i]) { 69 delete inputBuffers[i]; 70 } 71 } 72 73 for (int i = 0; i < numOutputs; i++) { 74 if (outputBuffers[i]) { 75 delete outputBuffers[i]; 76 } 77 } 78 } 79 80 void setBufferSize(long bufferSize) { 81 if (bufferSize > currentBufferSize) { 82 for (int i = 0; i < numInputs; i++) { 83 if (inputBuffers[i]) { 84 delete inputBuffers[i]; 85 } 86 inputBuffers[i] = new t_sample[bufferSize]; 87 } 88 89 for (int i = 0; i < numOutputs; i++) { 90 if (outputBuffers[i]) { 91 delete outputBuffers[i]; 92 } 93 outputBuffers[i] = new t_sample[bufferSize]; 94 } 95 } 96 currentBufferSize = bufferSize; 97 moduleState->vs = currentBufferSize; 98 count = 0; 99 } 100 101 102 void process(const ProcessArgs& args) override { 103 if (args.sampleRate != currentSampleRate) { 104 moduleState->sr = args.sampleRate; 105 currentSampleRate = args.sampleRate; 106 } 107 108 if (count >= currentBufferSize) { 109 count = 0; 110 } 111 112 // Fill inputs 113 for (int i = 0; i < numInputs; i++) { 114 if (inputs[i].isConnected()) { 115 inputBuffers[i][count] = inputs[i].getVoltage() / 5.f; 116 } 117 else { 118 inputBuffers[i][count] = 0.f; 119 } 120 } 121 122 // Set output 123 for (int i = 0; i < numOutputs; i++) { 124 outputs[i].setVoltage(outputBuffers[i][count] * 5.f); 125 } 126 127 // Step forward 128 count++; 129 130 // Perform when we've filled the buffer 131 if (count == currentBufferSize) { 132 // Update any parameters 133 for (int i = 0; i < numParams; i++) { 134 // Get VCV inputs 135 float knobVal = params[i].getValue(); // Already scaled to range that genlib will understand 136 float cvVal = inputs[i + numInputs].isConnected() ? inputs[i + numInputs].getVoltage() / 5.f : 0.f; // Normalize to -1..1 137 138 // Scale to range of parameter 139 t_param min = getparametermin(moduleState, i); 140 t_param max = getparametermax(moduleState, i); 141 t_param range = fabs(max - min); 142 t_param val = clamp(knobVal + cvVal * range, min, max); // Offset the knobVal by the CV input 143 144 setparameter(moduleState, i, val, NULL); 145 } 146 147 // Fill the buffers 148 perform(moduleState, inputBuffers, numInputs, outputBuffers, numOutputs, currentBufferSize); 149 } 150 } 151 }; 152 153 154 /// Main module UI 155 156 struct BufferSizeMenuItem : MenuItem { 157 GEN_RACK_NAME* module; 158 int bufferSize; 159 void onAction(const event::Action& e) override { 160 if (!module) return; 161 module->setBufferSize(bufferSize); 162 } 163 }; 164 165 struct GEN_RACK_NAMEWidget : ModuleWidget { 166 int numParams; 167 int numInputs; 168 int numOutputs; 169 170 std::vector<std::string> inputLabels; 171 std::vector<std::string> outputLabels; 172 std::vector<std::string> paramLabels; 173 174 // Each column of ports has a certain number of "cells" that contain a port and label. 175 int ports_per_col = 6; 176 177 // Each column of params has a certain number of "cells" that contain a port, a label, and a knob. 178 int params_per_col = 4; 179 180 // Box off the actual section of "cells" with a margin 181 int l_margin = RACK_GRID_WIDTH; 182 int r_margin = RACK_GRID_WIDTH; 183 int bot_margin = 2 * RACK_GRID_WIDTH; 184 // The title and top margin together make up the top band of margin 185 int top_margin = RACK_GRID_WIDTH; 186 int h_title = 3 * RACK_GRID_WIDTH; 187 // The height of the actual part that will contain ports and knobs 188 int active_box_height = RACK_GRID_HEIGHT - bot_margin - h_title - top_margin; 189 190 // A column will take up 3HP 191 int w_col = 3 * RACK_GRID_WIDTH; 192 193 // Offset from the top of a cell to the knobs, ports, and labels 194 float port_center_offset = active_box_height / ports_per_col * 0.25f; 195 float label_port_offset = active_box_height / ports_per_col * 0.55f; 196 float param_knob_center_offset = active_box_height / params_per_col * 0.25f; 197 float param_port_center_offset = active_box_height / params_per_col * 0.65f; 198 float param_label_offset = active_box_height / params_per_col * 0.85f; 199 200 int module_hp = 12; 201 202 genrack::Panel *panel; 203 bool dirty = false; 204 205 206 GEN_RACK_NAMEWidget(GEN_RACK_NAME* module) { 207 setModule(module); 208 box.size = Vec(RACK_GRID_WIDTH * module_hp, RACK_GRID_HEIGHT); 209 210 // Default background and title for module browser - will be drawn over when step() is called 211 panel = new genrack::Panel(40, 40, 40); 212 addChild(panel); 213 panel->box.size = box.size; 214 genrack::Title *title = new genrack::Title(box.size.x / 2, top_margin, box.size.x, "GEN_RACK_DISPLAY_NAME"); 215 addChild(title); 216 217 if (module) { 218 // Make these publically accessible to the widget 219 numParams = module->numParams; 220 numInputs = module->numInputs; 221 numOutputs = module->numOutputs; 222 223 for (int i = 0; i < numInputs; i++) { 224 std::string inputLabel = std::string("in ") + std::to_string(i + 1); 225 inputLabels.push_back(inputLabel); 226 } 227 228 for (int i = 0; i < numOutputs; i++) { 229 std::string outputLabel = std::string("out ") + std::to_string(i + 1); 230 outputLabels.push_back(outputLabel); 231 } 232 233 for (int i = 0; i < numParams; i++) { 234 std::string paramLabel = std::string(getparametername(module->moduleState, i)); 235 paramLabel.resize(10); 236 paramLabels.push_back(paramLabel); 237 } 238 239 // Figure out the width of the module 240 module_hp = 2 + 3 * (genrack::util::int_div_round_up(numInputs, ports_per_col) 241 + genrack::util::int_div_round_up(numOutputs, ports_per_col) 242 + genrack::util::int_div_round_up(numParams, params_per_col)); 243 244 box.size = Vec(RACK_GRID_WIDTH * module_hp, RACK_GRID_HEIGHT); 245 246 // Draw on the next step 247 dirty = true; 248 step(); 249 } 250 } 251 252 253 // Runs with every UI frame update 254 void step() override { 255 256 // The widget will be dirtied after the module is registered in the constructor 257 if (dirty) { 258 // Background panel 259 panel = new genrack::Panel(40, 40, 40); 260 addChild(panel); 261 panel->box.size = box.size; 262 263 // Title text 264 genrack::Title *title = new genrack::Title(box.size.x / 2, top_margin, box.size.x, "GEN_RACK_DISPLAY_NAME"); 265 addChild(title); 266 267 // Screws 268 addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); 269 addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 0))); 270 addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 365))); 271 addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 365))); 272 273 // PORTS, PARAMS, LABELS 274 for (int i = 0; i < numInputs; i++) { 275 float left_x = l_margin 276 + int(i / ports_per_col) * w_col; 277 float center_x = left_x + w_col / 2; 278 279 float top_y = top_margin + h_title + (i % ports_per_col) * (active_box_height / ports_per_col); 280 float port_center_y = top_y + port_center_offset; 281 float label_center_y = top_y + label_port_offset; 282 283 addInput(createInputCentered<PJ301MPort>(Vec(center_x, port_center_y), module, i)); 284 285 genrack::TextLabel *label = new genrack::TextLabel(center_x, label_center_y, left_x, inputLabels[i].c_str(), nvgRGB(230, 230, 230), 10); 286 addChild(label); 287 } 288 289 for (int i = 0; i < numParams; i++) { 290 float left_x = l_margin 291 + genrack::util::int_div_round_up(numInputs, ports_per_col) * w_col 292 + int(i / params_per_col) * w_col; 293 float center_x = left_x + w_col / 2; 294 295 float top_y = top_margin + h_title + (i % params_per_col) * (active_box_height / params_per_col); 296 297 float knob_center_y = top_y + param_knob_center_offset; 298 float port_center_y = top_y + param_port_center_offset; 299 float label_center_y = top_y + param_label_offset; 300 301 addParam(createParamCentered<RoundSmallBlackKnob>(Vec(center_x, knob_center_y), module, i)); 302 addInput(createInputCentered<PJ301MPort>(Vec(center_x, port_center_y), module, i + numInputs)); 303 304 genrack::TextLabel *label = new genrack::TextLabel(center_x, label_center_y, left_x, paramLabels[i].c_str(), nvgRGB(230, 230, 230), 10); 305 addChild(label); 306 } 307 308 for (int i = 0; i < numOutputs; i++) { 309 float left_x = l_margin 310 + genrack::util::int_div_round_up(numInputs, ports_per_col) * w_col 311 + genrack::util::int_div_round_up(numParams, params_per_col) * w_col 312 + int(i / ports_per_col) * w_col; 313 314 float center_x = left_x + w_col / 2; 315 316 float top_y = top_margin + h_title + (i % ports_per_col) * (active_box_height / ports_per_col); 317 float port_center_y = top_y + port_center_offset; 318 float label_center_y = top_y + label_port_offset; 319 320 addOutput(createOutputCentered<PJ301MPort>(Vec(center_x, port_center_y), module, i)); 321 322 genrack::TextLabel *label = new genrack::TextLabel(center_x, label_center_y, left_x, outputLabels[i].c_str(), nvgRGB(230, 230, 230), 10); 323 addChild(label); 324 } 325 326 dirty = false; 327 } 328 329 ModuleWidget::step(); 330 } 331 332 333 void appendContextMenu(Menu* menu) override { 334 GEN_RACK_NAME* module = dynamic_cast<GEN_RACK_NAME*>(this->module); 335 336 // Buffer sizes selection 337 menu->addChild(new MenuSeparator()); 338 MenuItem* bufferSizeLabel = new MenuItem; 339 bufferSizeLabel->disabled = true; 340 bufferSizeLabel->text = "Buffer size"; 341 menu->addChild(bufferSizeLabel); 342 343 for (int i = 0; i < (int) module->validBufferSizes.size(); i++) { 344 BufferSizeMenuItem* item = new BufferSizeMenuItem; 345 item->module = module; 346 item->text = std::to_string(module->validBufferSizes[i]).c_str(); 347 item->rightText = CHECKMARK(module->currentBufferSize == module->validBufferSizes[i]); 348 item->bufferSize = module->validBufferSizes[i]; 349 menu->addChild(item); 350 } 351 } 352 }; 353 354 355 /// Register the model 356 Model* modelGEN_RACK_NAME = createModel<GEN_RACK_NAME, GEN_RACK_NAMEWidget>("GEN_RACK_DISPLAY_NAME");