Beispiel #1
0
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 --------------
}
Beispiel #3
0
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;
}
Beispiel #4
0
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();
}
Beispiel #9
0
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);
}