DISTRHO Juice Plugins
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.

391 lines
11KB

  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 "PowerJuiceX2Plugin.hpp"
  18. #include <cstdlib>
  19. #include <algorithm>
  20. START_NAMESPACE_DISTRHO
  21. // -----------------------------------------------------------------------
  22. PowerJuiceX2Plugin::PowerJuiceX2Plugin()
  23. : Plugin(paramCount, 1, 0) // 1 program, 0 states
  24. {
  25. std::memset(&RMSStack, 0, sizeof(RMSStack));
  26. std::memset(&lookaheadStack, 0, sizeof(lookaheadStack));
  27. // set default values
  28. loadProgram(0);
  29. // reset
  30. deactivate();
  31. }
  32. PowerJuiceX2Plugin::~PowerJuiceX2Plugin()
  33. {
  34. std::free(RMSStack.data);
  35. std::free(lookaheadStack.data);
  36. }
  37. // -----------------------------------------------------------------------
  38. // Init
  39. void PowerJuiceX2Plugin::initParameter(uint32_t index, Parameter& parameter)
  40. {
  41. switch (index)
  42. {
  43. case paramAttack:
  44. parameter.hints = kParameterIsAutomatable;
  45. parameter.name = "Attack";
  46. parameter.symbol = "att";
  47. parameter.unit = "ms";
  48. parameter.ranges.def = 20.0f;
  49. parameter.ranges.min = 0.1f;
  50. parameter.ranges.max = 1000.0f;
  51. break;
  52. case paramRelease:
  53. parameter.hints = kParameterIsAutomatable;
  54. parameter.name = "Release";
  55. parameter.symbol = "rel";
  56. parameter.unit = "ms";
  57. parameter.ranges.def = 200.0f;
  58. parameter.ranges.min = 0.1f;
  59. parameter.ranges.max = 1000.0f;
  60. break;
  61. case paramThreshold:
  62. parameter.hints = kParameterIsAutomatable;
  63. parameter.name = "Threshold";
  64. parameter.symbol = "thr";
  65. parameter.unit = "dB";
  66. parameter.ranges.def = 0.0f;
  67. parameter.ranges.min = -60.0f;
  68. parameter.ranges.max = 0.0f;
  69. break;
  70. case paramRatio:
  71. parameter.hints = kParameterIsAutomatable;
  72. parameter.name = "Ratio";
  73. parameter.symbol = "rat";
  74. parameter.unit = "";
  75. parameter.ranges.def = 1.0f;
  76. parameter.ranges.min = 1.0f;
  77. parameter.ranges.max = 10.0f;
  78. break;
  79. case paramMakeup:
  80. parameter.hints = kParameterIsAutomatable;
  81. parameter.name = "Make-Up";
  82. parameter.symbol = "mak";
  83. parameter.unit = "";
  84. parameter.ranges.def = 0.0f;
  85. parameter.ranges.min = 0.0f;
  86. parameter.ranges.max = 20.0f;
  87. break;
  88. case paramMix:
  89. parameter.hints = kParameterIsAutomatable;
  90. parameter.name = "Mix";
  91. parameter.symbol = "Mix";
  92. parameter.unit = "";
  93. parameter.ranges.def = 1.0f;
  94. parameter.ranges.min = 0.0f;
  95. parameter.ranges.max = 1.0f;
  96. break;
  97. }
  98. }
  99. void PowerJuiceX2Plugin::initProgramName(uint32_t index, String& programName)
  100. {
  101. if (index != 0)
  102. return;
  103. programName = "Default";
  104. }
  105. // -----------------------------------------------------------------------
  106. // Internal data
  107. float PowerJuiceX2Plugin::getParameterValue(uint32_t index) const
  108. {
  109. switch (index)
  110. {
  111. case paramAttack:
  112. return attack;
  113. case paramRelease:
  114. return release;
  115. case paramThreshold:
  116. return threshold;
  117. case paramRatio:
  118. return ratio;
  119. case paramMakeup:
  120. return makeup;
  121. case paramMix:
  122. return mix;
  123. default:
  124. return 0.0f;
  125. }
  126. }
  127. void PowerJuiceX2Plugin::setParameterValue(uint32_t index, float value)
  128. {
  129. switch (index)
  130. {
  131. case paramAttack:
  132. attack = value;
  133. attackSamples = getSampleRate()*(attack/1000.0f);
  134. break;
  135. case paramRelease:
  136. release = value;
  137. releaseSamples = getSampleRate()*(release/1000.0f);
  138. break;
  139. case paramThreshold:
  140. threshold = value;
  141. break;
  142. case paramRatio:
  143. ratio = value;
  144. break;
  145. case paramMakeup:
  146. makeup = value;
  147. makeupFloat = fromDB(makeup);
  148. break;
  149. case paramMix:
  150. mix = value;
  151. break;
  152. }
  153. }
  154. void PowerJuiceX2Plugin::loadProgram(uint32_t index)
  155. {
  156. if (index != 0)
  157. return;
  158. /* Default parameter values */
  159. attack = 20.0f;
  160. release = 200.0f;
  161. threshold = 0.0f;
  162. ratio = 1.0f;
  163. makeup = 0.0f;
  164. mix = 1.0f;
  165. makeupFloat = fromDB(makeup);
  166. attackSamples = getSampleRate()*(attack/1000.0f);
  167. releaseSamples = getSampleRate()*(release/1000.0f);
  168. w = 563; //waveform plane size, size of the plane in pixels;
  169. w2 = 1126; //wavefowm array
  170. h = 121; //waveform plane height
  171. x = 27; //waveform plane positions
  172. y = 53;
  173. dc = 113; //0DC line y position
  174. /* Default variable values */
  175. averageCounter = 0;
  176. inputMax = 0.0f;
  177. balancer = 1.0f;
  178. GR = 1.0f;
  179. newRepaint = false;
  180. input.start = 0;
  181. rms.start = 0;
  182. gainReduction.start = 0;
  183. RMSStack.start = 0;
  184. lookaheadStack.start = 0;
  185. repaintSkip = 0;
  186. kFloatRMSStackCount = 400.0f/44100.0f*getSampleRate();
  187. std::free(RMSStack.data);
  188. RMSStack.data = (float*) calloc(kFloatRMSStackCount, sizeof(float));
  189. kFloatLookaheadStackCount = 800.0f/44100.0f*getSampleRate();
  190. std::free(lookaheadStack.data);
  191. lookaheadStack.data = (float*) calloc(kFloatLookaheadStackCount, sizeof(float));
  192. refreshSkip= 300.0f/44100.0f*getSampleRate();
  193. std::memset(rms.data, 0, sizeof(float)*kFloatStackCount);
  194. std::memset(gainReduction.data, 0, sizeof(float)*kFloatStackCount);
  195. std::memset(RMSStack.data, 0, sizeof(float)*kFloatRMSStackCount);
  196. std::memset(lookaheadStack.data, 0, sizeof(float)*kFloatLookaheadStackCount);
  197. for (int j=0; j < kFloatStackCount; ++j)
  198. history.rms[j] = h +y;
  199. for (int j=0; j < kFloatStackCount; ++j)
  200. history.gainReduction[j] = h +y;
  201. activate();
  202. }
  203. float PowerJuiceX2Plugin::getRMSHistory(int n) {
  204. return history.rms[n];
  205. }
  206. bool PowerJuiceX2Plugin::repaintNeeded() {
  207. return newRepaint;
  208. }
  209. float PowerJuiceX2Plugin::getGainReductionHistory(int n) {
  210. if (n == kFloatStackCount-1) {
  211. newRepaint = false;
  212. //printf("falsing!\n");
  213. }
  214. return history.gainReduction[n];
  215. }
  216. // -----------------------------------------------------------------------
  217. // Process
  218. void PowerJuiceX2Plugin::activate()
  219. {
  220. }
  221. void PowerJuiceX2Plugin::deactivate()
  222. {
  223. // all values to zero
  224. }
  225. void PowerJuiceX2Plugin::run(const float** inputs, float** outputs, uint32_t frames)
  226. {
  227. const float* in1 = inputs[0];
  228. const float* in2 = inputs[1];
  229. float* out1 = outputs[0];
  230. float* out2 = outputs[1];
  231. float sum;
  232. float data;
  233. float difference;
  234. for (uint32_t i=0; i < frames; i++) {
  235. sum = 0.0f;
  236. data = 0.0f;
  237. difference = 0;
  238. //sanitizeDenormal(in1[i]); // FIXME - you cannot modify inputs
  239. //sanitizeDenormal(in2[i]); // FIXME - you cannot modify inputs
  240. /* compute last RMS */
  241. //store audio samples in an RMS buffer line
  242. RMSStack.data[RMSStack.start++] = std::max(in1[i], in2[i]);
  243. if (RMSStack.start == kFloatRMSStackCount)
  244. RMSStack.start = 0;
  245. //compute RMS over last kFloatRMSStackCount samples
  246. for (int j=0; j < kFloatRMSStackCount; ++j) {
  247. data = RMSStack.data[(RMSStack.start+j) % kFloatRMSStackCount];
  248. sum += data * data;
  249. }
  250. //root mean SQUARE
  251. float RMS = sqrt(sum / kFloatRMSStackCount);
  252. sanitizeDenormal(RMS);
  253. /* compute gain reduction if needed */
  254. float RMSDB = toDB(RMS);
  255. if (RMSDB>threshold) {
  256. //attack stage
  257. float difference = (RMSDB-threshold);
  258. //sanitizeDenormal(difference);
  259. targetGR = difference - difference/ratio;
  260. if (targetGR>difference/(ratio/4.0f)) {
  261. targetGR = difference - difference/(ratio*1.5f);
  262. //more power!
  263. }
  264. //
  265. if (GR<targetGR) {
  266. //approach targetGR at attackSamples rate
  267. GR -= (GR-targetGR)/(attackSamples);
  268. } else {
  269. //approach targetGR at releaseSamples rate
  270. GR -= (GR-targetGR)/releaseSamples;
  271. }
  272. sanitizeDenormal(GR);
  273. } else {
  274. //release stage
  275. //approach targetGR at releaseSamples rate, targetGR = 0.0f
  276. GR -= GR/releaseSamples;
  277. }
  278. //store audio in lookahead buffer
  279. lookaheadStack.data[lookaheadStack.start++] = std::max(in1[i], in2[i]);
  280. //printf("rms\n");
  281. if (lookaheadStack.start == kFloatLookaheadStackCount)
  282. lookaheadStack.start = 0;
  283. if (++averageCounter >= refreshSkip) {
  284. //add relevant values to the shared memory
  285. rms.data[rms.start++] = RMSDB;
  286. gainReduction.data[gainReduction.start++] = GR;
  287. //rewind stack reading heads if needed
  288. if (rms.start == kFloatStackCount)
  289. rms.start = 0;
  290. if (gainReduction.start == kFloatStackCount)
  291. gainReduction.start = 0;
  292. //saving in gfx format, for speed
  293. //share memory
  294. for (int j=0; j < kFloatStackCount; ++j)
  295. history.rms[j] = -toIEC(rms.data[(rms.start+j) % kFloatStackCount])/200*h +h +y;
  296. for (int j=0; j < kFloatStackCount; ++j) {
  297. history.gainReduction[j] = -toIEC(-gainReduction.data[(gainReduction.start+j) % kFloatStackCount])/200*h +h +y;
  298. }
  299. repaintSkip++;
  300. if (repaintSkip>5) {
  301. repaintSkip = 0;
  302. newRepaint = true;
  303. }
  304. averageCounter = 0;
  305. inputMax = 0.0f;
  306. }
  307. /* compress, mix, done. */
  308. float realGR = fromDB(-GR);
  309. float compressedSignal1 = in1[i]*realGR;
  310. float compressedSignal2 = in2[i]*realGR;
  311. out1[i] = (compressedSignal1*makeupFloat*mix)+in1[i]*(1-mix);
  312. out2[i] = (compressedSignal2*makeupFloat*mix)+in2[i]*(1-mix);
  313. }
  314. }
  315. // -----------------------------------------------------------------------
  316. Plugin* createPlugin()
  317. {
  318. return new PowerJuiceX2Plugin();
  319. }
  320. // -----------------------------------------------------------------------
  321. END_NAMESPACE_DISTRHO