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