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.

196 lines
7.0KB

  1. /*
  2. * ZamComp mono compressor for Cardinal
  3. * Copyright (C) 2014-2019 Damien Zammit <damien@zamaudio.com>
  4. * Copyright (C) 2022 Filipe Coelho <falktx@falktx.com>
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License as
  8. * published by the Free Software Foundation; either version 2 of
  9. * the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * For a full copy of the GNU General Public License see the LICENSE file.
  17. */
  18. #include "plugin.hpp"
  19. #include "widgets.hpp"
  20. // --------------------------------------------------------------------------------------------------------------------
  21. struct ZamAudioCompModule : Module {
  22. enum ParamIds {
  23. PARAM_ATTACK,
  24. PARAM_RELEASE,
  25. PARAM_THRESHOLD,
  26. PARAM_RATIO,
  27. PARAM_KNEE,
  28. PARAM_SLEW,
  29. PARAM_MAKEUP,
  30. NUM_PARAMS
  31. };
  32. enum InputIds {
  33. AUDIO_INPUT,
  34. SIDECHAIN_INPUT,
  35. NUM_INPUTS
  36. };
  37. enum OutputIds {
  38. AUDIO_OUTPUT,
  39. NUM_OUTPUTS
  40. };
  41. enum LightIds {
  42. OUTLEVEL,
  43. GAINREDUCTION,
  44. NUM_LIGHTS
  45. };
  46. float gainred,outlevel; //lights
  47. float oldL_yl, oldL_y1, oldL_yg; //temp
  48. static inline float
  49. sanitize_denormal(float v) {
  50. if(!std::isnormal(v))
  51. return 0.f;
  52. return v;
  53. }
  54. static inline float
  55. from_dB(float gdb) {
  56. return (expf(0.05f*gdb*logf(10.f)));
  57. }
  58. static inline float
  59. to_dB(float g) {
  60. return (20.f*log10f(g));
  61. }
  62. ZamAudioCompModule()
  63. {
  64. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  65. configParam(PARAM_ATTACK, 0.1f, 100.f, 10.f, "Attack", " ms");
  66. configParam(PARAM_RELEASE, 1.f, 500.f, 80.f, "Release", " ms");
  67. configParam(PARAM_THRESHOLD, -80.f, 0.f, 0.f, "Threshold", " db");
  68. configParam(PARAM_RATIO, 1.f, 20.f, 4.f, "Ratio");
  69. configParam(PARAM_KNEE, 0.f, 8.f, 0.f, "Knee", " dB");
  70. configParam(PARAM_SLEW, 1.f, 150.f, 1.f, "Slew");
  71. configParam(PARAM_MAKEUP, 0.f, 30.f, 0.f, "Makeup", " dB");
  72. configInput(AUDIO_INPUT, "Audio");
  73. configInput(SIDECHAIN_INPUT, "Sidechain");
  74. configOutput(AUDIO_OUTPUT, "Audio");
  75. configLight(OUTLEVEL, "Output Level");
  76. configLight(GAINREDUCTION, "Gain Reduction");
  77. gainred = 0.0f;
  78. outlevel = -45.0f;
  79. oldL_yl = oldL_y1 = oldL_yg = 0.f;
  80. }
  81. void process(const ProcessArgs& args) override
  82. {
  83. const float attack = params[PARAM_ATTACK].getValue();
  84. const float release = params[PARAM_RELEASE].getValue();
  85. const float thresdb = params[PARAM_THRESHOLD].getValue();
  86. const float ratio = params[PARAM_RATIO].getValue();
  87. const float knee = params[PARAM_KNEE].getValue();
  88. const float slewfactor = params[PARAM_SLEW].getValue();
  89. const float makeup = params[PARAM_MAKEUP].getValue();
  90. const float srate = args.sampleRate;
  91. const float width = (6.f * knee) + 0.01;
  92. const float slewwidth = 1.8f;
  93. const float release_coeff = exp(-1000.f/(release * srate));
  94. // const float gain = std::pow(params[0].getValue(), 2.f);
  95. const float in0 = inputs[AUDIO_INPUT].getVoltageSum() * 0.1f;
  96. const float in = inputs[SIDECHAIN_INPUT].isConnected()
  97. ? inputs[SIDECHAIN_INPUT].getVoltageSum() * 0.1f
  98. : in0;
  99. const float Lxg = sanitize_denormal(in == 0.f ? -160.f : to_dB(fabsf(in)));
  100. const float checkwidth = 2.f*fabsf(Lxg-thresdb);
  101. bool attslew = false;
  102. float Lyg;
  103. if (2.f*(Lxg-thresdb) < -width) {
  104. Lyg = Lxg;
  105. } else if (checkwidth <= width) {
  106. Lyg = thresdb + (Lxg-thresdb)/ratio;
  107. Lyg = sanitize_denormal(Lyg);
  108. if (checkwidth <= slewwidth) {
  109. if (Lyg >= oldL_yg) {
  110. attslew = true;
  111. }
  112. }
  113. } else if (2.f*(Lxg-thresdb) > width) {
  114. Lyg = thresdb + (Lxg-thresdb)/ratio;
  115. Lyg = sanitize_denormal(Lyg);
  116. } else {
  117. Lyg = Lxg + (1.f/ratio-1.f)*(Lxg-thresdb+width/2.f)*(Lxg-thresdb+width/2.f)/(2.f*width);
  118. }
  119. const float attack_coeff = attslew
  120. ? exp(-1000.f/((attack + 2.0*(slewfactor - 1)) * srate))
  121. : exp(-1000.f/(attack * srate));
  122. // Don't slew on release
  123. const float Lxl = Lxg - Lyg;
  124. const float Lyl = sanitize_denormal(Lxl < oldL_yl ? release_coeff * oldL_yl + (1.f-release_coeff)*Lxl
  125. : Lxl > oldL_yl ? attack_coeff * oldL_yl+(1.f-attack_coeff)*Lxl : Lxl);
  126. const float Lgain = from_dB(-Lyl);
  127. const float out = in0 * Lgain * from_dB(makeup);
  128. outputs[AUDIO_OUTPUT].setVoltage(out * 10.0f);
  129. oldL_yl = Lyl;
  130. oldL_yg = Lyg;
  131. gainred = Lyl;
  132. // const float max = (fabsf(out) > max) ? fabsf(outputs[0][i]) : sanitize_denormal(max);
  133. // outlevel = (max == 0.f) ? -45.f : to_dB(max); // relative to - thresdb;
  134. }
  135. };
  136. // --------------------------------------------------------------------------------------------------------------------
  137. struct ZamAudioCompModuleWidget : ZamAudioModuleWidget {
  138. typedef FundamentalBlackKnob<36> BigKnob;
  139. ZamAudioCompModule* const module;
  140. ZamAudioCompModuleWidget(ZamAudioCompModule* const m)
  141. : ZamAudioModuleWidget(),
  142. module(m)
  143. {
  144. setModule(module);
  145. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ZamComp.svg")));
  146. addInput(createInput<PJ301MPort>(Vec(32, 280), m, ZamAudioCompModule::AUDIO_INPUT));
  147. addInput(createInput<PJ301MPort>(Vec(32, 310), m, ZamAudioCompModule::SIDECHAIN_INPUT));
  148. addOutput(createOutput<PJ301MPort>(Vec(100, 310), m, ZamAudioCompModule::AUDIO_OUTPUT));
  149. const float scale = 0.8f;
  150. addParam(createParamCentered<BigKnob>(Vec(45.75f * scale, 121.25f * scale), m, ZamAudioCompModule::PARAM_ATTACK));
  151. addParam(createParamCentered<BigKnob>(Vec(127.75f * scale, 121.25f * scale), m, ZamAudioCompModule::PARAM_RELEASE));
  152. addParam(createParamCentered<BigKnob>(Vec(48.25f * scale, 208.f * scale), m, ZamAudioCompModule::PARAM_THRESHOLD));
  153. addParam(createParamCentered<BigKnob>(Vec(130.75f * scale, 208.f * scale), m, ZamAudioCompModule::PARAM_RATIO));
  154. addParam(createParamCentered<BigKnob>(Vec(48.25f * scale, 305.f * scale), m, ZamAudioCompModule::PARAM_KNEE));
  155. addParam(createParamCentered<BigKnob>(Vec(130.75f * scale, 305.f * scale), m, ZamAudioCompModule::PARAM_SLEW));
  156. }
  157. };
  158. // --------------------------------------------------------------------------------------------------------------------
  159. Model* modelZamComp = createModel<ZamAudioCompModule, ZamAudioCompModuleWidget>("ZamComp");
  160. // --------------------------------------------------------------------------------------------------------------------