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.

386 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. // set default values
  26. loadProgram(0);
  27. // reset
  28. deactivate();
  29. }
  30. PowerJuiceX2Plugin::~PowerJuiceX2Plugin()
  31. {
  32. free(lookaheadStack.data);
  33. free(RMSStack.data);
  34. }
  35. // -----------------------------------------------------------------------
  36. // Init
  37. void PowerJuiceX2Plugin::initParameter(uint32_t index, Parameter& parameter)
  38. {
  39. switch (index)
  40. {
  41. case paramAttack:
  42. parameter.hints = kParameterIsAutomable;
  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 = kParameterIsAutomable;
  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 = kParameterIsAutomable;
  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 = kParameterIsAutomable;
  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 = kParameterIsAutomable;
  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 = kParameterIsAutomable;
  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 PowerJuiceX2Plugin::initProgramName(uint32_t index, String& programName)
  98. {
  99. if (index != 0)
  100. return;
  101. programName = "Default";
  102. }
  103. // -----------------------------------------------------------------------
  104. // Internal data
  105. float PowerJuiceX2Plugin::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 PowerJuiceX2Plugin::setParameterValue(uint32_t index, float value)
  126. {
  127. switch (index)
  128. {
  129. case paramAttack:
  130. attack = value;
  131. attackSamples = getSampleRate()*(attack/1000.0f);
  132. break;
  133. case paramRelease:
  134. release = value;
  135. releaseSamples = 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 PowerJuiceX2Plugin::loadProgram(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 = getSampleRate()*(attack/1000.0f);
  165. releaseSamples = 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*getSampleRate();
  185. RMSStack.data = (float*) calloc(kFloatRMSStackCount, sizeof(float));
  186. kFloatLookaheadStackCount = 800.0f/44100.0f*getSampleRate();
  187. lookaheadStack.data = (float*) calloc(kFloatLookaheadStackCount, sizeof(float));
  188. refreshSkip= 300.0f/44100.0f*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. activate();
  198. }
  199. float PowerJuiceX2Plugin::getRMSHistory(int n) {
  200. return history.rms[n];
  201. }
  202. bool PowerJuiceX2Plugin::repaintNeeded() {
  203. return newRepaint;
  204. }
  205. float PowerJuiceX2Plugin::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 PowerJuiceX2Plugin::activate()
  215. {
  216. }
  217. void PowerJuiceX2Plugin::deactivate()
  218. {
  219. // all values to zero
  220. }
  221. void PowerJuiceX2Plugin::run(const float** inputs, float** outputs, uint32_t frames)
  222. {
  223. const float* in1 = inputs[0];
  224. const float* in2 = inputs[1];
  225. float* out1 = outputs[0];
  226. float* out2 = outputs[1];
  227. float sum;
  228. float data;
  229. float difference;
  230. for (uint32_t i=0; i < frames; i++) {
  231. sum = 0.0f;
  232. data = 0.0f;
  233. difference = 0;
  234. //sanitizeDenormal(in1[i]); // FIXME - you cannot modify inputs
  235. //sanitizeDenormal(in2[i]); // FIXME - you cannot modify inputs
  236. /* compute last RMS */
  237. //store audio samples in an RMS buffer line
  238. RMSStack.data[RMSStack.start++] = std::max(in1[i], in2[i]);
  239. if (RMSStack.start == kFloatRMSStackCount)
  240. RMSStack.start = 0;
  241. //compute RMS over last kFloatRMSStackCount samples
  242. for (int j=0; j < kFloatRMSStackCount; ++j) {
  243. data = RMSStack.data[(RMSStack.start+j) % kFloatRMSStackCount];
  244. sum += data * data;
  245. }
  246. //root mean SQUARE
  247. float RMS = sqrt(sum / kFloatRMSStackCount);
  248. sanitizeDenormal(RMS);
  249. /* compute gain reduction if needed */
  250. float RMSDB = toDB(RMS);
  251. if (RMSDB>threshold) {
  252. //attack stage
  253. float difference = (RMSDB-threshold);
  254. //sanitizeDenormal(difference);
  255. targetGR = difference - difference/ratio;
  256. if (targetGR>difference/(ratio/4.0f)) {
  257. targetGR = difference - difference/(ratio*1.5f);
  258. //more power!
  259. }
  260. //
  261. if (GR<targetGR) {
  262. //approach targetGR at attackSamples rate
  263. GR -= (GR-targetGR)/(attackSamples);
  264. } else {
  265. //approach targetGR at releaseSamples rate
  266. GR -= (GR-targetGR)/releaseSamples;
  267. }
  268. sanitizeDenormal(GR);
  269. } else {
  270. //release stage
  271. //approach targetGR at releaseSamples rate, targetGR = 0.0f
  272. GR -= GR/releaseSamples;
  273. }
  274. //store audio in lookahead buffer
  275. lookaheadStack.data[lookaheadStack.start++] = std::max(in1[i], in2[i]);
  276. //printf("rms\n");
  277. if (lookaheadStack.start == kFloatLookaheadStackCount)
  278. lookaheadStack.start = 0;
  279. if (++averageCounter >= refreshSkip) {
  280. //add relevant values to the shared memory
  281. rms.data[rms.start++] = RMSDB;
  282. gainReduction.data[gainReduction.start++] = GR;
  283. //rewind stack reading heads if needed
  284. if (rms.start == kFloatStackCount)
  285. rms.start = 0;
  286. if (gainReduction.start == kFloatStackCount)
  287. gainReduction.start = 0;
  288. //saving in gfx format, for speed
  289. //share memory
  290. for (int j=0; j < kFloatStackCount; ++j)
  291. history.rms[j] = -toIEC(rms.data[(rms.start+j) % kFloatStackCount])/200*h +h +y;
  292. for (int j=0; j < kFloatStackCount; ++j) {
  293. history.gainReduction[j] = -toIEC(-gainReduction.data[(gainReduction.start+j) % kFloatStackCount])/200*h +h +y;
  294. }
  295. repaintSkip++;
  296. if (repaintSkip>5) {
  297. repaintSkip = 0;
  298. newRepaint = true;
  299. }
  300. averageCounter = 0;
  301. inputMax = 0.0f;
  302. }
  303. /* compress, mix, done. */
  304. float realGR = fromDB(-GR);
  305. float compressedSignal1 = in1[i]*realGR;
  306. float compressedSignal2 = in2[i]*realGR;
  307. out1[i] = (compressedSignal1*makeupFloat*mix)+in1[i]*(1-mix);
  308. out2[i] = (compressedSignal2*makeupFloat*mix)+in2[i]*(1-mix);
  309. }
  310. }
  311. // -----------------------------------------------------------------------
  312. Plugin* createPlugin()
  313. {
  314. return new PowerJuiceX2Plugin();
  315. }
  316. // -----------------------------------------------------------------------
  317. END_NAMESPACE_DISTRHO