Audacity  3.0.3
Compressor.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  Compressor.cpp
6 
7  Dominic Mazzoni
8  Martyn Shaw
9  Steve Jolly
10 
11 *******************************************************************//****************************************************************//*******************************************************************/
26 
27 
28 #include "Compressor.h"
29 #include "LoadEffects.h"
30 
31 #include <math.h>
32 
33 #include <wx/brush.h>
34 #include <wx/checkbox.h>
35 #include <wx/dcclient.h>
36 #include <wx/intl.h>
37 #include <wx/slider.h>
38 #include <wx/stattext.h>
39 
40 #include "AColor.h"
41 #include "Prefs.h"
42 #include "../Shuttle.h"
43 #include "../ShuttleGui.h"
44 #include "Theme.h"
45 #include "float_cast.h"
46 #include "../widgets/Ruler.h"
47 
48 #include "../WaveTrack.h"
49 #include "AllThemeResources.h"
50 
51 enum
52 {
53  ID_Threshold = 10000,
57  ID_Decay
58 };
59 
60 // Define keys, defaults, minimums, and maximums for the effect parameters
61 //
62 // Name Type Key Def Min Max Scale
63 Param( Threshold, double, wxT("Threshold"), -12.0, -60.0, -1.0, 1 );
64 Param( NoiseFloor, double, wxT("NoiseFloor"), -40.0, -80.0, -20.0, 0.2 );
65 Param( Ratio, double, wxT("Ratio"), 2.0, 1.1, 10.0, 10 );
66 Param( AttackTime, double, wxT("AttackTime"), 0.2, 0.1, 5.0, 100 );
67 Param( ReleaseTime, double, wxT("ReleaseTime"), 1.0, 1.0, 30.0, 10 );
68 Param( Normalize, bool, wxT("Normalize"), true, false, true, 1 );
69 Param( UsePeak, bool, wxT("UsePeak"), false, false, true, 1 );
70 
71 //----------------------------------------------------------------------------
72 // EffectCompressor
73 //----------------------------------------------------------------------------
74 
76 { XO("Compressor") };
77 
79 
80 BEGIN_EVENT_TABLE(EffectCompressor, wxEvtHandler)
81  EVT_SLIDER(wxID_ANY, EffectCompressor::OnSlider)
83 
85 {
86  mThresholdDB = DEF_Threshold;
87  mNoiseFloorDB = DEF_NoiseFloor;
88  mAttackTime = DEF_AttackTime; // seconds
89  mDecayTime = DEF_ReleaseTime; // seconds
90  mRatio = DEF_Ratio; // positive number > 1.0
91  mNormalize = DEF_Normalize;
92  mUsePeak = DEF_UsePeak;
93 
94  mThreshold = 0.25;
95  mNoiseFloor = 0.01;
96  mCompression = 0.5;
97  mFollowLen = 0;
98 
99  SetLinearEffectFlag(false);
100 }
101 
103 {
104 }
105 
106 // ComponentInterface implementation
107 
109 {
110  return Symbol;
111 }
112 
114 {
115  return XO("Compresses the dynamic range of audio");
116 }
117 
119 {
120  return L"Compressor";
121 }
122 
123 // EffectDefinitionInterface implementation
124 
126 {
127  return EffectTypeProcess;
128 }
129 
130 // EffectClientInterface implementation
132  S.SHUTTLE_PARAM( mThresholdDB, Threshold );
133  S.SHUTTLE_PARAM( mNoiseFloorDB, NoiseFloor );
134  S.SHUTTLE_PARAM( mRatio, Ratio);
135  S.SHUTTLE_PARAM( mAttackTime, AttackTime);
136  S.SHUTTLE_PARAM( mDecayTime, ReleaseTime);
137  S.SHUTTLE_PARAM( mNormalize, Normalize);
138  S.SHUTTLE_PARAM( mUsePeak, UsePeak);
139  return true;
140 }
141 
143 {
144  parms.Write(KEY_Threshold, mThresholdDB);
145  parms.Write(KEY_NoiseFloor, mNoiseFloorDB);
146  parms.Write(KEY_Ratio, mRatio);
147  parms.Write(KEY_AttackTime, mAttackTime);
148  parms.Write(KEY_ReleaseTime, mDecayTime);
149  parms.Write(KEY_Normalize, mNormalize);
150  parms.Write(KEY_UsePeak, mUsePeak);
151 
152  return true;
153 }
154 
156 {
157  ReadAndVerifyDouble(Threshold);
158  ReadAndVerifyDouble(NoiseFloor);
159  ReadAndVerifyDouble(Ratio);
160  ReadAndVerifyDouble(AttackTime);
161  ReadAndVerifyDouble(ReleaseTime);
162  ReadAndVerifyBool(Normalize);
163  ReadAndVerifyBool(UsePeak);
164 
165  mThresholdDB = Threshold;
166  mNoiseFloorDB = NoiseFloor;
167  mRatio = Ratio;
168  mAttackTime = AttackTime;
169  mDecayTime = ReleaseTime;
170  mNormalize = Normalize;
171  mUsePeak = UsePeak;
172 
173  return true;
174 }
175 
176 // Effect Implementation
177 
179 {
180  wxString base = wxT("/Effects/Compressor/");
181 
182  // Migrate settings from 2.1.0 or before
183 
184  // Already migrated, so bail
185  if (gPrefs->Exists(base + wxT("Migrated")))
186  {
187  return true;
188  }
189 
190  // Load the old "current" settings
191  if (gPrefs->Exists(base))
192  {
193  gPrefs->Read(base + wxT("ThresholdDB"), &mThresholdDB, -12.0f );
194  gPrefs->Read(base + wxT("NoiseFloorDB"), &mNoiseFloorDB, -40.0f );
195  gPrefs->Read(base + wxT("Ratio"), &mRatio, 2.0f );
196  gPrefs->Read(base + wxT("AttackTime"), &mAttackTime, 0.2f );
197  gPrefs->Read(base + wxT("DecayTime"), &mDecayTime, 1.0f );
198  gPrefs->Read(base + wxT("Normalize"), &mNormalize, true );
199  gPrefs->Read(base + wxT("UsePeak"), &mUsePeak, false );
200 
202 
203  // Do not migrate again
204  gPrefs->Write(base + wxT("Migrated"), true);
205  gPrefs->Flush();
206  }
207 
208  return true;
209 }
210 
211 namespace {
212 
214  /* i18n-hint: usually leave this as is as dB doesn't get translated*/
215 { return XO("%3d dB").Format(value); }
216 
218 { return XO("%.2f secs").Format( value ); }
219 
221 { return XO("%.1f secs").Format( value ); }
222 
223 TranslatableString RatioTextFormat( int sliderValue, double value )
224 {
225  auto format = (sliderValue % 10 == 0)
226  /* i18n-hint: Unless your language has a different convention for ratios,
227  * like 8:1, leave as is.*/
228  ? XO("%.0f:1")
229  /* i18n-hint: Unless your language has a different convention for ratios,
230  * like 8:1, leave as is.*/
231  : XO("%.1f:1");
232  return format.Format( value );
233 }
234 
235 TranslatableString RatioLabelFormat( int sliderValue, double value )
236 {
237  auto format = (sliderValue % 10 == 0)
238  ? XO("Ratio %.0f to 1")
239  : XO("Ratio %.1f to 1");
240  return format.Format( value );
241 }
242 
243 }
244 
246 {
247  S.SetBorder(5);
248 
249  S.StartHorizontalLay(wxEXPAND, true);
250  {
251  S.SetBorder(10);
253  mThresholdDB,
255  mRatio);
256  S.Prop(true)
257  .Position(wxEXPAND | wxALL)
258  .MinSize( { 400, 200 } )
259  .AddWindow(mPanel);
260  S.SetBorder(5);
261  }
262  S.EndHorizontalLay();
263 
264  S.StartStatic( {} );
265  {
266  S.StartMultiColumn(3, wxEXPAND);
267  {
268  S.SetStretchyCol(1);
269  mThresholdLabel = S.AddVariableText(XO("&Threshold:"), true,
270  wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
272  .Name(XO("Threshold"))
273  .Style(wxSL_HORIZONTAL)
274  .AddSlider( {},
275  DEF_Threshold * SCL_Threshold,
276  MAX_Threshold * SCL_Threshold,
277  MIN_Threshold * SCL_Threshold);
279  wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
280 
281  mNoiseFloorLabel = S.AddVariableText(XO("&Noise Floor:"), true,
282  wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
284  .Name(XO("Noise Floor"))
285  .Style(wxSL_HORIZONTAL)
286  .AddSlider( {},
287  DEF_NoiseFloor * SCL_NoiseFloor,
288  MAX_NoiseFloor * SCL_NoiseFloor,
289  MIN_NoiseFloor * SCL_NoiseFloor);
291  true, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
292 
293  mRatioLabel = S.AddVariableText(XO("&Ratio:"), true,
294  wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
296  .Name(XO("Ratio"))
297  .Style(wxSL_HORIZONTAL)
298  .AddSlider( {},
299  DEF_Ratio * SCL_Ratio,
300  MAX_Ratio * SCL_Ratio,
301  MIN_Ratio * SCL_Ratio);
302  mRatioSlider->SetPageSize(5);
303  mRatioText = S.AddVariableText(RatioTextFormat( 1, 99.9 ), true,
304  wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
305 
306  /* i18n-hint: Particularly in percussion, sounds can be regarded as having
307  * an 'attack' phase where the sound builds up and a 'decay' where the
308  * sound dies away. So this means 'onset duration'. */
309  mAttackLabel = S.AddVariableText(XO("&Attack Time:"), true,
310  wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
312  /* i18n-hint: Particularly in percussion, sounds can be regarded as having
313  * an 'attack' phase where the sound builds up and a 'decay' where the
314  * sound dies away. So this means 'onset duration'. */
315  .Name(XO("Attack Time"))
316  .Style(wxSL_HORIZONTAL)
317  .AddSlider( {},
318  DEF_AttackTime * SCL_AttackTime,
319  MAX_AttackTime * SCL_AttackTime,
320  MIN_AttackTime * SCL_AttackTime);
322  AttackTimeFormat(9.99),
323  true, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
324 
325  /* i18n-hint: Particularly in percussion, sounds can be regarded as having
326  * an 'attack' phase where the sound builds up and a 'decay' or 'release' where the
327  * sound dies away. */
328  mDecayLabel = S.AddVariableText(XO("R&elease Time:"), true,
329  wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
331  /* i18n-hint: Particularly in percussion, sounds can be regarded as having
332  * an 'attack' phase where the sound builds up and a 'decay' or 'release' where the
333  * sound dies away. */
334  .Name(XO("Release Time"))
335  .Style(wxSL_HORIZONTAL)
336  .AddSlider( {},
337  DEF_ReleaseTime * SCL_ReleaseTime,
338  MAX_ReleaseTime * SCL_ReleaseTime,
339  MIN_ReleaseTime * SCL_ReleaseTime);
340 
342  DecayTimeFormat(99.9),
343  true, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
344  }
345  S.EndMultiColumn();
346  }
347  S.EndStatic();
348 
349  S.StartHorizontalLay(wxCENTER, false);
350  {
351  /* i18n-hint: Make-up, i.e. correct for any reduction, rather than fabricate it.*/
352  mGainCheckBox = S.AddCheckBox(XXO("Ma&ke-up gain for 0 dB after compressing"),
353  DEF_Normalize);
354  /* i18n-hint: "Compress" here means reduce variations of sound volume,
355  NOT related to file-size compression; Peaks means extremes in volume */
356  mPeakCheckBox = S.AddCheckBox(XXO("C&ompress based on Peaks"),
357  DEF_UsePeak);
358  }
359  S.EndHorizontalLay();
360 }
361 
363 {
364  mThresholdSlider->SetValue(lrint(mThresholdDB));
365  mNoiseFloorSlider->SetValue(lrint(mNoiseFloorDB * SCL_NoiseFloor));
366  mRatioSlider->SetValue(lrint(mRatio * SCL_Ratio));
367  mAttackSlider->SetValue(lrint(mAttackTime * SCL_AttackTime));
368  mDecaySlider->SetValue(lrint(mDecayTime * SCL_ReleaseTime));
369  mGainCheckBox->SetValue(mNormalize);
370  mPeakCheckBox->SetValue(mUsePeak);
371 
372  UpdateUI();
373 
374  return true;
375 }
376 
378 {
379  if (!mUIParent->Validate())
380  {
381  return false;
382  }
383 
384  mThresholdDB = (double) mThresholdSlider->GetValue();
385  mNoiseFloorDB = (double) mNoiseFloorSlider->GetValue() / SCL_NoiseFloor;
386  mRatio = (double) mRatioSlider->GetValue() / SCL_Ratio;
387  mAttackTime = (double) mAttackSlider->GetValue() / 100.0; //SCL_AttackTime;
388  mDecayTime = (double) mDecaySlider->GetValue() / SCL_ReleaseTime;
389  mNormalize = mGainCheckBox->GetValue();
390  mUsePeak = mPeakCheckBox->GetValue();
391 
392  return true;
393 }
394 
395 // EffectTwoPassSimpleMono implementation
396 
398 {
401  mNoiseCounter = 100;
402 
403  mAttackInverseFactor = exp(log(mThreshold) / (mCurRate * mAttackTime + 0.5));
405  mDecayFactor = exp(log(mThreshold) / (mCurRate * mDecayTime + 0.5));
406 
407  if(mRatio > 1)
408  mCompression = 1.0-1.0/mRatio;
409  else
410  mCompression = 0.0;
411 
413 
414  mCircleSize = 100;
415  mCircle.reinit( mCircleSize, true );
416  mCirclePos = 0;
417  mRMSSum = 0.0;
418 
419  return true;
420 }
421 
423 {
424  mMax=0.0;
425  if (!mNormalize)
427 
428  // Find the maximum block length required for any track
429  size_t maxlen = inputTracks()->Selected< const WaveTrack >().max(
431  );
432  mFollow1.reset();
433  mFollow2.reset();
434  // Allocate buffers for the envelope
435  if(maxlen > 0) {
436  mFollow1.reinit(maxlen);
437  mFollow2.reinit(maxlen);
438  }
439  mFollowLen = maxlen;
440 
441  return true;
442 }
443 
445 {
446  // Actually, this should not even be called, because we call
447  // DisableSecondPass() before, if mNormalize is false.
448  return mNormalize;
449 }
450 
451 // Process the input with 2 buffers available at a time
452 // buffer1 will be written upon return
453 // buffer2 will be passed as buffer1 on the next call
455  (float *buffer1, size_t len1, float *buffer2, size_t len2)
456 {
457  // If buffers are bigger than allocated, then abort
458  // (this should never happen, but if it does, we don't want to crash)
459  if((len1 > mFollowLen) || (len2 > mFollowLen))
460  return false;
461 
462  // This makes sure that the initial value is well-chosen
463  // buffer1 == NULL on the first and only the first call
464  if (buffer1 == NULL) {
465  // Initialize the mLastLevel to the peak level in the first buffer
466  // This avoids problems with large spike events near the beginning of the track
468  for(size_t i=0; i<len2; i++) {
469  if(mLastLevel < fabs(buffer2[i]))
470  mLastLevel = fabs(buffer2[i]);
471  }
472  }
473 
474  // buffer2 is NULL on the last and only the last call
475  if(buffer2 != NULL) {
476  Follow(buffer2, mFollow2.get(), len2, mFollow1.get(), len1);
477  }
478 
479  if(buffer1 != NULL) {
480  for (size_t i = 0; i < len1; i++) {
481  buffer1[i] = DoCompression(buffer1[i], mFollow1[i]);
482  }
483  }
484 
485 
486 #if 0
487  // Copy the envelope over the track data (for debug purposes)
488  memcpy(buffer1, mFollow1, len1*sizeof(float));
489 #endif
490 
491  // Rotate the buffer pointers
492  mFollow1.swap(mFollow2);
493 
494  return true;
495 }
496 
497 bool EffectCompressor::ProcessPass2(float *buffer, size_t len)
498 {
499  if (mMax != 0)
500  {
501  for (size_t i = 0; i < len; i++)
502  buffer[i] /= mMax;
503  }
504 
505  return true;
506 }
507 
509 {
510  // Recompute the RMS sum periodically to prevent accumulation of rounding errors
511  // during long waveforms
512  mRMSSum = 0;
513  for(size_t i=0; i<mCircleSize; i++)
514  mRMSSum += mCircle[i];
515 }
516 
517 float EffectCompressor::AvgCircle(float value)
518 {
519  float level;
520 
521  // Calculate current level from root-mean-squared of
522  // circular buffer ("RMS")
524  mCircle[mCirclePos] = value*value;
526  level = sqrt(mRMSSum/mCircleSize);
528 
529  return level;
530 }
531 
532 void EffectCompressor::Follow(float *buffer, float *env, size_t len, float *previous, size_t previous_len)
533 {
534  /*
535 
536  "Follow"ing algorithm by Roger B. Dannenberg, taken from
537  Nyquist. His description follows. -DMM
538 
539  Description: this is a sophisticated envelope follower.
540  The input is an envelope, e.g. something produced with
541  the AVG function. The purpose of this function is to
542  generate a smooth envelope that is generally not less
543  than the input signal. In other words, we want to "ride"
544  the peaks of the signal with a smooth function. The
545  algorithm is as follows: keep a current output value
546  (called the "value"). The value is allowed to increase
547  by at most rise_factor and decrease by at most fall_factor.
548  Therefore, the next value should be between
549  value * rise_factor and value * fall_factor. If the input
550  is in this range, then the next value is simply the input.
551  If the input is less than value * fall_factor, then the
552  next value is just value * fall_factor, which will be greater
553  than the input signal. If the input is greater than value *
554  rise_factor, then we compute a rising envelope that meets
555  the input value by working bacwards in time, changing the
556  previous values to input / rise_factor, input / rise_factor^2,
557  input / rise_factor^3, etc. until this NEW envelope intersects
558  the previously computed values. There is only a limited buffer
559  in which we can work backwards, so if the NEW envelope does not
560  intersect the old one, then make yet another pass, this time
561  from the oldest buffered value forward, increasing on each
562  sample by rise_factor to produce a maximal envelope. This will
563  still be less than the input.
564 
565  The value has a lower limit of floor to make sure value has a
566  reasonable positive value from which to begin an attack.
567  */
568  double level,last;
569 
570  if(!mUsePeak) {
571  // Update RMS sum directly from the circle buffer
572  // to avoid accumulation of rounding errors
573  FreshenCircle();
574  }
575  // First apply a peak detect with the requested decay rate
576  last = mLastLevel;
577  for(size_t i=0; i<len; i++) {
578  if(mUsePeak)
579  level = fabs(buffer[i]);
580  else // use RMS
581  level = AvgCircle(buffer[i]);
582  // Don't increase gain when signal is continuously below the noise floor
583  if(level < mNoiseFloor) {
584  mNoiseCounter++;
585  } else {
586  mNoiseCounter = 0;
587  }
588  if(mNoiseCounter < 100) {
589  last *= mDecayFactor;
590  if(last < mThreshold)
591  last = mThreshold;
592  if(level > last)
593  last = level;
594  }
595  env[i] = last;
596  }
597  mLastLevel = last;
598 
599  // Next do the same process in reverse direction to get the requested attack rate
600  last = mLastLevel;
601  for(size_t i = len; i--;) {
602  last *= mAttackInverseFactor;
603  if(last < mThreshold)
604  last = mThreshold;
605  if(env[i] < last)
606  env[i] = last;
607  else
608  last = env[i];
609  }
610 
611  if((previous != NULL) && (previous_len > 0)) {
612  // If the previous envelope was passed, propagate the rise back until we intersect
613  for(size_t i = previous_len; i--;) {
614  last *= mAttackInverseFactor;
615  if(last < mThreshold)
616  last = mThreshold;
617  if(previous[i] < last)
618  previous[i] = last;
619  else // Intersected the previous envelope buffer, so we are finished
620  return;
621  }
622  // If we can't back up far enough, project the starting level forward
623  // until we intersect the desired envelope
624  last = previous[0];
625  for(size_t i=1; i<previous_len; i++) {
626  last *= mAttackFactor;
627  if(previous[i] > last)
628  previous[i] = last;
629  else // Intersected the desired envelope, so we are finished
630  return;
631  }
632  // If we still didn't intersect, then continue ramp up into current buffer
633  for(size_t i=0; i<len; i++) {
634  last *= mAttackFactor;
635  if(buffer[i] > last)
636  buffer[i] = last;
637  else // Finally got an intersect
638  return;
639  }
640  // If we still didn't intersect, then reset mLastLevel
641  mLastLevel = last;
642  }
643 }
644 
645 float EffectCompressor::DoCompression(float value, double env)
646 {
647  float out;
648  if(mUsePeak) {
649  // Peak values map 1.0 to 1.0 - 'upward' compression
650  out = value * pow(1.0/env, mCompression);
651  } else {
652  // With RMS-based compression don't change values below mThreshold - 'downward' compression
653  out = value * pow(mThreshold/env, mCompression);
654  }
655 
656  // Retain the maximum value for use in the normalization pass
657  if(mMax < fabs(out))
658  mMax = fabs(out);
659 
660  return out;
661 }
662 
663 void EffectCompressor::OnSlider(wxCommandEvent & WXUNUSED(evt))
664 {
666  UpdateUI();
667 }
668 
670 {
671  mThresholdLabel->SetName(wxString::Format(_("Threshold %d dB"), (int) mThresholdDB));
672  mThresholdText->SetLabel(ThresholdFormat((int) mThresholdDB).Translation());
673  mThresholdText->SetName(mThresholdText->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
674 
675  mNoiseFloorLabel->SetName(wxString::Format(_("Noise Floor %d dB"), (int) mNoiseFloorDB));
676  mNoiseFloorText->SetLabel(ThresholdFormat((int) mNoiseFloorDB).Translation());
677  mNoiseFloorText->SetName(mNoiseFloorText->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
678 
679  mRatioLabel->SetName(
681  mRatioText->SetLabel(
683  mRatioText->SetName(mRatioText->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
684 
685  mAttackLabel->SetName(wxString::Format(_("Attack Time %.2f secs"), mAttackTime));
686  mAttackText->SetLabel(AttackTimeFormat(mAttackTime).Translation());
687  mAttackText->SetName(mAttackText->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
688 
689  mDecayLabel->SetName(wxString::Format(_("Release Time %.1f secs"), mDecayTime));
690  mDecayText->SetLabel(DecayTimeFormat(mDecayTime).Translation());
691  mDecayText->SetName(mDecayText->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
692 
693  mPanel->Refresh(false);
694 
695  return;
696 }
697 
698 //----------------------------------------------------------------------------
699 // EffectCompressorPanel
700 //----------------------------------------------------------------------------
701 
702 BEGIN_EVENT_TABLE(EffectCompressorPanel, wxPanelWrapper)
706 
707 EffectCompressorPanel::EffectCompressorPanel(wxWindow *parent, wxWindowID winid,
708  double & threshold,
709  double & noiseFloor,
710  double & ratio)
711 : wxPanelWrapper(parent, winid),
712  threshold(threshold),
713  noiseFloor(noiseFloor),
714  ratio(ratio)
715 {
716 }
717 
718 void EffectCompressorPanel::OnPaint(wxPaintEvent & WXUNUSED(evt))
719 {
720  wxPaintDC dc(this);
721 
722  int width, height;
723  GetSize(&width, &height);
724 
725  double rangeDB = 60;
726 
727  // Ruler
728  int w = 0;
729  int h = 0;
730 
731  Ruler vRuler;
732  vRuler.SetBounds(0, 0, width, height);
733  vRuler.SetOrientation(wxVERTICAL);
734  vRuler.SetRange(0, -rangeDB);
736  vRuler.SetUnits(XO("dB"));
737  vRuler.GetMaxSize(&w, NULL);
738 
739  Ruler hRuler;
740  hRuler.SetBounds(0, 0, width, height);
741  hRuler.SetOrientation(wxHORIZONTAL);
742  hRuler.SetRange(-rangeDB, 0);
744  hRuler.SetUnits(XO("dB"));
745  hRuler.SetFlip(true);
746  hRuler.GetMaxSize(NULL, &h);
747 
748  vRuler.SetBounds(0, 0, w, height - h);
749  hRuler.SetBounds(w, height - h, width, height);
750 
751  vRuler.SetTickColour( theTheme.Colour( clrGraphLabels ));
752  hRuler.SetTickColour( theTheme.Colour( clrGraphLabels ));
753 
754 #if defined(__WXMSW__)
755  dc.Clear();
756 #endif
757 
758  wxRect border;
759  border.x = w;
760  border.y = 0;
761  border.width = width - w;
762  border.height = height - h + 1;
763 
764  dc.SetBrush(*wxWHITE_BRUSH);
765  dc.SetPen(*wxBLACK_PEN);
766  dc.DrawRectangle(border);
767 
768  wxRect envRect = border;
769  envRect.Deflate( 2, 2 );
770 
771  int kneeX = lrint((rangeDB+threshold)*envRect.width/rangeDB);
772  int kneeY = lrint((rangeDB+threshold/ratio)*envRect.height/rangeDB);
773 
774  int finalY = envRect.height;
775  int startY = lrint((threshold*(1.0/ratio-1.0))*envRect.height/rangeDB);
776 
777  // Yellow line for threshold
778 /* dc.SetPen(wxPen(wxColour(220, 220, 0), 1, wxSOLID));
779  AColor::Line(dc,
780  envRect.x,
781  envRect.y + envRect.height - kneeY,
782  envRect.x + envRect.width - 1,
783  envRect.y + envRect.height - kneeY);*/
784 
785  // Was: Nice dark red line for the compression diagram
786 // dc.SetPen(wxPen(wxColour(180, 40, 40), 3, wxSOLID));
787 
788  // Nice blue line for compressor, same color as used in the waveform envelope.
789  dc.SetPen( AColor::WideEnvelopePen) ;
790 
791  AColor::Line(dc,
792  envRect.x,
793  envRect.y + envRect.height - startY,
794  envRect.x + kneeX - 1,
795  envRect.y + envRect.height - kneeY);
796 
797  AColor::Line(dc,
798  envRect.x + kneeX,
799  envRect.y + envRect.height - kneeY,
800  envRect.x + envRect.width - 1,
801  envRect.y + envRect.height - finalY);
802 
803  // Paint border again
804  dc.SetBrush(*wxTRANSPARENT_BRUSH);
805  dc.SetPen(*wxBLACK_PEN);
806  dc.DrawRectangle(border);
807 
808  vRuler.Draw(dc);
809  hRuler.Draw(dc);
810 }
811 
812 void EffectCompressorPanel::OnSize(wxSizeEvent & WXUNUSED(evt))
813 {
814  Refresh(false);
815 }
EffectCompressor::GetDescription
TranslatableString GetDescription() override
Definition: Compressor.cpp:113
EffectCompressor::mCompression
double mCompression
Definition: Compressor.h:92
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
EffectCompressor
An Effect derived from EffectTwoPassSimpleMono.
Definition: Compressor.h:23
CommandParameters
CommandParameters, derived from wxFileConfig, is essentially doing the same things as the Shuttle cla...
Definition: EffectAutomationParameters.h:67
EffectCompressor::ManualPage
ManualPageID ManualPage() override
Definition: Compressor.cpp:118
WaveTrack
A Track that contains audio waveform data.
Definition: WaveTrack.h:69
EffectTypeProcess
@ EffectTypeProcess
Definition: EffectInterface.h:59
DB_TO_LINEAR
const double MIN_Threshold_Linear DB_TO_LINEAR(MIN_Threshold_dB)
ShuttleGuiBase::AddCheckBox
wxCheckBox * AddCheckBox(const TranslatableString &Prompt, bool Selected)
Definition: ShuttleGui.cpp:309
EffectCompressor::mRatioLabel
wxStaticText * mRatioLabel
Definition: Compressor.h:112
ID_Ratio
@ ID_Ratio
Definition: Compressor.cpp:55
EffectCompressor::mCircle
Doubles mCircle
Definition: Compressor.h:78
Ruler::LinearDBFormat
@ LinearDBFormat
Definition: Ruler.h:34
anonymous_namespace{Compressor.cpp}::DecayTimeFormat
TranslatableString DecayTimeFormat(double value)
Definition: Compressor.cpp:220
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:70
AllThemeResources.h
wxPanelWrapper
Definition: wxPanelWrapper.h:41
EffectCompressor::mNoiseFloorLabel
wxStaticText * mNoiseFloorLabel
Definition: Compressor.h:108
EffectCompressor::mFollow1
Floats mFollow1
Definition: Compressor.h:97
EffectCompressor::mNoiseFloor
double mNoiseFloor
Definition: Compressor.h:93
AColor::Line
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:109
EffectCompressor::InitPass1
bool InitPass1() override
Definition: Compressor.cpp:422
ArrayOf::reinit
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:57
ID_Threshold
@ ID_Threshold
Definition: Compressor.cpp:53
EffectCompressor::mNoiseCounter
int mNoiseCounter
Definition: Compressor.h:94
EffectCompressor::PopulateOrExchange
void PopulateOrExchange(ShuttleGui &S) override
Definition: Compressor.cpp:245
ShuttleGui::MinSize
ShuttleGui & MinSize()
Definition: ShuttleGui.h:733
XO
#define XO(s)
Definition: Internat.h:31
Ruler::SetTickColour
void SetTickColour(const wxColour &colour)
Definition: Ruler.h:148
EffectCompressor::mAttackTime
double mAttackTime
Definition: Compressor.h:80
Ruler::SetFormat
void SetFormat(RulerFormat format)
Definition: Ruler.cpp:131
ShuttleParams
Shuttle that deals with parameters. This is a base class with lots of virtual functions that do nothi...
Definition: Shuttle.h:62
Ruler::Draw
void Draw(wxDC &dc) const
Definition: Ruler.cpp:1428
EffectCompressor::mThresholdText
wxStaticText * mThresholdText
Definition: Compressor.h:106
EffectCompressor::mRatioText
wxStaticText * mRatioText
Definition: Compressor.h:114
ShuttleGuiBase::EndMultiColumn
void EndMultiColumn()
Definition: ShuttleGui.cpp:1238
Effect::SaveUserPreset
bool SaveUserPreset(const RegistryPath &name) override
Definition: Effect.cpp:570
EffectCompressor::mThresholdLabel
wxStaticText * mThresholdLabel
Definition: Compressor.h:104
Ruler::SetUnits
void SetUnits(const TranslatableString &units)
Definition: Ruler.cpp:153
EffectCompressor::~EffectCompressor
virtual ~EffectCompressor()
Definition: Compressor.cpp:102
EffectCompressor::mThresholdSlider
wxSlider * mThresholdSlider
Definition: Compressor.h:105
ComponentInterfaceSymbol
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
Definition: ComponentInterfaceSymbol.h:27
ShuttleGui::Id
ShuttleGui & Id(int id)
Definition: ShuttleGui.cpp:2274
Ruler::SetFlip
void SetFlip(bool flip)
Definition: Ruler.cpp:239
EffectCompressor::mNoiseFloorSlider
wxSlider * mNoiseFloorSlider
Definition: Compressor.h:109
EffectCompressor::mThreshold
double mThreshold
Definition: Compressor.h:91
Ruler::GetMaxSize
void GetMaxSize(wxCoord *width, wxCoord *height)
Definition: Ruler.cpp:1603
anonymous_namespace{Compressor.cpp}::ThresholdFormat
TranslatableString ThresholdFormat(int value)
Definition: Compressor.cpp:213
EffectCompressor::mCirclePos
size_t mCirclePos
Definition: Compressor.h:77
ShuttleGui::Style
ShuttleGui & Style(long iStyle)
Definition: ShuttleGui.h:727
EffectCompressor::mDecayTime
double mDecayTime
Definition: Compressor.h:87
ID_NoiseFloor
@ ID_NoiseFloor
Definition: Compressor.cpp:54
EffectCompressor::InitPass2
bool InitPass2() override
Definition: Compressor.cpp:444
EffectCompressor::NewTrackPass1
bool NewTrackPass1() override
Definition: Compressor.cpp:397
ID_Attack
@ ID_Attack
Definition: Compressor.cpp:56
EffectCompressor::mPeakCheckBox
wxCheckBox * mPeakCheckBox
Definition: Compressor.h:125
anonymous_namespace{Compressor.cpp}::RatioLabelFormat
TranslatableString RatioLabelFormat(int sliderValue, double value)
Definition: Compressor.cpp:235
XXO
#define XXO(s)
Definition: Internat.h:44
Effect::inputTracks
const TrackList * inputTracks() const
Definition: Effect.h:463
ShuttleGuiBase::EndHorizontalLay
void EndHorizontalLay()
Definition: ShuttleGui.cpp:1177
EffectCompressor::mAttackInverseFactor
double mAttackInverseFactor
Definition: Compressor.h:89
EffectCompressor::GetType
EffectType GetType() override
Definition: Compressor.cpp:125
EffectCompressor::OnSlider
void OnSlider(wxCommandEvent &evt)
Definition: Compressor.cpp:663
ShuttleGuiBase::StartHorizontalLay
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:1167
EffectCompressor::mAttackLabel
wxStaticText * mAttackLabel
Definition: Compressor.h:116
EffectCompressor::mGainCheckBox
wxCheckBox * mGainCheckBox
Definition: Compressor.h:124
ShuttleGuiBase::StartMultiColumn
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
Definition: ShuttleGui.cpp:1229
Compressor.h
EffectCompressor::mDecayFactor
double mDecayFactor
Definition: Compressor.h:90
Ruler
Used to display a Ruler.
Definition: Ruler.h:26
Param
Param(Threshold, double, wxT("Threshold"), -12.0, -60.0, -1.0, 1)
AColor::WideEnvelopePen
static wxPen WideEnvelopePen
Definition: AColor.h:105
Theme.h
EffectCompressorPanel::OnPaint
void OnPaint(wxPaintEvent &evt)
Definition: Compressor.cpp:718
format
int format
Definition: ExportPCM.cpp:56
ShuttleGuiBase::GetParent
wxWindow * GetParent()
Definition: ShuttleGui.h:496
EffectTwoPassSimpleMono::DisableSecondPass
void DisableSecondPass()
Definition: TwoPassSimpleMono.h:67
EffectCompressor::AvgCircle
float AvgCircle(float x)
Definition: Compressor.cpp:517
EffectCompressor::mFollow2
Floats mFollow2
Definition: Compressor.h:97
Ruler::SetRange
void SetRange(double min, double max)
Definition: Ruler.cpp:188
Ruler::SetOrientation
void SetOrientation(int orient)
Definition: Ruler.cpp:174
EffectCompressor::mMax
double mMax
Definition: Compressor.h:100
ReadAndVerifyDouble
#define ReadAndVerifyDouble(name)
Definition: Effect.h:633
ShuttleGui::Prop
ShuttleGui & Prop(int iProp)
Definition: ShuttleGui.h:725
ShuttleGuiBase::AddSlider
wxSlider * AddSlider(const TranslatableString &Prompt, int pos, int Max, int Min=0)
Definition: ShuttleGui.cpp:589
EffectCompressor::SetAutomationParameters
bool SetAutomationParameters(CommandParameters &parms) override
Definition: Compressor.cpp:155
EffectCompressor::UpdateUI
void UpdateUI()
Definition: Compressor.cpp:669
theTheme
THEME_API Theme theTheme
Definition: Theme.cpp:79
Effect::GetCurrentSettingsGroup
RegistryPath GetCurrentSettingsGroup() override
Definition: Effect.cpp:866
EffectCompressor::mRMSSum
double mRMSSum
Definition: Compressor.h:75
LoadEffects.h
EffectCompressor::mDecayText
wxStaticText * mDecayText
Definition: Compressor.h:122
EffectCompressor::mAttackText
wxStaticText * mAttackText
Definition: Compressor.h:118
ShuttleGuiBase::StartStatic
wxStaticBox * StartStatic(const TranslatableString &Str, int iProp=0)
Definition: ShuttleGui.cpp:893
WaveTrack::GetMaxBlockSize
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:1661
EffectCompressor::GetAutomationParameters
bool GetAutomationParameters(CommandParameters &parms) override
Definition: Compressor.cpp:142
ShuttleGui::Name
ShuttleGui & Name(const TranslatableString &name)
Definition: ShuttleGui.h:663
EffectCompressor::mDecayLabel
wxStaticText * mDecayLabel
Definition: Compressor.h:120
EffectCompressor::mAttackSlider
wxSlider * mAttackSlider
Definition: Compressor.h:117
EffectCompressor::DoCompression
float DoCompression(float x, double env)
Definition: Compressor.cpp:645
EffectCompressor::TwoBufferProcessPass1
bool TwoBufferProcessPass1(float *buffer1, size_t len1, float *buffer2, size_t len2) override
Definition: Compressor.cpp:455
FileConfig::Flush
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
EffectCompressor::Symbol
static const ComponentInterfaceSymbol Symbol
Definition: Compressor.h:25
BuiltinEffectsModule::Registration
Definition: LoadEffects.h:40
ShuttleGui::Position
ShuttleGui & Position(int flags)
Definition: ShuttleGui.h:712
anonymous_namespace{Compressor.cpp}::reg
BuiltinEffectsModule::Registration< EffectCompressor > reg
Definition: Compressor.cpp:78
EffectCompressor::mNoiseFloorText
wxStaticText * mNoiseFloorText
Definition: Compressor.h:110
EffectCompressor::mCircleSize
size_t mCircleSize
Definition: Compressor.h:76
Effect::mUIParent
wxWindow * mUIParent
Definition: Effect.h:478
TaggedIdentifier< ManualPageIDTag >
_
#define _(s)
Definition: Internat.h:75
anonymous_namespace{Compressor.cpp}::AttackTimeFormat
TranslatableString AttackTimeFormat(double value)
Definition: Compressor.cpp:217
ID_Decay
@ ID_Decay
Definition: Compressor.cpp:57
EffectCompressor::TransferDataToWindow
bool TransferDataToWindow() override
Definition: Compressor.cpp:362
Ruler::SetBounds
void SetBounds(int left, int top, int right, int bottom)
Definition: Ruler.cpp:332
EffectCompressor::mNormalize
bool mNormalize
Definition: Compressor.h:84
EffectCompressor::Follow
void Follow(float *buffer, float *env, size_t len, float *previous, size_t previous_len)
Definition: Compressor.cpp:532
EffectCompressor::mFollowLen
size_t mFollowLen
Definition: Compressor.h:98
EffectCompressor::mDecaySlider
wxSlider * mDecaySlider
Definition: Compressor.h:121
ThemeBase::Colour
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1189
EffectCompressor::mUsePeak
bool mUsePeak
Definition: Compressor.h:85
EffectCompressorPanel
Definition: Compressor.h:131
EffectCompressorPanel::ratio
double & ratio
Definition: Compressor.h:145
EffectCompressorPanel::OnSize
void OnSize(wxSizeEvent &evt)
Definition: Compressor.cpp:812
EffectCompressorPanel::threshold
double & threshold
Definition: Compressor.h:143
Prefs.h
TranslatableString::Translation
wxString Translation() const
Definition: TranslatableString.h:79
ShuttleGuiBase::SetBorder
void SetBorder(int Border)
Definition: ShuttleGui.h:489
anonymous_namespace{Compressor.cpp}::RatioTextFormat
TranslatableString RatioTextFormat(int sliderValue, double value)
Definition: Compressor.cpp:223
EffectCompressor::mAttackFactor
double mAttackFactor
Definition: Compressor.h:88
EffectCompressor::TransferDataFromWindow
bool TransferDataFromWindow() override
Definition: Compressor.cpp:377
ShuttleGuiBase::AddVariableText
wxStaticText * AddVariableText(const TranslatableString &Str, bool bCenter=false, int PositionFlags=0, int wrapWidth=0)
Definition: ShuttleGui.cpp:463
EffectCompressor::FreshenCircle
void FreshenCircle()
Definition: Compressor.cpp:508
ShuttleGuiBase::EndStatic
void EndStatic()
Definition: ShuttleGui.cpp:922
EffectType
EffectType
Definition: EffectInterface.h:55
safenew
#define safenew
Definition: MemoryX.h:10
lrint
#define lrint(dbl)
Definition: float_cast.h:169
float_cast.h
ShuttleGuiBase::SetStretchyCol
void SetStretchyCol(int i)
Used to modify an already placed FlexGridSizer to make a column stretchy.
Definition: ShuttleGui.cpp:202
EffectCompressor::mRatio
double mRatio
Definition: Compressor.h:83
AColor.h
EffectCompressor::GetSymbol
ComponentInterfaceSymbol GetSymbol() override
Definition: Compressor.cpp:108
EffectCompressor::mRatioSlider
wxSlider * mRatioSlider
Definition: Compressor.h:113
TrackList::Selected
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:1388
EffectCompressor::ProcessPass2
bool ProcessPass2(float *buffer, size_t len) override
Definition: Compressor.cpp:497
EffectCompressor::DefineParams
bool DefineParams(ShuttleParams &S) override
Definition: Compressor.cpp:131
END_EVENT_TABLE
END_EVENT_TABLE()
ReadAndVerifyBool
#define ReadAndVerifyBool(name)
Definition: Effect.h:635
EffectCompressor::mNoiseFloorDB
double mNoiseFloorDB
Definition: Compressor.h:82
EffectTwoPassSimpleMono::mCurRate
double mCurRate
Definition: TwoPassSimpleMono.h:71
EffectCompressor::mLastLevel
double mLastLevel
Definition: Compressor.h:96
EffectCompressor::mThresholdDB
double mThresholdDB
Definition: Compressor.h:81
ShuttleGui
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:631
EffectCompressor::Startup
bool Startup() override
Definition: Compressor.cpp:178
EffectCompressor::mPanel
EffectCompressorPanel * mPanel
Definition: Compressor.h:102