Audio plugin host https://kx.studio/carla
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.

380 lines
10KB

  1. /*
  2. * Power Juice Plugin
  3. * Copyright (C) 2014 Andre Sklenar <andre.sklenar@gmail.com>, www.juicelab.cz
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #include "PowerJuicePlugin.hpp"
  18. //#include <cstring>
  19. #include <cstdlib>
  20. START_NAMESPACE_DISTRHO
  21. // -----------------------------------------------------------------------
  22. PowerJuicePlugin::PowerJuicePlugin()
  23. : Plugin(paramCount, 1, 0) // 1 program, 0 states
  24. {
  25. // set default values
  26. d_setProgram(0);
  27. // reset
  28. d_deactivate();
  29. }
  30. PowerJuicePlugin::~PowerJuicePlugin()
  31. {
  32. free(lookaheadStack.data);
  33. free(RMSStack.data);
  34. }
  35. // -----------------------------------------------------------------------
  36. // Init
  37. void PowerJuicePlugin::d_initParameter(uint32_t index, Parameter& parameter)
  38. {
  39. switch (index)
  40. {
  41. case paramAttack:
  42. parameter.hints = PARAMETER_IS_AUTOMABLE;
  43. parameter.name = "Attack";
  44. parameter.symbol = "att";
  45. parameter.unit = "ms";
  46. parameter.ranges.def = 20.0f;
  47. parameter.ranges.min = 0.1f;
  48. parameter.ranges.max = 1000.0f;
  49. break;
  50. case paramRelease:
  51. parameter.hints = PARAMETER_IS_AUTOMABLE;
  52. parameter.name = "Release";
  53. parameter.symbol = "rel";
  54. parameter.unit = "ms";
  55. parameter.ranges.def = 200.0f;
  56. parameter.ranges.min = 0.1f;
  57. parameter.ranges.max = 1000.0f;
  58. break;
  59. case paramThreshold:
  60. parameter.hints = PARAMETER_IS_AUTOMABLE;
  61. parameter.name = "Threshold";
  62. parameter.symbol = "thr";
  63. parameter.unit = "dB";
  64. parameter.ranges.def = 0.0f;
  65. parameter.ranges.min = -60.0f;
  66. parameter.ranges.max = 0.0f;
  67. break;
  68. case paramRatio:
  69. parameter.hints = PARAMETER_IS_AUTOMABLE;
  70. parameter.name = "Ratio";
  71. parameter.symbol = "rat";
  72. parameter.unit = "";
  73. parameter.ranges.def = 1.0f;
  74. parameter.ranges.min = 1.0f;
  75. parameter.ranges.max = 10.0f;
  76. break;
  77. case paramMakeup:
  78. parameter.hints = PARAMETER_IS_AUTOMABLE;
  79. parameter.name = "Make-Up";
  80. parameter.symbol = "mak";
  81. parameter.unit = "";
  82. parameter.ranges.def = 0.0f;
  83. parameter.ranges.min = 0.0f;
  84. parameter.ranges.max = 20.0f;
  85. break;
  86. case paramMix:
  87. parameter.hints = PARAMETER_IS_AUTOMABLE;
  88. parameter.name = "Mix";
  89. parameter.symbol = "Mix";
  90. parameter.unit = "";
  91. parameter.ranges.def = 1.0f;
  92. parameter.ranges.min = 0.0f;
  93. parameter.ranges.max = 1.0f;
  94. break;
  95. }
  96. }
  97. void PowerJuicePlugin::d_initProgramName(uint32_t index, d_string& programName)
  98. {
  99. if (index != 0)
  100. return;
  101. programName = "Default";
  102. }
  103. // -----------------------------------------------------------------------
  104. // Internal data
  105. float PowerJuicePlugin::d_getParameterValue(uint32_t index) const
  106. {
  107. switch (index)
  108. {
  109. case paramAttack:
  110. return attack;
  111. case paramRelease:
  112. return release;
  113. case paramThreshold:
  114. return threshold;
  115. case paramRatio:
  116. return ratio;
  117. case paramMakeup:
  118. return makeup;
  119. case paramMix:
  120. return mix;
  121. default:
  122. return 0.0f;
  123. }
  124. }
  125. void PowerJuicePlugin::d_setParameterValue(uint32_t index, float value)
  126. {
  127. switch (index)
  128. {
  129. case paramAttack:
  130. attack = value;
  131. attackSamples = d_getSampleRate()*(attack/1000.0f);
  132. break;
  133. case paramRelease:
  134. release = value;
  135. releaseSamples = d_getSampleRate()*(release/1000.0f);
  136. break;
  137. case paramThreshold:
  138. threshold = value;
  139. break;
  140. case paramRatio:
  141. ratio = value;
  142. break;
  143. case paramMakeup:
  144. makeup = value;
  145. makeupFloat = fromDB(makeup);
  146. break;
  147. case paramMix:
  148. mix = value;
  149. break;
  150. }
  151. }
  152. void PowerJuicePlugin::d_setProgram(uint32_t index)
  153. {
  154. if (index != 0)
  155. return;
  156. /* Default parameter values */
  157. attack = 20.0f;
  158. release = 200.0f;
  159. threshold = 0.0f;
  160. ratio = 1.0f;
  161. makeup = 0.0f;
  162. mix = 1.0f;
  163. makeupFloat = fromDB(makeup);
  164. attackSamples = d_getSampleRate()*(attack/1000.0f);
  165. releaseSamples = d_getSampleRate()*(release/1000.0f);
  166. w = 563; //waveform plane size, size of the plane in pixels;
  167. w2 = 1126; //wavefowm array
  168. h = 121; //waveform plane height
  169. x = 27; //waveform plane positions
  170. y = 53;
  171. dc = 113; //0DC line y position
  172. /* Default variable values */
  173. averageCounter = 0;
  174. inputMax = 0.0f;
  175. balancer = 1.0f;
  176. GR = 1.0f;
  177. newRepaint = false;
  178. input.start = 0;
  179. rms.start = 0;
  180. gainReduction.start = 0;
  181. RMSStack.start = 0;
  182. lookaheadStack.start = 0;
  183. repaintSkip = 0;
  184. kFloatRMSStackCount = 400.0f/44100.0f*d_getSampleRate();
  185. RMSStack.data = (float*) calloc(kFloatRMSStackCount, sizeof(float));
  186. kFloatLookaheadStackCount = 800.0f/44100.0f*d_getSampleRate();
  187. lookaheadStack.data = (float*) calloc(kFloatLookaheadStackCount, sizeof(float));
  188. refreshSkip= 300.0f/44100.0f*d_getSampleRate();
  189. std::memset(rms.data, 0, sizeof(float)*kFloatStackCount);
  190. std::memset(gainReduction.data, 0, sizeof(float)*kFloatStackCount);
  191. std::memset(RMSStack.data, 0, sizeof(float)*kFloatRMSStackCount);
  192. std::memset(lookaheadStack.data, 0, sizeof(float)*kFloatLookaheadStackCount);
  193. for (int j=0; j < kFloatStackCount; ++j)
  194. history.rms[j] = h +y;
  195. for (int j=0; j < kFloatStackCount; ++j)
  196. history.gainReduction[j] = h +y;
  197. d_activate();
  198. }
  199. float PowerJuicePlugin::getRMSHistory(int n) {
  200. return history.rms[n];
  201. }
  202. bool PowerJuicePlugin::repaintNeeded() {
  203. return newRepaint;
  204. }
  205. float PowerJuicePlugin::getGainReductionHistory(int n) {
  206. if (n == kFloatStackCount-1) {
  207. newRepaint = false;
  208. //printf("falsing!\n");
  209. }
  210. return history.gainReduction[n];
  211. }
  212. // -----------------------------------------------------------------------
  213. // Process
  214. void PowerJuicePlugin::d_activate()
  215. {
  216. }
  217. void PowerJuicePlugin::d_deactivate()
  218. {
  219. // all values to zero
  220. }
  221. void PowerJuicePlugin::d_run(const float** inputs, float** outputs, uint32_t frames)
  222. {
  223. const float* in = inputs[0];
  224. float* out = outputs[0];
  225. float sum;
  226. float data;
  227. float difference;
  228. for (uint32_t i=0; i < frames; i++) {
  229. sum = 0.0f;
  230. data = 0.0f;
  231. difference = 0;
  232. //sanitizeDenormal(in[i]); // FIXME - you cannot modify inputs
  233. /* compute last RMS */
  234. //store audio samples in an RMS buffer line
  235. RMSStack.data[RMSStack.start++] = in[i];
  236. if (RMSStack.start == kFloatRMSStackCount)
  237. RMSStack.start = 0;
  238. //compute RMS over last kFloatRMSStackCount samples
  239. for (int j=0; j < kFloatRMSStackCount; ++j) {
  240. data = RMSStack.data[(RMSStack.start+j) % kFloatRMSStackCount];
  241. sum += data * data;
  242. }
  243. //root mean SQUARE
  244. float RMS = sqrt(sum / kFloatRMSStackCount);
  245. sanitizeDenormal(RMS);
  246. /* compute gain reduction if needed */
  247. float RMSDB = toDB(RMS);
  248. if (RMSDB>threshold) {
  249. //attack stage
  250. float difference = (RMSDB-threshold);
  251. //sanitizeDenormal(difference);
  252. targetGR = difference - difference/ratio;
  253. if (targetGR>difference/(ratio/4.0f)) {
  254. targetGR = difference - difference/(ratio*1.5f);
  255. //more power!
  256. }
  257. //
  258. if (GR<targetGR) {
  259. //approach targetGR at attackSamples rate
  260. GR -= (GR-targetGR)/(attackSamples);
  261. } else {
  262. //approach targetGR at releaseSamples rate
  263. GR -= (GR-targetGR)/releaseSamples;
  264. }
  265. sanitizeDenormal(GR);
  266. } else {
  267. //release stage
  268. //approach targetGR at releaseSamples rate, targetGR = 0.0f
  269. GR -= GR/releaseSamples;
  270. }
  271. //store audio in lookahead buffer
  272. lookaheadStack.data[lookaheadStack.start++] = in[i];
  273. //printf("rms\n");
  274. if (lookaheadStack.start == kFloatLookaheadStackCount)
  275. lookaheadStack.start = 0;
  276. if (++averageCounter >= refreshSkip) {
  277. //add relevant values to the shared memory
  278. rms.data[rms.start++] = RMSDB;
  279. gainReduction.data[gainReduction.start++] = GR;
  280. //rewind stack reading heads if needed
  281. if (rms.start == kFloatStackCount)
  282. rms.start = 0;
  283. if (gainReduction.start == kFloatStackCount)
  284. gainReduction.start = 0;
  285. //saving in gfx format, for speed
  286. //share memory
  287. for (int j=0; j < kFloatStackCount; ++j)
  288. history.rms[j] = -toIEC(rms.data[(rms.start+j) % kFloatStackCount])/200*h +h +y;
  289. for (int j=0; j < kFloatStackCount; ++j) {
  290. history.gainReduction[j] = -toIEC(-gainReduction.data[(gainReduction.start+j) % kFloatStackCount])/200*h +h +y;
  291. }
  292. repaintSkip++;
  293. if (repaintSkip>5) {
  294. repaintSkip = 0;
  295. newRepaint = true;
  296. }
  297. averageCounter = 0;
  298. inputMax = 0.0f;
  299. }
  300. /* compress, mix, done. */
  301. float compressedSignal = in[i]*fromDB(-GR);
  302. out[i] = (compressedSignal*makeupFloat*mix)+in[i]*(1-mix);
  303. }
  304. }
  305. // -----------------------------------------------------------------------
  306. Plugin* createPlugin()
  307. {
  308. return new PowerJuicePlugin();
  309. }
  310. // -----------------------------------------------------------------------
  311. END_NAMESPACE_DISTRHO