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