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.

280 lines
7.8KB

  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. configInput(EXP1_INPUT, "Exponential CV 1");
  27. configInput(LIN1_INPUT, "Linear CV 1");
  28. configInput(IN1_INPUT, "In 1");
  29. configInput(EXP2_INPUT, "Exponential CV 2");
  30. configInput(LIN2_INPUT, "Linear CV 2");
  31. configInput(IN2_INPUT, "In 2");
  32. configOutput(OUT1_OUTPUT, "Out 1");
  33. configOutput(OUT2_OUTPUT, "Out 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. configParam(EXP_PARAM, 0.0, 1.0, 1.0, "Response mode");
  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. const Vec margin = Vec(3, 3);
  176. Rect r = box.zeroPos().grow(margin.neg());
  177. int channels = module ? module->lastChannels : 1;
  178. engine::ParamQuantity* pq = getParamQuantity();
  179. float value = pq ? pq->getValue() : 1.f;
  180. // Segment value
  181. nvgBeginPath(args.vg);
  182. nvgRect(args.vg,
  183. r.pos.x,
  184. r.pos.y + r.size.y * (1 - value),
  185. r.size.x,
  186. r.size.y * value);
  187. nvgFillColor(args.vg, color::mult(color::WHITE, 0.33));
  188. nvgFill(args.vg);
  189. // Segment gain
  190. nvgBeginPath(args.vg);
  191. for (int c = 0; c < channels; c++) {
  192. float gain = module ? module->lastGains[c] : 1.f;
  193. if (gain >= 0.005f) {
  194. nvgRect(args.vg,
  195. r.pos.x + r.size.x * c / channels,
  196. r.pos.y + r.size.y * (1 - gain),
  197. r.size.x / channels,
  198. r.size.y * gain);
  199. }
  200. }
  201. nvgFillColor(args.vg, SCHEME_GREEN);
  202. nvgFill(args.vg);
  203. // Invisible separators
  204. const int segs = 25;
  205. nvgBeginPath(args.vg);
  206. for (int i = 1; i <= segs; i++) {
  207. nvgRect(args.vg,
  208. r.pos.x - 1.0,
  209. r.pos.y + r.size.y * i / segs,
  210. r.size.x + 2.0,
  211. 1.0);
  212. }
  213. nvgFillColor(args.vg, color::BLACK);
  214. nvgFill(args.vg);
  215. }
  216. };
  217. struct VCA_1Widget : ModuleWidget {
  218. VCA_1Widget(VCA_1* module) {
  219. setModule(module);
  220. setPanel(createPanel(asset::plugin(pluginInstance, "res/VCA-1.svg")));
  221. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  222. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  223. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  224. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  225. VCA_1VUKnob* levelParam = createParam<VCA_1VUKnob>(mm2px(Vec(2.62103, 12.31692)), module, VCA_1::LEVEL_PARAM);
  226. levelParam->module = module;
  227. addParam(levelParam);
  228. addParam(createParam<CKSS>(mm2px(Vec(5.24619, 79.9593)), module, VCA_1::EXP_PARAM));
  229. addInput(createInput<PJ301MPort>(mm2px(Vec(3.51261, 60.4008)), module, VCA_1::CV_INPUT));
  230. addInput(createInput<PJ301MPort>(mm2px(Vec(3.51398, 97.74977)), module, VCA_1::IN_INPUT));
  231. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.51398, 108.64454)), module, VCA_1::OUT_OUTPUT));
  232. }
  233. };
  234. Model* modelVCA_1 = createModel<VCA_1, VCA_1Widget>("VCA-1");