CompressorControlDialog.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  1. /*
  2. * CompressorControlDialog.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 "CompressorControlDialog.h"
  26. #include "CompressorControls.h"
  27. #include <QLabel>
  28. #include <QLayout>
  29. #include <QPainter>
  30. #include "embed.h"
  31. #include "GuiApplication.h"
  32. #include "gui_templates.h"
  33. #include "interpolation.h"
  34. #include "MainWindow.h"
  35. #include "ToolTip.h"
  36. CompressorControlDialog::CompressorControlDialog(CompressorControls* controls) :
  37. EffectControlDialog(controls),
  38. m_controls(controls),
  39. m_inVolAreaColor(209, 216, 228, 17),
  40. m_inVolColor(209, 216, 228, 100),
  41. m_outVolAreaColor(209, 216, 228, 30),
  42. m_outVolColor(209, 216, 228, 240),
  43. m_gainReductionColor(180, 100, 100, 210),
  44. m_kneeColor(39, 171, 95, 255),
  45. m_kneeColor2(9, 171, 160, 255),
  46. m_threshColor(39, 171, 95, 100),
  47. m_textColor(209, 216, 228, 50),
  48. m_graphColor(209, 216, 228, 50),
  49. m_resetColor(200, 100, 15, 200)
  50. {
  51. setAutoFillBackground(true);
  52. QPalette pal;
  53. pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork"));
  54. setPalette(pal);
  55. setMinimumSize(MIN_COMP_SCREEN_X, MIN_COMP_SCREEN_Y);
  56. resize(COMP_SCREEN_X, COMP_SCREEN_Y);
  57. m_graphPixmap.fill(QColor("transparent"));
  58. m_visPixmap.fill(QColor("transparent"));
  59. m_kneePixmap.fill(QColor("transparent"));
  60. m_kneePixmap2.fill(QColor("transparent"));
  61. m_miscPixmap.fill(QColor("transparent"));
  62. m_controlsBoxLabel = new QLabel(this);
  63. m_controlsBoxLabel->setPixmap(PLUGIN_NAME::getIconPixmap("controlsBox"));
  64. m_controlsBoxLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
  65. m_rmsEnabledLabel = new QLabel(this);
  66. m_rmsEnabledLabel->setPixmap(PLUGIN_NAME::getIconPixmap("knob_enabled"));
  67. m_rmsEnabledLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
  68. m_blendEnabledLabel = new QLabel(this);
  69. m_blendEnabledLabel->setPixmap(PLUGIN_NAME::getIconPixmap("knob_enabled"));
  70. m_blendEnabledLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
  71. m_lookaheadEnabledLabel = new QLabel(this);
  72. m_lookaheadEnabledLabel->setPixmap(PLUGIN_NAME::getIconPixmap("knob_enabled"));
  73. m_lookaheadEnabledLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
  74. m_ratioEnabledLabel = new QLabel(this);
  75. m_ratioEnabledLabel->setPixmap(PLUGIN_NAME::getIconPixmap("knob_enabled_large"));
  76. m_ratioEnabledLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
  77. m_thresholdKnob = new Knob(knobStyled, this);
  78. makeLargeKnob(m_thresholdKnob, tr("Threshold:") , " dBFS");
  79. m_thresholdKnob->setModel(&controls->m_thresholdModel);
  80. ToolTip::add(m_thresholdKnob, tr("Volume at which the compression begins to take place"));
  81. m_ratioKnob = new Knob(knobStyled, this);
  82. makeLargeKnob(m_ratioKnob, tr("Ratio:") , ":1");
  83. m_ratioKnob->setModel(&controls->m_ratioModel);
  84. ToolTip::add(m_ratioKnob, tr("How far the compressor must turn the volume down after crossing the threshold"));
  85. m_attackKnob = new Knob(knobStyled, this);
  86. makeLargeKnob(m_attackKnob, tr("Attack:") , " ms");
  87. m_attackKnob->setModel(&controls->m_attackModel);
  88. ToolTip::add(m_attackKnob, tr("Speed at which the compressor starts to compress the audio"));
  89. m_releaseKnob = new Knob(knobStyled, this);
  90. makeLargeKnob(m_releaseKnob, tr("Release:") , " ms");
  91. m_releaseKnob->setModel(&controls->m_releaseModel);
  92. ToolTip::add(m_releaseKnob, tr("Speed at which the compressor ceases to compress the audio"));
  93. m_kneeKnob = new Knob(knobStyled, this);
  94. makeSmallKnob(m_kneeKnob, tr("Knee:") , " dB");
  95. m_kneeKnob->setModel(&controls->m_kneeModel);
  96. ToolTip::add(m_kneeKnob, tr("Smooth out the gain reduction curve around the threshold"));
  97. m_rangeKnob = new Knob(knobStyled, this);
  98. makeSmallKnob(m_rangeKnob, tr("Range:") , " dBFS");
  99. m_rangeKnob->setModel(&controls->m_rangeModel);
  100. ToolTip::add(m_rangeKnob, tr("Maximum gain reduction"));
  101. m_lookaheadLengthKnob = new Knob(knobStyled, this);
  102. makeSmallKnob(m_lookaheadLengthKnob, tr("Lookahead Length:") , " ms");
  103. m_lookaheadLengthKnob->setModel(&controls->m_lookaheadLengthModel);
  104. ToolTip::add(m_lookaheadLengthKnob, tr("How long the compressor has to react to the sidechain signal ahead of time"));
  105. m_holdKnob = new Knob(knobStyled, this);
  106. makeSmallKnob(m_holdKnob, tr("Hold:") , " ms");
  107. m_holdKnob->setModel(&controls->m_holdModel);
  108. ToolTip::add(m_holdKnob, tr("Delay between attack and release stages"));
  109. m_rmsKnob = new Knob(knobStyled, this);
  110. makeSmallKnob(m_rmsKnob, tr("RMS Size:") , "");
  111. m_rmsKnob->setModel(&controls->m_rmsModel);
  112. ToolTip::add(m_rmsKnob, tr("Size of the RMS buffer"));
  113. m_inBalanceKnob = new Knob(knobStyled, this);
  114. makeSmallKnob(m_inBalanceKnob, tr("Input Balance:") , "");
  115. m_inBalanceKnob->setModel(&controls->m_inBalanceModel);
  116. ToolTip::add(m_inBalanceKnob, tr("Bias the input audio to the left/right or mid/side"));
  117. m_outBalanceKnob = new Knob(knobStyled, this);
  118. makeSmallKnob(m_outBalanceKnob, tr("Output Balance:") , "");
  119. m_outBalanceKnob->setModel(&controls->m_outBalanceModel);
  120. ToolTip::add(m_outBalanceKnob, tr("Bias the output audio to the left/right or mid/side"));
  121. m_stereoBalanceKnob = new Knob(knobStyled, this);
  122. makeSmallKnob(m_stereoBalanceKnob, tr("Stereo Balance:") , "");
  123. m_stereoBalanceKnob->setModel(&controls->m_stereoBalanceModel);
  124. ToolTip::add(m_stereoBalanceKnob, tr("Bias the sidechain signal to the left/right or mid/side"));
  125. m_blendKnob = new Knob(knobStyled, this);
  126. makeSmallKnob(m_blendKnob, tr("Stereo Link Blend:") , "");
  127. m_blendKnob->setModel(&controls->m_blendModel);
  128. ToolTip::add(m_blendKnob, tr("Blend between unlinked/maximum/average/minimum stereo linking modes"));
  129. m_tiltKnob = new Knob(knobStyled, this);
  130. makeSmallKnob(m_tiltKnob, tr("Tilt Gain:") , " dB");
  131. m_tiltKnob->setModel(&controls->m_tiltModel);
  132. ToolTip::add(m_tiltKnob, tr("Bias the sidechain signal to the low or high frequencies. -6 db is lowpass, 6 db is highpass."));
  133. m_tiltFreqKnob = new Knob(knobStyled, this);
  134. makeSmallKnob(m_tiltFreqKnob, tr("Tilt Frequency:") , " Hz");
  135. m_tiltFreqKnob->setModel(&controls->m_tiltFreqModel);
  136. ToolTip::add(m_tiltFreqKnob, tr("Center frequency of sidechain tilt filter"));
  137. m_mixKnob = new Knob(knobStyled, this);
  138. makeSmallKnob(m_mixKnob, tr("Mix:") , "%");
  139. m_mixKnob->setModel(&controls->m_mixModel);
  140. ToolTip::add(m_mixKnob, tr("Balance between wet and dry signals"));
  141. m_autoAttackKnob = new Knob(knobStyled, this);
  142. makeSmallKnob(m_autoAttackKnob, tr("Auto Attack:") , "%");
  143. m_autoAttackKnob->setModel(&controls->m_autoAttackModel);
  144. ToolTip::add(m_autoAttackKnob, tr("Automatically control attack value depending on crest factor"));
  145. m_autoReleaseKnob = new Knob(knobStyled, this);
  146. makeSmallKnob(m_autoReleaseKnob, tr("Auto Release:") , "%");
  147. m_autoReleaseKnob->setModel(&controls->m_autoReleaseModel);
  148. ToolTip::add(m_autoReleaseKnob, tr("Automatically control release value depending on crest factor"));
  149. m_outFader = new EqFader(&controls->m_outGainModel,tr("Output gain"),
  150. this, &controls->m_outPeakL, &controls->m_outPeakR);
  151. m_outFader->setDisplayConversion(false);
  152. m_outFader->setHintText(tr("Gain"), "dBFS");
  153. ToolTip::add(m_outFader, tr("Output volume"));
  154. m_inFader = new EqFader(&controls->m_inGainModel,tr("Input gain"),
  155. this, &controls->m_inPeakL, &controls->m_inPeakR);
  156. m_inFader->setDisplayConversion(false);
  157. m_inFader->setHintText(tr("Gain"), "dBFS");
  158. ToolTip::add(m_inFader, tr("Input volume"));
  159. rmsButton = new PixmapButton(this, tr("Root Mean Square"));
  160. rmsButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("rms_sel"));
  161. rmsButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("rms_unsel"));
  162. ToolTip::add(rmsButton, tr("Use RMS of the input"));
  163. peakButton = new PixmapButton(this, tr("Peak"));
  164. peakButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("peak_sel"));
  165. peakButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("peak_unsel"));
  166. ToolTip::add(peakButton, tr("Use absolute value of the input"));
  167. rmsPeakGroup = new automatableButtonGroup(this);
  168. rmsPeakGroup->addButton(rmsButton);
  169. rmsPeakGroup->addButton(peakButton);
  170. rmsPeakGroup->setModel(&controls->m_peakmodeModel);
  171. leftRightButton = new PixmapButton(this, tr("Left/Right"));
  172. leftRightButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("leftright_sel"));
  173. leftRightButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("leftright_unsel"));
  174. ToolTip::add(leftRightButton, tr("Compress left and right audio"));
  175. midSideButton = new PixmapButton(this, tr("Mid/Side"));
  176. midSideButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("midside_sel"));
  177. midSideButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("midside_unsel"));
  178. ToolTip::add(midSideButton, tr("Compress mid and side audio"));
  179. leftRightMidSideGroup = new automatableButtonGroup(this);
  180. leftRightMidSideGroup->addButton(leftRightButton);
  181. leftRightMidSideGroup->addButton(midSideButton);
  182. leftRightMidSideGroup->setModel(&controls->m_midsideModel);
  183. compressButton = new PixmapButton(this, tr("Compressor"));
  184. compressButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("compressor_sel"));
  185. compressButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("compressor_unsel"));
  186. ToolTip::add(compressButton, tr("Compress the audio"));
  187. limitButton = new PixmapButton(this, tr("Limiter"));
  188. limitButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("limiter_sel"));
  189. limitButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("limiter_unsel"));
  190. ToolTip::add(limitButton, tr("Set Ratio to infinity (is not guaranteed to limit audio volume)"));
  191. compressLimitGroup = new automatableButtonGroup(this);
  192. compressLimitGroup->addButton(compressButton);
  193. compressLimitGroup->addButton(limitButton);
  194. compressLimitGroup->setModel(&controls->m_limiterModel);
  195. unlinkedButton = new PixmapButton(this, tr("Unlinked"));
  196. unlinkedButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("unlinked_sel"));
  197. unlinkedButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("unlinked_unsel"));
  198. ToolTip::add(unlinkedButton, tr("Compress each channel separately"));
  199. maximumButton = new PixmapButton(this, tr("Maximum"));
  200. maximumButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("maximum_sel"));
  201. maximumButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("maximum_unsel"));
  202. ToolTip::add(maximumButton, tr("Compress based on the loudest channel"));
  203. averageButton = new PixmapButton(this, tr("Average"));
  204. averageButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("average_sel"));
  205. averageButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("average_unsel"));
  206. ToolTip::add(averageButton, tr("Compress based on the averaged channel volume"));
  207. minimumButton = new PixmapButton(this, tr("Minimum"));
  208. minimumButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("minimum_sel"));
  209. minimumButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("minimum_unsel"));
  210. ToolTip::add(minimumButton, tr("Compress based on the quietest channel"));
  211. blendButton = new PixmapButton(this, tr("Blend"));
  212. blendButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("blend_sel"));
  213. blendButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("blend_unsel"));
  214. ToolTip::add(blendButton, tr("Blend between stereo linking modes"));
  215. stereoLinkGroup = new automatableButtonGroup(this);
  216. stereoLinkGroup->addButton(unlinkedButton);
  217. stereoLinkGroup->addButton(maximumButton);
  218. stereoLinkGroup->addButton(averageButton);
  219. stereoLinkGroup->addButton(minimumButton);
  220. stereoLinkGroup->addButton(blendButton);
  221. stereoLinkGroup->setModel(&controls->m_stereoLinkModel);
  222. autoMakeupButton = new PixmapButton(this, tr("Auto Makeup Gain"));
  223. autoMakeupButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("autogain_sel"));
  224. autoMakeupButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("autogain_unsel"));
  225. ToolTip::add(autoMakeupButton, tr("Automatically change makeup gain depending on threshold, knee, and ratio settings"));
  226. autoMakeupButton->setCheckable(true);
  227. autoMakeupButton->setModel(&controls->m_autoMakeupModel);
  228. auditionButton = new PixmapButton(this, tr("Soft Clip"));
  229. auditionButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("audition_sel"));
  230. auditionButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("audition_unsel"));
  231. ToolTip::add(auditionButton, tr("Play the delta signal"));
  232. auditionButton->setCheckable(true);
  233. auditionButton->setModel(&controls->m_auditionModel);
  234. feedbackButton = new PixmapButton(this, tr("Soft Clip"));
  235. feedbackButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("feedback_sel"));
  236. feedbackButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("feedback_unsel"));
  237. ToolTip::add(feedbackButton, tr("Use the compressor's output as the sidechain input"));
  238. feedbackButton->setCheckable(true);
  239. feedbackButton->setModel(&controls->m_feedbackModel);
  240. lookaheadButton = new PixmapButton(this, tr("Lookahead Enabled"));
  241. lookaheadButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("lookahead_sel"));
  242. lookaheadButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("lookahead_unsel"));
  243. ToolTip::add(lookaheadButton, tr("Enable Lookahead, which introduces 20 milliseconds of latency"));
  244. lookaheadButton->setCheckable(true);
  245. lookaheadButton->setModel(&controls->m_lookaheadModel);
  246. connect(gui->mainWindow(), SIGNAL(periodicUpdate()), this, SLOT(updateDisplay()));
  247. connect(&m_controls->m_peakmodeModel, SIGNAL(dataChanged()), this, SLOT(peakmodeChanged()));
  248. connect(&m_controls->m_stereoLinkModel, SIGNAL(dataChanged()), this, SLOT(stereoLinkChanged()));
  249. connect(&m_controls->m_lookaheadModel, SIGNAL(dataChanged()), this, SLOT(lookaheadChanged()));
  250. connect(&m_controls->m_limiterModel, SIGNAL(dataChanged()), this, SLOT(limiterChanged()));
  251. m_timeElapsed.start();
  252. peakmodeChanged();
  253. stereoLinkChanged();
  254. lookaheadChanged();
  255. limiterChanged();
  256. }
  257. void CompressorControlDialog::makeLargeKnob(Knob * knob, QString hint, QString unit)
  258. {
  259. knob->setHintText(hint, unit);
  260. knob->setFixedSize(56, 56);
  261. knob->setOuterRadius(23);
  262. knob->setInnerRadius(15);
  263. knob->setCenterPointX(28);
  264. knob->setCenterPointY(28);
  265. }
  266. void CompressorControlDialog::makeSmallKnob(Knob * knob, QString hint, QString unit)
  267. {
  268. knob->setHintText(hint, unit);
  269. knob->setFixedSize(30, 30);
  270. knob->setOuterRadius(10);
  271. knob->setInnerRadius(4);
  272. knob->setCenterPointX(15);
  273. knob->setCenterPointY(15);
  274. }
  275. void CompressorControlDialog::peakmodeChanged()
  276. {
  277. m_rmsKnob->setVisible(!m_controls->m_peakmodeModel.value());
  278. m_rmsEnabledLabel->setVisible(!m_controls->m_peakmodeModel.value());
  279. }
  280. void CompressorControlDialog::stereoLinkChanged()
  281. {
  282. m_blendKnob->setVisible(m_controls->m_stereoLinkModel.value() == 4);
  283. m_blendEnabledLabel->setVisible(m_controls->m_stereoLinkModel.value() == 4);
  284. }
  285. void CompressorControlDialog::lookaheadChanged()
  286. {
  287. m_lookaheadLengthKnob->setVisible(m_controls->m_lookaheadModel.value());
  288. m_lookaheadEnabledLabel->setVisible(m_controls->m_lookaheadModel.value());
  289. }
  290. void CompressorControlDialog::limiterChanged()
  291. {
  292. m_ratioKnob->setVisible(!m_controls->m_limiterModel.value());
  293. m_ratioEnabledLabel->setVisible(!m_controls->m_limiterModel.value());
  294. }
  295. void CompressorControlDialog::updateDisplay()
  296. {
  297. if (!isVisible())
  298. {
  299. m_timeElapsed.restart();
  300. return;
  301. }
  302. int elapsedMil = m_timeElapsed.elapsed();
  303. m_timeElapsed.restart();
  304. m_timeSinceLastUpdate += elapsedMil;
  305. m_compPixelMovement = int(m_timeSinceLastUpdate / COMP_MILLI_PER_PIXEL);
  306. m_timeSinceLastUpdate %= COMP_MILLI_PER_PIXEL;
  307. // Time Change / Daylight Savings Time protection
  308. if (!m_compPixelMovement || m_compPixelMovement <= 0)
  309. {
  310. return;
  311. }
  312. if (!m_controls->m_effect->isEnabled() || !m_controls->m_effect->isRunning())
  313. {
  314. m_controls->m_effect->m_displayPeak[0] = COMP_NOISE_FLOOR;
  315. m_controls->m_effect->m_displayPeak[1] = COMP_NOISE_FLOOR;
  316. m_controls->m_effect->m_displayGain[0] = 1;
  317. m_controls->m_effect->m_displayGain[1] = 1;
  318. }
  319. m_peakAvg = (m_controls->m_effect->m_displayPeak[0] + m_controls->m_effect->m_displayPeak[1]) * 0.5f;
  320. m_gainAvg = (m_controls->m_effect->m_displayGain[0] + m_controls->m_effect->m_displayGain[1]) * 0.5f;
  321. m_controls->m_effect->m_displayPeak[0] = m_controls->m_effect->m_yL[0];
  322. m_controls->m_effect->m_displayPeak[1] = m_controls->m_effect->m_yL[1];
  323. m_controls->m_effect->m_displayGain[0] = m_controls->m_effect->m_gainResult[0];
  324. m_controls->m_effect->m_displayGain[1] = m_controls->m_effect->m_gainResult[1];
  325. m_yPoint = dbfsToYPoint(ampToDbfs(m_peakAvg));
  326. m_yGainPoint = dbfsToYPoint(ampToDbfs(m_gainAvg));
  327. m_threshYPoint = dbfsToYPoint(m_controls->m_effect->m_thresholdVal);
  328. m_threshXPoint = m_kneeWindowSizeY - m_threshYPoint;
  329. drawVisPixmap();
  330. if (m_controls->m_effect->m_redrawKnee)
  331. {
  332. redrawKnee();
  333. }
  334. drawKneePixmap2();
  335. if (m_controls->m_effect->m_redrawThreshold)
  336. {
  337. drawMiscPixmap();
  338. }
  339. m_lastPoint = m_yPoint;
  340. m_lastGainPoint = m_yGainPoint;
  341. update();
  342. }
  343. void CompressorControlDialog::drawVisPixmap()
  344. {
  345. m_p.begin(&m_visPixmap);
  346. // Move entire display to the left
  347. m_p.setCompositionMode(QPainter::CompositionMode_Source);
  348. m_p.drawPixmap(-m_compPixelMovement, 0, m_visPixmap);
  349. m_p.fillRect(m_windowSizeX-m_compPixelMovement, 0, m_windowSizeX, m_windowSizeY, QColor("transparent"));
  350. m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
  351. m_p.setRenderHint(QPainter::Antialiasing, true);
  352. // Draw translucent portion of input volume line
  353. m_p.setPen(QPen(m_inVolAreaColor, 1));
  354. for (int i = 0; i < m_compPixelMovement; ++i)
  355. {
  356. const int temp = linearInterpolate(m_lastPoint, m_yPoint, float(i) / float(m_compPixelMovement));
  357. m_p.drawLine(m_windowSizeX-m_compPixelMovement+i, temp, m_windowSizeX-m_compPixelMovement+i, m_windowSizeY);
  358. }
  359. // Draw input volume line
  360. m_p.setPen(QPen(m_inVolColor, 1));
  361. m_p.drawLine(m_windowSizeX-m_compPixelMovement-1, m_lastPoint, m_windowSizeX, m_yPoint);
  362. // Draw translucent portion of output volume line
  363. m_p.setPen(QPen(m_outVolAreaColor, 1));
  364. for (int i = 0; i < m_compPixelMovement; ++i)
  365. {
  366. const int temp = linearInterpolate(m_lastPoint+m_lastGainPoint, m_yPoint+m_yGainPoint, float(i) / float(m_compPixelMovement));
  367. m_p.drawLine(m_windowSizeX-m_compPixelMovement+i, temp, m_windowSizeX-m_compPixelMovement+i, m_windowSizeY);
  368. }
  369. // Draw output volume line
  370. m_p.setPen(QPen(m_outVolColor, 1));
  371. m_p.drawLine(m_windowSizeX-m_compPixelMovement-1, m_lastPoint+m_lastGainPoint, m_windowSizeX, m_yPoint+m_yGainPoint);
  372. // Draw gain reduction line
  373. m_p.setPen(QPen(m_gainReductionColor, 2));
  374. m_p.drawLine(m_windowSizeX-m_compPixelMovement-1, m_lastGainPoint, m_windowSizeX, m_yGainPoint);
  375. m_p.end();
  376. }
  377. void CompressorControlDialog::redrawKnee()
  378. {
  379. m_controls->m_effect->m_redrawKnee = false;
  380. // Start drawing knee visualizer
  381. m_p.begin(&m_kneePixmap);
  382. m_p.setRenderHint(QPainter::Antialiasing, false);
  383. // Clear display
  384. m_p.setCompositionMode(QPainter::CompositionMode_Source);
  385. m_p.fillRect(0, 0, m_windowSizeX, m_kneeWindowSizeY, QColor("transparent"));
  386. m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
  387. m_p.setRenderHint(QPainter::Antialiasing, true);
  388. m_p.setPen(QPen(m_kneeColor, 3));
  389. // Limiter = infinite ratio
  390. float actualRatio = m_controls->m_limiterModel.value() ? 0 : m_controls->m_effect->m_ratioVal;
  391. // Calculate endpoints for the two straight lines
  392. float kneePoint1 = m_controls->m_effect->m_thresholdVal - m_controls->m_effect->m_kneeVal;
  393. float kneePoint2X = m_controls->m_effect->m_thresholdVal + m_controls->m_effect->m_kneeVal;
  394. float kneePoint2Y = (m_controls->m_effect->m_thresholdVal + (-m_controls->m_effect->m_thresholdVal * (actualRatio * (m_controls->m_effect->m_kneeVal / -m_controls->m_effect->m_thresholdVal))));
  395. float ratioPoint = m_controls->m_effect->m_thresholdVal + (-m_controls->m_effect->m_thresholdVal * actualRatio);
  396. // Draw two straight lines
  397. m_p.drawLine(0, m_kneeWindowSizeY, dbfsToXPoint(kneePoint1), dbfsToYPoint(kneePoint1));
  398. if (dbfsToXPoint(kneePoint2X) < m_kneeWindowSizeY)
  399. {
  400. m_p.drawLine(dbfsToXPoint(kneePoint2X), dbfsToYPoint(kneePoint2Y), m_kneeWindowSizeY, dbfsToYPoint(ratioPoint));
  401. }
  402. // Draw knee section
  403. if (m_controls->m_effect->m_kneeVal)
  404. {
  405. m_p.setPen(QPen(m_kneeColor2, 3));
  406. float prevPoint[2] = {kneePoint1, kneePoint1};
  407. float newPoint[2] = {0, 0};
  408. // Draw knee curve using many straight lines.
  409. for (int i = 0; i < COMP_KNEE_LINES; ++i)
  410. {
  411. newPoint[0] = linearInterpolate(kneePoint1, kneePoint2X, (i + 1) / (float)COMP_KNEE_LINES);
  412. const float temp = newPoint[0] - m_controls->m_effect->m_thresholdVal + m_controls->m_effect->m_kneeVal;
  413. newPoint[1] = (newPoint[0] + (actualRatio - 1) * temp * temp / (4 * m_controls->m_effect->m_kneeVal));
  414. m_p.drawLine(dbfsToXPoint(prevPoint[0]), dbfsToYPoint(prevPoint[1]), dbfsToXPoint(newPoint[0]), dbfsToYPoint(newPoint[1]));
  415. prevPoint[0] = newPoint[0];
  416. prevPoint[1] = newPoint[1];
  417. }
  418. }
  419. m_p.setRenderHint(QPainter::Antialiasing, false);
  420. // Erase right portion
  421. m_p.setCompositionMode(QPainter::CompositionMode_Source);
  422. m_p.fillRect(m_kneeWindowSizeX + 1, 0, m_windowSizeX, m_kneeWindowSizeY, QColor("transparent"));
  423. m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
  424. m_p.end();
  425. m_p.begin(&m_kneePixmap2);
  426. m_p.setCompositionMode(QPainter::CompositionMode_Source);
  427. m_p.fillRect(0, 0, m_windowSizeX, m_kneeWindowSizeY, QColor("transparent"));
  428. m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
  429. m_p.end();
  430. m_lastKneePoint = 0;
  431. }
  432. void CompressorControlDialog::drawKneePixmap2()
  433. {
  434. m_p.begin(&m_kneePixmap2);
  435. m_p.setRenderHint(QPainter::Antialiasing, false);
  436. int kneePoint = dbfsToXPoint(ampToDbfs(m_peakAvg));
  437. if (kneePoint > m_lastKneePoint)
  438. {
  439. QRectF knee2Rect = QRect(m_lastKneePoint, 0, kneePoint - m_lastKneePoint, m_kneeWindowSizeY);
  440. m_p.drawPixmap(knee2Rect, m_kneePixmap, knee2Rect);
  441. }
  442. else
  443. {
  444. m_p.setCompositionMode(QPainter::CompositionMode_Source);
  445. m_p.fillRect(kneePoint, 0, m_lastKneePoint, m_kneeWindowSizeY, QColor("transparent"));
  446. m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
  447. }
  448. m_lastKneePoint = kneePoint;
  449. m_p.end();
  450. }
  451. void CompressorControlDialog::drawMiscPixmap()
  452. {
  453. m_p.begin(&m_miscPixmap);
  454. m_p.setCompositionMode(QPainter::CompositionMode_Source);
  455. m_p.fillRect(0, 0, m_windowSizeX, m_windowSizeY, QColor("transparent"));
  456. m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
  457. m_p.setRenderHint(QPainter::Antialiasing, true);
  458. // Draw threshold lines
  459. m_p.setPen(QPen(m_threshColor, 2, Qt::DotLine));
  460. m_p.drawLine(0, m_threshYPoint, m_windowSizeX, m_threshYPoint);
  461. m_p.drawLine(m_threshXPoint, 0, m_threshXPoint, m_kneeWindowSizeY);
  462. m_p.end();
  463. m_controls->m_effect->m_redrawThreshold = false;
  464. }
  465. void CompressorControlDialog::paintEvent(QPaintEvent *event)
  466. {
  467. if (!isVisible())
  468. {
  469. return;
  470. }
  471. m_p.begin(this);
  472. m_p.setCompositionMode(QPainter::CompositionMode_Source);
  473. m_p.fillRect(0, 0, m_windowSizeX, m_windowSizeY, QColor("transparent"));
  474. m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
  475. m_p.drawPixmap(0, 0, m_graphPixmap);
  476. m_p.drawPixmap(0, 0, m_visPixmap);
  477. m_p.setOpacity(0.25);
  478. m_p.drawPixmap(0, 0, m_kneePixmap);
  479. m_p.setOpacity(1);
  480. if (m_controls->m_effect->isEnabled() && m_controls->m_effect->isRunning())
  481. {
  482. m_p.drawPixmap(0, 0, m_kneePixmap2);
  483. }
  484. m_p.drawPixmap(0, 0, m_miscPixmap);
  485. m_p.end();
  486. }
  487. inline int CompressorControlDialog::dbfsToYPoint(float inDbfs)
  488. {
  489. return (-((inDbfs + m_dbRange) / m_dbRange) + 1) * m_windowSizeY;
  490. }
  491. inline int CompressorControlDialog::dbfsToXPoint(float inDbfs)
  492. {
  493. return m_kneeWindowSizeY - dbfsToYPoint(inDbfs);
  494. }
  495. void CompressorControlDialog::resizeEvent(QResizeEvent *event)
  496. {
  497. resetCompressorView();
  498. }
  499. void CompressorControlDialog::wheelEvent(QWheelEvent * event)
  500. {
  501. const float temp = m_dbRange;
  502. const float dbRangeNew = m_dbRange - copysignf(COMP_GRID_SPACING, event->delta());
  503. m_dbRange = round(qBound(COMP_GRID_SPACING, dbRangeNew, COMP_GRID_MAX) / COMP_GRID_SPACING) * COMP_GRID_SPACING;
  504. // Only reset view if the scolling had an effect
  505. if (m_dbRange != temp)
  506. {
  507. drawGraph();
  508. m_controls->m_effect->m_redrawKnee = true;
  509. m_controls->m_effect->m_redrawThreshold = true;
  510. }
  511. }
  512. void CompressorControlDialog::drawGraph()
  513. {
  514. m_p.begin(&m_graphPixmap);
  515. m_p.setRenderHint(QPainter::Antialiasing, false);
  516. m_p.setCompositionMode(QPainter::CompositionMode_Source);
  517. m_p.fillRect(0, 0, m_windowSizeX, m_windowSizeY, QColor("transparent"));
  518. m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
  519. m_p.setPen(QPen(m_textColor, 1));
  520. // Arbitrary formula for increasing font size when window size increases
  521. m_p.setFont(QFont("Arial", qMax(int(m_windowSizeY / 1080.f * 24), 12)));
  522. // Redraw graph
  523. m_p.setPen(QPen(m_graphColor, 1));
  524. for (int i = 1; i < m_dbRange / COMP_GRID_SPACING + 1; ++i)
  525. {
  526. m_p.drawLine(0, dbfsToYPoint(-COMP_GRID_SPACING * i), m_windowSizeX, dbfsToYPoint(-COMP_GRID_SPACING * i));
  527. m_p.drawLine(dbfsToXPoint(-COMP_GRID_SPACING * i), 0, dbfsToXPoint(-COMP_GRID_SPACING * i), m_kneeWindowSizeY);
  528. m_p.drawText(QRectF(m_windowSizeX - 50, dbfsToYPoint(-COMP_GRID_SPACING * i), 50, 50), Qt::AlignRight | Qt::AlignTop, QString::number(i * -COMP_GRID_SPACING));
  529. }
  530. m_p.end();
  531. }
  532. void CompressorControlDialog::resetCompressorView()
  533. {
  534. m_windowSizeX = size().width();
  535. m_windowSizeY = size().height();
  536. m_kneeWindowSizeX = m_windowSizeY;
  537. m_kneeWindowSizeY = m_windowSizeY;
  538. m_controlsBoxX = (m_windowSizeX - COMP_BOX_X) * 0.5;
  539. m_controlsBoxY = m_windowSizeY - 40 - COMP_BOX_Y;
  540. m_controls->m_effect->m_redrawKnee = true;
  541. m_controls->m_effect->m_redrawThreshold = true;
  542. m_lastKneePoint = 0;
  543. drawGraph();
  544. m_p.begin(&m_visPixmap);
  545. m_p.setCompositionMode(QPainter::CompositionMode_Source);
  546. m_p.fillRect(0, 0, m_windowSizeX, m_windowSizeY, QColor("transparent"));
  547. m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
  548. // Draw line at right side, so the sudden
  549. // content that the visualizer will display
  550. // later on won't look too ugly
  551. m_p.setPen(QPen(m_resetColor, 3));
  552. m_p.drawLine(m_windowSizeX, 0, m_windowSizeX, m_windowSizeY);
  553. m_p.end();
  554. m_controlsBoxLabel->move(m_controlsBoxX, m_controlsBoxY);
  555. m_rmsEnabledLabel->move(m_controlsBoxX + 429, m_controlsBoxY + 209);
  556. m_blendEnabledLabel->move(m_controlsBoxX + 587, m_controlsBoxY + 197);
  557. m_lookaheadEnabledLabel->move(m_controlsBoxX + 221, m_controlsBoxY + 135);
  558. m_ratioEnabledLabel->move(m_controlsBoxX + 267, m_controlsBoxY + 21);
  559. m_thresholdKnob->move(m_controlsBoxX + 137, m_controlsBoxY + 21);
  560. m_ratioKnob->move(m_controlsBoxX + 267, m_controlsBoxY + 21);
  561. m_attackKnob->move(m_controlsBoxX + 397, m_controlsBoxY + 21);
  562. m_releaseKnob->move(m_controlsBoxX + 527, m_controlsBoxY + 21);
  563. m_kneeKnob->move(m_controlsBoxX + 97, m_controlsBoxY + 135);
  564. m_rangeKnob->move(m_controlsBoxX + 159, m_controlsBoxY + 135);
  565. m_lookaheadLengthKnob->move(m_controlsBoxX + 221, m_controlsBoxY + 135);
  566. m_holdKnob->move(m_controlsBoxX + 283, m_controlsBoxY + 135);
  567. m_rmsKnob->move(m_controlsBoxX + 429, m_controlsBoxY + 209);
  568. m_inBalanceKnob->move(m_controlsBoxX + 27, m_controlsBoxY + 219);
  569. m_outBalanceKnob->move(m_controlsBoxX + 662, m_controlsBoxY + 219);
  570. m_stereoBalanceKnob->move(m_controlsBoxX + 522, m_controlsBoxY + 137);
  571. m_blendKnob->move(m_controlsBoxX + 587, m_controlsBoxY + 197);
  572. m_tiltKnob->move(m_controlsBoxX + 364, m_controlsBoxY + 138);
  573. m_tiltFreqKnob->move(m_controlsBoxX + 415, m_controlsBoxY + 138);
  574. m_mixKnob->move(m_controlsBoxX + 27, m_controlsBoxY + 13);
  575. m_outFader->move(m_controlsBoxX + 666, m_controlsBoxY + 91);
  576. m_inFader->move(m_controlsBoxX + 31, m_controlsBoxY + 91);
  577. rmsButton->move(m_controlsBoxX + 337, m_controlsBoxY + 231);
  578. peakButton->move(m_controlsBoxX + 337, m_controlsBoxY + 248);
  579. leftRightButton->move(m_controlsBoxX + 220, m_controlsBoxY + 231);
  580. midSideButton->move(m_controlsBoxX + 220, m_controlsBoxY + 248);
  581. compressButton->move(m_controlsBoxX + 98, m_controlsBoxY + 231);
  582. limitButton->move(m_controlsBoxX + 98, m_controlsBoxY + 248);
  583. unlinkedButton->move(m_controlsBoxX + 495, m_controlsBoxY + 180);
  584. maximumButton->move(m_controlsBoxX + 495, m_controlsBoxY + 197);
  585. averageButton->move(m_controlsBoxX + 495, m_controlsBoxY + 214);
  586. minimumButton->move(m_controlsBoxX + 495, m_controlsBoxY + 231);
  587. blendButton->move(m_controlsBoxX + 495, m_controlsBoxY + 248);
  588. autoMakeupButton->move(m_controlsBoxX + 220, m_controlsBoxY + 206);
  589. auditionButton->move(m_controlsBoxX + 658, m_controlsBoxY + 14);
  590. feedbackButton->move(m_controlsBoxX + 98, m_controlsBoxY + 206);
  591. m_autoAttackKnob->move(m_controlsBoxX + 460, m_controlsBoxY + 38);
  592. m_autoReleaseKnob->move(m_controlsBoxX + 590, m_controlsBoxY + 38);
  593. lookaheadButton->move(m_controlsBoxX + 202, m_controlsBoxY + 171);
  594. }