Compressor.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. /*
  2. * Compressor.cpp
  3. *
  4. * Copyright (c) 2020 Lost Robot <r94231@gmail.com>
  5. *
  6. * This file is part of LMMS - https://lmms.io
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2 of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public
  19. * License along with this program (see COPYING); if not, write to the
  20. * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  21. * Boston, MA 02110-1301 USA.
  22. *
  23. */
  24. #include "Compressor.h"
  25. #include "embed.h"
  26. #include "interpolation.h"
  27. #include "lmms_math.h"
  28. #include "plugin_export.h"
  29. extern "C"
  30. {
  31. Plugin::Descriptor PLUGIN_EXPORT compressor_plugin_descriptor =
  32. {
  33. STRINGIFY(PLUGIN_NAME),
  34. "Compressor",
  35. QT_TRANSLATE_NOOP("PluginBrowser", "A dynamic range compressor."),
  36. "Lost Robot <r94231@gmail.com>",
  37. 0x0100,
  38. Plugin::Effect,
  39. new PluginPixmapLoader("logo"),
  40. NULL,
  41. NULL
  42. } ;
  43. }
  44. CompressorEffect::CompressorEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key) :
  45. Effect(&compressor_plugin_descriptor, parent, key),
  46. m_compressorControls(this)
  47. {
  48. m_sampleRate = Engine::mixer()->processingSampleRate();
  49. m_yL[0] = m_yL[1] = COMP_NOISE_FLOOR;
  50. m_maxLookaheadVal[0] = 0;
  51. m_maxLookaheadVal[1] = 0;
  52. // 200 ms
  53. m_crestTimeConst = exp(-1.f / (0.2f * m_sampleRate));
  54. connect(&m_compressorControls.m_attackModel, SIGNAL(dataChanged()), this, SLOT(calcAttack()), Qt::DirectConnection);
  55. connect(&m_compressorControls.m_releaseModel, SIGNAL(dataChanged()), this, SLOT(calcRelease()), Qt::DirectConnection);
  56. connect(&m_compressorControls.m_holdModel, SIGNAL(dataChanged()), this, SLOT(calcHold()), Qt::DirectConnection);
  57. connect(&m_compressorControls.m_ratioModel, SIGNAL(dataChanged()), this, SLOT(calcRatio()), Qt::DirectConnection);
  58. connect(&m_compressorControls.m_rangeModel, SIGNAL(dataChanged()), this, SLOT(calcRange()), Qt::DirectConnection);
  59. connect(&m_compressorControls.m_rmsModel, SIGNAL(dataChanged()), this, SLOT(resizeRMS()), Qt::DirectConnection);
  60. connect(&m_compressorControls.m_lookaheadLengthModel, SIGNAL(dataChanged()), this, SLOT(calcLookaheadLength()), Qt::DirectConnection);
  61. connect(&m_compressorControls.m_thresholdModel, SIGNAL(dataChanged()), this, SLOT(calcThreshold()), Qt::DirectConnection);
  62. connect(&m_compressorControls.m_kneeModel, SIGNAL(dataChanged()), this, SLOT(calcKnee()), Qt::DirectConnection);
  63. connect(&m_compressorControls.m_outGainModel, SIGNAL(dataChanged()), this, SLOT(calcOutGain()), Qt::DirectConnection);
  64. connect(&m_compressorControls.m_inGainModel, SIGNAL(dataChanged()), this, SLOT(calcInGain()), Qt::DirectConnection);
  65. connect(&m_compressorControls.m_tiltModel, SIGNAL(dataChanged()), this, SLOT(calcTiltCoeffs()), Qt::DirectConnection);
  66. connect(&m_compressorControls.m_tiltFreqModel, SIGNAL(dataChanged()), this, SLOT(calcTiltCoeffs()), Qt::DirectConnection);
  67. connect(&m_compressorControls.m_limiterModel, SIGNAL(dataChanged()), this, SLOT(redrawKnee()), Qt::DirectConnection);
  68. connect(&m_compressorControls.m_mixModel, SIGNAL(dataChanged()), this, SLOT(calcMix()), Qt::DirectConnection);
  69. connect(&m_compressorControls.m_autoAttackModel, SIGNAL(dataChanged()), this, SLOT(calcAutoAttack()), Qt::DirectConnection);
  70. connect(&m_compressorControls.m_autoReleaseModel, SIGNAL(dataChanged()), this, SLOT(calcAutoRelease()), Qt::DirectConnection);
  71. connect(&m_compressorControls.m_thresholdModel, SIGNAL(dataChanged()), this, SLOT(calcAutoMakeup()), Qt::DirectConnection);
  72. connect(&m_compressorControls.m_ratioModel, SIGNAL(dataChanged()), this, SLOT(calcAutoMakeup()), Qt::DirectConnection);
  73. connect(&m_compressorControls.m_kneeModel, SIGNAL(dataChanged()), this, SLOT(calcAutoMakeup()), Qt::DirectConnection);
  74. connect(&m_compressorControls.m_autoMakeupModel, SIGNAL(dataChanged()), this, SLOT(calcAutoMakeup()), Qt::DirectConnection);
  75. connect(Engine::mixer(), SIGNAL(sampleRateChanged()), this, SLOT(changeSampleRate()));
  76. changeSampleRate();
  77. }
  78. CompressorEffect::~CompressorEffect()
  79. {
  80. }
  81. float CompressorEffect::msToCoeff(float ms)
  82. {
  83. // Convert time in milliseconds to applicable lowpass coefficient
  84. return exp(m_coeffPrecalc / ms);
  85. }
  86. void CompressorEffect::calcAutoMakeup()
  87. {
  88. // Formulas using the compressor's Threshold, Ratio, and Knee values to estimate a good makeup gain value
  89. float tempGainResult;
  90. if (-m_thresholdVal < m_kneeVal)
  91. {
  92. const float temp = -m_thresholdVal + m_kneeVal;
  93. tempGainResult = ((m_compressorControls.m_limiterModel.value() ? 0 : m_ratioVal) - 1) * temp * temp / (4 * m_kneeVal);
  94. }
  95. else// Above knee
  96. {
  97. tempGainResult = m_compressorControls.m_limiterModel.value()
  98. ? m_thresholdVal
  99. : m_thresholdVal - m_thresholdVal * m_ratioVal;
  100. }
  101. m_autoMakeupVal = 1.f / dbfsToAmp(tempGainResult);
  102. }
  103. void CompressorEffect::calcAttack()
  104. {
  105. m_attCoeff = msToCoeff(m_compressorControls.m_attackModel.value());
  106. }
  107. void CompressorEffect::calcRelease()
  108. {
  109. m_relCoeff = msToCoeff(m_compressorControls.m_releaseModel.value());
  110. }
  111. void CompressorEffect::calcAutoAttack()
  112. {
  113. m_autoAttVal = m_compressorControls.m_autoAttackModel.value() * 0.01f;
  114. }
  115. void CompressorEffect::calcAutoRelease()
  116. {
  117. m_autoRelVal = m_compressorControls.m_autoReleaseModel.value() * 0.01f;
  118. }
  119. void CompressorEffect::calcHold()
  120. {
  121. m_holdLength = m_compressorControls.m_holdModel.value() * 0.001f * m_sampleRate;
  122. m_holdTimer[0] = 0;
  123. m_holdTimer[1] = 0;
  124. }
  125. void CompressorEffect::calcOutGain()
  126. {
  127. // 0.999 is needed to keep the values from crossing the threshold all the time
  128. // (most commonly for limiters specifically), and is kept across all modes for consistency.
  129. m_outGainVal = dbfsToAmp(m_compressorControls.m_outGainModel.value()) * 0.999;
  130. }
  131. void CompressorEffect::calcRatio()
  132. {
  133. m_ratioVal = 1.f / m_compressorControls.m_ratioModel.value();
  134. m_redrawKnee = true;
  135. }
  136. void CompressorEffect::calcRange()
  137. {
  138. // Range is inactive when turned all the way down
  139. m_rangeVal = (m_compressorControls.m_rangeModel.value() > m_compressorControls.m_rangeModel.minValue())
  140. ? dbfsToAmp(m_compressorControls.m_rangeModel.value())
  141. : 0;
  142. }
  143. void CompressorEffect::resizeRMS()
  144. {
  145. m_rmsTimeConst = exp(-1.f / (m_compressorControls.m_rmsModel.value() * 0.001f * m_sampleRate));
  146. }
  147. void CompressorEffect::calcLookaheadLength()
  148. {
  149. m_lookaheadLength = qMax(m_compressorControls.m_lookaheadLengthModel.value() * 0.001f * m_sampleRate, 1.f);
  150. m_preLookaheadLength = ceil(m_lookaheadDelayLength - m_lookaheadLength);
  151. }
  152. void CompressorEffect::calcThreshold()
  153. {
  154. m_thresholdVal = m_compressorControls.m_thresholdModel.value();
  155. m_thresholdAmpVal = dbfsToAmp(m_thresholdVal);
  156. m_redrawKnee = true;
  157. m_redrawThreshold = true;
  158. }
  159. void CompressorEffect::calcKnee()
  160. {
  161. m_kneeVal = m_compressorControls.m_kneeModel.value() * 0.5f;
  162. m_redrawKnee = true;
  163. }
  164. void CompressorEffect::calcInGain()
  165. {
  166. m_inGainVal = dbfsToAmp(m_compressorControls.m_inGainModel.value());
  167. }
  168. void CompressorEffect::redrawKnee()
  169. {
  170. m_redrawKnee = true;
  171. }
  172. void CompressorEffect::calcTiltCoeffs()
  173. {
  174. m_tiltVal = m_compressorControls.m_tiltModel.value();
  175. const float amp = 6 / log(2);
  176. const float gfactor = 5;
  177. const float g1 = m_tiltVal > 0 ? -gfactor * m_tiltVal : -m_tiltVal;
  178. const float g2 = m_tiltVal > 0 ? m_tiltVal : gfactor * m_tiltVal;
  179. m_lgain = exp(g1 / amp) - 1;
  180. m_hgain = exp(g2 / amp) - 1;
  181. const float omega = 2 * F_PI * m_compressorControls.m_tiltFreqModel.value();
  182. const float n = 1 / (m_sampleRate * 3 + omega);
  183. m_a0 = 2 * omega * n;
  184. m_b1 = (m_sampleRate * 3 - omega) * n;
  185. }
  186. void CompressorEffect::calcMix()
  187. {
  188. m_mixVal = m_compressorControls.m_mixModel.value() * 0.01;
  189. }
  190. bool CompressorEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames)
  191. {
  192. if (!isEnabled() || !isRunning())
  193. {
  194. // Clear lookahead buffers and other values when needed
  195. if (!m_cleanedBuffers)
  196. {
  197. m_yL[0] = m_yL[1] = COMP_NOISE_FLOOR;
  198. m_gainResult[0] = m_gainResult[1] = 1;
  199. m_displayPeak[0] = m_displayPeak[1] = COMP_NOISE_FLOOR;
  200. m_displayGain[0] = m_displayGain[1] = COMP_NOISE_FLOOR;
  201. std::fill(std::begin(m_lookaheadBuf[0]), std::end(m_lookaheadBuf[0]), 0);
  202. std::fill(std::begin(m_lookaheadBuf[1]), std::end(m_lookaheadBuf[1]), 0);
  203. m_lookaheadBufLoc[0] = 0;
  204. m_lookaheadBufLoc[1] = 0;
  205. std::fill(std::begin(m_preLookaheadBuf[0]), std::end(m_preLookaheadBuf[0]), 0);
  206. std::fill(std::begin(m_preLookaheadBuf[1]), std::end(m_preLookaheadBuf[1]), 0);
  207. m_preLookaheadBufLoc[0] = 0;
  208. m_preLookaheadBufLoc[1] = 0;
  209. std::fill(std::begin(m_inputBuf[0]), std::end(m_inputBuf[0]), 0);
  210. std::fill(std::begin(m_inputBuf[1]), std::end(m_inputBuf[1]), 0);
  211. m_inputBufLoc = 0;
  212. m_cleanedBuffers = true;
  213. }
  214. return false;
  215. }
  216. else
  217. {
  218. m_cleanedBuffers = false;
  219. }
  220. float outSum = 0.0;
  221. const float d = dryLevel();
  222. const float w = wetLevel();
  223. float lOutPeak = 0.0;
  224. float rOutPeak = 0.0;
  225. float lInPeak = 0.0;
  226. float rInPeak = 0.0;
  227. const bool midside = m_compressorControls.m_midsideModel.value();
  228. const bool peakmode = m_compressorControls.m_peakmodeModel.value();
  229. const float inBalance = m_compressorControls.m_inBalanceModel.value();
  230. const float outBalance = m_compressorControls.m_outBalanceModel.value();
  231. const bool limiter = m_compressorControls.m_limiterModel.value();
  232. const float blend = m_compressorControls.m_blendModel.value();
  233. const float stereoBalance = m_compressorControls.m_stereoBalanceModel.value();
  234. const bool autoMakeup = m_compressorControls.m_autoMakeupModel.value();
  235. const int stereoLink = m_compressorControls.m_stereoLinkModel.value();
  236. const bool audition = m_compressorControls.m_auditionModel.value();
  237. const bool feedback = m_compressorControls.m_feedbackModel.value();
  238. const bool lookahead = m_compressorControls.m_lookaheadModel.value();
  239. for(fpp_t f = 0; f < frames; ++f)
  240. {
  241. sample_t drySignal[2] = {buf[f][0], buf[f][1]};
  242. sample_t s[2] = {drySignal[0] * m_inGainVal, drySignal[1] * m_inGainVal};
  243. // Calculate tilt filters, to bias the sidechain to the low or high frequencies
  244. if (m_tiltVal)
  245. {
  246. calcTiltFilter(s[0], s[0], 0);
  247. calcTiltFilter(s[1], s[1], 1);
  248. }
  249. if (midside)// Convert left/right to mid/side
  250. {
  251. const float temp = s[0];
  252. s[0] = (temp + s[1]) * 0.5;
  253. s[1] = temp - s[1];
  254. }
  255. s[0] *= inBalance > 0 ? 1 - inBalance : 1;
  256. s[1] *= inBalance < 0 ? 1 + inBalance : 1;
  257. m_gainResult[0] = 0;
  258. m_gainResult[1] = 0;
  259. for (int i = 0; i < 2; i++)
  260. {
  261. float inputValue = feedback ? m_prevOut[i] : s[i];
  262. // Calculate the crest factor of the audio by diving the peak by the RMS
  263. m_crestPeakVal[i] = qMax(inputValue * inputValue, m_crestTimeConst * m_crestPeakVal[i] + (1 - m_crestTimeConst) * (inputValue * inputValue));
  264. m_crestRmsVal[i] = m_crestTimeConst * m_crestRmsVal[i] + ((1 - m_crestTimeConst) * (inputValue * inputValue));
  265. m_crestFactorVal[i] = m_crestPeakVal[i] / m_crestRmsVal[i];
  266. m_rmsVal[i] = m_rmsTimeConst * m_rmsVal[i] + ((1 - m_rmsTimeConst) * (inputValue * inputValue));
  267. // Grab the peak or RMS value
  268. inputValue = qMax(COMP_NOISE_FLOOR, peakmode ? abs(inputValue) : sqrt(m_rmsVal[i]));
  269. // The following code uses math magic to semi-efficiently
  270. // find the largest value in the lookahead buffer.
  271. // This can probably be improved.
  272. if (lookahead)
  273. {
  274. // Pre-lookahead delay, so the total delay always matches 20 ms
  275. ++m_preLookaheadBufLoc[i];
  276. if (m_preLookaheadBufLoc[i] >= m_preLookaheadLength)
  277. {
  278. m_preLookaheadBufLoc[i] = 0;
  279. }
  280. const float tempInputValue = inputValue;
  281. inputValue = m_preLookaheadBuf[i][m_preLookaheadBufLoc[i]];
  282. m_preLookaheadBuf[i][m_preLookaheadBufLoc[i]] = tempInputValue;
  283. // Increment ring buffer location
  284. ++m_lookaheadBufLoc[i];
  285. if (m_lookaheadBufLoc[i] >= m_lookaheadLength)
  286. {
  287. m_lookaheadBufLoc[i] = 0;
  288. }
  289. m_lookaheadBuf[i][m_lookaheadBufLoc[i]] = inputValue;
  290. // If the new input value is larger than the stored maximum,
  291. // store that as the maximum
  292. if (inputValue >= m_maxLookaheadVal[i])
  293. {
  294. m_maxLookaheadVal[i] = inputValue;
  295. m_maxLookaheadTimer[i] = m_lookaheadLength;
  296. }
  297. // Decrement timer. When the timer reaches 0, that means the
  298. // stored maximum value has left the buffer and a new
  299. // maximum value must be found.
  300. if (--m_maxLookaheadTimer[i] <= 0)
  301. {
  302. m_maxLookaheadTimer[i] = std::distance(std::begin(m_lookaheadBuf[i]),
  303. std::max_element(std::begin(m_lookaheadBuf[i]), std::begin(m_lookaheadBuf[i]) + m_lookaheadLength));
  304. m_maxLookaheadVal[i] = m_lookaheadBuf[i][m_maxLookaheadTimer[i]];
  305. m_maxLookaheadTimer[i] = realmod(m_maxLookaheadTimer[i] - m_lookaheadBufLoc[i], m_lookaheadLength);
  306. }
  307. inputValue = m_maxLookaheadVal[i];
  308. }
  309. float t = inputValue;
  310. if (t > m_yL[i])// Attack phase
  311. {
  312. // We want the "resting value" of our crest factor to be with a sine wave,
  313. // which with this variable has a value of 2.
  314. // So, we pull this value down to 0, and multiply it by the percentage of
  315. // automatic attack control that is applied. We then add 2 back to it.
  316. float crestFactorValTemp = ((m_crestFactorVal[i] - 2.f) * m_autoAttVal) + 2.f;
  317. // Calculate attack value depending on crest factor
  318. const float att = m_autoAttVal
  319. ? msToCoeff(2.f * m_compressorControls.m_attackModel.value() / (crestFactorValTemp))
  320. : m_attCoeff;
  321. m_yL[i] = m_yL[i] * att + (1 - att) * t;
  322. m_holdTimer[i] = m_holdLength;// Reset hold timer
  323. }
  324. else// Release phase
  325. {
  326. float crestFactorValTemp = ((m_crestFactorVal[i] - 2.f) * m_autoRelVal) + 2.f;
  327. const float rel = m_autoRelVal
  328. ? msToCoeff(2.f * m_compressorControls.m_releaseModel.value() / (crestFactorValTemp))
  329. : m_relCoeff;
  330. if (m_holdTimer[i])// Don't change peak if hold is being applied
  331. {
  332. --m_holdTimer[i];
  333. }
  334. else
  335. {
  336. m_yL[i] = m_yL[i] * rel + (1 - rel) * t;
  337. }
  338. }
  339. // Keep it above the noise floor
  340. m_yL[i] = qMax(COMP_NOISE_FLOOR, m_yL[i]);
  341. // For the visualizer
  342. m_displayPeak[i] = qMax(m_yL[i], m_displayPeak[i]);
  343. const float currentPeakDbfs = ampToDbfs(m_yL[i]);
  344. // Now find the gain change that should be applied,
  345. // depending on the measured input value.
  346. if (currentPeakDbfs - m_thresholdVal < -m_kneeVal)// Below knee
  347. {
  348. m_gainResult[i] = currentPeakDbfs;
  349. }
  350. else if (currentPeakDbfs - m_thresholdVal < m_kneeVal)// Within knee
  351. {
  352. const float temp = currentPeakDbfs - m_thresholdVal + m_kneeVal;
  353. m_gainResult[i] = currentPeakDbfs + ((limiter ? 0 : m_ratioVal) - 1) * temp * temp / (4 * m_kneeVal);
  354. }
  355. else// Above knee
  356. {
  357. m_gainResult[i] = limiter
  358. ? m_thresholdVal
  359. : m_thresholdVal + (currentPeakDbfs - m_thresholdVal) * m_ratioVal;
  360. }
  361. m_gainResult[i] = dbfsToAmp(m_gainResult[i]) / m_yL[i];
  362. m_gainResult[i] = qMax(m_rangeVal, m_gainResult[i]);
  363. }
  364. switch (stereoLink)
  365. {
  366. case Unlinked:
  367. {
  368. break;
  369. }
  370. case Maximum:
  371. {
  372. m_gainResult[0] = m_gainResult[1] = qMin(m_gainResult[0], m_gainResult[1]);
  373. break;
  374. }
  375. case Average:
  376. {
  377. m_gainResult[0] = m_gainResult[1] = (m_gainResult[0] + m_gainResult[1]) * 0.5f;
  378. break;
  379. }
  380. case Minimum:
  381. {
  382. m_gainResult[0] = m_gainResult[1] = qMax(m_gainResult[0], m_gainResult[1]);
  383. break;
  384. }
  385. case Blend:
  386. {
  387. if (blend > 0)// 0 is unlinked
  388. {
  389. if (blend <= 1)// Blend to minimum volume
  390. {
  391. const float temp1 = qMin(m_gainResult[0], m_gainResult[1]);
  392. m_gainResult[0] = linearInterpolate(m_gainResult[0], temp1, blend);
  393. m_gainResult[1] = linearInterpolate(m_gainResult[1], temp1, blend);
  394. }
  395. else if (blend <= 2)// Blend to average volume
  396. {
  397. const float temp1 = qMin(m_gainResult[0], m_gainResult[1]);
  398. const float temp2 = (m_gainResult[0] + m_gainResult[1]) * 0.5f;
  399. m_gainResult[0] = linearInterpolate(temp1, temp2, blend - 1);
  400. m_gainResult[1] = m_gainResult[0];
  401. }
  402. else// Blend to maximum volume
  403. {
  404. const float temp1 = (m_gainResult[0] + m_gainResult[1]) * 0.5f;
  405. const float temp2 = qMax(m_gainResult[0], m_gainResult[1]);
  406. m_gainResult[0] = linearInterpolate(temp1, temp2, blend - 2);
  407. m_gainResult[1] = m_gainResult[0];
  408. }
  409. }
  410. break;
  411. }
  412. }
  413. // Bias compression to the left or right (or mid or side)
  414. if (stereoBalance != 0)
  415. {
  416. m_gainResult[0] = 1 - ((1 - m_gainResult[0]) * (stereoBalance > 0 ? 1 - stereoBalance : 1));
  417. m_gainResult[1] = 1 - ((1 - m_gainResult[1]) * (stereoBalance < 0 ? 1 + stereoBalance : 1));
  418. }
  419. // For visualizer
  420. m_displayGain[0] = qMax(m_gainResult[0], m_displayGain[0]);
  421. m_displayGain[1] = qMax(m_gainResult[1], m_displayGain[1]);
  422. // Delay the signal by 20 ms via ring buffer if lookahead is enabled
  423. if (lookahead)
  424. {
  425. ++m_inputBufLoc;
  426. if (m_inputBufLoc >= m_lookaheadDelayLength)
  427. {
  428. m_inputBufLoc = 0;
  429. }
  430. const float temp[2] = {drySignal[0], drySignal[1]};
  431. s[0] = m_inputBuf[0][m_inputBufLoc];
  432. s[1] = m_inputBuf[1][m_inputBufLoc];
  433. m_inputBuf[0][m_inputBufLoc] = temp[0];
  434. m_inputBuf[1][m_inputBufLoc] = temp[1];
  435. }
  436. else
  437. {
  438. s[0] = drySignal[0];
  439. s[1] = drySignal[1];
  440. }
  441. float delayedDrySignal[2] = {s[0], s[1]};
  442. if (midside)// Convert left/right to mid/side
  443. {
  444. const float temp = s[0];
  445. s[0] = (temp + s[1]) * 0.5;
  446. s[1] = temp - s[1];
  447. }
  448. s[0] *= inBalance > 0 ? 1 - inBalance : 1;
  449. s[1] *= inBalance < 0 ? 1 + inBalance : 1;
  450. s[0] *= m_gainResult[0] * m_inGainVal * m_outGainVal * (outBalance > 0 ? 1 - outBalance : 1);
  451. s[1] *= m_gainResult[1] * m_inGainVal * m_outGainVal * (outBalance < 0 ? 1 + outBalance : 1);
  452. if (midside)// Convert mid/side back to left/right
  453. {
  454. const float temp1 = s[0];
  455. const float temp2 = s[1] * 0.5;
  456. s[0] = temp1 + temp2;
  457. s[1] = temp1 - temp2;
  458. }
  459. m_prevOut[0] = s[0];
  460. m_prevOut[1] = s[1];
  461. // Negate wet signal from dry signal
  462. if (audition)
  463. {
  464. s[0] = (-s[0] + delayedDrySignal[0] * m_outGainVal);
  465. s[1] = (-s[1] + delayedDrySignal[1] * m_outGainVal);
  466. }
  467. else if (autoMakeup)
  468. {
  469. s[0] *= m_autoMakeupVal;
  470. s[1] *= m_autoMakeupVal;
  471. }
  472. // Calculate wet/dry value results
  473. const float temp1 = delayedDrySignal[0];
  474. const float temp2 = delayedDrySignal[1];
  475. buf[f][0] = d * temp1 + w * s[0];
  476. buf[f][1] = d * temp2 + w * s[1];
  477. buf[f][0] = (1 - m_mixVal) * temp1 + m_mixVal * buf[f][0];
  478. buf[f][1] = (1 - m_mixVal) * temp2 + m_mixVal * buf[f][1];
  479. outSum += buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1];
  480. lInPeak = drySignal[0] > lInPeak ? drySignal[0] : lInPeak;
  481. rInPeak = drySignal[1] > rInPeak ? drySignal[1] : rInPeak;
  482. lOutPeak = s[0] > lOutPeak ? s[0] : lOutPeak;
  483. rOutPeak = s[1] > rOutPeak ? s[1] : rOutPeak;
  484. }
  485. checkGate(outSum / frames);
  486. m_compressorControls.m_outPeakL = lOutPeak;
  487. m_compressorControls.m_outPeakR = rOutPeak;
  488. m_compressorControls.m_inPeakL = lInPeak;
  489. m_compressorControls.m_inPeakR = rInPeak;
  490. return isRunning();
  491. }
  492. // Regular modulo doesn't handle negative numbers correctly. This does.
  493. inline int CompressorEffect::realmod(int k, int n)
  494. {
  495. return (k %= n) < 0 ? k+n : k;
  496. }
  497. // Regular fmod doesn't handle negative numbers correctly. This does.
  498. inline float CompressorEffect::realfmod(float k, float n)
  499. {
  500. return (k = fmod(k, n)) < 0 ? k+n : k;
  501. }
  502. inline void CompressorEffect::calcTiltFilter(sample_t inputSample, sample_t &outputSample, int filtNum)
  503. {
  504. m_tiltOut[filtNum] = m_a0 * inputSample + m_b1 * m_tiltOut[filtNum];
  505. outputSample = inputSample + m_lgain * m_tiltOut[filtNum] + m_hgain * (inputSample - m_tiltOut[filtNum]);
  506. }
  507. void CompressorEffect::changeSampleRate()
  508. {
  509. m_sampleRate = Engine::mixer()->processingSampleRate();
  510. m_coeffPrecalc = COMP_LOG / (m_sampleRate * 0.001f);
  511. // 200 ms
  512. m_crestTimeConst = exp(-1.f / (0.2f * m_sampleRate));
  513. // 20 ms
  514. m_lookaheadDelayLength = 0.02 * m_sampleRate;
  515. m_inputBuf[0].resize(m_lookaheadDelayLength);
  516. m_inputBuf[1].resize(m_lookaheadDelayLength);
  517. m_lookaheadBuf[0].resize(m_lookaheadDelayLength);
  518. m_lookaheadBuf[1].resize(m_lookaheadDelayLength);
  519. m_preLookaheadBuf[0].resize(m_lookaheadDelayLength);
  520. m_preLookaheadBuf[1].resize(m_lookaheadDelayLength);
  521. calcThreshold();
  522. calcKnee();
  523. calcRatio();
  524. calcAutoMakeup();// This should be after Threshold, Knee, and Ratio
  525. calcAttack();
  526. calcRelease();
  527. calcRange();
  528. calcLookaheadLength();
  529. calcHold();
  530. resizeRMS();
  531. calcOutGain();
  532. calcInGain();
  533. calcTiltCoeffs();
  534. calcMix();
  535. }
  536. extern "C"
  537. {
  538. // necessary for getting instance out of shared lib
  539. PLUGIN_EXPORT Plugin * lmms_plugin_main(Model* parent, void* data)
  540. {
  541. return new CompressorEffect(parent, static_cast<const Plugin::Descriptor::SubPluginFeatures::Key *>(data));
  542. }
  543. }