Audacity 3.2.0
EqualizationUI.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 EqualizationUI.cpp
6
7 Mitch Golden
8 Vaughan Johnson (Preview)
9 Martyn Shaw (FIR filters, response curve, graphic EQ)
10
11 Paul Licameli split from Equalization.cpp
12
13**********************************************************************/
14#include "EqualizationUI.h"
16#include "EqualizationPanel.h"
17#include "EffectEditor.h"
18#include <wx/button.h>
19#include <wx/choice.h>
20#include <wx/radiobut.h>
21#include <wx/sizer.h>
22#include <wx/checkbox.h>
23#include <wx/stattext.h>
24#include "ShuttleGui.h"
25#include "../widgets/RulerPanel.h"
26#include "../widgets/LinearUpdater.h"
27#include "../widgets/LogarithmicUpdater.h"
28#include "../widgets/IntFormat.h"
29#include "../widgets/LinearDBFormat.h"
30
31#if wxUSE_ACCESSIBILITY
32#include "WindowAccessible.h"
33#endif
34
35BEGIN_EVENT_TABLE(EqualizationUI, wxEvtHandler)
36 EVT_SIZE( EqualizationUI::OnSize )
38
40
41bool EqualizationUI::ValidateUI(EffectSettings &)
42{
43 const auto &parameters = mCurvesList.mParameters;
44 const auto &curveName = parameters.mCurveName;
45 auto &logEnvelope = parameters.mLogEnvelope;
46 const auto &curves = mCurvesList.mCurves;
47
48 // If editing a macro, we don't want to be using the unnamed curve so
49 // we offer to save it.
50
51 if (mDisallowCustom && curveName == wxT("unnamed"))
52 {
53 // PRL: This is unreachable. mDisallowCustom is always false.
54
56 mName,
57 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."),
58 XO("Filter Curve EQ needs a different name") );
59 return false;
60 }
61
62 EQCurveWriter{ curves }.SaveCurves();
63
64 parameters.SaveConfig(mManager);
65
66 return true;
67}
68
69std::unique_ptr<EffectEditor> EqualizationUI::PopulateOrExchange(
71 const EffectOutputs *)
72{
73 auto &parameters = mCurvesList.mParameters;
74 const auto &M = parameters.mM;
75 const auto &loFreq = parameters.mLoFreq;
76 const auto &hiFreq = parameters.mHiFreq;
77 const auto &curves = mCurvesList.mCurves;
78
79 auto &drawMode = parameters.mDrawMode;
80
81 S.SetBorder(0);
82
83 S.SetSizerProportion(1);
84 S.Prop(1).StartMultiColumn(1, wxEXPAND);
85 {
86 S.SetStretchyCol(0);
87 //S.SetStretchyRow(0); // The 5px Top border
88 S.SetStretchyRow(1); // The Graph
89 S.SetStretchyRow(2); // The EQ sliders
90 szrV = S.GetSizer();
91
92 // -------------------------------------------------------------------
93 // ROW 0: Top border
94 // -------------------------------------------------------------------
95 S.AddSpace(5);
96
97 // -------------------------------------------------------------------
98 // ROW 1: Equalization panel and sliders for vertical scale
99 // -------------------------------------------------------------------
100 S.SetSizerProportion(1);
101 S.Prop(1).StartMultiColumn(3, wxEXPAND);
102 {
103 S.SetStretchyCol(1);
104 S.SetStretchyRow(0);
105 szr1 = S.GetSizer();
106
107 S.StartVerticalLay(wxEXPAND, 1);
108 {
109 // Inserted into sizer later, but the EQ panel needs to point to it
111 S.GetParent(), wxID_ANY, wxHORIZONTAL,
112 wxSize{ 100, 100 }, // Ruler can't handle small sizes
113 RulerPanel::Range{ loFreq, hiFreq },
115 XO("Hz"),
117 .Log(true)
118 .Flip(true)
119 .LabelEdges(true)
120 .TicksAtExtremes(true)
121 .TickColour( { 0, 0, 0 } )
122 );
123
125 S.GetParent(), wxID_ANY, wxVERTICAL,
126 wxSize{ 100, 100 }, // Ruler can't handle small sizes
127 RulerPanel::Range{ 60.0, -120.0 },
129 XO("dB"),
131 .LabelEdges(true)
132 .TicksAtExtremes(true)
133 .TickColour( { 0, 0, 0 } )
134 );
135
136 S.Prop(0).AddSpace(0, 1);
137 S.Prop(1)
138 .Position(wxEXPAND)
139 .AddWindow(mdBRuler);
140 S.AddSpace(0, 1);
141 }
142 S.EndVerticalLay();
143
144 parameters.ChooseEnvelope().Flatten(0.);
145 parameters.ChooseEnvelope().SetTrackLen(1.0);
146 mPanel = safenew EqualizationPanel(S.GetParent(), wxID_ANY,
148 S.Prop(1)
149 .Position(wxEXPAND)
150 .MinSize( { wxDefaultCoord, wxDefaultCoord } )
151 .AddWindow(mPanel);
152
153 S.SetBorder(5);
154 S.StartVerticalLay();
155 {
156 S.AddVariableText(XO("+ dB"), false, wxCENTER);
158 .Name(XO("Max dB"))
159 .Style(wxSL_VERTICAL | wxSL_INVERSE)
160 .AddSlider( {}, 30, 60, 0);
161#if wxUSE_ACCESSIBILITY
162 mdBMaxSlider->SetAccessible(safenew SliderAx(mdBMaxSlider, XO("%d dB")));
163#endif
164 BindTo(*mdBMaxSlider, wxEVT_SLIDER,
166
168 .Name(XO("Min dB"))
169 .Style(wxSL_VERTICAL | wxSL_INVERSE)
170 .AddSlider( {}, -30, -10, -120);
171 S.AddVariableText(XO("- dB"), false, wxCENTER);
172#if wxUSE_ACCESSIBILITY
173 mdBMinSlider->SetAccessible(safenew SliderAx(mdBMinSlider, XO("%d dB")));
174#endif
175 BindTo(*mdBMinSlider, wxEVT_SLIDER,
177 }
178 S.EndVerticalLay();
179 S.SetBorder(0);
180
181 // -------------------------------------------------------------------
182 // Frequency ruler below graph
183 // -------------------------------------------------------------------
184
185 // Column 1 is empty
186 S.AddSpace(1, 1);
187
188 S.SetBorder(1);
189 S.Prop(1)
190 .Position(wxEXPAND | wxALIGN_LEFT | wxALIGN_TOP | wxLEFT)
191 .AddWindow(mFreqRuler);
192 S.SetBorder(0);
193
194 // Column 3 is empty
195 S.AddSpace(1, 1);
196 }
197 S.EndMultiColumn();
198
199 // -------------------------------------------------------------------
200 // ROW 2: Graphic EQ
201 // -------------------------------------------------------------------
202 S.SetSizerProportion(1);
203 S.StartHorizontalLay(wxEXPAND, 1);
204 {
205 szrG = S.GetSizer();
206
207 // Panel used to host the sliders since they will be positioned manually.
208 //mGraphicPanel = S.Prop(1)
209 //.Position(wxEXPAND)
210 //.Size( { -1, 150 } )
211 //.StartPanel();
212 S.AddSpace(15,0);
213 {
215 S.AddSpace(15,0);
216 } //S.EndPanel();
217 }
218 S.EndHorizontalLay();
219
220 // -------------------------------------------------------------------
221 // ROW 4: Various controls
222 // -------------------------------------------------------------------
223 S.SetSizerProportion(1);
224 S.Prop(1).StartMultiColumn(7, wxALIGN_CENTER_HORIZONTAL);
225 {
226 S.SetBorder(5);
227
228 S.AddSpace(5, 5);
229
230 if( mOptions == kEqLegacy )
231 {
232 S.StartHorizontalLay(wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
233 {
234 S.AddPrompt(XXO("&EQ Type:"));
235 }
236 S.EndHorizontalLay();
237
238 S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
239 {
240 S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
241 {
242 mDraw = S
243 .Name(XO("Draw Curves"))
244 .AddRadioButton(XXO("&Draw"));
245 BindTo(*mDraw, wxEVT_RADIOBUTTON,
247
248 mGraphic = S
249 .Name(XO("Graphic EQ"))
250 .AddRadioButtonToGroup(XXO("&Graphic"));
251 BindTo(*mGraphic, wxEVT_RADIOBUTTON,
253 }
254 S.EndHorizontalLay();
255 }
256 S.EndHorizontalLay();
257 }
258
259 S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0);
260 {
261 szrH = S.GetSizer();
262
263 S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
264 {
265 szrI = S.GetSizer();
266
268 .Name(XO("Interpolation type"))
269 .AddChoice( {},
272 0 );
273#if wxUSE_ACCESSIBILITY
274 // so that name can be set on a standard control
276#endif
277 BindTo(*mInterpChoice, wxEVT_CHOICE,
279 }
280 S.EndHorizontalLay();
281
282 S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
283 {
284 szrL = S.GetSizer();
285
286 mLinFreq = S
287 .Name(XO("Linear Frequency Scale"))
288 .AddCheckBox(XXO("Li&near Frequency Scale"), false);
289 BindTo(*mLinFreq, wxEVT_CHECKBOX,
291 }
292 S.EndHorizontalLay();
293 }
294 S.EndHorizontalLay();
295
296 // -------------------------------------------------------------------
297 // Filter length grouping
298 // -------------------------------------------------------------------
299
300 if( mOptions == kEqLegacy ){
301 S.StartHorizontalLay(wxEXPAND, 0);
302 {
303 S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0);
304 {
305 S.AddPrompt(XXO("Length of &Filter:"));
306 }
307 S.EndHorizontalLay();
308
309 S.StartHorizontalLay(wxEXPAND, 1);
310 {
311 mMSlider = S
312 .Name(XO("Length of Filter"))
313 .Style(wxSL_HORIZONTAL)
314 .AddSlider( {}, (M - 1) / 2, 4095, 10);
315 BindTo(*mMSlider, wxEVT_SLIDER,
317 }
318 S.EndHorizontalLay();
319
320 S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0);
321 {
322 wxString label;
323 label.Printf(wxT("%ld"), M);
324 mMText = S.Name( Verbatim( label ) )
325 // fix for bug 577 (NVDA/Narrator screen readers do not
326 // read static text in dialogs)
327 .AddVariableText( Verbatim( label ) );
328 }
329 S.EndHorizontalLay();
330 }
331 S.EndHorizontalLay();
332
333 S.AddSpace(1, 1);
334 }
335
336 S.AddSpace(5, 5);
337
338 if( mOptions == kEqLegacy ){
339 S.AddSpace(5, 5);
340 S.StartHorizontalLay(wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
341 {
342 S.AddPrompt(XXO("&Select Curve:"));
343 }
344 S.EndHorizontalLay();
345
346 S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
347 {
348 S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
349 {
350 mCurve = S
351 .Name(XO("Select Curve"))
352 .AddChoice( {},
353 [&curves]{
355 for (const auto &curve : curves)
356 names.push_back( Verbatim( curve.Name ) );
357 return names;
358 }()
359 );
360 BindTo(*mCurve, wxEVT_CHOICE,
362 }
363 S.EndHorizontalLay();
364 }
365 S.EndHorizontalLay();
366
367 const auto pButton = S
368 .AddButton(XXO("S&ave/Manage Curves..."));
369 BindTo(*pButton, wxEVT_BUTTON, &EqualizationUI::OnManage);
370 }
371
372 S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 1);
373 {
374 auto pButton = S
375 .AddButton(XXO("Fla&tten"));
376 BindTo(*pButton, wxEVT_BUTTON, &EqualizationUI::OnClear);
377
378 pButton = S
379 .AddButton(XXO("&Invert"));
380 BindTo(*pButton, wxEVT_BUTTON, &EqualizationUI::OnInvert);
381
382 mGridOnOff = S
383 .Name(XO("Show grid lines"))
384 .AddCheckBox(XXO("Show g&rid lines"), false);
385 BindTo(*mGridOnOff, wxEVT_CHECKBOX,
387 }
388 S.EndHorizontalLay();
389
390 S.AddSpace(5, 5);
391 }
392 S.EndMultiColumn();
393 }
394 S.EndMultiColumn();
395
396 mUIParent->SetAutoLayout(false);
398 mUIParent->Layout();
399
401 drawMode = true;
403 drawMode = false;
404
405 // "show" settings for graphics mode before setting the size of the dialog
406 // as this needs more space than draw mode
407 szrV->Show(szrG,!drawMode); // eq sliders
408 szrH->Show(szrI,true); // interpolation choice
409 szrH->Show(szrL,false); // linear freq checkbox
410
412 mPanel->Show( false );
413 wxSize sz = szrV->GetMinSize();
414 sz += wxSize( 30, 0);
415 mUIParent->SetMinSize(sz);
416 }
417 else{
418 mPanel->Show( true );
419 szrV->Show(szr1, true);
420 // This sizing calculation is hacky.
421 // Rather than set the true minimum size we set a size we would
422 // like to have.
423 // This makes the default size of the dialog good, but has the
424 // downside that the user can't adjust the dialog smaller.
425 wxSize sz = szrV->GetMinSize();
426 sz += wxSize( 400, 100);
427 szrV->SetMinSize(sz);
428 }
430
431 return nullptr;
432}
433
435{
436 auto &parameters = mCurvesList.mParameters;
437 const auto &lin = parameters.mLin;
438 const auto &drawGrid = parameters.mDrawGrid;
439 const auto &M = parameters.mM;
440 const auto &dBMin = parameters.mdBMin;
441 const auto &dBMax = parameters.mdBMax;
442 const auto &interp = parameters.mInterp;
443
444 auto &drawMode = parameters.mDrawMode;
445
446 // Set log or lin freq scale (affects interpolation as well)
447 mLinFreq->SetValue( lin );
448 wxCommandEvent dummyEvent;
449 OnLinFreq(dummyEvent); // causes a CalcFilter
450
451 mGridOnOff->SetValue( drawGrid ); // checks/unchecks the box on the interface
452
453 if( mMSlider )
454 mMSlider->SetValue((M - 1) / 2);
455
456 mdBMinSlider->SetValue((int)dBMin);
457 mdBMaxSlider->SetValue((int)dBMax);
458
459 // Reload the curve names
460 UpdateCurves();
461
462 // Set graphic interpolation mode
463 mInterpChoice->SetSelection(interp);
464
465 // Override draw mode, if we're not displaying the radio buttons.
467 drawMode = true;
469 drawMode = false;
470
471 if( mDraw )
472 mDraw->SetValue(drawMode);
473 szrV->Show(szr1,mOptions != kEqOptionGraphic); // Graph
474 szrV->Show(szrG,!drawMode); // eq sliders
475 szrH->Show(szrI,mOptions == kEqLegacy ); // interpolation choice
476 szrH->Show(szrL, drawMode); // linear freq checkbox
477 if( mGraphic)
478 mGraphic->SetValue(!drawMode);
479 mGridOnOff->Show( drawMode );
480
481 // Set Graphic (Fader) or Draw mode
482 if (!drawMode)
484
485 UpdateRuler();
486
487 mUIParent->Layout();
488 wxGetTopLevelParent(mUIParent)->Layout();
489
490 return true;
491}
492
494{
495 const auto &parameters = mCurvesList.mParameters;
496 const auto &dBMin = parameters.mdBMin;
497 const auto &dBMax = parameters.mdBMax;
498
499 // Refresh ruler when values have changed
500 int w1, w2, h;
501 mdBRuler->ruler.GetMaxSize(&w1, &h);
502 mdBRuler->ruler.SetRange(dBMax, dBMin);
503 mdBRuler->ruler.GetMaxSize(&w2, &h);
504 if( w1 != w2 ) // Reduces flicker
505 {
506 mdBRuler->SetSize(wxSize(w2,h));
507 mFreqRuler->Refresh(false);
508 }
509 mdBRuler->Refresh(false);
510
511 mPanel->Refresh(false);
512}
513
514//
515// Make the passed curve index the active one
516//
517void EqualizationUI::setCurve(int currentCurve)
518{
519 auto &parameters = mCurvesList.mParameters;
520 constexpr auto loFreqI = EqualizationFilter::loFreqI;
521
522 const auto &lin = parameters.mLin;
523 const auto &hiFreq = parameters.mHiFreq;
524 auto &curves = mCurvesList.mCurves;
525
526 // Set current choice
527 wxASSERT( currentCurve < (int) curves.size() );
528 mCurvesList.Select(currentCurve);
529
530 int numPoints = (int) curves[currentCurve].points.size();
531
532 auto &env = parameters.ChooseEnvelope();
533 env.Flatten(0.);
534 env.SetTrackLen(1.0);
535
536 // Handle special case of no points.
537 if (numPoints == 0) {
539 return;
540 }
541
542 double when, value;
543
544 // Handle special case 1 point.
545 if (numPoints == 1) {
546 // only one point, so ensure it is in range then return.
547 when = curves[currentCurve].points[0].Freq;
548 if (lin) {
549 when = when / hiFreq;
550 }
551 else { // log scale
552 // We don't go below loFreqI (20 Hz) in log view.
553 double loLog = log10((double)loFreqI);
554 double hiLog = log10(hiFreq);
555 double denom = hiLog - loLog;
556 when =
557 (log10(std::max<double>(loFreqI, when))
558 - loLog) / denom;
559 }
560 value = curves[currentCurve].points[0].dB;
561 env.Insert(std::min(1.0, std::max(0.0, when)), value);
563 return;
564 }
565
566 // We have at least two points, so ensure they are in frequency order.
567 std::sort(curves[currentCurve].points.begin(),
568 curves[currentCurve].points.end());
569
570 if (curves[currentCurve].points[0].Freq < 0) {
571 // Corrupt or invalid curve, so bail.
573 return;
574 }
575
576 if(lin) { // linear Hz scale
577 for(int pointCount = 0; pointCount < numPoints; pointCount++) {
578 when = curves[currentCurve].points[pointCount].Freq / hiFreq;
579 value = curves[currentCurve].points[pointCount].dB;
580 if(when <= 1) {
581 env.Insert(when, value);
582 if (when == 1)
583 break;
584 }
585 else {
586 // There are more points at higher freqs,
587 // so interpolate next one then stop.
588 when = 1.0;
589 double nextDB = curves[currentCurve].points[pointCount].dB;
590 if (pointCount > 0) {
591 double nextF = curves[currentCurve].points[pointCount].Freq;
592 double lastF = curves[currentCurve].points[pointCount-1].Freq;
593 double lastDB = curves[currentCurve].points[pointCount-1].dB;
594 value = lastDB +
595 ((nextDB - lastDB) *
596 ((hiFreq - lastF) / (nextF - lastF)));
597 }
598 else
599 value = nextDB;
600 env.Insert(when, value);
601 break;
602 }
603 }
604 }
605 else { // log Hz scale
606 double loLog = log10((double) loFreqI);
607 double hiLog = log10(hiFreq);
608 double denom = hiLog - loLog;
609 int firstAbove20Hz;
610
611 // log scale EQ starts at 20 Hz (threshold of hearing).
612 // so find the first point (if any) above 20 Hz.
613 for (firstAbove20Hz = 0; firstAbove20Hz < numPoints; firstAbove20Hz++) {
614 if (curves[currentCurve].points[firstAbove20Hz].Freq > loFreqI)
615 break;
616 }
617
618 if (firstAbove20Hz == numPoints) {
619 // All points below 20 Hz, so just use final point.
620 when = 0.0;
621 value = curves[currentCurve].points[numPoints-1].dB;
622 env.Insert(when, value);
624 return;
625 }
626
627 if (firstAbove20Hz > 0) {
628 // At least one point is before 20 Hz and there are more
629 // beyond 20 Hz, so interpolate the first
630 double prevF = curves[currentCurve].points[firstAbove20Hz-1].Freq;
631 prevF = log10(std::max(1.0, prevF)); // log zero is bad.
632 double prevDB = curves[currentCurve].points[firstAbove20Hz-1].dB;
633 double nextF = log10(curves[currentCurve].points[firstAbove20Hz].Freq);
634 double nextDB = curves[currentCurve].points[firstAbove20Hz].dB;
635 when = 0.0;
636 value = nextDB - ((nextDB - prevDB) * ((nextF - loLog) / (nextF - prevF)));
637 env.Insert(when, value);
638 }
639
640 // Now get the rest.
641 for(int pointCount = firstAbove20Hz; pointCount < numPoints; pointCount++)
642 {
643 double flog = log10(curves[currentCurve].points[pointCount].Freq);
644 wxASSERT(curves[currentCurve].points[pointCount].Freq >= loFreqI);
645
646 when = (flog - loLog)/denom;
647 value = curves[currentCurve].points[pointCount].dB;
648 if(when <= 1.0) {
649 env.Insert(when, value);
650 }
651 else {
652 // This looks weird when adjusting curve in Draw mode if
653 // there is a point off-screen.
654
655 /*
656 // we have a point beyond fs/2. Insert it so that env code can use it.
657 // but just this one, we have no use for the rest
658 env.SetTrackLen(when); // can't Insert if the envelope isn't long enough
659 env.Insert(when, value);
660 break;
661 */
662
663 // interpolate the final point instead
664 when = 1.0;
665 if (pointCount > 0) {
666 double lastDB = curves[currentCurve].points[pointCount-1].dB;
667 double logLastF =
668 log10(curves[currentCurve].points[pointCount-1].Freq);
669 value = lastDB +
670 ((value - lastDB) *
671 ((log10(hiFreq) - logLastF) / (flog - logLastF)));
672 }
673 env.Insert(when, value);
674 break;
675 }
676 }
677 }
679}
680
682{
683 const auto &curves = mCurvesList.mCurves;
684 setCurve((int) curves.size() - 1);
685}
686
687void EqualizationUI::setCurve(const wxString &curveName)
688{
689 const auto &curves = mCurvesList.mCurves;
690 unsigned i = 0;
691 for( i = 0; i < curves.size(); i++ )
692 if( curveName == curves[ i ].Name )
693 break;
694 if( i == curves.size())
695 {
697 XO("Requested curve not found, using 'unnamed'"),
698 XO("Curve not found"),
699 wxOK|wxICON_ERROR);
700 setCurve();
701 }
702 else
703 setCurve( i );
704}
705
707//
708// All EffectEqualization methods beyond this point interact with the UI, so
709// can't be called while the UI is not displayed.
710//
712
714{
715 auto &parameters = mCurvesList.mParameters;
716 auto &curveName = parameters.mCurveName;
717 const auto &curves = mCurvesList.mCurves;
718
719 // Reload the curve names
720 if( mCurve )
721 mCurve->Clear();
722 bool selectedCurveExists = false;
723 for (size_t i = 0, cnt = curves.size(); i < cnt; i++)
724 {
725 if (curveName == curves[ i ].Name)
726 selectedCurveExists = true;
727 if( mCurve )
728 mCurve->Append(curves[ i ].Name);
729 }
730 // In rare circumstances, curveName may not exist (bug 1891)
731 if (!selectedCurveExists)
732 curveName = curves[ (int)curves.size() - 1 ].Name;
733 if( mCurve )
734 mCurve->SetStringSelection(curveName);
735
736 // Allow the control to resize
737 if( mCurve )
738 mCurve->SetMinSize({-1, -1});
739
740 // Set initial curve
741 setCurve( curveName );
742}
743
745{
746 auto &parameters = mCurvesList.mParameters;
747 const auto &lin = parameters.mLin;
748 auto &linEnvelope = parameters.mLinEnvelope;
749 auto &logEnvelope = parameters.mLogEnvelope;
750 const auto &hiFreq = parameters.mHiFreq;
751
752 size_t numPoints = logEnvelope.GetNumberOfPoints();
753 Doubles when{ numPoints };
754 Doubles value{ numPoints };
755 double deltadB = 0.1;
756 double dx, dy, dx1, dy1, err;
757
758 logEnvelope.GetPoints( when.get(), value.get(), numPoints );
759
760 // set 'unnamed' as the selected curve
762
763 bool flag = true;
764 while (flag)
765 {
766 flag = false;
767 int numDeleted = 0;
768 logEnvelope.GetPoints( when.get(), value.get(), numPoints );
769 for (size_t j = 0; j + 2 < numPoints; j++)
770 {
771 dx = when[j+2+numDeleted] - when[j+numDeleted];
772 dy = value[j+2+numDeleted] - value[j+numDeleted];
773 dx1 = when[j+numDeleted+1] - when[j+numDeleted];
774 dy1 = dy * dx1 / dx;
775 err = fabs(value[j+numDeleted+1] - (value[j+numDeleted] + dy1));
776 if( err < deltadB )
777 { // within < deltadB dB?
778 logEnvelope.Delete(j+1);
779 numPoints--;
780 numDeleted++;
781 flag = true;
782 }
783 }
784 }
785
786 if(lin) // do not use IsLinear() here
787 {
790 mFreqRuler->ruler.SetRange(0, hiFreq);
791 }
792
793 szrV->Show(szrG,false);
794 szrH->Show(szrI,false);
795 szrH->Show(szrL,true);
796
797 mUIParent->Layout();
798 wxGetTopLevelParent(mUIParent)->Layout();
799 mCurvesList.ForceRecalc(); // it may have changed slightly due to the deletion of points
800}
801
803{
804 auto &parameters = mCurvesList.mParameters;
805 const auto &lin = parameters.mLin;
806 auto &linEnvelope = parameters.mLinEnvelope;
807 auto &logEnvelope = parameters.mLogEnvelope;
808 const auto &loFreq = parameters.mLoFreq;
809 const auto &hiFreq = parameters.mHiFreq;
810
811 auto &drawMode = parameters.mDrawMode;
812
813 if(lin) //going from lin to log freq scale - do not use IsLinear() here
814 { // add some extra points to the linear envelope for the graphic to follow
815 double step = pow(2., 1./12.); // twelve steps per octave
816 double when,value;
817 for(double freq=10.; freq<hiFreq; freq*=step)
818 {
819 when = freq/hiFreq;
820 value = linEnvelope.GetValue(when);
821 linEnvelope.Insert(when, value);
822 }
823
826 mFreqRuler->ruler.SetRange(loFreq, hiFreq);
827 }
828
829 mBands.ErrMin(); //move sliders to minimise error
830
831 szrV->Show(szrG,true); // eq sliders
832 szrH->Show(szrI,mOptions == kEqLegacy ); // interpolation choice
833 szrH->Show(szrL,false); // linear freq checkbox
834
835 mUIParent->Layout();
836 wxGetTopLevelParent(mUIParent)->Layout();
837 mUIParent->Layout();
838 wxGetTopLevelParent(mUIParent)->Layout();
839
840 mBands.GraphicEQ(logEnvelope);
841 drawMode = false;
842}
843
844void EqualizationUI::OnSize(wxSizeEvent & event)
845{
846 mUIParent->Layout();
847 event.Skip();
848}
849
850void EqualizationUI::OnInterp(wxCommandEvent & WXUNUSED(event))
851{
852 auto &parameters = mCurvesList.mParameters;
853 bool bIsGraphic = !parameters.mDrawMode;
854 if (bIsGraphic)
855 {
856 mBands.GraphicEQ(parameters.mLogEnvelope);
858 }
859 parameters.mInterp = mInterpChoice->GetSelection();
860}
861
862void EqualizationUI::OnDrawMode(wxCommandEvent & WXUNUSED(event))
863{
865 UpdateDraw();
866}
867
868void EqualizationUI::OnGraphicMode(wxCommandEvent & WXUNUSED(event))
869{
872}
873
874void EqualizationUI::OnSliderM(wxCommandEvent & WXUNUSED(event))
875{
876 auto &M = mCurvesList.mParameters.mM;
877
878 size_t m = 2 * mMSlider->GetValue() + 1;
879 // Must be odd
880 wxASSERT( (m & 1) == 1 );
881
882 if (m != M) {
883 M = m;
884 wxString tip;
885 tip.Printf(wxT("%d"), (int)M);
886 mMText->SetLabel(tip);
887 mMText->SetName(mMText->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
888 mMSlider->SetToolTip(tip);
889
891 }
892}
893
894void EqualizationUI::OnSliderDBMIN(wxCommandEvent & WXUNUSED(event))
895{
896 auto &dBMin = mCurvesList.mParameters.mdBMin;
897
898 float dB = mdBMinSlider->GetValue();
899 if (dB != dBMin) {
900 dBMin = dB;
901 wxString tip;
902 tip.Printf(_("%d dB"), (int)dBMin);
903 mdBMinSlider->SetToolTip(tip);
904 UpdateRuler();
905 }
906}
907
908void EqualizationUI::OnSliderDBMAX(wxCommandEvent & WXUNUSED(event))
909{
910 auto &dBMax = mCurvesList.mParameters.mdBMax;
911
912 float dB = mdBMaxSlider->GetValue();
913 if (dB != dBMax) {
914 dBMax = dB;
915 wxString tip;
916 tip.Printf(_("%d dB"), (int)dBMax);
917 mdBMaxSlider->SetToolTip(tip);
918 UpdateRuler();
919 }
920}
921
922//
923// New curve was selected
924//
925void EqualizationUI::OnCurve(wxCommandEvent & WXUNUSED(event))
926{
927 // Select NEW curve
928 wxASSERT( mCurve != NULL );
929 setCurve( mCurve->GetCurrentSelection() );
932}
933
934//
935// User wants to modify the list in some way
936//
937void EqualizationUI::OnManage(wxCommandEvent & WXUNUSED(event))
938{
939 auto &curves = mCurvesList.mCurves;
941 curves, mCurve->GetSelection());
942 if (d.ShowModal()) {
943 wxGetTopLevelParent(mUIParent)->Layout();
944 setCurve(d.GetItem());
945 }
946
947 // Reload the curve names
948 UpdateCurves();
949
950 // Allow control to resize
951 mUIParent->Layout();
952}
953
954void EqualizationUI::OnClear(wxCommandEvent & WXUNUSED(event))
955{
956 mBands.Flatten();
957}
958
959void EqualizationUI::OnInvert(wxCommandEvent & WXUNUSED(event))
960{
961 mBands.Invert();
962}
963
964void EqualizationUI::OnGridOnOff(wxCommandEvent & WXUNUSED(event))
965{
967 mPanel->Refresh(false);
968}
969
970void EqualizationUI::OnLinFreq(wxCommandEvent & WXUNUSED(event))
971{
972 auto &parameters = mCurvesList.mParameters;
973 auto &lin = parameters.mLin;
974 const auto &loFreq = parameters.mLoFreq;
975 const auto &hiFreq = parameters.mHiFreq;
976
977 lin = mLinFreq->IsChecked();
978 if(parameters.IsLinear()) //going from log to lin freq scale
979 {
981 mFreqRuler->ruler.SetRange(0, hiFreq);
983 lin = true;
984 }
985 else //going from lin to log freq scale
986 {
988 mFreqRuler->ruler.SetRange(loFreq, hiFreq);
990 lin = false;
991 }
992 mFreqRuler->Refresh(false);
994}
995
996void EqualizationUI::OnIdle(wxIdleEvent &event)
997{
998 event.Skip();
999 if (mCurve)
1000 mCurve->SetStringSelection(mCurvesList.mParameters.mCurveName);
1001}
wxT("CloseDown"))
END_EVENT_TABLE()
int min(int a, int b)
const int kEqLegacy
const int kEqOptionCurve
const int kEqOptionGraphic
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define _(s)
Definition: Internat.h:73
#define safenew
Definition: MemoryX.h:10
TranslatableStrings Msgids(const EnumValueSymbol strings[], size_t nStrings)
Convenience function often useful when adding choice controls.
TranslatableString label
Definition: TagsEditor.cpp:164
static TranslatableStrings names
Definition: TagsEditor.cpp:152
#define S(N)
Definition: ToChars.cpp:64
static Settings & settings()
Definition: TrackInfo.cpp:87
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
std::vector< TranslatableString > TranslatableStrings
static std::once_flag flag
Serializer of curves into XML files.
void SaveCurves(const wxString &fileName={})
Performs effect computation.
Hold values to send to effect output meters.
EqualizationCurvesDialog manages the available preset curves.
EqualizationPanel is used with EqualizationDialog and controls a graph for EffectEqualization....
wxSizer * szrH
void OnIdle(wxIdleEvent &event)
void OnSliderDBMAX(wxCommandEvent &event)
void OnDrawMode(wxCommandEvent &event)
void OnManage(wxCommandEvent &event)
EqualizationCurvesList & mCurvesList
void OnGraphicMode(wxCommandEvent &event)
wxRadioButton * mGraphic
wxWeakRef< EqualizationPanel > mPanel
wxSizer * szrV
wxCheckBox * mGridOnOff
void OnClear(wxCommandEvent &event)
wxChoice * mInterpChoice
wxStaticText * mMText
RulerPanel * mFreqRuler
void OnLinFreq(wxCommandEvent &event)
void OnInterp(wxCommandEvent &event)
void OnCurve(wxCommandEvent &event)
void BindTo(wxEvtHandler &src, const EventTag &eventType, void(Class::*pmf)(Event &))
const int mOptions
void OnSliderDBMIN(wxCommandEvent &event)
EqualizationBandSliders mBands
wxCheckBox * mLinFreq
wxSizer * szrG
void OnInvert(wxCommandEvent &event)
const wxWeakRef< wxWindow > & mUIParent
wxSlider * mdBMinSlider
wxSlider * mdBMaxSlider
void OnSliderM(wxCommandEvent &event)
std::unique_ptr< EffectEditor > PopulateOrExchange(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access, const EffectOutputs *pOutputs)
wxSlider * mMSlider
bool TransferDataToWindow(const EffectSettings &settings)
RulerPanel * mdBRuler
void OnSize(wxSizeEvent &event)
wxRadioButton * mDraw
TranslatableString mName
wxWeakRef< wxChoice > mCurve
void OnGridOnOff(wxCommandEvent &event)
static const IntFormat & Instance()
Definition: IntFormat.cpp:14
static const LinearDBFormat & Instance()
static const LinearUpdater & Instance()
static const LogarithmicUpdater & Instance()
void SetUpdater(const RulerUpdater *pUpdater)
Definition: Ruler.cpp:111
void GetMaxSize(wxCoord *width, wxCoord *height)
Definition: Ruler.cpp:613
void SetRange(double min, double max)
Definition: Ruler.cpp:151
RulerPanel class allows you to work with a Ruler like any other wxWindow.
Definition: RulerPanel.h:19
Ruler ruler
Definition: RulerPanel.h:79
std::pair< double, double > Range
Definition: RulerPanel.h:23
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:625
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
int DoMessageBox(const TranslatableString &name, const TranslatableString &msg, const TranslatableString &titleStr, long style=wxOK|wxCENTRE)
Externalized state of a plug-in.
void AddBandSliders(ShuttleGui &S)
EqualizationFilter & mParameters
static constexpr int loFreqI
static const EnumValueSymbol kInterpStrings[nInterpolations]
Options & LabelEdges(bool l)
Definition: RulerPanel.h:41
Options & Log(bool l)
Definition: RulerPanel.h:35