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