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.

285 lines
8.0KB

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