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.

271 lines
7.5KB

  1. #include "plugin.hpp"
  2. struct VCA : Module {
  3. enum ParamIds {
  4. LEVEL1_PARAM,
  5. LEVEL2_PARAM,
  6. NUM_PARAMS
  7. };
  8. enum InputIds {
  9. EXP1_INPUT,
  10. LIN1_INPUT,
  11. IN1_INPUT,
  12. EXP2_INPUT,
  13. LIN2_INPUT,
  14. IN2_INPUT,
  15. NUM_INPUTS
  16. };
  17. enum OutputIds {
  18. OUT1_OUTPUT,
  19. OUT2_OUTPUT,
  20. NUM_OUTPUTS
  21. };
  22. VCA() {
  23. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS);
  24. configParam(LEVEL1_PARAM, 0.0, 1.0, 1.0, "Ch 1 level", "%", 0, 100);
  25. configParam(LEVEL2_PARAM, 0.0, 1.0, 1.0, "Ch 2 level", "%", 0, 100);
  26. }
  27. void processChannel(Input& in, Param& level, Input& lin, Input& exp, Output& out) {
  28. // Get input
  29. int channels = std::max(in.getChannels(), 1);
  30. simd::float_4 v[4];
  31. for (int c = 0; c < channels; c += 4) {
  32. v[c / 4] = simd::float_4::load(in.getVoltages(c));
  33. }
  34. // Apply knob gain
  35. float gain = level.getValue();
  36. for (int c = 0; c < channels; c += 4) {
  37. v[c / 4] *= gain;
  38. }
  39. // Apply linear CV gain
  40. if (lin.isConnected()) {
  41. if (lin.isPolyphonic()) {
  42. for (int c = 0; c < channels; c += 4) {
  43. simd::float_4 cv = simd::float_4::load(lin.getVoltages(c)) / 10.f;
  44. cv = clamp(cv, 0.f, 1.f);
  45. v[c / 4] *= cv;
  46. }
  47. }
  48. else {
  49. float cv = lin.getVoltage() / 10.f;
  50. cv = clamp(cv, 0.f, 1.f);
  51. for (int c = 0; c < channels; c += 4) {
  52. v[c / 4] *= cv;
  53. }
  54. }
  55. }
  56. // Apply exponential CV gain
  57. const float expBase = 50.f;
  58. if (exp.isConnected()) {
  59. if (exp.isPolyphonic()) {
  60. for (int c = 0; c < channels; c += 4) {
  61. simd::float_4 cv = simd::float_4::load(exp.getVoltages(c)) / 10.f;
  62. cv = clamp(cv, 0.f, 1.f);
  63. cv = rescale(pow(expBase, cv), 1.f, expBase, 0.f, 1.f);
  64. v[c / 4] *= cv;
  65. }
  66. }
  67. else {
  68. float cv = exp.getVoltage() / 10.f;
  69. cv = clamp(cv, 0.f, 1.f);
  70. cv = rescale(std::pow(expBase, cv), 1.f, expBase, 0.f, 1.f);
  71. for (int c = 0; c < channels; c += 4) {
  72. v[c / 4] *= cv;
  73. }
  74. }
  75. }
  76. // Set output
  77. out.setChannels(channels);
  78. for (int c = 0; c < channels; c += 4) {
  79. v[c / 4].store(out.getVoltages(c));
  80. }
  81. }
  82. void process(const ProcessArgs& args) override {
  83. processChannel(inputs[IN1_INPUT], params[LEVEL1_PARAM], inputs[LIN1_INPUT], inputs[EXP1_INPUT], outputs[OUT1_OUTPUT]);
  84. processChannel(inputs[IN2_INPUT], params[LEVEL2_PARAM], inputs[LIN2_INPUT], inputs[EXP2_INPUT], outputs[OUT2_OUTPUT]);
  85. }
  86. };
  87. struct VCAWidget : ModuleWidget {
  88. VCAWidget(VCA* module) {
  89. setModule(module);
  90. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/VCA.svg")));
  91. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  92. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  93. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  94. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  95. addParam(createParam<RoundLargeBlackKnob>(mm2px(Vec(6.35, 19.11753)), module, VCA::LEVEL1_PARAM));
  96. addParam(createParam<RoundLargeBlackKnob>(mm2px(Vec(6.35, 74.80544)), module, VCA::LEVEL2_PARAM));
  97. addInput(createInput<PJ301MPort>(mm2px(Vec(2.5907, 38.19371)), module, VCA::EXP1_INPUT));
  98. addInput(createInput<PJ301MPort>(mm2px(Vec(14.59752, 38.19371)), module, VCA::LIN1_INPUT));
  99. addInput(createInput<PJ301MPort>(mm2px(Vec(2.5907, 52.80642)), module, VCA::IN1_INPUT));
  100. addInput(createInput<PJ301MPort>(mm2px(Vec(2.5907, 93.53435)), module, VCA::EXP2_INPUT));
  101. addInput(createInput<PJ301MPort>(mm2px(Vec(14.59752, 93.53435)), module, VCA::LIN2_INPUT));
  102. addInput(createInput<PJ301MPort>(mm2px(Vec(2.5907, 108.14706)), module, VCA::IN2_INPUT));
  103. addOutput(createOutput<PJ301MPort>(mm2px(Vec(14.59752, 52.80642)), module, VCA::OUT1_OUTPUT));
  104. addOutput(createOutput<PJ301MPort>(mm2px(Vec(14.59752, 108.14706)), module, VCA::OUT2_OUTPUT));
  105. }
  106. };
  107. Model* modelVCA = createModel<VCA, VCAWidget>("VCA");
  108. struct VCA_1 : Module {
  109. enum ParamIds {
  110. LEVEL_PARAM,
  111. EXP_PARAM,
  112. NUM_PARAMS
  113. };
  114. enum InputIds {
  115. CV_INPUT,
  116. IN_INPUT,
  117. NUM_INPUTS
  118. };
  119. enum OutputIds {
  120. OUT_OUTPUT,
  121. NUM_OUTPUTS
  122. };
  123. enum LightIds {
  124. NUM_LIGHTS
  125. };
  126. int lastChannels = 1;
  127. float lastGains[16] = {};
  128. VCA_1() {
  129. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  130. configParam(LEVEL_PARAM, 0.0, 1.0, 1.0, "Level", "%", 0, 100);
  131. configParam(EXP_PARAM, 0.0, 1.0, 1.0, "Response mode");
  132. }
  133. void process(const ProcessArgs& args) override {
  134. int channels = std::max(inputs[IN_INPUT].getChannels(), 1);
  135. float level = params[LEVEL_PARAM].getValue();
  136. for (int c = 0; c < channels; c++) {
  137. // Get input
  138. float in = inputs[IN_INPUT].getVoltage(c);
  139. // Get gain
  140. float gain = level;
  141. if (inputs[CV_INPUT].isConnected()) {
  142. float cv = clamp(inputs[CV_INPUT].getPolyVoltage(c) / 10.f, 0.f, 1.f);
  143. if (int(params[EXP_PARAM].getValue()) == 0)
  144. cv = std::pow(cv, 4.f);
  145. gain *= cv;
  146. }
  147. // Apply gain
  148. in *= gain;
  149. lastGains[c] = gain;
  150. // Set output
  151. outputs[OUT_OUTPUT].setVoltage(in, c);
  152. }
  153. outputs[OUT_OUTPUT].setChannels(channels);
  154. lastChannels = channels;
  155. }
  156. };
  157. struct VCA_1VUKnob : SliderKnob {
  158. VCA_1* module = NULL;
  159. VCA_1VUKnob() {
  160. box.size = mm2px(Vec(10, 46));
  161. }
  162. void draw(const DrawArgs& args) override {
  163. nvgBeginPath(args.vg);
  164. nvgRoundedRect(args.vg, 0, 0, box.size.x, box.size.y, 2.0);
  165. nvgFillColor(args.vg, nvgRGB(0, 0, 0));
  166. nvgFill(args.vg);
  167. const Vec margin = Vec(3, 3);
  168. Rect r = box.zeroPos().grow(margin.neg());
  169. int channels = module ? module->lastChannels : 1;
  170. float value = paramQuantity ? paramQuantity->getValue() : 1.f;
  171. // Segment value
  172. nvgBeginPath(args.vg);
  173. nvgRect(args.vg,
  174. r.pos.x,
  175. r.pos.y + r.size.y * (1 - value),
  176. r.size.x,
  177. r.size.y * value);
  178. nvgFillColor(args.vg, color::mult(color::WHITE, 0.33));
  179. nvgFill(args.vg);
  180. // Segment gain
  181. nvgBeginPath(args.vg);
  182. for (int c = 0; c < channels; c++) {
  183. float gain = module ? module->lastGains[c] : 1.f;
  184. if (gain >= 0.005f) {
  185. nvgRect(args.vg,
  186. r.pos.x + r.size.x * c / channels,
  187. r.pos.y + r.size.y * (1 - gain),
  188. r.size.x / channels,
  189. r.size.y * gain);
  190. }
  191. }
  192. nvgFillColor(args.vg, SCHEME_GREEN);
  193. nvgFill(args.vg);
  194. // Invisible separators
  195. const int segs = 25;
  196. nvgBeginPath(args.vg);
  197. for (int i = 1; i <= segs; i++) {
  198. nvgRect(args.vg,
  199. r.pos.x - 1.0,
  200. r.pos.y + r.size.y * i / segs,
  201. r.size.x + 2.0,
  202. 1.0);
  203. }
  204. nvgFillColor(args.vg, color::BLACK);
  205. nvgFill(args.vg);
  206. }
  207. };
  208. struct VCA_1Widget : ModuleWidget {
  209. VCA_1Widget(VCA_1* module) {
  210. setModule(module);
  211. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/VCA-1.svg")));
  212. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  213. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  214. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  215. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  216. VCA_1VUKnob* levelParam = createParam<VCA_1VUKnob>(mm2px(Vec(2.62103, 12.31692)), module, VCA_1::LEVEL_PARAM);
  217. levelParam->module = module;
  218. addParam(levelParam);
  219. addParam(createParam<CKSS>(mm2px(Vec(5.24619, 79.9593)), module, VCA_1::EXP_PARAM));
  220. addInput(createInput<PJ301MPort>(mm2px(Vec(3.51261, 60.4008)), module, VCA_1::CV_INPUT));
  221. addInput(createInput<PJ301MPort>(mm2px(Vec(3.51398, 97.74977)), module, VCA_1::IN_INPUT));
  222. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.51398, 108.64454)), module, VCA_1::OUT_OUTPUT));
  223. }
  224. };
  225. Model* modelVCA_1 = createModel<VCA_1, VCA_1Widget>("VCA-1");