void SpectrumPrefs::Populate(int windowSize) { mSizeChoices.Add(_("8 - most wideband")); mSizeChoices.Add(wxT("16")); mSizeChoices.Add(wxT("32")); mSizeChoices.Add(wxT("64")); mSizeChoices.Add(wxT("128")); mSizeChoices.Add(_("256 - default")); mSizeChoices.Add(wxT("512")); mSizeChoices.Add(wxT("1024")); mSizeChoices.Add(wxT("2048")); mSizeChoices.Add(wxT("4096")); mSizeChoices.Add(wxT("8192")); mSizeChoices.Add(wxT("16384")); mSizeChoices.Add(_("32768 - most narrowband")); wxASSERT(mSizeChoices.size() == SpectrogramSettings::NumWindowSizes); PopulatePaddingChoices(windowSize); for (int i = 0; i < NumWindowFuncs(); i++) { mTypeChoices.Add(WindowFuncName(i)); } mScaleChoices = SpectrogramSettings::GetScaleNames(); mAlgorithmChoices = SpectrogramSettings::GetAlgorithmNames(); //------------------------- Main section -------------------- // Now construct the GUI itself. ShuttleGui S(this, eIsCreating); PopulateOrExchange(S); // ----------------------- End of main section -------------- }
void SpectrumPrefs::Populate() { mSizeChoices.Add(_("8 - most wideband")); mSizeChoices.Add(_("16")); mSizeChoices.Add(_("32")); mSizeChoices.Add(_("64")); mSizeChoices.Add(_("128")); mSizeChoices.Add(_("256 - default")); mSizeChoices.Add(_("512")); mSizeChoices.Add(_("1024")); mSizeChoices.Add(_("2048")); #ifdef EXPERIMENTAL_FIND_NOTES mSizeChoices.Add(_("4096")); mSizeChoices.Add(_("8192")); mSizeChoices.Add(_("16384")); mSizeChoices.Add(_("32768 - most narrowband")); #else mSizeChoices.Add(_("4096 - most narrowband")); #endif //LOGARITHMIC_SPECTRUM for (size_t i = 0; i < mSizeChoices.GetCount(); i++) { mSizeCodes.Add(1 << (i + 3)); } for (int i = 0; i < NumWindowFuncs(); i++) { mTypeChoices.Add(WindowFuncName(i)); mTypeCodes.Add(i); } //------------------------- Main section -------------------- // Now construct the GUI itself. // Use 'eIsCreatingFromPrefs' so that the GUI is // initialised with values from gPrefs. ShuttleGui S(this, eIsCreatingFromPrefs); PopulateOrExchange(S); // ----------------------- End of main section -------------- }
void FreqWindow::Recalc() { wxLogMessage(wxT("Starting FreqWindow::Recalc()")); if (mProcessed) delete[] mProcessed; mProcessed = NULL; if (!mData) { mFreqPlot->Refresh(true); return; } int alg = mAlgChoice->GetSelection(); int windowFunc = mFuncChoice->GetSelection(); long windowSize = 0; (mSizeChoice->GetStringSelection()).ToLong(&windowSize); int f = NumWindowFuncs(); if (!(windowSize >= 32 && windowSize <= 65536 && alg >= 0 && alg <= 4 && windowFunc >= 0 && windowFunc < f)) { mFreqPlot->Refresh(true); return; } mWindowSize = windowSize; if (mDataLen < mWindowSize) { mFreqPlot->Refresh(true); return; } mProcessed = new float[mWindowSize]; int i; for (i = 0; i < mWindowSize; i++) mProcessed[i] = float(0.0); int half = mWindowSize / 2; float *in = new float[mWindowSize]; float *in2 = new float[mWindowSize]; float *out = new float[mWindowSize]; float *out2 = new float[mWindowSize]; float *win = new float[mWindowSize]; // initialize the window for(int i=0; i<mWindowSize; i++) win[i] = 1.0; WindowFunc(windowFunc, mWindowSize, win); // Scale window such that an amplitude of 1.0 in the time domain // shows an amplitude of 0dB in the frequency domain double wss = 0; for(int i=0; i<mWindowSize; i++) wss += win[i]; if(wss > 0) wss = 4.0 / (wss*wss); else wss = 1.0; //Progress dialog over FFT operation wxLogMessage(wxT("Starting progress dialogue in FreqWindow::Recalc()")); ProgressDialog *mProgress = new ProgressDialog(_("FreqWindow"),_("Drawing Spectrum")); int start = 0; int windows = 0; while (start + mWindowSize <= mDataLen) { for (i = 0; i < mWindowSize; i++) in[i] = win[i] * mData[start + i]; switch (alg) { case 0: // Spectrum PowerSpectrum(mWindowSize, in, out); for (i = 0; i < half; i++) mProcessed[i] += out[i]; break; case 1: case 2: case 3: // Autocorrelation, Cuberoot AC or Enhanced AC // Take FFT #ifdef EXPERIMENTAL_USE_REALFFTF RealFFT(mWindowSize, in, out, out2); #else FFT(mWindowSize, false, in, NULL, out, out2); #endif // Compute power for (i = 0; i < mWindowSize; i++) in[i] = (out[i] * out[i]) + (out2[i] * out2[i]); if (alg == 1) { for (i = 0; i < mWindowSize; i++) in[i] = sqrt(in[i]); } if (alg == 2 || alg == 3) { // Tolonen and Karjalainen recommend taking the cube root // of the power, instead of the square root for (i = 0; i < mWindowSize; i++) in[i] = pow(in[i], 1.0f / 3.0f); } // Take FFT #ifdef EXPERIMENTAL_USE_REALFFTF RealFFT(mWindowSize, in, out, out2); #else FFT(mWindowSize, false, in, NULL, out, out2); #endif // Take real part of result for (i = 0; i < half; i++) mProcessed[i] += out[i]; break; case 4: // Cepstrum #ifdef EXPERIMENTAL_USE_REALFFTF RealFFT(mWindowSize, in, out, out2); #else FFT(mWindowSize, false, in, NULL, out, out2); #endif // Compute log power // Set a sane lower limit assuming maximum time amplitude of 1.0 float power; float minpower = 1e-20*mWindowSize*mWindowSize; for (i = 0; i < mWindowSize; i++) { power = (out[i] * out[i]) + (out2[i] * out2[i]); if(power < minpower) in[i] = log(minpower); else in[i] = log(power); } // Take IFFT #ifdef EXPERIMENTAL_USE_REALFFTF InverseRealFFT(mWindowSize, in, NULL, out); #else FFT(mWindowSize, true, in, NULL, out, out2); #endif // Take real part of result for (i = 0; i < half; i++) mProcessed[i] += out[i]; break; } //switch start += half; windows++; // only update the progress dialogue infrequently to reduce it's overhead // If we do it every time, it spends as much time updating X11 as doing // the calculations. 10 seems a reasonable compromise on Linux that // doesn't make it unresponsive, but avoids the slowdown. if ((windows % 10) == 0) mProgress->Update(1 - static_cast<float>(mDataLen - start) / mDataLen); } wxLogMessage(wxT("Finished updating progress dialogue in FreqWindow::Recalc()")); switch (alg) { double scale; case 0: // Spectrum // Convert to decibels mYMin = 1000000.; mYMax = -1000000.; scale = wss / (double)windows; for (i = 0; i < half; i++) { mProcessed[i] = 10 * log10(mProcessed[i] * scale); if(mProcessed[i] > mYMax) mYMax = mProcessed[i]; else if(mProcessed[i] < mYMin) mYMin = mProcessed[i]; } if(mYMin < -dBRange) mYMin = -dBRange; if(mYMax <= -dBRange) mYMax = -dBRange + 10.; // it's all out of range, but show a scale. else mYMax += .5; mProcessedSize = half; mYStep = 10; break; case 1: // Standard Autocorrelation case 2: // Cuberoot Autocorrelation for (i = 0; i < half; i++) mProcessed[i] = mProcessed[i] / windows; // Find min/max mYMin = mProcessed[0]; mYMax = mProcessed[0]; for (i = 1; i < half; i++) if (mProcessed[i] > mYMax) mYMax = mProcessed[i]; else if (mProcessed[i] < mYMin) mYMin = mProcessed[i]; mYStep = 1; mProcessedSize = half; break; case 3: // Enhanced Autocorrelation for (i = 0; i < half; i++) mProcessed[i] = mProcessed[i] / windows; // Peak Pruning as described by Tolonen and Karjalainen, 2000 // Clip at zero, copy to temp array for (i = 0; i < half; i++) { if (mProcessed[i] < 0.0) mProcessed[i] = float(0.0); out[i] = mProcessed[i]; } // Subtract a time-doubled signal (linearly interp.) from the original // (clipped) signal for (i = 0; i < half; i++) if ((i % 2) == 0) mProcessed[i] -= out[i / 2]; else mProcessed[i] -= ((out[i / 2] + out[i / 2 + 1]) / 2); // Clip at zero again for (i = 0; i < half; i++) if (mProcessed[i] < 0.0) mProcessed[i] = float(0.0); // Find new min/max mYMin = mProcessed[0]; mYMax = mProcessed[0]; for (i = 1; i < half; i++) if (mProcessed[i] > mYMax) mYMax = mProcessed[i]; else if (mProcessed[i] < mYMin) mYMin = mProcessed[i]; mYStep = 1; mProcessedSize = half; break; case 4: // Cepstrum for (i = 0; i < half; i++) mProcessed[i] = mProcessed[i] / windows; // Find min/max, ignoring first and last few values int ignore = 4; mYMin = mProcessed[ignore]; mYMax = mProcessed[ignore]; for (i = ignore + 1; i < half - ignore; i++) if (mProcessed[i] > mYMax) mYMax = mProcessed[i]; else if (mProcessed[i] < mYMin) mYMin = mProcessed[i]; mYStep = 1; mProcessedSize = half; break; } delete[]in; delete[]in2; delete[]out; delete[]out2; delete[]win; wxLogMessage(wxT("About to draw plot in FreqWindow::Recalc()")); DrawPlot(); mFreqPlot->Refresh(true); delete mProgress; }
FreqWindow::FreqWindow(wxWindow * parent, wxWindowID id, const wxString & title, const wxPoint & pos): wxDialog(parent, id, title, pos, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX), mData(NULL), mProcessed(NULL), mBitmap(NULL) { mMouseX = 0; mMouseY = 0; mRate = 0; mDataLen = 0; mBuffer = NULL; p = GetActiveProject(); if (!p) return; mFreqFont = wxFont(fontSize, wxSWISS, wxNORMAL, wxNORMAL); mArrowCursor = new wxCursor(wxCURSOR_ARROW); mCrossCursor = new wxCursor(wxCURSOR_CROSS); mLeftMargin = 40; mBottomMargin = 20; gPrefs->Read(wxT("/FreqWindow/DrawGrid"), &mDrawGrid, true); gPrefs->Read(wxT("/FreqWindow/SizeChoice"), &mSize, 2); gPrefs->Read(wxT("/FreqWindow/AlgChoice"), &mAlg, 0); gPrefs->Read(wxT("/FreqWindow/FuncChoice"), &mFunc, 3); gPrefs->Read(wxT("/FreqWindow/AxisChoice"), &mAxis, 0); gPrefs->Read(wxT("/GUI/EnvdBRange"), &dBRange, ENV_DB_RANGE); if(dBRange < 90.) dBRange = 90.; mFreqPlot = new FreqPlot(this, 0, wxDefaultPosition, wxDefaultSize); wxString algChoiceStrings[5] = { _("Spectrum"), _("Standard Autocorrelation"), _("Cuberoot Autocorrelation"), _("Enhanced Autocorrelation"), /* 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. */ _("Cepstrum") }; wxStaticText *algLabel = new wxStaticText(this, wxID_ANY, wxString(_("Algorithm")) + wxT(":")); mAlgChoice = new wxChoice(this, FreqAlgChoiceID, wxDefaultPosition, wxDefaultSize, 5, algChoiceStrings); mAlgChoice->SetName(_("Algorithm")); mAlgChoice->SetSelection(mAlg); wxString sizeChoiceStrings[8] = { wxT("128"), wxT("256"), wxT("512"), wxT("1024"), wxT("2048"), wxT("4096"), wxT("8192"), wxT("16384") }; wxStaticText *sizeLabel = new wxStaticText(this, wxID_ANY, wxString(_("Size")) + wxT(":")); mSizeChoice = new wxChoice(this, FreqSizeChoiceID, wxDefaultPosition, wxDefaultSize, 8, sizeChoiceStrings); mSizeChoice->SetName(_("Size")); mSizeChoice->SetSelection(mSize); int f = NumWindowFuncs(); wxString *funcChoiceStrings = new wxString[f]; for (int i = 0; i < f; i++) { /* i18n-hint: This refers to a "window function", used in the Frequency analyze dialog box. */ funcChoiceStrings[i] = WindowFuncName(i) + wxString(_(" window")); } wxStaticText *funcLabel = new wxStaticText(this, wxID_ANY, wxString(_("Function")) + wxT(":")); mFuncChoice = new wxChoice(this, FreqFuncChoiceID, wxDefaultPosition, wxDefaultSize, f, funcChoiceStrings); mFuncChoice->SetName(_("Function")); mFuncChoice->SetSelection(mFunc); delete[]funcChoiceStrings; wxString axisChoiceStrings[2] = { _("Linear frequency"), _("Log frequency") }; wxStaticText *axisLabel = new wxStaticText(this, wxID_ANY, wxString(_("Axis")) + wxT(":")); mAxisChoice = new wxChoice(this, FreqAxisChoiceID, wxDefaultPosition, wxDefaultSize, 2, axisChoiceStrings); mAxisChoice->SetName(_("Axis")); mAxisChoice->SetSelection(mAxis); mLogAxis = mAxis?true:false; mExportButton = new wxButton(this, FreqExportButtonID, _("&Export...")); mExportButton->SetName(_("Export")); mReplotButton = new wxButton(this, ReplotButtonID, _("&Replot")); mReplotButton->SetName(_("Replot")); mCloseButton = new wxButton(this, wxID_CANCEL, _("Close")); mCloseButton->SetName(_("Close")); mGridOnOff = new wxCheckBox(this, GridOnOffID, _("Grids"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); mGridOnOff->SetName(_("Grids")); mGridOnOff->SetValue(mDrawGrid); #ifndef TARGET_CARBON mCloseButton->SetDefault(); mCloseButton->SetFocus(); #endif mInfo = new wxStatusBar(this, wxID_ANY, wxST_SIZEGRIP); // Set minimum sizes mFreqPlot->SetMinSize( wxSize( FREQ_WINDOW_WIDTH, FREQ_WINDOW_HEIGHT ) ); wxRect r( 0, 0, 0, 0 ); r.Union( algLabel->GetRect() ); r.Union( funcLabel->GetRect() ); algLabel->SetMinSize( r.GetSize() ); funcLabel->SetMinSize( r.GetSize() ); r = wxRect( 0, 0, 0, 0 ); r.Union( mAlgChoice->GetRect() ); r.Union( mFuncChoice->GetRect() ); mAlgChoice->SetMinSize( r.GetSize() ); mFuncChoice->SetMinSize( r.GetSize() ); r = wxRect( 0, 0, 0, 0 ); r.Union( sizeLabel->GetRect() ); r.Union( axisLabel->GetRect() ); sizeLabel->SetMinSize( r.GetSize() ); axisLabel->SetMinSize( r.GetSize() ); r = wxRect( 0, 0, 0, 0 ); r.Union( mSizeChoice->GetRect() ); r.Union( mAxisChoice->GetRect() ); mSizeChoice->SetMinSize( r.GetSize() ); mAxisChoice->SetMinSize( r.GetSize() ); r = wxRect( 0, 0, 0, 0 ); r.Union( mExportButton->GetRect() ); r.Union( mCloseButton->GetRect() ); mExportButton->SetMinSize( r.GetSize() ); mCloseButton->SetMinSize( r.GetSize() ); // Add everything to the sizers wxBoxSizer *vs = new wxBoxSizer( wxVERTICAL ); wxBoxSizer *hs; szr = new wxFlexGridSizer(2); szr->AddGrowableCol( 1, 1 ); szr->AddGrowableRow( 0, 1 ); szr->SetFlexibleDirection( wxBOTH ); vRuler = new RulerPanel(this, wxID_ANY); vRuler->ruler.SetBounds(0, 0, 100, 100); // Ruler can't handle small sizes vRuler->ruler.SetOrientation(wxVERTICAL); vRuler->ruler.SetRange(10.0, -dBRange); // Note inversion for vertical. vRuler->ruler.SetFormat(Ruler::LinearDBFormat); vRuler->ruler.SetUnits(_("dB")); vRuler->ruler.SetLabelEdges(false); int w, h; vRuler->ruler.GetMaxSize(&w, NULL); vRuler->SetSize(wxSize(w, 150)); // height needed for wxGTK szr->Add( vRuler, 0, wxEXPAND|wxTOP|wxBOTTOM, 1 ); //for border around graph szr->Add( mFreqPlot, 1, wxEXPAND ); szr->Add(1,1); //spacer on next row, under vRuler hRuler = new RulerPanel(this, wxID_ANY); hRuler->ruler.SetBounds(0, 0, 100, 100); // Ruler can't handle small sizes hRuler->ruler.SetOrientation(wxHORIZONTAL); hRuler->ruler.SetLog(false); //default for freq axis hRuler->ruler.SetRange(10, 20000); //dummy values - will get replaced hRuler->ruler.SetFormat(Ruler::RealFormat); hRuler->ruler.SetUnits(_("Hz")); hRuler->ruler.SetFlip(true); hRuler->ruler.SetLabelEdges(false); hRuler->ruler.GetMaxSize(NULL, &h); hRuler->SetMinSize(wxSize(-1, h)); szr->Add( hRuler, 0, wxEXPAND|wxLEFT|wxRIGHT, 1 ); //for border around graph szr->Add(1,1); //spacer mInfoText = new wxStaticText(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE); //box for info text szr->Add( mInfoText, 0, wxEXPAND|wxALL, 5); vs->Add(szr, 1, wxEXPAND|wxALL, 5); vs->Add( 1, 5, 0 ); wxFlexGridSizer *gs = new wxFlexGridSizer( 2 ); hs = new wxBoxSizer( wxHORIZONTAL ); hs->Add( algLabel, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, 5 ); hs->Add( mAlgChoice, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, 5 ); hs->Add( sizeLabel, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, 5 ); hs->Add( mSizeChoice, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, 5 ); hs->Add( 10, 1, 0 ); hs->Add( mExportButton, 0, wxALIGN_CENTER | wxALIGN_RIGHT | wxLEFT | wxRIGHT, 5 ); gs->Add( hs, 0, wxALIGN_CENTER_HORIZONTAL | wxLEFT | wxRIGHT , 5 ); gs->Add( mReplotButton, 0, wxALIGN_CENTER | wxALIGN_RIGHT | wxLEFT | wxRIGHT | wxBOTTOM, 5 ); hs = new wxBoxSizer( wxHORIZONTAL ); hs->Add( funcLabel, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, 5 ); hs->Add( mFuncChoice, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, 5 ); hs->Add( axisLabel, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, 5 ); hs->Add( mAxisChoice, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, 5 ); hs->Add( 10, 1, 0 ); hs->Add( mCloseButton, 0, wxALIGN_CENTER | wxALIGN_RIGHT | wxLEFT | wxRIGHT, 5 ); gs->Add( hs, 0, wxALIGN_CENTER_HORIZONTAL | wxLEFT | wxRIGHT | wxBOTTOM, 5 ); gs->Add( mGridOnOff, 0, wxALIGN_CENTER | wxALIGN_RIGHT | wxLEFT | wxRIGHT, 5 ); vs->Add( gs, 0, wxEXPAND | wxBOTTOM, 0 ); vs->Add( mInfo, 0, wxEXPAND | wxBOTTOM, 0 ); SetAutoLayout( false ); SetSizerAndFit( vs ); Layout(); }
FreqWindow::FreqWindow(wxWindow * parent, wxWindowID id, const wxString & title, const wxPoint & pos) : wxDialogWrapper(parent, id, title, pos, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX), mAnalyst(std::make_unique<SpectrumAnalyst>()) { SetName(GetTitle()); mMouseX = 0; mMouseY = 0; mRate = 0; mDataLen = 0; p = GetActiveProject(); if (!p) return; wxArrayString algChoices; algChoices.Add(_("Spectrum")); algChoices.Add(_("Standard Autocorrelation")); algChoices.Add(_("Cuberoot Autocorrelation")); algChoices.Add(_("Enhanced Autocorrelation")); /* 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. */ algChoices.Add(_("Cepstrum")); wxArrayString sizeChoices; sizeChoices.Add(wxT("128")); sizeChoices.Add(wxT("256")); sizeChoices.Add(wxT("512")); sizeChoices.Add(wxT("1024")); sizeChoices.Add(wxT("2048")); sizeChoices.Add(wxT("4096")); sizeChoices.Add(wxT("8192")); sizeChoices.Add(wxT("16384")); sizeChoices.Add(wxT("32768")); sizeChoices.Add(wxT("65536")); wxArrayString funcChoices; for (int i = 0, cnt = NumWindowFuncs(); i < cnt; i++) { /* i18n-hint: This refers to a "window function", * such as Hann or Rectangular, used in the * Frequency analyze dialog box. */ funcChoices.Add(wxString::Format("%s window", WindowFuncName(i) ) ); } wxArrayString axisChoices; axisChoices.Add(_("Linear frequency")); axisChoices.Add(_("Log frequency")); mFreqFont = wxFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); mArrowCursor = std::make_unique<wxCursor>(wxCURSOR_ARROW); mCrossCursor = std::make_unique<wxCursor>(wxCURSOR_CROSS); gPrefs->Read(wxT("/FreqWindow/DrawGrid"), &mDrawGrid, true); long size; gPrefs->Read(wxT("/FreqWindow/SizeChoice"), &mSize, 3); sizeChoices[mSize].ToLong(&size); mWindowSize = size; int alg; gPrefs->Read(wxT("/FreqWindow/AlgChoice"), &alg, 0); mAlg = static_cast<SpectrumAnalyst::Algorithm>(alg); gPrefs->Read(wxT("/FreqWindow/FuncChoice"), &mFunc, 3); gPrefs->Read(wxT("/FreqWindow/AxisChoice"), &mAxis, 1); gPrefs->Read(ENV_DB_KEY, &dBRange, ENV_DB_RANGE); if(dBRange < 90.) dBRange = 90.; ShuttleGui S(this, eIsCreating); S.SetBorder(0); S.AddSpace(5); S.SetSizerProportion(1); S.StartMultiColumn(3, wxEXPAND); { S.SetStretchyCol(1); S.SetStretchyRow(0); // ------------------------------------------------------------------- // ROW 1: Freq response panel and sliders for vertical scale // ------------------------------------------------------------------- S.StartVerticalLay(2); { vRuler = safenew RulerPanel(this, wxID_ANY); vRuler->ruler.SetBounds(0, 0, 100, 100); // Ruler can't handle small sizes vRuler->ruler.SetOrientation(wxVERTICAL); vRuler->ruler.SetRange(0.0, -dBRange); vRuler->ruler.SetFormat(Ruler::LinearDBFormat); vRuler->ruler.SetUnits(_("dB")); vRuler->ruler.SetLabelEdges(true); int w; vRuler->ruler.GetMaxSize(&w, NULL); vRuler->SetMinSize(wxSize(w, 150)); // height needed for wxGTK vRuler->SetTickColour( theTheme.Colour( clrGraphLabels )); S.AddSpace(wxDefaultCoord, 1); S.Prop(1); S.AddWindow(vRuler, wxALIGN_RIGHT | wxALIGN_TOP); S.AddSpace(wxDefaultCoord, 1); } S.EndVerticalLay(); mFreqPlot = safenew FreqPlot(this); mFreqPlot->SetMinSize(wxSize(wxDefaultCoord, FREQ_WINDOW_HEIGHT)); S.Prop(1); S.AddWindow(mFreqPlot, wxEXPAND); S.StartHorizontalLay(wxEXPAND, 0); { S.StartVerticalLay(); { mPanScroller = safenew wxScrollBar(this, FreqPanScrollerID, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL); mPanScroller->SetName(_("Scroll")); S.Prop(1); S.AddWindow(mPanScroller, wxALIGN_LEFT | wxTOP); } S.EndVerticalLay(); S.StartVerticalLay(); { wxStaticBitmap *zi = safenew wxStaticBitmap(this, wxID_ANY, wxBitmap(ZoomIn)); S.AddWindow((wxWindow *) zi, wxALIGN_CENTER); S.AddSpace(5); mZoomSlider = safenew wxSlider(this, FreqZoomSliderID, 100, 1, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL); S.Prop(1); S.AddWindow(mZoomSlider, wxALIGN_CENTER_HORIZONTAL); mZoomSlider->SetName(_("Zoom")); S.AddSpace(5); wxStaticBitmap *zo = safenew wxStaticBitmap(this, wxID_ANY, wxBitmap(ZoomOut)); S.AddWindow((wxWindow *) zo, wxALIGN_CENTER); } S.EndVerticalLay(); S.AddSpace(5, wxDefaultCoord); } S.EndHorizontalLay(); // ------------------------------------------------------------------- // ROW 2: Frequency ruler // ------------------------------------------------------------------- S.AddSpace(1); S.StartHorizontalLay(wxEXPAND, 0); { hRuler = safenew RulerPanel(this, wxID_ANY); hRuler->ruler.SetBounds(0, 0, 100, 100); // Ruler can't handle small sizes hRuler->ruler.SetOrientation(wxHORIZONTAL); hRuler->ruler.SetLog(true); hRuler->ruler.SetRange(10, 20000); hRuler->ruler.SetFormat(Ruler::RealFormat); hRuler->ruler.SetUnits(_("Hz")); hRuler->ruler.SetFlip(true); hRuler->ruler.SetLabelEdges(true); int h; hRuler->ruler.GetMaxSize(NULL, &h); hRuler->SetMinSize(wxSize(wxDefaultCoord, h)); hRuler->SetTickColour( theTheme.Colour( clrGraphLabels )); S.AddSpace(1, wxDefaultCoord); S.Prop(1); S.AddWindow(hRuler, wxALIGN_LEFT | wxALIGN_TOP); 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); S.StartMultiColumn(6); S.SetStretchyCol(1); S.SetStretchyCol(3); { S.AddPrompt(_("Cursor:")); S.SetStyle(wxTE_READONLY); mCursorText = S.AddTextBox( {}, wxT(""), 10); S.AddPrompt(_("Peak:")); S.SetStyle(wxTE_READONLY); mPeakText = S.AddTextBox( {}, wxT(""), 10); S.AddSpace(5); mGridOnOff = S.Id(GridOnOffID).AddCheckBox(_("&Grids"), wxT("false")); mGridOnOff->SetValue(mDrawGrid); } S.EndMultiColumn(); } S.EndHorizontalLay(); S.AddSpace(1); } S.EndMultiColumn(); // ------------------------------------------------------------------- // ROW 5: Spacer // ------------------------------------------------------------------- S.AddSpace(5); S.SetBorder(2); S.SetSizerProportion(0); S.StartMultiColumn(9, wxALIGN_CENTER); { // ---------------------------------------------------------------- // ROW 6: Algorithm, Size, Export, Replot // ---------------------------------------------------------------- S.AddSpace(5); mAlgChoice = S.Id(FreqAlgChoiceID).AddChoice(_("&Algorithm:"), wxT(""), &algChoices); mAlgChoice->SetSelection(mAlg); S.SetSizeHints(wxDefaultCoord, wxDefaultCoord); S.AddSpace(5); mSizeChoice = S.Id(FreqSizeChoiceID).AddChoice(_("&Size:"), wxT(""), &sizeChoices); mSizeChoice->SetSelection(mSize); S.SetSizeHints(wxDefaultCoord, wxDefaultCoord); S.AddSpace(5); mExportButton = S.Id(FreqExportButtonID).AddButton(_("&Export...")); S.AddSpace(5); // ---------------------------------------------------------------- // ROW 7: Function, Axix, Grids, Close // ---------------------------------------------------------------- S.AddSpace(5); mFuncChoice = S.Id(FreqFuncChoiceID).AddChoice(_("&Function:"), wxT(""), &funcChoices); mFuncChoice->SetSelection(mFunc); S.SetSizeHints(wxDefaultCoord, wxDefaultCoord); mFuncChoice->MoveAfterInTabOrder(mSizeChoice); S.AddSpace(5); mAxisChoice = S.Id(FreqAxisChoiceID).AddChoice(_("&Axis:"), wxT(""), &axisChoices); mAxisChoice->SetSelection(mAxis); S.SetSizeHints(wxDefaultCoord, wxDefaultCoord); mAxisChoice->MoveAfterInTabOrder(mFuncChoice); S.AddSpace(5); mReplotButton = S.Id(ReplotButtonID).AddButton(_("&Replot...")); S.AddSpace(5); //mCloseButton = S.Id(wxID_CANCEL).AddButton(_("&Close")); //S.AddSpace(5); } S.EndMultiColumn(); S.AddStandardButtons( eHelpButton | eCloseButton ); // ------------------------------------------------------------------- // ROW 8: Spacer // ------------------------------------------------------------------- S.AddSpace(5); mProgress = safenew FreqGauge(this); //, wxID_ANY, wxST_SIZEGRIP); S.AddWindow(mProgress, wxEXPAND); // Log-frequency axis works for spectrum plots only. if (mAlg != SpectrumAnalyst::Spectrum) { mAxis = 0; mAxisChoice->Disable(); } mLogAxis = mAxis != 0; mCloseButton = reinterpret_cast<wxButton*>(FindWindowById( wxID_CANCEL )); mCloseButton->SetDefault(); mCloseButton->SetFocus(); Layout(); Fit(); // Bug 1607: Center(); SetMinSize(GetSize()); mAlgChoice->SetFocus(); #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. // gtk_widget_set_can_focus(mPanScroller->m_widget, true); #endif }
bool SpectrumAnalyst::Calculate(Algorithm alg, int windowFunc, size_t windowSize, double rate, const float *data, size_t dataLen, float *pYMin, float *pYMax, FreqGauge *progress) { // Wipe old data mProcessed.resize(0); mRate = 0.0; mWindowSize = 0; // Validate inputs int f = NumWindowFuncs(); if (!(windowSize >= 32 && windowSize <= 65536 && alg >= SpectrumAnalyst::Spectrum && alg < SpectrumAnalyst::NumAlgorithms && windowFunc >= 0 && windowFunc < f)) { return false; } if (dataLen < windowSize) { return false; } // Now repopulate mRate = rate; mWindowSize = windowSize; mAlg = alg; auto half = mWindowSize / 2; mProcessed.resize(mWindowSize); Floats in{ mWindowSize }; Floats out{ mWindowSize }; Floats out2{ mWindowSize }; Floats win{ mWindowSize }; for (size_t i = 0; i < mWindowSize; i++) { mProcessed[i] = 0.0f; win[i] = 1.0f; } WindowFunc(windowFunc, mWindowSize, win.get()); // Scale window such that an amplitude of 1.0 in the time domain // shows an amplitude of 0dB in the frequency domain double wss = 0; for (size_t i = 0; i<mWindowSize; i++) wss += win[i]; if(wss > 0) wss = 4.0 / (wss*wss); else wss = 1.0; if (progress) { progress->SetRange(dataLen); } size_t start = 0; int windows = 0; while (start + mWindowSize <= dataLen) { for (size_t i = 0; i < mWindowSize; i++) in[i] = win[i] * data[start + i]; switch (alg) { case Spectrum: PowerSpectrum(mWindowSize, in.get(), out.get()); for (size_t i = 0; i < half; i++) mProcessed[i] += out[i]; break; case Autocorrelation: case CubeRootAutocorrelation: case EnhancedAutocorrelation: // Take FFT RealFFT(mWindowSize, in.get(), out.get(), out2.get()); // Compute power for (size_t i = 0; i < mWindowSize; i++) in[i] = (out[i] * out[i]) + (out2[i] * out2[i]); if (alg == Autocorrelation) { for (size_t i = 0; i < mWindowSize; i++) in[i] = sqrt(in[i]); } if (alg == CubeRootAutocorrelation || alg == EnhancedAutocorrelation) { // Tolonen and Karjalainen recommend taking the cube root // of the power, instead of the square root for (size_t i = 0; i < mWindowSize; i++) in[i] = pow(in[i], 1.0f / 3.0f); } // Take FFT RealFFT(mWindowSize, in.get(), out.get(), out2.get()); // Take real part of result for (size_t i = 0; i < half; i++) mProcessed[i] += out[i]; break; case Cepstrum: RealFFT(mWindowSize, in.get(), out.get(), out2.get()); // Compute log power // Set a sane lower limit assuming maximum time amplitude of 1.0 { float power; float minpower = 1e-20*mWindowSize*mWindowSize; for (size_t i = 0; i < mWindowSize; i++) { power = (out[i] * out[i]) + (out2[i] * out2[i]); if(power < minpower) in[i] = log(minpower); else in[i] = log(power); } // Take IFFT InverseRealFFT(mWindowSize, in.get(), NULL, out.get()); // Take real part of result for (size_t i = 0; i < half; i++) mProcessed[i] += out[i]; } break; default: wxASSERT(false); break; } //switch // Update the progress bar if (progress) { progress->SetValue(start); } start += half; windows++; } if (progress) { // Reset for next time progress->Reset(); } float mYMin = 1000000, mYMax = -1000000; double scale; switch (alg) { case Spectrum: // Convert to decibels mYMin = 1000000.; mYMax = -1000000.; scale = wss / (double)windows; for (size_t i = 0; i < half; i++) { mProcessed[i] = 10 * log10(mProcessed[i] * scale); if(mProcessed[i] > mYMax) mYMax = mProcessed[i]; else if(mProcessed[i] < mYMin) mYMin = mProcessed[i]; } break; case Autocorrelation: case CubeRootAutocorrelation: for (size_t i = 0; i < half; i++) mProcessed[i] = mProcessed[i] / windows; // Find min/max mYMin = mProcessed[0]; mYMax = mProcessed[0]; for (size_t i = 1; i < half; i++) if (mProcessed[i] > mYMax) mYMax = mProcessed[i]; else if (mProcessed[i] < mYMin) mYMin = mProcessed[i]; break; case EnhancedAutocorrelation: for (size_t i = 0; i < half; i++) mProcessed[i] = mProcessed[i] / windows; // Peak Pruning as described by Tolonen and Karjalainen, 2000 // Clip at zero, copy to temp array for (size_t i = 0; i < half; i++) { if (mProcessed[i] < 0.0) mProcessed[i] = float(0.0); out[i] = mProcessed[i]; } // Subtract a time-doubled signal (linearly interp.) from the original // (clipped) signal for (size_t i = 0; i < half; i++) if ((i % 2) == 0) mProcessed[i] -= out[i / 2]; else mProcessed[i] -= ((out[i / 2] + out[i / 2 + 1]) / 2); // Clip at zero again for (size_t i = 0; i < half; i++) if (mProcessed[i] < 0.0) mProcessed[i] = float(0.0); // Find NEW min/max mYMin = mProcessed[0]; mYMax = mProcessed[0]; for (size_t i = 1; i < half; i++) if (mProcessed[i] > mYMax) mYMax = mProcessed[i]; else if (mProcessed[i] < mYMin) mYMin = mProcessed[i]; break; case Cepstrum: for (size_t i = 0; i < half; i++) mProcessed[i] = mProcessed[i] / windows; // Find min/max, ignoring first and last few values { size_t ignore = 4; mYMin = mProcessed[ignore]; mYMax = mProcessed[ignore]; for (size_t i = ignore + 1; i + ignore < half; i++) if (mProcessed[i] > mYMax) mYMax = mProcessed[i]; else if (mProcessed[i] < mYMin) mYMin = mProcessed[i]; } break; default: wxASSERT(false); break; } if (pYMin) *pYMin = mYMin; if (pYMax) *pYMax = mYMax; return true; }
bool SpectrogramSettings::Validate(bool quiet) { if (!quiet && maxFreq < 100) { wxMessageBox(_("Maximum frequency must be 100 Hz or above")); return false; } else maxFreq = std::max(100, maxFreq); if (!quiet && minFreq < 0) { wxMessageBox(_("Minimum frequency must be at least 0 Hz")); return false; } else minFreq = std::max(0, minFreq); if (!quiet && maxFreq <= minFreq) { wxMessageBox(_("Minimum frequency must be less than maximum frequency")); return false; } else maxFreq = std::max(1 + minFreq, maxFreq); if (!quiet && range <= 0) { wxMessageBox(_("The range must be at least 1 dB")); return false; } else range = std::max(1, range); if (!quiet && frequencyGain < 0) { wxMessageBox(_("The frequency gain cannot be negative")); return false; } else if (!quiet && frequencyGain > 60) { wxMessageBox(_("The frequency gain must be no more than 60 dB/dec")); return false; } else frequencyGain = std::max(0, std::min(60, frequencyGain)); // The rest are controlled by drop-down menus so they can't go wrong // in the Preferences dialog, but we also come here after reading fom saved // preference files, which could be or from future versions. Validate quietly. windowType = std::max(0, std::min(NumWindowFuncs() - 1, windowType)); scaleType = ScaleType(std::max(0, std::min((int)(SpectrogramSettings::stNumScaleTypes) - 1, (int)(scaleType)))); algorithm = Algorithm( std::max(0, std::min((int)(algNumAlgorithms) - 1, (int)(algorithm))) ); ConvertToEnumeratedWindowSizes(); ConvertToActualWindowSizes(); return true; }
void SpectrumPrefs::PopulateOrExchange( ShuttleGui & S ) { wxArrayString windowTypeList; for(int i=0; i<NumWindowFuncs(); i++) windowTypeList.Add(WindowFuncName(i)); S.SetBorder( 2 ); S.StartHorizontalLay(wxEXPAND, 0 ); S.StartStatic( _("FFT Size"), 0 ); { S.StartRadioButtonGroup( wxT("/Spectrum/FFTSize"), 256 ); S.TieRadioButton( _("8 - most wideband"), 8); S.TieRadioButton( wxT("16"), 16); S.TieRadioButton( wxT("32"), 32); S.TieRadioButton( wxT("64"), 64); S.TieRadioButton( wxT("128"), 128); S.TieRadioButton( _("256 - default"), 256); S.TieRadioButton( wxT("512"), 512); S.TieRadioButton( wxT("1024"), 1024); S.TieRadioButton( wxT("2048"), 2048); #ifdef EXPERIMENTAL_FIND_NOTES S.TieRadioButton( wxT("4096"), 4096); S.TieRadioButton( wxT("8192"), 8192); S.TieRadioButton( wxT("16384"), 16384); S.TieRadioButton( _("32768 - most narrowband"),32768); #else S.TieRadioButton( _("4096 - most narrowband"),4096); #endif //LOGARITHMIC_SPECTRUM S.EndRadioButtonGroup(); // add choice for windowtype S.StartMultiColumn(2, wxCENTER); { S.TieChoice( _("Window type:"), windowType, &windowTypeList); S.SetSizeHints(-1,-1); } S.EndMultiColumn(); } S.EndStatic(); #ifdef EXPERIMENTAL_FFT_SKIP_POINTS S.StartHorizontalLay(wxEXPAND, 0 ); S.StartStatic( _("FFT Skip Points"), 0 ); { S.StartRadioButtonGroup(wxT("/Spectrum/FFTSkipPoints"), 0); S.TieRadioButton(wxT("0"), 0); S.TieRadioButton(wxT("1"), 1); S.TieRadioButton(wxT("3"), 3); S.TieRadioButton(wxT("7"), 7); S.TieRadioButton(wxT("15"), 15); S.TieRadioButton(wxT("31"), 31); S.TieRadioButton(wxT("63"), 63); S.EndRadioButtonGroup(); } S.EndStatic(); #endif //EXPERIMENTAL_FFT_SKIP_POINTS S.StartStatic( _("Display"),1 ); { // JC: For layout of mixtures of controls I prefer checkboxes on the right, // with everything in two columns over what we have here. S.TieCheckBox( _("&Grayscale"), wxT("/Spectrum/Grayscale"), false); S.StartTwoColumn(); // 2 cols because we have a control with a separate label. S.Id(ID_MINFREQUENCY).TieTextBox( _("Minimum Frequency (Hz):"), // prompt minFreqStr, // String to exchange with 12 // max number of characters (used to size the control). ); S.Id(ID_MAXFREQUENCY).TieTextBox( _("Maximum Frequency (Hz):"), // prompt maxFreqStr, // String to exchange with 12 // max number of characters (used to size the control). ); S.EndTwoColumn(); #ifdef EXPERIMENTAL_FFT_Y_GRID S.TieCheckBox( _("&Y-Grid"), wxT("/Spectrum/FFTYGrid"), false); #endif //EXPERIMENTAL_FFT_Y_GRID #ifdef EXPERIMENTAL_FIND_NOTES S.TieCheckBox( _("&Find Notes"), wxT("/Spectrum/FFTFindNotes"), false); S.TieCheckBox( _("&Quantize Notes"), wxT("/Spectrum/FindNotesQuantize"), false); S.StartTwoColumn(); // 2 cols because we have a control with a separate label. S.Id(ID_FIND_NOTES_MIN_A).TieTextBox( _("Minimum Amplitude (dB):"), // prompt findNotesMinAStr, // String to exchange with 8 // max number of characters (used to size the control). ); S.Id(ID_FIND_NOTES_N).TieTextBox( _("Max. Number of Notes (1..128):"), // prompt findNotesNStr, // String to exchange with 8 // max number of characters (used to size the control). ); S.EndTwoColumn(); #endif //EXPERIMENTAL_FIND_NOTES } S.EndStatic(); S.EndHorizontalLay(); }
FreqWindow::FreqWindow(wxWindow * parent, wxWindowID id, const wxString & title, const wxPoint & pos):wxFrame(parent, id, title, pos, wxSize (FREQ_WINDOW_WIDTH, FREQ_WINDOW_HEIGHT)), mData(NULL), mProcessed(NULL), mBitmap(NULL) { mMouseX = 0; mMouseY = 0; mArrowCursor = new wxCursor(wxCURSOR_ARROW); mCrossCursor = new wxCursor(wxCURSOR_CROSS); mFreqPlot = new FreqPlot(this, 0, wxPoint(0, 0), wxSize(FREQ_WINDOW_WIDTH, 250)); mUpdateRect.x = 0; mUpdateRect.y = 0; mUpdateRect.width = FREQ_WINDOW_WIDTH; mUpdateRect.height = 250; mPlotRect.x = 10; mPlotRect.y = 10; mPlotRect.width = 460; mPlotRect.height = 215; mLeftMargin = 40; mBottomMargin = 20; mInfoRect.x = 10; mInfoRect.y = 230; mInfoRect.width = 460; mInfoRect.height = 15; mExportButton = new wxButton(this, FreqExportButtonID, _("Export..."), wxPoint(390, 260), wxSize(70, 20)); mCloseButton = new wxButton(this, FreqCloseButtonID, _("Close"), wxPoint(390, 290), wxSize(70, 20)); #ifndef TARGET_CARBON mCloseButton->SetDefault(); mCloseButton->SetFocus(); #endif wxString algChoiceStrings[5] = { _("Spectrum"), _("Standard Autocorrelation"), _("Cuberoot Autocorrelation"), _("Enhanced Autocorrelation"), _("Cepstrum") }; mAlgChoice = new wxChoice(this, FreqAlgChoiceID, wxPoint(10, 260), wxSize(200, 20), 4, algChoiceStrings); mAlgChoice->SetSelection(0); wxString sizeChoiceStrings[8] = { "128", "256", "512", "1024", "2048", "4096", "8192", "16384" }; mSizeChoice = new wxChoice(this, FreqSizeChoiceID, wxPoint(220, 260), wxSize(160, 20), 8, sizeChoiceStrings); mSizeChoice->SetSelection(2); int f = NumWindowFuncs(); wxString *funcChoiceStrings = new wxString[f]; for (int i = 0; i < f; i++) funcChoiceStrings[i] = WindowFuncName(i) + wxString(_(" window")); mFuncChoice = new wxChoice(this, FreqFuncChoiceID, wxPoint(10, 290), wxSize(200, 20), f, funcChoiceStrings); mFuncChoice->SetSelection(3); delete[]funcChoiceStrings; wxString axisChoiceStrings[2] = { _("Linear frequency"), _("Log frequency") }; mAxisChoice = new wxChoice(this, FreqAxisChoiceID, wxPoint(220, 290), wxSize(160, 20), 2, axisChoiceStrings); mAxisChoice->SetSelection(0); mLogAxis = false; mBackgroundBrush.SetColour(wxColour(204, 204, 204)); mBackgroundPen.SetColour(wxColour(204, 204, 204)); SetBackgroundColour(wxColour(204, 204, 204)); // Min size, max size SetSizeHints(FREQ_WINDOW_WIDTH, FREQ_WINDOW_HEIGHT, 20000, 20000); }