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 
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  { wxT("ChebyshevTypeI"), XO("Chebyshev Type I") },
95  /*i18n-hint: Chebyshev is the name of the person after whom the filter type is named.*/
96  { wxT("ChebyshevTypeII"), XO("Chebyshev Type II") }
97 };
98 
100 {
104 };
105 
107 {
108  // These are acceptable dual purpose internal/visible names
109  { XO("Lowpass") },
110  { XO("Highpass") }
111 };
112 
113 // Define keys, defaults, minimums, and maximums for the effect parameters
114 //
115 // Name Type Key Def Min Max Scale
116 Param( Type, int, wxT("FilterType"), kButterworth, 0, nTypes - 1, 1 );
117 Param( Subtype, int, wxT("FilterSubtype"), kLowPass, 0, nSubTypes - 1, 1 );
118 Param( Order, int, wxT("Order"), 1, 1, 10, 1 );
119 Param( Cutoff, float, wxT("Cutoff"), 1000.0, 1.0, FLT_MAX, 1 );
120 Param( Passband, float, wxT("PassbandRipple"), 1.0, 0.0, 100.0, 1 );
121 Param( Stopband, float, wxT("StopbandRipple"), 30.0, 0.0, 100.0, 1 );
122 
123 static const double s_fChebyCoeffs[MAX_Order][MAX_Order + 1] =
124 {
125  // For Chebyshev polynomials of the first kind (see http://en.wikipedia.org/wiki/Chebyshev_polynomial)
126  // Coeffs are in the order 0, 1, 2...9
127  { 0, 1}, // order 1
128  {-1, 0, 2}, // order 2 etc.
129  { 0, -3, 0, 4},
130  { 1, 0, -8, 0, 8},
131  { 0, 5, 0, -20, 0, 16},
132  {-1, 0, 18, 0, -48, 0, 32},
133  { 0, -7, 0, 56, 0, -112, 0, 64},
134  { 1, 0, -32, 0, 160, 0, -256, 0, 128},
135  { 0, 9, 0, -120, 0, 432, 0, -576, 0, 256},
136  {-1, 0, 50, 0, -400, 0, 1120, 0, -1280, 0, 512}
137 };
138 
139 //----------------------------------------------------------------------------
140 // EffectScienFilter
141 //----------------------------------------------------------------------------
142 
143 BEGIN_EVENT_TABLE(EffectScienFilter, wxEvtHandler)
144  EVT_SIZE(EffectScienFilter::OnSize)
145 
146  EVT_SLIDER(ID_dBMax, EffectScienFilter::OnSliderDBMAX)
147  EVT_SLIDER(ID_dBMin, EffectScienFilter::OnSliderDBMIN)
148  EVT_CHOICE(ID_Order, EffectScienFilter::OnOrder)
149  EVT_CHOICE(ID_Type, EffectScienFilter::OnFilterType)
150  EVT_CHOICE(ID_SubType, EffectScienFilter::OnFilterSubtype)
151  EVT_TEXT(ID_Cutoff, EffectScienFilter::OnCutoff)
152  EVT_TEXT(ID_Ripple, EffectScienFilter::OnRipple)
153  EVT_TEXT(ID_StopbandRipple, EffectScienFilter::OnStopbandRipple)
155 
157 : mpBiquad{ size_t( MAX_Order / 2 ), true }
158 {
159  mOrder = DEF_Order;
160  mFilterType = DEF_Type;
161  mFilterSubtype = DEF_Subtype;
162  mCutoff = DEF_Cutoff;
163  mRipple = DEF_Passband;
164  mStopbandRipple = DEF_Stopband;
165 
166  SetLinearEffectFlag(true);
167 
168  mOrderIndex = mOrder - 1;
169 
170  mdBMin = -30.0;
171  mdBMax = 30.0;
172 
173  mLoFreq = 20; // Lowest frequency to display in response graph
174  mNyquist = 44100.0 / 2.0; // only used during initialization, updated when effect is used
175 }
176 
178 {
179 }
180 
181 // IdentInterface implementation
182 
184 {
186 }
187 
189 {
190  return _("Performs IIR filtering that emulates analog filters");
191 }
192 
194 {
195  return wxT("Classic_Filters");
196 }
197 
198 
199 // EffectDefinitionInterface implementation
200 
202 {
203  return EffectTypeProcess;
204 }
205 
206 // EffectClientInterface implementation
207 
209 {
210  return 1;
211 }
212 
214 {
215  return 1;
216 }
217 
218 bool EffectScienFilter::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames WXUNUSED(chanMap))
219 {
220  for (int iPair = 0; iPair < (mOrder + 1) / 2; iPair++)
221  {
222  mpBiquad[iPair].fPrevIn = 0;
223  mpBiquad[iPair].fPrevPrevIn = 0;
224  mpBiquad[iPair].fPrevOut = 0;
225  mpBiquad[iPair].fPrevPrevOut = 0;
226  }
227 
228  return true;
229 }
230 
231 size_t EffectScienFilter::ProcessBlock(float **inBlock, float **outBlock, size_t blockLen)
232 {
233  float *ibuf = inBlock[0];
234  for (int iPair = 0; iPair < (mOrder + 1) / 2; iPair++)
235  {
236  mpBiquad[iPair].pfIn = ibuf;
237  mpBiquad[iPair].pfOut = outBlock[0];
238  Biquad_Process(&mpBiquad[iPair], blockLen);
239  ibuf = outBlock[0];
240  }
241 
242  return blockLen;
243 }
245  S.SHUTTLE_ENUM_PARAM( mFilterType, Type, kTypeStrings, nTypes );
246  S.SHUTTLE_ENUM_PARAM( mFilterSubtype, Subtype, kSubTypeStrings, nSubTypes );
247  S.SHUTTLE_PARAM( mOrder, Order );
248  S.SHUTTLE_PARAM( mCutoff, Cutoff );
249  S.SHUTTLE_PARAM( mRipple, Passband );
250  S.SHUTTLE_PARAM( mStopbandRipple, Stopband );
251  return true;
252 }
253 
255 {
256  parms.Write(KEY_Type, kTypeStrings[mFilterType].Internal());
257  parms.Write(KEY_Subtype, kSubTypeStrings[mFilterSubtype].Internal());
258  parms.Write(KEY_Order, mOrder);
259  parms.WriteFloat(KEY_Cutoff, mCutoff);
260  parms.WriteFloat(KEY_Passband, mRipple);
261  parms.WriteFloat(KEY_Stopband, mStopbandRipple);
262 
263  return true;
264 }
265 
267 {
268  ReadAndVerifyEnum(Type, kTypeStrings, nTypes);
269  ReadAndVerifyEnum(Subtype, kSubTypeStrings, nSubTypes);
270  ReadAndVerifyInt(Order);
271  ReadAndVerifyFloat(Cutoff);
272  ReadAndVerifyFloat(Passband);
273  ReadAndVerifyFloat(Stopband);
274 
275  mFilterType = Type;
276  mFilterSubtype = Subtype;
277  mOrder = Order;
278  mCutoff = Cutoff;
279  mRipple = Passband;
280  mStopbandRipple = Stopband;
281 
282  mOrderIndex = mOrder - 1;
283 
284  CalcFilter();
285 
286  return true;
287 }
288 
289 // Effect implementation
290 
292 {
293  wxString base = wxT("/SciFilter/");
294 
295  // Migrate settings from 2.1.0 or before
296 
297  // Already migrated, so bail
298  if (gPrefs->Exists(base + wxT("Migrated")))
299  {
300  return true;
301  }
302 
303  // Load the old "current" settings
304  if (gPrefs->Exists(base))
305  {
306  double dTemp;
307  gPrefs->Read(base + wxT("Order"), &mOrder, 1);
308  mOrder = wxMax (1, mOrder);
309  mOrder = wxMin (MAX_Order, mOrder);
310  gPrefs->Read(base + wxT("FilterType"), &mFilterType, 0);
311  mFilterType = wxMax (0, mFilterType);
312  mFilterType = wxMin (2, mFilterType);
313  gPrefs->Read(base + wxT("FilterSubtype"), &mFilterSubtype, 0);
314  mFilterSubtype = wxMax (0, mFilterSubtype);
315  mFilterSubtype = wxMin (1, mFilterSubtype);
316  gPrefs->Read(base + wxT("Cutoff"), &dTemp, 1000.0);
317  mCutoff = (float)dTemp;
318  mCutoff = wxMax (1, mCutoff);
319  mCutoff = wxMin (100000, mCutoff);
320  gPrefs->Read(base + wxT("Ripple"), &dTemp, 1.0);
321  mRipple = dTemp;
322  mRipple = wxMax (0, mRipple);
323  mRipple = wxMin (100, mRipple);
324  gPrefs->Read(base + wxT("StopbandRipple"), &dTemp, 30.0);
325  mStopbandRipple = dTemp;
326  mStopbandRipple = wxMax (0, mStopbandRipple);
327  mStopbandRipple = wxMin (100, mStopbandRipple);
328 
330 
331  // Do not migrate again
332  gPrefs->Write(base + wxT("Migrated"), true);
333  gPrefs->Flush();
334  }
335 
336  return true;
337 }
338 
340 {
341  int selcount = 0;
342  double rate = 0.0;
343 
345  WaveTrack *t = (WaveTrack *) iter.First();
346 
347  mNyquist = (t ? t->GetRate() : GetActiveProject()->GetRate()) / 2.0;
348 
349  while (t)
350  {
351  if (t->GetSelected())
352  {
353  if (selcount == 0)
354  {
355  rate = t->GetRate();
356  }
357  else
358  {
359  if (t->GetRate() != rate)
360  {
361  Effect::MessageBox(_("To apply a filter, all selected tracks must have the same sample rate."));
362  return false;
363  }
364  }
365  selcount++;
366  }
367  t = (WaveTrack *) iter.Next();
368  }
369 
370  return true;
371 }
372 
374 {
375  wxWindow *const parent = S.GetParent();
376 
377  S.AddSpace(5);
378  S.SetSizerProportion(1);
379  S.StartMultiColumn(3, wxEXPAND);
380  {
381  S.SetStretchyCol(1);
382  S.SetStretchyRow(0);
383 
384  // -------------------------------------------------------------------
385  // ROW 1: Freq response panel and sliders for vertical scale
386  // -------------------------------------------------------------------
387 
388  S.StartVerticalLay();
389  {
391  parent, wxID_ANY, wxVERTICAL,
392  wxSize{ 100, 100 }, // Ruler can't handle small sizes
393  RulerPanel::Range{ 30.0, -120.0 },
395  _("dB"),
397  .LabelEdges(true)
398  );
399 
400  S.SetBorder(1);
401  S.AddSpace(1, 1);
402  S.Prop(1);
403  S.AddWindow(mdBRuler, wxALIGN_RIGHT | wxTOP);
404  S.AddSpace(1, 1);
405  }
406  S.EndVerticalLay();
407 
409  parent, wxID_ANY,
410  this, mLoFreq, mNyquist
411  );
412 
413  S.SetBorder(5);
414  S.Prop(1);
415  S.AddWindow(mPanel, wxEXPAND | wxRIGHT);
416  S.SetSizeHints(-1, -1);
417 
418  S.StartVerticalLay();
419  {
420  S.AddVariableText(_("+ dB"), false, wxCENTER);
421  S.SetStyle(wxSL_VERTICAL | wxSL_INVERSE);
422  mdBMaxSlider = S.Id(ID_dBMax).AddSlider( {}, 10, 20, 0);
423 #if wxUSE_ACCESSIBILITY
424  mdBMaxSlider->SetName(_("Max dB"));
425  mdBMaxSlider->SetAccessible(safenew SliderAx(mdBMaxSlider, _("%d dB")));
426 #endif
427 
428  S.SetStyle(wxSL_VERTICAL | wxSL_INVERSE);
429  mdBMinSlider = S.Id(ID_dBMin).AddSlider( {}, -10, -10, -120);
430  S.AddVariableText(_("- dB"), false, wxCENTER);
431 #if wxUSE_ACCESSIBILITY
432  mdBMinSlider->SetName(_("Min dB"));
433  mdBMinSlider->SetAccessible(safenew SliderAx(mdBMinSlider, _("%d dB")));
434 #endif
435  }
436  S.EndVerticalLay();
437 
438  // -------------------------------------------------------------------
439  // ROW 2: Frequency ruler
440  // -------------------------------------------------------------------
441 
442  S.AddSpace(1, 1);
443 
445  parent, wxID_ANY, wxHORIZONTAL,
446  wxSize{ 100, 100 }, // Ruler can't handle small sizes
449  wxT(""),
451  .Log(true)
452  .Flip(true)
453  .LabelEdges(true)
454  );
455 
456  S.Prop(1);
457  S.AddWindow(mfreqRuler, wxEXPAND | wxALIGN_LEFT | wxRIGHT);
458 
459  S.AddSpace(1, 1);
460 
461  // -------------------------------------------------------------------
462  // ROW 3 and 4: Type, Order, Ripple, Subtype, Cutoff
463  // -------------------------------------------------------------------
464 
465  S.AddSpace(1, 1);
466  S.SetSizerProportion(0);
467  S.StartMultiColumn(8, wxALIGN_CENTER);
468  {
469  wxASSERT(nTypes == WXSIZEOF(kTypeStrings));
470 
471  auto typeChoices = LocalizedStrings(kTypeStrings, nTypes);
472  mFilterTypeCtl = S.Id(ID_Type).AddChoice(_("&Filter Type:"), wxT(""), &typeChoices);
473  mFilterTypeCtl->SetValidator(wxGenericValidator(&mFilterType));
474  S.SetSizeHints(-1, -1);
475 
476  wxArrayString orders;
477  for (int i = 1; i <= 10; i++)
478  {
479  orders.Add(wxString::Format(wxT("%d"), i));
480  }
481  /*i18n-hint: 'Order' means the complexity of the filter, and is a number between 1 and 10.*/
482  mFilterOrderCtl = S.Id(ID_Order).AddChoice(_("O&rder:"), wxT(""), &orders);
483  mFilterOrderCtl->SetValidator(wxGenericValidator(&mOrderIndex));
484  S.SetSizeHints(-1, -1);
485  S.AddSpace(1, 1);
486 
487  FloatingPointValidator<float> vldRipple(1, &mRipple);
488  vldRipple.SetRange(MIN_Passband, MAX_Passband);
489 
490  mRippleCtlP = S.AddVariableText(_("&Passband Ripple:"), false, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
491  mRippleCtl = S.Id(ID_Ripple).AddTextBox( {}, wxT(""), 10);
492  mRippleCtl->SetName(_("Passband Ripple (dB)"));
493  mRippleCtl->SetValidator(vldRipple);
494  mRippleCtlU = S.AddVariableText(_("dB"), false, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
495 
496  wxASSERT(nSubTypes == WXSIZEOF(kSubTypeStrings));
497 
498  auto subTypeChoices = LocalizedStrings(kSubTypeStrings, nSubTypes);
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  wxWindow *parent, wxWindowID winid,
1017  EffectScienFilter *effect, double lo, double hi)
1018 : wxPanelWrapper(parent, winid, wxDefaultPosition, wxSize(400, 200))
1019 {
1020  mEffect = effect;
1021  mParent = parent;
1022 
1023  mBitmap = NULL;
1024  mWidth = 0;
1025  mHeight = 0;
1026  mLoFreq = 0.0;
1027  mHiFreq = 0.0;
1028  mDbMin = 0.0;
1029  mDbMax = 0.0;
1030 
1031  SetFreqRange(lo, hi);
1032 }
1033 
1035 {
1036 }
1037 
1038 void EffectScienFilterPanel::SetFreqRange(double lo, double hi)
1039 {
1040  mLoFreq = lo;
1041  mHiFreq = hi;
1042  Refresh(false);
1043 }
1044 
1045 void EffectScienFilterPanel::SetDbRange(double min, double max)
1046 {
1047  mDbMin = min;
1048  mDbMax = max;
1049  Refresh(false);
1050 }
1051 
1053 {
1054  return false;
1055 }
1056 
1058 {
1059  return false;
1060 }
1061 
1062 void EffectScienFilterPanel::OnSize(wxSizeEvent & WXUNUSED(evt))
1063 {
1064  Refresh(false);
1065 }
1066 
1067 void EffectScienFilterPanel::OnPaint(wxPaintEvent & WXUNUSED(evt))
1068 {
1069  wxPaintDC dc(this);
1070  int width, height;
1071  GetSize(&width, &height);
1072 
1073  if (!mBitmap || mWidth != width || mHeight != height)
1074  {
1075  mWidth = width;
1076  mHeight = height;
1077  mBitmap = std::make_unique<wxBitmap>(mWidth, mHeight);
1078  }
1079 
1080  wxBrush bkgndBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
1081 
1082  wxMemoryDC memDC;
1083  memDC.SelectObject(*mBitmap);
1084 
1085  wxRect bkgndRect;
1086  bkgndRect.x = 0;
1087  bkgndRect.y = 0;
1088  bkgndRect.width = mWidth;
1089  bkgndRect.height = mHeight;
1090  memDC.SetBrush(bkgndBrush);
1091  memDC.SetPen(*wxTRANSPARENT_PEN);
1092  memDC.DrawRectangle(bkgndRect);
1093 
1094  bkgndRect.y = mHeight;
1095  memDC.DrawRectangle(bkgndRect);
1096 
1097  wxRect border;
1098  border.x = 0;
1099  border.y = 0;
1100  border.width = mWidth;
1101  border.height = mHeight;
1102 
1103  memDC.SetBrush(*wxWHITE_BRUSH);
1104  memDC.SetPen(*wxBLACK_PEN);
1105  memDC.DrawRectangle(border);
1106 
1107  mEnvRect = border;
1108  mEnvRect.Deflate(2, 2);
1109 
1110  // Pure blue x-axis line
1111  memDC.SetPen(wxPen(theTheme.Colour(clrGraphLines), 1, wxSOLID));
1112  int center = (int) (mEnvRect.height * mDbMax / (mDbMax - mDbMin) + 0.5);
1113  AColor::Line(memDC,
1114  mEnvRect.GetLeft(), mEnvRect.y + center,
1115  mEnvRect.GetRight(), mEnvRect.y + center);
1116 
1117  //Now draw the actual response that you will get.
1118  //mFilterFunc has a linear scale, window has a log one so we have to fiddle about
1119  memDC.SetPen(wxPen(theTheme.Colour(clrResponseLines), 3, wxSOLID));
1120  double scale = (double) mEnvRect.height / (mDbMax - mDbMin); // pixels per dB
1121  double yF; // gain at this freq
1122 
1123  double loLog = log10(mLoFreq);
1124  double step = log10(mHiFreq) - loLog;
1125  step /= ((double) mEnvRect.width - 1.0);
1126  double freq; // actual freq corresponding to x position
1127  int x, y, xlast = 0, ylast = 0;
1128  for (int i = 0; i < mEnvRect.width; i++)
1129  {
1130  x = mEnvRect.x + i;
1131  freq = pow(10.0, loLog + i * step); //Hz
1132  yF = mEffect->FilterMagnAtFreq (freq);
1133  yF = LINEAR_TO_DB(yF);
1134 
1135  if (yF < mDbMin)
1136  {
1137  yF = mDbMin;
1138  }
1139 
1140  yF = center-scale * yF;
1141  if (yF > mEnvRect.height)
1142  {
1143  yF = (double) mEnvRect.height - 1.0;
1144  }
1145  if (yF < 0.0)
1146  {
1147  yF = 0.0;
1148  }
1149  y = (int) (yF + 0.5);
1150 
1151  if (i != 0 && (y < mEnvRect.height - 1 || ylast < mEnvRect.y + mEnvRect.height - 1))
1152  {
1153  AColor::Line(memDC, xlast, ylast, x, mEnvRect.y + y);
1154  }
1155  xlast = x;
1156  ylast = mEnvRect.y + y;
1157  }
1158 
1159  memDC.SetPen(*wxBLACK_PEN);
1160  mEffect->mfreqRuler->ruler.DrawGrid(memDC, mEnvRect.height + 2, true, true, 0, 1);
1161  mEffect->mdBRuler->ruler.DrawGrid(memDC, mEnvRect.width + 2, true, true, 1, 2);
1162 
1163  dc.Blit(0, 0, mWidth, mHeight, &memDC, 0, 0, wxCOPY, FALSE);
1164 
1165  memDC.SelectObject(wxNullBitmap);
1166 }
bool Init() override
#define PI
Definition: ScienFilter.cpp:62
wxChoice * mFilterTypeCtl
Definition: ScienFilter.h:128
void SetSizeHints(int minX, int minY)
Used to modify an already placed Window.
Definition: ShuttleGui.cpp:194
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
EffectScienFilterPanel is used with EffectScienFilter and controls a graph for EffectScienFilter.
Definition: ScienFilter.h:140
bool TransferGraphLimitsFromWindow()
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:209
bool SaveUserPreset(const wxString &name) override
Definition: Effect.cpp:600
void SetFreqRange(double lo, double hi)
int MessageBox(const wxString &message, long style=DefaultMessageBoxStyle, const wxString &titleStr=wxString{})
Definition: Effect.cpp:2660
bool BilinTransform(float fSX, float fSY, float *pfZX, float *pfZY)
Definition: Biquad.cpp:39
wxStaticText * mRippleCtlP
Definition: ScienFilter.h:118
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:409
Param(Type, int, wxT("FilterType"), kButterworth, 0, nTypes-1, 1)
bool SetAutomationParameters(CommandParameters &parms) override
std::unique_ptr< wxBitmap > mBitmap
Definition: ScienFilter.h:170
wxString GetCurrentSettingsGroup() override
Definition: Effect.cpp:801
bool GetSelected() const
Definition: Track.h:275
wxWindow * AddWindow(wxWindow *pWindow, int Flags=wxALIGN_CENTRE|wxALL)
Definition: ShuttleGui.cpp:288
#define ReadAndVerifyFloat(name)
Definition: Effect.h:799
void OnSliderDBMIN(wxCommandEvent &evt)
bool DefineParams(ShuttleParams &S) override
static const IdentInterfaceSymbol kSubTypeStrings[nSubTypes]
wxArrayString LocalizedStrings(const IdentInterfaceSymbol strings[], size_t nStrings)
Definition: Internat.cpp:303
wxSlider * mdBMinSlider
Definition: ScienFilter.h:115
friend class EffectScienFilterPanel
Definition: ScienFilter.h:137
wxTextCtrl * mStopbandRippleCtl
Definition: ScienFilter.h:125
RulerPanel * mfreqRuler
Definition: ScienFilter.h:133
#define square(a)
Definition: ScienFilter.cpp:66
Options & LabelEdges(bool l)
Definition: Ruler.h:268
void GetMaxSize(wxCoord *width, wxCoord *height)
Definition: Ruler.cpp:1549
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:105
#define XO(s)
Definition: Internat.h:33
void OnSize(wxSizeEvent &evt)
wxChoice * mFilterSubTypeCtl
Definition: ScienFilter.h:129
wxTextCtrl * mCutoffCtl
Definition: ScienFilter.h:122
Shuttle that deals with parameters. This is a base class with lots of virtual functions that do nothi...
Definition: Shuttle.h:60
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:246
double ChebyPoly(int Order, double NormFreq)
void SetSizerProportion(int iProp)
Definition: ShuttleGui.h:289
void EnableDisableRippleCtl(int FilterType)
TrackList * inputTracks() const
Definition: Effect.h:458
#define safenew
Definition: Audacity.h:230
wxChoice * mFilterOrderCtl
Definition: ScienFilter.h:130
wxStaticText * mRippleCtlU
Definition: ScienFilter.h:120
wxString ManualPage() override
virtual ~EffectScienFilterPanel()
EffectScienFilterPanel * mPanel
Definition: ScienFilter.h:114
bool AcceptsFocus() const
Track * Next(bool skiplinked=false) override
Definition: Track.cpp:569
IdentInterfaceSymbol GetSymbol() override
bool WriteFloat(const wxString &key, float f)
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:260
void EndVerticalLay()
Options & Flip(bool f)
Definition: Ruler.h:265
wxTextCtrl * AddTextBox(const wxString &Caption, const wxString &Value, const int nChars)
Definition: ShuttleGui.cpp:540
#define ReadAndVerifyEnum(name, list, listSize)
Definition: Effect.h:786
#define ReadAndVerifyInt(name)
Definition: Effect.h:797
static const double s_fChebyCoeffs[MAX_Order][MAX_Order+1]
void OnSize(wxSizeEvent &evt)
RulerPanel * mdBRuler
Definition: ScienFilter.h:132
wxWindow * GetParent()
Definition: ShuttleGui.h:294
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
wxChoice * AddChoice(const wxString &Prompt, const wxString &Selected, const wxArrayString *pChoices)
Definition: ShuttleGui.cpp:371
#define CLASSICFILTERS_PLUGIN_SYMBOL
Definition: ScienFilter.h:34
CommandParameters, derived from wxFileConfig, is essentially doing the same things as the Shuttle cla...
void OnPaint(wxPaintEvent &evt)
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
ShuttleGui & Id(int id)
float Calc2D_DistSqr(float fX1, float fY1, float fX2, float fY2)
Definition: Biquad.cpp:47
void SetStyle(int Style)
Definition: ShuttleGui.h:287
bool Startup() override
Options & Log(bool l)
Definition: Ruler.h:262
void OnSliderDBMAX(wxCommandEvent &evt)
int min(int a, int b)
kSubTypes
Definition: ScienFilter.cpp:99
IdentInterfaceSymbol pairs a persistent string identifier used internally with an optional...
bool GetAutomationParameters(CommandParameters &parms) override
EffectType GetType() override
enum ChannelName * ChannelNames
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:122
wxTextCtrl * mRippleCtl
Definition: ScienFilter.h:119
wxString GetDescription() override
wxWindow * mUIParent
Definition: Effect.h:472
_("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
void DrawGrid(wxDC &dc, int length, bool minor=true, bool major=true, int xOffset=0, int yOffset=0)
Definition: Ruler.cpp:1465
#define LINEAR_TO_DB(x)
Definition: Audacity.h:217
void SetRange(double min, double max)
Definition: Ruler.cpp:236
EffectType
size_t ProcessBlock(float **inBlock, float **outBlock, size_t blockLen) override
virtual ~EffectScienFilter()
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:308
wxStaticText * AddVariableText(const wxString &Str, bool bCenter=false, int PositionFlags=0)
Definition: ShuttleGui.cpp:414
void SetDbRange(double min, double max)
bool AcceptsFocusFromKeyboard() const
void PopulateOrExchange(ShuttleGui &S) override
static const IdentInterfaceSymbol kTypeStrings[nTypes]
Definition: ScienFilter.cpp:89
Track * First(TrackList *val=NULL) override
Definition: Track.cpp:558
std::pair< double, double > Range
Definition: Ruler.h:250
EffectScienFilter * mEffect
Definition: ScienFilter.h:161
ShuttleGui & Prop(int iProp)
Definition: ShuttleGui.h:418
void OnOrder(wxCommandEvent &evt)
bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap=NULL) override
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1225
double GetRate() const
Definition: Project.h:199
END_EVENT_TABLE()
wxSizerItem * AddSpace(int width, int height)
double GetRate() const
Definition: WaveTrack.cpp:398
void SetBorder(int Border)
Definition: ShuttleGui.h:286
const double MIN_Threshold_Linear DB_TO_LINEAR(MIN_Threshold_dB)
bool TransferDataToWindow() override
void OnStopbandRipple(wxCommandEvent &evt)
wxSlider * mdBMaxSlider
Definition: ScienFilter.h:116
wxStaticText * mStopbandRippleCtlP
Definition: ScienFilter.h:124
wxStaticText * mStopbandRippleCtlU
Definition: ScienFilter.h:126
Ruler ruler
Definition: Ruler.h:306
void SetStretchyCol(int i)
Used to modify an already placed FlexGridSizer to make a column stretchy.
Definition: ShuttleGui.cpp:203
virtual bool EnableApply(bool enable=true)
Definition: Effect.cpp:1886
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:213
wxSlider * AddSlider(const wxString &Prompt, int pos, int Max, int Min=0)
Definition: ShuttleGui.cpp:497
unsigned GetAudioInCount() override
void StartVerticalLay(int iProp=1)