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.

293 lines
8.3KB

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