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