Audacity  3.0.3
Equalization.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  EffectEqualization.cpp
6 
7  Mitch Golden
8  Vaughan Johnson (Preview)
9  Martyn Shaw (FIR filters, response curve, graphic EQ)
10 
11 *******************************************************************//****************************************************************//****************************************************************//****************************************************************//****************************************************************//*******************************************************************/
53 
54 
55 
56 #include "Equalization.h"
57 #include "LoadEffects.h"
58 
59 #include <math.h>
60 #include <vector>
61 
62 #include <wx/setup.h> // for wxUSE_* macros
63 
64 #include <wx/bitmap.h>
65 #include <wx/button.h>
66 #include <wx/brush.h>
67 #include <wx/button.h> // not really needed here
68 #include <wx/dcclient.h>
69 #include <wx/dcmemory.h>
70 #include <wx/event.h>
71 #include <wx/listctrl.h>
72 #include <wx/image.h>
73 #include <wx/intl.h>
74 #include <wx/choice.h>
75 #include <wx/radiobut.h>
76 #include <wx/slider.h>
77 #include <wx/stattext.h>
78 #include <wx/string.h>
79 #include <wx/textdlg.h>
80 #include <wx/ffile.h>
81 #include <wx/filefn.h>
82 #include <wx/stdpaths.h>
83 #include <wx/settings.h>
84 #include <wx/sizer.h>
85 #include <wx/checkbox.h>
86 #include <wx/tooltip.h>
87 #include <wx/utils.h>
88 
89 #include "../AColor.h"
90 #include "../Shuttle.h"
91 #include "../ShuttleGui.h"
92 #include "../PlatformCompatibility.h"
93 #include "../FileNames.h"
94 #include "../Envelope.h"
95 #include "../EnvelopeEditor.h"
96 #include "../widgets/ErrorDialog.h"
97 #include "../FFT.h"
98 #include "../Prefs.h"
99 #include "../Project.h"
100 #include "../Theme.h"
101 #include "../TrackArtist.h"
102 #include "../WaveClip.h"
103 #include "../ViewInfo.h"
104 #include "../WaveTrack.h"
105 #include "../widgets/Ruler.h"
106 #include "../xml/XMLFileReader.h"
107 #include "../AllThemeResources.h"
108 #include "../float_cast.h"
109 
110 #if wxUSE_ACCESSIBILITY
111 #include "../widgets/WindowAccessible.h"
112 #endif
113 
114 #include "../widgets/FileDialog/FileDialog.h"
115 
116 #ifdef EXPERIMENTAL_EQ_SSE_THREADED
117 #include "Equalization48x.h"
118 #endif
119 
120 
121 enum
122 {
123  ID_Length = 10000,
137 #ifdef EXPERIMENTAL_EQ_SSE_THREADED
138  ID_DefaultMath,
139  ID_SSE,
140  ID_SSEThreaded,
141  ID_AVX,
142  ID_AVXThreaded,
143  ID_Bench,
144 #endif
145  ID_Slider, // needs to come last
146 };
147 
149 {
154 };
155 
156 // Increment whenever EQCurves.xml is updated
157 #define EQCURVES_VERSION 1
158 #define EQCURVES_REVISION 0
159 #define UPDATE_ALL 0 // 0 = merge NEW presets only, 1 = Update all factory presets.
160 
162 {
163  // These are acceptable dual purpose internal/visible names
164 
165  /* i18n-hint: Technical term for a kind of curve.*/
166  { XO("B-spline") },
167  { XO("Cosine") },
168  { XO("Cubic") }
169 };
170 
171 static const double kThirdOct[] =
172 {
173  20., 25., 31., 40., 50., 63., 80., 100., 125., 160., 200.,
174  250., 315., 400., 500., 630., 800., 1000., 1250., 1600., 2000.,
175  2500., 3150., 4000., 5000., 6300., 8000., 10000., 12500., 16000., 20000.,
176 };
177 
178 // Define keys, defaults, minimums, and maximums for the effect parameters
179 //
180 // Name Type Key Def Min Max Scale
181 Param( FilterLength, int, wxT("FilterLength"), 8191, 21, 8191, 0 );
182 Param( CurveName, wxChar*, wxT("CurveName"), wxT("unnamed"), wxT(""), wxT(""), wxT(""));
183 Param( InterpLin, bool, wxT("InterpolateLin"), false, false, true, false );
184 Param( InterpMeth, int, wxT("InterpolationMethod"), 0, 0, 0, 0 );
185 Param( DrawMode, bool, wxT(""), true, false, true, false );
186 Param( DrawGrid, bool, wxT(""), true, false, true, false );
187 Param( dBMin, float, wxT(""), -30.0, -120.0, -10.0, 0 );
188 Param( dBMax, float, wxT(""), 30.0, 0.0, 60.0, 0 );
189 
191 // EffectEqualization
192 //----------------------------------------------------------------------------
193 
195 { XO("Equalization") };
196 
197 // namespace{ BuiltinEffectsModule::Registration< EffectEqualization > reg; }
198 
199 // "Filter Curve EQ" in the user-facing string, but preserve the old
200 // internal string
202 { wxT("Filter Curve"), XO("Filter Curve EQ") };
203 
205 
207 { wxT("Graphic EQ"), XO("Graphic EQ") };
208 
210 
211 BEGIN_EVENT_TABLE(EffectEqualization, wxEvtHandler)
212  EVT_SIZE( EffectEqualization::OnSize )
213 
219  wxEVT_COMMAND_SLIDER_UPDATED,
220  EffectEqualization::OnSlider)
221  EVT_CHOICE( ID_Interp, EffectEqualization::OnInterp )
222 
223  EVT_CHOICE( ID_Curve, EffectEqualization::OnCurve )
227 
228  EVT_RADIOBUTTON(ID_Draw, EffectEqualization::OnDrawMode)
229  EVT_RADIOBUTTON(ID_Graphic, EffectEqualization::OnGraphicMode)
230  EVT_CHECKBOX(ID_Linear, EffectEqualization::OnLinFreq)
231  EVT_CHECKBOX(ID_Grid, EffectEqualization::OnGridOnOff)
232 
233 #ifdef EXPERIMENTAL_EQ_SSE_THREADED
234  EVT_RADIOBUTTON(ID_DefaultMath, EffectEqualization::OnProcessingRadio)
235  EVT_RADIOBUTTON(ID_SSE, EffectEqualization::OnProcessingRadio)
236  EVT_RADIOBUTTON(ID_SSEThreaded, EffectEqualization::OnProcessingRadio)
237  EVT_RADIOBUTTON(ID_AVX, EffectEqualization::OnProcessingRadio)
238  EVT_RADIOBUTTON(ID_AVXThreaded, EffectEqualization::OnProcessingRadio)
239  EVT_BUTTON(ID_Bench, EffectEqualization::OnBench)
240 #endif
242 
244  : mFFTBuffer{ windowSize }
245  , mFilterFuncR{ windowSize }
246  , mFilterFuncI{ windowSize }
247 {
248  mOptions = Options;
249  mGraphic = NULL;
250  mDraw = NULL;
251  mCurve = NULL;
252  mPanel = NULL;
253  mMSlider = NULL;
254 
255  hFFT = GetFFT(windowSize);
256 
257  SetLinearEffectFlag(true);
258 
259  mM = DEF_FilterLength;
260  mLin = DEF_InterpLin;
261  mInterp = DEF_InterpMeth;
262  mCurveName = DEF_CurveName;
263 
264  GetPrivateConfig(GetCurrentSettingsGroup(), wxT("dBMin"), mdBMin, DEF_dBMin);
265  GetPrivateConfig(GetCurrentSettingsGroup(), wxT("dBMax"), mdBMax, DEF_dBMax);
266  GetPrivateConfig(GetCurrentSettingsGroup(), wxT("DrawMode"), mDrawMode, DEF_DrawMode);
267  GetPrivateConfig(GetCurrentSettingsGroup(), wxT("DrawGrid"), mDrawGrid, DEF_DrawGrid);
268 
269  mLogEnvelope = std::make_unique<Envelope>
270  (false,
271  MIN_dBMin, MAX_dBMax, // MB: this is the highest possible range
272  0.0);
273  mLogEnvelope->SetTrackLen(1.0);
274 
275  mLinEnvelope = std::make_unique<Envelope>
276  (false,
277  MIN_dBMin, MAX_dBMax, // MB: this is the highest possible range
278  0.0);
279  mLinEnvelope->SetTrackLen(1.0);
280 
281  mEnvelope = (mLin ? mLinEnvelope : mLogEnvelope).get();
282 
283  mWindowSize = windowSize;
284 
285  mDirty = false;
286  mDisallowCustom = false;
287 
288  // Load the EQ curves
289  LoadCurves();
290 
291  // Note: initial curve is set in TransferDataToWindow
292 
293  mBandsInUse = NUMBER_OF_BANDS;
294  //double loLog = log10(mLoFreq);
295  //double stepLog = (log10(mHiFreq) - loLog)/((double)NUM_PTS-1.);
296  for(int i=0; i<NUM_PTS-1; i++)
297  mWhens[i] = (double)i/(NUM_PTS-1.);
298  mWhens[NUM_PTS-1] = 1.;
299  mWhenSliders[NUMBER_OF_BANDS] = 1.;
300  mEQVals[NUMBER_OF_BANDS] = 0.;
301 
302 #ifdef EXPERIMENTAL_EQ_SSE_THREADED
303  bool useSSE;
304  GetPrivateConfig(GetCurrentSettingsGroup(), wxT("/SSE/GUI"), useSSE, false);
305  if(useSSE && !mEffectEqualization48x)
306  mEffectEqualization48x = std::make_unique<EffectEqualization48x>();
307  else if(!useSSE)
308  mEffectEqualization48x.reset();
309  mBench=false;
310 #endif
311 
312  // We expect these Hi and Lo frequencies to be overridden by Init().
313  // Don't use inputTracks(). See bug 2321.
314 #if 0
315  auto trackList = inputTracks();
316  const auto t = trackList
317  ? *trackList->Any< const WaveTrack >().first
318  : nullptr
319  ;
320  mHiFreq =
321  (t
322  ? t->GetRate()
323  : mProjectRate)
324  / 2.0;
325 #endif
326  mHiFreq = mProjectRate / 2.0;
327  mLoFreq = loFreqI;
328 }
329 
330 
332 {
333 }
334 
335 // ComponentInterface implementation
336 
338 {
339  if( mOptions == kEqOptionGraphic )
341  if( mOptions == kEqOptionCurve )
344 }
345 
347 {
348  return XO("Adjusts the volume levels of particular frequencies");
349 }
350 
352 {
353  // Bug 2509: Must use _ and not space in names.
354  if( mOptions == kEqOptionGraphic )
355  return wxT("Graphic_EQ");
356  if( mOptions == kEqOptionCurve )
357  return wxT("Filter_Curve_EQ");
358  return wxT("Equalization");
359 }
360 
361 // EffectDefinitionInterface implementation
362 
364 {
365  return EffectTypeProcess;
366 }
367 
368 // EffectClientInterface implementation
370  S.SHUTTLE_PARAM( mM, FilterLength );
371  //S.SHUTTLE_PARAM( mCurveName, CurveName);
372  S.SHUTTLE_PARAM( mLin, InterpLin);
373  S.SHUTTLE_ENUM_PARAM( mInterp, InterpMeth, kInterpStrings, nInterpolations );
374 
375  // if saving the preferences...
376  if( dynamic_cast<ShuttleGetAutomation*>(&S))
377  {
378  int numPoints = mCurves[ 0 ].points.size();
379  int point;
380  for( point = 0; point < numPoints; point++ )
381  {
382  const wxString nameFreq = wxString::Format("f%i",point);
383  const wxString nameVal = wxString::Format("v%i",point);
384  S.Define( mCurves[ 0 ].points[ point ].Freq, nameFreq, 0.0, 0.0, 0.0, 0.0 );
385  S.Define( mCurves[ 0 ].points[ point ].dB, nameVal, 0.0, 0.0, 0.0, 0.0 );
386  }
387 
388  }
389  else
390  {
391  mCurves[0].points.clear();
392 
393  for (int i = 0; i < 200; i++)
394  {
395  const wxString nameFreq = wxString::Format("f%i",i);
396  const wxString nameVal = wxString::Format("v%i",i);
397  double f = -1000.0;
398  double d = 0.0;
399  S.Define( f, nameFreq, 0.0, -10000.0, 1000000.0, 0.0 );
400  S.Define( d, nameVal, 0.0, -10000.0, 10000.0, 0.0 );
401  if( f <= 0.0 )
402  break;
403  mCurves[0].points.push_back( EQPoint( f,d ));
404  }
405  setCurve( 0 );
406  }
407 
408  return true;
409 }
410 
412 {
413  parms.Write(KEY_FilterLength, (unsigned long)mM);
414  //parms.Write(KEY_CurveName, mCurveName);
415  parms.Write(KEY_InterpLin, mLin);
416  parms.WriteEnum(KEY_InterpMeth, mInterp, kInterpStrings, nInterpolations);
417 
418  return true;
419 }
420 
422 {
423  // Pretty sure the interpolation name shouldn't have been interpreted when
424  // specified in chains, but must keep it that way for compatibility.
425 
426  ReadAndVerifyInt(FilterLength);
427  //ReadAndVerifyString(CurveName);
428  ReadAndVerifyBool(InterpLin);
430 
431  mM = FilterLength;
432  //mCurveName = CurveName;
433  mLin = InterpLin;
434  mInterp = InterpMeth;
435 
436  if (InterpMeth >= nInterpolations)
437  {
438  InterpMeth -= nInterpolations;
439  }
440 
441  mEnvelope = (mLin ? mLinEnvelope : mLogEnvelope).get();
442 
443  return true;
444 }
445 
446 // This function Apparently not used anymore.
448 {
449  mdBMin = DEF_dBMin;
450  mdBMax = DEF_dBMax;
451  mDrawMode = DEF_DrawMode;
452  mDrawGrid = DEF_DrawGrid;
453 
454  if( mOptions == kEqOptionCurve)
455  mDrawMode = true;
456  if( mOptions == kEqOptionGraphic)
457  mDrawMode = false;
458 
460 }
461 
462 // Constants determining who the prests are for.
463 const bool kCURVE = false;
464 const bool kBOTH = true;
465 
466 static const struct
467 {
468  const bool bForBoth; // more extended set is used for Filter EQ
469  // See Bug 2254 for rationale.
471  const wxChar *values;
472 }
473 FactoryPresets[] =
474 {
475  { kCURVE, XO("100Hz Rumble"), wxT("f0=\"20.0\" v0=\"-80.0\" f1=\"49.237316986327\" v1=\"-33.107692718506\" f2=\"54.196034330446\" v2=\"-29.553844451904\" f3=\"88.033573501041\" v3=\"-6.923076629639\" f4=\"95.871851182279\" v4=\"-4.523078918457\" f5=\"108.957037410504\" v5=\"-1.938461303711\" f6=\"123.828171198057\" v6=\"-0.73846244812\" f7=\"149.228077614658\" v7=\"-0.092308044434\"") },
476  { kCURVE, XO("AM Radio"), wxT("f0=\"20.0\" v0=\"-63.67\" f1=\"31.0\" v1=\"-33.219\" f2=\"50.0\" v2=\"-3.01\" f3=\"63.0\" v3=\"-0.106\" f4=\"100.0\" v4=\"0.0\" f5=\"2500.0\" v5=\"0.0\" f6=\"4000.0\" v6=\"-0.614\" f7=\"5000.0\" v7=\"-8.059\" f8=\"8000.0\" v8=\"-39.981\" f9=\"20000.0\" v9=\"-103.651\" f10=\"48000.0\" v10=\"-164.485\"") },
477  { kBOTH, XO("Bass Boost"), wxT("f0=\"100.0\" v0=\"9.0\" f1=\"500.0\" v1=\"0.0\"") },
478  { kBOTH, XO("Bass Cut"), wxT("f0=\"150.0\" v0=\"-50.0\" f1=\"300.0\" v1=\"0.0\"") },
479  { kCURVE, XO("Low rolloff for speech"), wxT("f0=\"50.0\" v0=\"-120.0\" f1=\"60.0\" v1=\"-50.0\" f2=\"65.0\" v2=\"-24.0\" f3=\"70.0\" v3=\"-12.0\" f4=\"80.0\" v4=\"-4.0\" f5=\"90.0\" v5=\"-1.0\" f6=\"100.0\" v6=\"0.0\"") },
480  { kBOTH, XO("RIAA"), wxT("f0=\"20.0\" v0=\"19.274\" f1=\"25.0\" v1=\"18.954\" f2=\"31.0\" v2=\"18.516\" f3=\"40.0\" v3=\"17.792\" f4=\"50.0\" v4=\"16.946\" f5=\"63.0\" v5=\"15.852\" f6=\"80.0\" v6=\"14.506\" f7=\"100.0\" v7=\"13.088\" f8=\"125.0\" v8=\"11.563\" f9=\"160.0\" v9=\"9.809\" f10=\"200.0\" v10=\"8.219\" f11=\"250.0\" v11=\"6.677\" f12=\"315.0\" v12=\"5.179\" f13=\"400.0\" v13=\"3.784\" f14=\"500.0\" v14=\"2.648\" f15=\"630.0\" v15=\"1.642\" f16=\"800.0\" v16=\"0.751\" f17=\"1000.0\" v17=\"0.0\" f18=\"1250.0\" v18=\"-0.744\" f19=\"1600.0\" v19=\"-1.643\" f20=\"2000.0\" v20=\"-2.589\" f21=\"2500.0\" v21=\"-3.7\" f22=\"3150.0\" v22=\"-5.038\" f23=\"4000.0\" v23=\"-6.605\" f24=\"5000.0\" v24=\"-8.21\" f25=\"6300.0\" v25=\"-9.98\" f26=\"8000.0\" v26=\"-11.894\" f27=\"10000.0\" v27=\"-13.734\" f28=\"12500.0\" v28=\"-15.609\" f29=\"16000.0\" v29=\"-17.708\" f30=\"20000.0\" v30=\"-19.62\" f31=\"25000.0\" v31=\"-21.542\" f32=\"48000.0\" v32=\"-27.187\"") },
481  { kCURVE, XO("Telephone"), wxT("f0=\"20.0\" v0=\"-94.087\" f1=\"200.0\" v1=\"-14.254\" f2=\"250.0\" v2=\"-7.243\" f3=\"315.0\" v3=\"-2.245\" f4=\"400.0\" v4=\"-0.414\" f5=\"500.0\" v5=\"0.0\" f6=\"2500.0\" v6=\"0.0\" f7=\"3150.0\" v7=\"-0.874\" f8=\"4000.0\" v8=\"-3.992\" f9=\"5000.0\" v9=\"-9.993\" f10=\"48000.0\" v10=\"-88.117\"") },
482  { kBOTH, XO("Treble Boost"), wxT("f0=\"4000.0\" v0=\"0.0\" f1=\"5000.0\" v1=\"9.0\"") },
483  { kBOTH, XO("Treble Cut"), wxT("f0=\"6000.0\" v0=\"0.0\" f1=\"10000.0\" v1=\"-110.0\"") },
484  { kCURVE, XO("Walkie-talkie"), wxT("f0=\"100.0\" v0=\"-120.0\" f1=\"101.0\" v1=\"0.0\" f2=\"2000.0\" v2=\"0.0\" f3=\"2001.0\" v3=\"-120.0\"") },
485 };
486 
487 
488 
489 
491 {
493 
494  for (size_t i = 0; i < WXSIZEOF(FactoryPresets); i++)
495  {
496  if ((mOptions == kEqOptionGraphic) && (FactoryPresets[i].bForBoth == false))
497  continue;
498  names.push_back(FactoryPresets[i].name.Translation());
499  }
500 
501  return names;
502 }
503 
505 {
506  int index = -1;
507  for (size_t i = 0; i < WXSIZEOF(FactoryPresets); i++)
508  {
509  if ((mOptions == kEqOptionGraphic) && (FactoryPresets[i].bForBoth == false))
510  continue;
511  if (id-- == 0) {
512  index = i;
513  break;
514  }
515  }
516  if (index < 0)
517  return false;
518 
519  // mParams =
520  wxString params = FactoryPresets[index].values;
521 
524  S.SetForWriting( &eap );
525  DefineParams( S );
526 
527  if (mUIDialog)
528  {
530  }
531 
532  return true;
533 }
534 
535 
536 
537 // EffectUIClientInterface implementation
538 
540 {
541  // If editing a macro, we don't want to be using the unnamed curve so
542  // we offer to save it.
543 
544  if (mDisallowCustom && mCurveName == wxT("unnamed"))
545  {
546  // PRL: This is unreachable. mDisallowCustom is always false.
547 
549  XO("To use this filter curve in a macro, please choose a new name for it.\nChoose the 'Save/Manage Curves...' button and rename the 'unnamed' curve, then use that one."),
550  wxOK | wxCENTRE,
551  XO("Filter Curve EQ needs a different name") );
552  return false;
553  }
554 
555  // Update unnamed curve (so it's there for next time)
556  //(done in a hurry, may not be the neatest -MJS)
557  if (mDirty && !mDrawMode)
558  {
559  size_t numPoints = mLogEnvelope->GetNumberOfPoints();
560  Doubles when{ numPoints };
561  Doubles value{ numPoints };
562  mLogEnvelope->GetPoints(when.get(), value.get(), numPoints);
563  for (size_t i = 0, j = 0; j + 2 < numPoints; i++, j++)
564  {
565  if ((value[i] < value[i + 1] + .05) && (value[i] > value[i + 1] - .05) &&
566  (value[i + 1] < value[i + 2] + .05) && (value[i + 1] > value[i + 2] - .05))
567  { // within < 0.05 dB?
568  mLogEnvelope->Delete(j + 1);
569  numPoints--;
570  j--;
571  }
572  }
573  Select((int) mCurves.size() - 1);
574  }
575  SaveCurves();
576 
581 
582  return true;
583 }
584 
585 // Effect implementation
586 
588 {
589  wxString base = wxT("/Effects/Equalization/");
590  if( mOptions == kEqOptionGraphic )
591  base = wxT("/Effects/GraphicEq/");
592  else if( mOptions == kEqOptionCurve )
593  base = wxT("/Effects/FilterCurve/");
594  return base;
595 }
596 
597 
599 {
600  wxString base = GetPrefsPrefix();
601 
602  // Migrate settings from 2.1.0 or before
603 
604  // Already migrated, so bail
605  if (gPrefs->Exists(base + wxT("Migrated")))
606  {
607  return true;
608  }
609 
610  // Load the old "current" settings
611  if (gPrefs->Exists(base))
612  {
613  // These get saved to the current preset
614  int filterLength;
615  gPrefs->Read(base + wxT("FilterLength"), &filterLength, 4001);
616  mM = std::max(0, filterLength);
617  if ((mM < 21) || (mM > 8191)) { // corrupted Prefs?
618  mM = 4001; //default
619  }
620  gPrefs->Read(base + wxT("CurveName"), &mCurveName, wxT("unnamed"));
621  gPrefs->Read(base + wxT("Lin"), &mLin, false);
622  gPrefs->Read(base + wxT("Interp"), &mInterp, 0);
623 
625 
626  // These persist across preset changes
627  double dBMin;
628  gPrefs->Read(base + wxT("dBMin"), &dBMin, -30.0);
629  if ((dBMin < -120) || (dBMin > -10)) { // corrupted Prefs?
630  dBMin = -30; //default
631  }
632  mdBMin = dBMin;
634 
635  double dBMax;
636  gPrefs->Read(base + wxT("dBMax"), &dBMax, 30.);
637  if ((dBMax < 0) || (dBMax > 60)) { // corrupted Prefs?
638  dBMax = 30; //default
639  }
640  mdBMax = dBMax;
642 
643  gPrefs->Read(base + wxT("DrawMode"), &mDrawMode, true);
645 
646  gPrefs->Read(base + wxT("DrawGrid"), &mDrawGrid, true);
648 
649  // Do not migrate again
650  gPrefs->Write(base + wxT("Migrated"), true);
651  gPrefs->Flush();
652  }
653 
654  return true;
655 }
656 
658 {
659  int selcount = 0;
660  double rate = 0.0;
661 
662  auto trackRange =
663  TrackList::Get( *FindProject() ).Selected< const WaveTrack >();
664  if (trackRange) {
665  rate = (*(trackRange.first++)) -> GetRate();
666  ++selcount;
667 
668  for (auto track : trackRange) {
669  if (track->GetRate() != rate) {
671  XO(
672 "To apply Equalization, all selected tracks must have the same sample rate.") );
673  return(false);
674  }
675  ++selcount;
676  }
677  }
678 
679  mHiFreq = rate / 2.0;
680  // Unlikely, but better than crashing.
681  if (mHiFreq <= loFreqI) {
683  XO("Track sample rate is too low for this effect."),
684  wxOK | wxCENTRE,
685  XO("Effect Unavailable") );
686  return(false);
687  }
688 
689  mLoFreq = loFreqI;
690 
691  mBandsInUse = 0;
692  while (kThirdOct[mBandsInUse] <= mHiFreq) {
693  mBandsInUse++;
695  break;
696  }
697 
698  mEnvelope = (mLin ? mLinEnvelope : mLogEnvelope).get();
699 
701 
702  CalcFilter();
703 
704  return(true);
705 }
706 
708 {
709 #ifdef EXPERIMENTAL_EQ_SSE_THREADED
710  if(mEffectEqualization48x) {
711  if(mBench) {
712  mBench=false;
713  return mEffectEqualization48x->Benchmark(this);
714  }
715  else
716  return mEffectEqualization48x->Process(this);
717  }
718 #endif
719  this->CopyInputTracks(); // Set up mOutputTracks.
720  CalcFilter();
721  bool bGoodResult = true;
722 
723  int count = 0;
724  for( auto track : mOutputTracks->Selected< WaveTrack >() ) {
725  double trackStart = track->GetStartTime();
726  double trackEnd = track->GetEndTime();
727  double t0 = mT0 < trackStart? trackStart: mT0;
728  double t1 = mT1 > trackEnd? trackEnd: mT1;
729 
730  if (t1 > t0) {
731  auto start = track->TimeToLongSamples(t0);
732  auto end = track->TimeToLongSamples(t1);
733  auto len = end - start;
734 
735  if (!ProcessOne(count, track, start, len))
736  {
737  bGoodResult = false;
738  break;
739  }
740  }
741 
742  count++;
743  }
744 
745  this->ReplaceProcessedTracks(bGoodResult);
746  return bGoodResult;
747 }
748 
750 {
751  mCurve = NULL;
752  mPanel = NULL;
753 
754  return Effect::CloseUI();
755 }
756 
758 {
759  if ( (S.GetMode() == eIsCreating ) && !IsBatchProcessing() )
761 
762  //LoadCurves();
763 
764 
765 
766  S.SetBorder(0);
767 
768  S.SetSizerProportion(1);
769  S.Prop(1).StartMultiColumn(1, wxEXPAND);
770  {
771  S.SetStretchyCol(0);
772  //S.SetStretchyRow(0); // The 5px Top border
773  S.SetStretchyRow(1); // The Graph
774  S.SetStretchyRow(2); // The EQ sliders
775  szrV = S.GetSizer();
776 
777  // -------------------------------------------------------------------
778  // ROW 0: Top border
779  // -------------------------------------------------------------------
780  S.AddSpace(5);
781 
782  // -------------------------------------------------------------------
783  // ROW 1: Equalization panel and sliders for vertical scale
784  // -------------------------------------------------------------------
785  S.SetSizerProportion(1);
786  S.Prop(1).StartMultiColumn(3, wxEXPAND);
787  {
788  S.SetStretchyCol(1);
789  S.SetStretchyRow(0);
790  szr1 = S.GetSizer();
791 
792  S.StartVerticalLay(wxEXPAND, 1);
793  {
795  S.GetParent(), wxID_ANY, wxVERTICAL,
796  wxSize{ 100, 100 }, // Ruler can't handle small sizes
797  RulerPanel::Range{ 60.0, -120.0 },
799  XO("dB"),
801  .LabelEdges(true)
802  .TicksAtExtremes(true)
803  .TickColour( { 0, 0, 0 } )
804  );
805 
806  S.Prop(0).AddSpace(0, 1);
807  S.Prop(1)
808  .Position(wxEXPAND)
810  S.AddSpace(0, 1);
811  }
812  S.EndVerticalLay();
813 
814  mPanel = safenew EqualizationPanel(S.GetParent(), wxID_ANY, this);
815  S.Prop(1)
816  .Position(wxEXPAND)
817  .MinSize( { wxDefaultCoord, wxDefaultCoord } )
818  .AddWindow(mPanel);
819 
820  S.SetBorder(5);
821  S.StartVerticalLay();
822  {
823  S.AddVariableText(XO("+ dB"), false, wxCENTER);
825  .Name(XO("Max dB"))
826  .Style(wxSL_VERTICAL | wxSL_INVERSE)
827  .AddSlider( {}, 30, 60, 0);
828 #if wxUSE_ACCESSIBILITY
829  mdBMaxSlider->SetAccessible(safenew SliderAx(mdBMaxSlider, XO("%d dB")));
830 #endif
831 
833  .Name(XO("Min dB"))
834  .Style(wxSL_VERTICAL | wxSL_INVERSE)
835  .AddSlider( {}, -30, -10, -120);
836  S.AddVariableText(XO("- dB"), false, wxCENTER);
837 #if wxUSE_ACCESSIBILITY
838  mdBMinSlider->SetAccessible(safenew SliderAx(mdBMinSlider, XO("%d dB")));
839 #endif
840  }
841  S.EndVerticalLay();
842  S.SetBorder(0);
843 
844  // -------------------------------------------------------------------
845  // Frequency ruler below graph
846  // -------------------------------------------------------------------
847 
848  // Column 1 is empty
849  S.AddSpace(1, 1);
850 
852  S.GetParent(), wxID_ANY, wxHORIZONTAL,
853  wxSize{ 100, 100 }, // Ruler can't handle small sizes
854  RulerPanel::Range{ mLoFreq, mHiFreq },
856  XO("Hz"),
858  .Log(true)
859  .Flip(true)
860  .LabelEdges(true)
861  .TicksAtExtremes(true)
862  .TickColour( { 0, 0, 0 } )
863  );
864 
865  S.SetBorder(1);
866  S.Prop(1)
867  .Position(wxEXPAND | wxALIGN_LEFT | wxALIGN_TOP | wxLEFT)
869  S.SetBorder(0);
870 
871  // Column 3 is empty
872  S.AddSpace(1, 1);
873  }
874  S.EndMultiColumn();
875 
876  // -------------------------------------------------------------------
877  // ROW 2: Graphic EQ
878  // -------------------------------------------------------------------
879  S.SetSizerProportion(1);
880  S.StartHorizontalLay(wxEXPAND, 1);
881  {
882  szrG = S.GetSizer();
883 
884  // Panel used to host the sliders since they will be positioned manually.
885  //mGraphicPanel = S.Prop(1)
886  //.Position(wxEXPAND)
887  //.Size( { -1, 150 } )
888  //.StartPanel();
889  wxWindow *pParent = S.GetParent();
890  S.AddSpace(15,0);
891  {
892 
893  // for (int i = 0; (i < NUMBER_OF_BANDS) && (kThirdOct[i] <= mHiFreq); ++i)
894  // May show more sliders than needed. Fixes Bug 2269
895  for (int i = 0; i < NUMBER_OF_BANDS; ++i)
896  {
897  TranslatableString freq = kThirdOct[i] < 1000.
898  ? XO("%d Hz").Format((int)kThirdOct[i])
899  : XO("%g kHz").Format(kThirdOct[i] / 1000.);
900  TranslatableString fNum = kThirdOct[i] < 1000.
901  ? Verbatim("%d").Format((int)kThirdOct[i])
902  /* i18n-hint k is SI abbreviation for x1,000. Usually unchanged in translation. */
903  : XO("%gk").Format(kThirdOct[i] / 1000.);
904  S.StartVerticalLay();
905  {
906  S.AddFixedText( fNum );
907  mSliders[i] = safenew wxSliderWrapper(pParent, ID_Slider + i, 0, -20, +20,
908  wxDefaultPosition, wxSize(-1,50), wxSL_VERTICAL | wxSL_INVERSE);
909 
910 #if wxUSE_ACCESSIBILITY
911  mSliders[i]->SetAccessible(safenew SliderAx(mSliders[i], XO("%d dB")));
912 #endif
913 
914  mSlidersOld[i] = 0;
915  mEQVals[i] = 0.;
916  S.Prop(1)
917  .Name(freq)
918  .ConnectRoot(
919  wxEVT_ERASE_BACKGROUND, &EffectEqualization::OnErase)
920  .Position(wxEXPAND)
921  .Size({ -1, 50 })
922  .AddWindow(mSliders[i]);
923  }
924  S.EndVerticalLay();
925  }
926  S.AddSpace(15,0);
927 
928  } //S.EndPanel();
929  }
930  S.EndHorizontalLay();
931 
932  // -------------------------------------------------------------------
933  // ROW 4: Various controls
934  // -------------------------------------------------------------------
935  S.SetSizerProportion(1);
936  S.Prop(1).StartMultiColumn(7, wxALIGN_CENTER_HORIZONTAL);
937  {
938  S.SetBorder(5);
939 
940  S.AddSpace(5, 5);
941 
942  if( mOptions == kEqLegacy )
943  {
944  S.StartHorizontalLay(wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
945  {
946  S.AddPrompt(XXO("&EQ Type:"));
947  }
948  S.EndHorizontalLay();
949 
950  S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
951  {
952  S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
953  {
954  mDraw = S.Id(ID_Draw)
955  .Name(XO("Draw Curves"))
956  .AddRadioButton(XXO("&Draw"));
957 
958  mGraphic = S.Id(ID_Graphic)
959  .Name(XO("Graphic EQ"))
960  .AddRadioButtonToGroup(XXO("&Graphic"));
961  }
962  S.EndHorizontalLay();
963  }
964  S.EndHorizontalLay();
965  }
966 
967  S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0);
968  {
969  szrH = S.GetSizer();
970 
971  S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
972  {
973  szrI = S.GetSizer();
974 
976  .Name(XO("Interpolation type"))
977  .AddChoice( {},
979 #if wxUSE_ACCESSIBILITY
980  // so that name can be set on a standard control
982 #endif
983  }
984  S.EndHorizontalLay();
985 
986  S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
987  {
988  szrL = S.GetSizer();
989 
990  mLinFreq = S.Id(ID_Linear)
991  .Name(XO("Linear Frequency Scale"))
992  .AddCheckBox(XXO("Li&near Frequency Scale"), false);
993  }
994  S.EndHorizontalLay();
995  }
996  S.EndHorizontalLay();
997 
998  // -------------------------------------------------------------------
999  // Filter length grouping
1000  // -------------------------------------------------------------------
1001 
1002  if( mOptions == kEqLegacy ){
1003  S.StartHorizontalLay(wxEXPAND, 0);
1004  {
1005  S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0);
1006  {
1007  S.AddPrompt(XXO("Length of &Filter:"));
1008  }
1009  S.EndHorizontalLay();
1010 
1011  S.StartHorizontalLay(wxEXPAND, 1);
1012  {
1013  mMSlider = S.Id(ID_Length)
1014  .Name(XO("Length of Filter"))
1015  .Style(wxSL_HORIZONTAL)
1016  .AddSlider( {}, (mM - 1) / 2, 4095, 10);
1017  }
1018  S.EndHorizontalLay();
1019 
1020  S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0);
1021  {
1022  wxString label;
1023  label.Printf(wxT("%ld"), mM);
1024  mMText = S.Name( Verbatim( label ) )
1025  // fix for bug 577 (NVDA/Narrator screen readers do not
1026  // read static text in dialogs)
1027  .AddVariableText( Verbatim( label ) );
1028  }
1029  S.EndHorizontalLay();
1030  }
1031  S.EndHorizontalLay();
1032 
1033  S.AddSpace(1, 1);
1034  }
1035 
1036  S.AddSpace(5, 5);
1037 
1038  if( mOptions == kEqLegacy ){
1039  S.AddSpace(5, 5);
1040  S.StartHorizontalLay(wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
1041  {
1042  S.AddPrompt(XXO("&Select Curve:"));
1043  }
1044  S.EndHorizontalLay();
1045 
1046  S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
1047  {
1048  S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
1049  {
1050  mCurve = S.Id(ID_Curve)
1051  .Name(XO("Select Curve"))
1052  .AddChoice( {},
1053  [this]{
1054  TranslatableStrings curves;
1055  for (const auto &curve : mCurves)
1056  curves.push_back( Verbatim( curve.Name ) );
1057  return curves;
1058  }()
1059  );
1060  }
1061  S.EndHorizontalLay();
1062  }
1063  S.EndHorizontalLay();
1064 
1065  S.Id(ID_Manage).AddButton(XXO("S&ave/Manage Curves..."));
1066  }
1067 
1068  S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
1069  {
1070  S.Id(ID_Clear).AddButton(XXO("Fla&tten"));
1071  S.Id(ID_Invert).AddButton(XXO("&Invert"));
1072 
1073  mGridOnOff = S.Id(ID_Grid)
1074  .Name(XO("Show grid lines"))
1075  .AddCheckBox(XXO("Show g&rid lines"), false);
1076  }
1077  S.EndHorizontalLay();
1078 
1079  S.AddSpace(5, 5);
1080  }
1081  S.EndMultiColumn();
1082  }
1083  S.EndMultiColumn();
1084 
1085 #ifdef EXPERIMENTAL_EQ_SSE_THREADED
1086  if (mEffectEqualization48x)
1087  {
1088  // -------------------------------------------------------------------
1089  // ROW 6: Processing routine selection
1090  // -------------------------------------------------------------------
1091 
1092  // Column 1 is blank
1093  S.AddSpace(1, 1);
1094 
1095  S.StartHorizontalLay();
1096  {
1097  S.AddUnits(XO("&Processing: "));
1098 
1099  // update the control state
1100  int mathPath = EffectEqualization48x::GetMathPath();
1101  int value =
1102  (mathPath & MATH_FUNCTION_SSE)
1103  ? (mathPath & MATH_FUNCTION_THREADED)
1104  ? 2
1105  : 1
1106  : false // (mathPath & MATH_FUNCTION_AVX) // not implemented
1107  ? (mathPath & MATH_FUNCTION_THREADED)
1108  ? 4
1109  : 3
1110  : 0;
1111 
1112  mMathProcessingType[0] = S.Id(ID_DefaultMath)
1113  .AddRadioButton(XXO("D&efault"),
1114  0, value);
1115  mMathProcessingType[1] = S.Id(ID_SSE)
1116  .Disable(!EffectEqualization48x::GetMathCaps()->SSE)
1117  .AddRadioButtonToGroup(XXO("&SSE"),
1118  1, value);
1119  mMathProcessingType[2] = S.Id(ID_SSEThreaded)
1120  .Disable(!EffectEqualization48x::GetMathCaps()->SSE)
1121  .AddRadioButtonToGroup(XXO("SSE &Threaded"),
1122  2, value);
1123  mMathProcessingType[3] = S.Id(ID_AVX)
1124  // not implemented
1125  .Disable(true /* !EffectEqualization48x::GetMathCaps()->AVX */)
1126  .AddRadioButtonToGroup(XXO("A&VX"),
1127  3, value);
1128  mMathProcessingType[4] = S.Id(ID_AVXThreaded)
1129  // not implemented
1130  .Disable(true /* !EffectEqualization48x::GetMathCaps()->AVX */)
1131  .AddRadioButtonToGroup(XXO("AV&X Threaded"),
1132  4, value);
1133  S.Id(ID_Bench).AddButton(XXO("&Bench"));
1134  }
1135  S.EndHorizontalLay();
1136 
1137  // Column 3 is blank
1138  S.AddSpace(1, 1);
1139  }
1140 #endif
1141 
1142  mUIParent->SetAutoLayout(false);
1143  if( mOptions != kEqOptionGraphic)
1144  mUIParent->Layout();
1145 
1146  if( mOptions == kEqOptionCurve)
1147  mDrawMode = true;
1148  if( mOptions == kEqOptionGraphic)
1149  mDrawMode = false;
1150 
1151  // "show" settings for graphics mode before setting the size of the dialog
1152  // as this needs more space than draw mode
1153  szrV->Show(szrG,!mDrawMode); // eq sliders
1154  szrH->Show(szrI,true); // interpolation choice
1155  szrH->Show(szrL,false); // linear freq checkbox
1156 
1157  if( mOptions == kEqOptionGraphic){
1158  mPanel->Show( false );
1159  wxSize sz = szrV->GetMinSize();
1160  sz += wxSize( 30, 0);
1161  mUIParent->SetMinSize(sz);
1162  }
1163  else{
1164  mPanel->Show( true );
1165  szrV->Show(szr1, true);
1166  mUIParent->SetMinSize(mUIParent->GetBestSize());
1167  }
1168  ForceRecalc();
1169 
1170  return;
1171 }
1172 
1173 //
1174 // Populate the window with relevant variables
1175 //
1177 {
1178  // Set log or lin freq scale (affects interpolation as well)
1179  mLinFreq->SetValue( mLin );
1180  wxCommandEvent dummyEvent;
1181  OnLinFreq(dummyEvent); // causes a CalcFilter
1182 
1183  mGridOnOff->SetValue( mDrawGrid ); // checks/unchecks the box on the interface
1184 
1185  if( mMSlider )
1186  mMSlider->SetValue((mM - 1) / 2);
1187  mM = 0; // force refresh in TransferDataFromWindow()
1188 
1189  mdBMinSlider->SetValue((int)mdBMin);
1190  mdBMin = 0; // force refresh in TransferDataFromWindow()
1191 
1192  mdBMaxSlider->SetValue((int)mdBMax);
1193  mdBMax = 0; // force refresh in TransferDataFromWindow()
1194 
1195  // Reload the curve names
1196  UpdateCurves();
1197 
1198  // Set graphic interpolation mode
1199  mInterpChoice->SetSelection(mInterp);
1200 
1201  // Override draw mode, if we're not displaying the radio buttons.
1202  if( mOptions == kEqOptionCurve)
1203  mDrawMode = true;
1204  if( mOptions == kEqOptionGraphic)
1205  mDrawMode = false;
1206 
1207  if( mDraw )
1208  mDraw->SetValue(mDrawMode);
1209  szrV->Show(szr1,mOptions != kEqOptionGraphic); // Graph
1210  szrV->Show(szrG,!mDrawMode); // eq sliders
1211  szrH->Show(szrI,mOptions == kEqLegacy ); // interpolation choice
1212  szrH->Show(szrL, mDrawMode); // linear freq checkbox
1213  if( mGraphic)
1214  mGraphic->SetValue(!mDrawMode);
1215  mGridOnOff->Show( mDrawMode );
1216 
1217  // Set Graphic (Fader) or Draw mode
1218  if (!mDrawMode)
1219  UpdateGraphic();
1220 
1222 
1223  mUIParent->Layout();
1224  wxGetTopLevelParent(mUIParent)->Layout();
1225 
1226  return true;
1227 }
1228 
1229 //
1230 // Retrieve data from the window
1231 //
1233 {
1234  wxString tip;
1235 
1236  bool rr = false;
1237  float dB = (float) mdBMinSlider->GetValue();
1238  if (dB != mdBMin) {
1239  rr = true;
1240  mdBMin = dB;
1241  tip.Printf(_("%d dB"), (int)mdBMin);
1242  mdBMinSlider->SetToolTip(tip);
1243  }
1244 
1245  dB = (float) mdBMaxSlider->GetValue();
1246  if (dB != mdBMax) {
1247  rr = true;
1248  mdBMax = dB;
1249  tip.Printf(_("%d dB"), (int)mdBMax);
1250  mdBMaxSlider->SetToolTip(tip);
1251  }
1252 
1253  // Refresh ruler if values have changed
1254  if (rr) {
1255  int w1, w2, h;
1256  mdBRuler->ruler.GetMaxSize(&w1, &h);
1258  mdBRuler->ruler.GetMaxSize(&w2, &h);
1259  if( w1 != w2 ) // Reduces flicker
1260  {
1261  mdBRuler->SetSize(wxSize(w2,h));
1262  mFreqRuler->Refresh(false);
1263  }
1264  mdBRuler->Refresh(false);
1265 
1266  mPanel->Refresh(false);
1267  }
1268 
1269  size_t m = DEF_FilterLength; // m must be odd.
1270  if (mMSlider )
1271  m = 2* mMSlider->GetValue()+1;
1272  wxASSERT( (m & 1) ==1 );
1273  if (m != mM) {
1274  mM = m;
1275  ForceRecalc();
1276 
1277  if( mMSlider)
1278  {
1279  tip.Printf(wxT("%d"), (int)mM);
1280  mMText->SetLabel(tip);
1281  mMText->SetName(mMText->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
1282  mMSlider->SetToolTip(tip);
1283  }
1284  }
1285 
1286  return true;
1287 }
1288 
1289 // EffectEqualization implementation
1290 
1292  sampleCount start, sampleCount len)
1293 {
1294  // create a NEW WaveTrack to hold all of the output, including 'tails' each end
1295  auto output = t->EmptyCopy();
1297 
1298  wxASSERT(mM - 1 < windowSize);
1299  size_t L = windowSize - (mM - 1); //Process L samples at a go
1300  auto s = start;
1301  auto idealBlockLen = t->GetMaxBlockSize() * 4;
1302  if (idealBlockLen % L != 0)
1303  idealBlockLen += (L - (idealBlockLen % L));
1304 
1305  Floats buffer{ idealBlockLen };
1306 
1307  Floats window1{ windowSize };
1308  Floats window2{ windowSize };
1309  float *thisWindow = window1.get();
1310  float *lastWindow = window2.get();
1311 
1312  auto originalLen = len;
1313 
1314  for(size_t i = 0; i < windowSize; i++)
1315  lastWindow[i] = 0;
1316 
1317  TrackProgress(count, 0.);
1318  bool bLoopSuccess = true;
1319  size_t wcopy = 0;
1320  int offset = (mM - 1) / 2;
1321 
1322  while (len != 0)
1323  {
1324  auto block = limitSampleBufferSize( idealBlockLen, len );
1325 
1326  t->Get((samplePtr)buffer.get(), floatSample, s, block);
1327 
1328  for(size_t i = 0; i < block; i += L) //go through block in lumps of length L
1329  {
1330  wcopy = std::min <size_t> (L, block - i);
1331  for(size_t j = 0; j < wcopy; j++)
1332  thisWindow[j] = buffer[i+j]; //copy the L (or remaining) samples
1333  for(auto j = wcopy; j < windowSize; j++)
1334  thisWindow[j] = 0; //this includes the padding
1335 
1336  Filter(windowSize, thisWindow);
1337 
1338  // Overlap - Add
1339  for(size_t j = 0; (j < mM - 1) && (j < wcopy); j++)
1340  buffer[i+j] = thisWindow[j] + lastWindow[L + j];
1341  for(size_t j = mM - 1; j < wcopy; j++)
1342  buffer[i+j] = thisWindow[j];
1343 
1344  std::swap( thisWindow, lastWindow );
1345  } //next i, lump of this block
1346 
1347  output->Append((samplePtr)buffer.get(), floatSample, block);
1348  len -= block;
1349  s += block;
1350 
1351  if (TrackProgress(count, ( s - start ).as_double() /
1352  originalLen.as_double()))
1353  {
1354  bLoopSuccess = false;
1355  break;
1356  }
1357  }
1358 
1359  if(bLoopSuccess)
1360  {
1361  // mM-1 samples of 'tail' left in lastWindow, get them now
1362  if(wcopy < (mM - 1)) {
1363  // Still have some overlap left to process
1364  // (note that lastWindow and thisWindow have been exchanged at this point
1365  // so that 'thisWindow' is really the window prior to 'lastWindow')
1366  size_t j = 0;
1367  for(; j < mM - 1 - wcopy; j++)
1368  buffer[j] = lastWindow[wcopy + j] + thisWindow[L + wcopy + j];
1369  // And fill in the remainder after the overlap
1370  for( ; j < mM - 1; j++)
1371  buffer[j] = lastWindow[wcopy + j];
1372  } else {
1373  for(size_t j = 0; j < mM - 1; j++)
1374  buffer[j] = lastWindow[wcopy + j];
1375  }
1376  output->Append((samplePtr)buffer.get(), floatSample, mM - 1);
1377  output->Flush();
1378 
1379  std::vector<EnvPoint> envPoints;
1380 
1381  // now move the appropriate bit of the output back to the track
1382  // (this could be enhanced in the future to use the tails)
1383  double offsetT0 = t->LongSamplesToTime(offset);
1384  double lenT = t->LongSamplesToTime(originalLen);
1385  // 'start' is the sample offset in 't', the passed in track
1386  // 'startT' is the equivalent time value
1387  // 'output' starts at zero
1388  double startT = t->LongSamplesToTime(start);
1389 
1390  //output has one waveclip for the total length, even though
1391  //t might have whitespace separating multiple clips
1392  //we want to maintain the original clip structure, so
1393  //only paste the intersections of the NEW clip.
1394 
1395  //Find the bits of clips that need replacing
1396  std::vector<std::pair<double, double> > clipStartEndTimes;
1397  std::vector<std::pair<double, double> > clipRealStartEndTimes; //the above may be truncated due to a clip being partially selected
1398  for (const auto &clip : t->GetClips())
1399  {
1400  double clipStartT;
1401  double clipEndT;
1402 
1403  clipStartT = clip->GetStartTime();
1404  clipEndT = clip->GetEndTime();
1405  if( clipEndT <= startT )
1406  continue; // clip is not within selection
1407  if( clipStartT >= startT + lenT )
1408  continue; // clip is not within selection
1409 
1410  //save the actual clip start/end so that we can rejoin them after we paste.
1411  clipRealStartEndTimes.push_back(std::pair<double,double>(clipStartT,clipEndT));
1412 
1413  if( clipStartT < startT ) // does selection cover the whole clip?
1414  clipStartT = startT; // don't copy all the NEW clip
1415  if( clipEndT > startT + lenT ) // does selection cover the whole clip?
1416  clipEndT = startT + lenT; // don't copy all the NEW clip
1417 
1418  //save them
1419  clipStartEndTimes.push_back(std::pair<double,double>(clipStartT,clipEndT));
1420 
1421  // Save the envelope points
1422  const auto &env = *clip->GetEnvelope();
1423  for (size_t i = 0, numPoints = env.GetNumberOfPoints(); i < numPoints; ++i) {
1424  envPoints.push_back(env[i]);
1425  }
1426  }
1427 
1428  //now go thru and replace the old clips with NEW
1429  for(unsigned int i = 0; i < clipStartEndTimes.size(); i++)
1430  {
1431  //remove the old audio and get the NEW
1432  t->Clear(clipStartEndTimes[i].first,clipStartEndTimes[i].second);
1433  auto toClipOutput = output->Copy(clipStartEndTimes[i].first-startT+offsetT0,clipStartEndTimes[i].second-startT+offsetT0);
1434  //put the processed audio in
1435  t->Paste(clipStartEndTimes[i].first, toClipOutput.get());
1436  //if the clip was only partially selected, the Paste will have created a split line. Join is needed to take care of this
1437  //This is not true when the selection is fully contained within one clip (second half of conditional)
1438  if( (clipRealStartEndTimes[i].first != clipStartEndTimes[i].first ||
1439  clipRealStartEndTimes[i].second != clipStartEndTimes[i].second) &&
1440  !(clipRealStartEndTimes[i].first <= startT &&
1441  clipRealStartEndTimes[i].second >= startT+lenT) )
1442  t->Join(clipRealStartEndTimes[i].first,clipRealStartEndTimes[i].second);
1443  }
1444 
1445  // Restore the envelope points
1446  for (auto point : envPoints) {
1447  WaveClip *clip = t->GetClipAtTime(point.GetT());
1448  clip->GetEnvelope()->Insert(point.GetT(), point.GetVal());
1449  }
1450  }
1451 
1452  return bLoopSuccess;
1453 }
1454 
1456 {
1457  double loLog = log10(mLoFreq);
1458  double hiLog = log10(mHiFreq);
1459  double denom = hiLog - loLog;
1460 
1461  double delta = mHiFreq / ((double)(mWindowSize / 2.));
1462  double val0;
1463  double val1;
1464 
1465  if( IsLinear() )
1466  {
1467  val0 = mLinEnvelope->GetValue(0.0); //no scaling required - saved as dB
1468  val1 = mLinEnvelope->GetValue(1.0);
1469  }
1470  else
1471  {
1472  val0 = mLogEnvelope->GetValue(0.0); //no scaling required - saved as dB
1473  val1 = mLogEnvelope->GetValue(1.0);
1474  }
1475  mFilterFuncR[0] = val0;
1476  double freq = delta;
1477 
1478  for(size_t i = 1; i <= mWindowSize / 2; i++)
1479  {
1480  double when;
1481  if( IsLinear() )
1482  when = freq/mHiFreq;
1483  else
1484  when = (log10(freq) - loLog)/denom;
1485  if(when < 0.)
1486  {
1487  mFilterFuncR[i] = val0;
1488  }
1489  else if(when > 1.0)
1490  {
1491  mFilterFuncR[i] = val1;
1492  }
1493  else
1494  {
1495  if( IsLinear() )
1496  mFilterFuncR[i] = mLinEnvelope->GetValue(when);
1497  else
1498  mFilterFuncR[i] = mLogEnvelope->GetValue(when);
1499  }
1500  freq += delta;
1501  }
1502  mFilterFuncR[mWindowSize / 2] = val1;
1503 
1505 
1506  {
1507  size_t i = 1;
1508  for(; i < mWindowSize / 2; i++)
1509  {
1511  mFilterFuncR[mWindowSize - i] = mFilterFuncR[i]; //Fill entire array
1512  }
1513  mFilterFuncR[i] = DB_TO_LINEAR(mFilterFuncR[i]); //do last one
1514  }
1515 
1516  //transfer to time domain to do the padding and windowing
1517  Floats outr{ mWindowSize };
1518  Floats outi{ mWindowSize };
1519  InverseRealFFT(mWindowSize, mFilterFuncR.get(), NULL, outr.get()); // To time domain
1520 
1521  {
1522  size_t i = 0;
1523  for(; i <= (mM - 1) / 2; i++)
1524  { //Windowing - could give a choice, fixed for now - MJS
1525  // double mult=0.54-0.46*cos(2*M_PI*(i+(mM-1)/2.0)/(mM-1)); //Hamming
1526  //Blackman
1527  double mult =
1528  0.42 -
1529  0.5 * cos(2 * M_PI * (i + (mM - 1) / 2.0) / (mM - 1)) +
1530  .08 * cos(4 * M_PI * (i + (mM - 1) / 2.0) / (mM - 1));
1531  outr[i] *= mult;
1532  if(i != 0){
1533  outr[mWindowSize - i] *= mult;
1534  }
1535  }
1536  for(; i <= mWindowSize / 2; i++)
1537  { //Padding
1538  outr[i] = 0;
1539  outr[mWindowSize - i] = 0;
1540  }
1541  }
1542  Floats tempr{ mM };
1543  {
1544  size_t i = 0;
1545  for(; i < (mM - 1) / 2; i++)
1546  { //shift so that padding on right
1547  tempr[(mM - 1) / 2 + i] = outr[i];
1548  tempr[i] = outr[mWindowSize - (mM - 1) / 2 + i];
1549  }
1550  tempr[(mM - 1) / 2 + i] = outr[i];
1551  }
1552 
1553  for (size_t i = 0; i < mM; i++)
1554  { //and copy useful values back
1555  outr[i] = tempr[i];
1556  }
1557  for (size_t i = mM; i < mWindowSize; i++)
1558  { //rest is padding
1559  outr[i]=0.;
1560  }
1561 
1562  //Back to the frequency domain so we can use it
1563  RealFFT(mWindowSize, outr.get(), mFilterFuncR.get(), mFilterFuncI.get());
1564 
1565  return TRUE;
1566 }
1567 
1568 void EffectEqualization::Filter(size_t len, float *buffer)
1569 {
1570  float re,im;
1571  // Apply FFT
1572  RealFFTf(buffer, hFFT.get());
1573  //FFT(len, false, inr, NULL, outr, outi);
1574 
1575  // Apply filter
1576  // DC component is purely real
1577  mFFTBuffer[0] = buffer[0] * mFilterFuncR[0];
1578  for(size_t i = 1; i < (len / 2); i++)
1579  {
1580  re=buffer[hFFT->BitReversed[i] ];
1581  im=buffer[hFFT->BitReversed[i]+1];
1582  mFFTBuffer[2*i ] = re*mFilterFuncR[i] - im*mFilterFuncI[i];
1583  mFFTBuffer[2*i+1] = re*mFilterFuncI[i] + im*mFilterFuncR[i];
1584  }
1585  // Fs/2 component is purely real
1586  mFFTBuffer[1] = buffer[1] * mFilterFuncR[len/2];
1587 
1588  // Inverse FFT and normalization
1589  InverseRealFFTf(mFFTBuffer.get(), hFFT.get());
1590  ReorderToTime(hFFT.get(), mFFTBuffer.get(), buffer);
1591 }
1592 
1593 //
1594 // Load external curves with fallback to default, then message
1595 //
1596 void EffectEqualization::LoadCurves(const wxString &fileName, bool append)
1597 {
1598 // We've disabled the XML management of curves.
1599 // Just going via .cfg files now.
1600 #if 1
1601  (void)fileName;
1602  (void)append;
1603  mCurves.clear();
1604  mCurves.push_back( wxT("unnamed") ); // we still need a default curve to use
1605 #else
1606  // Construct normal curve filename
1607  //
1608  // LLL: Wouldn't you know that as of WX 2.6.2, there is a conflict
1609  // between wxStandardPaths and wxConfig under Linux. The latter
1610  // creates a normal file as "$HOME/.audacity", while the former
1611  // expects the ".audacity" portion to be a directory.
1612  // MJS: I don't know what the above means, or if I have broken it.
1613  wxFileName fn;
1614 
1615  if(fileName.empty()) {
1616  // Check if presets are up to date.
1617  wxString eqCurvesCurrentVersion = wxString::Format(wxT("%d.%d"), EQCURVES_VERSION, EQCURVES_REVISION);
1618  wxString eqCurvesInstalledVersion;
1619  gPrefs->Read(GetPrefsPrefix() + "PresetVersion", &eqCurvesInstalledVersion, wxT(""));
1620 
1621  bool needUpdate = (eqCurvesCurrentVersion != eqCurvesInstalledVersion);
1622 
1623  // UpdateDefaultCurves allows us to import NEW factory presets only,
1624  // or update all factory preset curves.
1625  if (needUpdate)
1627  fn = wxFileName( FileNames::DataDir(), wxT("EQCurves.xml") );
1628  }
1629  else
1630  fn = fileName; // user is loading a specific set of curves
1631 
1632  // If requested file doesn't exist...
1633  if( !fn.FileExists() && !GetDefaultFileName(fn) ) {
1634  mCurves.clear();
1635  /* i18n-hint: name of the 'unnamed' custom curve */
1636  mCurves.push_back( _("unnamed") ); // we still need a default curve to use
1637  return;
1638  }
1639 
1640  EQCurve tempCustom(wxT("temp"));
1641  if( append == false ) // Start from scratch
1642  mCurves.clear();
1643  else // appending so copy and remove 'unnamed', to replace later
1644  {
1645  tempCustom.points = mCurves.back().points;
1646  mCurves.pop_back();
1647  }
1648 
1649  // Load the curves
1650  XMLFileReader reader;
1651  const wxString fullPath{ fn.GetFullPath() };
1652  if( !reader.Parse( this, fullPath ) )
1653  {
1654  /* i18n-hint: EQ stands for 'Equalization'.*/
1655  auto msg = XO("Error Loading EQ Curves from file:\n%s\nError message says:\n%s")
1656  .Format( fullPath, reader.GetErrorStr() );
1657  // Inform user of load failure
1659  msg,
1660  wxOK | wxCENTRE,
1661  XO("Error Loading EQ Curves") );
1662  mCurves.push_back( _("unnamed") ); // we always need a default curve to use
1663  return;
1664  }
1665 
1666  // Move "unnamed" to end, if it exists in current language.
1667  int numCurves = mCurves.size();
1668  int curve;
1669  EQCurve tempUnnamed(wxT("tempUnnamed"));
1670  for( curve = 0; curve < numCurves-1; curve++ )
1671  {
1672  if( mCurves[curve].Name == _("unnamed") )
1673  {
1674  tempUnnamed.points = mCurves[curve].points;
1675  mCurves.erase(mCurves.begin() + curve);
1676  mCurves.push_back( _("unnamed") ); // add 'unnamed' back at the end
1677  mCurves.back().points = tempUnnamed.points;
1678  }
1679  }
1680 
1681  if( mCurves.back().Name != _("unnamed") )
1682  mCurves.push_back( _("unnamed") ); // we always need a default curve to use
1683  if( append == true )
1684  {
1685  mCurves.back().points = tempCustom.points;
1686  }
1687 #endif
1688  return;
1689 }
1690 
1691 //
1692 // Update presets to match Audacity version.
1693 //
1694 void EffectEqualization::UpdateDefaultCurves(bool updateAll /* false */)
1695 {
1696  if (mCurves.size() == 0)
1697  return;
1698 
1699  wxString unnamed = wxT("unnamed");
1700 
1701  // Save the "unnamed" curve and remove it so we can add it back as the final curve.
1702  EQCurve userUnnamed(wxT("temp"));
1703  userUnnamed = mCurves.back();
1704  mCurves.pop_back();
1705 
1706  EQCurveArray userCurves = mCurves;
1707  mCurves.clear();
1708  // We only wamt to look for the shipped EQDefaultCurves.xml
1709  wxFileName fn = wxFileName(FileNames::ResourcesDir(), wxT("EQDefaultCurves.xml"));
1710  wxLogDebug(wxT("Attempting to load EQDefaultCurves.xml from %s"),fn.GetFullPath());
1711  XMLFileReader reader;
1712 
1713  if(!reader.Parse(this, fn.GetFullPath())) {
1714  wxLogError(wxT("EQDefaultCurves.xml could not be read."));
1715  return;
1716  }
1717  else {
1718  wxLogDebug(wxT("Loading EQDefaultCurves.xml successful."));
1719  }
1720 
1721  EQCurveArray defaultCurves = mCurves;
1722  mCurves.clear(); // clear now so that we can sort then add back.
1723 
1724  // Remove "unnamed" if it exists.
1725  if (defaultCurves.back().Name == unnamed) {
1726  defaultCurves.pop_back();
1727  }
1728  else {
1729  wxLogError(wxT("Error in EQDefaultCurves.xml"));
1730  }
1731 
1732  int numUserCurves = userCurves.size();
1733  int numDefaultCurves = defaultCurves.size();
1734  EQCurve tempCurve(wxT("test"));
1735 
1736  if (updateAll) {
1737  // Update all factory preset curves.
1738  // Sort and add factory defaults first;
1739  mCurves = defaultCurves;
1740  std::sort(mCurves.begin(), mCurves.end());
1741  // then add remaining user curves:
1742  for (int curveCount = 0; curveCount < numUserCurves; curveCount++) {
1743  bool isCustom = true;
1744  tempCurve = userCurves[curveCount];
1745  // is the name in the default set?
1746  for (int defCurveCount = 0; defCurveCount < numDefaultCurves; defCurveCount++) {
1747  if (tempCurve.Name == mCurves[defCurveCount].Name) {
1748  isCustom = false;
1749  break;
1750  }
1751  }
1752  // if tempCurve is not in the default set, add it to mCurves.
1753  if (isCustom) {
1754  mCurves.push_back(tempCurve);
1755  }
1756  }
1757  }
1758  else {
1759  // Import NEW factory defaults but retain all user modified curves.
1760  for (int defCurveCount = 0; defCurveCount < numDefaultCurves; defCurveCount++) {
1761  bool isUserCurve = false;
1762  // Add if the curve is in the user's set (preserve user's copy)
1763  for (int userCurveCount = 0; userCurveCount < numUserCurves; userCurveCount++) {
1764  if (userCurves[userCurveCount].Name == defaultCurves[defCurveCount].Name) {
1765  isUserCurve = true;
1766  mCurves.push_back(userCurves[userCurveCount]);
1767  break;
1768  }
1769  }
1770  if (!isUserCurve) {
1771  mCurves.push_back(defaultCurves[defCurveCount]);
1772  }
1773  }
1774  std::sort(mCurves.begin(), mCurves.end());
1775  // now add the rest of the user's curves.
1776  for (int userCurveCount = 0; userCurveCount < numUserCurves; userCurveCount++) {
1777  bool isDefaultCurve = false;
1778  tempCurve = userCurves[userCurveCount];
1779  for (int defCurveCount = 0; defCurveCount < numDefaultCurves; defCurveCount++) {
1780  if (tempCurve.Name == defaultCurves[defCurveCount].Name) {
1781  isDefaultCurve = true;
1782  break;
1783  }
1784  }
1785  if (!isDefaultCurve) {
1786  mCurves.push_back(tempCurve);
1787  }
1788  }
1789  }
1790  defaultCurves.clear();
1791  userCurves.clear();
1792 
1793  // Add back old "unnamed"
1794  if(userUnnamed.Name == unnamed) {
1795  mCurves.push_back( userUnnamed ); // we always need a default curve to use
1796  }
1797 
1798  SaveCurves();
1799 
1800  // Write current EqCurve version number
1801  // TODO: Probably better if we used pluginregistry.cfg
1802  wxString eqCurvesCurrentVersion = wxString::Format(wxT("%d.%d"), EQCURVES_VERSION, EQCURVES_REVISION);
1803  gPrefs->Write(GetPrefsPrefix()+"PresetVersion", eqCurvesCurrentVersion);
1804  gPrefs->Flush();
1805 
1806  return;
1807 }
1808 
1809 //
1810 // Get fully qualified filename of EQDefaultCurves.xml
1811 //
1812 bool EffectEqualization::GetDefaultFileName(wxFileName &fileName)
1813 {
1814  // look in data dir first, in case the user has their own defaults (maybe downloaded ones)
1815  fileName = wxFileName( FileNames::DataDir(), wxT("EQDefaultCurves.xml") );
1816  if( !fileName.FileExists() )
1817  { // Default file not found in the data dir. Fall back to Resources dir.
1818  // See http://docs.wxwidgets.org/trunk/classwx_standard_paths.html#5514bf6288ee9f5a0acaf065762ad95d
1819  fileName = wxFileName( FileNames::ResourcesDir(), wxT("EQDefaultCurves.xml") );
1820  }
1821  if( !fileName.FileExists() )
1822  {
1823  // LLL: Is there really a need for an error message at all???
1824  //auto errorMessage = XO("EQCurves.xml and EQDefaultCurves.xml were not found on your system.\nPlease press 'help' to visit the download page.\n\nSave the curves at %s")
1825  // .Format( FileNames::DataDir() );
1826  //ShowErrorDialog(mUIParent, XO("EQCurves.xml and EQDefaultCurves.xml missing"),
1827  // errorMessage, wxT("http://wiki.audacityteam.org/wiki/EQCurvesDownload"), false);
1828 
1829  // Have another go at finding EQCurves.xml in the data dir, in case 'help' helped
1830  fileName = wxFileName( FileNames::DataDir(), wxT("EQDefaultCurves.xml") );
1831  }
1832  return (fileName.FileExists());
1833 }
1834 
1835 
1836 //
1837 // Save curves to external file
1838 //
1839 void EffectEqualization::SaveCurves(const wxString &fileName)
1840 {
1841  wxFileName fn;
1842  if( fileName.empty() )
1843  {
1844  // Construct default curve filename
1845  //
1846  // LLL: Wouldn't you know that as of WX 2.6.2, there is a conflict
1847  // between wxStandardPaths and wxConfig under Linux. The latter
1848  // creates a normal file as "$HOME/.audacity", while the former
1849  // expects the ".audacity" portion to be a directory.
1850  fn = wxFileName( FileNames::DataDir(), wxT("EQCurves.xml") );
1851 
1852  // If the directory doesn't exist...
1853  if( !fn.DirExists() )
1854  {
1855  // Attempt to create it
1856  if( !fn.Mkdir( fn.GetPath(), 511, wxPATH_MKDIR_FULL ) )
1857  {
1858  // MkDir() will emit message
1859  return;
1860  }
1861  }
1862  }
1863  else
1864  fn = fileName;
1865 
1866  GuardedCall( [&] {
1867  // Create/Open the file
1868  const wxString fullPath{ fn.GetFullPath() };
1869  XMLFileWriter eqFile{ fullPath, XO("Error Saving Equalization Curves") };
1870 
1871  // Write the curves
1872  WriteXML( eqFile );
1873 
1874  eqFile.Commit();
1875  } );
1876 }
1877 
1878 //
1879 // Make the passed curve index the active one
1880 //
1881 void EffectEqualization::setCurve(int currentCurve)
1882 {
1883  // Set current choice
1884  wxASSERT( currentCurve < (int) mCurves.size() );
1885  Select(currentCurve);
1886 
1887  Envelope *env;
1888  int numPoints = (int) mCurves[currentCurve].points.size();
1889 
1890  if (mLin) { // linear freq mode
1891  env = mLinEnvelope.get();
1892  }
1893  else { // log freq mode
1894  env = mLogEnvelope.get();
1895  }
1896  env->Flatten(0.);
1897  env->SetTrackLen(1.0);
1898 
1899  // Handle special case of no points.
1900  if (numPoints == 0) {
1901  ForceRecalc();
1902  return;
1903  }
1904 
1905  double when, value;
1906 
1907  // Handle special case 1 point.
1908  if (numPoints == 1) {
1909  // only one point, so ensure it is in range then return.
1910  when = mCurves[currentCurve].points[0].Freq;
1911  if (mLin) {
1912  when = when / mHiFreq;
1913  }
1914  else { // log scale
1915  // We don't go below loFreqI (20 Hz) in log view.
1916  double loLog = log10((double)loFreqI);
1917  double hiLog = log10(mHiFreq);
1918  double denom = hiLog - loLog;
1919  when = (log10(std::max((double) loFreqI, when)) - loLog)/denom;
1920  }
1921  value = mCurves[currentCurve].points[0].dB;
1922  env->Insert(std::min(1.0, std::max(0.0, when)), value);
1923  ForceRecalc();
1924  return;
1925  }
1926 
1927  // We have at least two points, so ensure they are in frequency order.
1928  std::sort(mCurves[currentCurve].points.begin(),
1929  mCurves[currentCurve].points.end());
1930 
1931  if (mCurves[currentCurve].points[0].Freq < 0) {
1932  // Corrupt or invalid curve, so bail.
1933  ForceRecalc();
1934  return;
1935  }
1936 
1937  if(mLin) { // linear Hz scale
1938  for(int pointCount = 0; pointCount < numPoints; pointCount++) {
1939  when = mCurves[currentCurve].points[pointCount].Freq / mHiFreq;
1940  value = mCurves[currentCurve].points[pointCount].dB;
1941  if(when <= 1) {
1942  env->Insert(when, value);
1943  if (when == 1)
1944  break;
1945  }
1946  else {
1947  // There are more points at higher freqs,
1948  // so interpolate next one then stop.
1949  when = 1.0;
1950  double nextDB = mCurves[currentCurve].points[pointCount].dB;
1951  if (pointCount > 0) {
1952  double nextF = mCurves[currentCurve].points[pointCount].Freq;
1953  double lastF = mCurves[currentCurve].points[pointCount-1].Freq;
1954  double lastDB = mCurves[currentCurve].points[pointCount-1].dB;
1955  value = lastDB +
1956  ((nextDB - lastDB) *
1957  ((mHiFreq - lastF) / (nextF - lastF)));
1958  }
1959  else
1960  value = nextDB;
1961  env->Insert(when, value);
1962  break;
1963  }
1964  }
1965  }
1966  else { // log Hz scale
1967  double loLog = log10((double) loFreqI);
1968  double hiLog = log10(mHiFreq);
1969  double denom = hiLog - loLog;
1970  int firstAbove20Hz;
1971 
1972  // log scale EQ starts at 20 Hz (threshold of hearing).
1973  // so find the first point (if any) above 20 Hz.
1974  for (firstAbove20Hz = 0; firstAbove20Hz < numPoints; firstAbove20Hz++) {
1975  if (mCurves[currentCurve].points[firstAbove20Hz].Freq > loFreqI)
1976  break;
1977  }
1978 
1979  if (firstAbove20Hz == numPoints) {
1980  // All points below 20 Hz, so just use final point.
1981  when = 0.0;
1982  value = mCurves[currentCurve].points[numPoints-1].dB;
1983  env->Insert(when, value);
1984  ForceRecalc();
1985  return;
1986  }
1987 
1988  if (firstAbove20Hz > 0) {
1989  // At least one point is before 20 Hz and there are more
1990  // beyond 20 Hz, so interpolate the first
1991  double prevF = mCurves[currentCurve].points[firstAbove20Hz-1].Freq;
1992  prevF = log10(std::max(1.0, prevF)); // log zero is bad.
1993  double prevDB = mCurves[currentCurve].points[firstAbove20Hz-1].dB;
1994  double nextF = log10(mCurves[currentCurve].points[firstAbove20Hz].Freq);
1995  double nextDB = mCurves[currentCurve].points[firstAbove20Hz].dB;
1996  when = 0.0;
1997  value = nextDB - ((nextDB - prevDB) * ((nextF - loLog) / (nextF - prevF)));
1998  env->Insert(when, value);
1999  }
2000 
2001  // Now get the rest.
2002  for(int pointCount = firstAbove20Hz; pointCount < numPoints; pointCount++)
2003  {
2004  double flog = log10(mCurves[currentCurve].points[pointCount].Freq);
2005  wxASSERT(mCurves[currentCurve].points[pointCount].Freq >= loFreqI);
2006 
2007  when = (flog - loLog)/denom;
2008  value = mCurves[currentCurve].points[pointCount].dB;
2009  if(when <= 1.0) {
2010  env->Insert(when, value);
2011  }
2012  else {
2013  // This looks weird when adjusting curve in Draw mode if
2014  // there is a point off-screen.
2015 
2016  /*
2017  // we have a point beyond fs/2. Insert it so that env code can use it.
2018  // but just this one, we have no use for the rest
2019  env->SetTrackLen(when); // can't Insert if the envelope isn't long enough
2020  env->Insert(when, value);
2021  break;
2022  */
2023 
2024  // interpolate the final point instead
2025  when = 1.0;
2026  if (pointCount > 0) {
2027  double lastDB = mCurves[currentCurve].points[pointCount-1].dB;
2028  double logLastF =
2029  log10(mCurves[currentCurve].points[pointCount-1].Freq);
2030  value = lastDB +
2031  ((value - lastDB) *
2032  ((log10(mHiFreq) - logLastF) / (flog - logLastF)));
2033  }
2034  env->Insert(when, value);
2035  break;
2036  }
2037  }
2038  }
2039  ForceRecalc();
2040 }
2041 
2043 {
2044  setCurve((int) mCurves.size() - 1);
2045 }
2046 
2047 void EffectEqualization::setCurve(const wxString &curveName)
2048 {
2049  unsigned i = 0;
2050  for( i = 0; i < mCurves.size(); i++ )
2051  if( curveName == mCurves[ i ].Name )
2052  break;
2053  if( i == mCurves.size())
2054  {
2056  XO("Requested curve not found, using 'unnamed'"),
2057  wxOK|wxICON_ERROR,
2058  XO("Curve not found") );
2059  setCurve();
2060  }
2061  else
2062  setCurve( i );
2063 }
2064 
2065 //
2066 // Set NEW curve selection (safe to call outside of the UI)
2067 //
2069 {
2070  // Set current choice
2071  if (mCurve)
2072  {
2073  mCurve->SetSelection( curve );
2074  mCurveName = mCurves[ curve ].Name;
2075  }
2076 }
2077 
2078 //
2079 // Tell panel to recalc (safe to call outside of UI)
2080 //
2082 {
2083  if (mPanel)
2084  {
2085  mPanel->ForceRecalc();
2086  }
2087 }
2088 
2089 //
2090 // Capture updated envelope
2091 //
2093 {
2094  if (IsLinear())
2095  {
2096  EnvelopeUpdated(mLinEnvelope.get(), true);
2097  }
2098  else
2099  {
2100  EnvelopeUpdated(mLogEnvelope.get(), false);
2101  }
2102 }
2103 
2105 {
2106  // Allocate and populate point arrays
2107  size_t numPoints = env->GetNumberOfPoints();
2108  Doubles when{ numPoints };
2109  Doubles value{ numPoints };
2110  env->GetPoints( when.get(), value.get(), numPoints );
2111 
2112  // Clear the unnamed curve
2113  int curve = mCurves.size() - 1;
2114  mCurves[ curve ].points.clear();
2115 
2116  if(lin)
2117  {
2118  // Copy and convert points
2119  for (size_t point = 0; point < numPoints; point++)
2120  {
2121  double freq = when[ point ] * mHiFreq;
2122  double db = value[ point ];
2123 
2124  // Add it to the curve
2125  mCurves[ curve ].points.push_back( EQPoint( freq, db ) );
2126  }
2127  }
2128  else
2129  {
2130  double loLog = log10( 20. );
2131  double hiLog = log10( mHiFreq );
2132  double denom = hiLog - loLog;
2133 
2134  // Copy and convert points
2135  for (size_t point = 0; point < numPoints; point++)
2136  {
2137  double freq = pow( 10., ( ( when[ point ] * denom ) + loLog ));
2138  double db = value[ point ];
2139 
2140  // Add it to the curve
2141  mCurves[ curve ].points.push_back( EQPoint( freq, db ) );
2142  }
2143  }
2144  // Remember that we've updated the unnamed curve
2145  mDirty = true;
2146 
2147  // set 'unnamed' as the selected curve
2148  Select( (int) mCurves.size() - 1 );
2149 }
2150 
2151 //
2152 //
2153 //
2155 {
2156  return mDrawMode && mLin;
2157 }
2158 
2159 //
2160 // Flatten the curve
2161 //
2163 {
2164  mLogEnvelope->Flatten(0.);
2165  mLogEnvelope->SetTrackLen(1.0);
2166  mLinEnvelope->Flatten(0.);
2167  mLinEnvelope->SetTrackLen(1.0);
2168  ForceRecalc();
2169  if( !mDrawMode )
2170  {
2171  for( size_t i = 0; i < mBandsInUse; i++)
2172  {
2173  mSliders[i]->SetValue(0);
2174  mSlidersOld[i] = 0;
2175  mEQVals[i] = 0.;
2176 
2177  wxString tip;
2178  if( kThirdOct[i] < 1000.)
2179  tip.Printf( wxT("%dHz\n%.1fdB"), (int)kThirdOct[i], 0. );
2180  else
2181  tip.Printf( wxT("%gkHz\n%.1fdB"), kThirdOct[i]/1000., 0. );
2182  mSliders[i]->SetToolTip(tip);
2183  }
2184  }
2185  EnvelopeUpdated();
2186 }
2187 
2188 //
2189 // Process XML tags and handle the ones we recognize
2190 //
2191 bool EffectEqualization::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
2192 {
2193  // May want to add a version strings...
2194  if( !wxStrcmp( tag, wxT("equalizationeffect") ) )
2195  {
2196  return true;
2197  }
2198 
2199  // Located a NEW curve
2200  if( !wxStrcmp(tag, wxT("curve") ) )
2201  {
2202  // Process the attributes
2203  while( *attrs )
2204  {
2205  // Cache attr/value and bump to next
2206  const wxChar *attr = *attrs++;
2207  const wxChar *value = *attrs++;
2208 
2209  // Create a NEW curve and name it
2210  if( !wxStrcmp( attr, wxT("name") ) )
2211  {
2212  const wxString strValue = value;
2213  if (!XMLValueChecker::IsGoodString(strValue))
2214  return false;
2215  // check for a duplicate name and add (n) if there is one
2216  int n = 0;
2217  wxString strValueTemp = strValue;
2218  bool exists;
2219  do
2220  {
2221  exists = false;
2222  for(size_t i = 0; i < mCurves.size(); i++)
2223  {
2224  if(n>0)
2225  strValueTemp.Printf(wxT("%s (%d)"),strValue,n);
2226  if(mCurves[i].Name == strValueTemp)
2227  {
2228  exists = true;
2229  break;
2230  }
2231  }
2232  n++;
2233  }
2234  while(exists == true);
2235 
2236  mCurves.push_back( EQCurve( strValueTemp ) );
2237  }
2238  }
2239 
2240  // Tell caller it was processed
2241  return true;
2242  }
2243 
2244  // Located a NEW point
2245  if( !wxStrcmp( tag, wxT("point") ) )
2246  {
2247  // Set defaults in case attributes are missing
2248  double f = 0.0;
2249  double d = 0.0;
2250 
2251  // Process the attributes
2252  double dblValue;
2253  while( *attrs )
2254  { // Cache attr/value and bump to next
2255  const wxChar *attr = *attrs++;
2256  const wxChar *value = *attrs++;
2257 
2258  const wxString strValue = value;
2259 
2260  // Get the frequency
2261  if( !wxStrcmp( attr, wxT("f") ) )
2262  {
2263  if (!XMLValueChecker::IsGoodString(strValue) ||
2264  !Internat::CompatibleToDouble(strValue, &dblValue))
2265  return false;
2266  f = dblValue;
2267  }
2268  // Get the dB
2269  else if( !wxStrcmp( attr, wxT("d") ) )
2270  {
2271  if (!XMLValueChecker::IsGoodString(strValue) ||
2272  !Internat::CompatibleToDouble(strValue, &dblValue))
2273  return false;
2274  d = dblValue;
2275  }
2276  }
2277 
2278  // Create a NEW point
2279  mCurves[ mCurves.size() - 1 ].points.push_back( EQPoint( f, d ) );
2280 
2281  // Tell caller it was processed
2282  return true;
2283  }
2284 
2285  // Tell caller we didn't understand the tag
2286  return false;
2287 }
2288 
2289 //
2290 // Return handler for recognized tags
2291 //
2293 {
2294  if( !wxStrcmp( tag, wxT("equalizationeffect") ) )
2295  {
2296  return this;
2297  }
2298 
2299  if( !wxStrcmp( tag, wxT("curve") ) )
2300  {
2301  return this;
2302  }
2303 
2304  if( !wxStrcmp( tag, wxT("point") ) )
2305  {
2306  return this;
2307  }
2308 
2309  return NULL;
2310 }
2311 
2312 //
2313 // Write all of the curves to the XML file
2314 //
2316 // may throw
2317 {
2318  // Start our hierarchy
2319  xmlFile.StartTag( wxT( "equalizationeffect" ) );
2320 
2321  // Write all curves
2322  int numCurves = mCurves.size();
2323  int curve;
2324  for( curve = 0; curve < numCurves; curve++ )
2325  {
2326  // Start a NEW curve
2327  xmlFile.StartTag( wxT( "curve" ) );
2328  xmlFile.WriteAttr( wxT( "name" ), mCurves[ curve ].Name );
2329 
2330  // Write all points
2331  int numPoints = mCurves[ curve ].points.size();
2332  int point;
2333  for( point = 0; point < numPoints; point++ )
2334  {
2335  // Write NEW point
2336  xmlFile.StartTag( wxT( "point" ) );
2337  xmlFile.WriteAttr( wxT( "f" ), mCurves[ curve ].points[ point ].Freq, 12 );
2338  xmlFile.WriteAttr( wxT( "d" ), mCurves[ curve ].points[ point ].dB, 12 );
2339  xmlFile.EndTag( wxT( "point" ) );
2340  }
2341 
2342  // Terminate curve
2343  xmlFile.EndTag( wxT( "curve" ) );
2344  }
2345 
2346  // Terminate our hierarchy
2347  xmlFile.EndTag( wxT( "equalizationeffect" ) );
2348 }
2349 
2351 //
2352 // All EffectEqualization methods beyond this point interact with the UI, so
2353 // can't be called while the UI is not displayed.
2354 //
2356 
2358 {
2359 
2360  // Reload the curve names
2361  if( mCurve )
2362  mCurve->Clear();
2363  bool selectedCurveExists = false;
2364  for (size_t i = 0, cnt = mCurves.size(); i < cnt; i++)
2365  {
2366  if (mCurveName == mCurves[ i ].Name)
2367  selectedCurveExists = true;
2368  if( mCurve )
2369  mCurve->Append(mCurves[ i ].Name);
2370  }
2371  // In rare circumstances, mCurveName may not exist (bug 1891)
2372  if (!selectedCurveExists)
2373  mCurveName = mCurves[ (int)mCurves.size() - 1 ].Name;
2374  if( mCurve )
2375  mCurve->SetStringSelection(mCurveName);
2376 
2377  // Allow the control to resize
2378  if( mCurve )
2379  mCurve->SetMinSize({-1, -1});
2380 
2381  // Set initial curve
2382  setCurve( mCurveName );
2383 }
2384 
2386 {
2387  size_t numPoints = mLogEnvelope->GetNumberOfPoints();
2388  Doubles when{ numPoints };
2389  Doubles value{ numPoints };
2390  double deltadB = 0.1;
2391  double dx, dy, dx1, dy1, err;
2392 
2393  mLogEnvelope->GetPoints( when.get(), value.get(), numPoints );
2394 
2395  // set 'unnamed' as the selected curve
2396  EnvelopeUpdated();
2397 
2398  bool flag = true;
2399  while (flag)
2400  {
2401  flag = false;
2402  int numDeleted = 0;
2403  mLogEnvelope->GetPoints( when.get(), value.get(), numPoints );
2404  for (size_t j = 0; j + 2 < numPoints; j++)
2405  {
2406  dx = when[j+2+numDeleted] - when[j+numDeleted];
2407  dy = value[j+2+numDeleted] - value[j+numDeleted];
2408  dx1 = when[j+numDeleted+1] - when[j+numDeleted];
2409  dy1 = dy * dx1 / dx;
2410  err = fabs(value[j+numDeleted+1] - (value[j+numDeleted] + dy1));
2411  if( err < deltadB )
2412  { // within < deltadB dB?
2413  mLogEnvelope->Delete(j+1);
2414  numPoints--;
2415  numDeleted++;
2416  flag = true;
2417  }
2418  }
2419  }
2420 
2421  if(mLin) // do not use IsLinear() here
2422  {
2423  EnvLogToLin();
2424  mEnvelope = mLinEnvelope.get();
2425  mFreqRuler->ruler.SetLog(false);
2427  }
2428 
2429  szrV->Show(szrG,false);
2430  szrH->Show(szrI,false);
2431  szrH->Show(szrL,true);
2432 
2433  mUIParent->Layout();
2434  wxGetTopLevelParent(mUIParent)->Layout();
2435  ForceRecalc(); // it may have changed slightly due to the deletion of points
2436 }
2437 
2439 {
2440  double loLog = log10(mLoFreq);
2441  double hiLog = log10(mHiFreq);
2442  double denom = hiLog - loLog;
2443 
2444  if(mLin) //going from lin to log freq scale - do not use IsLinear() here
2445  { // add some extra points to the linear envelope for the graphic to follow
2446  double step = pow(2., 1./12.); // twelve steps per octave
2447  double when,value;
2448  for(double freq=10.; freq<mHiFreq; freq*=step)
2449  {
2450  when = freq/mHiFreq;
2451  value = mLinEnvelope->GetValue(when);
2452  mLinEnvelope->Insert(when, value);
2453  }
2454 
2455  EnvLinToLog();
2456  mEnvelope = mLogEnvelope.get();
2457  mFreqRuler->ruler.SetLog(true);
2459  }
2460 
2461  for (size_t i = 0; i < mBandsInUse; i++)
2462  {
2463  if( kThirdOct[i] == mLoFreq )
2464  mWhenSliders[i] = 0.;
2465  else
2466  mWhenSliders[i] = (log10(kThirdOct[i])-loLog)/denom;
2467  mEQVals[i] = mLogEnvelope->GetValue(mWhenSliders[i]); //set initial values of sliders
2468  if( mEQVals[i] > 20.)
2469  mEQVals[i] = 20.;
2470  if( mEQVals[i] < -20.)
2471  mEQVals[i] = -20.;
2472  }
2473  ErrMin(); //move sliders to minimise error
2474  for (size_t i = 0; i < mBandsInUse; i++)
2475  {
2476  mSliders[i]->SetValue(lrint(mEQVals[i])); //actually set slider positions
2477  mSlidersOld[i] = mSliders[i]->GetValue();
2478  wxString tip;
2479  if( kThirdOct[i] < 1000.)
2480  tip.Printf( wxT("%dHz\n%.1fdB"), (int)kThirdOct[i], mEQVals[i] );
2481  else
2482  tip.Printf( wxT("%gkHz\n%.1fdB"), kThirdOct[i]/1000., mEQVals[i] );
2483  mSliders[i]->SetToolTip(tip);
2484  }
2485 
2486  szrV->Show(szrG,true); // eq sliders
2487  szrH->Show(szrI,mOptions == kEqLegacy ); // interpolation choice
2488  szrH->Show(szrL,false); // linear freq checkbox
2489 
2490  mUIParent->Layout();
2491  wxGetTopLevelParent(mUIParent)->Layout();
2492  mUIParent->Layout();
2493  wxGetTopLevelParent(mUIParent)->Layout();
2494 
2495  GraphicEQ(mLogEnvelope.get());
2496  mDrawMode = false;
2497 }
2498 
2500 {
2501  size_t numPoints = mLogEnvelope->GetNumberOfPoints();
2502  if( numPoints == 0 )
2503  {
2504  return;
2505  }
2506 
2507  Doubles when{ numPoints };
2508  Doubles value{ numPoints };
2509 
2510  mLinEnvelope->Flatten(0.);
2511  mLinEnvelope->SetTrackLen(1.0);
2512  mLogEnvelope->GetPoints( when.get(), value.get(), numPoints );
2513  mLinEnvelope->Reassign(0., value[0]);
2514  double loLog = log10(20.);
2515  double hiLog = log10(mHiFreq);
2516  double denom = hiLog - loLog;
2517 
2518  for (size_t i = 0; i < numPoints; i++)
2519  mLinEnvelope->Insert(pow( 10., ((when[i] * denom) + loLog))/mHiFreq , value[i]);
2520  mLinEnvelope->Reassign(1., value[numPoints-1]);
2521 }
2522 
2524 {
2525  size_t numPoints = mLinEnvelope->GetNumberOfPoints();
2526  if( numPoints == 0 )
2527  {
2528  return;
2529  }
2530 
2531  Doubles when{ numPoints };
2532  Doubles value{ numPoints };
2533 
2534  mLogEnvelope->Flatten(0.);
2535  mLogEnvelope->SetTrackLen(1.0);
2536  mLinEnvelope->GetPoints( when.get(), value.get(), numPoints );
2537  mLogEnvelope->Reassign(0., value[0]);
2538  double loLog = log10(20.);
2539  double hiLog = log10(mHiFreq);
2540  double denom = hiLog - loLog;
2541  bool changed = false;
2542 
2543  for (size_t i = 0; i < numPoints; i++)
2544  {
2545  if( when[i]*mHiFreq >= 20 )
2546  {
2547  // Caution: on Linux, when when == 20, the log calculation rounds
2548  // to just under zero, which causes an assert error.
2549  double flog = (log10(when[i]*mHiFreq)-loLog)/denom;
2550  mLogEnvelope->Insert(std::max(0.0, flog) , value[i]);
2551  }
2552  else
2553  { //get the first point as close as we can to the last point requested
2554  changed = true;
2555  double v = value[i];
2556  mLogEnvelope->Insert(0., v);
2557  }
2558  }
2559  mLogEnvelope->Reassign(1., value[numPoints - 1]);
2560 
2561  if(changed)
2562  EnvelopeUpdated(mLogEnvelope.get(), false);
2563 }
2564 
2566 {
2567  double vals[NUM_PTS];
2568  double error = 0.0;
2569  double oldError = 0.0;
2570  double mEQValsOld = 0.0;
2571  double correction = 1.6;
2572  bool flag;
2573  size_t j=0;
2574  Envelope testEnvelope{ *mLogEnvelope };
2575 
2576  for(size_t i = 0; i < NUM_PTS; i++)
2577  vals[i] = testEnvelope.GetValue(mWhens[i]);
2578 
2579  // Do error minimisation
2580  error = 0.;
2581  GraphicEQ(&testEnvelope);
2582  for(size_t i = 0; i < NUM_PTS; i++) //calc initial error
2583  {
2584  double err = vals[i] - testEnvelope.GetValue(mWhens[i]);
2585  error += err*err;
2586  }
2587  oldError = error;
2588  while( j < mBandsInUse*12 ) //loop over the sliders a number of times
2589  {
2590  auto i = j % mBandsInUse; //use this slider
2591  if( (j > 0) & (i == 0) ) // if we've come back to the first slider again...
2592  {
2593  if( correction > 0 )
2594  correction = -correction; //go down
2595  else
2596  correction = -correction/2.; //go up half as much
2597  }
2598  flag = true; // check if we've hit the slider limit
2599  do
2600  {
2601  oldError = error;
2602  mEQValsOld = mEQVals[i];
2603  mEQVals[i] += correction; //move fader value
2604  if( mEQVals[i] > 20. )
2605  {
2606  mEQVals[i] = 20.;
2607  flag = false;
2608  }
2609  if( mEQVals[i] < -20. )
2610  {
2611  mEQVals[i] = -20.;
2612  flag = false;
2613  }
2614  GraphicEQ(&testEnvelope); //calculate envelope
2615  error = 0.;
2616  for(size_t k = 0; k < NUM_PTS; k++) //calculate error
2617  {
2618  double err = vals[k] - testEnvelope.GetValue(mWhens[k]);
2619  error += err*err;
2620  }
2621  }
2622  while( (error < oldError) && flag );
2623  if( error > oldError )
2624  {
2625  mEQVals[i] = mEQValsOld; //last one didn't work
2626  error = oldError;
2627  }
2628  else
2629  oldError = error;
2630  if( error < .0025 * mBandsInUse)
2631  break; // close enuff
2632  j++; //try next slider
2633  }
2634  if( error > .0025 * mBandsInUse ) // not within 0.05dB on each slider, on average
2635  {
2636  Select( (int) mCurves.size() - 1 );
2637  EnvelopeUpdated(&testEnvelope, false);
2638  }
2639 }
2640 
2642 {
2643  // JKC: 'value' is for height of curve.
2644  // The 0.0 initial value would only get used if NUM_PTS were 0.
2645  double value = 0.0;
2646  double dist, span, s;
2647 
2648  env->Flatten(0.);
2649  env->SetTrackLen(1.0);
2650 
2651  switch( mInterp )
2652  {
2653  case kBspline: // B-spline
2654  {
2655  int minF = 0;
2656  for(size_t i = 0; i < NUM_PTS; i++)
2657  {
2658  while( (mWhenSliders[minF] <= mWhens[i]) & (minF < (int)mBandsInUse) )
2659  minF++;
2660  minF--;
2661  if( minF < 0 ) //before first slider
2662  {
2663  dist = mWhens[i] - mWhenSliders[0];
2664  span = mWhenSliders[1] - mWhenSliders[0];
2665  s = dist/span;
2666  if( s < -1.5 )
2667  value = 0.;
2668  else if( s < -.5 )
2669  value = mEQVals[0]*(s + 1.5)*(s + 1.5)/2.;
2670  else
2671  value = mEQVals[0]*(.75 - s*s) + mEQVals[1]*(s + .5)*(s + .5)/2.;
2672  }
2673  else
2674  {
2675  if( mWhens[i] > mWhenSliders[mBandsInUse-1] ) //after last fader
2676  {
2677  dist = mWhens[i] - mWhenSliders[mBandsInUse-1];
2679  s = dist/span;
2680  if( s > 1.5 )
2681  value = 0.;
2682  else if( s > .5 )
2683  value = mEQVals[mBandsInUse-1]*(s - 1.5)*(s - 1.5)/2.;
2684  else
2685  value = mEQVals[mBandsInUse-1]*(.75 - s*s) +
2686  mEQVals[mBandsInUse-2]*(s - .5)*(s - .5)/2.;
2687  }
2688  else //normal case
2689  {
2690  dist = mWhens[i] - mWhenSliders[minF];
2691  span = mWhenSliders[minF+1] - mWhenSliders[minF];
2692  s = dist/span;
2693  if(s < .5 )
2694  {
2695  value = mEQVals[minF]*(0.75 - s*s);
2696  if( minF+1 < (int)mBandsInUse )
2697  value += mEQVals[minF+1]*(s+.5)*(s+.5)/2.;
2698  if( minF-1 >= 0 )
2699  value += mEQVals[minF-1]*(s-.5)*(s-.5)/2.;
2700  }
2701  else
2702  {
2703  value = mEQVals[minF]*(s-1.5)*(s-1.5)/2.;
2704  if( minF+1 < (int)mBandsInUse )
2705  value += mEQVals[minF+1]*(.75-(1.-s)*(1.-s));
2706  if( minF+2 < (int)mBandsInUse )
2707  value += mEQVals[minF+2]*(s-.5)*(s-.5)/2.;
2708  }
2709  }
2710  }
2711  if(mWhens[i]<=0.)
2712  env->Reassign(0., value);
2713  env->Insert( mWhens[i], value );
2714  }
2715  env->Reassign( 1., value );
2716  break;
2717  }
2718 
2719  case kCosine: // Cosine squared
2720  {
2721  int minF = 0;
2722  for(size_t i = 0; i < NUM_PTS; i++)
2723  {
2724  while( (mWhenSliders[minF] <= mWhens[i]) & (minF < (int)mBandsInUse) )
2725  minF++;
2726  minF--;
2727  if( minF < 0 ) //before first slider
2728  {
2729  dist = mWhenSliders[0] - mWhens[i];
2730  span = mWhenSliders[1] - mWhenSliders[0];
2731  if( dist < span )
2732  value = mEQVals[0]*(1. + cos(M_PI*dist/span))/2.;
2733  else
2734  value = 0.;
2735  }
2736  else
2737  {
2738  if( mWhens[i] > mWhenSliders[mBandsInUse-1] ) //after last fader
2739  {
2741  dist = mWhens[i] - mWhenSliders[mBandsInUse-1];
2742  if( dist < span )
2743  value = mEQVals[mBandsInUse-1]*(1. + cos(M_PI*dist/span))/2.;
2744  else
2745  value = 0.;
2746  }
2747  else //normal case
2748  {
2749  span = mWhenSliders[minF+1] - mWhenSliders[minF];
2750  dist = mWhenSliders[minF+1] - mWhens[i];
2751  value = mEQVals[minF]*(1. + cos(M_PI*(span-dist)/span))/2. +
2752  mEQVals[minF+1]*(1. + cos(M_PI*dist/span))/2.;
2753  }
2754  }
2755  if(mWhens[i]<=0.)
2756  env->Reassign(0., value);
2757  env->Insert( mWhens[i], value );
2758  }
2759  env->Reassign( 1., value );
2760  break;
2761  }
2762 
2763  case kCubic: // Cubic Spline
2764  {
2765  double y2[NUMBER_OF_BANDS+1];
2768  for(double xf=0; xf<1.; xf+=1./NUM_PTS)
2769  {
2770  env->Insert(xf, splint(mWhenSliders, mEQVals, mBandsInUse+1, y2, xf));
2771  }
2772  break;
2773  }
2774  }
2775 
2776  ForceRecalc();
2777 }
2778 
2779 void EffectEqualization::spline(double x[], double y[], size_t n, double y2[])
2780 {
2781  wxASSERT( n > 0 );
2782 
2783  double p, sig;
2784  Doubles u{ n };
2785 
2786  y2[0] = 0.; //
2787  u[0] = 0.; //'natural' boundary conditions
2788  for (size_t i = 1; i + 1 < n; i++)
2789  {
2790  sig = ( x[i] - x[i-1] ) / ( x[i+1] - x[i-1] );
2791  p = sig * y2[i-1] + 2.;
2792  y2[i] = (sig - 1.)/p;
2793  u[i] = ( y[i+1] - y[i] ) / ( x[i+1] - x[i] ) - ( y[i] - y[i-1] ) / ( x[i] - x[i-1] );
2794  u[i] = (6.*u[i]/( x[i+1] - x[i-1] ) - sig * u[i-1]) / p;
2795  }
2796  y2[n - 1] = 0.;
2797  for (size_t i = n - 1; i--;)
2798  y2[i] = y2[i]*y2[i+1] + u[i];
2799 }
2800 
2801 double EffectEqualization::splint(double x[], double y[], size_t n, double y2[], double xr)
2802 {
2803  wxASSERT( n > 1 );
2804 
2805  double a, b, h;
2806  static double xlast = 0.; // remember last x value requested
2807  static size_t k = 0; // and which interval we were in
2808 
2809  if( xr < xlast )
2810  k = 0; // gone back to start, (or somewhere to the left)
2811  xlast = xr;
2812  while( (x[k] <= xr) && (k + 1 < n) )
2813  k++;
2814  wxASSERT( k > 0 );
2815  k--;
2816  h = x[k+1] - x[k];
2817  a = ( x[k+1] - xr )/h;
2818  b = (xr - x[k])/h;
2819  return( a*y[k]+b*y[k+1]+((a*a*a-a)*y2[k]+(b*b*b-b)*y2[k+1])*h*h/6.);
2820 }
2821 
2823 {
2824 }
2825 
2826 void EffectEqualization::OnSize(wxSizeEvent & event)
2827 {
2828  mUIParent->Layout();
2829  event.Skip();
2830 }
2831 
2832 void EffectEqualization::OnSlider(wxCommandEvent & event)
2833 {
2834  wxSlider *s = (wxSlider *)event.GetEventObject();
2835  for (size_t i = 0; i < mBandsInUse; i++)
2836  {
2837  if( s == mSliders[i])
2838  {
2839  int posn = mSliders[i]->GetValue();
2840  if( wxGetKeyState(WXK_SHIFT) )
2841  {
2842  if( posn > mSlidersOld[i] )
2843  mEQVals[i] += (float).1;
2844  else
2845  if( posn < mSlidersOld[i] )
2846  mEQVals[i] -= .1f;
2847  }
2848  else
2849  mEQVals[i] += (posn - mSlidersOld[i]);
2850  if( mEQVals[i] > 20. )
2851  mEQVals[i] = 20.;
2852  if( mEQVals[i] < -20. )
2853  mEQVals[i] = -20.;
2854  int newPosn = (int)mEQVals[i];
2855  mSliders[i]->SetValue( newPosn );
2856  mSlidersOld[i] = newPosn;
2857  wxString tip;
2858  if( kThirdOct[i] < 1000.)
2859  tip.Printf( wxT("%dHz\n%.1fdB"), (int)kThirdOct[i], mEQVals[i] );
2860  else
2861  tip.Printf( wxT("%gkHz\n%.1fdB"), kThirdOct[i]/1000., mEQVals[i] );
2862  s->SetToolTip(tip);
2863  break;
2864  }
2865  }
2866  GraphicEQ(mLogEnvelope.get());
2867  EnvelopeUpdated();
2868 }
2869 
2870 void EffectEqualization::OnInterp(wxCommandEvent & WXUNUSED(event))
2871 {
2872  bool bIsGraphic = !mDrawMode;
2873  if (bIsGraphic)
2874  {
2875  GraphicEQ(mLogEnvelope.get());
2876  EnvelopeUpdated();
2877  }
2878  mInterp = mInterpChoice->GetSelection();
2879 }
2880 
2881 void EffectEqualization::OnDrawMode(wxCommandEvent & WXUNUSED(event))
2882 {
2883  mDrawMode = true;
2884  UpdateDraw();
2885 }
2886 
2887 void EffectEqualization::OnGraphicMode(wxCommandEvent & WXUNUSED(event))
2888 {
2889  mDrawMode = false;
2890  UpdateGraphic();
2891 }
2892 
2893 void EffectEqualization::OnSliderM(wxCommandEvent & WXUNUSED(event))
2894 {
2896  ForceRecalc();
2897 }
2898 
2899 void EffectEqualization::OnSliderDBMIN(wxCommandEvent & WXUNUSED(event))
2900 {
2902 }
2903 
2904 void EffectEqualization::OnSliderDBMAX(wxCommandEvent & WXUNUSED(event))
2905 {
2907 }
2908 
2909 //
2910 // New curve was selected
2911 //
2912 void EffectEqualization::OnCurve(wxCommandEvent & WXUNUSED(event))
2913 {
2914  // Select NEW curve
2915  wxASSERT( mCurve != NULL );
2916  setCurve( mCurve->GetCurrentSelection() );
2917  if( !mDrawMode )
2918  UpdateGraphic();
2919 }
2920 
2921 //
2922 // User wants to modify the list in some way
2923 //
2924 void EffectEqualization::OnManage(wxCommandEvent & WXUNUSED(event))
2925 {
2926  EditCurvesDialog d(mUIParent, this, mCurve->GetSelection());
2927  d.ShowModal();
2928 
2929  // Reload the curve names
2930  UpdateCurves();
2931 
2932  // Allow control to resize
2933  mUIParent->Layout();
2934 }
2935 
2936 void EffectEqualization::OnClear(wxCommandEvent & WXUNUSED(event))
2937 {
2938  Flatten();
2939 }
2940 
2941 void EffectEqualization::OnInvert(wxCommandEvent & WXUNUSED(event)) // Inverts any curve
2942 {
2943  if(!mDrawMode) // Graphic (Slider) mode. Invert the sliders.
2944  {
2945  for (size_t i = 0; i < mBandsInUse; i++)
2946  {
2947  mEQVals[i] = -mEQVals[i];
2948  int newPosn = (int)mEQVals[i];
2949  mSliders[i]->SetValue( newPosn );
2950  mSlidersOld[i] = newPosn;
2951 
2952  wxString tip;
2953  if( kThirdOct[i] < 1000.)
2954  tip.Printf( wxT("%dHz\n%.1fdB"), (int)kThirdOct[i], mEQVals[i] );
2955  else
2956  tip.Printf( wxT("%gkHz\n%.1fdB"), kThirdOct[i]/1000., mEQVals[i] );
2957  mSliders[i]->SetToolTip(tip);
2958  }
2959  GraphicEQ(mLogEnvelope.get());
2960  }
2961  else // Draw mode. Invert the points.
2962  {
2963  bool lin = IsLinear(); // refers to the 'log' or 'lin' of the frequency scale, not the amplitude
2964  size_t numPoints; // number of points in the curve/envelope
2965 
2966  // determine if log or lin curve is the current one
2967  // and find out how many points are in the curve
2968  if(lin) // lin freq scale and so envelope
2969  {
2970  numPoints = mLinEnvelope->GetNumberOfPoints();
2971  }
2972  else
2973  {
2974  numPoints = mLogEnvelope->GetNumberOfPoints();
2975  }
2976 
2977  if( numPoints == 0 )
2978  return;
2979 
2980  Doubles when{ numPoints };
2981  Doubles value{ numPoints };
2982 
2983  if(lin)
2984  mLinEnvelope->GetPoints( when.get(), value.get(), numPoints );
2985  else
2986  mLogEnvelope->GetPoints( when.get(), value.get(), numPoints );
2987 
2988  // invert the curve
2989  for (size_t i = 0; i < numPoints; i++)
2990  {
2991  if(lin)
2992  mLinEnvelope->Reassign(when[i] , -value[i]);
2993  else
2994  mLogEnvelope->Reassign(when[i] , -value[i]);
2995  }
2996 
2997  // copy it back to the other one (just in case)
2998  if(lin)
2999  EnvLinToLog();
3000  else
3001  EnvLogToLin();
3002  }
3003 
3004  // and update the display etc
3005  ForceRecalc();
3006  EnvelopeUpdated();
3007 }
3008 
3009 void EffectEqualization::OnGridOnOff(wxCommandEvent & WXUNUSED(event))
3010 {
3011  mDrawGrid = mGridOnOff->IsChecked();
3012  mPanel->Refresh(false);
3013 }
3014 
3015 void EffectEqualization::OnLinFreq(wxCommandEvent & WXUNUSED(event))
3016 {
3017  mLin = mLinFreq->IsChecked();
3018  if(IsLinear()) //going from log to lin freq scale
3019  {
3020  mFreqRuler->ruler.SetLog(false);
3022  EnvLogToLin();
3023  mEnvelope = mLinEnvelope.get();
3024  mLin = true;
3025  }
3026  else //going from lin to log freq scale
3027  {
3028  mFreqRuler->ruler.SetLog(true);
3030  EnvLinToLog();
3031  mEnvelope = mLogEnvelope.get();
3032  mLin = false;
3033  }
3034  mFreqRuler->Refresh(false);
3035  ForceRecalc();
3036 }
3037 
3038 #ifdef EXPERIMENTAL_EQ_SSE_THREADED
3039 
3040 void EffectEqualization::OnProcessingRadio(wxCommandEvent & event)
3041 {
3042  int testEvent=event.GetId();
3043  switch(testEvent)
3044  {
3045  case ID_DefaultMath: EffectEqualization48x::SetMathPath(MATH_FUNCTION_ORIGINAL);
3046  break;
3047  case ID_SSE: EffectEqualization48x::SetMathPath(MATH_FUNCTION_SSE);
3048  break;
3049  case ID_SSEThreaded: EffectEqualization48x::SetMathPath(MATH_FUNCTION_THREADED | MATH_FUNCTION_SSE);
3050  break;
3051  case ID_AVX: testEvent = 2;
3052  break;
3053  case ID_AVXThreaded: testEvent = 2;
3054  break;
3055  }
3056 
3057 };
3058 
3059 void EffectEqualization::OnBench( wxCommandEvent & event)
3060 {
3061  mBench=true;
3062  // OnOk(event);
3063 }
3064 
3065 #endif
3066 
3067 //----------------------------------------------------------------------------
3068 // EqualizationPanel
3069 //----------------------------------------------------------------------------
3070 
3071 BEGIN_EVENT_TABLE(EqualizationPanel, wxPanelWrapper)
3072  EVT_PAINT(EqualizationPanel::OnPaint)
3073  EVT_MOUSE_EVENTS(EqualizationPanel::OnMouseEvent)
3074  EVT_MOUSE_CAPTURE_LOST(EqualizationPanel::OnCaptureLost)
3075  EVT_SIZE(EqualizationPanel::OnSize)
3077 
3079  wxWindow *parent, wxWindowID winid, EffectEqualization *effect)
3080 : wxPanelWrapper(parent, winid)
3081 {
3082  mParent = parent;
3083  mEffect = effect;
3084 
3085  mBitmap = NULL;
3086  mWidth = 0;
3087  mHeight = 0;
3088 
3089  mLinEditor = std::make_unique<EnvelopeEditor>(*mEffect->mLinEnvelope, false);
3090  mLogEditor = std::make_unique<EnvelopeEditor>(*mEffect->mLogEnvelope, false);
3091  mEffect->mEnvelope->Flatten(0.);
3092  mEffect->mEnvelope->SetTrackLen(1.0);
3093 
3094  ForceRecalc();
3095 }
3096 
3098 {
3099  if(HasCapture())
3100  ReleaseMouse();
3101 }
3102 
3104 {
3105  mRecalcRequired = true;
3106  Refresh(false);
3107 }
3108 
3110 {
3113 
3114  mEffect->CalcFilter(); //to calculate the actual response
3116 }
3117 
3118 void EqualizationPanel::OnSize(wxSizeEvent & WXUNUSED(event))
3119 {
3120  Refresh( false );
3121 }
3122 
3123 #include "../TrackPanelDrawingContext.h"
3124 void EqualizationPanel::OnPaint(wxPaintEvent & WXUNUSED(event))
3125 {
3126  wxPaintDC dc(this);
3127  if(mRecalcRequired) {
3128  Recalc();
3129  mRecalcRequired = false;
3130  }
3131  int width, height;
3132  GetSize(&width, &height);
3133 
3134  if (!mBitmap || mWidth!=width || mHeight!=height)
3135  {
3136  mWidth = width;
3137  mHeight = height;
3138  mBitmap = std::make_unique<wxBitmap>(mWidth, mHeight,24);
3139  }
3140 
3141  wxBrush bkgndBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
3142 
3143  wxMemoryDC memDC;
3144  memDC.SelectObject(*mBitmap);
3145 
3146  wxRect bkgndRect;
3147  bkgndRect.x = 0;
3148  bkgndRect.y = 0;
3149  bkgndRect.width = mWidth;
3150  bkgndRect.height = mHeight;
3151  memDC.SetBrush(bkgndBrush);
3152  memDC.SetPen(*wxTRANSPARENT_PEN);
3153  memDC.DrawRectangle(bkgndRect);
3154 
3155  bkgndRect.y = mHeight;
3156  memDC.DrawRectangle(bkgndRect);
3157 
3158  wxRect border;
3159  border.x = 0;
3160  border.y = 0;
3161  border.width = mWidth;
3162  border.height = mHeight;
3163 
3164  memDC.SetBrush(*wxWHITE_BRUSH);
3165  memDC.SetPen(*wxBLACK_PEN);
3166  memDC.DrawRectangle(border);
3167 
3168  mEnvRect = border;
3169  mEnvRect.Deflate(PANELBORDER, PANELBORDER);
3170 
3171  // Pure blue x-axis line
3172  memDC.SetPen(wxPen(theTheme.Colour( clrGraphLines ), 1, wxPENSTYLE_SOLID));
3173  int center = (int) (mEnvRect.height * mEffect->mdBMax/(mEffect->mdBMax-mEffect->mdBMin) + .5);
3174  AColor::Line(memDC,
3175  mEnvRect.GetLeft(), mEnvRect.y + center,
3176  mEnvRect.GetRight(), mEnvRect.y + center);
3177 
3178  // Draw the grid, if asked for. Do it now so it's underneath the main plots.
3179  if( mEffect->mDrawGrid )
3180  {
3181  mEffect->mFreqRuler->ruler.DrawGrid(memDC, mEnvRect.height, true, true, PANELBORDER, PANELBORDER);
3182  mEffect->mdBRuler->ruler.DrawGrid(memDC, mEnvRect.width, true, true, PANELBORDER, PANELBORDER);
3183  }
3184 
3185  // Med-blue envelope line
3186  memDC.SetPen(wxPen(theTheme.Colour(clrGraphLines), 3, wxPENSTYLE_SOLID));
3187 
3188  // Draw envelope
3189  int x, y, xlast = 0, ylast = 0;
3190  {
3191  Doubles values{ size_t(mEnvRect.width) };
3192  mEffect->mEnvelope->GetValues(values.get(), mEnvRect.width, 0.0, 1.0 / mEnvRect.width);
3193  bool off = false, off1 = false;
3194  for (int i = 0; i < mEnvRect.width; i++)
3195  {
3196  x = mEnvRect.x + i;
3197  y = lrint(mEnvRect.height*((mEffect->mdBMax - values[i]) / (mEffect->mdBMax - mEffect->mdBMin)) + .25); //needs more optimising, along with'what you get'?
3198  if (y >= mEnvRect.height)
3199  {
3200  y = mEnvRect.height - 1;
3201  off = true;
3202  }
3203  else
3204  {
3205  off = false;
3206  off1 = false;
3207  }
3208  if ((i != 0) & (!off1))
3209  {
3210  AColor::Line(memDC, xlast, ylast,
3211  x, mEnvRect.y + y);
3212  }
3213  off1 = off;
3214  xlast = x;
3215  ylast = mEnvRect.y + y;
3216  }
3217  }
3218 
3219  //Now draw the actual response that you will get.
3220  //mFilterFunc has a linear scale, window has a log one so we have to fiddle about
3221  memDC.SetPen(wxPen(theTheme.Colour( clrResponseLines ), 1, wxPENSTYLE_SOLID));
3222  double scale = (double)mEnvRect.height/(mEffect->mdBMax-mEffect->mdBMin); //pixels per dB
3223  double yF; //gain at this freq
3224  double delta = mEffect->mHiFreq / (((double)mEffect->mWindowSize / 2.)); //size of each freq bin
3225 
3226  bool lin = mEffect->IsLinear(); // log or lin scale?
3227 
3228  double loLog = log10(mEffect->mLoFreq);
3229  double step = lin ? mEffect->mHiFreq : (log10(mEffect->mHiFreq) - loLog);
3230  step /= ((double)mEnvRect.width-1.);
3231  double freq; //actual freq corresponding to x position
3232  int halfM = (mEffect->mM - 1) / 2;
3233  int n; //index to mFreqFunc
3234  for(int i=0; i<mEnvRect.width; i++)
3235  {
3236  x = mEnvRect.x + i;
3237  freq = lin ? step*i : pow(10., loLog + i*step); //Hz
3238  if( ( lin ? step : (pow(10., loLog + (i+1)*step)-freq) ) < delta)
3239  { //not enough resolution in FFT
3240  // set up for calculating cos using recurrence - faster than calculating it directly each time
3241  double theta = M_PI*freq/mEffect->mHiFreq; //radians, normalized
3242  double wtemp = sin(0.5 * theta);
3243  double wpr = -2.0 * wtemp * wtemp;
3244  double wpi = -1.0 * sin(theta);
3245  double wr = cos(theta*halfM);
3246  double wi = sin(theta*halfM);
3247 
3248  yF = 0.;
3249  for(int j=0;j<halfM;j++)
3250  {
3251  yF += 2. * mOutr[j] * wr; // This works for me, compared to the previous version. Compare wr to cos(theta*(halfM-j)). Works for me. Keep everything as doubles though.
3252  // do recurrence
3253  wr = (wtemp = wr) * wpr - wi * wpi + wr;
3254  wi = wi * wpr + wtemp * wpi + wi;
3255  }
3256  yF += mOutr[halfM];
3257  yF = fabs(yF);
3258  if(yF!=0.)
3259  yF = LINEAR_TO_DB(yF);
3260  else
3261  yF = mEffect->mdBMin;
3262  }
3263  else
3264  { //use FFT, it has enough resolution
3265  n = (int)(freq/delta + .5);
3266  if(pow(mEffect->mFilterFuncR[n],2)+pow(mEffect->mFilterFuncI[n],2)!=0.)
3267  yF = 10.0*log10(pow(mEffect->mFilterFuncR[n],2)+pow(mEffect->mFilterFuncI[n],2)); //10 here, a power
3268  else
3269  yF = mEffect->mdBMin;
3270  }
3271  if(yF < mEffect->mdBMin)
3272  yF = mEffect->mdBMin;
3273  yF = center-scale*yF;
3274  if(yF>mEnvRect.height)
3275  yF = mEnvRect.height - 1;
3276  if(yF<0.)
3277  yF=0.;
3278  y = (int)(yF+.5);
3279 
3280  if (i != 0)
3281  {
3282  AColor::Line(memDC, xlast, ylast, x, mEnvRect.y + y);
3283  }
3284  xlast = x;
3285  ylast = mEnvRect.y + y;
3286  }
3287 
3288  memDC.SetPen(*wxBLACK_PEN);
3289  if( mEffect->mDrawMode )
3290  {
3291  ZoomInfo zoomInfo( 0.0, mEnvRect.width-1 );
3292 
3293  // Back pointer to TrackPanel won't be needed in the one drawing
3294  // function we use here
3295  TrackArtist artist( nullptr );
3296 
3297  artist.pZoomInfo = &zoomInfo;
3298  TrackPanelDrawingContext context{ memDC, {}, {}, &artist };
3300  context, mEnvRect, false, 0.0,
3301  mEffect->mdBMin, mEffect->mdBMax, false);
3302  }
3303 
3304  dc.Blit(0, 0, mWidth, mHeight, &memDC, 0, 0, wxCOPY, FALSE);
3305 }
3306 
3307 void EqualizationPanel::OnMouseEvent(wxMouseEvent & event)
3308 {
3309  if (!mEffect->mDrawMode)
3310  {
3311  return;
3312  }
3313 
3314  if (event.ButtonDown() && !HasCapture())
3315  {
3316  CaptureMouse();
3317  }
3318 
3319  auto &pEditor = (mEffect->mLin ? mLinEditor : mLogEditor);
3320  if (pEditor->MouseEvent(event, mEnvRect, ZoomInfo(0.0, mEnvRect.width),
3321  false, 0.0,
3323  {
3325  ForceRecalc();
3326  }
3327 
3328  if (event.ButtonUp() && HasCapture())
3329  {
3330  ReleaseMouse();
3331  }
3332 }
3333 
3334 void EqualizationPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(event))
3335 {
3336  if (HasCapture())
3337  {
3338  ReleaseMouse();
3339  }
3340 }
3341 
3342 //----------------------------------------------------------------------------
3343 // EditCurvesDialog
3344 //----------------------------------------------------------------------------
3345 // Note that the 'modified' curve used to be called 'custom' but is now called 'unnamed'
3346 // Some things that deal with 'unnamed' curves still use, for example, 'mCustomBackup' as variable names.
3348 
3349 BEGIN_EVENT_TABLE(EditCurvesDialog, wxDialogWrapper)
3356  EVT_BUTTON(LibraryButtonID, EditCurvesDialog::OnLibrary)
3360  EditCurvesDialog::OnListSelectionChange)
3361  EVT_LIST_ITEM_DESELECTED(CurvesListID,
3362  EditCurvesDialog::OnListSelectionChange)
3364 
3365 EditCurvesDialog::EditCurvesDialog(wxWindow * parent, EffectEqualization * effect, int position):
3366 wxDialogWrapper(parent, wxID_ANY, XO("Manage Curves List"),
3367  wxDefaultPosition, wxDefaultSize,
3368  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
3369 {
3370  SetLabel(XO("Manage Curves")); // Provide visual label
3371  SetName(XO("Manage Curves List")); // Provide audible label
3372  mParent = parent;
3373  mEffect = effect;
3374  mPosition = position;
3375  // make a copy of mEffect->mCurves here to muck about with.
3376  mEditCurves.clear();
3377  for (unsigned int i = 0; i < mEffect->mCurves.size(); i++)
3378  {
3379  mEditCurves.push_back(mEffect->mCurves[i].Name);
3380  mEditCurves[i].points = mEffect->mCurves[i].points;
3381  }
3382 
3383  Populate();
3384  SetMinSize(GetSize());
3385 }
3386 
3388 {
3389 }
3390 
3393 {
3394  //------------------------- Main section --------------------
3395  ShuttleGui S(this, eIsCreating);
3396  PopulateOrExchange(S);
3397  // ----------------------- End of main section --------------
3398 }
3399 
3402 {
3403  S.StartHorizontalLay(wxEXPAND);
3404  {
3405  S.StartStatic(XO("&Curves"), 1);
3406  {
3407  mList = S.Id(CurvesListID)
3408  .Style(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES )
3410  { XO("Curve Name"), wxLIST_FORMAT_RIGHT }
3411  });
3412  }
3413  S.EndStatic();
3414  S.StartVerticalLay(0);
3415  {
3416  S.Id(UpButtonID).AddButton(XXO("Move &Up"), wxALIGN_LEFT);
3417  S.Id(DownButtonID).AddButton(XXO("Move &Down"), wxALIGN_LEFT);
3418  S.Id(RenameButtonID).AddButton(XXO("&Rename..."), wxALIGN_LEFT);
3419  S.Id(DeleteButtonID).AddButton(XXO("D&elete..."), wxALIGN_LEFT);
3420  S.Id(ImportButtonID).AddButton(XXO("I&mport..."), wxALIGN_LEFT);
3421  S.Id(ExportButtonID).AddButton(XXO("E&xport..."), wxALIGN_LEFT);
3422  S.Id(LibraryButtonID).AddButton(XXO("&Get More..."), wxALIGN_LEFT);
3423  S.Id(DefaultsButtonID).AddButton(XXO("De&faults"), wxALIGN_LEFT);
3424  }
3425  S.EndVerticalLay();
3426  }
3427  S.EndHorizontalLay();
3428  S.AddStandardButtons();
3429  S.StartStatic(XO("Help"));
3430  S.AddConstTextBox( {}, XO("Rename 'unnamed' to save a new entry.\n'OK' saves all changes, 'Cancel' doesn't."));
3431  S.EndStatic();
3433  Fit();
3434 
3435  return;
3436 }
3437 
3439 {
3440  mList->DeleteAllItems();
3441  for (unsigned int i = 0; i < mEditCurves.size(); i++)
3442  mList->InsertItem(i, mEditCurves[i].Name);
3443  mList->SetColumnWidth(0, wxLIST_AUTOSIZE);
3444  int curvesWidth = mList->GetColumnWidth(0);
3445  mList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER);
3446  int headerWidth = mList->GetColumnWidth(0);
3447  mList->SetColumnWidth(0, wxMax(headerWidth, curvesWidth));
3448  // use 'position' to set focus
3449  mList->EnsureVisible(position);
3450  mList->SetItemState(position, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
3451 }
3452 
3453 void EditCurvesDialog::OnUp(wxCommandEvent & WXUNUSED(event))
3454 {
3455  long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3456  if ( item == -1 )
3457  return; // no items selected
3458  if( item == 0 )
3459  item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); // top item selected, can't move up
3460  int state;
3461  while( item != -1 )
3462  {
3463  if ( item == mList->GetItemCount()-1)
3464  { // 'unnamed' always stays at the bottom
3465  mEffect->Effect::MessageBox(
3466  XO("'unnamed' always stays at the bottom of the list"),
3468  XO("'unnamed' is special") ); // these could get tedious!
3469  return;
3470  }
3471  state = mList->GetItemState(item-1, wxLIST_STATE_SELECTED);
3472  if ( state != wxLIST_STATE_SELECTED )
3473  { // swap this with one above but only if it isn't selected
3474  EQCurve temp(wxT("temp"));
3475  temp.Name = mEditCurves[item].Name;
3476  temp.points = mEditCurves[item].points;
3477  mEditCurves[item].Name = mEditCurves[item-1].Name;
3478  mEditCurves[item].points = mEditCurves[item-1].points;
3479  mEditCurves[item-1].Name = temp.Name;
3480  mEditCurves[item-1].points = temp.points;
3481  wxString sTemp = mList->GetItemText(item);
3482  mList->SetItem(item, 0, mList->GetItemText(item-1));
3483  mList->SetItem(item-1, 0, sTemp);
3484  mList->SetItemState(item, 0, wxLIST_STATE_SELECTED);
3485  mList->SetItemState(item-1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
3486  }
3487  item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3488  }
3489 }
3490 
3491 void EditCurvesDialog::OnDown(wxCommandEvent & WXUNUSED(event))
3492 { // looks harder than OnUp as we need to seek backwards up the list, hence GetPreviousItem
3493  long item = GetPreviousItem(mList->GetItemCount());
3494  if( item == -1 )
3495  return; // nothing selected
3496  int state;
3497  while( item != -1 )
3498  {
3499  if( (item != mList->GetItemCount()-1) && (item != mList->GetItemCount()-2) )
3500  { // can't move 'unnamed' down, or the one above it
3501  state = mList->GetItemState(item+1, wxLIST_STATE_SELECTED);
3502  if ( state != wxLIST_STATE_SELECTED )
3503  { // swap this with one below but only if it isn't selected
3504  EQCurve temp(wxT("temp"));
3505  temp.Name = mEditCurves[item].Name;
3506  temp.points = mEditCurves[item].points;
3507  mEditCurves[item].Name = mEditCurves[item+1].Name;
3508  mEditCurves[item].points = mEditCurves[item+1].points;
3509  mEditCurves[item+1].Name = temp.Name;
3510  mEditCurves[item+1].points = temp.points;
3511  wxString sTemp = mList->GetItemText(item);
3512  mList->SetItem(item, 0, mList->GetItemText(item+1));
3513  mList->SetItem(item+1, 0, sTemp);
3514  mList->SetItemState(item, 0, wxLIST_STATE_SELECTED);
3515  mList->SetItemState(item+1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
3516  }
3517  }
3518  item = GetPreviousItem(item);
3519  }
3520 }
3521 
3522 long EditCurvesDialog::GetPreviousItem(long item) // wx doesn't have this
3523 {
3524  long lastItem = -1;
3525  long itemTemp = mList->GetNextItem(-1, wxLIST_NEXT_ALL,
3526  wxLIST_STATE_SELECTED);
3527  while( (itemTemp != -1) && (itemTemp < item) )
3528  {
3529  lastItem = itemTemp;
3530  itemTemp = mList->GetNextItem(itemTemp, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3531  }
3532  return lastItem;
3533 }
3534 
3535 // Rename curve/curves
3536 void EditCurvesDialog::OnRename(wxCommandEvent & WXUNUSED(event))
3537 {
3538  wxString name;
3539  int numCurves = mEditCurves.size();
3540  int curve = 0;
3541 
3542  // Setup list of characters that aren't allowed
3543  wxArrayStringEx exclude{
3544  wxT("<") ,
3545  wxT(">") ,
3546  wxT("'") ,
3547  wxT("\"") ,
3548  };
3549 
3550  // Get the first one to be renamed
3551  long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3552  long firstItem = item; // for reselection with PopulateList
3553  while(item >= 0)
3554  {
3555  // Prompt the user until a valid name is enter or cancelled
3556  bool overwrite = false;
3557  bool bad = true;
3558  while( bad ) // Check for an unacceptable duplicate
3559  { // Show the dialog and bail if the user cancels
3560  bad = false;
3561  // build the dialog
3562  AudacityTextEntryDialog dlg( this,
3563  XO("Rename '%s' to...").Format( mEditCurves[ item ].Name ),
3564  XO("Rename...") );
3565  dlg.SetTextValidator( wxFILTER_EXCLUDE_CHAR_LIST );
3566  dlg.SetName(
3567  wxString::Format( _("Rename '%s'"), mEditCurves[ item ].Name ) );
3568  wxTextValidator *tv = dlg.GetTextValidator();
3569  tv->SetExcludes( exclude ); // Tell the validator about excluded chars
3570  if( dlg.ShowModal() == wxID_CANCEL )
3571  {
3572  bad = true;
3573  break;
3574  }
3575 
3576  // Extract the name from the dialog
3577  name = dlg.GetValue();
3578 
3579  // Search list of curves for a duplicate name
3580  for( curve = 0; curve < numCurves; curve++ )
3581  {
3582  wxString temp = mEditCurves[ curve ].Name;
3583  if( name == mEditCurves[ curve ].Name ) // case sensitive
3584  {
3585  bad = true;
3586  if( curve == item ) // trying to rename a curve with the same name
3587  {
3588  mEffect->Effect::MessageBox(
3589  XO("Name is the same as the original one"),
3590  wxOK,
3591  XO("Same name") );
3592  break;
3593  }
3594  int answer = mEffect->Effect::MessageBox(
3595  XO("Overwrite existing curve '%s'?").Format( name ),
3596  wxYES_NO,
3597  XO("Curve exists") );
3598  if (answer == wxYES)
3599  {
3600  bad = false;
3601  overwrite = true; // we are going to overwrite the one with this name
3602  break;
3603  }
3604  }
3605  }
3606  if( name.empty() || name == wxT("unnamed") )
3607  bad = true;
3608  }
3609 
3610  // if bad, we cancelled the rename dialog, so nothing to do.
3611  if( bad == true )
3612  ;
3613  else if(overwrite){
3614  // Overwrite another curve.
3615  // JKC: because 'overwrite' is true, 'curve' is the number of the curve that
3616  // we are about to overwrite.
3617  mEditCurves[ curve ].Name = name;
3618  mEditCurves[ curve ].points = mEditCurves[ item ].points;
3619  // if renaming the unnamed item, then select it,
3620  // otherwise get rid of the item we've renamed.
3621  if( item == (numCurves-1) )
3622  mList->SetItem(curve, 0, name);
3623  else
3624  {
3625  mEditCurves.erase( mEditCurves.begin() + item );
3626  numCurves--;
3627  }
3628  }
3629  else if( item == (numCurves-1) ) // renaming 'unnamed'
3630  { // Create a NEW entry
3631  mEditCurves.push_back( EQCurve( wxT("unnamed") ) );
3632  // Copy over the points
3633  mEditCurves[ numCurves ].points = mEditCurves[ numCurves - 1 ].points;
3634  // Give the original unnamed entry the NEW name
3635  mEditCurves[ numCurves - 1 ].Name = name;
3636  numCurves++;
3637  }
3638  else // just rename (the 'normal' case)
3639  {
3640  mEditCurves[ item ].Name = name;
3641  mList->SetItem(item, 0, name);
3642  }
3643  // get next selected item
3644  item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3645  }
3646 
3647  PopulateList(firstItem); // Note: only saved to file when you OK out of the dialog
3648  return;
3649 }
3650 
3651 // Delete curve/curves
3652 void EditCurvesDialog::OnDelete(wxCommandEvent & WXUNUSED(event))
3653 {
3654  // We could count them here
3655  // And then put in a 'Delete N items?' prompt.
3656 
3657 #if 0 // 'one at a time' prompt code
3658  // Get the first one to be deleted
3659  long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3660  // Take care, mList and mEditCurves will get out of sync as curves are deleted
3661  int deleted = 0;
3662  long highlight = -1;
3663 
3664  while(item >= 0)
3665  {
3666  if(item == mList->GetItemCount()-1) //unnamed
3667  {
3668  mEffect->Effect::MessageBox(
3669  XO("You cannot delete the 'unnamed' curve."),
3670  wxOK | wxCENTRE,
3671  XO("Can't delete 'unnamed'") );
3672  }
3673  else
3674  {
3675  // Create the prompt
3676  auto quest = XO("Delete '%s'?")
3677  .Format(mEditCurves[ item-deleted ].Name));
3678 
3679  // Ask for confirmation before removal
3680  int ans = mEffect->Effect::MessageBox(
3681  quest,
3682  wxYES_NO | wxCENTRE,
3683  XO("Confirm Deletion") );
3684  if( ans == wxYES )
3685  { // Remove the curve from the array
3686  mEditCurves.RemoveAt( item-deleted );
3687  deleted++;
3688  }
3689  else
3690  highlight = item-deleted; // if user presses 'No', select that curve
3691  }
3692  // get next selected item
3693  item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3694  }
3695 
3696  if(highlight == -1)
3697  PopulateList(mEditCurves.size()-1); // set 'unnamed' as the selected curve
3698  else
3699  PopulateList(highlight); // user said 'No' to deletion
3700 #else // 'DELETE all N' code
3701  int count = mList->GetSelectedItemCount();
3702  long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3703  // Create the prompt
3704  TranslatableString quest;
3705  if( count > 1 )
3706  quest = XO("Delete %d items?").Format( count );
3707  else
3708  if( count == 1 )
3709  quest = XO("Delete '%s'?").Format( mEditCurves[ item ].Name );
3710  else
3711  return;
3712  // Ask for confirmation before removal
3713  int ans = mEffect->Effect::MessageBox(
3714  quest,
3715  wxYES_NO | wxCENTRE,
3716  XO("Confirm Deletion") );
3717  if( ans == wxYES )
3718  { // Remove the curve(s) from the array
3719  // Take care, mList and mEditCurves will get out of sync as curves are deleted
3720  int deleted = 0;
3721  while(item >= 0)
3722  {
3723  // TODO: Migrate to the standard "Manage" dialog.
3724  if(item == mList->GetItemCount()-1) //unnamed
3725  {
3726  mEffect->Effect::MessageBox(
3727  XO("You cannot delete the 'unnamed' curve, it is special."),
3729  XO("Can't delete 'unnamed'") );
3730  }
3731  else
3732  {
3733  mEditCurves.erase( mEditCurves.begin() + item - deleted );
3734  deleted++;
3735  }
3736  item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3737  }
3738  PopulateList(mEditCurves.size() - 1); // set 'unnamed' as the selected curve
3739  }
3740 #endif
3741 }
3742 
3744 {
3745  static const FileNames::FileTypes results{
3747  };
3748  return results;
3749 }
3750 
3751 void EditCurvesDialog::OnImport( wxCommandEvent & WXUNUSED(event))
3752 {
3753  FileDialogWrapper filePicker(
3754  this,
3755  XO("Choose an EQ curve file"), FileNames::DataDir(), wxT(""),
3756  XMLtypes() );
3757  wxString fileName;
3758  if( filePicker.ShowModal() == wxID_CANCEL)
3759  return;
3760  else
3761  fileName = filePicker.GetPath();
3762  // Use EqualizationDialog::LoadCurves to read into (temporary) mEditCurves
3763  // This may not be the best OOP way of doing it, but I don't know better (MJS)
3764  EQCurveArray temp;
3765  temp = mEffect->mCurves; // temp copy of the main dialog curves
3766  mEffect->mCurves = mEditCurves; // copy EditCurvesDialog to main interface
3767  mEffect->LoadCurves(fileName, true); // use main interface to load imported curves
3768  mEditCurves = mEffect->mCurves; // copy back to this interface
3769  mEffect->mCurves = temp; // and reset the main interface how it was
3770  PopulateList(0); // update the EditCurvesDialog dialog
3771  return;
3772 }
3773 
3774 void EditCurvesDialog::OnExport( wxCommandEvent & WXUNUSED(event))
3775 {
3776  FileDialogWrapper filePicker(this, XO("Export EQ curves as..."),
3777  FileNames::DataDir(), wxT(""),
3778  XMLtypes(),
3779  wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER); // wxFD_CHANGE_DIR?
3780  wxString fileName;
3781  if( filePicker.ShowModal() == wxID_CANCEL)
3782  return;
3783  else
3784  fileName = filePicker.GetPath();
3785 
3786  EQCurveArray temp;
3787  temp = mEffect->mCurves; // backup the parent's curves
3788  EQCurveArray exportCurves; // Copy selected curves to export
3789  exportCurves.clear();
3790  long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3791  int i=0;
3792  while(item >= 0)
3793  {
3794  if(item != mList->GetItemCount()-1) // not 'unnamed'
3795  {
3796  exportCurves.push_back(mEditCurves[item].Name);
3797  exportCurves[i].points = mEditCurves[item].points;
3798  i++;
3799  }
3800  else
3801  mEffect->Effect::MessageBox(
3802  XO("You cannot export 'unnamed' curve, it is special."),
3804  XO("Cannot Export 'unnamed'") );
3805  // get next selected item
3806  item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3807  }
3808  if(i>0)
3809  {
3810  mEffect->mCurves = exportCurves;
3811  mEffect->SaveCurves(fileName);
3812  mEffect->mCurves = temp;
3813  auto message = XO("%d curves exported to %s").Format( i, fileName );
3814  mEffect->Effect::MessageBox(
3815  message,
3817  XO("Curves exported") );
3818  }
3819  else
3820  mEffect->Effect::MessageBox(
3821  XO("No curves exported"),
3823  XO("No curves exported") );
3824 }
3825 
3826 void EditCurvesDialog::OnLibrary( wxCommandEvent & WXUNUSED(event))
3827 {
3828  // full path to wiki.
3829  wxLaunchDefaultBrowser(wxT("https://wiki.audacityteam.org/wiki/EQCurvesDownload"));
3830 }
3831 
3832 void EditCurvesDialog::OnDefaults( wxCommandEvent & WXUNUSED(event))
3833 {
3834  EQCurveArray temp;
3835  temp = mEffect->mCurves;
3836  // we expect this to fail in LoadCurves (due to a lack of path) and handle that there
3837  mEffect->LoadCurves( wxT("EQDefaultCurves.xml") );
3839  mEffect->mCurves = temp;
3840  PopulateList(0); // update the EditCurvesDialog dialog
3841 }
3842 
3843 void EditCurvesDialog::OnOK(wxCommandEvent & WXUNUSED(event))
3844 {
3845  // Make a backup of the current curves
3846  wxString backupPlace = wxFileName( FileNames::DataDir(), wxT("EQBackup.xml") ).GetFullPath();
3847  mEffect->SaveCurves(backupPlace);
3848  // Load back into the main dialog
3849  mEffect->mCurves.clear();
3850  for (unsigned int i = 0; i < mEditCurves.size(); i++)
3851  {
3852  mEffect->mCurves.push_back(mEditCurves[i].Name);
3853  mEffect->mCurves[i].points = mEditCurves[i].points;
3854  }
3855  mEffect->SaveCurves();
3856  mEffect->LoadCurves();
3857 // mEffect->CreateChoice();
3858  wxGetTopLevelParent(mEffect->mUIParent)->Layout();
3859 // mEffect->mUIParent->Layout();
3860 
3861  // Select something sensible
3862  long item = mList->GetNextItem(-1,
3863  wxLIST_NEXT_ALL,
3864  wxLIST_STATE_SELECTED);
3865  if (item == -1)
3866  item = mList->GetItemCount()-1; // nothing selected, default to 'unnamed'
3867  mEffect->setCurve(item);
3868  EndModal(true);
3869 }
3870 
3872 {
3873  const bool enable = mList->GetSelectedItemCount() > 0;
3874  static const int ids[] = {
3875  UpButtonID,
3876  DownButtonID,
3879  };
3880  for (auto id : ids)
3881  FindWindowById(id, this)->Enable(enable);
3882 }
3883 
XMLWriter
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:23
EffectEqualization::HandleXMLChild
XMLTagHandler * HandleXMLChild(const wxChar *tag) override
Definition: Equalization.cpp:2292
EVT_BUTTON
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
RegistryPaths
std::vector< RegistryPath > RegistryPaths
Definition: Types.h:262
EffectEqualization::OnGridOnOff
void OnGridOnOff(wxCommandEvent &event)
Definition: Equalization.cpp:3009
EqualizationPanel::mRecalcRequired
bool mRecalcRequired
Definition: Equalization.h:333
EqualizationPanel::ForceRecalc
void ForceRecalc()
Definition: Equalization.cpp:3103
EffectEqualization::ManualPage
wxString ManualPage() override
Definition: Equalization.cpp:351
EditCurvesDialog::LibraryButtonID
@ LibraryButtonID
Definition: Equalization.h:369
ID_Graphic
@ ID_Graphic
Definition: Equalization.cpp:130
ShuttleSetAutomation
Shuttle that sets parameters to a value (from a string)
Definition: Shuttle.h:105
TranslatableString
Definition: Types.h:290
EffectEqualization::mLogEnvelope
std::unique_ptr< Envelope > mLogEnvelope
Definition: Equalization.h:235
Equalization48x.h
CommandParameters
CommandParameters, derived from wxFileConfig, is essentially doing the same things as the Shuttle cla...
Definition: EffectAutomationParameters.h:67
EffectEqualization::OnGraphicMode
void OnGraphicMode(wxCommandEvent &event)
Definition: Equalization.cpp:2887
eIsCreating
@ eIsCreating
Definition: ShuttleGui.h:36
EditCurvesDialog::OnExport
void OnExport(wxCommandEvent &event)
Definition: Equalization.cpp:3774
ID_Invert
@ ID_Invert
Definition: Equalization.cpp:127
TranslatableString::empty
bool empty() const
Definition: Types.h:329
ShuttleGuiBase::AddChoice
wxChoice * AddChoice(const TranslatableString &Prompt, const TranslatableStrings &choices, int Selected=-1)
Definition: ShuttleGui.cpp:391
kEqOptionGraphic
const int kEqOptionGraphic
Definition: Equalization.h:24
Effect::FindProject
const AudacityProject * FindProject() const
Definition: Effect.cpp:2271
ShuttleGuiBase::StartVerticalLay
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:1177
EditCurvesDialog::~EditCurvesDialog
~EditCurvesDialog()
Definition: Equalization.cpp:3387
WaveTrack
A Track that contains audio waveform data.
Definition: WaveTrack.h:68
ImportButtonID
@ ImportButtonID
Definition: BatchProcessDialog.cpp:504
CommandParameters::WriteEnum
bool WriteEnum(const wxString &key, int value, const EnumValueSymbol choices[], size_t nChoices)
Definition: EffectAutomationParameters.h:203
EffectTypeProcess
@ EffectTypeProcess
Definition: EffectInterface.h:59
wxSliderWrapper
wxSlider wxSliderWrapper
Definition: ShuttleGui.h:103
bForBoth
const bool bForBoth
Definition: Equalization.cpp:468
DB_TO_LINEAR
const double MIN_Threshold_Linear DB_TO_LINEAR(MIN_Threshold_dB)
Effect::mUIDialog
wxDialog * mUIDialog
Definition: Effect.h:476
EffectEqualization::mGraphic
wxRadioButton * mGraphic
Definition: Equalization.h:261
EQCURVES_REVISION
#define EQCURVES_REVISION
Definition: Equalization.cpp:158
EditCurvesDialog::GetPreviousItem
long GetPreviousItem(long item)
Definition: Equalization.cpp:3522
UpButtonID
@ UpButtonID
Definition: BatchProcessDialog.cpp:513
RulerPanel
RulerPanel class allows you to work with a Ruler like any other wxWindow.
Definition: Ruler.h:228
DownButtonID
@ DownButtonID
Definition: BatchProcessDialog.cpp:514
Effect::CloseUI
bool CloseUI() override
Definition: Effect.cpp:651
EditCurvesDialog::DownButtonID
@ DownButtonID
Definition: Equalization.h:364
EditCurvesDialog::RenameButtonID
@ RenameButtonID
Definition: Equalization.h:365
EqualizationPanel::mBitmap
std::unique_ptr< wxBitmap > mBitmap
Definition: Equalization.h:335
Ruler::LinearDBFormat
@ LinearDBFormat
Definition: Ruler.h:34
fn
static const auto fn
Definition: WaveformView.cpp:1102
EQCurve::Name
wxString Name
Definition: Equalization.h:82
EditCurvesDialog::ImportButtonID
@ ImportButtonID
Definition: Equalization.h:367
EffectEqualization::mdBMin
float mdBMin
Definition: Equalization.h:214
flag
static std::once_flag flag
Definition: WaveformView.cpp:1113
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:67
EffectEqualization::WriteXML
void WriteXML(XMLWriter &xmlFile) const
Definition: Equalization.cpp:2315
Effect::MessageBox
int MessageBox(const TranslatableString &message, long style=DefaultMessageBoxStyle, const TranslatableString &titleStr={})
Definition: Effect.cpp:2478
WaveClip::GetEnvelope
Envelope * GetEnvelope()
Definition: WaveClip.h:242
ID_dBMin
@ ID_dBMin
Definition: Equalization.cpp:125
EffectEqualization::LoadCurves
void LoadCurves(const wxString &fileName={}, bool append=false)
Definition: Equalization.cpp:1596
Effect::CopyInputTracks
void CopyInputTracks(bool allSyncLockSelected=false)
Definition: Effect.cpp:2070
EffectEqualization::OnSize
void OnSize(wxSizeEvent &event)
Definition: Equalization.cpp:2826
EffectEqualization::GetType
EffectType GetType() override
Definition: Equalization.cpp:363
wxPanelWrapper
Definition: wxPanelWrapper.h:41
ZoomInfo
Definition: ZoomInfo.h:47
EVT_LIST_ITEM_SELECTED
EVT_LIST_ITEM_SELECTED(CurvesListID, EditCurvesDialog::OnListSelectionChange) EVT_LIST_ITEM_DESELECTED(CurvesListID
Constructor.
EffectEqualization::mWindowSize
size_t mWindowSize
Definition: Equalization.h:228
AudacityTextEntryDialog
Wrap wxTextEntryDialog so that caption IS translatable.
Definition: ErrorDialog.h:73
EffectEqualization::OnDrawMode
void OnDrawMode(wxCommandEvent &event)
Definition: Equalization.cpp:2881
EffectEqualization::mMText
wxStaticText * mMText
Definition: Equalization.h:267
EffectEqualization::GetAutomationParameters
bool GetAutomationParameters(CommandParameters &parms) override
Definition: Equalization.cpp:411
EffectEqualization
An Effect that modifies volume in different frequency bands.
Definition: Equalization.h:94
EffectEqualization::GetDescription
TranslatableString GetDescription() override
Definition: Equalization.cpp:346
TrackPanelDrawingContext
Definition: TrackPanelDrawingContext.h:22
EffectEqualization::GraphicEQ
void GraphicEQ(Envelope *env)
Definition: Equalization.cpp:2641
EqualizationPanel::mLogEditor
std::unique_ptr< EnvelopeEditor > mLogEditor
Definition: Equalization.h:331
EffectEqualization::TransferDataFromWindow
bool TransferDataFromWindow() override
Definition: Equalization.cpp:1232
EQCurve::points
std::vector< EQPoint > points
Definition: Equalization.h:83
Envelope::Insert
void Insert(int point, const EnvPoint &p)
insert a point
Definition: Envelope.cpp:364
Format
Abstract base class used in importing a file.
EffectEqualization::OnSliderDBMAX
void OnSliderDBMAX(wxCommandEvent &event)
Definition: Equalization.cpp:2904
ShuttleGuiBase::AddRadioButtonToGroup
wxRadioButton * AddRadioButtonToGroup(const TranslatableString &Prompt, int selector=1, int initValue=0)
Definition: ShuttleGui.cpp:567
AColor::Line
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:112
Ruler::SetLog
void SetLog(bool log)
Definition: Ruler.cpp:142
RealFFTf
void RealFFTf(fft_type *buffer, const FFTParam *h)
Definition: RealFFTf.cpp:165
EffectEqualization::hFFT
HFFT hFFT
Definition: Equalization.h:208
EditCurvesDialog::mEditCurves
EQCurveArray mEditCurves
Definition: Equalization.h:374
Envelope
Piecewise linear or piecewise exponential function from double to double.
Definition: Envelope.h:71
EffectEqualizationGraphic::Symbol
static const ComponentInterfaceSymbol Symbol
Definition: Equalization.h:295
kInterpStrings
static const EnumValueSymbol kInterpStrings[nInterpolations]
Definition: Equalization.cpp:161
ID_Manage
@ ID_Manage
Definition: Equalization.cpp:135
Effect::mT1
double mT1
Definition: Effect.h:466
Effect::LoadUserPreset
bool LoadUserPreset(const RegistryPath &name) override
Definition: Effect.cpp:553
EffectEqualization::mLoFreq
double mLoFreq
Definition: Equalization.h:226
FileNames::DataDir
AUDACITY_DLL_API FilePath DataDir()
Audacity user data directory.
Envelope::GetPoints
void GetPoints(double *bufferWhen, double *bufferValue, int bufferLen) const
Returns the sets of when and value pairs.
Definition: Envelope.cpp:697
ShuttleGui::AddSpace
wxSizerItem * AddSpace(int width, int height, int prop=0)
Definition: ShuttleGui.cpp:2421
Envelope::SetTrackLen
void SetTrackLen(double trackLen, double sampleDur=0.0)
Definition: Envelope.cpp:790
XMLtypes
static const FileNames::FileTypes & XMLtypes()
Definition: Equalization.cpp:3743
ShuttleGui::MinSize
ShuttleGui & MinSize()
Definition: ShuttleGui.h:740
XO
#define XO(s)
Definition: Internat.h:32
ID_Delete
@ ID_Delete
Definition: Equalization.cpp:136
FileNames::XMLFiles
AUDACITY_DLL_API const FileType XMLFiles
Definition: FileNames.h:77
EffectEqualization::CloseUI
bool CloseUI() override
Definition: Equalization.cpp:749
EffectEqualization::mEQVals
double mEQVals[NUMBER_OF_BANDS+1]
Definition: Equalization.h:231
EffectEqualization::mdBMax
float mdBMax
Definition: Equalization.h:213
EqualizationPanel::OnMouseEvent
void OnMouseEvent(wxMouseEvent &event)
Definition: Equalization.cpp:3307
EffectEqualization::DefineParams
bool DefineParams(ShuttleParams &S) override
Definition: Equalization.cpp:369
Effect::SetPrivateConfig
bool SetPrivateConfig(const RegistryPath &group, const RegistryPath &key, const wxString &value) override
Definition: Effect.cpp:986
EqualizationPanel::mLinEditor
std::unique_ptr< EnvelopeEditor > mLinEditor
Definition: Equalization.h:331
Envelope::Reassign
int Reassign(double when, double value)
Move a point at when to value.
Definition: Envelope.cpp:672
EditCurvesDialog::mList
wxListCtrl * mList
Definition: Equalization.h:373
ShuttleParams
Shuttle that deals with parameters. This is a base class with lots of virtual functions that do nothi...
Definition: Shuttle.h:61
ID_Length
@ ID_Length
Definition: Equalization.cpp:123
ShuttleGuiBase::EndMultiColumn
void EndMultiColumn()
Definition: ShuttleGui.cpp:1212
Effect::IsBatchProcessing
virtual bool IsBatchProcessing()
Definition: Effect.cpp:1189
TrackArtist::pZoomInfo
ZoomInfo * pZoomInfo
Definition: TrackArtist.h:135
kThirdOct
static const double kThirdOct[]
Definition: Equalization.cpp:171
kBOTH
const bool kBOTH
Definition: Equalization.cpp:464
floatSample
@ floatSample
Definition: Types.h:722
EffectEqualization::GetFactoryPresets
RegistryPaths GetFactoryPresets() override
Definition: Equalization.cpp:490
EffectEqualization::mBandsInUse
size_t mBandsInUse
Definition: Equalization.h:221
EffectEqualization::Startup
bool Startup() override
Definition: Equalization.cpp:598
EffectEqualization::EqualizationPanel
friend class EqualizationPanel
Definition: Equalization.h:280
EffectEqualization::UpdateGraphic
void UpdateGraphic(void)
Definition: Equalization.cpp:2438
EditCurvesDialog::DefaultsButtonID
@ DefaultsButtonID
Definition: Equalization.h:370
Effect::DefaultMessageBoxStyle
@ DefaultMessageBoxStyle
Definition: Effect.h:273
ID_Mode
@ ID_Mode
Definition: Equalization.cpp:128
EffectEqualization::mWhenSliders
double mWhenSliders[NUMBER_OF_BANDS+1]
Definition: Equalization.h:220
Effect::SaveUserPreset
bool SaveUserPreset(const RegistryPath &name) override
Definition: Effect.cpp:569
RealFFT
void RealFFT(size_t NumSamples, const float *RealIn, float *RealOut, float *ImagOut)
Definition: FFT.cpp:231
TrackArtist
This class handles the actual rendering of WaveTracks (both waveforms and spectra),...
Definition: TrackArtist.h:58
ID_Curve
@ ID_Curve
Definition: Equalization.cpp:134
wxArrayStringEx
Definition: MemoryX.h:663
RenameButtonID
@ RenameButtonID
Definition: BatchProcessDialog.cpp:502
EffectEqualization::HandleXMLTag
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override
Definition: Equalization.cpp:2191
EffectEqualization::mdBMinSlider
wxSlider * mdBMinSlider
Definition: Equalization.h:269
EditCurvesDialog::mPosition
int mPosition
Definition: Equalization.h:377
WaveTrack::EmptyCopy
Holder EmptyCopy(const SampleBlockFactoryPtr &pFactory={}) const
Definition: WaveTrack.cpp:574
ComponentInterfaceSymbol
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
Definition: ComponentInterface.h:60
nInterpolations
@ nInterpolations
Definition: Equalization.cpp:153
XMLValueChecker::IsGoodString
static bool IsGoodString(const wxString &str)
Definition: XMLTagHandler.cpp:39
EffectEqualization::mSlidersOld
int mSlidersOld[NUMBER_OF_BANDS]
Definition: Equalization.h:230
EffectEqualization::loFreqI
@ loFreqI
Definition: Equalization.h:146
EffectEqualization::SaveCurves
void SaveCurves(const wxString &fileName={})
Definition: Equalization.cpp:1839
ShuttleGui::Id
ShuttleGui & Id(int id)
Definition: ShuttleGui.cpp:2248
EffectEqualization::szr1
wxSizer * szr1
Definition: Equalization.h:250
WaveTrack::Get
bool Get(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const
Definition: WaveTrack.cpp:1895
EditCurvesDialog
Definition: Equalization.h:353
XMLFileReader::Parse
bool Parse(XMLTagHandler *baseHandler, const FilePath &fname)
Definition: XMLFileReader.cpp:41
limitSampleBufferSize
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: Types.h:706
ReorderToTime
void ReorderToTime(const FFTParam *hFFT, const fft_type *buffer, fft_type *TimeOut)
Definition: RealFFTf.cpp:364
PANELBORDER
#define PANELBORDER
Definition: Equalization.h:16
EffectEqualization::mLin
bool mLin
Definition: Equalization.h:212
Ruler::GetMaxSize
void GetMaxSize(wxCoord *width, wxCoord *height)
Definition: Ruler.cpp:1603
EffectEqualization::mFilterFuncR
Floats mFilterFuncR
Definition: Equalization.h:209
EditCurvesDialog::OnDelete
void OnDelete(wxCommandEvent &event)
Definition: Equalization.cpp:3652
RulerPanel::Range
std::pair< double, double > Range
Definition: Ruler.h:232
EffectEqualization::szrI
wxSizer * szrI
Definition: Equalization.h:248
ShuttleGui::Style
ShuttleGui & Style(long iStyle)
Definition: ShuttleGui.h:734
EqualizationPanel::OnPaint
void OnPaint(wxPaintEvent &event)
Definition: Equalization.cpp:3124
EqualizationPanel::OnSize
void OnSize(wxSizeEvent &event)
Definition: Equalization.cpp:3118
WaveClip
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:172
EQPoint
EQPoint is used with EQCurve and hence EffectEqualization.
Definition: Equalization.h:50
TranslatableStrings
std::vector< TranslatableString > TranslatableStrings
Definition: Types.h:555
ReadAndVerifyEnum
#define ReadAndVerifyEnum(name, list, listSize)
Definition: Effect.h:620
ShuttleGuiBase::SetSizerProportion
void SetSizerProportion(int iProp)
Definition: ShuttleGui.h:498
EffectEqualization::setCurve
void setCurve(int currentCurve)
Definition: Equalization.cpp:1881
EditCurvesDialog::PopulateList
void PopulateList(int position)
Definition: Equalization.cpp:3438
ShuttleGuiBase::AddConstTextBox
void AddConstTextBox(const TranslatableString &Caption, const TranslatableString &Value)
Single line text box of fixed size.
Definition: ShuttleGui.cpp:726
EffectEqualization::Flatten
void Flatten()
Definition: Equalization.cpp:2162
EffectEqualization::mLinEnvelope
std::unique_ptr< Envelope > mLinEnvelope
Definition: Equalization.h:235
ReadAndVerifyInt
#define ReadAndVerifyInt(name)
Definition: Effect.h:631
EditCurvesDialog::mEffect
EffectEqualization * mEffect
Definition: Equalization.h:376
samplePtr
char * samplePtr
Definition: Types.h:737
EffectEqualization::mPanel
EqualizationPanel * mPanel
Definition: Equalization.h:258
kBspline
@ kBspline
Definition: Equalization.cpp:150
XXO
#define XXO(s)
Definition: Internat.h:45
WaveTrack::ConvertToSampleFormat
void ConvertToSampleFormat(sampleFormat format, const std::function< void(size_t)> &progressReport={})
Definition: WaveTrack.cpp:455
WaveTrack::Clear
void Clear(double t0, double t1) override
Definition: WaveTrack.cpp:654
EffectEqualization::spline
void spline(double x[], double y[], size_t n, double y2[])
Definition: Equalization.cpp:2779
ShuttleGuiBase::EndHorizontalLay
void EndHorizontalLay()
Definition: ShuttleGui.cpp:1170
XMLFileReader::GetErrorStr
const TranslatableString & GetErrorStr() const
Definition: XMLFileReader.cpp:177
EditCurvesDialog::PopulateOrExchange
void PopulateOrExchange(ShuttleGui &S)
Defines the dialog and does data exchange with it.
Definition: Equalization.cpp:3401
Effect::mT0
double mT0
Definition: Effect.h:465
EQCURVES_VERSION
#define EQCURVES_VERSION
Definition: Equalization.cpp:157
name
const TranslatableString name
Definition: Equalization.cpp:470
ShuttleGetAutomation
Shuttle that gets parameter values into a string.
Definition: Shuttle.h:87
EffectEqualization::mMSlider
wxSlider * mMSlider
Definition: Equalization.h:268
EffectEqualization::Select
void Select(int sel)
Definition: Equalization.cpp:2068
EqualizationPanel::Recalc
void Recalc()
Definition: Equalization.cpp:3109
label
TranslatableString label
Definition: Tags.cpp:755
ShuttleGuiBase::StartHorizontalLay
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:1160
InverseRealFFT
void InverseRealFFT(size_t NumSamples, const float *RealIn, const float *ImagIn, float *RealOut)
Definition: FFT.cpp:269
WaveTrack::LongSamplesToTime
double LongSamplesToTime(sampleCount pos) const
Convert correctly between a number of samples and an (absolute) time in seconds.
Definition: WaveTrack.cpp:1772
EffectEqualization::UpdateCurves
void UpdateCurves()
Definition: Equalization.cpp:2357
EffectEqualization::mM
size_t mM
Definition: Equalization.h:210
FactoryPresets
static const struct @1 FactoryPresets[]
EffectEqualization::mOptions
int mOptions
Definition: Equalization.h:207
ShuttleGui::Disable
ShuttleGui & Disable(bool disabled=true)
Definition: ShuttleGui.h:657
ShuttleGuiBase::StartMultiColumn
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
Definition: ShuttleGui.cpp:1203
ID_Draw
@ ID_Draw
Definition: Equalization.cpp:129
EditCurvesDialog::CurvesListID
@ CurvesListID
Definition: Equalization.h:362
ShuttleGuiBase::EndVerticalLay
void EndVerticalLay()
Definition: ShuttleGui.cpp:1196
FileDialog::GetPath
virtual wxString GetPath() const
Definition: gtk/FileDialogPrivate.cpp:514
EffectEqualization::setCurve
void setCurve(void)
Definition: Equalization.cpp:2042
Effect::LoadFactoryDefaults
bool LoadFactoryDefaults() override
Definition: Effect.cpp:605
EffectEqualization::mCurves
EQCurveArray mCurves
Definition: Equalization.h:233
ShuttleGuiBase::AddUnits
void AddUnits(const TranslatableString &Prompt, int wrapWidth=0)
Left aligned text string.
Definition: ShuttleGui.cpp:256
EffectEqualization::OnManage
void OnManage(wxCommandEvent &event)
Definition: Equalization.cpp:2924
ShuttleGuiBase::AddFixedText
void AddFixedText(const TranslatableString &Str, bool bCenter=false, int wrapWidth=0)
Definition: ShuttleGui.cpp:433
WaveTrack::GetClips
WaveClipHolders & GetClips()
Definition: WaveTrack.h:339
EffectEqualization::OnLinFreq
void OnLinFreq(wxCommandEvent &event)
Definition: Equalization.cpp:3015
kCURVE
const bool kCURVE
Definition: Equalization.cpp:463
ID_Slider
@ ID_Slider
Definition: Equalization.cpp:145
EqualizationPanel::OnCaptureLost
void OnCaptureLost(wxMouseCaptureLostEvent &event)
Definition: Equalization.cpp:3334
Effect::ReplaceProcessedTracks
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: Effect.cpp:2192
Msgids
TranslatableStrings Msgids(const EnumValueSymbol strings[], size_t nStrings)
Definition: Internat.cpp:267
WaveTrack::Paste
void Paste(double t0, const Track *src) override
Definition: WaveTrack.cpp:1171
EditCurvesDialog::OnUp
void OnUp(wxCommandEvent &event)
Definition: Equalization.cpp:3453
EffectEqualization::szrG
wxSizer * szrG
Definition: Equalization.h:245
EffectEqualization::GetSymbol
ComponentInterfaceSymbol GetSymbol() override
Definition: Equalization.cpp:337
EditCurvesDialog::OnRename
void OnRename(wxCommandEvent &event)
Definition: Equalization.cpp:3536
ShuttleGuiBase::GetParent
wxWindow * GetParent()
Definition: ShuttleGui.h:503
kCubic
@ kCubic
Definition: Equalization.cpp:152
EqualizationPanel::mWidth
int mWidth
Definition: Equalization.h:337
kEqLegacy
const int kEqLegacy
Definition: Equalization.h:27
Ruler::SetRange
void SetRange(double min, double max)
Definition: Ruler.cpp:188
WindowAccessible
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
Ruler::IntFormat
@ IntFormat
Definition: Ruler.h:30
EffectEqualization::splint
double splint(double x[], double y[], size_t n, double y2[], double xr)
Definition: Equalization.cpp:2801
anonymous_namespace{NoteTrack.cpp}::swap
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:735
EffectEqualization::LoadFactoryPreset
bool LoadFactoryPreset(int id) override
Definition: Equalization.cpp:504
EqualizationPanel::mEffect
EffectEqualization * mEffect
Definition: Equalization.h:330
EqualizationPanel
EqualizationPanel is used with EqualizationDialog and controls a graph for EffectEqualization....
Definition: Equalization.h:301
EnvelopeEditor::DrawPoints
static void DrawPoints(const Envelope &env, TrackPanelDrawingContext &context, const wxRect &r, bool dB, double dBRange, float zoomMin, float zoomMax, bool mirrored)
Definition: EnvelopeEditor.cpp:36
EffectEqualization::LoadFactoryDefaults
bool LoadFactoryDefaults() override
Definition: Equalization.cpp:447
ShuttleGuiBase::AddRadioButton
wxRadioButton * AddRadioButton(const TranslatableString &Prompt, int selector=0, int initValue=0)
Definition: ShuttleGui.cpp:561
EffectEqualization::mLinFreq
wxCheckBox * mLinFreq
Definition: Equalization.h:262
InverseRealFFTf
void InverseRealFFTf(fft_type *buffer, const FFTParam *h)
Definition: RealFFTf.cpp:267
EffectEqualization::mDirty
bool mDirty
Definition: Equalization.h:229
ID_dBMax
@ ID_dBMax
Definition: Equalization.cpp:124
EditCurvesDialog::OnImport
void OnImport(wxCommandEvent &event)
Definition: Equalization.cpp:3751
EffectEqualization::mInterp
int mInterp
Definition: Equalization.h:216
WaveTrack::GetClipAtTime
WaveClip * GetClipAtTime(double time)
Definition: WaveTrack.cpp:2102
Effect::mOutputTracks
std::shared_ptr< TrackList > mOutputTracks
Definition: Effect.h:464
ID_Grid
@ ID_Grid
Definition: Equalization.cpp:133
NUMBER_OF_BANDS
#define NUMBER_OF_BANDS
Definition: Equalization.h:14
FileNames::ResourcesDir
AUDACITY_DLL_API FilePath ResourcesDir()
DefaultsButtonID
@ DefaultsButtonID
Definition: BatchProcessDialog.cpp:508
ShuttleGui::Prop
ShuttleGui & Prop(int iProp)
Definition: ShuttleGui.h:732
ShuttleGuiBase::AddSlider
wxSlider * AddSlider(const TranslatableString &Prompt, int pos, int Max, int Min=0)
Definition: ShuttleGui.cpp:582
EffectEqualization::EnvLogToLin
void EnvLogToLin(void)
Definition: Equalization.cpp:2499
RulerPanel::Options::LabelEdges
Options & LabelEdges(bool l)
Definition: Ruler.h:250
EffectEqualization::ProcessOne
bool ProcessOne(int count, WaveTrack *t, sampleCount start, sampleCount len)
Definition: Equalization.cpp:1291
Effect::GetCurrentSettingsGroup
RegistryPath GetCurrentSettingsGroup() override
Definition: Effect.cpp:865
EditCurvesDialog::DeleteButtonID
@ DeleteButtonID
Definition: Equalization.h:366
TrackTyper::List
std::tuple< Pair< Track, TrackKind::All >, Pair< AudioTrack, TrackKind::Audio >, Pair< PlayableTrack, TrackKind::Playable >, Pair< LabelTrack, TrackKind::Label >, Pair< NoteTrack, TrackKind::Note >, Pair< TimeTrack, TrackKind::Time >, Pair< WaveTrack, TrackKind::Wave > > List
Compile-time map from types to enum values.
Definition: Track.h:116
EqualizationPanel::mOuti
Floats mOuti
Definition: Equalization.h:342
XMLTagHandler
This class is an interface which should be implemented by classes which wish to be able to load and s...
Definition: XMLTagHandler.h:80
ShuttleGuiBase::AddButton
wxButton * AddButton(const TranslatableString &Text, int PositionFlags=wxALIGN_CENTRE, bool setDefault=false)
Definition: ShuttleGui.cpp:353
EffectEqualization::IsLinear
bool IsLinear()
Definition: Equalization.cpp:2154
LoadEffects.h
ShuttleGuiBase::StartStatic
wxStaticBox * StartStatic(const TranslatableString &Str, int iProp=0)
Definition: ShuttleGui.cpp:886
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
WaveTrack::GetMaxBlockSize
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:1578
EffectEqualization::mdBMaxSlider
wxSlider * mdBMaxSlider
Definition: Equalization.h:270
ShuttleGui::Name
ShuttleGui & Name(const TranslatableString &name)
Definition: ShuttleGui.h:670
wxDialogWrapper
Definition: wxPanelWrapper.h:81
kEqOptionCurve
const int kEqOptionCurve
Definition: Equalization.h:25
EffectEqualization::szrH
wxSizer * szrH
Definition: Equalization.h:247
EditCurvesDialog::OnListSelectionChange
void OnListSelectionChange(wxListEvent &event)
Definition: Equalization.cpp:3871
WaveTrack::Join
void Join(double t0, double t1)
Definition: WaveTrack.cpp:1485
ShuttleGuiBase::AddWindow
wxWindow * AddWindow(wxWindow *pWindow)
Definition: ShuttleGui.cpp:292
FileConfig::Flush
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:151
EffectEqualization::EnvLinToLog
void EnvLinToLog(void)
Definition: Equalization.cpp:2523
ID_Linear
@ ID_Linear
Definition: Equalization.cpp:132
EffectEqualization::OnCurve
void OnCurve(wxCommandEvent &event)
Definition: Equalization.cpp:2912
anonymous_namespace{Equalization.cpp}::reg2
BuiltinEffectsModule::Registration< EffectEqualizationCurve > reg2
Definition: Equalization.cpp:204
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:495
EffectEqualization::~EffectEqualization
virtual ~EffectEqualization()
Definition: Equalization.cpp:331
EffectEqualization::mDisallowCustom
bool mDisallowCustom
Definition: Equalization.h:225
BuiltinEffectsModule::Registration
Definition: LoadEffects.h:40
XMLFileReader
Reads a file and passes the results through an XMLTagHandler.
Definition: XMLFileReader.h:20
ShuttleGui::Position
ShuttleGui & Position(int flags)
Definition: ShuttleGui.h:719
ShuttleGuiBase::GetSizer
wxSizer * GetSizer()
Definition: ShuttleGui.h:513
Effect::mUIParent
wxWindow * mUIParent
Definition: Effect.h:477
FileDialogWrapper
Definition: wxPanelWrapper.h:165
EditCurvesDialog::Populate
void Populate()
Creates the dialog and its contents.
Definition: Equalization.cpp:3392
names
static TranslatableStrings names
Definition: Tags.cpp:743
_
#define _(s)
Definition: Internat.h:76
EffectEqualization::SetAutomationParameters
bool SetAutomationParameters(CommandParameters &parms) override
Definition: Equalization.cpp:421
EffectEqualization::Symbol
static const ComponentInterfaceSymbol Symbol
Definition: Equalization.h:96
ShuttleGuiBase::AddListControlReportMode
wxListCtrl * AddListControlReportMode(std::initializer_list< const ListControlColumn > columns={}, long listControlStyles=0)
Definition: ShuttleGui.cpp:795
ShuttleGui::ConnectRoot
auto ConnectRoot(wxEventTypeTag< Tag > eventType, void(Handler::*func)(Argument &)) -> typename std::enable_if< std::is_base_of< Argument, Tag >::value, ShuttleGui & >::type
Definition: ShuttleGui.h:706
sampleCount
Definition: Types.h:589
EffectEqualization::mGridOnOff
wxCheckBox * mGridOnOff
Definition: Equalization.h:263
EffectEqualization::Process
bool Process() override
Definition: Equalization.cpp:707
XMLFileWriter
Wrapper to output XML data to files.
Definition: XMLWriter.h:81
EffectEqualization::ForceRecalc
void ForceRecalc()
Definition: Equalization.cpp:2081
Verbatim
TranslatableString Verbatim(wxString str)
Definition: Types.h:581
EffectEqualization::szrL
wxSizer * szrL
Definition: Equalization.h:249
GuardedCall
R GuardedCall(const F1 &body, const F2 &handler=F2::Default(), const F3 &delayedHandler={})
Execute some code on any thread; catch any AudacityException; enqueue error report on the main thread...
Definition: AudacityException.h:189
EffectEqualization::GetDefaultFileName
bool GetDefaultFileName(wxFileName &fileName)
Definition: Equalization.cpp:1812
EffectEqualization::mCurveName
wxString mCurveName
Definition: Equalization.h:211
ID_Interp
@ ID_Interp
Definition: Equalization.cpp:131
EffectEqualization::mDrawMode
bool mDrawMode
Definition: Equalization.h:215
Equalization.h
EqualizationPanel::~EqualizationPanel
~EqualizationPanel()
Definition: Equalization.cpp:3097
EffectEqualization::mWhens
double mWhens[NUM_PTS]
Definition: Equalization.h:219
EffectEqualization::EnvelopeUpdated
void EnvelopeUpdated()
Definition: Equalization.cpp:2092
RulerPanel::Options
Definition: Ruler.h:234
EffectEqualization::GetPrefsPrefix
wxString GetPrefsPrefix()
Definition: Equalization.cpp:587
EQCurveArray
std::vector< EQCurve > EQCurveArray
Definition: Equalization.h:86
EffectEqualization::TransferDataToWindow
bool TransferDataToWindow() override
Definition: Equalization.cpp:1176
EffectEqualization::Filter
void Filter(size_t len, float *buffer)
Definition: Equalization.cpp:1568
EditCurvesDialog::UpButtonID
@ UpButtonID
Definition: Equalization.h:363
RulerPanel::ruler
Ruler ruler
Definition: Ruler.h:288
ThemeBase::Colour
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1192
LINEAR_TO_DB
#define LINEAR_TO_DB(x)
Definition: MemoryX.h:721
Envelope::Flatten
void Flatten(double value)
Definition: Envelope.cpp:133
EffectEqualization::mFreqRuler
RulerPanel * mFreqRuler
Definition: Equalization.h:223
M_PI
#define M_PI
Definition: Distortion.cpp:29
EqualizationPanel::mHeight
int mHeight
Definition: Equalization.h:338
EffectEqualization::szrV
wxSizer * szrV
Definition: Equalization.h:246
ShuttleGui::AddStandardButtons
void AddStandardButtons(long buttons=eOkButton|eCancelButton, wxWindow *extra=NULL)
Definition: ShuttleGui.cpp:2406
EQCurve
EQCurve is used with EffectEqualization.
Definition: Equalization.h:72
EffectEqualization::mFFTBuffer
Floats mFFTBuffer
Definition: Equalization.h:209
EffectEqualization::UpdateDraw
void UpdateDraw()
Definition: Equalization.cpp:2385
EffectEqualization::mHiFreq
double mHiFreq
Definition: Equalization.h:227
ShuttleGui::GetMode
teShuttleMode GetMode()
Definition: ShuttleGui.h:759
EffectEqualization::mEnvelope
Envelope * mEnvelope
Definition: Equalization.h:236
kInterpolations
kInterpolations
Definition: Equalization.cpp:149
FileNames::FileTypes
std::vector< FileType > FileTypes
Definition: FileNames.h:79
EffectEqualization::OnSliderDBMIN
void OnSliderDBMIN(wxCommandEvent &event)
Definition: Equalization.cpp:2899
GetFFT
HFFT GetFFT(size_t fftlen)
Definition: RealFFTf.cpp:108
theTheme
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:201
EffectEqualization::CalcFilter
bool CalcFilter()
Definition: Equalization.cpp:1455
kCosine
@ kCosine
Definition: Equalization.cpp:151
EffectEqualization::mdBRuler
RulerPanel * mdBRuler
Definition: Equalization.h:222
params
EffectDistortion::Params params
Definition: Distortion.cpp:99
ShuttleGuiBase::AddPrompt
void AddPrompt(const TranslatableString &Prompt, int wrapWidth=0)
Right aligned text string.
Definition: ShuttleGui.cpp:231
NUM_PTS
#define NUM_PTS
Definition: Equalization.h:15
TranslatableString::Translation
wxString Translation() const
Definition: Types.h:337
ShuttleGuiBase::SetBorder
void SetBorder(int Border)
Definition: ShuttleGui.h:497
EditCurvesDialog::ExportButtonID
@ ExportButtonID
Definition: Equalization.h:368
EditCurvesDialog::OnLibrary
void OnLibrary(wxCommandEvent &event)
Definition: Equalization.cpp:3826
ShuttleParams::Define
virtual void Define(bool &var, const wxChar *key, const bool vdefault, const bool vmin=false, const bool vmax=false, const bool vscl=false)
Definition: Shuttle.cpp:332
EffectEqualization::mFilterFuncI
Floats mFilterFuncI
Definition: Equalization.h:209
UPDATE_ALL
#define UPDATE_ALL
Definition: Equalization.cpp:159
TranslatableString::Format
TranslatableString & Format(Args &&...args) &
Definition: Types.h:363
ShuttleGuiBase::AddVariableText
wxStaticText * AddVariableText(const TranslatableString &Str, bool bCenter=false, int PositionFlags=0, int wrapWidth=0)
Definition: ShuttleGui.cpp:456
ID_Clear
@ ID_Clear
Definition: Equalization.cpp:126
EffectEqualization::OnClear
void OnClear(wxCommandEvent &event)
Definition: Equalization.cpp:2936
EffectEqualization::OnSliderM
void OnSliderM(wxCommandEvent &event)
Definition: Equalization.cpp:2893
ShuttleGuiBase::EndStatic
void EndStatic()
Definition: ShuttleGui.cpp:915
EffectEqualization::mDrawGrid
bool mDrawGrid
Definition: Equalization.h:217
EffectType
EffectType
Definition: EffectInterface.h:55
safenew
#define safenew
Definition: MemoryX.h:8
EffectEqualization::OnErase
void OnErase(wxEvent &event)
Definition: Equalization.cpp:2822
EditCurvesDialog::OnOK
void OnOK(wxCommandEvent &event)
Definition: Equalization.cpp:3843
lrint
#define lrint(dbl)
Definition: float_cast.h:148
values
const wxChar * values
Definition: Equalization.cpp:471
ShuttleGuiBase::SetStretchyCol
void SetStretchyCol(int i)
Used to modify an already placed FlexGridSizer to make a column stretchy.
Definition: ShuttleGui.cpp:195
EffectEqualization::mInterpChoice
wxChoice * mInterpChoice
Definition: Equalization.h:264
EditCurvesDialog::OnDefaults
void OnDefaults(wxCommandEvent &event)
Definition: Equalization.cpp:3832
Ruler::DrawGrid
void DrawGrid(wxDC &dc, int length, bool minor=true, bool major=true, int xOffset=0, int yOffset=0) const
Definition: Ruler.cpp:1521
EffectEqualizationCurve::Symbol
static const ComponentInterfaceSymbol Symbol
Definition: Equalization.h:287
EffectEqualization::OnInterp
void OnInterp(wxCommandEvent &event)
Definition: Equalization.cpp:2870
FileDialog::ShowModal
virtual int ShowModal()
Definition: gtk/FileDialogPrivate.cpp:445
TrackList::Selected
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:1371
anonymous_namespace{Menus.cpp}::Options
std::vector< CommandFlagOptions > & Options()
Definition: Menus.cpp:526
EffectEqualization::PopulateOrExchange
void PopulateOrExchange(ShuttleGui &S) override
Definition: Equalization.cpp:757
END_EVENT_TABLE
END_EVENT_TABLE()
EffectEqualization::ValidateUI
bool ValidateUI() override
Definition: Equalization.cpp:539
EffectEqualization::OnSlider
void OnSlider(wxCommandEvent &event)
Definition: Equalization.cpp:2832
ShuttleGuiBase::SetStretchyRow
void SetStretchyRow(int i)
Used to modify an already placed FlexGridSizer to make a row stretchy.
Definition: ShuttleGui.cpp:205
Effect::TrackProgress
bool TrackProgress(int whichTrack, double frac, const TranslatableString &={})
Definition: Effect.cpp:2024
ArrayOf< double >
ShuttleSetAutomation::SetForWriting
void SetForWriting(CommandParameters *pEap)
Definition: Shuttle.h:113
EffectEqualization::Init
bool Init() override
Definition: Equalization.cpp:657
Envelope::GetValues
static void GetValues(const Envelope &env, double aligned_time, double sampleDur, double *buffer, int bufferLen, int leftOffset, const ZoomInfo &zoomInfo)
Get many envelope points for pixel columns at once, but don't assume uniform time per pixel.
Definition: Envelope.cpp:1471
EffectEqualization::mSliders
wxSlider * mSliders[NUMBER_OF_BANDS]
Definition: Equalization.h:271
RulerPanel::Options::Log
Options & Log(bool l)
Definition: Ruler.h:244
ReadAndVerifyBool
#define ReadAndVerifyBool(name)
Definition: Effect.h:634
Envelope::GetNumberOfPoints
size_t GetNumberOfPoints() const
Return number of points.
Definition: Envelope.cpp:692
EVT_COMMAND_RANGE
EVT_COMMAND_RANGE(ID_Slider, ID_Slider+NUMBER_OF_BANDS - 1, wxEVT_COMMAND_SLIDER_UPDATED, EffectEqualization::OnSlider) EffectEqualization
Definition: Equalization.cpp:217
EqualizationPanel::mEnvRect
wxRect mEnvRect
Definition: Equalization.h:336
EditCurvesDialog::OnDown
void OnDown(wxCommandEvent &event)
Definition: Equalization.cpp:3491
EffectEqualization::mDraw
wxRadioButton * mDraw
Definition: Equalization.h:260
EffectEqualization::UpdateDefaultCurves
void UpdateDefaultCurves(bool updateAll=false)
Definition: Equalization.cpp:1694
ShuttleGui
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:638
Param
Param(FilterLength, int, wxT("FilterLength"), 8191, 21, 8191, 0)
anonymous_namespace{Equalization.cpp}::reg3
BuiltinEffectsModule::Registration< EffectEqualizationGraphic > reg3
Definition: Equalization.cpp:209
Internat::CompatibleToDouble
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:139
DeleteButtonID
@ DeleteButtonID
Definition: BatchProcessDialog.cpp:512
ExportButtonID
@ ExportButtonID
Definition: BatchProcessDialog.cpp:505
EffectEqualization::OnInvert
void OnInvert(wxCommandEvent &event)
Definition: Equalization.cpp:2941
EqualizationPanel::mOutr
Floats mOutr
Definition: Equalization.h:342
EffectEqualization::mCurve
wxChoice * mCurve
Definition: Equalization.h:265
EffectEqualization::ErrMin
void ErrMin(void)
Definition: Equalization.cpp:2565
EffectEqualization::windowSize
static const size_t windowSize
Definition: Equalization.h:142