Audacity 3.2.0
FreqWindow.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 FreqWindow.cpp
6
7 Dominic Mazzoni
8
9*******************************************************************//****************************************************************//*******************************************************************/
28
29/*
30 Salvo Ventura - November 2006
31 Extended range check for additional FFT windows
32*/
33
34#include "FreqWindow.h"
35
36#include <algorithm>
37
38#include <wx/setup.h> // for wxUSE_* macros
39
40#include <wx/brush.h>
41#include <wx/button.h>
42#include <wx/checkbox.h>
43#include <wx/choice.h>
44#include <wx/dcclient.h>
45#include <wx/dcmemory.h>
46#include <wx/font.h>
47#include <wx/file.h>
48#include <wx/scrolbar.h>
49#include <wx/slider.h>
50#include <wx/statbmp.h>
51#include <wx/stattext.h>
52#include <wx/statusbr.h>
53
54#include <wx/textctrl.h>
55#include <wx/textfile.h>
56
57#include <wx/wfstream.h>
58#include <wx/txtstrm.h>
59
60#include <math.h>
61
62#include "SelectFile.h"
63#include "ShuttleGui.h"
64#include "AColor.h"
65#include "CommonCommandFlags.h"
66#include "FFT.h"
67#include "PitchName.h"
68#include "Decibels.h"
69#include "Prefs.h"
70#include "Project.h"
71#include "ProjectWindow.h"
72#include "Theme.h"
73#include "ViewInfo.h"
74#include "AllThemeResources.h"
75
76#include "FileNames.h"
77
78#include "WaveTrack.h"
79
80#include "HelpSystem.h"
81#include "AudacityMessageBox.h"
82#include "widgets/RulerPanel.h"
86#include "widgets/RealFormat.h"
88
89#if wxUSE_ACCESSIBILITY
90#include "WindowAccessible.h"
91#endif
92
93#define FrequencyAnalysisTitle XO("Frequency Analysis")
94
95DEFINE_EVENT_TYPE(EVT_FREQWINDOW_RECALC);
96
97enum {
98 FirstID = 7000,
99
110
111// These specify the minimum plot window width
112
113#define FREQ_WINDOW_WIDTH 480
114#define FREQ_WINDOW_HEIGHT 330
115
116static const char * ZoomIn[] = {
117"16 16 6 1",
118" c None",
119"+ c #1C1C1C",
120"@ c #AEAEAE",
121"# c #F7F7F7",
122"$ c #CFCECC",
123"* c #1C1CA0",
124" ++++ ",
125" @+# @[email protected] ",
126" + @** [email protected] ",
127" +#@ ** #+ ",
129" + ****** [email protected]",
130" +# ** #[email protected]",
131" + ** [email protected]@",
132" +++# #[email protected]@ ",
134" [email protected]@ @@@@ ",
139" @@ "};
140
141static const char * ZoomOut[] = {
142"16 16 6 1",
143" c None",
144"+ c #1C1C1C",
145"@ c #AEAEAE",
146"# c #F7F7F7",
147"$ c #CFCECC",
148"* c #1C1CA0",
149" ++++ ",
150" @+# [email protected] ",
151" + @@ [email protected] ",
152" +# @ #+ ",
154" + ****** [email protected]",
155" +# #[email protected]",
157" +++# #[email protected]@ ",
159" [email protected]@ @@@@ ",
164" @@ "};
165
166// FrequencyPlotDialog
167
168BEGIN_EVENT_TABLE(FrequencyPlotDialog, wxDialogWrapper)
182 EVT_COMMAND(wxID_ANY, EVT_FREQWINDOW_RECALC, FrequencyPlotDialog::OnRecalc)
184
185FrequencyPlotDialog::FrequencyPlotDialog(wxWindow * parent, wxWindowID id,
186 AudacityProject &project,
188 const wxPoint & pos)
189: wxDialogWrapper(parent, id, title, pos, wxDefaultSize,
190 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX),
191 mProject{ &project }
192, mAnalyst(std::make_unique<SpectrumAnalyst>())
193{
194 SetName();
195
196 mMouseX = 0;
197 mMouseY = 0;
198 mRate = 0;
199 mDataLen = 0;
200
201 gPrefs->Read(wxT("/FrequencyPlotDialog/DrawGrid"), &mDrawGrid, true);
202 gPrefs->Read(wxT("/FrequencyPlotDialog/SizeChoice"), &mSize, 3);
203
204 int alg;
205 gPrefs->Read(wxT("/FrequencyPlotDialog/AlgChoice"), &alg, 0);
206 mAlg = static_cast<SpectrumAnalyst::Algorithm>(alg);
207
208 gPrefs->Read(wxT("/FrequencyPlotDialog/FuncChoice"), &mFunc, 3);
209 gPrefs->Read(wxT("/FrequencyPlotDialog/AxisChoice"), &mAxis, 1);
210
211 Populate();
212}
213
215{
216}
217
219{
221
222 TranslatableStrings algChoices{
223 XO("Spectrum") ,
224 XO("Standard Autocorrelation") ,
225 XO("Cuberoot Autocorrelation") ,
226 XO("Enhanced Autocorrelation") ,
227 /* i18n-hint: This is a technical term, derived from the word
228 * "spectrum". Do not translate it unless you are sure you
229 * know the correct technical word in your language. */
230 XO("Cepstrum") ,
231 };
232
233 TranslatableStrings sizeChoices{
234 Verbatim( "128" ) ,
235 Verbatim( "256" ) ,
236 Verbatim( "512" ) ,
237 Verbatim( "1024" ) ,
238 Verbatim( "2048" ) ,
239 Verbatim( "4096" ) ,
240 Verbatim( "8192" ) ,
241 Verbatim( "16384" ) ,
242 Verbatim( "32768" ) ,
243 Verbatim( "65536" ) ,
244 Verbatim( "131072" ) ,
245 };
246
247 TranslatableStrings funcChoices;
248 for (int i = 0, cnt = NumWindowFuncs(); i < cnt; i++)
249 {
250 funcChoices.push_back(
251 /* i18n-hint: This refers to a "window function",
252 * such as Hann or Rectangular, used in the
253 * Frequency analyze dialog box. */
254 XO("%s window").Format( WindowFuncName(i) ) );
255 }
256
257 TranslatableStrings axisChoices{
258 XO("Linear frequency") ,
259 XO("Log frequency") ,
260 };
261
262 mFreqFont = wxFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
263 mArrowCursor = std::make_unique<wxCursor>(wxCURSOR_ARROW);
264 mCrossCursor = std::make_unique<wxCursor>(wxCURSOR_CROSS);
265
266 long size;
267 // reinterpret one of the verbatim strings above as a number
268 sizeChoices[mSize].MSGID().GET().ToLong(&size);
270
272 if(dBRange < 90.)
273 dBRange = 90.;
274
275 ShuttleGui S(this, eIsCreating);
276
277 S.SetBorder(0);
278
279 S.AddSpace(5);
280
281 S.SetSizerProportion(1);
282 S.StartMultiColumn(3, wxEXPAND);
283 {
284 S.SetStretchyCol(1);
285 S.SetStretchyRow(0);
286
287 // -------------------------------------------------------------------
288 // ROW 1: Freq response panel and sliders for vertical scale
289 // -------------------------------------------------------------------
290
291 S.StartVerticalLay(2);
292 {
294 S.GetParent(), wxID_ANY, wxVERTICAL,
295 wxSize{ 100, 100 }, // Ruler can't handle small sizes
296 RulerPanel::Range{ 0.0, -dBRange },
298 XO("dB"),
300 .LabelEdges(true)
301 .TickColour( theTheme.Colour( clrGraphLabels ) )
302 );
303
304 S.AddSpace(wxDefaultCoord, 1);
305 S.Prop(1)
306 .Position(wxALIGN_RIGHT | wxALIGN_TOP)
307 .AddWindow(vRuler);
308 S.AddSpace(wxDefaultCoord, 1);
309 }
310 S.EndVerticalLay();
311
312 mFreqPlot = safenew FreqPlot(S.GetParent(), wxID_ANY);
313 S.Prop(1)
314 .Position(wxEXPAND)
315 .MinSize( { wxDefaultCoord, FREQ_WINDOW_HEIGHT } )
316 .AddWindow(mFreqPlot);
317
318 S.StartHorizontalLay(wxEXPAND, 0);
319 {
320 S.StartVerticalLay();
321 {
322 mPanScroller = safenew wxScrollBar(S.GetParent(), FreqPanScrollerID,
323 wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL);
324#if wxUSE_ACCESSIBILITY
325 // so that name can be set on a standard control
327#endif
328 S.Prop(1);
329 S
330 .Name(XO("Scroll"))
331 .Position( wxALIGN_LEFT | wxTOP)
332 .AddWindow(mPanScroller);
333 }
334 S.EndVerticalLay();
335
336 S.StartVerticalLay();
337 {
338 wxStaticBitmap *zi = safenew wxStaticBitmap(S.GetParent(), wxID_ANY, wxBitmap(ZoomIn));
339 S.Position(wxALIGN_CENTER)
340 .AddWindow(zi);
341
342 S.AddSpace(5);
343
344 mZoomSlider = safenew wxSliderWrapper(S.GetParent(), FreqZoomSliderID, 100, 1, 100,
345 wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL);
346 S.Prop(1);
347 S
348 .Name(XO("Zoom"))
349 .Position(wxALIGN_CENTER_HORIZONTAL)
350 .AddWindow(mZoomSlider);
351#if wxUSE_ACCESSIBILITY
352 // so that name can be set on a standard control
354#endif
355
356 S.AddSpace(5);
357
358 wxStaticBitmap *zo = safenew wxStaticBitmap(S.GetParent(), wxID_ANY, wxBitmap(ZoomOut));
359 S.Position(wxALIGN_CENTER)
360 .AddWindow(zo);
361 }
362 S.EndVerticalLay();
363
364 S.AddSpace(5, wxDefaultCoord);
365 }
366 S.EndHorizontalLay();
367
368 // -------------------------------------------------------------------
369 // ROW 2: Frequency ruler
370 // -------------------------------------------------------------------
371
372 S.AddSpace(1);
373
374 S.StartHorizontalLay(wxEXPAND, 0);
375 {
377 S.GetParent(), wxID_ANY, wxHORIZONTAL,
378 wxSize{ 100, 100 }, // Ruler can't handle small sizes
379 RulerPanel::Range{ 10, 20000 },
381 XO("Hz"),
383 .Log(true)
384 .Flip(true)
385 .LabelEdges(true)
386 .TickColour( theTheme.Colour( clrGraphLabels ) )
387 );
388
389 S.AddSpace(1, wxDefaultCoord);
390 S.Prop(1)
391 .Position(wxALIGN_LEFT | wxALIGN_TOP)
392 .AddWindow(hRuler);
393 S.AddSpace(1, wxDefaultCoord);
394 }
395 S.EndHorizontalLay();
396
397 S.AddSpace(1);
398
399 // -------------------------------------------------------------------
400 // ROW 3: Spacer
401 // -------------------------------------------------------------------
402
403 S.AddSpace(5);
404 S.AddSpace(5);
405 S.AddSpace(5);
406
407 // -------------------------------------------------------------------
408 // ROW 4: Info
409 // -------------------------------------------------------------------
410
411 S.AddSpace(1);
412
413 S.StartHorizontalLay(wxEXPAND);
414 {
415 S.SetSizerProportion(1);
416 S.StartMultiColumn(6);
417 S.SetStretchyCol(1);
418 S.SetStretchyCol(3);
419 {
420 S.AddPrompt(XXO("Cursor:"));
421
422 mCursorText = S.Style(wxTE_READONLY)
423 .AddTextBox( {}, wxT(""), 10);
424
425 S.AddPrompt(XXO("Peak:"));
426
427 mPeakText = S.Style(wxTE_READONLY)
428 .AddTextBox( {}, wxT(""), 10);
429 S.AddSpace(5);
430
431 mGridOnOff = S.Id(GridOnOffID).AddCheckBox(XXO("&Grids"), mDrawGrid);
432 }
433 S.EndMultiColumn();
434 }
435 S.EndHorizontalLay();
436
437 S.AddSpace(1);
438 }
439 S.EndMultiColumn();
440
441 // -------------------------------------------------------------------
442 // ROW 5: Spacer
443 // -------------------------------------------------------------------
444
445 S.AddSpace(5);
446
447 S.SetBorder(2);
448 S.SetSizerProportion(0);
449 S.StartMultiColumn(9, wxALIGN_CENTER);
450 {
451 // ----------------------------------------------------------------
452 // ROW 6: Algorithm, Size, Export, Replot
453 // ----------------------------------------------------------------
454
455 S.AddSpace(5);
456
457 mAlgChoice = S.Id(FreqAlgChoiceID).Focus()
458 .MinSize( { wxDefaultCoord, wxDefaultCoord } )
459 .AddChoice(XXO("&Algorithm:"), algChoices, mAlg);
460
461 S.AddSpace(5);
462
464 .MinSize( { wxDefaultCoord, wxDefaultCoord } )
465 .AddChoice(XXO("&Size:"), sizeChoices, mSize);
466
467 S.AddSpace(5);
468
469 mExportButton = S.Id(FreqExportButtonID).AddButton(XXO("&Export..."));
470
471 S.AddSpace(5);
472
473 // ----------------------------------------------------------------
474 // ROW 7: Function, Axis, Grids, Close
475 // ----------------------------------------------------------------
476
477 S.AddSpace(5);
478
480 .MinSize( { wxDefaultCoord, wxDefaultCoord } )
481 .AddChoice(XXO("&Function:"), funcChoices, mFunc);
482 mFuncChoice->MoveAfterInTabOrder(mSizeChoice);
483
484 S.AddSpace(5);
485
487 .MinSize( { wxDefaultCoord, wxDefaultCoord } )
488 .AddChoice(XXO("&Axis:"), axisChoices, mAxis);
489 mAxisChoice->MoveAfterInTabOrder(mFuncChoice);
490
491 S.AddSpace(5);
492
493 mReplotButton = S.Id(ReplotButtonID).AddButton(XXO("&Replot..."));
494
495 S.AddSpace(5);
496
497 //mCloseButton = S.Id(wxID_CANCEL).AddButton(XO("&Close"));
498
499 //S.AddSpace(5);
500 }
501 S.EndMultiColumn();
502 S.AddStandardButtons( eHelpButton | eCloseButton );
503
504 // -------------------------------------------------------------------
505 // ROW 8: Spacer
506 // -------------------------------------------------------------------
507
508 S.AddSpace(5);
509
510 mProgress = safenew FreqGauge(S.GetParent(), wxID_ANY); //, wxST_SIZEGRIP);
511 S.Position(wxEXPAND)
512 .AddWindow(mProgress);
513
514 // Log-frequency axis works for spectrum plots only.
516 {
517 mAxis = 0;
518 mAxisChoice->Disable();
519 }
520 mLogAxis = mAxis != 0;
521
522 mCloseButton = static_cast<wxButton*>(FindWindowById( wxID_CANCEL ));
523 mCloseButton->SetDefault();
524
525 Layout();
526 Fit();
527 // Bug 1607:
528 Center();
529
530 SetMinSize(GetSize());
531
532#if defined(__WXGTK__)
533 // This should be rechecked with wx3.
534 //
535 // The scrollbar (focus some reason) doesn't allow tabbing past it
536 // because it can't receive focus. So, convince it otherwise.
537 //
538 // Unfortunately, this still doesn't let you adjust the scrollbar
539 // from the keyboard. Near as I can tell, wxWGTK is capturing the
540 // keyboard input, so the GTK widget doesn't see it, preventing
541 // the normal scroll events from being generated.
542 //
543 // I guess the only way round it would be to handle key actions
544 // ourselves, but we'll leave that for a future date.
545// gtk_widget_set_can_focus(mPanScroller->m_widget, true);
546#endif
547}
548
549void FrequencyPlotDialog::OnGetURL(wxCommandEvent & WXUNUSED(event))
550{
551 // Original help page is back on-line (March 2016), but the manual should be more reliable.
552 // http://www.eramp.com/WCAG_2_audio_contrast_tool_help.htm
553 HelpSystem::ShowHelp(this, L"Plot_Spectrum");
554}
555
557{
558 if (!show)
559 {
560 mFreqPlot->SetCursor(*mArrowCursor);
561 }
562
563 bool shown = IsShown();
564
565 if (show && !shown)
566 {
568 if(dBRange < 90.)
569 dBRange = 90.;
570 GetAudio();
571 // Don't send an event. We need the recalc right away.
572 // so that mAnalyst is valid when we paint.
573 //SendRecalcEvent();
574 Recalc();
575 }
576
577 bool res = wxDialogWrapper::Show(show);
578
579 return res;
580}
581
583{
584 mData.reset();
585 mDataLen = 0;
586
587 int selcount = 0;
588 bool warning = false;
589 for (auto track : TrackList::Get( *mProject ).Selected< const WaveTrack >()) {
590 auto &selectedRegion = ViewInfo::Get( *mProject ).selectedRegion;
591 if (selcount==0) {
592 mRate = track->GetRate();
593 auto start = track->TimeToLongSamples(selectedRegion.t0());
594 auto end = track->TimeToLongSamples(selectedRegion.t1());
595 auto dataLen = end - start;
596 // Permit approximately 46.60 minutes of selected samples at
597 // a sampling frequency of 48 kHz (11.65 minutes at 192 kHz).
598 auto maxDataLen = size_t(2) << 26;
599 if (dataLen > maxDataLen) {
600 warning = true;
601 mDataLen = maxDataLen;
602 }
603 else {
604 mDataLen = dataLen.as_size_t();
605 }
606 mData = Floats{ mDataLen };
607 // Don't allow throw for bad reads
608 track->GetFloats(mData.get(), start, mDataLen,
609 fillZero, false);
610 }
611 else {
612 if (track->GetRate() != mRate) {
614 XO(
615"To plot the spectrum, all selected tracks must be the same sample rate.") );
616 mData.reset();
617 mDataLen = 0;
618 return;
619 }
620 auto start = track->TimeToLongSamples(selectedRegion.t0());
621 Floats buffer2{ mDataLen };
622 // Again, stop exceptions
623 track->GetFloats(buffer2.get(), start, mDataLen,
624 fillZero, false);
625 for (size_t i = 0; i < mDataLen; i++)
626 mData[i] += buffer2[i];
627 }
628 selcount++;
629 }
630
631 if (selcount == 0)
632 return;
633
634 if (warning) {
635 auto msg = XO(
636"Too much audio was selected. Only the first %.1f seconds of audio will be analyzed.")
637 .Format(mDataLen / mRate);
638 AudacityMessageBox( msg );
639 }
640}
641
642void FrequencyPlotDialog::OnSize(wxSizeEvent & WXUNUSED(event))
643{
644 Layout();
645
646 DrawPlot();
647
648 Refresh(true);
649}
650
652{
653 Layout();
654
655 mBitmap.reset();
656
657 mPlotRect = mFreqPlot->GetClientRect();
658
659 mBitmap = std::make_unique<wxBitmap>(mPlotRect.width, mPlotRect.height,24);
660
661 dc.SelectObject(*mBitmap);
662
663 dc.SetBackground(wxBrush(wxColour(254, 254, 254)));// DONT-THEME Mask colour.
664 dc.Clear();
665
666 dc.SetPen(*wxBLACK_PEN);
667 dc.SetBrush(*wxWHITE_BRUSH);
668 dc.DrawRectangle(mPlotRect);
669
670 dc.SetFont(mFreqFont);
671}
672
674{
675 if (!mData || mDataLen < mWindowSize || mAnalyst->GetProcessedSize() == 0) {
676 wxMemoryDC memDC;
677
680
682 hRuler->ruler.SetRange(0, 1);
683
684 DrawBackground(memDC);
685
686 if (mDataLen < mWindowSize) {
687 wxString msg = _("Not enough data selected.");
688 wxSize sz = memDC.GetTextExtent(msg);
689 memDC.DrawText(msg,
690 (mPlotRect.GetWidth() - sz.GetWidth()) / 2,
691 (mPlotRect.GetHeight() - sz.GetHeight()) / 2);
692 }
693
694 memDC.SelectObject(wxNullBitmap);
695
696 mFreqPlot->Refresh();
697
698 Refresh();
699
700 return;
701 }
702
703 float yRange = mYMax - mYMin;
704 float yTotal = yRange * ((float) mZoomSlider->GetValue() / 100.0f);
705
706 int sTotal = yTotal * 100;
707 int sRange = yRange * 100;
708 int sPos = mPanScroller->GetThumbPosition() + ((mPanScroller->GetThumbSize() - sTotal) / 2);
709 mPanScroller->SetScrollbar(sPos, sTotal, sRange, sTotal);
710
711 float yMax = mYMax - ((float)sPos / 100);
712 float yMin = yMax - yTotal;
713
714 // Set up y axis ruler
715
717 vRuler->ruler.SetUnits(XO("dB"));
719 } else {
720 vRuler->ruler.SetUnits({});
722 }
723 int w1, w2, h;
724 vRuler->ruler.GetMaxSize(&w1, &h);
725 vRuler->ruler.SetRange(yMax, yMin); // Note inversion for vertical.
726 vRuler->ruler.GetMaxSize(&w2, &h);
727 if( w1 != w2 ) // Reduces flicker
728 {
729 vRuler->SetMinSize(wxSize(w2,h));
730 Layout();
731 }
732 vRuler->Refresh(false);
733
734 wxMemoryDC memDC;
735 DrawBackground(memDC);
736
737 // Get the plot dimensions
738 //
739 // Must be done after setting the vertical ruler above since the
740 // the width could change.
741 wxRect r = mPlotRect;
742
743 // Set up x axis ruler
744
745 int width = r.width - 2;
746
747 float xMin, xMax, xRatio, xStep;
748
750 xMin = mRate / mWindowSize;
751 xMax = mRate / 2;
752 xRatio = xMax / xMin;
753 if (mLogAxis)
754 {
755 xStep = pow(2.0f, (log(xRatio) / log(2.0f)) / width);
757 }
758 else
759 {
760 xStep = (xMax - xMin) / width;
762 }
763 hRuler->ruler.SetUnits(XO("Hz"));
764 } else {
765 xMin = 0;
766 xMax = mAnalyst->GetProcessedSize() / mRate;
767 xStep = (xMax - xMin) / width;
769 /* i18n-hint: short form of 'seconds'.*/
770 hRuler->ruler.SetUnits(XO("s"));
771 }
772 hRuler->ruler.SetRange(xMin, xMax-xStep);
773 hRuler->Refresh(false);
774
775 // Draw the plot
777 memDC.SetPen(wxPen(theTheme.Colour( clrHzPlot ), 1, wxPENSTYLE_SOLID));
778 else
779 memDC.SetPen(wxPen(theTheme.Colour( clrWavelengthPlot), 1, wxPENSTYLE_SOLID));
780
781 float xPos = xMin;
782
783 for (int i = 0; i < width; i++) {
784 float y;
785
786 if (mLogAxis)
787 y = mAnalyst->GetProcessedValue(xPos, xPos * xStep);
788 else
789 y = mAnalyst->GetProcessedValue(xPos, xPos + xStep);
790
791 float ynorm = (y - yMin) / yTotal;
792
793 int lineheight = (int)(ynorm * (r.height - 1));
794
795 if (lineheight > r.height - 2)
796 lineheight = r.height - 2;
797
798 if (ynorm > 0.0)
799 AColor::Line(memDC, r.x + 1 + i, r.y + r.height - 1 - lineheight,
800 r.x + 1 + i, r.y + r.height - 1);
801
802 if (mLogAxis)
803 xPos *= xStep;
804 else
805 xPos += xStep;
806 }
807
808 // Outline the graph
809 memDC.SetPen(*wxBLACK_PEN);
810 memDC.SetBrush(*wxTRANSPARENT_BRUSH);
811 memDC.DrawRectangle(r);
812
813 if(mDrawGrid)
814 {
815 hRuler->ruler.DrawGrid(memDC, r.height, true, true, 1, 1);
816 vRuler->ruler.DrawGrid(memDC, r.width, true, true, 1, 1);
817 }
818
819 memDC.SelectObject( wxNullBitmap );
820
821 mFreqPlot->Refresh();
822}
823
824
825void FrequencyPlotDialog::PlotMouseEvent(wxMouseEvent & event)
826{
827 if (event.Moving() && (event.m_x != mMouseX || event.m_y != mMouseY)) {
828 mMouseX = event.m_x;
829 mMouseY = event.m_y;
830
831 if (mPlotRect.Contains(mMouseX, mMouseY))
832 mFreqPlot->SetCursor(*mCrossCursor);
833 else
834 mFreqPlot->SetCursor(*mArrowCursor);
835
836 mFreqPlot->Refresh(false);
837 }
838}
839
840void FrequencyPlotDialog::OnPanScroller(wxScrollEvent & WXUNUSED(event))
841{
842 DrawPlot();
843}
844
845void FrequencyPlotDialog::OnZoomSlider(wxCommandEvent & WXUNUSED(event))
846{
847 DrawPlot();
848}
849
850void FrequencyPlotDialog::OnAlgChoice(wxCommandEvent & WXUNUSED(event))
851{
852 mAlg = SpectrumAnalyst::Algorithm(mAlgChoice->GetSelection());
853
854 // Log-frequency axis works for spectrum plots only.
856 mAxisChoice->Enable(true);
857 mLogAxis = mAxisChoice->GetSelection() ? true : false;
858 }
859 else {
860 mAxisChoice->Disable();
861 mLogAxis = false;
862 }
863
865}
866
867void FrequencyPlotDialog::OnSizeChoice(wxCommandEvent & WXUNUSED(event))
868{
869 long windowSize = 0;
870 mSizeChoice->GetStringSelection().ToLong(&windowSize);
871 mWindowSize = windowSize;
872
874}
875
876void FrequencyPlotDialog::OnFuncChoice(wxCommandEvent & WXUNUSED(event))
877{
879}
880
881void FrequencyPlotDialog::OnAxisChoice(wxCommandEvent & WXUNUSED(event))
882{
883 mLogAxis = mAxisChoice->GetSelection() ? true : false;
884 DrawPlot();
885}
886
887void FrequencyPlotDialog::PlotPaint(wxPaintEvent & event)
888{
889 wxPaintDC dc( (wxWindow *) event.GetEventObject() );
890
891 dc.DrawBitmap( *mBitmap, 0, 0, true );
892 // Fix for Bug 1226 "Plot Spectrum freezes... if insufficient samples selected"
893 if (!mData || mDataLen < mWindowSize)
894 return;
895
896 dc.SetFont(mFreqFont);
897
898 wxRect r = mPlotRect;
899
900 int width = r.width - 2;
901
902 float xMin, xMax, xRatio, xStep;
903
905 xMin = mRate / mWindowSize;
906 xMax = mRate / 2;
907 xRatio = xMax / xMin;
908 if (mLogAxis)
909 xStep = pow(2.0f, (log(xRatio) / log(2.0f)) / width);
910 else
911 xStep = (xMax - xMin) / width;
912 } else {
913 xMin = 0;
914 xMax = mAnalyst->GetProcessedSize() / mRate;
915 xStep = (xMax - xMin) / width;
916 }
917
918 float xPos = xMin;
919
920 // Find the peak nearest the cursor and plot it
921 if ( r.Contains(mMouseX, mMouseY) && (mMouseX!=0) && (mMouseX!=r.width-1) ) {
922 if (mLogAxis)
923 xPos = xMin * pow(xStep, mMouseX - (r.x + 1));
924 else
925 xPos = xMin + xStep * (mMouseX - (r.x + 1));
926
927 float bestValue = 0;
928 float bestpeak = mAnalyst->FindPeak(xPos, &bestValue);
929
930 int px;
931 if (mLogAxis)
932 px = (int)(log(bestpeak / xMin) / log(xStep));
933 else
934 px = (int)((bestpeak - xMin) * width / (xMax - xMin));
935
936 dc.SetPen(wxPen(wxColour(255, 32, 32), 1, wxPENSTYLE_SOLID));
937 AColor::Line(dc, r.x + 1 + px, r.y, r.x + 1 + px, r.y + r.height);
938
939 // print out info about the cursor location
940
941 float value;
942
943 if (mLogAxis) {
944 xPos = xMin * pow(xStep, mMouseX - (r.x + 1));
945 value = mAnalyst->GetProcessedValue(xPos, xPos * xStep);
946 } else {
947 xPos = xMin + xStep * (mMouseX - (r.x + 1));
948 value = mAnalyst->GetProcessedValue(xPos, xPos + xStep);
949 }
950
951 TranslatableString cursor;
953
955 auto xp = PitchName_Absolute(FreqToMIDInote(xPos));
956 auto pp = PitchName_Absolute(FreqToMIDInote(bestpeak));
957 /* i18n-hint: The %d's are replaced by numbers, the %s by musical notes, e.g. A#*/
958 cursor = XO("%d Hz (%s) = %d dB")
959 .Format( (int)(xPos + 0.5), xp, (int)(value + 0.5));
960 /* i18n-hint: The %d's are replaced by numbers, the %s by musical notes, e.g. A#*/
961 peak = XO("%d Hz (%s) = %.1f dB")
962 .Format( (int)(bestpeak + 0.5), pp, bestValue );
963 } else if (xPos > 0.0 && bestpeak > 0.0) {
964 auto xp = PitchName_Absolute(FreqToMIDInote(1.0 / xPos));
965 auto pp = PitchName_Absolute(FreqToMIDInote(1.0 / bestpeak));
966 /* i18n-hint: The %d's are replaced by numbers, the %s by musical notes, e.g. A#
967 * the %.4f are numbers, and 'sec' should be an abbreviation for seconds */
968 cursor = XO("%.4f sec (%d Hz) (%s) = %f")
969 .Format( xPos, (int)(1.0 / xPos + 0.5), xp, value );
970 /* i18n-hint: The %d's are replaced by numbers, the %s by musical notes, e.g. A#
971 * the %.4f are numbers, and 'sec' should be an abbreviation for seconds */
972 peak = XO("%.4f sec (%d Hz) (%s) = %.3f")
973 .Format( bestpeak, (int)(1.0 / bestpeak + 0.5), pp, bestValue );
974 }
975 mCursorText->SetValue( cursor.Translation() );
976 mPeakText->SetValue( peak.Translation() );
977 }
978 else {
979 mCursorText->SetValue(wxT(""));
980 mPeakText->SetValue(wxT(""));
981 }
982
983
984 // Outline the graph
985 dc.SetPen(*wxBLACK_PEN);
986 dc.SetBrush(*wxTRANSPARENT_BRUSH);
987 dc.DrawRectangle(r);
988}
989
990void FrequencyPlotDialog::OnCloseWindow(wxCloseEvent & WXUNUSED(event))
991{
992 Show(false);
993}
994
995void FrequencyPlotDialog::OnCloseButton(wxCommandEvent & WXUNUSED(event))
996{
997 gPrefs->Write(wxT("/FrequencyPlotDialog/DrawGrid"), mDrawGrid);
998 gPrefs->Write(wxT("/FrequencyPlotDialog/SizeChoice"), mSizeChoice->GetSelection());
999 gPrefs->Write(wxT("/FrequencyPlotDialog/AlgChoice"), mAlgChoice->GetSelection());
1000 gPrefs->Write(wxT("/FrequencyPlotDialog/FuncChoice"), mFuncChoice->GetSelection());
1001 gPrefs->Write(wxT("/FrequencyPlotDialog/AxisChoice"), mAxisChoice->GetSelection());
1002 gPrefs->Flush();
1003 mData.reset();
1004 Show(false);
1005}
1006
1008{
1009 wxCommandEvent e(EVT_FREQWINDOW_RECALC, wxID_ANY);
1010 GetEventHandler()->AddPendingEvent(e);
1011}
1012
1014{
1015 if (!mData || mDataLen < mWindowSize) {
1016 DrawPlot();
1017 return;
1018 }
1019
1021 SpectrumAnalyst::Algorithm(mAlgChoice->GetSelection());
1022 int windowFunc = mFuncChoice->GetSelection();
1023
1024 wxWindow *hadFocus = FindFocus();
1025 // In wxMac, the skipped window MUST be a top level window. I'd originally made it
1026 // just the mProgress window with the idea of preventing user interaction with the
1027 // controls while the plot was being recalculated. This doesn't appear to be necessary
1028 // so just use the top level window instead.
1029 {
1030 std::optional<wxWindowDisabler> blocker;
1031 if (IsShown())
1032 blocker.emplace(this);
1033 wxYieldIfNeeded();
1034
1035 mAnalyst->Calculate(alg, windowFunc, mWindowSize, mRate,
1036 mData.get(), mDataLen,
1037 &mYMin, &mYMax, mProgress);
1038 }
1039 if (hadFocus) {
1040 hadFocus->SetFocus();
1041 }
1042
1043 if (alg == SpectrumAnalyst::Spectrum) {
1044 if(mYMin < -dBRange)
1045 mYMin = -dBRange;
1046 if(mYMax <= -dBRange)
1047 mYMax = -dBRange + 10.; // it's all out of range, but show a scale.
1048 else
1049 mYMax += .5;
1050 }
1051
1052 // Prime the scrollbar
1053 mPanScroller->SetScrollbar(0, (mYMax - mYMin) * 100, (mYMax - mYMin) * 100, 1);
1054
1055 DrawPlot();
1056}
1057
1058void FrequencyPlotDialog::OnExport(wxCommandEvent & WXUNUSED(event))
1059{
1060 wxString fName = _("spectrum.txt");
1061
1062 fName = SelectFile(FileNames::Operation::Export,
1063 XO("Export Spectral Data As:"),
1064 wxEmptyString,
1065 fName,
1066 wxT("txt"),
1068 wxFD_SAVE | wxRESIZE_BORDER,
1069 this);
1070
1071 if (fName.empty())
1072 return;
1073
1074 wxFFileOutputStream ffStream{ fName };
1075 if (!ffStream.IsOk()) {
1076 AudacityMessageBox( XO("Couldn't write to file: %s").Format( fName ) );
1077 return;
1078 }
1079
1080 wxTextOutputStream ss(ffStream);
1081
1082 const int processedSize = mAnalyst->GetProcessedSize();
1083 const float *const processed = mAnalyst->GetProcessed();
1084 if (mAlgChoice->GetSelection() == 0) {
1085 ss
1086 << XO("Frequency (Hz)\tLevel (dB)") << '\n';
1087 for (int i = 1; i < processedSize; i++)
1088 ss
1089 << wxString::Format(wxT("%f\t%f\n"),
1090 i * mRate / mWindowSize, processed[i] );
1091 }
1092 else {
1093 ss
1094 << XO("Lag (seconds)\tFrequency (Hz)\tLevel") << '\n';
1095 for (int i = 1; i < processedSize; i++)
1096 ss
1097 << wxString::Format(wxT("%f\t%f\t%f\n"),
1098 i / mRate, mRate / i, processed[i] );
1099 }
1100}
1101
1102void FrequencyPlotDialog::OnReplot(wxCommandEvent & WXUNUSED(event))
1103{
1105 if(dBRange < 90.)
1106 dBRange = 90.;
1107 GetAudio();
1109}
1110
1111void FrequencyPlotDialog::OnGridOnOff(wxCommandEvent & WXUNUSED(event))
1112{
1113 mDrawGrid = mGridOnOff->IsChecked();
1114
1115 DrawPlot();
1116}
1117
1118void FrequencyPlotDialog::OnRecalc(wxCommandEvent & WXUNUSED(event))
1119{
1120 Recalc();
1121}
1122
1124{
1125 bool shown = IsShown();
1126 if (shown) {
1127 Show(false);
1128 }
1129
1130 auto zoomSlider = mZoomSlider->GetValue();
1131 auto drawGrid = mGridOnOff->GetValue();
1132 auto sizeChoice = mSizeChoice->GetStringSelection();
1133 auto algChoice = mAlgChoice->GetSelection();
1134 auto funcChoice = mFuncChoice->GetSelection();
1135 auto axisChoice = mAxisChoice->GetSelection();
1136
1137 SetSizer(nullptr);
1138 DestroyChildren();
1139
1140 Populate();
1141
1142 mZoomSlider->SetValue(zoomSlider);
1143
1144 mDrawGrid = drawGrid;
1145 mGridOnOff->SetValue(drawGrid);
1146
1147 long windowSize = 0;
1148 sizeChoice.ToLong(&windowSize);
1149 mWindowSize = windowSize;
1150 mSizeChoice->SetStringSelection(sizeChoice);
1151
1152 mAlg = static_cast<SpectrumAnalyst::Algorithm>(algChoice);
1153 mAlgChoice->SetSelection(algChoice);
1154
1155 mFunc = funcChoice;
1156 mFuncChoice->SetSelection(funcChoice);
1157
1158 mAxis = axisChoice;
1159 mAxisChoice->SetSelection(axisChoice);
1160
1161 if (shown) {
1162 Show(true);
1163 }
1164}
1165
1166BEGIN_EVENT_TABLE(FreqPlot, wxWindow)
1167 EVT_ERASE_BACKGROUND(FreqPlot::OnErase)
1168 EVT_PAINT(FreqPlot::OnPaint)
1169 EVT_MOUSE_EVENTS(FreqPlot::OnMouseEvent)
1171
1172FreqPlot::FreqPlot(wxWindow *parent, wxWindowID winid)
1173: wxWindow(parent, winid)
1174{
1175 freqWindow = (FrequencyPlotDialog *) parent;
1176}
1177
1179{
1180 return false;
1181}
1182
1183void FreqPlot::OnErase(wxEraseEvent & WXUNUSED(event))
1184{
1185 // Ignore it to prevent flashing
1186}
1187
1188void FreqPlot::OnPaint(wxPaintEvent & evt)
1189{
1190 freqWindow->PlotPaint(evt);
1191}
1192
1193void FreqPlot::OnMouseEvent(wxMouseEvent & event)
1194{
1195 freqWindow->PlotMouseEvent(event);
1196}
1197
1198// Remaining code hooks this add-on into the application
1201#include "ProjectWindows.h"
1202
1203namespace {
1204
1205AttachedWindows::RegisteredFactory sFrequencyWindowKey{
1206 []( AudacityProject &parent ) -> wxWeakRef< wxWindow > {
1207 auto &window = ProjectWindow::Get( parent );
1209 &window, -1, parent, FrequencyAnalysisTitle,
1210 wxPoint{ 150, 150 }
1211 );
1212 }
1213};
1214
1215// Define our extra menu item that invokes that factory
1216void OnPlotSpectrum(const CommandContext &context)
1217{
1218 auto &project = context.project;
1219 CommandManager::Get(project).RegisterLastAnalyzer(context); //Register Plot Spectrum as Last Analyzer
1220 auto freqWindow = &GetAttachedWindows(project)
1222
1223 if( VetoDialogHook::Call( freqWindow ) )
1224 return;
1225 freqWindow->Show(true);
1226 freqWindow->Raise();
1227 freqWindow->SetFocus();
1228}
1229
1230// Register that menu item
1231
1232using namespace MenuTable;
1233AttachedItem sAttachment{ wxT("Analyze/Analyzers/Windows"),
1234 Command( wxT("PlotSpectrum"), XXO("Plot Spectrum..."),
1237};
1238
1239}
1240
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
END_EVENT_TABLE()
const ReservedCommandFlag & AudioIONotBusyFlag()
const ReservedCommandFlag & TimeSelectedFlag()
const ReservedCommandFlag & WaveTracksSelectedFlag()
IntSetting DecibelScaleCutoff
Negation of this value is the lowest dB level that should be shown in dB scales.
Definition: Decibels.cpp:12
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
int NumWindowFuncs()
Definition: FFT.cpp:327
const TranslatableString WindowFuncName(int whichFunction)
Definition: FFT.cpp:332
#define FrequencyAnalysisTitle
Definition: FreqWindow.cpp:93
@ FreqAxisChoiceID
Definition: FreqWindow.cpp:106
@ FreqFuncChoiceID
Definition: FreqWindow.cpp:105
@ FreqPanScrollerID
Definition: FreqWindow.cpp:101
@ FreqExportButtonID
Definition: FreqWindow.cpp:102
@ ReplotButtonID
Definition: FreqWindow.cpp:107
@ FreqAlgChoiceID
Definition: FreqWindow.cpp:103
@ FreqSizeChoiceID
Definition: FreqWindow.cpp:104
@ FreqZoomSliderID
Definition: FreqWindow.cpp:100
@ GridOnOffID
Definition: FreqWindow.cpp:108
@ FirstID
Definition: FreqWindow.cpp:98
static const char * ZoomOut[]
Definition: FreqWindow.cpp:141
#define FREQ_WINDOW_HEIGHT
Definition: FreqWindow.cpp:114
DEFINE_EVENT_TYPE(EVT_FREQWINDOW_RECALC)
static const char * ZoomIn[]
Definition: FreqWindow.cpp:116
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define _(s)
Definition: Internat.h:73
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
Definition: LabelDialog.cpp:88
#define safenew
Definition: MemoryX.h:10
static const auto title
TranslatableString PitchName_Absolute(const double dMIDInote, const PitchNameChoice choice)
Definition: PitchName.cpp:153
double FreqToMIDInote(const double freq)
Definition: PitchName.cpp:28
FileConfig * gPrefs
Definition: Prefs.cpp:70
AUDACITY_DLL_API AttachedWindows & GetAttachedWindows(AudacityProject &project)
accessors for certain important windows associated with each project
@ fillZero
Definition: SampleFormat.h:60
FilePath SelectFile(FileNames::Operation op, const TranslatableString &message, const FilePath &default_path, const FilePath &default_filename, const FileExtension &default_extension, const FileTypes &fileTypes, int flags, wxWindow *parent)
Definition: SelectFile.cpp:17
@ eIsCreating
Definition: ShuttleGui.h:37
@ eCloseButton
Definition: ShuttleGui.h:604
@ eHelpButton
Definition: ShuttleGui.h:598
THEME_API Theme theTheme
Definition: Theme.cpp:82
#define S(N)
Definition: ToChars.cpp:64
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
std::vector< TranslatableString > TranslatableStrings
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:187
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
Subclass & Get(const RegisteredFactory &key)
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:309
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
AudacityProject & project
void RegisterLastAnalyzer(const CommandContext &context)
static CommandManager & Get(AudacityProject &project)
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
FILES_API const FileType AllFiles
Definition: FileNames.h:70
FILES_API const FileType TextFiles
Definition: FileNames.h:73
Abstract base class used in importing a file.
Works with FrequencyPlotDialog to display a spectrum plot of the waveform. This class actually does t...
Definition: FreqWindow.h:38
void OnErase(wxEraseEvent &event)
bool AcceptsFocus() const
void OnMouseEvent(wxMouseEvent &event)
void OnPaint(wxPaintEvent &event)
FrequencyPlotDialog * freqWindow
Definition: FreqWindow.h:51
Displays a spectrum plot of the waveform. Has options for selecting parameters of the plot.
Definition: FreqWindow.h:58
std::unique_ptr< wxCursor > mArrowCursor
Definition: FreqWindow.h:122
std::unique_ptr< wxBitmap > mBitmap
Definition: FreqWindow.h:149
void OnGetURL(wxCommandEvent &event)
Definition: FreqWindow.cpp:549
friend class FreqPlot
Definition: FreqWindow.h:158
void OnAlgChoice(wxCommandEvent &event)
Definition: FreqWindow.cpp:850
void OnPanScroller(wxScrollEvent &event)
Definition: FreqWindow.cpp:840
wxChoice * mAlgChoice
Definition: FreqWindow.h:129
void OnZoomSlider(wxCommandEvent &event)
Definition: FreqWindow.cpp:845
wxChoice * mSizeChoice
Definition: FreqWindow.h:130
wxButton * mExportButton
Definition: FreqWindow.h:126
void OnAxisChoice(wxCommandEvent &event)
Definition: FreqWindow.cpp:881
RulerPanel * hRuler
Definition: FreqWindow.h:114
std::unique_ptr< wxCursor > mCrossCursor
Definition: FreqWindow.h:123
wxButton * mReplotButton
Definition: FreqWindow.h:127
FreqPlot * mFreqPlot
Definition: FreqWindow.h:115
wxSlider * mZoomSlider
Definition: FreqWindow.h:134
void OnRecalc(wxCommandEvent &event)
SpectrumAnalyst::Algorithm mAlg
Definition: FreqWindow.h:101
virtual ~FrequencyPlotDialog()
Definition: FreqWindow.cpp:214
std::unique_ptr< SpectrumAnalyst > mAnalyst
Definition: FreqWindow.h:154
void OnFuncChoice(wxCommandEvent &event)
Definition: FreqWindow.cpp:876
void OnCloseButton(wxCommandEvent &event)
Definition: FreqWindow.cpp:995
void UpdatePrefs() override
wxChoice * mFuncChoice
Definition: FreqWindow.h:131
static const int fontSize
Definition: FreqWindow.h:108
void OnReplot(wxCommandEvent &event)
void OnCloseWindow(wxCloseEvent &event)
Definition: FreqWindow.cpp:990
void OnExport(wxCommandEvent &event)
wxTextCtrl * mCursorText
Definition: FreqWindow.h:135
RulerPanel * vRuler
Definition: FreqWindow.h:113
void PlotPaint(wxPaintEvent &event)
Definition: FreqWindow.cpp:887
FreqGauge * mProgress
Definition: FreqWindow.h:116
wxTextCtrl * mPeakText
Definition: FreqWindow.h:136
void OnSize(wxSizeEvent &event)
Definition: FreqWindow.cpp:642
wxButton * mCloseButton
Definition: FreqWindow.h:125
void OnGridOnOff(wxCommandEvent &event)
bool Show(bool show=true) override
Definition: FreqWindow.cpp:556
void OnSizeChoice(wxCommandEvent &event)
Definition: FreqWindow.cpp:867
void DrawBackground(wxMemoryDC &dc)
Definition: FreqWindow.cpp:651
wxChoice * mAxisChoice
Definition: FreqWindow.h:132
wxScrollBar * mPanScroller
Definition: FreqWindow.h:133
void PlotMouseEvent(wxMouseEvent &event)
Definition: FreqWindow.cpp:825
wxCheckBox * mGridOnOff
Definition: FreqWindow.h:128
static result_type Call(Arguments &&...arguments)
Null check of the installed function is done for you.
static void ShowHelp(wxWindow *parent, const FilePath &localFileName, const URLString &remoteURL, bool bModal=false, bool alwaysDefaultBrowser=false)
Definition: HelpSystem.cpp:233
static const LinearDBFormat & Instance()
static const LinearUpdater & Instance()
static const LogarithmicUpdater & Instance()
static ProjectWindow & Get(AudacityProject &project)
static const RealFormat & LinearInstance()
Definition: RealFormat.cpp:14
void DrawGrid(wxDC &dc, int length, bool minor=true, bool major=true, int xOffset=0, int yOffset=0) const
Definition: Ruler.cpp:531
void SetUpdater(const RulerUpdater *pUpdater)
Definition: Ruler.cpp:111
void SetFormat(const RulerFormat *pFormat)
Definition: Ruler.cpp:103
void GetMaxSize(wxCoord *width, wxCoord *height)
Definition: Ruler.cpp:613
void SetUnits(const TranslatableString &units)
Definition: Ruler.cpp:119
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
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:200
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:625
wxColour & Colour(int iIndex)
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:487
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:1457
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:219
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
A Track that contains audio waveform data.
Definition: WaveTrack.h:51
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
void SetTitle(const TranslatableString &title)
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:343
std::unique_ptr< CommandItem > Command(const CommandID &name, const TranslatableString &label_in, void(Handler::*pmf)(const CommandContext &), CommandFlag flags, const CommandManager::Options &options={}, CommandHandlerFinder finder=FinderScope::DefaultFinder())
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
void OnPlotSpectrum(const CommandContext &context)
AttachedWindows::RegisteredFactory sFrequencyWindowKey
Options & LabelEdges(bool l)
Definition: RulerPanel.h:41
Options & Log(bool l)
Definition: RulerPanel.h:35