You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
5.1KB

  1. #include "mtsch.hpp"
  2. #include <iostream>
  3. #define NUM_CHANNELS 4
  4. // The range of the knobs. 16 should be enough since Rationals can be stacked.
  5. #define MAX_VALUE 16
  6. // IO, digit and knob positions.
  7. #define X_POS 13
  8. #define Y_POS 80
  9. #define CHANNEL_SPACING 70
  10. #define KNOB_X_OFFSET 30
  11. #define DIGIT_X_OFFSET 33
  12. #define DIGIT_Y_OFFSET 4
  13. #define DIGIT_SPACING 17
  14. #define NUMDEN_SPACING 35
  15. #define IO_Y_OFFSET 19
  16. #define OUT_X_OFFSET KNOB_X_OFFSET + 71
  17. #define GRID_X_POS 52
  18. #define GRID_Y_POS 11
  19. #define GRID_SPACING 24
  20. namespace rack_plugin_mtsch_plugins {
  21. #include "DigitDisplay.hpp"
  22. struct Rationals : Module {
  23. enum ParamIds {
  24. PARAMS,
  25. NUM_PARAMS = PARAMS + 2 * NUM_CHANNELS
  26. };
  27. enum InputIds {
  28. INPUTS,
  29. NUM_INPUTS = INPUTS + 3 * NUM_CHANNELS
  30. };
  31. enum OutputIds {
  32. OUTPUTS,
  33. NUM_OUTPUTS = OUTPUTS + NUM_CHANNELS
  34. };
  35. enum LightIds {
  36. NUM_LIGHTS
  37. };
  38. Rationals() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  39. void step() override;
  40. char display[4*NUM_CHANNELS];
  41. };
  42. void Rationals::step() {
  43. for (int i = 0; i < NUM_CHANNELS; i++) {
  44. float num_cv = std::round(inputs[INPUTS + 1 + 3*i].value);
  45. float num_par = std::round(params[PARAMS + 2*i].value);
  46. float den_cv = std::round(inputs[INPUTS + 2 + 3*i].value);
  47. float den_par = std::round(params[PARAMS + 1 + 2*i].value);
  48. float num = num_cv + num_par;
  49. float den = den_cv + den_par;
  50. num = num > 0 ? num : 1;
  51. den = den > 0 ? den : 1;
  52. int digit_offset = NUM_CHANNELS * i;
  53. int dig1 = int(num / 10.0) % 10;
  54. int dig2 = int(num) % 10;
  55. if (dig1 != 0) {
  56. display[0 + digit_offset] = dig1 + '0';
  57. } else {
  58. display[0 + digit_offset] = '\0';
  59. }
  60. display[1 + digit_offset] = dig2 + '0';
  61. dig1 = int(den / 10.0) % 10;
  62. dig2 = int(den) % 10;
  63. if (dig1 != 0) {
  64. display[2 + digit_offset] = dig1 + '0';
  65. } else {
  66. display[2 + digit_offset] = '\0';
  67. }
  68. display[3 + digit_offset] = dig2 + '0';
  69. outputs[OUTPUTS + i].value = inputs[INPUTS + 3*i].value + log2f(num/den);
  70. }
  71. }
  72. struct RationalsWidget : ModuleWidget {
  73. RationalsWidget(Rationals *module);
  74. };
  75. RationalsWidget::RationalsWidget(Rationals *module) : ModuleWidget(module) {
  76. box.size = Vec(10 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
  77. {
  78. SVGPanel *panel = new SVGPanel();
  79. panel->box.size = box.size;
  80. panel->setBackground(SVG::load(assetPlugin(plugin, "res/Rationals.svg")));
  81. addChild(panel);
  82. }
  83. addChild(Widget::create<ScrewSilver>(Vec(0, 0)));
  84. addChild(Widget::create<ScrewSilver>(Vec(0, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  85. for (int i = 0; i < NUM_CHANNELS; i++) {
  86. int y_pos = Y_POS + i * CHANNEL_SPACING;
  87. int offset = NUM_CHANNELS * i;
  88. int digit_x = X_POS + KNOB_X_OFFSET + DIGIT_X_OFFSET;
  89. // Numerator digits.
  90. addChild(new DigitDisplay(Vec(digit_x, y_pos + DIGIT_Y_OFFSET),
  91. 5.f, &module->display[0+offset]));
  92. addChild(new DigitDisplay(Vec(digit_x + DIGIT_SPACING, y_pos + DIGIT_Y_OFFSET),
  93. 5.f, &module->display[1+offset]));
  94. // Denominator digits.
  95. addChild(new DigitDisplay(Vec(digit_x, y_pos + DIGIT_Y_OFFSET + NUMDEN_SPACING),
  96. 5.f, &module->display[2+offset]));
  97. addChild(new DigitDisplay(Vec(digit_x + DIGIT_SPACING, y_pos + DIGIT_Y_OFFSET + NUMDEN_SPACING),
  98. 5.f, &module->display[3+offset]));
  99. // Numerator knob.
  100. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(X_POS + KNOB_X_OFFSET, y_pos),
  101. module, Rationals::PARAMS + 2*i, 1, MAX_VALUE, 1));
  102. // Denominator knob.
  103. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(X_POS + KNOB_X_OFFSET, y_pos + NUMDEN_SPACING),
  104. module, Rationals::PARAMS + 1 + 2*i, 1, MAX_VALUE, 1));
  105. // IO.
  106. addInput(Port::create<PJ301MPort>(Vec(X_POS, y_pos + IO_Y_OFFSET),
  107. Port::INPUT, module, Rationals::INPUTS + i*3));
  108. addOutput(Port::create<PJ301MPort>(Vec(X_POS + OUT_X_OFFSET, y_pos + IO_Y_OFFSET),
  109. Port::OUTPUT, module, Rationals::OUTPUTS + i));
  110. }
  111. // CV mod grid.
  112. for (int i = 0; i < NUM_CHANNELS; i++) {
  113. addInput(Port::create<PJ301MPort>(Vec(GRID_X_POS + i*GRID_SPACING, GRID_Y_POS),
  114. Port::INPUT, module, Rationals::INPUTS + 1 + i*3));
  115. addInput(Port::create<PJ301MPort>(Vec(GRID_X_POS + i*GRID_SPACING, GRID_Y_POS + GRID_SPACING),
  116. Port::INPUT, module, Rationals::INPUTS + 2 + i*3));
  117. }
  118. }
  119. } // namespace rack_plugin_mtsch_plugins
  120. using namespace rack_plugin_mtsch_plugins;
  121. RACK_PLUGIN_MODEL_INIT(mtsch_plugins, Rationals) {
  122. Model *modelRationals = Model::create<Rationals, RationalsWidget>(
  123. "mtsch", "Rationals", "Rationals", UTILITY_TAG);
  124. return modelRationals;
  125. }