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 "../xml/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  std::vector<EnvPoint> envPoints;
1388 
1389  // now move the appropriate bit of the output back to the track
1390  // (this could be enhanced in the future to use the tails)
1391  double offsetT0 = t->LongSamplesToTime(offset);
1392  double lenT = t->LongSamplesToTime(originalLen);
1393  // 'start' is the sample offset in 't', the passed in track
1394  // 'startT' is the equivalent time value
1395  // 'output' starts at zero
1396  double startT = t->LongSamplesToTime(start);
1397 
1398  //output has one waveclip for the total length, even though
1399  //t might have whitespace separating multiple clips
1400  //we want to maintain the original clip structure, so
1401  //only paste the intersections of the NEW clip.
1402 
1403  //Find the bits of clips that need replacing
1404  std::vector<std::pair<double, double> > clipStartEndTimes;
1405  std::vector<std::pair<double, double> > clipRealStartEndTimes; //the above may be truncated due to a clip being partially selected
1406  for (const auto &clip : t->GetClips())
1407  {
1408  double clipStartT;
1409  double clipEndT;
1410 
1411  clipStartT = clip->GetStartTime();
1412  clipEndT = clip->GetEndTime();
1413  if( clipEndT <= startT )
1414  continue; // clip is not within selection
1415  if( clipStartT >= startT + lenT )
1416  continue; // clip is not within selection
1417 
1418  //save the actual clip start/end so that we can rejoin them after we paste.
1419  clipRealStartEndTimes.push_back(std::pair<double,double>(clipStartT,clipEndT));
1420 
1421  if( clipStartT < startT ) // does selection cover the whole clip?
1422  clipStartT = startT; // don't copy all the NEW clip
1423  if( clipEndT > startT + lenT ) // does selection cover the whole clip?
1424  clipEndT = startT + lenT; // don't copy all the NEW clip
1425 
1426  //save them
1427  clipStartEndTimes.push_back(std::pair<double,double>(clipStartT,clipEndT));
1428 
1429  // Save the envelope points
1430  const auto &env = *clip->GetEnvelope();
1431  for (size_t i = 0, numPoints = env.GetNumberOfPoints(); i < numPoints; ++i) {
1432  envPoints.push_back(env[i]);
1433  }
1434  }
1435 
1436  //now go thru and replace the old clips with NEW
1437  for(unsigned int i = 0; i < clipStartEndTimes.size(); i++)
1438  {
1439  //remove the old audio and get the NEW
1440  t->Clear(clipStartEndTimes[i].first,clipStartEndTimes[i].second);
1441  auto toClipOutput = output->Copy(clipStartEndTimes[i].first-startT+offsetT0,clipStartEndTimes[i].second-startT+offsetT0);
1442  //put the processed audio in
1443  t->Paste(clipStartEndTimes[i].first, toClipOutput.get());
1444  //if the clip was only partially selected, the Paste will have created a split line. Join is needed to take care of this
1445  //This is not true when the selection is fully contained within one clip (second half of conditional)
1446  if( (clipRealStartEndTimes[i].first != clipStartEndTimes[i].first ||
1447  clipRealStartEndTimes[i].second != clipStartEndTimes[i].second) &&
1448  !(clipRealStartEndTimes[i].first <= startT &&
1449  clipRealStartEndTimes[i].second >= startT+lenT) )
1450  t->Join(clipRealStartEndTimes[i].first,clipRealStartEndTimes[i].second);
1451  }
1452 
1453  // Restore the envelope points
1454  for (auto point : envPoints) {
1455  WaveClip *clip = t->GetClipAtTime(point.GetT());
1456  clip->GetEnvelope()->Insert(point.GetT(), point.GetVal());
1457  }
1458  }
1459 
1460  return bLoopSuccess;
1461 }
1462 
1464 {
1465  double loLog = log10(mLoFreq);
1466  double hiLog = log10(mHiFreq);
1467  double denom = hiLog - loLog;
1468 
1469  double delta = mHiFreq / ((double)(mWindowSize / 2.));
1470  double val0;
1471  double val1;
1472 
1473  if( IsLinear() )
1474  {
1475  val0 = mLinEnvelope->GetValue(0.0); //no scaling required - saved as dB
1476  val1 = mLinEnvelope->GetValue(1.0);
1477  }
1478  else
1479  {
1480  val0 = mLogEnvelope->GetValue(0.0); //no scaling required - saved as dB
1481  val1 = mLogEnvelope->GetValue(1.0);
1482  }
1483  mFilterFuncR[0] = val0;
1484  double freq = delta;
1485 
1486  for(size_t i = 1; i <= mWindowSize / 2; i++)
1487  {
1488  double when;
1489  if( IsLinear() )
1490  when = freq/mHiFreq;
1491  else
1492  when = (log10(freq) - loLog)/denom;
1493  if(when < 0.)
1494  {
1495  mFilterFuncR[i] = val0;
1496  }
1497  else if(when > 1.0)
1498  {
1499  mFilterFuncR[i] = val1;
1500  }
1501  else
1502  {
1503  if( IsLinear() )
1504  mFilterFuncR[i] = mLinEnvelope->GetValue(when);
1505  else
1506  mFilterFuncR[i] = mLogEnvelope->GetValue(when);
1507  }
1508  freq += delta;
1509  }
1510  mFilterFuncR[mWindowSize / 2] = val1;
1511 
1513 
1514  {
1515  size_t i = 1;
1516  for(; i < mWindowSize / 2; i++)
1517  {
1519  mFilterFuncR[mWindowSize - i] = mFilterFuncR[i]; //Fill entire array
1520  }
1521  mFilterFuncR[i] = DB_TO_LINEAR(mFilterFuncR[i]); //do last one
1522  }
1523 
1524  //transfer to time domain to do the padding and windowing
1525  Floats outr{ mWindowSize };
1526  Floats outi{ mWindowSize };
1527  InverseRealFFT(mWindowSize, mFilterFuncR.get(), NULL, outr.get()); // To time domain
1528 
1529  {
1530  size_t i = 0;
1531  for(; i <= (mM - 1) / 2; i++)
1532  { //Windowing - could give a choice, fixed for now - MJS
1533  // double mult=0.54-0.46*cos(2*M_PI*(i+(mM-1)/2.0)/(mM-1)); //Hamming
1534  //Blackman
1535  double mult =
1536  0.42 -
1537  0.5 * cos(2 * M_PI * (i + (mM - 1) / 2.0) / (mM - 1)) +
1538  .08 * cos(4 * M_PI * (i + (mM - 1) / 2.0) / (mM - 1));
1539  outr[i] *= mult;
1540  if(i != 0){
1541  outr[mWindowSize - i] *= mult;
1542  }
1543  }
1544  for(; i <= mWindowSize / 2; i++)
1545  { //Padding
1546  outr[i] = 0;
1547  outr[mWindowSize - i] = 0;
1548  }
1549  }
1550  Floats tempr{ mM };
1551  {
1552  size_t i = 0;
1553  for(; i < (mM - 1) / 2; i++)
1554  { //shift so that padding on right
1555  tempr[(mM - 1) / 2 + i] = outr[i];
1556  tempr[i] = outr[mWindowSize - (mM - 1) / 2 + i];
1557  }
1558  tempr[(mM - 1) / 2 + i] = outr[i];
1559  }
1560 
1561  for (size_t i = 0; i < mM; i++)
1562  { //and copy useful values back
1563  outr[i] = tempr[i];
1564  }
1565  for (size_t i = mM; i < mWindowSize; i++)
1566  { //rest is padding
1567  outr[i]=0.;
1568  }
1569 
1570  //Back to the frequency domain so we can use it
1571  RealFFT(mWindowSize, outr.get(), mFilterFuncR.get(), mFilterFuncI.get());
1572 
1573  return TRUE;
1574 }
1575 
1576 void EffectEqualization::Filter(size_t len, float *buffer)
1577 {
1578  float re,im;
1579  // Apply FFT
1580  RealFFTf(buffer, hFFT.get());
1581  //FFT(len, false, inr, NULL, outr, outi);
1582 
1583  // Apply filter
1584  // DC component is purely real
1585  mFFTBuffer[0] = buffer[0] * mFilterFuncR[0];
1586  for(size_t i = 1; i < (len / 2); i++)
1587  {
1588  re=buffer[hFFT->BitReversed[i] ];
1589  im=buffer[hFFT->BitReversed[i]+1];
1590  mFFTBuffer[2*i ] = re*mFilterFuncR[i] - im*mFilterFuncI[i];
1591  mFFTBuffer[2*i+1] = re*mFilterFuncI[i] + im*mFilterFuncR[i];
1592  }
1593  // Fs/2 component is purely real
1594  mFFTBuffer[1] = buffer[1] * mFilterFuncR[len/2];
1595 
1596  // Inverse FFT and normalization
1597  InverseRealFFTf(mFFTBuffer.get(), hFFT.get());
1598  ReorderToTime(hFFT.get(), mFFTBuffer.get(), buffer);
1599 }
1600 
1601 //
1602 // Load external curves with fallback to default, then message
1603 //
1604 void EffectEqualization::LoadCurves(const wxString &fileName, bool append)
1605 {
1606 // We've disabled the XML management of curves.
1607 // Just going via .cfg files now.
1608 #if 1
1609  (void)fileName;
1610  (void)append;
1611  mCurves.clear();
1612  mCurves.push_back( wxT("unnamed") ); // we still need a default curve to use
1613 #else
1614  // Construct normal curve filename
1615  //
1616  // LLL: Wouldn't you know that as of WX 2.6.2, there is a conflict
1617  // between wxStandardPaths and wxConfig under Linux. The latter
1618  // creates a normal file as "$HOME/.audacity", while the former
1619  // expects the ".audacity" portion to be a directory.
1620  // MJS: I don't know what the above means, or if I have broken it.
1621  wxFileName fn;
1622 
1623  if(fileName.empty()) {
1624  // Check if presets are up to date.
1625  wxString eqCurvesCurrentVersion = wxString::Format(wxT("%d.%d"), EQCURVES_VERSION, EQCURVES_REVISION);
1626  wxString eqCurvesInstalledVersion;
1627  gPrefs->Read(GetPrefsPrefix() + "PresetVersion", &eqCurvesInstalledVersion, wxT(""));
1628 
1629  bool needUpdate = (eqCurvesCurrentVersion != eqCurvesInstalledVersion);
1630 
1631  // UpdateDefaultCurves allows us to import NEW factory presets only,
1632  // or update all factory preset curves.
1633  if (needUpdate)
1635  fn = wxFileName( FileNames::DataDir(), wxT("EQCurves.xml") );
1636  }
1637  else
1638  fn = fileName; // user is loading a specific set of curves
1639 
1640  // If requested file doesn't exist...
1641  if( !fn.FileExists() && !GetDefaultFileName(fn) ) {
1642  mCurves.clear();
1643  /* i18n-hint: name of the 'unnamed' custom curve */
1644  mCurves.push_back( _("unnamed") ); // we still need a default curve to use
1645  return;
1646  }
1647 
1648  EQCurve tempCustom(wxT("temp"));
1649  if( append == false ) // Start from scratch
1650  mCurves.clear();
1651  else // appending so copy and remove 'unnamed', to replace later
1652  {
1653  tempCustom.points = mCurves.back().points;
1654  mCurves.pop_back();
1655  }
1656 
1657  // Load the curves
1658  XMLFileReader reader;
1659  const wxString fullPath{ fn.GetFullPath() };
1660  if( !reader.Parse( this, fullPath ) )
1661  {
1662  /* i18n-hint: EQ stands for 'Equalization'.*/
1663  auto msg = XO("Error Loading EQ Curves from file:\n%s\nError message says:\n%s")
1664  .Format( fullPath, reader.GetErrorStr() );
1665  // Inform user of load failure
1667  msg,
1668  wxOK | wxCENTRE,
1669  XO("Error Loading EQ Curves") );
1670  mCurves.push_back( _("unnamed") ); // we always need a default curve to use
1671  return;
1672  }
1673 
1674  // Move "unnamed" to end, if it exists in current language.
1675  int numCurves = mCurves.size();
1676  int curve;
1677  EQCurve tempUnnamed(wxT("tempUnnamed"));
1678  for( curve = 0; curve < numCurves-1; curve++ )
1679  {
1680  if( mCurves[curve].Name == _("unnamed") )
1681  {
1682  tempUnnamed.points = mCurves[curve].points;
1683  mCurves.erase(mCurves.begin() + curve);
1684  mCurves.push_back( _("unnamed") ); // add 'unnamed' back at the end
1685  mCurves.back().points = tempUnnamed.points;
1686  }
1687  }
1688 
1689  if( mCurves.back().Name != _("unnamed") )
1690  mCurves.push_back( _("unnamed") ); // we always need a default curve to use
1691  if( append == true )
1692  {
1693  mCurves.back().points = tempCustom.points;
1694  }
1695 #endif
1696  return;
1697 }
1698 
1699 //
1700 // Update presets to match Audacity version.
1701 //
1702 void EffectEqualization::UpdateDefaultCurves(bool updateAll /* false */)
1703 {
1704  if (mCurves.size() == 0)
1705  return;
1706 
1707  wxString unnamed = wxT("unnamed");
1708 
1709  // Save the "unnamed" curve and remove it so we can add it back as the final curve.
1710  EQCurve userUnnamed(wxT("temp"));
1711  userUnnamed = mCurves.back();
1712  mCurves.pop_back();
1713 
1714  EQCurveArray userCurves = mCurves;
1715  mCurves.clear();
1716  // We only wamt to look for the shipped EQDefaultCurves.xml
1717  wxFileName fn = wxFileName(FileNames::ResourcesDir(), wxT("EQDefaultCurves.xml"));
1718  wxLogDebug(wxT("Attempting to load EQDefaultCurves.xml from %s"),fn.GetFullPath());
1719  XMLFileReader reader;
1720 
1721  if(!reader.Parse(this, fn.GetFullPath())) {
1722  wxLogError(wxT("EQDefaultCurves.xml could not be read."));
1723  return;
1724  }
1725  else {
1726  wxLogDebug(wxT("Loading EQDefaultCurves.xml successful."));
1727  }
1728 
1729  EQCurveArray defaultCurves = mCurves;
1730  mCurves.clear(); // clear now so that we can sort then add back.
1731 
1732  // Remove "unnamed" if it exists.
1733  if (defaultCurves.back().Name == unnamed) {
1734  defaultCurves.pop_back();
1735  }
1736  else {
1737  wxLogError(wxT("Error in EQDefaultCurves.xml"));
1738  }
1739 
1740  int numUserCurves = userCurves.size();
1741  int numDefaultCurves = defaultCurves.size();
1742  EQCurve tempCurve(wxT("test"));
1743 
1744  if (updateAll) {
1745  // Update all factory preset curves.
1746  // Sort and add factory defaults first;
1747  mCurves = defaultCurves;
1748  std::sort(mCurves.begin(), mCurves.end());
1749  // then add remaining user curves:
1750  for (int curveCount = 0; curveCount < numUserCurves; curveCount++) {
1751  bool isCustom = true;
1752  tempCurve = userCurves[curveCount];
1753  // is the name in the default set?
1754  for (int defCurveCount = 0; defCurveCount < numDefaultCurves; defCurveCount++) {
1755  if (tempCurve.Name == mCurves[defCurveCount].Name) {
1756  isCustom = false;
1757  break;
1758  }
1759  }
1760  // if tempCurve is not in the default set, add it to mCurves.
1761  if (isCustom) {
1762  mCurves.push_back(tempCurve);
1763  }
1764  }
1765  }
1766  else {
1767  // Import NEW factory defaults but retain all user modified curves.
1768  for (int defCurveCount = 0; defCurveCount < numDefaultCurves; defCurveCount++) {
1769  bool isUserCurve = false;
1770  // Add if the curve is in the user's set (preserve user's copy)
1771  for (int userCurveCount = 0; userCurveCount < numUserCurves; userCurveCount++) {
1772  if (userCurves[userCurveCount].Name == defaultCurves[defCurveCount].Name) {
1773  isUserCurve = true;
1774  mCurves.push_back(userCurves[userCurveCount]);
1775  break;
1776  }
1777  }
1778  if (!isUserCurve) {
1779  mCurves.push_back(defaultCurves[defCurveCount]);
1780  }
1781  }
1782  std::sort(mCurves.begin(), mCurves.end());
1783  // now add the rest of the user's curves.
1784  for (int userCurveCount = 0; userCurveCount < numUserCurves; userCurveCount++) {
1785  bool isDefaultCurve = false;
1786  tempCurve = userCurves[userCurveCount];
1787  for (int defCurveCount = 0; defCurveCount < numDefaultCurves; defCurveCount++) {
1788  if (tempCurve.Name == defaultCurves[defCurveCount].Name) {
1789  isDefaultCurve = true;
1790  break;
1791  }
1792  }
1793  if (!isDefaultCurve) {
1794  mCurves.push_back(tempCurve);
1795  }
1796  }
1797  }
1798  defaultCurves.clear();
1799  userCurves.clear();
1800 
1801  // Add back old "unnamed"
1802  if(userUnnamed.Name == unnamed) {
1803  mCurves.push_back( userUnnamed ); // we always need a default curve to use
1804  }
1805 
1806  SaveCurves();
1807 
1808  // Write current EqCurve version number
1809  // TODO: Probably better if we used pluginregistry.cfg
1810  wxString eqCurvesCurrentVersion = wxString::Format(wxT("%d.%d"), EQCURVES_VERSION, EQCURVES_REVISION);
1811  gPrefs->Write(GetPrefsPrefix()+"PresetVersion", eqCurvesCurrentVersion);
1812  gPrefs->Flush();
1813 
1814  return;
1815 }
1816 
1817 //
1818 // Get fully qualified filename of EQDefaultCurves.xml
1819 //
1820 bool EffectEqualization::GetDefaultFileName(wxFileName &fileName)
1821 {
1822  // look in data dir first, in case the user has their own defaults (maybe downloaded ones)
1823  fileName = wxFileName( FileNames::DataDir(), wxT("EQDefaultCurves.xml") );
1824  if( !fileName.FileExists() )
1825  { // Default file not found in the data dir. Fall back to Resources dir.
1826  // See http://docs.wxwidgets.org/trunk/classwx_standard_paths.html#5514bf6288ee9f5a0acaf065762ad95d
1827  fileName = wxFileName( FileNames::ResourcesDir(), wxT("EQDefaultCurves.xml") );
1828  }
1829  if( !fileName.FileExists() )
1830  {
1831  // LLL: Is there really a need for an error message at all???
1832  //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")
1833  // .Format( FileNames::DataDir() );
1834  //BasicUI::ShowErrorDialog( wxWidgetsWindowPlacement{ mUIParent },
1835  // XO("EQCurves.xml and EQDefaultCurves.xml missing"),
1836  // errorMessage, wxT("http://wiki.audacityteam.org/wiki/EQCurvesDownload"), false);
1837 
1838  // Have another go at finding EQCurves.xml in the data dir, in case 'help' helped
1839  fileName = wxFileName( FileNames::DataDir(), wxT("EQDefaultCurves.xml") );
1840  }
1841  return (fileName.FileExists());
1842 }
1843 
1844 
1845 //
1846 // Save curves to external file
1847 //
1848 void EffectEqualization::SaveCurves(const wxString &fileName)
1849 {
1850  wxFileName fn;
1851  if( fileName.empty() )
1852  {
1853  // Construct default curve filename
1854  //
1855  // LLL: Wouldn't you know that as of WX 2.6.2, there is a conflict
1856  // between wxStandardPaths and wxConfig under Linux. The latter
1857  // creates a normal file as "$HOME/.audacity", while the former
1858  // expects the ".audacity" portion to be a directory.
1859  fn = wxFileName( FileNames::DataDir(), wxT("EQCurves.xml") );
1860 
1861  // If the directory doesn't exist...
1862  if( !fn.DirExists() )
1863  {
1864  // Attempt to create it
1865  if( !fn.Mkdir( fn.GetPath(), 511, wxPATH_MKDIR_FULL ) )
1866  {
1867  // MkDir() will emit message
1868  return;
1869  }
1870  }
1871  }
1872  else
1873  fn = fileName;
1874 
1875  GuardedCall( [&] {
1876  // Create/Open the file
1877  const wxString fullPath{ fn.GetFullPath() };
1878  XMLFileWriter eqFile{ fullPath, XO("Error Saving Equalization Curves") };
1879 
1880  // Write the curves
1881  WriteXML( eqFile );
1882 
1883  eqFile.Commit();
1884  } );
1885 }
1886 
1887 //
1888 // Make the passed curve index the active one
1889 //
1890 void EffectEqualization::setCurve(int currentCurve)
1891 {
1892  // Set current choice
1893  wxASSERT( currentCurve < (int) mCurves.size() );
1894  Select(currentCurve);
1895 
1896  Envelope *env;
1897  int numPoints = (int) mCurves[currentCurve].points.size();
1898 
1899  if (mLin) { // linear freq mode
1900  env = mLinEnvelope.get();
1901  }
1902  else { // log freq mode
1903  env = mLogEnvelope.get();
1904  }
1905  env->Flatten(0.);
1906  env->SetTrackLen(1.0);
1907 
1908  // Handle special case of no points.
1909  if (numPoints == 0) {
1910  ForceRecalc();
1911  return;
1912  }
1913 
1914  double when, value;
1915 
1916  // Handle special case 1 point.
1917  if (numPoints == 1) {
1918  // only one point, so ensure it is in range then return.
1919  when = mCurves[currentCurve].points[0].Freq;
1920  if (mLin) {
1921  when = when / mHiFreq;
1922  }
1923  else { // log scale
1924  // We don't go below loFreqI (20 Hz) in log view.
1925  double loLog = log10((double)loFreqI);
1926  double hiLog = log10(mHiFreq);
1927  double denom = hiLog - loLog;
1928  when = (log10(std::max((double) loFreqI, when)) - loLog)/denom;
1929  }
1930  value = mCurves[currentCurve].points[0].dB;
1931  env->Insert(std::min(1.0, std::max(0.0, when)), value);
1932  ForceRecalc();
1933  return;
1934  }
1935 
1936  // We have at least two points, so ensure they are in frequency order.
1937  std::sort(mCurves[currentCurve].points.begin(),
1938  mCurves[currentCurve].points.end());
1939 
1940  if (mCurves[currentCurve].points[0].Freq < 0) {
1941  // Corrupt or invalid curve, so bail.
1942  ForceRecalc();
1943  return;
1944  }
1945 
1946  if(mLin) { // linear Hz scale
1947  for(int pointCount = 0; pointCount < numPoints; pointCount++) {
1948  when = mCurves[currentCurve].points[pointCount].Freq / mHiFreq;
1949  value = mCurves[currentCurve].points[pointCount].dB;
1950  if(when <= 1) {
1951  env->Insert(when, value);
1952  if (when == 1)
1953  break;
1954  }
1955  else {
1956  // There are more points at higher freqs,
1957  // so interpolate next one then stop.
1958  when = 1.0;
1959  double nextDB = mCurves[currentCurve].points[pointCount].dB;
1960  if (pointCount > 0) {
1961  double nextF = mCurves[currentCurve].points[pointCount].Freq;
1962  double lastF = mCurves[currentCurve].points[pointCount-1].Freq;
1963  double lastDB = mCurves[currentCurve].points[pointCount-1].dB;
1964  value = lastDB +
1965  ((nextDB - lastDB) *
1966  ((mHiFreq - lastF) / (nextF - lastF)));
1967  }
1968  else
1969  value = nextDB;
1970  env->Insert(when, value);
1971  break;
1972  }
1973  }
1974  }
1975  else { // log Hz scale
1976  double loLog = log10((double) loFreqI);
1977  double hiLog = log10(mHiFreq);
1978  double denom = hiLog - loLog;
1979  int firstAbove20Hz;
1980 
1981  // log scale EQ starts at 20 Hz (threshold of hearing).
1982  // so find the first point (if any) above 20 Hz.
1983  for (firstAbove20Hz = 0; firstAbove20Hz < numPoints; firstAbove20Hz++) {
1984  if (mCurves[currentCurve].points[firstAbove20Hz].Freq > loFreqI)
1985  break;
1986  }
1987 
1988  if (firstAbove20Hz == numPoints) {
1989  // All points below 20 Hz, so just use final point.
1990  when = 0.0;
1991  value = mCurves[currentCurve].points[numPoints-1].dB;
1992  env->Insert(when, value);
1993  ForceRecalc();
1994  return;
1995  }
1996 
1997  if (firstAbove20Hz > 0) {
1998  // At least one point is before 20 Hz and there are more
1999  // beyond 20 Hz, so interpolate the first
2000  double prevF = mCurves[currentCurve].points[firstAbove20Hz-1].Freq;
2001  prevF = log10(std::max(1.0, prevF)); // log zero is bad.
2002  double prevDB = mCurves[currentCurve].points[firstAbove20Hz-1].dB;
2003  double nextF = log10(mCurves[currentCurve].points[firstAbove20Hz].Freq);
2004  double nextDB = mCurves[currentCurve].points[firstAbove20Hz].dB;
2005  when = 0.0;
2006  value = nextDB - ((nextDB - prevDB) * ((nextF - loLog) / (nextF - prevF)));
2007  env->Insert(when, value);
2008  }
2009 
2010  // Now get the rest.
2011  for(int pointCount = firstAbove20Hz; pointCount < numPoints; pointCount++)
2012  {
2013  double flog = log10(mCurves[currentCurve].points[pointCount].Freq);
2014  wxASSERT(mCurves[currentCurve].points[pointCount].Freq >= loFreqI);
2015 
2016  when = (flog - loLog)/denom;
2017  value = mCurves[currentCurve].points[pointCount].dB;
2018  if(when <= 1.0) {
2019  env->Insert(when, value);
2020  }
2021  else {
2022  // This looks weird when adjusting curve in Draw mode if
2023  // there is a point off-screen.
2024 
2025  /*
2026  // we have a point beyond fs/2. Insert it so that env code can use it.
2027  // but just this one, we have no use for the rest
2028  env->SetTrackLen(when); // can't Insert if the envelope isn't long enough
2029  env->Insert(when, value);
2030  break;
2031  */
2032 
2033  // interpolate the final point instead
2034  when = 1.0;
2035  if (pointCount > 0) {
2036  double lastDB = mCurves[currentCurve].points[pointCount-1].dB;
2037  double logLastF =
2038  log10(mCurves[currentCurve].points[pointCount-1].Freq);
2039  value = lastDB +
2040  ((value - lastDB) *
2041  ((log10(mHiFreq) - logLastF) / (flog - logLastF)));
2042  }
2043  env->Insert(when, value);
2044  break;
2045  }
2046  }
2047  }
2048  ForceRecalc();
2049 }
2050 
2052 {
2053  setCurve((int) mCurves.size() - 1);
2054 }
2055 
2056 void EffectEqualization::setCurve(const wxString &curveName)
2057 {
2058  unsigned i = 0;
2059  for( i = 0; i < mCurves.size(); i++ )
2060  if( curveName == mCurves[ i ].Name )
2061  break;
2062  if( i == mCurves.size())
2063  {
2065  XO("Requested curve not found, using 'unnamed'"),
2066  wxOK|wxICON_ERROR,
2067  XO("Curve not found") );
2068  setCurve();
2069  }
2070  else
2071  setCurve( i );
2072 }
2073 
2074 //
2075 // Set NEW curve selection (safe to call outside of the UI)
2076 //
2078 {
2079  // Set current choice
2080  if (mCurve)
2081  {
2082  mCurve->SetSelection( curve );
2083  mCurveName = mCurves[ curve ].Name;
2084  }
2085 }
2086 
2087 //
2088 // Tell panel to recalc (safe to call outside of UI)
2089 //
2091 {
2092  if (mPanel)
2093  {
2094  mPanel->ForceRecalc();
2095  }
2096 }
2097 
2098 //
2099 // Capture updated envelope
2100 //
2102 {
2103  if (IsLinear())
2104  {
2105  EnvelopeUpdated(mLinEnvelope.get(), true);
2106  }
2107  else
2108  {
2109  EnvelopeUpdated(mLogEnvelope.get(), false);
2110  }
2111 }
2112 
2114 {
2115  // Allocate and populate point arrays
2116  size_t numPoints = env->GetNumberOfPoints();
2117  Doubles when{ numPoints };
2118  Doubles value{ numPoints };
2119  env->GetPoints( when.get(), value.get(), numPoints );
2120 
2121  // Clear the unnamed curve
2122  int curve = mCurves.size() - 1;
2123  mCurves[ curve ].points.clear();
2124 
2125  if(lin)
2126  {
2127  // Copy and convert points
2128  for (size_t point = 0; point < numPoints; point++)
2129  {
2130  double freq = when[ point ] * mHiFreq;
2131  double db = value[ point ];
2132 
2133  // Add it to the curve
2134  mCurves[ curve ].points.push_back( EQPoint( freq, db ) );
2135  }
2136  }
2137  else
2138  {
2139  double loLog = log10( 20. );
2140  double hiLog = log10( mHiFreq );
2141  double denom = hiLog - loLog;
2142 
2143  // Copy and convert points
2144  for (size_t point = 0; point < numPoints; point++)
2145  {
2146  double freq = pow( 10., ( ( when[ point ] * denom ) + loLog ));
2147  double db = value[ point ];
2148 
2149  // Add it to the curve
2150  mCurves[ curve ].points.push_back( EQPoint( freq, db ) );
2151  }
2152  }
2153  // Remember that we've updated the unnamed curve
2154  mDirty = true;
2155 
2156  // set 'unnamed' as the selected curve
2157  Select( (int) mCurves.size() - 1 );
2158 }
2159 
2160 //
2161 //
2162 //
2164 {
2165  return mDrawMode && mLin;
2166 }
2167 
2168 //
2169 // Flatten the curve
2170 //
2172 {
2173  mLogEnvelope->Flatten(0.);
2174  mLogEnvelope->SetTrackLen(1.0);
2175  mLinEnvelope->Flatten(0.);
2176  mLinEnvelope->SetTrackLen(1.0);
2177  ForceRecalc();
2178  if( !mDrawMode )
2179  {
2180  for( size_t i = 0; i < mBandsInUse; i++)
2181  {
2182  mSliders[i]->SetValue(0);
2183  mSlidersOld[i] = 0;
2184  mEQVals[i] = 0.;
2185 
2186  wxString tip;
2187  if( kThirdOct[i] < 1000.)
2188  tip.Printf( wxT("%dHz\n%.1fdB"), (int)kThirdOct[i], 0. );
2189  else
2190  tip.Printf( wxT("%gkHz\n%.1fdB"), kThirdOct[i]/1000., 0. );
2191  mSliders[i]->SetToolTip(tip);
2192  }
2193  }
2194  EnvelopeUpdated();
2195 }
2196 
2197 //
2198 // Process XML tags and handle the ones we recognize
2199 //
2200 bool EffectEqualization::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
2201 {
2202  // May want to add a version strings...
2203  if( !wxStrcmp( tag, wxT("equalizationeffect") ) )
2204  {
2205  return true;
2206  }
2207 
2208  // Located a NEW curve
2209  if( !wxStrcmp(tag, wxT("curve") ) )
2210  {
2211  // Process the attributes
2212  while( *attrs )
2213  {
2214  // Cache attr/value and bump to next
2215  const wxChar *attr = *attrs++;
2216  const wxChar *value = *attrs++;
2217 
2218  // Create a NEW curve and name it
2219  if( !wxStrcmp( attr, wxT("name") ) )
2220  {
2221  const wxString strValue = value;
2222  if (!XMLValueChecker::IsGoodString(strValue))
2223  return false;
2224  // check for a duplicate name and add (n) if there is one
2225  int n = 0;
2226  wxString strValueTemp = strValue;
2227  bool exists;
2228  do
2229  {
2230  exists = false;
2231  for(size_t i = 0; i < mCurves.size(); i++)
2232  {
2233  if(n>0)
2234  strValueTemp.Printf(wxT("%s (%d)"),strValue,n);
2235  if(mCurves[i].Name == strValueTemp)
2236  {
2237  exists = true;
2238  break;
2239  }
2240  }
2241  n++;
2242  }
2243  while(exists == true);
2244 
2245  mCurves.push_back( EQCurve( strValueTemp ) );
2246  }
2247  }
2248 
2249  // Tell caller it was processed
2250  return true;
2251  }
2252 
2253  // Located a NEW point
2254  if( !wxStrcmp( tag, wxT("point") ) )
2255  {
2256  // Set defaults in case attributes are missing
2257  double f = 0.0;
2258  double d = 0.0;
2259 
2260  // Process the attributes
2261  double dblValue;
2262  while( *attrs )
2263  { // Cache attr/value and bump to next
2264  const wxChar *attr = *attrs++;
2265  const wxChar *value = *attrs++;
2266 
2267  const wxString strValue = value;
2268 
2269  // Get the frequency
2270  if( !wxStrcmp( attr, wxT("f") ) )
2271  {
2272  if (!XMLValueChecker::IsGoodString(strValue) ||
2273  !Internat::CompatibleToDouble(strValue, &dblValue))
2274  return false;
2275  f = dblValue;
2276  }
2277  // Get the dB
2278  else if( !wxStrcmp( attr, wxT("d") ) )
2279  {
2280  if (!XMLValueChecker::IsGoodString(strValue) ||
2281  !Internat::CompatibleToDouble(strValue, &dblValue))
2282  return false;
2283  d = dblValue;
2284  }
2285  }
2286 
2287  // Create a NEW point
2288  mCurves[ mCurves.size() - 1 ].points.push_back( EQPoint( f, d ) );
2289 
2290  // Tell caller it was processed
2291  return true;
2292  }
2293 
2294  // Tell caller we didn't understand the tag
2295  return false;
2296 }
2297 
2298 //
2299 // Return handler for recognized tags
2300 //
2302 {
2303  if( !wxStrcmp( tag, wxT("equalizationeffect") ) )
2304  {
2305  return this;
2306  }
2307 
2308  if( !wxStrcmp( tag, wxT("curve") ) )
2309  {
2310  return this;
2311  }
2312 
2313  if( !wxStrcmp( tag, wxT("point") ) )
2314  {
2315  return this;
2316  }
2317 
2318  return NULL;
2319 }
2320 
2321 //
2322 // Write all of the curves to the XML file
2323 //
2325 // may throw
2326 {
2327  // Start our hierarchy
2328  xmlFile.StartTag( wxT( "equalizationeffect" ) );
2329 
2330  // Write all curves
2331  int numCurves = mCurves.size();
2332  int curve;
2333  for( curve = 0; curve < numCurves; curve++ )
2334  {
2335  // Start a NEW curve
2336  xmlFile.StartTag( wxT( "curve" ) );
2337  xmlFile.WriteAttr( wxT( "name" ), mCurves[ curve ].Name );
2338 
2339  // Write all points
2340  int numPoints = mCurves[ curve ].points.size();
2341  int point;
2342  for( point = 0; point < numPoints; point++ )
2343  {
2344  // Write NEW point
2345  xmlFile.StartTag( wxT( "point" ) );
2346  xmlFile.WriteAttr( wxT( "f" ), mCurves[ curve ].points[ point ].Freq, 12 );
2347  xmlFile.WriteAttr( wxT( "d" ), mCurves[ curve ].points[ point ].dB, 12 );
2348  xmlFile.EndTag( wxT( "point" ) );
2349  }
2350 
2351  // Terminate curve
2352  xmlFile.EndTag( wxT( "curve" ) );
2353  }
2354 
2355  // Terminate our hierarchy
2356  xmlFile.EndTag( wxT( "equalizationeffect" ) );
2357 }
2358 
2360 //
2361 // All EffectEqualization methods beyond this point interact with the UI, so
2362 // can't be called while the UI is not displayed.
2363 //
2365 
2367 {
2368 
2369  // Reload the curve names
2370  if( mCurve )
2371  mCurve->Clear();
2372  bool selectedCurveExists = false;
2373  for (size_t i = 0, cnt = mCurves.size(); i < cnt; i++)
2374  {
2375  if (mCurveName == mCurves[ i ].Name)
2376  selectedCurveExists = true;
2377  if( mCurve )
2378  mCurve->Append(mCurves[ i ].Name);
2379  }
2380  // In rare circumstances, mCurveName may not exist (bug 1891)
2381  if (!selectedCurveExists)
2382  mCurveName = mCurves[ (int)mCurves.size() - 1 ].Name;
2383  if( mCurve )
2384  mCurve->SetStringSelection(mCurveName);
2385 
2386  // Allow the control to resize
2387  if( mCurve )
2388  mCurve->SetMinSize({-1, -1});
2389 
2390  // Set initial curve
2391  setCurve( mCurveName );
2392 }
2393 
2395 {
2396  size_t numPoints = mLogEnvelope->GetNumberOfPoints();
2397  Doubles when{ numPoints };
2398  Doubles value{ numPoints };
2399  double deltadB = 0.1;
2400  double dx, dy, dx1, dy1, err;
2401 
2402  mLogEnvelope->GetPoints( when.get(), value.get(), numPoints );
2403 
2404  // set 'unnamed' as the selected curve
2405  EnvelopeUpdated();
2406 
2407  bool flag = true;
2408  while (flag)
2409  {
2410  flag = false;
2411  int numDeleted = 0;
2412  mLogEnvelope->GetPoints( when.get(), value.get(), numPoints );
2413  for (size_t j = 0; j + 2 < numPoints; j++)
2414  {
2415  dx = when[j+2+numDeleted] - when[j+numDeleted];
2416  dy = value[j+2+numDeleted] - value[j+numDeleted];
2417  dx1 = when[j+numDeleted+1] - when[j+numDeleted];
2418  dy1 = dy * dx1 / dx;
2419  err = fabs(value[j+numDeleted+1] - (value[j+numDeleted] + dy1));
2420  if( err < deltadB )
2421  { // within < deltadB dB?
2422  mLogEnvelope->Delete(j+1);
2423  numPoints--;
2424  numDeleted++;
2425  flag = true;
2426  }
2427  }
2428  }
2429 
2430  if(mLin) // do not use IsLinear() here
2431  {
2432  EnvLogToLin();
2433  mEnvelope = mLinEnvelope.get();
2434  mFreqRuler->ruler.SetLog(false);
2436  }
2437 
2438  szrV->Show(szrG,false);
2439  szrH->Show(szrI,false);
2440  szrH->Show(szrL,true);
2441 
2442  mUIParent->Layout();
2443  wxGetTopLevelParent(mUIParent)->Layout();
2444  ForceRecalc(); // it may have changed slightly due to the deletion of points
2445 }
2446 
2448 {
2449  double loLog = log10(mLoFreq);
2450  double hiLog = log10(mHiFreq);
2451  double denom = hiLog - loLog;
2452 
2453  if(mLin) //going from lin to log freq scale - do not use IsLinear() here
2454  { // add some extra points to the linear envelope for the graphic to follow
2455  double step = pow(2., 1./12.); // twelve steps per octave
2456  double when,value;
2457  for(double freq=10.; freq<mHiFreq; freq*=step)
2458  {
2459  when = freq/mHiFreq;
2460  value = mLinEnvelope->GetValue(when);
2461  mLinEnvelope->Insert(when, value);
2462  }
2463 
2464  EnvLinToLog();
2465  mEnvelope = mLogEnvelope.get();
2466  mFreqRuler->ruler.SetLog(true);
2468  }
2469 
2470  for (size_t i = 0; i < mBandsInUse; i++)
2471  {
2472  if( kThirdOct[i] == mLoFreq )
2473  mWhenSliders[i] = 0.;
2474  else
2475  mWhenSliders[i] = (log10(kThirdOct[i])-loLog)/denom;
2476  mEQVals[i] = mLogEnvelope->GetValue(mWhenSliders[i]); //set initial values of sliders
2477  if( mEQVals[i] > 20.)
2478  mEQVals[i] = 20.;
2479  if( mEQVals[i] < -20.)
2480  mEQVals[i] = -20.;
2481  }
2482  ErrMin(); //move sliders to minimise error
2483  for (size_t i = 0; i < mBandsInUse; i++)
2484  {
2485  mSliders[i]->SetValue(lrint(mEQVals[i])); //actually set slider positions
2486  mSlidersOld[i] = mSliders[i]->GetValue();
2487  wxString tip;
2488  if( kThirdOct[i] < 1000.)
2489  tip.Printf( wxT("%dHz\n%.1fdB"), (int)kThirdOct[i], mEQVals[i] );
2490  else
2491  tip.Printf( wxT("%gkHz\n%.1fdB"), kThirdOct[i]/1000., mEQVals[i] );
2492  mSliders[i]->SetToolTip(tip);
2493  }
2494 
2495  szrV->Show(szrG,true); // eq sliders
2496  szrH->Show(szrI,mOptions == kEqLegacy ); // interpolation choice
2497  szrH->Show(szrL,false); // linear freq checkbox
2498 
2499  mUIParent->Layout();
2500  wxGetTopLevelParent(mUIParent)->Layout();
2501  mUIParent->Layout();
2502  wxGetTopLevelParent(mUIParent)->Layout();
2503 
2504  GraphicEQ(mLogEnvelope.get());
2505  mDrawMode = false;
2506 }
2507 
2509 {
2510  size_t numPoints = mLogEnvelope->GetNumberOfPoints();
2511  if( numPoints == 0 )
2512  {
2513  return;
2514  }
2515 
2516  Doubles when{ numPoints };
2517  Doubles value{ numPoints };
2518 
2519  mLinEnvelope->Flatten(0.);
2520  mLinEnvelope->SetTrackLen(1.0);
2521  mLogEnvelope->GetPoints( when.get(), value.get(), numPoints );
2522  mLinEnvelope->Reassign(0., value[0]);
2523  double loLog = log10(20.);
2524  double hiLog = log10(mHiFreq);
2525  double denom = hiLog - loLog;
2526 
2527  for (size_t i = 0; i < numPoints; i++)
2528  mLinEnvelope->Insert(pow( 10., ((when[i] * denom) + loLog))/mHiFreq , value[i]);
2529  mLinEnvelope->Reassign(1., value[numPoints-1]);
2530 }
2531 
2533 {
2534  size_t numPoints = mLinEnvelope->GetNumberOfPoints();
2535  if( numPoints == 0 )
2536  {
2537  return;
2538  }
2539 
2540  Doubles when{ numPoints };
2541  Doubles value{ numPoints };
2542 
2543  mLogEnvelope->Flatten(0.);
2544  mLogEnvelope->SetTrackLen(1.0);
2545  mLinEnvelope->GetPoints( when.get(), value.get(), numPoints );
2546  mLogEnvelope->Reassign(0., value[0]);
2547  double loLog = log10(20.);
2548  double hiLog = log10(mHiFreq);
2549  double denom = hiLog - loLog;
2550  bool changed = false;
2551 
2552  for (size_t i = 0; i < numPoints; i++)
2553  {
2554  if( when[i]*mHiFreq >= 20 )
2555  {
2556  // Caution: on Linux, when when == 20, the log calculation rounds
2557  // to just under zero, which causes an assert error.
2558  double flog = (log10(when[i]*mHiFreq)-loLog)/denom;
2559  mLogEnvelope->Insert(std::max(0.0, flog) , value[i]);
2560  }
2561  else
2562  { //get the first point as close as we can to the last point requested
2563  changed = true;
2564  double v = value[i];
2565  mLogEnvelope->Insert(0., v);
2566  }
2567  }
2568  mLogEnvelope->Reassign(1., value[numPoints - 1]);
2569 
2570  if(changed)
2571  EnvelopeUpdated(mLogEnvelope.get(), false);
2572 }
2573 
2575 {
2576  double vals[NUM_PTS];
2577  double error = 0.0;
2578  double oldError = 0.0;
2579  double mEQValsOld = 0.0;
2580  double correction = 1.6;
2581  bool flag;
2582  size_t j=0;
2583  Envelope testEnvelope{ *mLogEnvelope };
2584 
2585  for(size_t i = 0; i < NUM_PTS; i++)
2586  vals[i] = testEnvelope.GetValue(mWhens[i]);
2587 
2588  // Do error minimisation
2589  error = 0.;
2590  GraphicEQ(&testEnvelope);
2591  for(size_t i = 0; i < NUM_PTS; i++) //calc initial error
2592  {
2593  double err = vals[i] - testEnvelope.GetValue(mWhens[i]);
2594  error += err*err;
2595  }
2596  oldError = error;
2597  while( j < mBandsInUse*12 ) //loop over the sliders a number of times
2598  {
2599  auto i = j % mBandsInUse; //use this slider
2600  if( (j > 0) & (i == 0) ) // if we've come back to the first slider again...
2601  {
2602  if( correction > 0 )
2603  correction = -correction; //go down
2604  else
2605  correction = -correction/2.; //go up half as much
2606  }
2607  flag = true; // check if we've hit the slider limit
2608  do
2609  {
2610  oldError = error;
2611  mEQValsOld = mEQVals[i];
2612  mEQVals[i] += correction; //move fader value
2613  if( mEQVals[i] > 20. )
2614  {
2615  mEQVals[i] = 20.;
2616  flag = false;
2617  }
2618  if( mEQVals[i] < -20. )
2619  {
2620  mEQVals[i] = -20.;
2621  flag = false;
2622  }
2623  GraphicEQ(&testEnvelope); //calculate envelope
2624  error = 0.;
2625  for(size_t k = 0; k < NUM_PTS; k++) //calculate error
2626  {
2627  double err = vals[k] - testEnvelope.GetValue(mWhens[k]);
2628  error += err*err;
2629  }
2630  }
2631  while( (error < oldError) && flag );
2632  if( error > oldError )
2633  {
2634  mEQVals[i] = mEQValsOld; //last one didn't work
2635  error = oldError;
2636  }
2637  else
2638  oldError = error;
2639  if( error < .0025 * mBandsInUse)
2640  break; // close enuff
2641  j++; //try next slider
2642  }
2643  if( error > .0025 * mBandsInUse ) // not within 0.05dB on each slider, on average
2644  {
2645  Select( (int) mCurves.size() - 1 );
2646  EnvelopeUpdated(&testEnvelope, false);
2647  }
2648 }
2649 
2651 {
2652  // JKC: 'value' is for height of curve.
2653  // The 0.0 initial value would only get used if NUM_PTS were 0.
2654  double value = 0.0;
2655  double dist, span, s;
2656 
2657  env->Flatten(0.);
2658  env->SetTrackLen(1.0);
2659 
2660  switch( mInterp )
2661  {
2662  case kBspline: // B-spline
2663  {
2664  int minF = 0;
2665  for(size_t i = 0; i < NUM_PTS; i++)
2666  {
2667  while( (mWhenSliders[minF] <= mWhens[i]) & (minF < (int)mBandsInUse) )
2668  minF++;
2669  minF--;
2670  if( minF < 0 ) //before first slider
2671  {
2672  dist = mWhens[i] - mWhenSliders[0];
2673  span = mWhenSliders[1] - mWhenSliders[0];
2674  s = dist/span;
2675  if( s < -1.5 )
2676  value = 0.;
2677  else if( s < -.5 )
2678  value = mEQVals[0]*(s + 1.5)*(s + 1.5)/2.;
2679  else
2680  value = mEQVals[0]*(.75 - s*s) + mEQVals[1]*(s + .5)*(s + .5)/2.;
2681  }
2682  else
2683  {
2684  if( mWhens[i] > mWhenSliders[mBandsInUse-1] ) //after last fader
2685  {
2686  dist = mWhens[i] - mWhenSliders[mBandsInUse-1];
2688  s = dist/span;
2689  if( s > 1.5 )
2690  value = 0.;
2691  else if( s > .5 )
2692  value = mEQVals[mBandsInUse-1]*(s - 1.5)*(s - 1.5)/2.;
2693  else
2694  value = mEQVals[mBandsInUse-1]*(.75 - s*s) +
2695  mEQVals[mBandsInUse-2]*(s - .5)*(s - .5)/2.;
2696  }
2697  else //normal case
2698  {
2699  dist = mWhens[i] - mWhenSliders[minF];
2700  span = mWhenSliders[minF+1] - mWhenSliders[minF];
2701  s = dist/span;
2702  if(s < .5 )
2703  {
2704  value = mEQVals[minF]*(0.75 - s*s);
2705  if( minF+1 < (int)mBandsInUse )
2706  value += mEQVals[minF+1]*(s+.5)*(s+.5)/2.;
2707  if( minF-1 >= 0 )
2708  value += mEQVals[minF-1]*(s-.5)*(s-.5)/2.;
2709  }
2710  else
2711  {
2712  value = mEQVals[minF]*(s-1.5)*(s-1.5)/2.;
2713  if( minF+1 < (int)mBandsInUse )
2714  value += mEQVals[minF+1]*(.75-(1.-s)*(1.-s));
2715  if( minF+2 < (int)mBandsInUse )
2716  value += mEQVals[minF+2]*(s-.5)*(s-.5)/2.;
2717  }
2718  }
2719  }
2720  if(mWhens[i]<=0.)
2721  env->Reassign(0., value);
2722  env->Insert( mWhens[i], value );
2723  }
2724  env->Reassign( 1., value );
2725  break;
2726  }
2727 
2728  case kCosine: // Cosine squared
2729  {
2730  int minF = 0;
2731  for(size_t i = 0; i < NUM_PTS; i++)
2732  {
2733  while( (mWhenSliders[minF] <= mWhens[i]) & (minF < (int)mBandsInUse) )
2734  minF++;
2735  minF--;
2736  if( minF < 0 ) //before first slider
2737  {
2738  dist = mWhenSliders[0] - mWhens[i];
2739  span = mWhenSliders[1] - mWhenSliders[0];
2740  if( dist < span )
2741  value = mEQVals[0]*(1. + cos(M_PI*dist/span))/2.;
2742  else
2743  value = 0.;
2744  }
2745  else
2746  {
2747  if( mWhens[i] > mWhenSliders[mBandsInUse-1] ) //after last fader
2748  {
2750  dist = mWhens[i] - mWhenSliders[mBandsInUse-1];
2751  if( dist < span )
2752  value = mEQVals[mBandsInUse-1]*(1. + cos(M_PI*dist/span))/2.;
2753  else
2754  value = 0.;
2755  }
2756  else //normal case
2757  {
2758  span = mWhenSliders[minF+1] - mWhenSliders[minF];
2759  dist = mWhenSliders[minF+1] - mWhens[i];
2760  value = mEQVals[minF]*(1. + cos(M_PI*(span-dist)/span))/2. +
2761  mEQVals[minF+1]*(1. + cos(M_PI*dist/span))/2.;
2762  }
2763  }
2764  if(mWhens[i]<=0.)
2765  env->Reassign(0., value);
2766  env->Insert( mWhens[i], value );
2767  }
2768  env->Reassign( 1., value );
2769  break;
2770  }
2771 
2772  case kCubic: // Cubic Spline
2773  {
2774  double y2[NUMBER_OF_BANDS+1];
2777  for(double xf=0; xf<1.; xf+=1./NUM_PTS)
2778  {
2779  env->Insert(xf, splint(mWhenSliders, mEQVals, mBandsInUse+1, y2, xf));
2780  }
2781  break;
2782  }
2783  }
2784 
2785  ForceRecalc();
2786 }
2787 
2788 void EffectEqualization::spline(double x[], double y[], size_t n, double y2[])
2789 {
2790  wxASSERT( n > 0 );
2791 
2792  double p, sig;
2793  Doubles u{ n };
2794 
2795  y2[0] = 0.; //
2796  u[0] = 0.; //'natural' boundary conditions
2797  for (size_t i = 1; i + 1 < n; i++)
2798  {
2799  sig = ( x[i] - x[i-1] ) / ( x[i+1] - x[i-1] );
2800  p = sig * y2[i-1] + 2.;
2801  y2[i] = (sig - 1.)/p;
2802  u[i] = ( y[i+1] - y[i] ) / ( x[i+1] - x[i] ) - ( y[i] - y[i-1] ) / ( x[i] - x[i-1] );
2803  u[i] = (6.*u[i]/( x[i+1] - x[i-1] ) - sig * u[i-1]) / p;
2804  }
2805  y2[n - 1] = 0.;
2806  for (size_t i = n - 1; i--;)
2807  y2[i] = y2[i]*y2[i+1] + u[i];
2808 }
2809 
2810 double EffectEqualization::splint(double x[], double y[], size_t n, double y2[], double xr)
2811 {
2812  wxASSERT( n > 1 );
2813 
2814  double a, b, h;
2815  static double xlast = 0.; // remember last x value requested
2816  static size_t k = 0; // and which interval we were in
2817 
2818  if( xr < xlast )
2819  k = 0; // gone back to start, (or somewhere to the left)
2820  xlast = xr;
2821  while( (x[k] <= xr) && (k + 1 < n) )
2822  k++;
2823  wxASSERT( k > 0 );
2824  k--;
2825  h = x[k+1] - x[k];
2826  a = ( x[k+1] - xr )/h;
2827  b = (xr - x[k])/h;
2828  return( a*y[k]+b*y[k+1]+((a*a*a-a)*y2[k]+(b*b*b-b)*y2[k+1])*h*h/6.);
2829 }
2830 
2832 {
2833 }
2834 
2835 void EffectEqualization::OnSize(wxSizeEvent & event)
2836 {
2837  mUIParent->Layout();
2838  event.Skip();
2839 }
2840 
2841 void EffectEqualization::OnSlider(wxCommandEvent & event)
2842 {
2843  wxSlider *s = (wxSlider *)event.GetEventObject();
2844  for (size_t i = 0; i < mBandsInUse; i++)
2845  {
2846  if( s == mSliders[i])
2847  {
2848  int posn = mSliders[i]->GetValue();
2849  if( wxGetKeyState(WXK_SHIFT) )
2850  {
2851  if( posn > mSlidersOld[i] )
2852  mEQVals[i] += (float).1;
2853  else
2854  if( posn < mSlidersOld[i] )
2855  mEQVals[i] -= .1f;
2856  }
2857  else
2858  mEQVals[i] += (posn - mSlidersOld[i]);
2859  if( mEQVals[i] > 20. )
2860  mEQVals[i] = 20.;
2861  if( mEQVals[i] < -20. )
2862  mEQVals[i] = -20.;
2863  int newPosn = (int)mEQVals[i];
2864  mSliders[i]->SetValue( newPosn );
2865  mSlidersOld[i] = newPosn;
2866  wxString tip;
2867  if( kThirdOct[i] < 1000.)
2868  tip.Printf( wxT("%dHz\n%.1fdB"), (int)kThirdOct[i], mEQVals[i] );
2869  else
2870  tip.Printf( wxT("%gkHz\n%.1fdB"), kThirdOct[i]/1000., mEQVals[i] );
2871  s->SetToolTip(tip);
2872  break;
2873  }
2874  }
2875  GraphicEQ(mLogEnvelope.get());
2876  EnvelopeUpdated();
2877 }
2878 
2879 void EffectEqualization::OnInterp(wxCommandEvent & WXUNUSED(event))
2880 {
2881  bool bIsGraphic = !mDrawMode;
2882  if (bIsGraphic)
2883  {
2884  GraphicEQ(mLogEnvelope.get());
2885  EnvelopeUpdated();
2886  }
2887  mInterp = mInterpChoice->GetSelection();
2888 }
2889 
2890 void EffectEqualization::OnDrawMode(wxCommandEvent & WXUNUSED(event))
2891 {
2892  mDrawMode = true;
2893  UpdateDraw();
2894 }
2895 
2896 void EffectEqualization::OnGraphicMode(wxCommandEvent & WXUNUSED(event))
2897 {
2898  mDrawMode = false;
2899  UpdateGraphic();
2900 }
2901 
2902 void EffectEqualization::OnSliderM(wxCommandEvent & WXUNUSED(event))
2903 {
2905  ForceRecalc();
2906 }
2907 
2908 void EffectEqualization::OnSliderDBMIN(wxCommandEvent & WXUNUSED(event))
2909 {
2911 }
2912 
2913 void EffectEqualization::OnSliderDBMAX(wxCommandEvent & WXUNUSED(event))
2914 {
2916 }
2917 
2918 //
2919 // New curve was selected
2920 //
2921 void EffectEqualization::OnCurve(wxCommandEvent & WXUNUSED(event))
2922 {
2923  // Select NEW curve
2924  wxASSERT( mCurve != NULL );
2925  setCurve( mCurve->GetCurrentSelection() );
2926  if( !mDrawMode )
2927  UpdateGraphic();
2928 }
2929 
2930 //
2931 // User wants to modify the list in some way
2932 //
2933 void EffectEqualization::OnManage(wxCommandEvent & WXUNUSED(event))
2934 {
2935  EditCurvesDialog d(mUIParent, this, mCurve->GetSelection());
2936  d.ShowModal();
2937 
2938  // Reload the curve names
2939  UpdateCurves();
2940 
2941  // Allow control to resize
2942  mUIParent->Layout();
2943 }
2944 
2945 void EffectEqualization::OnClear(wxCommandEvent & WXUNUSED(event))
2946 {
2947  Flatten();
2948 }
2949 
2950 void EffectEqualization::OnInvert(wxCommandEvent & WXUNUSED(event)) // Inverts any curve
2951 {
2952  if(!mDrawMode) // Graphic (Slider) mode. Invert the sliders.
2953  {
2954  for (size_t i = 0; i < mBandsInUse; i++)
2955  {
2956  mEQVals[i] = -mEQVals[i];
2957  int newPosn = (int)mEQVals[i];
2958  mSliders[i]->SetValue( newPosn );
2959  mSlidersOld[i] = newPosn;
2960 
2961  wxString tip;
2962  if( kThirdOct[i] < 1000.)
2963  tip.Printf( wxT("%dHz\n%.1fdB"), (int)kThirdOct[i], mEQVals[i] );
2964  else
2965  tip.Printf( wxT("%gkHz\n%.1fdB"), kThirdOct[i]/1000., mEQVals[i] );
2966  mSliders[i]->SetToolTip(tip);
2967  }
2968  GraphicEQ(mLogEnvelope.get());
2969  }
2970  else // Draw mode. Invert the points.
2971  {
2972  bool lin = IsLinear(); // refers to the 'log' or 'lin' of the frequency scale, not the amplitude
2973  size_t numPoints; // number of points in the curve/envelope
2974 
2975  // determine if log or lin curve is the current one
2976  // and find out how many points are in the curve
2977  if(lin) // lin freq scale and so envelope
2978  {
2979  numPoints = mLinEnvelope->GetNumberOfPoints();
2980  }
2981  else
2982  {
2983  numPoints = mLogEnvelope->GetNumberOfPoints();
2984  }
2985 
2986  if( numPoints == 0 )
2987  return;
2988 
2989  Doubles when{ numPoints };
2990  Doubles value{ numPoints };
2991 
2992  if(lin)
2993  mLinEnvelope->GetPoints( when.get(), value.get(), numPoints );
2994  else
2995  mLogEnvelope->GetPoints( when.get(), value.get(), numPoints );
2996 
2997  // invert the curve
2998  for (size_t i = 0; i < numPoints; i++)
2999  {
3000  if(lin)
3001  mLinEnvelope->Reassign(when[i] , -value[i]);
3002  else
3003  mLogEnvelope->Reassign(when[i] , -value[i]);
3004  }
3005 
3006  // copy it back to the other one (just in case)
3007  if(lin)
3008  EnvLinToLog();
3009  else
3010  EnvLogToLin();
3011  }
3012 
3013  // and update the display etc
3014  ForceRecalc();
3015  EnvelopeUpdated();
3016 }
3017 
3018 void EffectEqualization::OnGridOnOff(wxCommandEvent & WXUNUSED(event))
3019 {
3020  mDrawGrid = mGridOnOff->IsChecked();
3021  mPanel->Refresh(false);
3022 }
3023 
3024 void EffectEqualization::OnLinFreq(wxCommandEvent & WXUNUSED(event))
3025 {
3026  mLin = mLinFreq->IsChecked();
3027  if(IsLinear()) //going from log to lin freq scale
3028  {
3029  mFreqRuler->ruler.SetLog(false);
3031  EnvLogToLin();
3032  mEnvelope = mLinEnvelope.get();
3033  mLin = true;
3034  }
3035  else //going from lin to log freq scale
3036  {
3037  mFreqRuler->ruler.SetLog(true);
3039  EnvLinToLog();
3040  mEnvelope = mLogEnvelope.get();
3041  mLin = false;
3042  }
3043  mFreqRuler->Refresh(false);
3044  ForceRecalc();
3045 }
3046 
3047 #ifdef EXPERIMENTAL_EQ_SSE_THREADED
3048 
3049 void EffectEqualization::OnProcessingRadio(wxCommandEvent & event)
3050 {
3051  int testEvent=event.GetId();
3052  switch(testEvent)
3053  {
3054  case ID_DefaultMath: EffectEqualization48x::SetMathPath(MATH_FUNCTION_ORIGINAL);
3055  break;
3056  case ID_SSE: EffectEqualization48x::SetMathPath(MATH_FUNCTION_SSE);
3057  break;
3058  case ID_SSEThreaded: EffectEqualization48x::SetMathPath(MATH_FUNCTION_THREADED | MATH_FUNCTION_SSE);
3059  break;
3060  case ID_AVX: testEvent = 2;
3061  break;
3062  case ID_AVXThreaded: testEvent = 2;
3063  break;
3064  }
3065 
3066 };
3067 
3068 void EffectEqualization::OnBench( wxCommandEvent & event)
3069 {
3070  mBench=true;
3071  // OnOk(event);
3072 }
3073 
3074 #endif
3075 
3076 //----------------------------------------------------------------------------
3077 // EqualizationPanel
3078 //----------------------------------------------------------------------------
3079 
3080 BEGIN_EVENT_TABLE(EqualizationPanel, wxPanelWrapper)
3081  EVT_PAINT(EqualizationPanel::OnPaint)
3082  EVT_MOUSE_EVENTS(EqualizationPanel::OnMouseEvent)
3083  EVT_MOUSE_CAPTURE_LOST(EqualizationPanel::OnCaptureLost)
3084  EVT_SIZE(EqualizationPanel::OnSize)
3086 
3088  wxWindow *parent, wxWindowID winid, EffectEqualization *effect)
3089 : wxPanelWrapper(parent, winid)
3090 {
3091  mParent = parent;
3092  mEffect = effect;
3093 
3094  mBitmap = NULL;
3095  mWidth = 0;
3096  mHeight = 0;
3097 
3098  mLinEditor = std::make_unique<EnvelopeEditor>(*mEffect->mLinEnvelope, false);
3099  mLogEditor = std::make_unique<EnvelopeEditor>(*mEffect->mLogEnvelope, false);
3100  mEffect->mEnvelope->Flatten(0.);
3101  mEffect->mEnvelope->SetTrackLen(1.0);
3102 
3103  ForceRecalc();
3104 }
3105 
3107 {
3108  if(HasCapture())
3109  ReleaseMouse();
3110 }
3111 
3113 {
3114  mRecalcRequired = true;
3115  Refresh(false);
3116 }
3117 
3119 {
3122 
3123  mEffect->CalcFilter(); //to calculate the actual response
3125 }
3126 
3127 void EqualizationPanel::OnSize(wxSizeEvent & WXUNUSED(event))
3128 {
3129  Refresh( false );
3130 }
3131 
3132 #include "../TrackPanelDrawingContext.h"
3133 void EqualizationPanel::OnPaint(wxPaintEvent & WXUNUSED(event))
3134 {
3135  wxPaintDC dc(this);
3136  if(mRecalcRequired) {
3137  Recalc();
3138  mRecalcRequired = false;
3139  }
3140  int width, height;
3141  GetSize(&width, &height);
3142 
3143  if (!mBitmap || mWidth!=width || mHeight!=height)
3144  {
3145  mWidth = width;
3146  mHeight = height;
3147  mBitmap = std::make_unique<wxBitmap>(mWidth, mHeight,24);
3148  }
3149 
3150  wxBrush bkgndBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
3151 
3152  wxMemoryDC memDC;
3153  memDC.SelectObject(*mBitmap);
3154 
3155  wxRect bkgndRect;
3156  bkgndRect.x = 0;
3157  bkgndRect.y = 0;
3158  bkgndRect.width = mWidth;
3159  bkgndRect.height = mHeight;
3160  memDC.SetBrush(bkgndBrush);
3161  memDC.SetPen(*wxTRANSPARENT_PEN);
3162  memDC.DrawRectangle(bkgndRect);
3163 
3164  bkgndRect.y = mHeight;
3165  memDC.DrawRectangle(bkgndRect);
3166 
3167  wxRect border;
3168  border.x = 0;
3169  border.y = 0;
3170  border.width = mWidth;
3171  border.height = mHeight;
3172 
3173  memDC.SetBrush(*wxWHITE_BRUSH);
3174  memDC.SetPen(*wxBLACK_PEN);
3175  memDC.DrawRectangle(border);
3176 
3177  mEnvRect = border;
3178  mEnvRect.Deflate(PANELBORDER, PANELBORDER);
3179 
3180  // Pure blue x-axis line
3181  memDC.SetPen(wxPen(theTheme.Colour( clrGraphLines ), 1, wxPENSTYLE_SOLID));
3182  int center = (int) (mEnvRect.height * mEffect->mdBMax/(mEffect->mdBMax-mEffect->mdBMin) + .5);
3183  AColor::Line(memDC,
3184  mEnvRect.GetLeft(), mEnvRect.y + center,
3185  mEnvRect.GetRight(), mEnvRect.y + center);
3186 
3187  // Draw the grid, if asked for. Do it now so it's underneath the main plots.
3188  if( mEffect->mDrawGrid )
3189  {
3190  mEffect->mFreqRuler->ruler.DrawGrid(memDC, mEnvRect.height, true, true, PANELBORDER, PANELBORDER);
3191  mEffect->mdBRuler->ruler.DrawGrid(memDC, mEnvRect.width, true, true, PANELBORDER, PANELBORDER);
3192  }
3193 
3194  // Med-blue envelope line
3195  memDC.SetPen(wxPen(theTheme.Colour(clrGraphLines), 3, wxPENSTYLE_SOLID));
3196 
3197  // Draw envelope
3198  int x, y, xlast = 0, ylast = 0;
3199  {
3200  Doubles values{ size_t(mEnvRect.width) };
3201  mEffect->mEnvelope->GetValues(values.get(), mEnvRect.width, 0.0, 1.0 / mEnvRect.width);
3202  bool off = false, off1 = false;
3203  for (int i = 0; i < mEnvRect.width; i++)
3204  {
3205  x = mEnvRect.x + i;
3206  y = lrint(mEnvRect.height*((mEffect->mdBMax - values[i]) / (mEffect->mdBMax - mEffect->mdBMin)) + .25); //needs more optimising, along with'what you get'?
3207  if (y >= mEnvRect.height)
3208  {
3209  y = mEnvRect.height - 1;
3210  off = true;
3211  }
3212  else
3213  {
3214  off = false;
3215  off1 = false;
3216  }
3217  if ((i != 0) & (!off1))
3218  {
3219  AColor::Line(memDC, xlast, ylast,
3220  x, mEnvRect.y + y);
3221  }
3222  off1 = off;
3223  xlast = x;
3224  ylast = mEnvRect.y + y;
3225  }
3226  }
3227 
3228  //Now draw the actual response that you will get.
3229  //mFilterFunc has a linear scale, window has a log one so we have to fiddle about
3230  memDC.SetPen(wxPen(theTheme.Colour( clrResponseLines ), 1, wxPENSTYLE_SOLID));
3231  double scale = (double)mEnvRect.height/(mEffect->mdBMax-mEffect->mdBMin); //pixels per dB
3232  double yF; //gain at this freq
3233  double delta = mEffect->mHiFreq / (((double)mEffect->mWindowSize / 2.)); //size of each freq bin
3234 
3235  bool lin = mEffect->IsLinear(); // log or lin scale?
3236 
3237  double loLog = log10(mEffect->mLoFreq);
3238  double step = lin ? mEffect->mHiFreq : (log10(mEffect->mHiFreq) - loLog);
3239  step /= ((double)mEnvRect.width-1.);
3240  double freq; //actual freq corresponding to x position
3241  int halfM = (mEffect->mM - 1) / 2;
3242  int n; //index to mFreqFunc
3243  for(int i=0; i<mEnvRect.width; i++)
3244  {
3245  x = mEnvRect.x + i;
3246  freq = lin ? step*i : pow(10., loLog + i*step); //Hz
3247  if( ( lin ? step : (pow(10., loLog + (i+1)*step)-freq) ) < delta)
3248  { //not enough resolution in FFT
3249  // set up for calculating cos using recurrence - faster than calculating it directly each time
3250  double theta = M_PI*freq/mEffect->mHiFreq; //radians, normalized
3251  double wtemp = sin(0.5 * theta);
3252  double wpr = -2.0 * wtemp * wtemp;
3253  double wpi = -1.0 * sin(theta);
3254  double wr = cos(theta*halfM);
3255  double wi = sin(theta*halfM);
3256 
3257  yF = 0.;
3258  for(int j=0;j<halfM;j++)
3259  {
3260  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.
3261  // do recurrence
3262  wr = (wtemp = wr) * wpr - wi * wpi + wr;
3263  wi = wi * wpr + wtemp * wpi + wi;
3264  }
3265  yF += mOutr[halfM];
3266  yF = fabs(yF);
3267  if(yF!=0.)
3268  yF = LINEAR_TO_DB(yF);
3269  else
3270  yF = mEffect->mdBMin;
3271  }
3272  else
3273  { //use FFT, it has enough resolution
3274  n = (int)(freq/delta + .5);
3275  if(pow(mEffect->mFilterFuncR[n],2)+pow(mEffect->mFilterFuncI[n],2)!=0.)
3276  yF = 10.0*log10(pow(mEffect->mFilterFuncR[n],2)+pow(mEffect->mFilterFuncI[n],2)); //10 here, a power
3277  else
3278  yF = mEffect->mdBMin;
3279  }
3280  if(yF < mEffect->mdBMin)
3281  yF = mEffect->mdBMin;
3282  yF = center-scale*yF;
3283  if(yF>mEnvRect.height)
3284  yF = mEnvRect.height - 1;
3285  if(yF<0.)
3286  yF=0.;
3287  y = (int)(yF+.5);
3288 
3289  if (i != 0)
3290  {
3291  AColor::Line(memDC, xlast, ylast, x, mEnvRect.y + y);
3292  }
3293  xlast = x;
3294  ylast = mEnvRect.y + y;
3295  }
3296 
3297  memDC.SetPen(*wxBLACK_PEN);
3298  if( mEffect->mDrawMode )
3299  {
3300  ZoomInfo zoomInfo( 0.0, mEnvRect.width-1 );
3301 
3302  // Back pointer to TrackPanel won't be needed in the one drawing
3303  // function we use here
3304  TrackArtist artist( nullptr );
3305 
3306  artist.pZoomInfo = &zoomInfo;
3307  TrackPanelDrawingContext context{ memDC, {}, {}, &artist };
3309  context, mEnvRect, false, 0.0,
3310  mEffect->mdBMin, mEffect->mdBMax, false);
3311  }
3312 
3313  dc.Blit(0, 0, mWidth, mHeight, &memDC, 0, 0, wxCOPY, FALSE);
3314 }
3315 
3316 void EqualizationPanel::OnMouseEvent(wxMouseEvent & event)
3317 {
3318  if (!mEffect->mDrawMode)
3319  {
3320  return;
3321  }
3322 
3323  if (event.ButtonDown() && !HasCapture())
3324  {
3325  CaptureMouse();
3326  }
3327 
3328  auto &pEditor = (mEffect->mLin ? mLinEditor : mLogEditor);
3329  if (pEditor->MouseEvent(event, mEnvRect, ZoomInfo(0.0, mEnvRect.width),
3330  false, 0.0,
3332  {
3334  ForceRecalc();
3335  }
3336 
3337  if (event.ButtonUp() && HasCapture())
3338  {
3339  ReleaseMouse();
3340  }
3341 }
3342 
3343 void EqualizationPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(event))
3344 {
3345  if (HasCapture())
3346  {
3347  ReleaseMouse();
3348  }
3349 }
3350 
3351 //----------------------------------------------------------------------------
3352 // EditCurvesDialog
3353 //----------------------------------------------------------------------------
3354 // Note that the 'modified' curve used to be called 'custom' but is now called 'unnamed'
3355 // Some things that deal with 'unnamed' curves still use, for example, 'mCustomBackup' as variable names.
3357 
3358 BEGIN_EVENT_TABLE(EditCurvesDialog, wxDialogWrapper)
3365  EVT_BUTTON(LibraryButtonID, EditCurvesDialog::OnLibrary)
3369  EditCurvesDialog::OnListSelectionChange)
3370  EVT_LIST_ITEM_DESELECTED(CurvesListID,
3371  EditCurvesDialog::OnListSelectionChange)
3373 
3374 EditCurvesDialog::EditCurvesDialog(wxWindow * parent, EffectEqualization * effect, int position):
3375 wxDialogWrapper(parent, wxID_ANY, XO("Manage Curves List"),
3376  wxDefaultPosition, wxDefaultSize,
3377  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
3378 {
3379  SetLabel(XO("Manage Curves")); // Provide visual label
3380  SetName(XO("Manage Curves List")); // Provide audible label
3381  mParent = parent;
3382  mEffect = effect;
3383  mPosition = position;
3384  // make a copy of mEffect->mCurves here to muck about with.
3385  mEditCurves.clear();
3386  for (unsigned int i = 0; i < mEffect->mCurves.size(); i++)
3387  {
3388  mEditCurves.push_back(mEffect->mCurves[i].Name);
3389  mEditCurves[i].points = mEffect->mCurves[i].points;
3390  }
3391 
3392  Populate();
3393  SetMinSize(GetSize());
3394 }
3395 
3397 {
3398 }
3399 
3402 {
3403  //------------------------- Main section --------------------
3404  ShuttleGui S(this, eIsCreating);
3405  PopulateOrExchange(S);
3406  // ----------------------- End of main section --------------
3407 }
3408 
3411 {
3412  S.StartHorizontalLay(wxEXPAND);
3413  {
3414  S.StartStatic(XO("&Curves"), 1);
3415  {
3416  mList = S.Id(CurvesListID)
3417  .Style(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES )
3419  { XO("Curve Name"), wxLIST_FORMAT_RIGHT }
3420  });
3421  }
3422  S.EndStatic();
3423  S.StartVerticalLay(0);
3424  {
3425  S.Id(UpButtonID).AddButton(XXO("Move &Up"), wxALIGN_LEFT);
3426  S.Id(DownButtonID).AddButton(XXO("Move &Down"), wxALIGN_LEFT);
3427  S.Id(RenameButtonID).AddButton(XXO("&Rename..."), wxALIGN_LEFT);
3428  S.Id(DeleteButtonID).AddButton(XXO("D&elete..."), wxALIGN_LEFT);
3429  S.Id(ImportButtonID).AddButton(XXO("I&mport..."), wxALIGN_LEFT);
3430  S.Id(ExportButtonID).AddButton(XXO("E&xport..."), wxALIGN_LEFT);
3431  S.Id(LibraryButtonID).AddButton(XXO("&Get More..."), wxALIGN_LEFT);
3432  S.Id(DefaultsButtonID).AddButton(XXO("De&faults"), wxALIGN_LEFT);
3433  }
3434  S.EndVerticalLay();
3435  }
3436  S.EndHorizontalLay();
3437  S.AddStandardButtons();
3438  S.StartStatic(XO("Help"));
3439  S.AddConstTextBox( {}, XO("Rename 'unnamed' to save a new entry.\n'OK' saves all changes, 'Cancel' doesn't."));
3440  S.EndStatic();
3442  Fit();
3443 
3444  return;
3445 }
3446 
3448 {
3449  mList->DeleteAllItems();
3450  for (unsigned int i = 0; i < mEditCurves.size(); i++)
3451  mList->InsertItem(i, mEditCurves[i].Name);
3452  mList->SetColumnWidth(0, wxLIST_AUTOSIZE);
3453  int curvesWidth = mList->GetColumnWidth(0);
3454  mList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER);
3455  int headerWidth = mList->GetColumnWidth(0);
3456  mList->SetColumnWidth(0, wxMax(headerWidth, curvesWidth));
3457  // use 'position' to set focus
3458  mList->EnsureVisible(position);
3459  mList->SetItemState(position, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
3460 }
3461 
3462 void EditCurvesDialog::OnUp(wxCommandEvent & WXUNUSED(event))
3463 {
3464  long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3465  if ( item == -1 )
3466  return; // no items selected
3467  if( item == 0 )
3468  item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); // top item selected, can't move up
3469  int state;
3470  while( item != -1 )
3471  {
3472  if ( item == mList->GetItemCount()-1)
3473  { // 'unnamed' always stays at the bottom
3474  mEffect->Effect::MessageBox(
3475  XO("'unnamed' always stays at the bottom of the list"),
3477  XO("'unnamed' is special") ); // these could get tedious!
3478  return;
3479  }
3480  state = mList->GetItemState(item-1, wxLIST_STATE_SELECTED);
3481  if ( state != wxLIST_STATE_SELECTED )
3482  { // swap this with one above but only if it isn't selected
3483  EQCurve temp(wxT("temp"));
3484  temp.Name = mEditCurves[item].Name;
3485  temp.points = mEditCurves[item].points;
3486  mEditCurves[item].Name = mEditCurves[item-1].Name;
3487  mEditCurves[item].points = mEditCurves[item-1].points;
3488  mEditCurves[item-1].Name = temp.Name;
3489  mEditCurves[item-1].points = temp.points;
3490  wxString sTemp = mList->GetItemText(item);
3491  mList->SetItem(item, 0, mList->GetItemText(item-1));
3492  mList->SetItem(item-1, 0, sTemp);
3493  mList->SetItemState(item, 0, wxLIST_STATE_SELECTED);
3494  mList->SetItemState(item-1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
3495  }
3496  item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3497  }
3498 }
3499 
3500 void EditCurvesDialog::OnDown(wxCommandEvent & WXUNUSED(event))
3501 { // looks harder than OnUp as we need to seek backwards up the list, hence GetPreviousItem
3502  long item = GetPreviousItem(mList->GetItemCount());
3503  if( item == -1 )
3504  return; // nothing selected
3505  int state;
3506  while( item != -1 )
3507  {
3508  if( (item != mList->GetItemCount()-1) && (item != mList->GetItemCount()-2) )
3509  { // can't move 'unnamed' down, or the one above it
3510  state = mList->GetItemState(item+1, wxLIST_STATE_SELECTED);
3511  if ( state != wxLIST_STATE_SELECTED )
3512  { // swap this with one below but only if it isn't selected
3513  EQCurve temp(wxT("temp"));
3514  temp.Name = mEditCurves[item].Name;
3515  temp.points = mEditCurves[item].points;
3516  mEditCurves[item].Name = mEditCurves[item+1].Name;
3517  mEditCurves[item].points = mEditCurves[item+1].points;
3518  mEditCurves[item+1].Name = temp.Name;
3519  mEditCurves[item+1].points = temp.points;
3520  wxString sTemp = mList->GetItemText(item);
3521  mList->SetItem(item, 0, mList->GetItemText(item+1));
3522  mList->SetItem(item+1, 0, sTemp);
3523  mList->SetItemState(item, 0, wxLIST_STATE_SELECTED);
3524  mList->SetItemState(item+1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
3525  }
3526  }
3527  item = GetPreviousItem(item);
3528  }
3529 }
3530 
3531 long EditCurvesDialog::GetPreviousItem(long item) // wx doesn't have this
3532 {
3533  long lastItem = -1;
3534  long itemTemp = mList->GetNextItem(-1, wxLIST_NEXT_ALL,
3535  wxLIST_STATE_SELECTED);
3536  while( (itemTemp != -1) && (itemTemp < item) )
3537  {
3538  lastItem = itemTemp;
3539  itemTemp = mList->GetNextItem(itemTemp, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3540  }
3541  return lastItem;
3542 }
3543 
3544 // Rename curve/curves
3545 void EditCurvesDialog::OnRename(wxCommandEvent & WXUNUSED(event))
3546 {
3547  wxString name;
3548  int numCurves = mEditCurves.size();
3549  int curve = 0;
3550 
3551  // Setup list of characters that aren't allowed
3552  wxArrayStringEx exclude{
3553  wxT("<") ,
3554  wxT(">") ,
3555  wxT("'") ,
3556  wxT("\"") ,
3557  };
3558 
3559  // Get the first one to be renamed
3560  long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3561  long firstItem = item; // for reselection with PopulateList
3562  while(item >= 0)
3563  {
3564  // Prompt the user until a valid name is enter or cancelled
3565  bool overwrite = false;
3566  bool bad = true;
3567  while( bad ) // Check for an unacceptable duplicate
3568  { // Show the dialog and bail if the user cancels
3569  bad = false;
3570  // build the dialog
3571  AudacityTextEntryDialog dlg( this,
3572  XO("Rename '%s' to...").Format( mEditCurves[ item ].Name ),
3573  XO("Rename...") );
3574  dlg.SetTextValidator( wxFILTER_EXCLUDE_CHAR_LIST );
3575  dlg.SetName(
3576  wxString::Format( _("Rename '%s'"), mEditCurves[ item ].Name ) );
3577  wxTextValidator *tv = dlg.GetTextValidator();
3578  tv->SetExcludes( exclude ); // Tell the validator about excluded chars
3579  if( dlg.ShowModal() == wxID_CANCEL )
3580  {
3581  bad = true;
3582  break;
3583  }
3584 
3585  // Extract the name from the dialog
3586  name = dlg.GetValue();
3587 
3588  // Search list of curves for a duplicate name
3589  for( curve = 0; curve < numCurves; curve++ )
3590  {
3591  wxString temp = mEditCurves[ curve ].Name;
3592  if( name == mEditCurves[ curve ].Name ) // case sensitive
3593  {
3594  bad = true;
3595  if( curve == item ) // trying to rename a curve with the same name
3596  {
3597  mEffect->Effect::MessageBox(
3598  XO("Name is the same as the original one"),
3599  wxOK,
3600  XO("Same name") );
3601  break;
3602  }
3603  int answer = mEffect->Effect::MessageBox(
3604  XO("Overwrite existing curve '%s'?").Format( name ),
3605  wxYES_NO,
3606  XO("Curve exists") );
3607  if (answer == wxYES)
3608  {
3609  bad = false;
3610  overwrite = true; // we are going to overwrite the one with this name
3611  break;
3612  }
3613  }
3614  }
3615  if( name.empty() || name == wxT("unnamed") )
3616  bad = true;
3617  }
3618 
3619  // if bad, we cancelled the rename dialog, so nothing to do.
3620  if( bad == true )
3621  ;
3622  else if(overwrite){
3623  // Overwrite another curve.
3624  // JKC: because 'overwrite' is true, 'curve' is the number of the curve that
3625  // we are about to overwrite.
3626  mEditCurves[ curve ].Name = name;
3627  mEditCurves[ curve ].points = mEditCurves[ item ].points;
3628  // if renaming the unnamed item, then select it,
3629  // otherwise get rid of the item we've renamed.
3630  if( item == (numCurves-1) )
3631  mList->SetItem(curve, 0, name);
3632  else
3633  {
3634  mEditCurves.erase( mEditCurves.begin() + item );
3635  numCurves--;
3636  }
3637  }
3638  else if( item == (numCurves-1) ) // renaming 'unnamed'
3639  { // Create a NEW entry
3640  mEditCurves.push_back( EQCurve( wxT("unnamed") ) );
3641  // Copy over the points
3642  mEditCurves[ numCurves ].points = mEditCurves[ numCurves - 1 ].points;
3643  // Give the original unnamed entry the NEW name
3644  mEditCurves[ numCurves - 1 ].Name = name;
3645  numCurves++;
3646  }
3647  else // just rename (the 'normal' case)
3648  {
3649  mEditCurves[ item ].Name = name;
3650  mList->SetItem(item, 0, name);
3651  }
3652  // get next selected item
3653  item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3654  }
3655 
3656  PopulateList(firstItem); // Note: only saved to file when you OK out of the dialog
3657  return;
3658 }
3659 
3660 // Delete curve/curves
3661 void EditCurvesDialog::OnDelete(wxCommandEvent & WXUNUSED(event))
3662 {
3663  // We could count them here
3664  // And then put in a 'Delete N items?' prompt.
3665 
3666 #if 0 // 'one at a time' prompt code
3667  // Get the first one to be deleted
3668  long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3669  // Take care, mList and mEditCurves will get out of sync as curves are deleted
3670  int deleted = 0;
3671  long highlight = -1;
3672 
3673  while(item >= 0)
3674  {
3675  if(item == mList->GetItemCount()-1) //unnamed
3676  {
3677  mEffect->Effect::MessageBox(
3678  XO("You cannot delete the 'unnamed' curve."),
3679  wxOK | wxCENTRE,
3680  XO("Can't delete 'unnamed'") );
3681  }
3682  else
3683  {
3684  // Create the prompt
3685  auto quest = XO("Delete '%s'?")
3686  .Format(mEditCurves[ item-deleted ].Name));
3687 
3688  // Ask for confirmation before removal
3689  int ans = mEffect->Effect::MessageBox(
3690  quest,
3691  wxYES_NO | wxCENTRE,
3692  XO("Confirm Deletion") );
3693  if( ans == wxYES )
3694  { // Remove the curve from the array
3695  mEditCurves.RemoveAt( item-deleted );
3696  deleted++;
3697  }
3698  else
3699  highlight = item-deleted; // if user presses 'No', select that curve
3700  }
3701  // get next selected item
3702  item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3703  }
3704 
3705  if(highlight == -1)
3706  PopulateList(mEditCurves.size()-1); // set 'unnamed' as the selected curve
3707  else
3708  PopulateList(highlight); // user said 'No' to deletion
3709 #else // 'DELETE all N' code
3710  int count = mList->GetSelectedItemCount();
3711  long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3712  // Create the prompt
3713  TranslatableString quest;
3714  if( count > 1 )
3715  quest = XO("Delete %d items?").Format( count );
3716  else
3717  if( count == 1 )
3718  quest = XO("Delete '%s'?").Format( mEditCurves[ item ].Name );
3719  else
3720  return;
3721  // Ask for confirmation before removal
3722  int ans = mEffect->Effect::MessageBox(
3723  quest,
3724  wxYES_NO | wxCENTRE,
3725  XO("Confirm Deletion") );
3726  if( ans == wxYES )
3727  { // Remove the curve(s) from the array
3728  // Take care, mList and mEditCurves will get out of sync as curves are deleted
3729  int deleted = 0;
3730  while(item >= 0)
3731  {
3732  // TODO: Migrate to the standard "Manage" dialog.
3733  if(item == mList->GetItemCount()-1) //unnamed
3734  {
3735  mEffect->Effect::MessageBox(
3736  XO("You cannot delete the 'unnamed' curve, it is special."),
3738  XO("Can't delete 'unnamed'") );
3739  }
3740  else
3741  {
3742  mEditCurves.erase( mEditCurves.begin() + item - deleted );
3743  deleted++;
3744  }
3745  item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3746  }
3747  PopulateList(mEditCurves.size() - 1); // set 'unnamed' as the selected curve
3748  }
3749 #endif
3750 }
3751 
3753 {
3754  static const FileNames::FileTypes results{
3756  };
3757  return results;
3758 }
3759 
3760 void EditCurvesDialog::OnImport( wxCommandEvent & WXUNUSED(event))
3761 {
3762  FileDialogWrapper filePicker(
3763  this,
3764  XO("Choose an EQ curve file"), FileNames::DataDir(), wxT(""),
3765  XMLtypes() );
3766  wxString fileName;
3767  if( filePicker.ShowModal() == wxID_CANCEL)
3768  return;
3769  else
3770  fileName = filePicker.GetPath();
3771  // Use EqualizationDialog::LoadCurves to read into (temporary) mEditCurves
3772  // This may not be the best OOP way of doing it, but I don't know better (MJS)
3773  EQCurveArray temp;
3774  temp = mEffect->mCurves; // temp copy of the main dialog curves
3775  mEffect->mCurves = mEditCurves; // copy EditCurvesDialog to main interface
3776  mEffect->LoadCurves(fileName, true); // use main interface to load imported curves
3777  mEditCurves = mEffect->mCurves; // copy back to this interface
3778  mEffect->mCurves = temp; // and reset the main interface how it was
3779  PopulateList(0); // update the EditCurvesDialog dialog
3780  return;
3781 }
3782 
3783 void EditCurvesDialog::OnExport( wxCommandEvent & WXUNUSED(event))
3784 {
3785  FileDialogWrapper filePicker(this, XO("Export EQ curves as..."),
3786  FileNames::DataDir(), wxT(""),
3787  XMLtypes(),
3788  wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER); // wxFD_CHANGE_DIR?
3789  wxString fileName;
3790  if( filePicker.ShowModal() == wxID_CANCEL)
3791  return;
3792  else
3793  fileName = filePicker.GetPath();
3794 
3795  EQCurveArray temp;
3796  temp = mEffect->mCurves; // backup the parent's curves
3797  EQCurveArray exportCurves; // Copy selected curves to export
3798  exportCurves.clear();
3799  long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3800  int i=0;
3801  while(item >= 0)
3802  {
3803  if(item != mList->GetItemCount()-1) // not 'unnamed'
3804  {
3805  exportCurves.push_back(mEditCurves[item].Name);
3806  exportCurves[i].points = mEditCurves[item].points;
3807  i++;
3808  }
3809  else
3810  mEffect->Effect::MessageBox(
3811  XO("You cannot export 'unnamed' curve, it is special."),
3813  XO("Cannot Export 'unnamed'") );
3814  // get next selected item
3815  item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
3816  }
3817  if(i>0)
3818  {
3819  mEffect->mCurves = exportCurves;
3820  mEffect->SaveCurves(fileName);
3821  mEffect->mCurves = temp;
3822  auto message = XO("%d curves exported to %s").Format( i, fileName );
3823  mEffect->Effect::MessageBox(
3824  message,
3826  XO("Curves exported") );
3827  }
3828  else
3829  mEffect->Effect::MessageBox(
3830  XO("No curves exported"),
3832  XO("No curves exported") );
3833 }
3834 
3835 void EditCurvesDialog::OnLibrary( wxCommandEvent & WXUNUSED(event))
3836 {
3837  // full path to wiki.
3838  wxLaunchDefaultBrowser(wxT("https://wiki.audacityteam.org/wiki/EQCurvesDownload"));
3839 }
3840 
3841 void EditCurvesDialog::OnDefaults( wxCommandEvent & WXUNUSED(event))
3842 {
3843  EQCurveArray temp;
3844  temp = mEffect->mCurves;
3845  // we expect this to fail in LoadCurves (due to a lack of path) and handle that there
3846  mEffect->LoadCurves( wxT("EQDefaultCurves.xml") );
3848  mEffect->mCurves = temp;
3849  PopulateList(0); // update the EditCurvesDialog dialog
3850 }
3851 
3852 void EditCurvesDialog::OnOK(wxCommandEvent & WXUNUSED(event))
3853 {
3854  // Make a backup of the current curves
3855  wxString backupPlace = wxFileName( FileNames::DataDir(), wxT("EQBackup.xml") ).GetFullPath();
3856  mEffect->SaveCurves(backupPlace);
3857  // Load back into the main dialog
3858  mEffect->mCurves.clear();
3859  for (unsigned int i = 0; i < mEditCurves.size(); i++)
3860  {
3861  mEffect->mCurves.push_back(mEditCurves[i].Name);
3862  mEffect->mCurves[i].points = mEditCurves[i].points;
3863  }
3864  mEffect->SaveCurves();
3865  mEffect->LoadCurves();
3866 // mEffect->CreateChoice();
3867  wxGetTopLevelParent(mEffect->mUIParent)->Layout();
3868 // mEffect->mUIParent->Layout();
3869 
3870  // Select something sensible
3871  long item = mList->GetNextItem(-1,
3872  wxLIST_NEXT_ALL,
3873  wxLIST_STATE_SELECTED);
3874  if (item == -1)
3875  item = mList->GetItemCount()-1; // nothing selected, default to 'unnamed'
3876  mEffect->setCurve(item);
3877  EndModal(true);
3878 }
3879 
3881 {
3882  const bool enable = mList->GetSelectedItemCount() > 0;
3883  static const int ids[] = {
3884  UpButtonID,
3885  DownButtonID,
3888  };
3889  for (auto id : ids)
3890  FindWindowById(id, this)->Enable(enable);
3891 }
3892 
XMLWriter
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:23
EffectEqualization::HandleXMLChild
XMLTagHandler * HandleXMLChild(const wxChar *tag) override
Definition: Equalization.cpp:2301
EVT_BUTTON
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
EffectEqualization::OnGridOnOff
void OnGridOnOff(wxCommandEvent &event)
Definition: Equalization.cpp:3018
EqualizationPanel::mRecalcRequired
bool mRecalcRequired
Definition: Equalization.h:333
EqualizationPanel::ForceRecalc
void ForceRecalc()
Definition: Equalization.cpp:3112
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:2896
eIsCreating
@ eIsCreating
Definition: ShuttleGui.h:38
EditCurvesDialog::OnExport
void OnExport(wxCommandEvent &event)
Definition: Equalization.cpp:3783
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:2271
ShuttleGuiBase::StartVerticalLay
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:1184
EditCurvesDialog::~EditCurvesDialog
~EditCurvesDialog()
Definition: Equalization.cpp:3396
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:3531
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:651
EditCurvesDialog::DownButtonID
@ DownButtonID
Definition: Equalization.h:364
EditCurvesDialog::RenameButtonID
@ RenameButtonID
Definition: Equalization.h:365
EqualizationPanel::mBitmap
std::unique_ptr< wxBitmap > mBitmap
Definition: Equalization.h:335
Ruler::LinearDBFormat
@ LinearDBFormat
Definition: Ruler.h:34
fn
static const auto fn
Definition: WaveformView.cpp:1114
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:1125
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:70
EffectEqualization::WriteXML
void WriteXML(XMLWriter &xmlFile) const
Definition: Equalization.cpp:2324
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:2481
WaveClip::GetEnvelope
Envelope * GetEnvelope()
Definition: WaveClip.h:243
EffectEqualization::LoadCurves
void LoadCurves(const wxString &fileName={}, bool append=false)
Definition: Equalization.cpp:1604
Effect::CopyInputTracks
void CopyInputTracks(bool allSyncLockSelected=false)
Definition: Effect.cpp:2070
EffectEqualization::OnSize
void OnSize(wxSizeEvent &event)
Definition: Equalization.cpp:2835
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:2890
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:2650
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:2913
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:112
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:553
EffectEqualization::mLoFreq
double mLoFreq
Definition: Equalization.h:226
ID_Linear
@ ID_Linear
Definition: Equalization.cpp:133
FileNames::DataDir
AUDACITY_DLL_API FilePath DataDir()
Audacity user data directory.
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:2447
Envelope::SetTrackLen
void SetTrackLen(double trackLen, double sampleDur=0.0)
Definition: Envelope.cpp:791
XMLtypes
static const FileNames::FileTypes & XMLtypes()
Definition: Equalization.cpp:3752
ShuttleGui::MinSize
ShuttleGui & MinSize()
Definition: ShuttleGui.h:733
XO
#define XO(s)
Definition: Internat.h:31
FileNames::XMLFiles
AUDACITY_DLL_API const FileType XMLFiles
Definition: FileNames.h:77
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:3316
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:986
EqualizationPanel::mLinEditor
std::unique_ptr< EnvelopeEditor > mLinEditor
Definition: Equalization.h:331
Envelope::Reassign
int Reassign(double when, double value)
Move a point at when to value.
Definition: Envelope.cpp: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:1189
TrackArtist::pZoomInfo
ZoomInfo * pZoomInfo
Definition: TrackArtist.h:147
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:2447
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:569
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:70
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:2200
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:572
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:41
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:1848
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
floatSample
@ floatSample
Definition: SampleFormat.h:34
EditCurvesDialog::OnDelete
void OnDelete(wxCommandEvent &event)
Definition: Equalization.cpp:3661
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:3133
EqualizationPanel::OnSize
void OnSize(wxSizeEvent &event)
Definition: Equalization.cpp:3127
WaveClip
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:173
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:1890
EditCurvesDialog::PopulateList
void PopulateList(int position)
Definition: Equalization.cpp:3447
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:2171
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:453
WaveTrack::Clear
void Clear(double t0, double t1) override
Definition: WaveTrack.cpp:652
EffectEqualization::spline
void spline(double x[], double y[], size_t n, double y2[])
Definition: Equalization.cpp:2788
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:3410
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:2077
EqualizationPanel::Recalc
void Recalc()
Definition: Equalization.cpp:3118
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:1775
EffectEqualization::UpdateCurves
void UpdateCurves()
Definition: Equalization.cpp:2366
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:2051
Effect::LoadFactoryDefaults
bool LoadFactoryDefaults() override
Definition: Effect.cpp:605
EffectEqualization::mCurves
EQCurveArray mCurves
Definition: Equalization.h:233
ShuttleGuiBase::AddUnits
void AddUnits(const TranslatableString &Prompt, int wrapWidth=0)
Left aligned text string.
Definition: ShuttleGui.cpp:263
EffectEqualization::OnManage
void OnManage(wxCommandEvent &event)
Definition: Equalization.cpp:2933
ShuttleGuiBase::AddFixedText
void AddFixedText(const TranslatableString &Str, bool bCenter=false, int wrapWidth=0)
Definition: ShuttleGui.cpp:440
WaveTrack::GetClips
WaveClipHolders & GetClips()
Definition: WaveTrack.h:366
EffectEqualization::OnLinFreq
void OnLinFreq(wxCommandEvent &event)
Definition: Equalization.cpp:3024
kCURVE
const bool kCURVE
Definition: Equalization.cpp:464
EqualizationPanel::OnCaptureLost
void OnCaptureLost(wxMouseCaptureLostEvent &event)
Definition: Equalization.cpp:3343
Effect::ReplaceProcessedTracks
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: Effect.cpp:2192
RenameButtonID
@ RenameButtonID
Definition: BatchProcessDialog.cpp:503
WaveTrack::Paste
void Paste(double t0, const Track *src) override
Definition: WaveTrack.cpp:1172
EditCurvesDialog::OnUp
void OnUp(wxCommandEvent &event)
Definition: Equalization.cpp:3462
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:3545
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:2810
anonymous_namespace{NoteTrack.cpp}::swap
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:735
EffectEqualization::LoadFactoryPreset
bool LoadFactoryPreset(int id) override
Definition: Equalization.cpp: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:3760
EffectEqualization::mInterp
int mInterp
Definition: Equalization.h:216
WaveTrack::GetClipAtTime
WaveClip * GetClipAtTime(double time)
Definition: WaveTrack.cpp:2105
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:2510
NUMBER_OF_BANDS
#define NUMBER_OF_BANDS
Definition: Equalization.h:14
FileNames::ResourcesDir
AUDACITY_DLL_API FilePath ResourcesDir()
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:2508
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
Effect::GetCurrentSettingsGroup
RegistryPath GetCurrentSettingsGroup() override
Definition: Effect.cpp:865
EditCurvesDialog::DeleteButtonID
@ DeleteButtonID
Definition: Equalization.h:366
TrackTyper::List
std::tuple< Pair< Track, TrackKind::All >, Pair< AudioTrack, TrackKind::Audio >, Pair< PlayableTrack, TrackKind::Playable >, Pair< LabelTrack, TrackKind::Label >, Pair< NoteTrack, TrackKind::Note >, Pair< TimeTrack, TrackKind::Time >, Pair< WaveTrack, TrackKind::Wave > > List
Compile-time map from types to enum values.
Definition: Track.h: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:80
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:2163
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:1581
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:3880
WaveTrack::Join
void Join(double t0, double t1)
Definition: WaveTrack.cpp:1488
FileConfig::Flush
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
EffectEqualization::EnvLinToLog
void EnvLinToLog(void)
Definition: Equalization.cpp:2532
EffectEqualization::OnCurve
void OnCurve(wxCommandEvent &event)
Definition: Equalization.cpp:2921
anonymous_namespace{Equalization.cpp}::reg2
BuiltinEffectsModule::Registration< EffectEqualizationCurve > reg2
Definition: Equalization.cpp:205
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:495
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:20
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:3401
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:2090
EffectEqualization::szrL
wxSizer * szrL
Definition: Equalization.h:249
EffectEqualization::GetDefaultFileName
bool GetDefaultFileName(wxFileName &fileName)
Definition: Equalization.cpp:1820
EffectEqualization::mCurveName
wxString mCurveName
Definition: Equalization.h:211
EffectEqualization::mDrawMode
bool mDrawMode
Definition: Equalization.h:215
Equalization.h
EqualizationPanel::~EqualizationPanel
~EqualizationPanel()
Definition: Equalization.cpp:3106
EffectEqualization::mWhens
double mWhens[NUM_PTS]
Definition: Equalization.h:219
EffectEqualization::EnvelopeUpdated
void EnvelopeUpdated()
Definition: Equalization.cpp:2101
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:1576
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:1197
LINEAR_TO_DB
#define LINEAR_TO_DB(x)
Definition: MemoryX.h:629
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:2432
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:2394
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:79
EffectEqualization::OnSliderDBMIN
void OnSliderDBMIN(wxCommandEvent &event)
Definition: Equalization.cpp:2908
GetFFT
HFFT GetFFT(size_t fftlen)
Definition: RealFFTf.cpp:105
theTheme
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:203
EffectEqualization::CalcFilter
bool CalcFilter()
Definition: Equalization.cpp:1463
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:3835
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:2945
EffectEqualization::OnSliderM
void OnSliderM(wxCommandEvent &event)
Definition: Equalization.cpp:2902
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:2831
EditCurvesDialog::OnOK
void OnOK(wxCommandEvent &event)
Definition: Equalization.cpp:3852
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:3841
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:21
DefaultsButtonID
@ DefaultsButtonID
Definition: BatchProcessDialog.cpp:509
EffectEqualization::OnInterp
void OnInterp(wxCommandEvent &event)
Definition: Equalization.cpp:2879
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:261
TrackList::Selected
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:1372
anonymous_namespace{Menus.cpp}::Options
std::vector< CommandFlagOptions > & Options()
Definition: Menus.cpp:526
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:2841
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:2024
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:3500
EffectEqualization::mDraw
wxRadioButton * mDraw
Definition: Equalization.h:260
EffectEqualization::UpdateDefaultCurves
void UpdateDefaultCurves(bool updateAll=false)
Definition: Equalization.cpp:1702
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:2950
EqualizationPanel::mOutr
Floats mOutr
Definition: Equalization.h:342
EffectEqualization::mCurve
wxChoice * mCurve
Definition: Equalization.h:265
EffectEqualization::ErrMin
void ErrMin(void)
Definition: Equalization.cpp:2574
EffectEqualization::windowSize
static const size_t windowSize
Definition: Equalization.h:142