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.

214 lines
7.3KB

  1. #include "plugin.hpp"
  2. struct VCMixer : Module {
  3. enum ParamIds {
  4. MIX_LVL_PARAM,
  5. ENUMS(LVL_PARAMS, 4),
  6. NUM_PARAMS
  7. };
  8. enum InputIds {
  9. MIX_CV_INPUT,
  10. ENUMS(CH_INPUTS, 4),
  11. ENUMS(CV_INPUTS, 4),
  12. NUM_INPUTS
  13. };
  14. enum OutputIds {
  15. MIX_OUTPUT,
  16. ENUMS(CH_OUTPUTS, 4),
  17. NUM_OUTPUTS
  18. };
  19. enum LightIds {
  20. ENUMS(LVL_LIGHTS, 4),
  21. NUM_LIGHTS
  22. };
  23. dsp::VuMeter2 chMeters[4];
  24. dsp::ClockDivider lightDivider;
  25. bool chExp = false;
  26. bool mixExp = false;
  27. VCMixer() {
  28. config(0, 0, 0, 0);
  29. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  30. // x^1 scaling up to 6 dB
  31. configParam(MIX_LVL_PARAM, 0.0, 2.0, 1.0, "Mix level", " dB", -10, 20);
  32. // x^2 scaling up to 6 dB
  33. configParam(LVL_PARAMS + 0, 0.0, M_SQRT2, 1.0, "Channel 1 level", " dB", -10, 40);
  34. configParam(LVL_PARAMS + 1, 0.0, M_SQRT2, 1.0, "Channel 2 level", " dB", -10, 40);
  35. configParam(LVL_PARAMS + 2, 0.0, M_SQRT2, 1.0, "Channel 3 level", " dB", -10, 40);
  36. configParam(LVL_PARAMS + 3, 0.0, M_SQRT2, 1.0, "Channel 4 level", " dB", -10, 40);
  37. configInput(MIX_CV_INPUT, "Mix CV");
  38. for (int i = 0; i < 4; i++)
  39. configInput(CH_INPUTS + i, string::f("Channel %d", i + 1));
  40. for (int i = 0; i < 4; i++)
  41. configInput(CV_INPUTS + i, string::f("Channel %d CV", i + 1));
  42. configOutput(MIX_OUTPUT, "Mix");
  43. for (int i = 0; i < 4; i++)
  44. configOutput(CH_OUTPUTS + i, string::f("Channel %d", i + 1));
  45. lightDivider.setDivision(512);
  46. }
  47. void onReset(const ResetEvent& e) override {
  48. chExp = false;
  49. mixExp = false;
  50. Module::onReset(e);
  51. }
  52. void process(const ProcessArgs& args) override {
  53. using simd::float_4;
  54. // Get number of poly channels for mix output
  55. int channels = 1;
  56. for (int i = 0; i < 4; i++) {
  57. channels = std::max(channels, inputs[CH_INPUTS + i].getChannels());
  58. }
  59. // Iterate polyphony channels (voices)
  60. float chSum[4] = {};
  61. for (int c = 0; c < channels; c += 4) {
  62. float_4 mix = 0.f;
  63. // Channel strips
  64. for (int i = 0; i < 4; i++) {
  65. float_4 out = 0.f;
  66. if (inputs[CH_INPUTS + i].isConnected()) {
  67. // Get input
  68. out = inputs[CH_INPUTS + i].getPolyVoltageSimd<float_4>(c);
  69. // Apply fader gain
  70. float gain = std::pow(params[LVL_PARAMS + i].getValue(), 2.f);
  71. out *= gain;
  72. // Apply CV gain
  73. if (inputs[CV_INPUTS + i].isConnected()) {
  74. float_4 cv = inputs[CV_INPUTS + i].getPolyVoltageSimd<float_4>(c) / 10.f;
  75. cv = simd::fmax(0.f, cv);
  76. if (chExp)
  77. cv = (cv * cv) * (cv * cv);
  78. out *= cv;
  79. }
  80. // Sum channel for VU meter
  81. for (int c2 = 0; c2 < 4; c2++) {
  82. chSum[i] += out[c2];
  83. }
  84. // Add to mix
  85. mix += out;
  86. }
  87. // Set channel output
  88. outputs[CH_OUTPUTS + i].setVoltageSimd(out, c);
  89. }
  90. // Mix output
  91. if (outputs[MIX_OUTPUT].isConnected()) {
  92. // Apply mix knob gain
  93. float gain = params[MIX_LVL_PARAM].getValue();
  94. mix *= gain;
  95. // Apply mix CV gain
  96. if (inputs[MIX_CV_INPUT].isConnected()) {
  97. float_4 cv = inputs[MIX_CV_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f;
  98. cv = simd::fmax(0.f, cv);
  99. if (mixExp)
  100. cv = (cv * cv) * (cv * cv);
  101. mix *= cv;
  102. }
  103. // Set mix output
  104. outputs[MIX_OUTPUT].setVoltageSimd(mix, c);
  105. }
  106. }
  107. // Set output channels
  108. for (int i = 0; i < 4; i++) {
  109. outputs[CH_OUTPUTS + i].setChannels(channels);
  110. }
  111. outputs[MIX_OUTPUT].setChannels(channels);
  112. // VU lights
  113. for (int i = 0; i < 4; i++) {
  114. chMeters[i].process(args.sampleTime, chSum[i] / 5.f);
  115. }
  116. if (lightDivider.process()) {
  117. for (int i = 0; i < 4; i++) {
  118. lights[LVL_LIGHTS + i].setBrightness(chMeters[i].getBrightness(-24.f, 0.f));
  119. }
  120. }
  121. }
  122. json_t* dataToJson() override {
  123. json_t* rootJ = json_object();
  124. // chExp
  125. json_object_set_new(rootJ, "chExp", json_boolean(chExp));
  126. // mixExp
  127. json_object_set_new(rootJ, "mixExp", json_boolean(mixExp));
  128. return rootJ;
  129. }
  130. void dataFromJson(json_t* rootJ) override {
  131. // chExp
  132. json_t* chExpJ = json_object_get(rootJ, "chExp");
  133. if (chExpJ)
  134. chExp = json_boolean_value(chExpJ);
  135. // mixExp
  136. json_t* mixExpJ = json_object_get(rootJ, "mixExp");
  137. if (mixExpJ)
  138. mixExp = json_boolean_value(mixExpJ);
  139. }
  140. };
  141. struct VCMixerWidget : ModuleWidget {
  142. VCMixerWidget(VCMixer* module) {
  143. setModule(module);
  144. setPanel(createPanel(asset::plugin(pluginInstance, "res/VCMixer.svg"), asset::plugin(pluginInstance, "res/VCMixer-dark.svg")));
  145. addChild(createWidget<ThemedScrew>(Vec(RACK_GRID_WIDTH, 0)));
  146. addChild(createWidget<ThemedScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  147. addChild(createWidget<ThemedScrew>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  148. addChild(createWidget<ThemedScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  149. addParam(createLightParamCentered<VCVLightSlider<YellowLight>>(mm2px(Vec(6.604, 33.605)), module, VCMixer::LVL_PARAMS + 0, VCMixer::LVL_LIGHTS + 0));
  150. addParam(createLightParamCentered<VCVLightSlider<YellowLight>>(mm2px(Vec(17.441, 33.605)), module, VCMixer::LVL_PARAMS + 1, VCMixer::LVL_LIGHTS + 1));
  151. addParam(createLightParamCentered<VCVLightSlider<YellowLight>>(mm2px(Vec(28.279, 33.605)), module, VCMixer::LVL_PARAMS + 2, VCMixer::LVL_LIGHTS + 2));
  152. addParam(createLightParamCentered<VCVLightSlider<YellowLight>>(mm2px(Vec(39.116, 33.605)), module, VCMixer::LVL_PARAMS + 3, VCMixer::LVL_LIGHTS + 3));
  153. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(22.776, 64.366)), module, VCMixer::MIX_LVL_PARAM));
  154. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(6.604, 64.347)), module, VCMixer::MIX_CV_INPUT));
  155. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(6.604, 80.549)), module, VCMixer::CV_INPUTS + 0));
  156. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(17.441, 80.549)), module, VCMixer::CV_INPUTS + 1));
  157. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(28.279, 80.549)), module, VCMixer::CV_INPUTS + 2));
  158. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(39.116, 80.549)), module, VCMixer::CV_INPUTS + 3));
  159. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(6.604, 96.859)), module, VCMixer::CH_INPUTS + 0));
  160. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(17.441, 96.859)), module, VCMixer::CH_INPUTS + 1));
  161. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(28.279, 96.859)), module, VCMixer::CH_INPUTS + 2));
  162. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(39.116, 96.821)), module, VCMixer::CH_INPUTS + 3));
  163. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(39.116, 64.347)), module, VCMixer::MIX_OUTPUT));
  164. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(6.604, 113.115)), module, VCMixer::CH_OUTPUTS + 0));
  165. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(17.441, 113.115)), module, VCMixer::CH_OUTPUTS + 1));
  166. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(28.279, 113.115)), module, VCMixer::CH_OUTPUTS + 2));
  167. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(39.116, 113.115)), module, VCMixer::CH_OUTPUTS + 3));
  168. }
  169. void appendContextMenu(Menu* menu) override {
  170. VCMixer* module = getModule<VCMixer>();
  171. assert(module);
  172. menu->addChild(new MenuSeparator);
  173. menu->addChild(createBoolPtrMenuItem("Exponential channel VCAs", "", &module->chExp));
  174. menu->addChild(createBoolPtrMenuItem("Exponential mix VCA", "", &module->mixExp));
  175. }
  176. };
  177. Model* modelVCMixer = createModel<VCMixer, VCMixerWidget>("VCMixer");