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.

201 lines
6.0KB

  1. #include "plugin.hpp"
  2. struct Rescale : Module {
  3. enum ParamId {
  4. GAIN_PARAM,
  5. OFFSET_PARAM,
  6. MAX_PARAM,
  7. MIN_PARAM,
  8. PARAMS_LEN
  9. };
  10. enum InputId {
  11. IN_INPUT,
  12. INPUTS_LEN
  13. };
  14. enum OutputId {
  15. OUT_OUTPUT,
  16. OUTPUTS_LEN
  17. };
  18. enum LightId {
  19. ENUMS(MAX_LIGHT, 2),
  20. ENUMS(MIN_LIGHT, 2),
  21. LIGHTS_LEN
  22. };
  23. float multiplier = 1.f;
  24. bool reflectMin = false;
  25. bool reflectMax = false;
  26. dsp::ClockDivider lightDivider;
  27. Rescale() {
  28. config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
  29. struct GainQuantity : ParamQuantity {
  30. float getDisplayValue() override {
  31. Rescale* module = reinterpret_cast<Rescale*>(this->module);
  32. if (module->multiplier == 1.f) {
  33. unit = "%";
  34. displayMultiplier = 100.f;
  35. }
  36. else {
  37. unit = "x";
  38. displayMultiplier = module->multiplier;
  39. }
  40. return ParamQuantity::getDisplayValue();
  41. }
  42. };
  43. configParam<GainQuantity>(GAIN_PARAM, -1.f, 1.f, 0.f, "Gain", "%", 0, 100);
  44. configParam(OFFSET_PARAM, -10.f, 10.f, 0.f, "Offset", " V");
  45. configParam(MAX_PARAM, -10.f, 10.f, 10.f, "Maximum", " V");
  46. configParam(MIN_PARAM, -10.f, 10.f, -10.f, "Minimum", " V");
  47. configInput(IN_INPUT, "Signal");
  48. configOutput(OUT_OUTPUT, "Signal");
  49. configBypass(IN_INPUT, OUT_OUTPUT);
  50. lightDivider.setDivision(16);
  51. }
  52. void onReset(const ResetEvent& e) override {
  53. Module::onReset(e);
  54. multiplier = 1.f;
  55. reflectMin = false;
  56. reflectMax = false;
  57. }
  58. void process(const ProcessArgs& args) override {
  59. using simd::float_4;
  60. int channels = std::max(1, inputs[IN_INPUT].getChannels());
  61. float gain = params[GAIN_PARAM].getValue() * multiplier;
  62. float offset = params[OFFSET_PARAM].getValue();
  63. float min = params[MIN_PARAM].getValue();
  64. float max = params[MAX_PARAM].getValue();
  65. bool maxLight = false;
  66. bool minLight = false;
  67. bool lightProcess = lightDivider.process();
  68. for (int c = 0; c < channels; c += 4) {
  69. float_4 x = inputs[IN_INPUT].getVoltageSimd<float_4>(c);
  70. x *= gain;
  71. x += offset;
  72. // Check lights
  73. if (lightProcess) {
  74. // Mask result for non factor of 4 channels.
  75. int mask = 0xffff >> (16 - channels + c);
  76. if (simd::movemask(x <= min) & mask)
  77. minLight = true;
  78. if (simd::movemask(x >= max) & mask)
  79. maxLight = true;
  80. }
  81. if (max <= min) {
  82. x = min;
  83. }
  84. else if (reflectMin && reflectMax) {
  85. // Cyclically reflect value between min and max
  86. float range = max - min;
  87. x = (x - min) / range;
  88. x = simd::fmod(x + 1.f, 2.f) - 1.f;
  89. x = simd::fabs(x);
  90. x = x * range + min;
  91. }
  92. else if (reflectMin) {
  93. x = simd::fabs(x - min) + min;
  94. x = simd::fmin(x, max);
  95. }
  96. else if (reflectMax) {
  97. x = max - simd::fabs(max - x);
  98. x = simd::fmax(x, min);
  99. }
  100. else {
  101. x = simd::fmin(x, max);
  102. x = simd::fmax(x, min);
  103. }
  104. outputs[OUT_OUTPUT].setVoltageSimd(x, c);
  105. }
  106. outputs[OUT_OUTPUT].setChannels(channels);
  107. // Lights
  108. if (lightProcess) {
  109. float lightTime = args.sampleTime * lightDivider.getDivision();
  110. lights[MAX_LIGHT + 0].setBrightnessSmooth(maxLight && (channels <= 1), lightTime);
  111. lights[MAX_LIGHT + 1].setBrightnessSmooth(maxLight && (channels > 1), lightTime);
  112. lights[MIN_LIGHT + 0].setBrightnessSmooth(minLight && (channels <= 1), lightTime);
  113. lights[MIN_LIGHT + 1].setBrightnessSmooth(minLight && (channels > 1), lightTime);
  114. }
  115. }
  116. json_t* dataToJson() override {
  117. json_t* rootJ = json_object();
  118. json_object_set_new(rootJ, "multiplier", json_real(multiplier));
  119. json_object_set_new(rootJ, "reflectMin", json_boolean(reflectMin));
  120. json_object_set_new(rootJ, "reflectMax", json_boolean(reflectMax));
  121. return rootJ;
  122. }
  123. void dataFromJson(json_t* rootJ) override {
  124. json_t* multiplierJ = json_object_get(rootJ, "multiplier");
  125. if (multiplierJ)
  126. multiplier = json_number_value(multiplierJ);
  127. json_t* reflectMinJ = json_object_get(rootJ, "reflectMin");
  128. if (reflectMinJ)
  129. reflectMin = json_boolean_value(reflectMinJ);
  130. json_t* reflectMaxJ = json_object_get(rootJ, "reflectMax");
  131. if (reflectMaxJ)
  132. reflectMax = json_boolean_value(reflectMaxJ);
  133. }
  134. };
  135. struct RescaleWidget : ModuleWidget {
  136. RescaleWidget(Rescale* module) {
  137. setModule(module);
  138. setPanel(createPanel(asset::plugin(pluginInstance, "res/Rescale.svg"), asset::plugin(pluginInstance, "res/Rescale-dark.svg")));
  139. addChild(createWidget<ThemedScrew>(Vec(RACK_GRID_WIDTH, 0)));
  140. addChild(createWidget<ThemedScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  141. addChild(createWidget<ThemedScrew>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  142. addChild(createWidget<ThemedScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  143. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(7.62, 24.723)), module, Rescale::GAIN_PARAM));
  144. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(7.617, 43.031)), module, Rescale::OFFSET_PARAM));
  145. addParam(createParamCentered<RoundSmallBlackKnob>(mm2px(Vec(7.612, 64.344)), module, Rescale::MAX_PARAM));
  146. addParam(createParamCentered<RoundSmallBlackKnob>(mm2px(Vec(7.612, 80.597)), module, Rescale::MIN_PARAM));
  147. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(7.62, 96.859)), module, Rescale::IN_INPUT));
  148. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(7.62, 113.115)), module, Rescale::OUT_OUTPUT));
  149. addChild(createLightCentered<TinyLight<YellowBlueLight<>>>(mm2px(Vec(12.327, 57.3)), module, Rescale::MAX_LIGHT));
  150. addChild(createLightCentered<TinyLight<YellowBlueLight<>>>(mm2px(Vec(12.327, 73.559)), module, Rescale::MIN_LIGHT));
  151. }
  152. void appendContextMenu(Menu* menu) override {
  153. Rescale* module = getModule<Rescale>();
  154. menu->addChild(new MenuSeparator);
  155. menu->addChild(createIndexSubmenuItem("Gain multiplier", {"1x", "10x", "100x", "1000x"},
  156. [=]() {
  157. return (int) std::log10(module->multiplier);
  158. },
  159. [=](int mode) {
  160. module->multiplier = std::pow(10.f, (float) mode);
  161. }
  162. ));
  163. menu->addChild(createBoolPtrMenuItem("Reflect at maximum", "", &module->reflectMax));
  164. menu->addChild(createBoolPtrMenuItem("Reflect at minimum", "", &module->reflectMin));
  165. }
  166. };
  167. Model* modelRescale = createModel<Rescale, RescaleWidget>("Rescale");