Audacity  2.2.2
ScienFilter.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  Effect/ScienFilter.cpp
6 
7  Norm C
8  Mitch Golden
9  Vaughan Johnson (Preview)
10 
11 *******************************************************************//****************************************************************//****************************************************************//*******************************************************************/
34 
35 #include "../Audacity.h"
36 #include "ScienFilter.h"
37 
38 #include <math.h>
39 #include <float.h>
40 
41 #include <wx/brush.h>
42 #include <wx/dcmemory.h>
43 #include <wx/intl.h>
44 #include <wx/settings.h>
45 #include <wx/utils.h>
46 #include <wx/valgen.h>
47 
48 #include "../AColor.h"
49 #include "../AllThemeResources.h"
50 #include "../PlatformCompatibility.h"
51 #include "../Prefs.h"
52 #include "../Project.h"
53 #include "../ShuttleGui.h"
54 #include "../Theme.h"
55 #include "../WaveTrack.h"
56 #include "../widgets/valnum.h"
57 #include "../widgets/ErrorDialog.h"
58 
59 #include "Equalization.h" // For SliderAx
60 
61 #if !defined(M_PI)
62 #define PI = 3.1415926535897932384626433832795
63 #else
64 #define PI M_PI
65 #endif
66 #define square(a) ((a)*(a))
67 
68 enum
69 {
70  ID_FilterPanel = 10000,
79 };
80 
81 enum kTypes
82 {
87 };
88 
89 static const wxChar *kTypeStrings[] =
90 {
91  /*i18n-hint: Butterworth is the name of the person after whom the filter type is named.*/
92  XO("Butterworth"),
93  /*i18n-hint: Chebyshev is the name of the person after whom the filter type is named.*/
94  XO("Chebyshev Type I"),
95  /*i18n-hint: Chebyshev is the name of the person after whom the filter type is named.*/
96  XO("Chebyshev Type II")
97 };
98 
100 {
104 };
105 
106 static const wxChar *kSubTypeStrings[] =
107 {
108  XO("Lowpass"),
109  XO("Highpass")
110 };
111 
112 // Define keys, defaults, minimums, and maximums for the effect parameters
113 //
114 // Name Type Key Def Min Max Scale
115 Param( Type, int, wxT("FilterType"), kButterworth, 0, kNumTypes - 1, 1 );
116 Param( Subtype, int, wxT("FilterSubtype"), kLowPass, 0, kNumSubTypes - 1, 1 );
117 Param( Order, int, wxT("Order"), 1, 1, 10, 1 );
118 Param( Cutoff, float, wxT("Cutoff"), 1000.0, 1.0, FLT_MAX, 1 );
119 Param( Passband, float, wxT("PassbandRipple"), 1.0, 0.0, 100.0, 1 );
120 Param( Stopband, float, wxT("StopbandRipple"), 30.0, 0.0, 100.0, 1 );
121 
122 static const double s_fChebyCoeffs[MAX_Order][MAX_Order + 1] =
123 {
124  // For Chebyshev polynomials of the first kind (see http://en.wikipedia.org/wiki/Chebyshev_polynomial)
125  // Coeffs are in the order 0, 1, 2...9
126  { 0, 1}, // order 1
127  {-1, 0, 2}, // order 2 etc.
128  { 0, -3, 0, 4},
129  { 1, 0, -8, 0, 8},
130  { 0, 5, 0, -20, 0, 16},
131  {-1, 0, 18, 0, -48, 0, 32},
132  { 0, -7, 0, 56, 0, -112, 0, 64},
133  { 1, 0, -32, 0, 160, 0, -256, 0, 128},
134  { 0, 9, 0, -120, 0, 432, 0, -576, 0, 256},
135  {-1, 0, 50, 0, -400, 0, 1120, 0, -1280, 0, 512}
136 };
137 
138 //----------------------------------------------------------------------------
139 // EffectScienFilter
140 //----------------------------------------------------------------------------
141 
142 BEGIN_EVENT_TABLE(EffectScienFilter, wxEvtHandler)
143  EVT_SIZE(EffectScienFilter::OnSize)
144 
145  EVT_SLIDER(ID_dBMax, EffectScienFilter::OnSliderDBMAX)
146  EVT_SLIDER(ID_dBMin, EffectScienFilter::OnSliderDBMIN)
147  EVT_CHOICE(ID_Order, EffectScienFilter::OnOrder)
148  EVT_CHOICE(ID_Type, EffectScienFilter::OnFilterType)
149  EVT_CHOICE(ID_SubType, EffectScienFilter::OnFilterSubtype)
150  EVT_TEXT(ID_Cutoff, EffectScienFilter::OnCutoff)
151  EVT_TEXT(ID_Ripple, EffectScienFilter::OnRipple)
152  EVT_TEXT(ID_StopbandRipple, EffectScienFilter::OnStopbandRipple)
154 
156 : mpBiquad{ size_t( MAX_Order / 2 ), true }
157 {
158  mOrder = DEF_Order;
159  mFilterType = DEF_Type;
160  mFilterSubtype = DEF_Subtype;
161  mCutoff = DEF_Cutoff;
162  mRipple = DEF_Passband;
163  mStopbandRipple = DEF_Stopband;
164 
165  SetLinearEffectFlag(true);
166 
167  mOrderIndex = mOrder - 1;
168 
169  mdBMin = -30.0;
170  mdBMax = 30.0;
171 
172  mLoFreq = 20; // Lowest frequency to display in response graph
173  mNyquist = 44100.0 / 2.0; // only used during initialization, updated when effect is used
174 }
175 
177 {
178 }
179 
180 // IdentInterface implementation
181 
183 {
185 }
186 
188 {
189  return _("Performs IIR filtering that emulates analog filters");
190 }
191 
193 {
194  return wxT("Classic_Filters");
195 }
196 
197 
198 // EffectIdentInterface implementation
199 
201 {
202  return EffectTypeProcess;
203 }
204 
205 // EffectClientInterface implementation
206 
208 {
209  return 1;
210 }
211 
213 {
214  return 1;
215 }
216 
217 bool EffectScienFilter::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames WXUNUSED(chanMap))
218 {
219  for (int iPair = 0; iPair < (mOrder + 1) / 2; iPair++)
220  {
221  mpBiquad[iPair].fPrevIn = 0;
222  mpBiquad[iPair].fPrevPrevIn = 0;
223  mpBiquad[iPair].fPrevOut = 0;
224  mpBiquad[iPair].fPrevPrevOut = 0;
225  }
226 
227  return true;
228 }
229 
230 size_t EffectScienFilter::ProcessBlock(float **inBlock, float **outBlock, size_t blockLen)
231 {
232  float *ibuf = inBlock[0];
233  for (int iPair = 0; iPair < (mOrder + 1) / 2; iPair++)
234  {
235  mpBiquad[iPair].pfIn = ibuf;
236  mpBiquad[iPair].pfOut = outBlock[0];
237  Biquad_Process(&mpBiquad[iPair], blockLen);
238  ibuf = outBlock[0];
239  }
240 
241  return blockLen;
242 }
243 
244 bool EffectScienFilter::GetAutomationParameters(EffectAutomationParameters & parms)
245 {
246  parms.Write(KEY_Type, kTypeStrings[mFilterType]);
247  parms.Write(KEY_Subtype, kSubTypeStrings[mFilterSubtype]);
248  parms.Write(KEY_Order, mOrder);
249  parms.WriteFloat(KEY_Cutoff, mCutoff);
250  parms.WriteFloat(KEY_Passband, mRipple);
251  parms.WriteFloat(KEY_Stopband, mStopbandRipple);
252 
253  return true;
254 }
255 
256 bool EffectScienFilter::SetAutomationParameters(EffectAutomationParameters & parms)
257 {
258  ReadAndVerifyEnum(Type, wxArrayString(kNumTypes, kTypeStrings));
259  ReadAndVerifyEnum(Subtype, wxArrayString(kNumSubTypes, kSubTypeStrings));
260  ReadAndVerifyInt(Order);
261  ReadAndVerifyFloat(Cutoff);
262  ReadAndVerifyFloat(Passband);
263  ReadAndVerifyFloat(Stopband);
264 
265  mFilterType = Type;
266  mFilterSubtype = Subtype;
267  mOrder = Order;
268  mCutoff = Cutoff;
269  mRipple = Passband;
270  mStopbandRipple = Stopband;
271 
272  mOrderIndex = mOrder - 1;
273 
274  CalcFilter();
275 
276  return true;
277 }
278 
279 // Effect implementation
280 
282 {
283  wxString base = wxT("/SciFilter/");
284 
285  // Migrate settings from 2.1.0 or before
286 
287  // Already migrated, so bail
288  if (gPrefs->Exists(base + wxT("Migrated")))
289  {
290  return true;
291  }
292 
293  // Load the old "current" settings
294  if (gPrefs->Exists(base))
295  {
296  double dTemp;
297  gPrefs->Read(base + wxT("Order"), &mOrder, 1);
298  mOrder = wxMax (1, mOrder);
299  mOrder = wxMin (MAX_Order, mOrder);
300  gPrefs->Read(base + wxT("FilterType"), &mFilterType, 0);
301  mFilterType = wxMax (0, mFilterType);
302  mFilterType = wxMin (2, mFilterType);
303  gPrefs->Read(base + wxT("FilterSubtype"), &mFilterSubtype, 0);
304  mFilterSubtype = wxMax (0, mFilterSubtype);
305  mFilterSubtype = wxMin (1, mFilterSubtype);
306  gPrefs->Read(base + wxT("Cutoff"), &dTemp, 1000.0);
307  mCutoff = (float)dTemp;
308  mCutoff = wxMax (1, mCutoff);
309  mCutoff = wxMin (100000, mCutoff);
310  gPrefs->Read(base + wxT("Ripple"), &dTemp, 1.0);
311  mRipple = dTemp;
312  mRipple = wxMax (0, mRipple);
313  mRipple = wxMin (100, mRipple);
314  gPrefs->Read(base + wxT("StopbandRipple"), &dTemp, 30.0);
315  mStopbandRipple = dTemp;
316  mStopbandRipple = wxMax (0, mStopbandRipple);
317  mStopbandRipple = wxMin (100, mStopbandRipple);
318 
320 
321  // Do not migrate again
322  gPrefs->Write(base + wxT("Migrated"), true);
323  gPrefs->Flush();
324  }
325 
326  return true;
327 }
328 
330 {
331  int selcount = 0;
332  double rate = 0.0;
333 
335  WaveTrack *t = (WaveTrack *) iter.First();
336 
337  mNyquist = (t ? t->GetRate() : GetActiveProject()->GetRate()) / 2.0;
338 
339  while (t)
340  {
341  if (t->GetSelected())
342  {
343  if (selcount == 0)
344  {
345  rate = t->GetRate();
346  }
347  else
348  {
349  if (t->GetRate() != rate)
350  {
351  Effect::MessageBox(_("To apply a filter, all selected tracks must have the same sample rate."));
352  return false;
353  }
354  }
355  selcount++;
356  }
357  t = (WaveTrack *) iter.Next();
358  }
359 
360  return true;
361 }
362 
364 {
365  wxWindow *const parent = S.GetParent();
366 
367  S.AddSpace(5);
368  S.SetSizerProportion(1);
369  S.StartMultiColumn(3, wxEXPAND);
370  {
371  S.SetStretchyCol(1);
372  S.SetStretchyRow(0);
373 
374  // -------------------------------------------------------------------
375  // ROW 1: Freq response panel and sliders for vertical scale
376  // -------------------------------------------------------------------
377 
378  S.StartVerticalLay();
379  {
380  mdBRuler = safenew RulerPanel(parent, wxID_ANY);
381  mdBRuler->ruler.SetBounds(0, 0, 100, 100); // Ruler can't handle small sizes
382  mdBRuler->ruler.SetOrientation(wxVERTICAL);
383  mdBRuler->ruler.SetRange(30.0, -120.0);
385  mdBRuler->ruler.SetUnits(_("dB"));
387  int w;
388  mdBRuler->ruler.GetMaxSize(&w, NULL);
389  mdBRuler->SetSize(wxSize(w, 150)); // height needed for wxGTK
390 
391  S.SetBorder(1);
392  S.AddSpace(1, 1);
393  S.Prop(1);
394  S.AddWindow(mdBRuler, wxALIGN_RIGHT | wxTOP);
395  S.AddSpace(1, 1);
396  }
397  S.EndVerticalLay();
398 
399  mPanel = safenew EffectScienFilterPanel(this, parent);
401 
402  S.SetBorder(5);
403  S.Prop(1);
404  S.AddWindow(mPanel, wxEXPAND | wxRIGHT);
405  S.SetSizeHints(-1, -1);
406 
407  S.StartVerticalLay();
408  {
409  S.AddVariableText(_("+ dB"), false, wxCENTER);
410  S.SetStyle(wxSL_VERTICAL | wxSL_INVERSE);
411  mdBMaxSlider = S.Id(ID_dBMax).AddSlider( {}, 10, 20, 0);
412 #if wxUSE_ACCESSIBILITY
413  mdBMaxSlider->SetName(_("Max dB"));
414  mdBMaxSlider->SetAccessible(safenew SliderAx(mdBMaxSlider, _("%d dB")));
415 #endif
416 
417  S.SetStyle(wxSL_VERTICAL | wxSL_INVERSE);
418  mdBMinSlider = S.Id(ID_dBMin).AddSlider( {}, -10, -10, -120);
419  S.AddVariableText(_("- dB"), false, wxCENTER);
420 #if wxUSE_ACCESSIBILITY
421  mdBMinSlider->SetName(_("Min dB"));
422  mdBMinSlider->SetAccessible(safenew SliderAx(mdBMinSlider, _("%d dB")));
423 #endif
424  }
425  S.EndVerticalLay();
426 
427  // -------------------------------------------------------------------
428  // ROW 2: Frequency ruler
429  // -------------------------------------------------------------------
430 
431  S.AddSpace(1, 1);
432 
433  mfreqRuler = safenew RulerPanel(parent, wxID_ANY);
434  mfreqRuler->ruler.SetBounds(0, 0, 100, 100); // Ruler can't handle small sizes
435  mfreqRuler->ruler.SetOrientation(wxHORIZONTAL);
436  mfreqRuler->ruler.SetLog(true);
439  mfreqRuler->ruler.SetUnits(wxT(""));
440  mfreqRuler->ruler.SetFlip(true);
442  int h;
443  mfreqRuler->ruler.GetMaxSize(NULL, &h);
444  mfreqRuler->SetMinSize(wxSize(-1, h));
445 
446  S.Prop(1);
447  S.AddWindow(mfreqRuler, wxEXPAND | wxALIGN_LEFT | wxRIGHT);
448 
449  S.AddSpace(1, 1);
450 
451  // -------------------------------------------------------------------
452  // ROW 3 and 4: Type, Order, Ripple, Subtype, Cutoff
453  // -------------------------------------------------------------------
454 
455  S.AddSpace(1, 1);
456  S.SetSizerProportion(0);
457  S.StartMultiColumn(8, wxALIGN_CENTER);
458  {
459  wxASSERT(kNumTypes == WXSIZEOF(kTypeStrings));
460 
461  wxArrayString typeChoices;
462  for (int i = 0; i < kNumTypes; i++)
463  {
464  typeChoices.Add(wxGetTranslation(kTypeStrings[i]));
465  }
466 
467  mFilterTypeCtl = S.Id(ID_Type).AddChoice(_("&Filter Type:"), wxT(""), &typeChoices);
468  mFilterTypeCtl->SetValidator(wxGenericValidator(&mFilterType));
469  S.SetSizeHints(-1, -1);
470 
471  wxArrayString orders;
472  for (int i = 1; i <= 10; i++)
473  {
474  orders.Add(wxString::Format(wxT("%d"), i));
475  }
476  /*i18n-hint: 'Order' means the complexity of the filter, and is a number between 1 and 10.*/
477  mFilterOrderCtl = S.Id(ID_Order).AddChoice(_("O&rder:"), wxT(""), &orders);
478  mFilterOrderCtl->SetValidator(wxGenericValidator(&mOrderIndex));
479  S.SetSizeHints(-1, -1);
480  S.AddSpace(1, 1);
481 
482  FloatingPointValidator<float> vldRipple(1, &mRipple);
483  vldRipple.SetRange(MIN_Passband, MAX_Passband);
484 
485  mRippleCtlP = S.AddVariableText(_("&Passband Ripple:"), false, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
486  mRippleCtl = S.Id(ID_Ripple).AddTextBox( {}, wxT(""), 10);
487  mRippleCtl->SetName(_("Passband Ripple (dB)"));
488  mRippleCtl->SetValidator(vldRipple);
489  mRippleCtlU = S.AddVariableText(_("dB"), false, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
490 
491  wxASSERT(kNumSubTypes == WXSIZEOF(kSubTypeStrings));
492 
493  wxArrayString subTypeChoices;
494  for (int i = 0; i < kNumSubTypes; i++)
495  {
496  subTypeChoices.Add(wxGetTranslation(kSubTypeStrings[i]));
497  }
498 
499  mFilterSubTypeCtl = S.Id(ID_SubType).AddChoice(_("&Subtype:"), wxT(""), &subTypeChoices);
500  mFilterSubTypeCtl->SetValidator(wxGenericValidator(&mFilterSubtype));
501  S.SetSizeHints(-1, -1);
502 
503  FloatingPointValidator<float> vldCutoff(1, &mCutoff);
504  vldCutoff.SetRange(MIN_Cutoff, mNyquist - 1);
505 
506  mCutoffCtl = S.Id(ID_Cutoff).AddTextBox(_("C&utoff:"), wxT(""), 10);
507  mCutoffCtl->SetName(_("Cutoff (Hz)"));
508  mCutoffCtl->SetValidator(vldCutoff);
509  S.AddUnits(_("Hz"));
510 
511  FloatingPointValidator<float> vldStopbandRipple(1, &mStopbandRipple);
512  vldStopbandRipple.SetRange(MIN_Stopband, MAX_Stopband);
513 
514  mStopbandRippleCtlP = S.AddVariableText(_("Minimum S&topband Attenuation:"), false, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
515  mStopbandRippleCtl = S.Id(ID_StopbandRipple).AddTextBox( {}, wxT(""), 10);
516  mStopbandRippleCtl->SetName(_("Minimum S&topband Attenuation (dB)"));
517  mStopbandRippleCtl->SetValidator(vldStopbandRipple);
518  mStopbandRippleCtlU = S.AddVariableText(_("dB"), false, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
519  }
520  S.EndMultiColumn();
521  S.AddSpace(1, 1);
522  }
523  S.EndMultiColumn();
524 
525  mFilterTypeCtl->SetFocus();
526 
527  return;
528 }
529 
530 //
531 // Populate the window with relevant variables
532 //
534 {
535  mOrderIndex = mOrder - 1;
536 
537  if (!mUIParent->TransferDataToWindow())
538  {
539  return false;
540  }
541 
542  mdBMinSlider->SetValue((int) mdBMin);
543  mdBMin = 0.0; // force refresh in TransferGraphLimitsFromWindow()
544 
545  mdBMaxSlider->SetValue((int) mdBMax);
546  mdBMax = 0.0; // force refresh in TransferGraphLimitsFromWindow()
547 
549 
551 }
552 
554 {
555  if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
556  {
557  return false;
558  }
559 
560  mOrder = mOrderIndex + 1;
561 
562  CalcFilter();
563 
564  return true;
565 }
566 
567 // EffectScienFilter implementation
568 
569 //
570 // Retrieve data from the window
571 //
573 {
574  // Read the sliders and send to the panel
575  wxString tip;
576 
577  bool rr = false;
578  int dB = mdBMinSlider->GetValue();
579  if (dB != mdBMin) {
580  rr = true;
581  mdBMin = dB;
582  tip.Printf(_("%d dB"), (int)mdBMin);
583  mdBMinSlider->SetToolTip(tip);
584  }
585 
586  dB = mdBMaxSlider->GetValue();
587  if (dB != mdBMax) {
588  rr = true;
589  mdBMax = dB;
590  tip.Printf(_("%d dB"),(int)mdBMax);
591  mdBMaxSlider->SetToolTip(tip);
592  }
593 
594  if (rr) {
596  }
597 
598  // Refresh ruler if values have changed
599  if (rr) {
600  int w1, w2, h;
601  mdBRuler->ruler.GetMaxSize(&w1, &h);
603  mdBRuler->ruler.GetMaxSize(&w2, &h);
604  if( w1 != w2 ) // Reduces flicker
605  {
606  mdBRuler->SetSize(wxSize(w2,h));
607  mUIParent->Layout();
608  mfreqRuler->Refresh(false);
609  }
610  mdBRuler->Refresh(false);
611  }
612 
613  mPanel->Refresh(false);
614 
615  return true;
616 }
617 
619 {
620  // Set up the coefficients in all the biquads
621  float fNorm = mCutoff / mNyquist;
622  if (fNorm >= 0.9999)
623  fNorm = 0.9999F;
624  float fC = tan (PI * fNorm / 2);
625  float fDCPoleDistSqr = 1.0F;
626  float fZPoleX, fZPoleY;
627  float fZZeroX, fZZeroY;
628  float beta = cos (fNorm*PI);
629  switch (mFilterType)
630  {
631  case kButterworth: // Butterworth
632  if ((mOrder & 1) == 0)
633  {
634  // Even order
635  for (int iPair = 0; iPair < mOrder/2; iPair++)
636  {
637  float fSPoleX = fC * cos (PI - (iPair + 0.5) * PI / mOrder);
638  float fSPoleY = fC * sin (PI - (iPair + 0.5) * PI / mOrder);
639  BilinTransform (fSPoleX, fSPoleY, &fZPoleX, &fZPoleY);
640  mpBiquad[iPair].fNumerCoeffs [0] = 1;
641  if (mFilterSubtype == kLowPass) // LOWPASS
642  mpBiquad[iPair].fNumerCoeffs [1] = 2;
643  else
644  mpBiquad[iPair].fNumerCoeffs [1] = -2;
645  mpBiquad[iPair].fNumerCoeffs [2] = 1;
646  mpBiquad[iPair].fDenomCoeffs [0] = -2 * fZPoleX;
647  mpBiquad[iPair].fDenomCoeffs [1] = square(fZPoleX) + square(fZPoleY);
648  if (mFilterSubtype == kLowPass) // LOWPASS
649  fDCPoleDistSqr *= Calc2D_DistSqr (1, 0, fZPoleX, fZPoleY);
650  else
651  fDCPoleDistSqr *= Calc2D_DistSqr (-1, 0, fZPoleX, fZPoleY); // distance from Nyquist
652  }
653  }
654  else
655  {
656  // Odd order - first do the 1st-order section
657  float fSPoleX = -fC;
658  float fSPoleY = 0;
659  BilinTransform (fSPoleX, fSPoleY, &fZPoleX, &fZPoleY);
660  mpBiquad[0].fNumerCoeffs [0] = 1;
661  if (mFilterSubtype == kLowPass) // LOWPASS
662  mpBiquad[0].fNumerCoeffs [1] = 1;
663  else
664  mpBiquad[0].fNumerCoeffs [1] = -1;
665  mpBiquad[0].fNumerCoeffs [2] = 0;
666  mpBiquad[0].fDenomCoeffs [0] = -fZPoleX;
667  mpBiquad[0].fDenomCoeffs [1] = 0;
668  if (mFilterSubtype == kLowPass) // LOWPASS
669  fDCPoleDistSqr = 1 - fZPoleX;
670  else
671  fDCPoleDistSqr = fZPoleX + 1; // dist from Nyquist
672  for (int iPair = 1; iPair <= mOrder/2; iPair++)
673  {
674  float fSPoleX = fC * cos (PI - iPair * PI / mOrder);
675  float fSPoleY = fC * sin (PI - iPair * PI / mOrder);
676  BilinTransform (fSPoleX, fSPoleY, &fZPoleX, &fZPoleY);
677  mpBiquad[iPair].fNumerCoeffs [0] = 1;
678  if (mFilterSubtype == kLowPass) // LOWPASS
679  mpBiquad[iPair].fNumerCoeffs [1] = 2;
680  else
681  mpBiquad[iPair].fNumerCoeffs [1] = -2;
682  mpBiquad[iPair].fNumerCoeffs [2] = 1;
683  mpBiquad[iPair].fDenomCoeffs [0] = -2 * fZPoleX;
684  mpBiquad[iPair].fDenomCoeffs [1] = square(fZPoleX) + square(fZPoleY);
685  if (mFilterSubtype == kLowPass) // LOWPASS
686  fDCPoleDistSqr *= Calc2D_DistSqr (1, 0, fZPoleX, fZPoleY);
687  else
688  fDCPoleDistSqr *= Calc2D_DistSqr (-1, 0, fZPoleX, fZPoleY); // distance from Nyquist
689  }
690  }
691  mpBiquad[0].fNumerCoeffs [0] *= fDCPoleDistSqr / (1 << mOrder); // mult by DC dist from poles, divide by dist from zeroes
692  mpBiquad[0].fNumerCoeffs [1] *= fDCPoleDistSqr / (1 << mOrder);
693  mpBiquad[0].fNumerCoeffs [2] *= fDCPoleDistSqr / (1 << mOrder);
694  break;
695 
696  case kChebyshevTypeI: // Chebyshev Type 1
697  double eps; eps = sqrt (pow (10.0, wxMax(0.001, mRipple) / 10.0) - 1);
698  double a; a = log (1 / eps + sqrt(1 / square(eps) + 1)) / mOrder;
699  // Assume even order to start
700  for (int iPair = 0; iPair < mOrder/2; iPair++)
701  {
702  float fSPoleX = -fC * sinh (a) * sin ((2*iPair + 1) * PI / (2 * mOrder));
703  float fSPoleY = fC * cosh (a) * cos ((2*iPair + 1) * PI / (2 * mOrder));
704  BilinTransform (fSPoleX, fSPoleY, &fZPoleX, &fZPoleY);
705  if (mFilterSubtype == kLowPass) // LOWPASS
706  {
707  fZZeroX = -1;
708  fDCPoleDistSqr = Calc2D_DistSqr (1, 0, fZPoleX, fZPoleY);
709  fDCPoleDistSqr /= 2*2; // dist from zero at Nyquist
710  }
711  else
712  {
713  // Highpass - do the digital LP->HP transform on the poles and zeroes
714  ComplexDiv (beta - fZPoleX, -fZPoleY, 1 - beta * fZPoleX, -beta * fZPoleY, &fZPoleX, &fZPoleY);
715  fZZeroX = 1;
716  fDCPoleDistSqr = Calc2D_DistSqr (-1, 0, fZPoleX, fZPoleY); // distance from Nyquist
717  fDCPoleDistSqr /= 2*2; // dist from zero at Nyquist
718  }
719  mpBiquad[iPair].fNumerCoeffs [0] = fDCPoleDistSqr;
720  mpBiquad[iPair].fNumerCoeffs [1] = -2 * fZZeroX * fDCPoleDistSqr;
721  mpBiquad[iPair].fNumerCoeffs [2] = fDCPoleDistSqr;
722  mpBiquad[iPair].fDenomCoeffs [0] = -2 * fZPoleX;
723  mpBiquad[iPair].fDenomCoeffs [1] = square(fZPoleX) + square(fZPoleY);
724  }
725  if ((mOrder & 1) == 0)
726  {
727  float fTemp = DB_TO_LINEAR(-wxMax(0.001, mRipple)); // at DC the response is down R dB (for even-order)
728  mpBiquad[0].fNumerCoeffs [0] *= fTemp;
729  mpBiquad[0].fNumerCoeffs [1] *= fTemp;
730  mpBiquad[0].fNumerCoeffs [2] *= fTemp;
731  }
732  else
733  {
734  // Odd order - now do the 1st-order section
735  float fSPoleX = -fC * sinh (a);
736  float fSPoleY = 0;
737  BilinTransform (fSPoleX, fSPoleY, &fZPoleX, &fZPoleY);
738  if (mFilterSubtype == kLowPass) // LOWPASS
739  {
740  fZZeroX = -1;
741  fDCPoleDistSqr = sqrt(Calc2D_DistSqr (1, 0, fZPoleX, fZPoleY));
742  fDCPoleDistSqr /= 2; // dist from zero at Nyquist
743  }
744  else
745  {
746  // Highpass - do the digital LP->HP transform on the poles and zeroes
747  ComplexDiv (beta - fZPoleX, -fZPoleY, 1 - beta * fZPoleX, -beta * fZPoleY, &fZPoleX, &fZPoleY);
748  fZZeroX = 1;
749  fDCPoleDistSqr = sqrt(Calc2D_DistSqr (-1, 0, fZPoleX, fZPoleY)); // distance from Nyquist
750  fDCPoleDistSqr /= 2; // dist from zero at Nyquist
751  }
752  mpBiquad[(mOrder-1)/2].fNumerCoeffs [0] = fDCPoleDistSqr;
753  mpBiquad[(mOrder-1)/2].fNumerCoeffs [1] = -fZZeroX * fDCPoleDistSqr;
754  mpBiquad[(mOrder-1)/2].fNumerCoeffs [2] = 0;
755  mpBiquad[(mOrder-1)/2].fDenomCoeffs [0] = -fZPoleX;
756  mpBiquad[(mOrder-1)/2].fDenomCoeffs [1] = 0;
757  }
758  break;
759 
760  case kChebyshevTypeII: // Chebyshev Type 2
761  float fSZeroX, fSZeroY;
762  float fSPoleX, fSPoleY;
763  eps = DB_TO_LINEAR(-wxMax(0.001, mStopbandRipple));
764  a = log (1 / eps + sqrt(1 / square(eps) + 1)) / mOrder;
765 
766  // Assume even order
767  for (int iPair = 0; iPair < mOrder/2; iPair++)
768  {
769  ComplexDiv (fC, 0, -sinh (a) * sin ((2*iPair + 1) * PI / (2 * mOrder)),
770  cosh (a) * cos ((2*iPair + 1) * PI / (2 * mOrder)),
771  &fSPoleX, &fSPoleY);
772  BilinTransform (fSPoleX, fSPoleY, &fZPoleX, &fZPoleY);
773  fSZeroX = 0;
774  fSZeroY = fC / cos (((2 * iPair) + 1) * PI / (2 * mOrder));
775  BilinTransform (fSZeroX, fSZeroY, &fZZeroX, &fZZeroY);
776 
777  if (mFilterSubtype == kLowPass) // LOWPASS
778  {
779  fDCPoleDistSqr = Calc2D_DistSqr (1, 0, fZPoleX, fZPoleY);
780  fDCPoleDistSqr /= Calc2D_DistSqr (1, 0, fZZeroX, fZZeroY);
781  }
782  else
783  {
784  // Highpass - do the digital LP->HP transform on the poles and zeroes
785  ComplexDiv (beta - fZPoleX, -fZPoleY, 1 - beta * fZPoleX, -beta * fZPoleY, &fZPoleX, &fZPoleY);
786  ComplexDiv (beta - fZZeroX, -fZZeroY, 1 - beta * fZZeroX, -beta * fZZeroY, &fZZeroX, &fZZeroY);
787  fDCPoleDistSqr = Calc2D_DistSqr (-1, 0, fZPoleX, fZPoleY); // distance from Nyquist
788  fDCPoleDistSqr /= Calc2D_DistSqr (-1, 0, fZZeroX, fZZeroY);
789  }
790  mpBiquad[iPair].fNumerCoeffs [0] = fDCPoleDistSqr;
791  mpBiquad[iPair].fNumerCoeffs [1] = -2 * fZZeroX * fDCPoleDistSqr;
792  mpBiquad[iPair].fNumerCoeffs [2] = (square(fZZeroX) + square(fZZeroY)) * fDCPoleDistSqr;
793  mpBiquad[iPair].fDenomCoeffs [0] = -2 * fZPoleX;
794  mpBiquad[iPair].fDenomCoeffs [1] = square(fZPoleX) + square(fZPoleY);
795  }
796  // Now, if it's odd order, we have one more to do
797  if (mOrder & 1)
798  {
799  int iPair = (mOrder-1)/2; // we'll do it as a biquad, but it's just first-order
800  ComplexDiv (fC, 0, -sinh (a) * sin ((2*iPair + 1) * PI / (2 * mOrder)),
801  cosh (a) * cos ((2*iPair + 1) * PI / (2 * mOrder)),
802  &fSPoleX, &fSPoleY);
803  BilinTransform (fSPoleX, fSPoleY, &fZPoleX, &fZPoleY);
804  fZZeroX = -1; // in the s-plane, the zero is at infinity
805  fZZeroY = 0;
806  if (mFilterSubtype == kLowPass) // LOWPASS
807  {
808  fDCPoleDistSqr = sqrt(Calc2D_DistSqr (1, 0, fZPoleX, fZPoleY));
809  fDCPoleDistSqr /= 2;
810  }
811  else
812  {
813  // Highpass - do the digital LP->HP transform on the poles and zeroes
814  ComplexDiv (beta - fZPoleX, -fZPoleY, 1 - beta * fZPoleX, -fZPoleY, &fZPoleX, &fZPoleY);
815  fZZeroX = 1;
816  fDCPoleDistSqr = sqrt(Calc2D_DistSqr (-1, 0, fZPoleX, fZPoleY)); // distance from Nyquist
817  fDCPoleDistSqr /= 2;
818  }
819  mpBiquad[iPair].fNumerCoeffs [0] = fDCPoleDistSqr;
820  mpBiquad[iPair].fNumerCoeffs [1] = -fZZeroX * fDCPoleDistSqr;
821  mpBiquad[iPair].fNumerCoeffs [2] = 0;
822  mpBiquad[iPair].fDenomCoeffs [0] = -fZPoleX;
823  mpBiquad[iPair].fDenomCoeffs [1] = 0;
824  }
825  break;
826  }
827  return true;
828 }
829 
830 double EffectScienFilter::ChebyPoly(int Order, double NormFreq) // NormFreq = 1 at the f0 point (where response is R dB down)
831 {
832  // Calc cosh (Order * acosh (NormFreq));
833  double x = 1;
834  double fSum = 0;
835  wxASSERT (Order >= MIN_Order && Order <= MAX_Order);
836  for (int i = 0; i <= Order; i++)
837  {
838  fSum += s_fChebyCoeffs [Order-1][i] * x;
839  x *= NormFreq;
840  }
841  return fSum;
842 }
843 
845 {
846  float Magn;
847  if (Freq >= mNyquist)
848  Freq = mNyquist - 1; // prevent tan(PI/2)
849  float FreqWarped = tan (PI * Freq/(2*mNyquist));
850  if (mCutoff >= mNyquist)
851  mCutoff = mNyquist - 1;
852  float CutoffWarped = tan (PI * mCutoff/(2*mNyquist));
853  float fOverflowThresh = pow (10.0, 12.0 / (2*mOrder)); // once we exceed 10^12 there's not much to be gained and overflow could happen
854 
855  switch (mFilterType)
856  {
857  case kButterworth: // Butterworth
858  default:
859  switch (mFilterSubtype)
860  {
861  case kLowPass: // lowpass
862  default:
863  if (FreqWarped/CutoffWarped > fOverflowThresh) // prevent pow() overflow
864  Magn = 0;
865  else
866  Magn = sqrt (1 / (1 + pow (FreqWarped/CutoffWarped, 2*mOrder)));
867  break;
868  case kHighPass: // highpass
869  if (FreqWarped/CutoffWarped > fOverflowThresh)
870  Magn = 1;
871  else
872  Magn = sqrt (pow (FreqWarped/CutoffWarped, 2*mOrder) / (1 + pow (FreqWarped/CutoffWarped, 2*mOrder)));
873  break;
874  }
875  break;
876 
877  case kChebyshevTypeI: // Chebyshev Type 1
878  double eps; eps = sqrt(pow (10.0, wxMax(0.001, mRipple)/10.0) - 1);
879  switch (mFilterSubtype)
880  {
881  case 0: // lowpass
882  default:
883  Magn = sqrt (1 / (1 + square(eps) * square(ChebyPoly(mOrder, FreqWarped/CutoffWarped))));
884  break;
885  case 1:
886  Magn = sqrt (1 / (1 + square(eps) * square(ChebyPoly(mOrder, CutoffWarped/FreqWarped))));
887  break;
888  }
889  break;
890 
891  case kChebyshevTypeII: // Chebyshev Type 2
892  eps = 1 / sqrt(pow (10.0, wxMax(0.001, mStopbandRipple)/10.0) - 1);
893  switch (mFilterSubtype)
894  {
895  case kLowPass: // lowpass
896  default:
897  Magn = sqrt (1 / (1 + 1 / (square(eps) * square(ChebyPoly(mOrder, CutoffWarped/FreqWarped)))));
898  break;
899  case kHighPass:
900  Magn = sqrt (1 / (1 + 1 / (square(eps) * square(ChebyPoly(mOrder, FreqWarped/CutoffWarped)))));
901  break;
902  }
903  break;
904  }
905 
906  return Magn;
907 }
908 
909 void EffectScienFilter::OnOrder(wxCommandEvent & WXUNUSED(evt))
910 {
911  mOrderIndex = mFilterOrderCtl->GetSelection();
912  mOrder = mOrderIndex + 1; // 0..n-1 -> 1..n
913  mPanel->Refresh(false);
914 }
915 
916 void EffectScienFilter::OnFilterType(wxCommandEvent & WXUNUSED(evt))
917 {
918  mFilterType = mFilterTypeCtl->GetSelection();
920  mPanel->Refresh(false);
921 }
922 
923 void EffectScienFilter::OnFilterSubtype(wxCommandEvent & WXUNUSED(evt))
924 {
925  mFilterSubtype = mFilterSubTypeCtl->GetSelection();
926  mPanel->Refresh(false);
927 }
928 
929 void EffectScienFilter::OnCutoff(wxCommandEvent & WXUNUSED(evt))
930 {
931  if (!EnableApply(mUIParent->TransferDataFromWindow()))
932  {
933  return;
934  }
935 
936  mPanel->Refresh(false);
937 }
938 
939 void EffectScienFilter::OnRipple(wxCommandEvent & WXUNUSED(evt))
940 {
941  if (!EnableApply(mUIParent->TransferDataFromWindow()))
942  {
943  return;
944  }
945 
946  mPanel->Refresh(false);
947 }
948 
949 void EffectScienFilter::OnStopbandRipple(wxCommandEvent & WXUNUSED(evt))
950 {
951  if (!EnableApply(mUIParent->TransferDataFromWindow()))
952  {
953  return;
954  }
955 
956  mPanel->Refresh(false);
957 }
958 
959 void EffectScienFilter::OnSliderDBMIN(wxCommandEvent & WXUNUSED(evt))
960 {
962 }
963 
964 void EffectScienFilter::OnSliderDBMAX(wxCommandEvent & WXUNUSED(evt))
965 {
967 }
968 
969 void EffectScienFilter::OnSize(wxSizeEvent & evt)
970 {
971  // On Windows the Passband and Stopband boxes do not refresh properly
972  // on a resize...no idea why.
973  mUIParent->Refresh();
974  evt.Skip();
975 }
976 
978 {
979  bool ripple;
980  bool stop;
981 
982  if (FilterType == kButterworth) // Butterworth
983  {
984  ripple = false;
985  stop = false;
986  }
987  else if (FilterType == kChebyshevTypeI) // Chebyshev Type1
988  {
989  ripple = true;
990  stop = false;
991  }
992  else // Chebyshev Type2
993  {
994  ripple = false;
995  stop = true;
996  }
997 
998  mRippleCtlP->Enable(ripple);
999  mRippleCtl->Enable(ripple);
1000  mRippleCtlU->Enable(ripple);
1001  mStopbandRippleCtlP->Enable(stop);
1002  mStopbandRippleCtl->Enable(stop);
1003  mStopbandRippleCtlU->Enable(stop);
1004 }
1005 
1006 //----------------------------------------------------------------------------
1007 // EffectScienFilterPanel
1008 //----------------------------------------------------------------------------
1009 
1010 BEGIN_EVENT_TABLE(EffectScienFilterPanel, wxPanelWrapper)
1011  EVT_PAINT(EffectScienFilterPanel::OnPaint)
1012  EVT_SIZE(EffectScienFilterPanel::OnSize)
1014 
1016 : wxPanelWrapper(parent, wxID_ANY, wxDefaultPosition, wxSize(400, 200))
1017 {
1018  mEffect = effect;
1019  mParent = parent;
1020 
1021  mBitmap = NULL;
1022  mWidth = 0;
1023  mHeight = 0;
1024  mLoFreq = 0.0;
1025  mHiFreq = 0.0;
1026  mDbMin = 0.0;
1027  mDbMax = 0.0;
1028 }
1029 
1031 {
1032 }
1033 
1034 void EffectScienFilterPanel::SetFreqRange(double lo, double hi)
1035 {
1036  mLoFreq = lo;
1037  mHiFreq = hi;
1038  Refresh(false);
1039 }
1040 
1041 void EffectScienFilterPanel::SetDbRange(double min, double max)
1042 {
1043  mDbMin = min;
1044  mDbMax = max;
1045  Refresh(false);
1046 }
1047 
1049 {
1050  return false;
1051 }
1052 
1054 {
1055  return false;
1056 }
1057 
1058 void EffectScienFilterPanel::OnSize(wxSizeEvent & WXUNUSED(evt))
1059 {
1060  Refresh(false);
1061 }
1062 
1063 void EffectScienFilterPanel::OnPaint(wxPaintEvent & WXUNUSED(evt))
1064 {
1065  wxPaintDC dc(this);
1066  int width, height;
1067  GetSize(&width, &height);
1068 
1069  if (!mBitmap || mWidth != width || mHeight != height)
1070  {
1071  mWidth = width;
1072  mHeight = height;
1073  mBitmap = std::make_unique<wxBitmap>(mWidth, mHeight);
1074  }
1075 
1076  wxBrush bkgndBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
1077 
1078  wxMemoryDC memDC;
1079  memDC.SelectObject(*mBitmap);
1080 
1081  wxRect bkgndRect;
1082  bkgndRect.x = 0;
1083  bkgndRect.y = 0;
1084  bkgndRect.width = mWidth;
1085  bkgndRect.height = mHeight;
1086  memDC.SetBrush(bkgndBrush);
1087  memDC.SetPen(*wxTRANSPARENT_PEN);
1088  memDC.DrawRectangle(bkgndRect);
1089 
1090  bkgndRect.y = mHeight;
1091  memDC.DrawRectangle(bkgndRect);
1092 
1093  wxRect border;
1094  border.x = 0;
1095  border.y = 0;
1096  border.width = mWidth;
1097  border.height = mHeight;
1098 
1099  memDC.SetBrush(*wxWHITE_BRUSH);
1100  memDC.SetPen(*wxBLACK_PEN);
1101  memDC.DrawRectangle(border);
1102 
1103  mEnvRect = border;
1104  mEnvRect.Deflate(2, 2);
1105 
1106  // Pure blue x-axis line
1107  memDC.SetPen(wxPen(theTheme.Colour(clrGraphLines), 1, wxSOLID));
1108  int center = (int) (mEnvRect.height * mDbMax / (mDbMax - mDbMin) + 0.5);
1109  AColor::Line(memDC,
1110  mEnvRect.GetLeft(), mEnvRect.y + center,
1111  mEnvRect.GetRight(), mEnvRect.y + center);
1112 
1113  //Now draw the actual response that you will get.
1114  //mFilterFunc has a linear scale, window has a log one so we have to fiddle about
1115  memDC.SetPen(wxPen(theTheme.Colour(clrResponseLines), 3, wxSOLID));
1116  double scale = (double) mEnvRect.height / (mDbMax - mDbMin); // pixels per dB
1117  double yF; // gain at this freq
1118 
1119  double loLog = log10(mLoFreq);
1120  double step = log10(mHiFreq) - loLog;
1121  step /= ((double) mEnvRect.width - 1.0);
1122  double freq; // actual freq corresponding to x position
1123  int x, y, xlast = 0, ylast = 0;
1124  for (int i = 0; i < mEnvRect.width; i++)
1125  {
1126  x = mEnvRect.x + i;
1127  freq = pow(10.0, loLog + i * step); //Hz
1128  yF = mEffect->FilterMagnAtFreq (freq);
1129  yF = LINEAR_TO_DB(yF);
1130 
1131  if (yF < mDbMin)
1132  {
1133  yF = mDbMin;
1134  }
1135 
1136  yF = center-scale * yF;
1137  if (yF > mEnvRect.height)
1138  {
1139  yF = (double) mEnvRect.height - 1.0;
1140  }
1141  if (yF < 0.0)
1142  {
1143  yF = 0.0;
1144  }
1145  y = (int) (yF + 0.5);
1146 
1147  if (i != 0 && (y < mEnvRect.height - 1 || ylast < mEnvRect.y + mEnvRect.height - 1))
1148  {
1149  AColor::Line(memDC, xlast, ylast, x, mEnvRect.y + y);
1150  }
1151  xlast = x;
1152  ylast = mEnvRect.y + y;
1153  }
1154 
1155  memDC.SetPen(*wxBLACK_PEN);
1156  mEffect->mfreqRuler->ruler.DrawGrid(memDC, mEnvRect.height + 2, true, true, 0, 1);
1157  mEffect->mdBRuler->ruler.DrawGrid(memDC, mEnvRect.width + 2, true, true, 1, 2);
1158 
1159  dc.Blit(0, 0, mWidth, mHeight, &memDC, 0, 0, wxCOPY, FALSE);
1160 
1161  memDC.SelectObject(wxNullBitmap);
1162 }
bool Init() override
#define PI
Definition: ScienFilter.cpp:62
wxChoice * mFilterTypeCtl
Definition: ScienFilter.h:127
void SetLog(bool log)
Definition: Ruler.cpp:197
EffectScienFilterPanel is used with EffectScienFilter and controls a graph for EffectScienFilter.
Definition: ScienFilter.h:139
bool TransferGraphLimitsFromWindow()
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:215
bool SaveUserPreset(const wxString &name) override
Definition: Effect.cpp:615
void SetFreqRange(double lo, double hi)
int MessageBox(const wxString &message, long style=DefaultMessageBoxStyle, const wxString &titleStr=wxString{})
Definition: Effect.cpp:2655
bool BilinTransform(float fSX, float fSY, float *pfZX, float *pfZY)
Definition: Biquad.cpp:39
wxStaticText * mRippleCtlP
Definition: ScienFilter.h:117
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:366
std::unique_ptr< wxBitmap > mBitmap
Definition: ScienFilter.h:167
wxString GetCurrentSettingsGroup() override
Definition: Effect.cpp:814
wxString GetSymbol() override
bool GetSelected() const
Definition: Track.h:217
wxWindow * AddWindow(wxWindow *pWindow, int Flags=wxALIGN_CENTRE|wxALL)
Definition: ShuttleGui.cpp:257
#define ReadAndVerifyFloat(name)
Definition: Effect.h:788
void OnSliderDBMIN(wxCommandEvent &evt)
wxSlider * mdBMinSlider
Definition: ScienFilter.h:114
friend class EffectScienFilterPanel
Definition: ScienFilter.h:136
wxTextCtrl * mStopbandRippleCtl
Definition: ScienFilter.h:124
RulerPanel * mfreqRuler
Definition: ScienFilter.h:132
bool GetAutomationParameters(EffectAutomationParameters &parms) override
#define square(a)
Definition: ScienFilter.cpp:66
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()
void OnFilterType(wxCommandEvent &evt)
void OnFilterSubtype(wxCommandEvent &evt)
An Effect that applies 'classical' IIR filters.
Definition: ScienFilter.h:38
bool TransferDataFromWindow() override
ArrayOf< BiquadStruct > mpBiquad
Definition: ScienFilter.h:104
#define XO(s)
Definition: Internat.h:30
#define ReadAndVerifyEnum(name, list)
Definition: Effect.h:781
void OnSize(wxSizeEvent &evt)
wxChoice * mFilterSubTypeCtl
Definition: ScienFilter.h:128
wxTextCtrl * mCutoffCtl
Definition: ScienFilter.h:121
void OnRipple(wxCommandEvent &evt)
float FilterMagnAtFreq(float Freq)
kTypes
Definition: Noise.cpp:30
RulerPanel class allows you to work with a Ruler like any other wxWindow.
Definition: Ruler.h:244
double ChebyPoly(int Order, double NormFreq)
void SetSizerProportion(int iProp)
Definition: ShuttleGui.h:254
void EnableDisableRippleCtl(int FilterType)
TrackList * inputTracks() const
Definition: Effect.h:457
#define safenew
Definition: Audacity.h:223
wxChoice * mFilterOrderCtl
Definition: ScienFilter.h:129
wxStaticText * mRippleCtlU
Definition: ScienFilter.h:119
void SetOrientation(int orient)
Definition: Ruler.cpp:220
wxString ManualPage() override
virtual ~EffectScienFilterPanel()
EffectScienFilterPanel * mPanel
Definition: ScienFilter.h:113
bool AcceptsFocus() const
Track * Next(bool skiplinked=false) override
Definition: Track.cpp:513
void OnCutoff(wxCommandEvent &evt)
void Biquad_Process(BiquadStruct *pBQ, int iNumSamples)
Definition: Biquad.cpp:5
unsigned GetAudioOutCount() override
void AddUnits(const wxString &Prompt)
Left aligned text string.
Definition: ShuttleGui.cpp:229
void EndVerticalLay()
Definition: ShuttleGui.cpp:991
void SetSizeHints(int minX=-1, int minY=-1)
wxFileConfig * gPrefs
Definition: Prefs.cpp:72
wxTextCtrl * AddTextBox(const wxString &Caption, const wxString &Value, const int nChars)
Definition: ShuttleGui.cpp:493
#define ReadAndVerifyInt(name)
Definition: Effect.h:786
void SetLabelEdges(bool labelEdges)
Definition: Ruler.cpp:272
static const double s_fChebyCoeffs[MAX_Order][MAX_Order+1]
void OnSize(wxSizeEvent &evt)
RulerPanel * mdBRuler
Definition: ScienFilter.h:131
wxWindow * GetParent()
Definition: ShuttleGui.h:259
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
Definition: ShuttleGui.cpp:998
wxChoice * AddChoice(const wxString &Prompt, const wxString &Selected, const wxArrayString *pChoices)
Definition: ShuttleGui.cpp:331
#define CLASSICFILTERS_PLUGIN_SYMBOL
Definition: ScienFilter.h:34
void OnPaint(wxPaintEvent &evt)
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
ShuttleGui & Id(int id)
void SetFlip(bool flip)
Definition: Ruler.cpp:285
float Calc2D_DistSqr(float fX1, float fY1, float fX2, float fY2)
Definition: Biquad.cpp:47
void SetStyle(int Style)
Definition: ShuttleGui.h:252
bool Startup() override
void OnSliderDBMAX(wxCommandEvent &evt)
void SetUnits(const wxString &units)
Definition: Ruler.cpp:208
int min(int a, int b)
kSubTypes
Definition: ScienFilter.cpp:99
EffectType GetType() override
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:122
wxTextCtrl * mRippleCtl
Definition: ScienFilter.h:118
wxString GetDescription() override
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 &)
static const wxChar * kSubTypeStrings[]
void DrawGrid(wxDC &dc, int length, bool minor=true, bool major=true, int xOffset=0, int yOffset=0)
Definition: Ruler.cpp:1449
#define LINEAR_TO_DB(x)
Definition: Audacity.h:210
void SetFormat(RulerFormat format)
Definition: Ruler.cpp:186
void SetRange(double min, double max)
Definition: Ruler.cpp:234
size_t ProcessBlock(float **inBlock, float **outBlock, size_t blockLen) override
Param(Type, int, wxT("FilterType"), kButterworth, 0, kNumTypes-1, 1)
bool SetAutomationParameters(EffectAutomationParameters &parms) override
virtual ~EffectScienFilter()
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:302
wxStaticText * AddVariableText(const wxString &Str, bool bCenter=false, int PositionFlags=0)
Definition: ShuttleGui.cpp:373
void SetDbRange(double min, double max)
bool AcceptsFocusFromKeyboard() const
void PopulateOrExchange(ShuttleGui &S) override
Track * First(TrackList *val=NULL) override
Definition: Track.cpp:502
EffectScienFilter * mEffect
Definition: ScienFilter.h:158
ShuttleGui & Prop(int iProp)
Definition: ShuttleGui.h:374
void OnOrder(wxCommandEvent &evt)
bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap=NULL) override
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1214
double GetRate() const
Definition: Project.h:184
END_EVENT_TABLE()
wxSizerItem * AddSpace(int width, int height)
double GetRate() const
Definition: WaveTrack.cpp:397
static const wxChar * kTypeStrings[]
Definition: ScienFilter.cpp:89
void SetBorder(int Border)
Definition: ShuttleGui.h:251
const double MIN_Threshold_Linear DB_TO_LINEAR(MIN_Threshold_dB)
bool TransferDataToWindow() override
void OnStopbandRipple(wxCommandEvent &evt)
wxSlider * mdBMaxSlider
Definition: ScienFilter.h:115
wxStaticText * mStopbandRippleCtlP
Definition: ScienFilter.h:123
wxStaticText * mStopbandRippleCtlU
Definition: ScienFilter.h:125
Ruler ruler
Definition: Ruler.h:270
void SetStretchyCol(int i)
Used to modify an already placed FlexGridSizer to make a column stretchy.
Definition: ShuttleGui.cpp:192
virtual bool EnableApply(bool enable=true)
Definition: Effect.cpp:1879
void ComplexDiv(float fNumerR, float fNumerI, float fDenomR, float fDenomI, float *pfQuotientR, float *pfQuotientI)
Definition: Biquad.cpp:32
void SetStretchyRow(int i)
Used to modify an already placed FlexGridSizer to make a row stretchy.
Definition: ShuttleGui.cpp:202
wxSlider * AddSlider(const wxString &Prompt, int pos, int Max, int Min=0)
Definition: ShuttleGui.cpp:456
unsigned GetAudioInCount() override
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:982