2010-01-23 19:44:49 +00:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
|
|
|
|
FreqWindow.cpp
|
|
|
|
|
|
|
|
Dominic Mazzoni
|
|
|
|
|
|
|
|
*******************************************************************//**
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
\class FrequencyPlotDialog
|
2010-01-23 19:44:49 +00:00
|
|
|
\brief Displays a spectrum plot of the waveform. Has options for
|
|
|
|
selecting parameters of the plot.
|
|
|
|
|
|
|
|
Has a feature that finds peaks and reports their value as you move
|
|
|
|
the mouse around.
|
|
|
|
|
|
|
|
*//****************************************************************//**
|
|
|
|
|
|
|
|
\class FreqPlot
|
2019-12-06 10:39:07 +00:00
|
|
|
\brief Works with FrequencyPlotDialog to dsplay a spectrum plot of the waveform.
|
2010-01-23 19:44:49 +00:00
|
|
|
This class actually does the graph display.
|
|
|
|
|
|
|
|
Has a feature that finds peaks and reports their value as you move
|
|
|
|
the mouse around.
|
|
|
|
|
|
|
|
*//*******************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
Salvo Ventura - November 2006
|
|
|
|
Extended range check for additional FFT windows
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2021-05-09 15:16:56 +00:00
|
|
|
|
2015-06-18 14:24:36 +00:00
|
|
|
#include "FreqWindow.h"
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-07-09 17:39:20 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
2019-03-23 18:28:37 +00:00
|
|
|
#include <wx/setup.h> // for wxUSE_* macros
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
#include <wx/brush.h>
|
|
|
|
#include <wx/button.h>
|
2018-11-11 22:30:55 +00:00
|
|
|
#include <wx/checkbox.h>
|
2010-01-23 19:44:49 +00:00
|
|
|
#include <wx/choice.h>
|
2018-11-14 22:05:30 +00:00
|
|
|
#include <wx/dcclient.h>
|
2019-01-07 03:22:52 +00:00
|
|
|
#include <wx/dcmemory.h>
|
2014-10-06 08:10:50 +00:00
|
|
|
#include <wx/font.h>
|
2010-01-23 19:44:49 +00:00
|
|
|
#include <wx/image.h>
|
|
|
|
#include <wx/file.h>
|
|
|
|
#include <wx/intl.h>
|
2018-11-14 20:23:25 +00:00
|
|
|
#include <wx/scrolbar.h>
|
2010-01-23 19:44:49 +00:00
|
|
|
#include <wx/sizer.h>
|
2018-11-11 21:18:23 +00:00
|
|
|
#include <wx/slider.h>
|
2015-05-03 20:23:33 +00:00
|
|
|
#include <wx/statbmp.h>
|
2010-01-23 19:44:49 +00:00
|
|
|
#include <wx/stattext.h>
|
|
|
|
#include <wx/statusbr.h>
|
|
|
|
|
2019-05-20 18:27:11 +00:00
|
|
|
#include <wx/textctrl.h>
|
2010-01-23 19:44:49 +00:00
|
|
|
#include <wx/textfile.h>
|
|
|
|
|
2019-12-27 03:01:36 +00:00
|
|
|
#include <wx/wfstream.h>
|
|
|
|
#include <wx/txtstrm.h>
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
#include <math.h>
|
|
|
|
|
2015-06-18 14:24:36 +00:00
|
|
|
#include "ShuttleGui.h"
|
2010-01-23 19:44:49 +00:00
|
|
|
#include "AColor.h"
|
2020-02-01 04:44:00 +00:00
|
|
|
#include "CommonCommandFlags.h"
|
2010-01-23 19:44:49 +00:00
|
|
|
#include "FFT.h"
|
|
|
|
#include "PitchName.h"
|
2015-07-09 17:39:20 +00:00
|
|
|
#include "prefs/GUISettings.h"
|
2010-01-23 19:44:49 +00:00
|
|
|
#include "Prefs.h"
|
|
|
|
#include "Project.h"
|
2020-02-01 04:44:00 +00:00
|
|
|
#include "ProjectWindow.h"
|
2020-05-26 17:31:32 +00:00
|
|
|
#include "Theme.h"
|
2019-04-28 10:49:47 +00:00
|
|
|
#include "ViewInfo.h"
|
2010-01-23 19:44:49 +00:00
|
|
|
#include "AllThemeResources.h"
|
|
|
|
|
2017-08-02 16:41:29 +00:00
|
|
|
#include "FileNames.h"
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-07-03 04:20:21 +00:00
|
|
|
#include "WaveTrack.h"
|
2015-05-03 20:23:33 +00:00
|
|
|
|
2017-08-14 22:10:06 +00:00
|
|
|
#include "./widgets/HelpSystem.h"
|
2019-05-20 18:27:11 +00:00
|
|
|
#include "widgets/AudacityMessageBox.h"
|
2018-10-23 20:36:02 +00:00
|
|
|
#include "widgets/Ruler.h"
|
2017-08-14 22:10:06 +00:00
|
|
|
|
2018-04-10 12:43:50 +00:00
|
|
|
#if wxUSE_ACCESSIBILITY
|
|
|
|
#include "widgets/WindowAccessible.h"
|
|
|
|
#endif
|
|
|
|
|
2021-02-14 07:25:52 +00:00
|
|
|
#define FrequencyAnalysisTitle XO("Frequency Analysis")
|
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
DEFINE_EVENT_TYPE(EVT_FREQWINDOW_RECALC);
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
enum {
|
|
|
|
FirstID = 7000,
|
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
FreqZoomSliderID,
|
|
|
|
FreqPanScrollerID,
|
2010-01-23 19:44:49 +00:00
|
|
|
FreqExportButtonID,
|
|
|
|
FreqAlgChoiceID,
|
|
|
|
FreqSizeChoiceID,
|
|
|
|
FreqFuncChoiceID,
|
|
|
|
FreqAxisChoiceID,
|
|
|
|
ReplotButtonID,
|
|
|
|
GridOnOffID
|
|
|
|
};
|
|
|
|
|
|
|
|
// These specify the minimum plot window width
|
|
|
|
|
|
|
|
#define FREQ_WINDOW_WIDTH 480
|
|
|
|
#define FREQ_WINDOW_HEIGHT 330
|
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
static const char * ZoomIn[] = {
|
|
|
|
"16 16 6 1",
|
|
|
|
" c None",
|
|
|
|
"+ c #1C1C1C",
|
|
|
|
"@ c #AEAEAE",
|
|
|
|
"# c #F7F7F7",
|
|
|
|
"$ c #CFCECC",
|
|
|
|
"* c #1C1CA0",
|
|
|
|
" ++++ ",
|
|
|
|
" @+# @$+@ ",
|
|
|
|
" + @** +@ ",
|
|
|
|
" +#@ ** #+ ",
|
|
|
|
" +@****** +@",
|
|
|
|
" + ****** +@",
|
|
|
|
" +# ** #+@",
|
|
|
|
" + ** +@@",
|
|
|
|
" +++# #+@@ ",
|
|
|
|
" +++@++++@@ ",
|
|
|
|
" +++@@ @@@@ ",
|
|
|
|
" +++@@ ",
|
|
|
|
" +++@@ ",
|
|
|
|
"+++@@ ",
|
|
|
|
"@+@@ ",
|
|
|
|
" @@ "};
|
|
|
|
|
|
|
|
|
|
|
|
static const char * ZoomOut[] = {
|
|
|
|
"16 16 6 1",
|
|
|
|
" c None",
|
|
|
|
"+ c #1C1C1C",
|
|
|
|
"@ c #AEAEAE",
|
|
|
|
"# c #F7F7F7",
|
|
|
|
"$ c #CFCECC",
|
|
|
|
"* c #1C1CA0",
|
|
|
|
" ++++ ",
|
|
|
|
" @+# $+@ ",
|
|
|
|
" + @@ +@ ",
|
|
|
|
" +# @ #+ ",
|
|
|
|
" +@****** +@",
|
|
|
|
" + ****** +@",
|
|
|
|
" +# #+@",
|
|
|
|
" + +@@",
|
|
|
|
" +++# #+@@ ",
|
|
|
|
" +++@++++@@ ",
|
|
|
|
" +++@@ @@@@ ",
|
|
|
|
" +++@@ ",
|
|
|
|
" +++@@ ",
|
|
|
|
"+++@@ ",
|
|
|
|
"@+@@ ",
|
|
|
|
" @@ "};
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
// FrequencyPlotDialog
|
|
|
|
|
|
|
|
BEGIN_EVENT_TABLE(FrequencyPlotDialog, wxDialogWrapper)
|
|
|
|
EVT_CLOSE(FrequencyPlotDialog::OnCloseWindow)
|
|
|
|
EVT_SIZE(FrequencyPlotDialog::OnSize)
|
|
|
|
EVT_SLIDER(FreqZoomSliderID, FrequencyPlotDialog::OnZoomSlider)
|
|
|
|
EVT_COMMAND_SCROLL(FreqPanScrollerID, FrequencyPlotDialog::OnPanScroller)
|
|
|
|
EVT_CHOICE(FreqAlgChoiceID, FrequencyPlotDialog::OnAlgChoice)
|
|
|
|
EVT_CHOICE(FreqSizeChoiceID, FrequencyPlotDialog::OnSizeChoice)
|
|
|
|
EVT_CHOICE(FreqFuncChoiceID, FrequencyPlotDialog::OnFuncChoice)
|
|
|
|
EVT_CHOICE(FreqAxisChoiceID, FrequencyPlotDialog::OnAxisChoice)
|
|
|
|
EVT_BUTTON(FreqExportButtonID, FrequencyPlotDialog::OnExport)
|
|
|
|
EVT_BUTTON(ReplotButtonID, FrequencyPlotDialog::OnReplot)
|
|
|
|
EVT_BUTTON(wxID_CANCEL, FrequencyPlotDialog::OnCloseButton)
|
|
|
|
EVT_BUTTON(wxID_HELP, FrequencyPlotDialog::OnGetURL)
|
|
|
|
EVT_CHECKBOX(GridOnOffID, FrequencyPlotDialog::OnGridOnOff)
|
|
|
|
EVT_COMMAND(wxID_ANY, EVT_FREQWINDOW_RECALC, FrequencyPlotDialog::OnRecalc)
|
2010-01-23 19:44:49 +00:00
|
|
|
END_EVENT_TABLE()
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
FrequencyPlotDialog::FrequencyPlotDialog(wxWindow * parent, wxWindowID id,
|
2020-01-07 21:11:09 +00:00
|
|
|
AudacityProject &project,
|
2019-12-08 05:25:47 +00:00
|
|
|
const TranslatableString & title,
|
2015-05-03 20:23:33 +00:00
|
|
|
const wxPoint & pos)
|
2016-07-10 21:10:50 +00:00
|
|
|
: wxDialogWrapper(parent, id, title, pos, wxDefaultSize,
|
2015-05-03 20:23:33 +00:00
|
|
|
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX),
|
2016-04-08 08:32:11 +00:00
|
|
|
mAnalyst(std::make_unique<SpectrumAnalyst>())
|
2020-01-07 21:11:09 +00:00
|
|
|
, mProject{ &project }
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2019-12-08 05:25:47 +00:00
|
|
|
SetName();
|
2015-05-12 13:29:27 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
mMouseX = 0;
|
|
|
|
mMouseY = 0;
|
|
|
|
mRate = 0;
|
|
|
|
mDataLen = 0;
|
2015-05-03 20:23:33 +00:00
|
|
|
|
2021-02-14 07:25:52 +00:00
|
|
|
gPrefs->Read(wxT("/FrequencyPlotDialog/DrawGrid"), &mDrawGrid, true);
|
|
|
|
gPrefs->Read(wxT("/FrequencyPlotDialog/SizeChoice"), &mSize, 3);
|
|
|
|
|
|
|
|
int alg;
|
|
|
|
gPrefs->Read(wxT("/FrequencyPlotDialog/AlgChoice"), &alg, 0);
|
|
|
|
mAlg = static_cast<SpectrumAnalyst::Algorithm>(alg);
|
|
|
|
|
|
|
|
gPrefs->Read(wxT("/FrequencyPlotDialog/FuncChoice"), &mFunc, 3);
|
|
|
|
gPrefs->Read(wxT("/FrequencyPlotDialog/AxisChoice"), &mAxis, 1);
|
|
|
|
|
|
|
|
Populate();
|
|
|
|
}
|
|
|
|
|
|
|
|
FrequencyPlotDialog::~FrequencyPlotDialog()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void FrequencyPlotDialog::Populate()
|
|
|
|
{
|
|
|
|
SetTitle(FrequencyAnalysisTitle);
|
|
|
|
|
2019-12-18 03:52:42 +00:00
|
|
|
TranslatableStrings algChoices{
|
|
|
|
XO("Spectrum") ,
|
|
|
|
XO("Standard Autocorrelation") ,
|
|
|
|
XO("Cuberoot Autocorrelation") ,
|
|
|
|
XO("Enhanced Autocorrelation") ,
|
2019-02-12 21:30:22 +00:00
|
|
|
/* i18n-hint: This is a technical term, derived from the word
|
|
|
|
* "spectrum". Do not translate it unless you are sure you
|
|
|
|
* know the correct technical word in your language. */
|
2019-12-18 03:52:42 +00:00
|
|
|
XO("Cepstrum") ,
|
2019-02-12 21:30:22 +00:00
|
|
|
};
|
|
|
|
|
2019-12-18 03:52:42 +00:00
|
|
|
TranslatableStrings sizeChoices{
|
|
|
|
Verbatim( "128" ) ,
|
|
|
|
Verbatim( "256" ) ,
|
|
|
|
Verbatim( "512" ) ,
|
|
|
|
Verbatim( "1024" ) ,
|
|
|
|
Verbatim( "2048" ) ,
|
|
|
|
Verbatim( "4096" ) ,
|
|
|
|
Verbatim( "8192" ) ,
|
|
|
|
Verbatim( "16384" ) ,
|
|
|
|
Verbatim( "32768" ) ,
|
|
|
|
Verbatim( "65536" ) ,
|
2019-02-12 21:30:22 +00:00
|
|
|
};
|
2015-05-03 20:23:33 +00:00
|
|
|
|
2019-12-18 03:52:42 +00:00
|
|
|
TranslatableStrings funcChoices;
|
2015-05-03 20:23:33 +00:00
|
|
|
for (int i = 0, cnt = NumWindowFuncs(); i < cnt; i++)
|
|
|
|
{
|
2019-12-21 17:04:02 +00:00
|
|
|
funcChoices.push_back(
|
|
|
|
/* i18n-hint: This refers to a "window function",
|
|
|
|
* such as Hann or Rectangular, used in the
|
|
|
|
* Frequency analyze dialog box. */
|
2019-12-18 03:52:42 +00:00
|
|
|
XO("%s window").Format( WindowFuncName(i) ) );
|
2015-05-03 20:23:33 +00:00
|
|
|
}
|
|
|
|
|
2019-12-18 03:52:42 +00:00
|
|
|
TranslatableStrings axisChoices{
|
|
|
|
XO("Linear frequency") ,
|
|
|
|
XO("Log frequency") ,
|
2019-02-12 21:30:22 +00:00
|
|
|
};
|
2015-05-03 20:23:33 +00:00
|
|
|
|
2014-10-06 08:10:50 +00:00
|
|
|
mFreqFont = wxFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
|
2016-04-08 08:32:11 +00:00
|
|
|
mArrowCursor = std::make_unique<wxCursor>(wxCURSOR_ARROW);
|
|
|
|
mCrossCursor = std::make_unique<wxCursor>(wxCURSOR_CROSS);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
long size;
|
2019-12-18 03:52:42 +00:00
|
|
|
// reinterpret one of the verbatim strings above as a number
|
|
|
|
sizeChoices[mSize].MSGID().GET().ToLong(&size);
|
2015-05-03 20:23:33 +00:00
|
|
|
mWindowSize = size;
|
2014-10-18 14:19:38 +00:00
|
|
|
|
2015-07-09 17:39:20 +00:00
|
|
|
gPrefs->Read(ENV_DB_KEY, &dBRange, ENV_DB_RANGE);
|
2010-01-23 19:44:49 +00:00
|
|
|
if(dBRange < 90.)
|
|
|
|
dBRange = 90.;
|
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
ShuttleGui S(this, eIsCreating);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
S.SetBorder(0);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
S.AddSpace(5);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
S.SetSizerProportion(1);
|
|
|
|
S.StartMultiColumn(3, wxEXPAND);
|
|
|
|
{
|
|
|
|
S.SetStretchyCol(1);
|
|
|
|
S.SetStretchyRow(0);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
// -------------------------------------------------------------------
|
|
|
|
// ROW 1: Freq response panel and sliders for vertical scale
|
|
|
|
// -------------------------------------------------------------------
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
S.StartVerticalLay(2);
|
|
|
|
{
|
2017-11-01 13:16:56 +00:00
|
|
|
vRuler = safenew RulerPanel(
|
2017-10-20 16:45:27 +00:00
|
|
|
S.GetParent(), wxID_ANY, wxVERTICAL,
|
2017-11-01 13:16:56 +00:00
|
|
|
wxSize{ 100, 100 }, // Ruler can't handle small sizes
|
|
|
|
RulerPanel::Range{ 0.0, -dBRange },
|
|
|
|
Ruler::LinearDBFormat,
|
2019-12-29 01:11:08 +00:00
|
|
|
XO("dB"),
|
2017-11-01 13:16:56 +00:00
|
|
|
RulerPanel::Options{}
|
|
|
|
.LabelEdges(true)
|
|
|
|
.TickColour( theTheme.Colour( clrGraphLabels ) )
|
|
|
|
);
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
S.AddSpace(wxDefaultCoord, 1);
|
2017-10-31 23:44:00 +00:00
|
|
|
S.Prop(1)
|
|
|
|
.Position(wxALIGN_RIGHT | wxALIGN_TOP)
|
|
|
|
.AddWindow(vRuler);
|
2015-05-03 20:23:33 +00:00
|
|
|
S.AddSpace(wxDefaultCoord, 1);
|
|
|
|
}
|
|
|
|
S.EndVerticalLay();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-10-20 16:45:27 +00:00
|
|
|
mFreqPlot = safenew FreqPlot(S.GetParent(), wxID_ANY);
|
2018-01-31 20:31:22 +00:00
|
|
|
S.Prop(1)
|
2017-10-31 23:44:00 +00:00
|
|
|
.Position(wxEXPAND)
|
2018-01-31 20:31:22 +00:00
|
|
|
.MinSize( { wxDefaultCoord, FREQ_WINDOW_HEIGHT } )
|
2017-10-31 23:44:00 +00:00
|
|
|
.AddWindow(mFreqPlot);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
S.StartHorizontalLay(wxEXPAND, 0);
|
|
|
|
{
|
|
|
|
S.StartVerticalLay();
|
|
|
|
{
|
2017-10-20 16:45:27 +00:00
|
|
|
mPanScroller = safenew wxScrollBar(S.GetParent(), FreqPanScrollerID,
|
2015-05-03 20:23:33 +00:00
|
|
|
wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL);
|
2018-04-10 12:43:50 +00:00
|
|
|
#if wxUSE_ACCESSIBILITY
|
|
|
|
// so that name can be set on a standard control
|
|
|
|
mPanScroller->SetAccessible(safenew WindowAccessible(mPanScroller));
|
|
|
|
#endif
|
2015-05-03 20:23:33 +00:00
|
|
|
S.Prop(1);
|
2017-10-29 14:27:23 +00:00
|
|
|
S
|
|
|
|
.Name(XO("Scroll"))
|
2017-10-31 23:44:00 +00:00
|
|
|
.Position( wxALIGN_LEFT | wxTOP)
|
|
|
|
.AddWindow(mPanScroller);
|
2015-05-03 20:23:33 +00:00
|
|
|
}
|
|
|
|
S.EndVerticalLay();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
S.StartVerticalLay();
|
|
|
|
{
|
2017-10-20 16:45:27 +00:00
|
|
|
wxStaticBitmap *zi = safenew wxStaticBitmap(S.GetParent(), wxID_ANY, wxBitmap(ZoomIn));
|
2017-10-31 23:44:00 +00:00
|
|
|
S.Position(wxALIGN_CENTER)
|
|
|
|
.AddWindow(zi);
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
S.AddSpace(5);
|
|
|
|
|
2017-10-20 16:45:27 +00:00
|
|
|
mZoomSlider = safenew wxSliderWrapper(S.GetParent(), FreqZoomSliderID, 100, 1, 100,
|
2015-05-03 20:23:33 +00:00
|
|
|
wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL);
|
|
|
|
S.Prop(1);
|
2017-10-29 14:27:23 +00:00
|
|
|
S
|
|
|
|
.Name(XO("Zoom"))
|
2017-10-31 23:44:00 +00:00
|
|
|
.Position(wxALIGN_CENTER_HORIZONTAL)
|
|
|
|
.AddWindow(mZoomSlider);
|
2018-04-10 12:43:50 +00:00
|
|
|
#if wxUSE_ACCESSIBILITY
|
|
|
|
// so that name can be set on a standard control
|
|
|
|
mZoomSlider->SetAccessible(safenew WindowAccessible(mZoomSlider));
|
|
|
|
#endif
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
S.AddSpace(5);
|
|
|
|
|
2017-10-20 16:45:27 +00:00
|
|
|
wxStaticBitmap *zo = safenew wxStaticBitmap(S.GetParent(), wxID_ANY, wxBitmap(ZoomOut));
|
2017-10-31 23:44:00 +00:00
|
|
|
S.Position(wxALIGN_CENTER)
|
|
|
|
.AddWindow(zo);
|
2015-05-03 20:23:33 +00:00
|
|
|
}
|
|
|
|
S.EndVerticalLay();
|
|
|
|
|
|
|
|
S.AddSpace(5, wxDefaultCoord);
|
|
|
|
}
|
|
|
|
S.EndHorizontalLay();
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
// ROW 2: Frequency ruler
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
|
|
|
|
S.AddSpace(1);
|
|
|
|
|
|
|
|
S.StartHorizontalLay(wxEXPAND, 0);
|
|
|
|
{
|
2017-11-01 13:16:56 +00:00
|
|
|
hRuler = safenew RulerPanel(
|
2017-10-20 16:45:27 +00:00
|
|
|
S.GetParent(), wxID_ANY, wxHORIZONTAL,
|
2017-11-01 13:16:56 +00:00
|
|
|
wxSize{ 100, 100 }, // Ruler can't handle small sizes
|
|
|
|
RulerPanel::Range{ 10, 20000 },
|
|
|
|
Ruler::RealFormat,
|
2019-12-29 01:11:08 +00:00
|
|
|
XO("Hz"),
|
2017-11-01 13:16:56 +00:00
|
|
|
RulerPanel::Options{}
|
|
|
|
.Log(true)
|
|
|
|
.Flip(true)
|
|
|
|
.LabelEdges(true)
|
|
|
|
.TickColour( theTheme.Colour( clrGraphLabels ) )
|
|
|
|
);
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
S.AddSpace(1, wxDefaultCoord);
|
2017-10-31 23:44:00 +00:00
|
|
|
S.Prop(1)
|
|
|
|
.Position(wxALIGN_LEFT | wxALIGN_TOP)
|
|
|
|
.AddWindow(hRuler);
|
2015-05-03 20:23:33 +00:00
|
|
|
S.AddSpace(1, wxDefaultCoord);
|
|
|
|
}
|
|
|
|
S.EndHorizontalLay();
|
|
|
|
|
|
|
|
S.AddSpace(1);
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
// ROW 3: Spacer
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
S.AddSpace(5);
|
|
|
|
S.AddSpace(5);
|
|
|
|
S.AddSpace(5);
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
// ROW 4: Info
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
|
|
|
|
S.AddSpace(1);
|
|
|
|
|
|
|
|
S.StartHorizontalLay(wxEXPAND);
|
|
|
|
{
|
|
|
|
S.SetSizerProportion(1);
|
2017-08-14 22:22:19 +00:00
|
|
|
S.StartMultiColumn(6);
|
2015-05-03 20:23:33 +00:00
|
|
|
S.SetStretchyCol(1);
|
2017-08-14 22:22:19 +00:00
|
|
|
S.SetStretchyCol(3);
|
2015-05-03 20:23:33 +00:00
|
|
|
{
|
2020-05-11 15:28:14 +00:00
|
|
|
S.AddPrompt(XXO("Cursor:"));
|
2015-05-03 20:23:33 +00:00
|
|
|
|
2017-10-31 18:52:01 +00:00
|
|
|
mCursorText = S.Style(wxTE_READONLY)
|
|
|
|
.AddTextBox( {}, wxT(""), 10);
|
2015-05-03 20:23:33 +00:00
|
|
|
|
2020-05-11 15:28:14 +00:00
|
|
|
S.AddPrompt(XXO("Peak:"));
|
2015-05-03 20:23:33 +00:00
|
|
|
|
2017-10-31 18:52:01 +00:00
|
|
|
mPeakText = S.Style(wxTE_READONLY)
|
|
|
|
.AddTextBox( {}, wxT(""), 10);
|
2017-08-14 22:10:06 +00:00
|
|
|
S.AddSpace(5);
|
|
|
|
|
2020-05-11 15:28:14 +00:00
|
|
|
mGridOnOff = S.Id(GridOnOffID).AddCheckBox(XXO("&Grids"), mDrawGrid);
|
2015-05-03 20:23:33 +00:00
|
|
|
}
|
|
|
|
S.EndMultiColumn();
|
|
|
|
}
|
|
|
|
S.EndHorizontalLay();
|
|
|
|
|
|
|
|
S.AddSpace(1);
|
2013-04-22 23:19:34 +00:00
|
|
|
}
|
2015-05-03 20:23:33 +00:00
|
|
|
S.EndMultiColumn();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
// -------------------------------------------------------------------
|
|
|
|
// ROW 5: Spacer
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
|
|
|
|
S.AddSpace(5);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
S.SetBorder(2);
|
|
|
|
S.SetSizerProportion(0);
|
2017-08-14 22:10:06 +00:00
|
|
|
S.StartMultiColumn(9, wxALIGN_CENTER);
|
2015-05-03 20:23:33 +00:00
|
|
|
{
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
// ROW 6: Algorithm, Size, Export, Replot
|
|
|
|
// ----------------------------------------------------------------
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
S.AddSpace(5);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2018-01-28 03:36:34 +00:00
|
|
|
mAlgChoice = S.Id(FreqAlgChoiceID).Focus()
|
2018-01-31 20:31:22 +00:00
|
|
|
.MinSize( { wxDefaultCoord, wxDefaultCoord } )
|
2020-05-11 15:28:14 +00:00
|
|
|
.AddChoice(XXO("&Algorithm:"), algChoices, mAlg);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
S.AddSpace(5);
|
|
|
|
|
2019-02-12 21:30:22 +00:00
|
|
|
mSizeChoice = S.Id(FreqSizeChoiceID)
|
2018-01-31 20:31:22 +00:00
|
|
|
.MinSize( { wxDefaultCoord, wxDefaultCoord } )
|
2020-05-11 15:28:14 +00:00
|
|
|
.AddChoice(XXO("&Size:"), sizeChoices, mSize);
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
S.AddSpace(5);
|
|
|
|
|
2020-05-11 15:28:14 +00:00
|
|
|
mExportButton = S.Id(FreqExportButtonID).AddButton(XXO("&Export..."));
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
S.AddSpace(5);
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
// ROW 7: Function, Axix, Grids, Close
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
|
|
|
S.AddSpace(5);
|
|
|
|
|
2019-02-12 21:30:22 +00:00
|
|
|
mFuncChoice = S.Id(FreqFuncChoiceID)
|
2018-01-31 20:31:22 +00:00
|
|
|
.MinSize( { wxDefaultCoord, wxDefaultCoord } )
|
2020-05-11 15:28:14 +00:00
|
|
|
.AddChoice(XXO("&Function:"), funcChoices, mFunc);
|
2015-05-16 07:39:57 +00:00
|
|
|
mFuncChoice->MoveAfterInTabOrder(mSizeChoice);
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
S.AddSpace(5);
|
|
|
|
|
2019-02-12 21:30:22 +00:00
|
|
|
mAxisChoice = S.Id(FreqAxisChoiceID)
|
2018-01-31 20:31:22 +00:00
|
|
|
.MinSize( { wxDefaultCoord, wxDefaultCoord } )
|
2020-05-11 15:28:14 +00:00
|
|
|
.AddChoice(XXO("&Axis:"), axisChoices, mAxis);
|
2015-05-16 07:39:57 +00:00
|
|
|
mAxisChoice->MoveAfterInTabOrder(mFuncChoice);
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
S.AddSpace(5);
|
|
|
|
|
2020-05-11 15:28:14 +00:00
|
|
|
mReplotButton = S.Id(ReplotButtonID).AddButton(XXO("&Replot..."));
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
S.AddSpace(5);
|
|
|
|
|
2019-12-22 19:20:01 +00:00
|
|
|
//mCloseButton = S.Id(wxID_CANCEL).AddButton(XO("&Close"));
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-08-14 22:10:06 +00:00
|
|
|
//S.AddSpace(5);
|
2015-05-03 20:23:33 +00:00
|
|
|
}
|
|
|
|
S.EndMultiColumn();
|
2017-08-14 22:10:06 +00:00
|
|
|
S.AddStandardButtons( eHelpButton | eCloseButton );
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
// ROW 8: Spacer
|
|
|
|
// -------------------------------------------------------------------
|
2016-04-07 12:15:31 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
S.AddSpace(5);
|
|
|
|
|
2017-10-20 16:45:27 +00:00
|
|
|
mProgress = safenew FreqGauge(S.GetParent(), wxID_ANY); //, wxST_SIZEGRIP);
|
2017-10-31 23:44:00 +00:00
|
|
|
S.Position(wxEXPAND)
|
|
|
|
.AddWindow(mProgress);
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
// Log-frequency axis works for spectrum plots only.
|
|
|
|
if (mAlg != SpectrumAnalyst::Spectrum)
|
|
|
|
{
|
|
|
|
mAxis = 0;
|
|
|
|
mAxisChoice->Disable();
|
|
|
|
}
|
|
|
|
mLogAxis = mAxis != 0;
|
2016-04-07 12:15:31 +00:00
|
|
|
|
2018-09-21 13:17:28 +00:00
|
|
|
mCloseButton = static_cast<wxButton*>(FindWindowById( wxID_CANCEL ));
|
2017-08-14 22:22:19 +00:00
|
|
|
mCloseButton->SetDefault();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
Layout();
|
2015-05-03 20:23:33 +00:00
|
|
|
Fit();
|
2017-05-14 13:25:40 +00:00
|
|
|
// Bug 1607:
|
|
|
|
Center();
|
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
SetMinSize(GetSize());
|
|
|
|
|
|
|
|
#if defined(__WXGTK__)
|
|
|
|
// This should be rechecked with wx3.
|
|
|
|
//
|
|
|
|
// The scrollbar (focus some reason) doesn't allow tabbing past it
|
|
|
|
// because it can't receive focus. So, convince it otherwise.
|
|
|
|
//
|
|
|
|
// Unfortunately, this still doesn't let you adjust the scrollbar
|
|
|
|
// from the keyboard. Near as I can tell, wxWGTK is capturing the
|
|
|
|
// keyboard input, so the GTK widget doesn't see it, preventing
|
|
|
|
// the normal scroll events from being generated.
|
|
|
|
//
|
|
|
|
// I guess the only way round it would be to handle key actions
|
|
|
|
// ourselves, but we'll leave that for a future date.
|
2015-07-15 04:33:53 +00:00
|
|
|
// gtk_widget_set_can_focus(mPanScroller->m_widget, true);
|
2015-05-03 20:23:33 +00:00
|
|
|
#endif
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::OnGetURL(wxCommandEvent & WXUNUSED(event))
|
2017-08-14 22:10:06 +00:00
|
|
|
{
|
|
|
|
// Original help page is back on-line (March 2016), but the manual should be more reliable.
|
|
|
|
// http://www.eramp.com/WCAG_2_audio_contrast_tool_help.htm
|
2021-06-06 19:40:11 +00:00
|
|
|
HelpSystem::ShowHelp(this, L"Plot Spectrum");
|
2017-08-14 22:10:06 +00:00
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
bool FrequencyPlotDialog::Show(bool show)
|
2015-05-03 20:23:33 +00:00
|
|
|
{
|
|
|
|
if (!show)
|
|
|
|
{
|
|
|
|
mFreqPlot->SetCursor(*mArrowCursor);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool shown = IsShown();
|
|
|
|
|
|
|
|
if (show && !shown)
|
|
|
|
{
|
2015-07-09 17:39:20 +00:00
|
|
|
gPrefs->Read(ENV_DB_KEY, &dBRange, ENV_DB_RANGE);
|
2015-05-03 20:23:33 +00:00
|
|
|
if(dBRange < 90.)
|
|
|
|
dBRange = 90.;
|
|
|
|
GetAudio();
|
2015-10-06 20:28:22 +00:00
|
|
|
// Don't send an event. We need the recalc right away.
|
|
|
|
// so that mAnalyst is valid when we paint.
|
|
|
|
//SendRecalcEvent();
|
|
|
|
Recalc();
|
2015-05-03 20:23:33 +00:00
|
|
|
}
|
|
|
|
|
2016-07-10 21:10:50 +00:00
|
|
|
bool res = wxDialogWrapper::Show(show);
|
2015-10-06 20:28:22 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
return res;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::GetAudio()
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2016-04-14 16:25:43 +00:00
|
|
|
mData.reset();
|
2015-05-03 20:23:33 +00:00
|
|
|
mDataLen = 0;
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
int selcount = 0;
|
|
|
|
bool warning = false;
|
2020-01-07 21:11:09 +00:00
|
|
|
for (auto track : TrackList::Get( *mProject ).Selected< const WaveTrack >()) {
|
|
|
|
auto &selectedRegion = ViewInfo::Get( *mProject ).selectedRegion;
|
2018-09-18 16:03:37 +00:00
|
|
|
if (selcount==0) {
|
|
|
|
mRate = track->GetRate();
|
2019-04-28 10:49:47 +00:00
|
|
|
auto start = track->TimeToLongSamples(selectedRegion.t0());
|
|
|
|
auto end = track->TimeToLongSamples(selectedRegion.t1());
|
2018-09-18 16:03:37 +00:00
|
|
|
auto dataLen = end - start;
|
|
|
|
if (dataLen > 10485760) {
|
|
|
|
warning = true;
|
|
|
|
mDataLen = 10485760;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2018-09-18 16:03:37 +00:00
|
|
|
else
|
|
|
|
// dataLen is not more than 10 * 2 ^ 20
|
|
|
|
mDataLen = dataLen.as_size_t();
|
|
|
|
mData = Floats{ mDataLen };
|
|
|
|
// Don't allow throw for bad reads
|
2021-05-23 21:43:38 +00:00
|
|
|
track->GetFloats(mData.get(), start, mDataLen,
|
2018-09-18 16:03:37 +00:00
|
|
|
fillZero, false);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (track->GetRate() != mRate) {
|
2019-12-07 19:30:07 +00:00
|
|
|
AudacityMessageBox(
|
|
|
|
XO(
|
|
|
|
"To plot the spectrum, all selected tracks must be the same sample rate.") );
|
2018-09-18 16:03:37 +00:00
|
|
|
mData.reset();
|
|
|
|
mDataLen = 0;
|
|
|
|
return;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2019-04-28 10:49:47 +00:00
|
|
|
auto start = track->TimeToLongSamples(selectedRegion.t0());
|
2018-09-18 16:03:37 +00:00
|
|
|
Floats buffer2{ mDataLen };
|
|
|
|
// Again, stop exceptions
|
2021-05-23 21:43:38 +00:00
|
|
|
track->GetFloats(buffer2.get(), start, mDataLen,
|
2018-09-18 16:03:37 +00:00
|
|
|
fillZero, false);
|
|
|
|
for (size_t i = 0; i < mDataLen; i++)
|
|
|
|
mData[i] += buffer2[i];
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2018-09-18 16:03:37 +00:00
|
|
|
selcount++;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2014-06-03 20:30:19 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
if (selcount == 0)
|
|
|
|
return;
|
2011-07-23 00:18:07 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
if (warning) {
|
2019-12-07 19:30:07 +00:00
|
|
|
auto msg = XO(
|
|
|
|
"Too much audio was selected. Only the first %.1f seconds of audio will be analyzed.")
|
|
|
|
.Format(mDataLen / mRate);
|
|
|
|
AudacityMessageBox( msg );
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::OnSize(wxSizeEvent & WXUNUSED(event))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
Layout();
|
|
|
|
|
|
|
|
DrawPlot();
|
|
|
|
|
|
|
|
Refresh(true);
|
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::DrawBackground(wxMemoryDC & dc)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2015-05-03 20:23:33 +00:00
|
|
|
Layout();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2016-04-08 08:32:11 +00:00
|
|
|
mBitmap.reset();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
mPlotRect = mFreqPlot->GetClientRect();
|
2014-06-03 20:30:19 +00:00
|
|
|
|
2018-08-03 17:29:49 +00:00
|
|
|
mBitmap = std::make_unique<wxBitmap>(mPlotRect.width, mPlotRect.height,24);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
dc.SelectObject(*mBitmap);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
dc.SetBackground(wxBrush(wxColour(254, 254, 254)));// DONT-THEME Mask colour.
|
|
|
|
dc.Clear();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
dc.SetPen(*wxBLACK_PEN);
|
|
|
|
dc.SetBrush(*wxWHITE_BRUSH);
|
|
|
|
dc.DrawRectangle(mPlotRect);
|
|
|
|
|
|
|
|
dc.SetFont(mFreqFont);
|
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::DrawPlot()
|
2015-05-03 20:23:33 +00:00
|
|
|
{
|
2015-08-02 05:58:46 +00:00
|
|
|
if (!mData || mDataLen < mWindowSize || mAnalyst->GetProcessedSize() == 0) {
|
2015-05-03 20:23:33 +00:00
|
|
|
wxMemoryDC memDC;
|
|
|
|
|
|
|
|
vRuler->ruler.SetLog(false);
|
|
|
|
vRuler->ruler.SetRange(0.0, -dBRange);
|
|
|
|
|
|
|
|
hRuler->ruler.SetLog(false);
|
|
|
|
hRuler->ruler.SetRange(0, 1);
|
|
|
|
|
|
|
|
DrawBackground(memDC);
|
|
|
|
|
|
|
|
if (mDataLen < mWindowSize) {
|
|
|
|
wxString msg = _("Not enough data selected.");
|
|
|
|
wxSize sz = memDC.GetTextExtent(msg);
|
|
|
|
memDC.DrawText(msg,
|
|
|
|
(mPlotRect.GetWidth() - sz.GetWidth()) / 2,
|
|
|
|
(mPlotRect.GetHeight() - sz.GetHeight()) / 2);
|
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
memDC.SelectObject(wxNullBitmap);
|
|
|
|
|
|
|
|
mFreqPlot->Refresh();
|
|
|
|
|
|
|
|
Refresh();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
float yRange = mYMax - mYMin;
|
|
|
|
float yTotal = yRange * ((float) mZoomSlider->GetValue() / 100.0f);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
int sTotal = yTotal * 100;
|
|
|
|
int sRange = yRange * 100;
|
|
|
|
int sPos = mPanScroller->GetThumbPosition() + ((mPanScroller->GetThumbSize() - sTotal) / 2);
|
|
|
|
mPanScroller->SetScrollbar(sPos, sTotal, sRange, sTotal);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
float yMax = mYMax - ((float)sPos / 100);
|
|
|
|
float yMin = yMax - yTotal;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
// Set up y axis ruler
|
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
if (mAlg == SpectrumAnalyst::Spectrum) {
|
2019-12-29 01:11:08 +00:00
|
|
|
vRuler->ruler.SetUnits(XO("dB"));
|
2010-01-23 19:44:49 +00:00
|
|
|
vRuler->ruler.SetFormat(Ruler::LinearDBFormat);
|
|
|
|
} else {
|
2019-12-29 01:11:08 +00:00
|
|
|
vRuler->ruler.SetUnits({});
|
2010-01-23 19:44:49 +00:00
|
|
|
vRuler->ruler.SetFormat(Ruler::RealFormat);
|
|
|
|
}
|
|
|
|
int w1, w2, h;
|
|
|
|
vRuler->ruler.GetMaxSize(&w1, &h);
|
2015-05-03 20:23:33 +00:00
|
|
|
vRuler->ruler.SetRange(yMax, yMin); // Note inversion for vertical.
|
2010-01-23 19:44:49 +00:00
|
|
|
vRuler->ruler.GetMaxSize(&w2, &h);
|
|
|
|
if( w1 != w2 ) // Reduces flicker
|
|
|
|
{
|
2015-05-03 20:23:33 +00:00
|
|
|
vRuler->SetMinSize(wxSize(w2,h));
|
|
|
|
Layout();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
vRuler->Refresh(false);
|
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
wxMemoryDC memDC;
|
|
|
|
DrawBackground(memDC);
|
|
|
|
|
|
|
|
// Get the plot dimensions
|
|
|
|
//
|
|
|
|
// Must be done after setting the vertical ruler above since the
|
|
|
|
// the width could change.
|
|
|
|
wxRect r = mPlotRect;
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
// Set up x axis ruler
|
|
|
|
|
|
|
|
int width = r.width - 2;
|
|
|
|
|
2012-02-09 13:17:49 +00:00
|
|
|
float xMin, xMax, xRatio, xStep;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
if (mAlg == SpectrumAnalyst::Spectrum) {
|
2010-01-23 19:44:49 +00:00
|
|
|
xMin = mRate / mWindowSize;
|
|
|
|
xMax = mRate / 2;
|
|
|
|
xRatio = xMax / xMin;
|
|
|
|
if (mLogAxis)
|
|
|
|
{
|
|
|
|
xStep = pow(2.0f, (log(xRatio) / log(2.0f)) / width);
|
|
|
|
hRuler->ruler.SetLog(true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
xStep = (xMax - xMin) / width;
|
|
|
|
hRuler->ruler.SetLog(false);
|
|
|
|
}
|
2019-12-29 01:11:08 +00:00
|
|
|
hRuler->ruler.SetUnits(XO("Hz"));
|
2010-01-23 19:44:49 +00:00
|
|
|
} else {
|
|
|
|
xMin = 0;
|
2014-10-18 14:19:38 +00:00
|
|
|
xMax = mAnalyst->GetProcessedSize() / mRate;
|
2010-01-23 19:44:49 +00:00
|
|
|
xStep = (xMax - xMin) / width;
|
|
|
|
hRuler->ruler.SetLog(false);
|
2019-12-29 01:11:08 +00:00
|
|
|
/* i18n-hint: short form of 'seconds'.*/
|
|
|
|
hRuler->ruler.SetUnits(XO("s"));
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
hRuler->ruler.SetRange(xMin, xMax-xStep);
|
|
|
|
hRuler->Refresh(false);
|
|
|
|
|
|
|
|
// Draw the plot
|
2015-05-03 20:23:33 +00:00
|
|
|
if (mAlg == SpectrumAnalyst::Spectrum)
|
2018-07-23 17:21:15 +00:00
|
|
|
memDC.SetPen(wxPen(theTheme.Colour( clrHzPlot ), 1, wxPENSTYLE_SOLID));
|
2010-01-23 19:44:49 +00:00
|
|
|
else
|
2018-07-23 17:21:15 +00:00
|
|
|
memDC.SetPen(wxPen(theTheme.Colour( clrWavelengthPlot), 1, wxPENSTYLE_SOLID));
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2012-02-09 13:17:49 +00:00
|
|
|
float xPos = xMin;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
for (int i = 0; i < width; i++) {
|
2010-01-23 19:44:49 +00:00
|
|
|
float y;
|
|
|
|
|
|
|
|
if (mLogAxis)
|
2014-10-18 14:19:38 +00:00
|
|
|
y = mAnalyst->GetProcessedValue(xPos, xPos * xStep);
|
2010-01-23 19:44:49 +00:00
|
|
|
else
|
2014-10-18 14:19:38 +00:00
|
|
|
y = mAnalyst->GetProcessedValue(xPos, xPos + xStep);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
float ynorm = (y - yMin) / yTotal;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2016-09-20 12:26:42 +00:00
|
|
|
int lineheight = (int)(ynorm * (r.height - 1));
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
if (lineheight > r.height - 2)
|
|
|
|
lineheight = r.height - 2;
|
|
|
|
|
|
|
|
if (ynorm > 0.0)
|
|
|
|
AColor::Line(memDC, r.x + 1 + i, r.y + r.height - 1 - lineheight,
|
|
|
|
r.x + 1 + i, r.y + r.height - 1);
|
|
|
|
|
|
|
|
if (mLogAxis)
|
|
|
|
xPos *= xStep;
|
|
|
|
else
|
|
|
|
xPos += xStep;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Outline the graph
|
|
|
|
memDC.SetPen(*wxBLACK_PEN);
|
|
|
|
memDC.SetBrush(*wxTRANSPARENT_BRUSH);
|
|
|
|
memDC.DrawRectangle(r);
|
|
|
|
|
|
|
|
if(mDrawGrid)
|
|
|
|
{
|
|
|
|
hRuler->ruler.DrawGrid(memDC, r.height, true, true, 1, 1);
|
|
|
|
vRuler->ruler.DrawGrid(memDC, r.width, true, true, 1, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
memDC.SelectObject( wxNullBitmap );
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
mFreqPlot->Refresh();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::PlotMouseEvent(wxMouseEvent & event)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
if (event.Moving() && (event.m_x != mMouseX || event.m_y != mMouseY)) {
|
|
|
|
mMouseX = event.m_x;
|
|
|
|
mMouseY = event.m_y;
|
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
if (mPlotRect.Contains(mMouseX, mMouseY))
|
2010-01-23 19:44:49 +00:00
|
|
|
mFreqPlot->SetCursor(*mCrossCursor);
|
|
|
|
else
|
|
|
|
mFreqPlot->SetCursor(*mArrowCursor);
|
|
|
|
|
|
|
|
mFreqPlot->Refresh(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::OnPanScroller(wxScrollEvent & WXUNUSED(event))
|
2015-05-03 20:23:33 +00:00
|
|
|
{
|
|
|
|
DrawPlot();
|
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::OnZoomSlider(wxCommandEvent & WXUNUSED(event))
|
2015-05-03 20:23:33 +00:00
|
|
|
{
|
|
|
|
DrawPlot();
|
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::OnAlgChoice(wxCommandEvent & WXUNUSED(event))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2015-05-03 20:23:33 +00:00
|
|
|
mAlg = SpectrumAnalyst::Algorithm(mAlgChoice->GetSelection());
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
// Log-frequency axis works for spectrum plots only.
|
2015-05-03 20:23:33 +00:00
|
|
|
if (mAlg == SpectrumAnalyst::Spectrum) {
|
2010-01-23 19:44:49 +00:00
|
|
|
mAxisChoice->Enable(true);
|
2015-05-03 20:23:33 +00:00
|
|
|
mLogAxis = mAxisChoice->GetSelection() ? true : false;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
mAxisChoice->Disable();
|
|
|
|
mLogAxis = false;
|
|
|
|
}
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
SendRecalcEvent();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::OnSizeChoice(wxCommandEvent & WXUNUSED(event))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2015-05-03 20:23:33 +00:00
|
|
|
long windowSize = 0;
|
|
|
|
mSizeChoice->GetStringSelection().ToLong(&windowSize);
|
|
|
|
mWindowSize = windowSize;
|
|
|
|
|
|
|
|
SendRecalcEvent();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::OnFuncChoice(wxCommandEvent & WXUNUSED(event))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2015-05-03 20:23:33 +00:00
|
|
|
SendRecalcEvent();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::OnAxisChoice(wxCommandEvent & WXUNUSED(event))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2015-05-03 20:23:33 +00:00
|
|
|
mLogAxis = mAxisChoice->GetSelection() ? true : false;
|
2010-01-23 19:44:49 +00:00
|
|
|
DrawPlot();
|
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::PlotPaint(wxPaintEvent & event)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2015-05-03 20:23:33 +00:00
|
|
|
wxPaintDC dc( (wxWindow *) event.GetEventObject() );
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
dc.DrawBitmap( *mBitmap, 0, 0, true );
|
2015-10-06 20:28:22 +00:00
|
|
|
// Fix for Bug 1226 "Plot Spectrum freezes... if insufficient samples selected"
|
|
|
|
if (!mData || mDataLen < mWindowSize)
|
2015-05-03 20:23:33 +00:00
|
|
|
return;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
dc.SetFont(mFreqFont);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
wxRect r = mPlotRect;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
int width = r.width - 2;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
float xMin, xMax, xRatio, xStep;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
if (mAlg == SpectrumAnalyst::Spectrum) {
|
|
|
|
xMin = mRate / mWindowSize;
|
|
|
|
xMax = mRate / 2;
|
|
|
|
xRatio = xMax / xMin;
|
|
|
|
if (mLogAxis)
|
|
|
|
xStep = pow(2.0f, (log(xRatio) / log(2.0f)) / width);
|
|
|
|
else
|
|
|
|
xStep = (xMax - xMin) / width;
|
|
|
|
} else {
|
|
|
|
xMin = 0;
|
|
|
|
xMax = mAnalyst->GetProcessedSize() / mRate;
|
|
|
|
xStep = (xMax - xMin) / width;
|
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
float xPos = xMin;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
// Find the peak nearest the cursor and plot it
|
|
|
|
if ( r.Contains(mMouseX, mMouseY) & (mMouseX!=0) & (mMouseX!=r.width-1) ) {
|
|
|
|
if (mLogAxis)
|
|
|
|
xPos = xMin * pow(xStep, mMouseX - (r.x + 1));
|
|
|
|
else
|
|
|
|
xPos = xMin + xStep * (mMouseX - (r.x + 1));
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
float bestValue = 0;
|
|
|
|
float bestpeak = mAnalyst->FindPeak(xPos, &bestValue);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
int px;
|
|
|
|
if (mLogAxis)
|
2016-09-20 12:26:42 +00:00
|
|
|
px = (int)(log(bestpeak / xMin) / log(xStep));
|
2015-05-03 20:23:33 +00:00
|
|
|
else
|
2016-09-20 12:26:42 +00:00
|
|
|
px = (int)((bestpeak - xMin) * width / (xMax - xMin));
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2018-07-23 17:21:15 +00:00
|
|
|
dc.SetPen(wxPen(wxColour(160,160,160), 1, wxPENSTYLE_SOLID));
|
2015-05-03 20:23:33 +00:00
|
|
|
AColor::Line(dc, r.x + 1 + px, r.y, r.x + 1 + px, r.y + r.height);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
// print out info about the cursor location
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
float value;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
if (mLogAxis) {
|
|
|
|
xPos = xMin * pow(xStep, mMouseX - (r.x + 1));
|
|
|
|
value = mAnalyst->GetProcessedValue(xPos, xPos * xStep);
|
|
|
|
} else {
|
|
|
|
xPos = xMin + xStep * (mMouseX - (r.x + 1));
|
|
|
|
value = mAnalyst->GetProcessedValue(xPos, xPos + xStep);
|
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2019-12-21 16:51:03 +00:00
|
|
|
TranslatableString cursor;
|
|
|
|
TranslatableString peak;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
if (mAlg == SpectrumAnalyst::Spectrum) {
|
2019-12-21 16:51:03 +00:00
|
|
|
auto xp = PitchName_Absolute(FreqToMIDInote(xPos));
|
|
|
|
auto pp = PitchName_Absolute(FreqToMIDInote(bestpeak));
|
2012-03-20 21:59:30 +00:00
|
|
|
/* i18n-hint: The %d's are replaced by numbers, the %s by musical notes, e.g. A#*/
|
2019-12-21 16:51:03 +00:00
|
|
|
cursor = XO("%d Hz (%s) = %d dB")
|
|
|
|
.Format( (int)(xPos + 0.5), xp, (int)(value + 0.5));
|
|
|
|
/* i18n-hint: The %d's are replaced by numbers, the %s by musical notes, e.g. A#*/
|
|
|
|
peak = XO("%d Hz (%s) = %.1f dB")
|
|
|
|
.Format( (int)(bestpeak + 0.5), pp, bestValue );
|
2010-01-23 19:44:49 +00:00
|
|
|
} else if (xPos > 0.0 && bestpeak > 0.0) {
|
2019-12-21 16:51:03 +00:00
|
|
|
auto xp = PitchName_Absolute(FreqToMIDInote(1.0 / xPos));
|
|
|
|
auto pp = PitchName_Absolute(FreqToMIDInote(1.0 / bestpeak));
|
|
|
|
/* i18n-hint: The %d's are replaced by numbers, the %s by musical notes, e.g. A#
|
|
|
|
* the %.4f are numbers, and 'sec' should be an abbreviation for seconds */
|
|
|
|
cursor = XO("%.4f sec (%d Hz) (%s) = %f")
|
|
|
|
.Format( xPos, (int)(1.0 / xPos + 0.5), xp, value );
|
2012-03-20 21:59:30 +00:00
|
|
|
/* i18n-hint: The %d's are replaced by numbers, the %s by musical notes, e.g. A#
|
2012-03-20 15:36:02 +00:00
|
|
|
* the %.4f are numbers, and 'sec' should be an abbreviation for seconds */
|
2019-12-21 16:51:03 +00:00
|
|
|
peak = XO("%.4f sec (%d Hz) (%s) = %.3f")
|
|
|
|
.Format( bestpeak, (int)(1.0 / bestpeak + 0.5), pp, bestValue );
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2019-12-21 16:51:03 +00:00
|
|
|
mCursorText->SetValue( cursor.Translation() );
|
|
|
|
mPeakText->SetValue( peak.Translation() );
|
2015-05-03 20:23:33 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
mCursorText->SetValue(wxT(""));
|
|
|
|
mPeakText->SetValue(wxT(""));
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Outline the graph
|
|
|
|
dc.SetPen(*wxBLACK_PEN);
|
|
|
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
|
|
|
dc.DrawRectangle(r);
|
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::OnCloseWindow(wxCloseEvent & WXUNUSED(event))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2015-05-03 20:23:33 +00:00
|
|
|
Show(false);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::OnCloseButton(wxCommandEvent & WXUNUSED(event))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2019-12-06 10:39:07 +00:00
|
|
|
gPrefs->Write(wxT("/FrequencyPlotDialog/DrawGrid"), mDrawGrid);
|
|
|
|
gPrefs->Write(wxT("/FrequencyPlotDialog/SizeChoice"), mSizeChoice->GetSelection());
|
|
|
|
gPrefs->Write(wxT("/FrequencyPlotDialog/AlgChoice"), mAlgChoice->GetSelection());
|
|
|
|
gPrefs->Write(wxT("/FrequencyPlotDialog/FuncChoice"), mFuncChoice->GetSelection());
|
|
|
|
gPrefs->Write(wxT("/FrequencyPlotDialog/AxisChoice"), mAxisChoice->GetSelection());
|
2012-08-02 06:03:19 +00:00
|
|
|
gPrefs->Flush();
|
2015-05-03 20:23:33 +00:00
|
|
|
Show(false);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::SendRecalcEvent()
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2015-05-03 20:23:33 +00:00
|
|
|
wxCommandEvent e(EVT_FREQWINDOW_RECALC, wxID_ANY);
|
|
|
|
GetEventHandler()->AddPendingEvent(e);
|
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::Recalc()
|
2015-05-03 20:23:33 +00:00
|
|
|
{
|
|
|
|
if (!mData || mDataLen < mWindowSize) {
|
|
|
|
DrawPlot();
|
|
|
|
return;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
SpectrumAnalyst::Algorithm alg =
|
|
|
|
SpectrumAnalyst::Algorithm(mAlgChoice->GetSelection());
|
|
|
|
int windowFunc = mFuncChoice->GetSelection();
|
|
|
|
|
|
|
|
wxWindow *hadFocus = FindFocus();
|
2015-09-02 05:52:20 +00:00
|
|
|
// In wxMac, the skipped window MUST be a top level window. I'd originally made it
|
|
|
|
// just the mProgress window with the idea of preventing user interaction with the
|
|
|
|
// controls while the plot was being recalculated. This doesn't appear to be necessary
|
2020-04-11 07:08:33 +00:00
|
|
|
// so just use the top level window instead.
|
2016-02-01 01:39:24 +00:00
|
|
|
{
|
2020-01-19 14:51:50 +00:00
|
|
|
Optional<wxWindowDisabler> blocker;
|
2016-06-18 15:55:43 +00:00
|
|
|
if (IsShown())
|
2020-01-19 14:51:50 +00:00
|
|
|
blocker.emplace(this);
|
2016-02-01 01:39:24 +00:00
|
|
|
wxYieldIfNeeded();
|
2015-05-03 20:23:33 +00:00
|
|
|
|
2016-02-01 01:39:24 +00:00
|
|
|
mAnalyst->Calculate(alg, windowFunc, mWindowSize, mRate,
|
2016-04-14 16:25:43 +00:00
|
|
|
mData.get(), mDataLen,
|
2016-02-01 01:39:24 +00:00
|
|
|
&mYMin, &mYMax, mProgress);
|
|
|
|
}
|
2015-05-03 20:23:33 +00:00
|
|
|
if (hadFocus) {
|
|
|
|
hadFocus->SetFocus();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2015-05-03 20:23:33 +00:00
|
|
|
|
|
|
|
if (alg == SpectrumAnalyst::Spectrum) {
|
|
|
|
if(mYMin < -dBRange)
|
|
|
|
mYMin = -dBRange;
|
|
|
|
if(mYMax <= -dBRange)
|
|
|
|
mYMax = -dBRange + 10.; // it's all out of range, but show a scale.
|
|
|
|
else
|
|
|
|
mYMax += .5;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prime the scrollbar
|
|
|
|
mPanScroller->SetScrollbar(0, (mYMax - mYMin) * 100, (mYMax - mYMin) * 100, 1);
|
|
|
|
|
|
|
|
DrawPlot();
|
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::OnExport(wxCommandEvent & WXUNUSED(event))
|
2015-05-03 20:23:33 +00:00
|
|
|
{
|
|
|
|
wxString fName = _("spectrum.txt");
|
|
|
|
|
2017-08-02 16:41:29 +00:00
|
|
|
fName = FileNames::SelectFile(FileNames::Operation::Export,
|
2019-12-27 03:48:00 +00:00
|
|
|
XO("Export Spectral Data As:"),
|
|
|
|
wxEmptyString,
|
|
|
|
fName,
|
|
|
|
wxT("txt"),
|
|
|
|
{ FileNames::TextFiles, FileNames::AllFiles },
|
|
|
|
wxFD_SAVE | wxRESIZE_BORDER,
|
|
|
|
this);
|
2015-05-03 20:23:33 +00:00
|
|
|
|
2019-03-14 17:04:37 +00:00
|
|
|
if (fName.empty())
|
2015-05-03 20:23:33 +00:00
|
|
|
return;
|
|
|
|
|
2019-12-27 03:01:36 +00:00
|
|
|
wxFFileOutputStream ffStream{ fName };
|
|
|
|
if (!ffStream.IsOk()) {
|
2019-12-07 19:30:07 +00:00
|
|
|
AudacityMessageBox( XO("Couldn't write to file: %s").Format( fName ) );
|
2015-05-03 20:23:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-12-27 03:01:36 +00:00
|
|
|
wxTextOutputStream ss(ffStream);
|
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
const int processedSize = mAnalyst->GetProcessedSize();
|
|
|
|
const float *const processed = mAnalyst->GetProcessed();
|
|
|
|
if (mAlgChoice->GetSelection() == 0) {
|
2019-12-27 03:01:36 +00:00
|
|
|
ss
|
|
|
|
<< XO("Frequency (Hz)\tLevel (dB)") << '\n';
|
2015-05-03 20:23:33 +00:00
|
|
|
for (int i = 1; i < processedSize; i++)
|
2019-12-27 03:01:36 +00:00
|
|
|
ss
|
|
|
|
<< wxString::Format(wxT("%f\t%f\n"),
|
|
|
|
i * mRate / mWindowSize, processed[i] );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ss
|
|
|
|
<< XO("Lag (seconds)\tFrequency (Hz)\tLevel") << '\n';
|
2015-05-03 20:23:33 +00:00
|
|
|
for (int i = 1; i < processedSize; i++)
|
2019-12-27 03:01:36 +00:00
|
|
|
ss
|
|
|
|
<< wxString::Format(wxT("%f\t%f\t%f\n"),
|
|
|
|
i / mRate, mRate / i, processed[i] );
|
2015-05-03 20:23:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::OnReplot(wxCommandEvent & WXUNUSED(event))
|
2015-05-03 20:23:33 +00:00
|
|
|
{
|
2015-07-09 17:39:20 +00:00
|
|
|
gPrefs->Read(ENV_DB_KEY, &dBRange, ENV_DB_RANGE);
|
2015-05-03 20:23:33 +00:00
|
|
|
if(dBRange < 90.)
|
|
|
|
dBRange = 90.;
|
|
|
|
GetAudio();
|
|
|
|
SendRecalcEvent();
|
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::OnGridOnOff(wxCommandEvent & WXUNUSED(event))
|
2015-05-03 20:23:33 +00:00
|
|
|
{
|
|
|
|
mDrawGrid = mGridOnOff->IsChecked();
|
|
|
|
|
|
|
|
DrawPlot();
|
|
|
|
}
|
|
|
|
|
2019-12-06 10:39:07 +00:00
|
|
|
void FrequencyPlotDialog::OnRecalc(wxCommandEvent & WXUNUSED(event))
|
2015-05-03 20:23:33 +00:00
|
|
|
{
|
2010-01-23 19:44:49 +00:00
|
|
|
Recalc();
|
2015-05-03 20:23:33 +00:00
|
|
|
}
|
|
|
|
|
2021-02-14 07:25:52 +00:00
|
|
|
void FrequencyPlotDialog::UpdatePrefs()
|
|
|
|
{
|
|
|
|
bool shown = IsShown();
|
|
|
|
if (shown) {
|
|
|
|
Show(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto zoomSlider = mZoomSlider->GetValue();
|
|
|
|
auto drawGrid = mGridOnOff->GetValue();
|
|
|
|
auto sizeChoice = mSizeChoice->GetStringSelection();
|
|
|
|
auto algChoice = mAlgChoice->GetSelection();
|
|
|
|
auto funcChoice = mFuncChoice->GetSelection();
|
|
|
|
auto axisChoice = mAxisChoice->GetSelection();
|
|
|
|
|
|
|
|
SetSizer(nullptr);
|
|
|
|
DestroyChildren();
|
|
|
|
|
|
|
|
Populate();
|
|
|
|
|
|
|
|
mZoomSlider->SetValue(zoomSlider);
|
|
|
|
|
|
|
|
mDrawGrid = drawGrid;
|
|
|
|
mGridOnOff->SetValue(drawGrid);
|
|
|
|
|
|
|
|
long windowSize = 0;
|
|
|
|
sizeChoice.ToLong(&windowSize);
|
|
|
|
mWindowSize = windowSize;
|
|
|
|
mSizeChoice->SetStringSelection(sizeChoice);
|
|
|
|
|
|
|
|
mAlg = static_cast<SpectrumAnalyst::Algorithm>(algChoice);
|
|
|
|
mAlgChoice->SetSelection(algChoice);
|
|
|
|
|
|
|
|
mFunc = funcChoice;
|
|
|
|
mFuncChoice->SetSelection(funcChoice);
|
|
|
|
|
|
|
|
mAxis = axisChoice;
|
|
|
|
mAxisChoice->SetSelection(axisChoice);
|
|
|
|
|
|
|
|
if (shown) {
|
|
|
|
Show(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-03 20:23:33 +00:00
|
|
|
BEGIN_EVENT_TABLE(FreqPlot, wxWindow)
|
|
|
|
EVT_ERASE_BACKGROUND(FreqPlot::OnErase)
|
|
|
|
EVT_PAINT(FreqPlot::OnPaint)
|
|
|
|
EVT_MOUSE_EVENTS(FreqPlot::OnMouseEvent)
|
|
|
|
END_EVENT_TABLE()
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-10-20 16:06:29 +00:00
|
|
|
FreqPlot::FreqPlot(wxWindow *parent, wxWindowID winid)
|
|
|
|
: wxWindow(parent, winid)
|
2015-05-03 20:23:33 +00:00
|
|
|
{
|
2019-12-06 10:39:07 +00:00
|
|
|
freqWindow = (FrequencyPlotDialog *) parent;
|
2015-05-03 20:23:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool FreqPlot::AcceptsFocus() const
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FreqPlot::OnErase(wxEraseEvent & WXUNUSED(event))
|
|
|
|
{
|
|
|
|
// Ignore it to prevent flashing
|
|
|
|
}
|
|
|
|
|
|
|
|
void FreqPlot::OnPaint(wxPaintEvent & evt)
|
|
|
|
{
|
|
|
|
freqWindow->PlotPaint(evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FreqPlot::OnMouseEvent(wxMouseEvent & event)
|
|
|
|
{
|
|
|
|
freqWindow->PlotMouseEvent(event);
|
|
|
|
}
|
|
|
|
|
2020-02-01 04:44:00 +00:00
|
|
|
// Remaining code hooks this add-on into the application
|
|
|
|
#include "commands/CommandContext.h"
|
|
|
|
#include "commands/CommandManager.h"
|
|
|
|
#include "commands/ScreenshotCommand.h"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
AudacityProject::AttachedWindows::RegisteredFactory sFrequencyWindowKey{
|
|
|
|
[]( AudacityProject &parent ) -> wxWeakRef< wxWindow > {
|
|
|
|
auto &window = ProjectWindow::Get( parent );
|
|
|
|
return safenew FrequencyPlotDialog(
|
2021-02-14 07:25:52 +00:00
|
|
|
&window, -1, parent, FrequencyAnalysisTitle,
|
2020-02-01 04:44:00 +00:00
|
|
|
wxPoint{ 150, 150 }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Define our extra menu item that invokes that factory
|
|
|
|
struct Handler : CommandHandlerObject {
|
|
|
|
void OnPlotSpectrum(const CommandContext &context)
|
|
|
|
{
|
|
|
|
auto &project = context.project;
|
2021-02-03 11:02:49 +00:00
|
|
|
CommandManager::Get(project).RegisterLastAnalyzer(context); //Register Plot Spectrum as Last Analyzer
|
2020-02-01 04:44:00 +00:00
|
|
|
auto freqWindow =
|
|
|
|
&project.AttachedWindows::Get< FrequencyPlotDialog >( sFrequencyWindowKey );
|
|
|
|
|
|
|
|
if( ScreenshotCommand::MayCapture( freqWindow ) )
|
|
|
|
return;
|
|
|
|
freqWindow->Show(true);
|
|
|
|
freqWindow->Raise();
|
|
|
|
freqWindow->SetFocus();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CommandHandlerObject &findCommandHandler(AudacityProject &) {
|
|
|
|
// Handler is not stateful. Doesn't need a factory registered with
|
|
|
|
// AudacityProject.
|
|
|
|
static Handler instance;
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register that menu item
|
|
|
|
|
|
|
|
using namespace MenuTable;
|
|
|
|
AttachedItem sAttachment{ wxT("Analyze/Analyzers/Windows"),
|
|
|
|
( FinderScope{ findCommandHandler },
|
|
|
|
Command( wxT("PlotSpectrum"), XXO("Plot Spectrum..."),
|
|
|
|
&Handler::OnPlotSpectrum,
|
|
|
|
AudioIONotBusyFlag() | WaveTracksSelectedFlag() | TimeSelectedFlag() ) )
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|