// -*- C++ -*- generated by wxGlade 0.3.1 on Wed Sep 29 16:09:00 2004
/*
 * Copyright (C) 2008 Vaclav Peroutka <vaclavpe@seznam.cz>
 *
 * Licensed under the GNU General Public License Version 2
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "AudMeS.h"

#include <math.h>

#ifdef __WXMSW__
#include <windows.h>
#include <wx/clipbrd.h>
#endif
#include <libfccp/csv.h>

#include <fstream>
#include <iostream>

#include "dlg_audiointerface.h"
#include "event_ids.h"
#include "fourier.h"

//#define XSCALEINTIME 1

IMPLEMENT_CLASS(MainFrame, wxFrame)

BEGIN_EVENT_TABLE(MainFrame, wxFrame)
EVT_TOGGLEBUTTON(ID_SPANSTART, MainFrame::OnSpanStart)
EVT_TOGGLEBUTTON(ID_GENSTART, MainFrame::OnGenStart)
EVT_TOGGLEBUTTON(ID_OSCSTART, MainFrame::OnOscStart)
EVT_TOGGLEBUTTON(ID_FRMSTART, MainFrame::OnFrmStart)
EVT_MENU(wxID_ABOUT, MainFrame::OnAboutClick)
EVT_MENU(wxID_EXIT, MainFrame::OnExitClick)
EVT_CLOSE(MainFrame::OnClose)
EVT_MENU(ID_SNDCARD, MainFrame::OnSelectSndCard)
EVT_TIMER(ID_TIMERID, MainFrame::OnTimer)
EVT_CHECKBOX(ID_GENLENB, MainFrame::OnGeneratorChanged)
EVT_CHECKBOX(ID_GENRENB, MainFrame::OnGeneratorChanged)
EVT_CHECKBOX(ID_GENSYNC, MainFrame::OnGeneratorChanged)
EVT_TEXT_ENTER(ID_GENPHASE, MainFrame::OnGeneratorChanged)
EVT_COMMAND_SCROLL(ID_GENLFREQ, MainFrame::OnGenScrollLChanged)
EVT_COMMAND_SCROLL(ID_GENRFREQ, MainFrame::OnGenScrollRChanged)
EVT_COMMAND_SCROLL(ID_GENLAMP, MainFrame::OnGenScrollChanged)
EVT_COMMAND_SCROLL(ID_GENRAMP, MainFrame::OnGenScrollChanged)
EVT_CHOICE(ID_OSCLTRIG, MainFrame::OnOscChoiceChanged)
EVT_CHOICE(ID_OSCRTRIG, MainFrame::OnOscChoiceChanged)
EVT_TEXT_ENTER(ID_TXT_FREQ_L, MainFrame::OnTxtFreqLChanged)
EVT_TEXT_ENTER(ID_TXT_FREQ_R, MainFrame::OnTxtFreqRChanged)
EVT_CHOICE(ID_GENSHP_L, MainFrame::OnGeneratorChanged)
EVT_CHOICE(ID_GENSHP_R, MainFrame::OnGeneratorChanged)
EVT_MENU(wxID_OPEN, MainFrame::OnOpenClick)
EVT_MENU(wxID_SAVE, MainFrame::OnSaveClick)
EVT_MENU(wxID_SAVEAS, MainFrame::OnSaveAsClick)
EVT_MENU(ID_LOAD_FRM, MainFrame::OnLoadFRM)
EVT_MENU(ID_SAVE_FRM, MainFrame::OnSaveFRM)
EVT_BUTTON(ID_AUTOCAL, MainFrame::OnAutoCalClick)
EVT_CHOICE(ID_OSCXSCALE, MainFrame::OnOscXScaleChanged)
EVT_CHOICE(ID_FFTLENGTH, MainFrame::OnOscXScaleChanged)
END_EVENT_TABLE()

float* g_OscBuffer_Left;
float* g_OscBuffer_Right;
long int g_OscBufferPosition;

float* g_SpeBuffer_Left;
float* g_SpeBuffer_Right;
long int g_SpeBufferPosition;

int g_OscBufferChanged;
int g_SpeBufferChanged;

///////////////////////////////////////////////////////////////////////
MainFrame::MainFrame(wxWindow* parent, int id, const wxString& title, const wxPoint& pos,
                     const wxSize& size, long WXUNUSED(style))
    : wxFrame(parent, id, title, pos, size, wxDEFAULT_FRAME_STYLE | wxFULL_REPAINT_ON_RESIZE) {
  // begin wxGlade: MainFrame::MainFrame
  notebook_1 = new wxNotebook(this, -1, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM);
  notebook_1_spe = new wxPanel(notebook_1, -1);
  notebook_1_osc = new wxPanel(notebook_1, -1);
  notebook_1_gen = new wxPanel(notebook_1, -1);
  notebook_1_frm = new wxPanel(notebook_1, -1);
  sizer_4_copy_staticbox = new wxStaticBox(notebook_1_gen, -1, wxT("Right channel"));
  sizer_12_staticbox = new wxStaticBox(notebook_1_osc, -1, wxT("Left channel (Red)"));
  sizer_12_copy_staticbox = new wxStaticBox(notebook_1_osc, -1, wxT("Right channel (Green)"));
  sizer_4_staticbox = new wxStaticBox(notebook_1_gen, -1, wxT("Left channel"));
  frame_1_menubar = new wxMenuBar();
  wxMenu* wxglade_tmp_menu_1 = new wxMenu();
  wxglade_tmp_menu_1->Append(wxID_OPEN, wxT("&Open config...\tAlt+O"), wxT(""), wxITEM_NORMAL);
  wxglade_tmp_menu_1->Append(wxID_SAVE, wxT("&Save config...\tAlt+S"), wxT(""), wxITEM_NORMAL);
  wxglade_tmp_menu_1->Append(wxID_SAVEAS, wxT("Save &As"), wxT(""), wxITEM_NORMAL);
  wxglade_tmp_menu_1->Append(ID_LOAD_FRM, wxT("Load freq.resp."), wxT(""), wxITEM_NORMAL);
  wxglade_tmp_menu_1->Append(ID_SAVE_FRM, wxT("Save freq.resp."), wxT(""), wxITEM_NORMAL);
  wxglade_tmp_menu_1->AppendSeparator();
  wxglade_tmp_menu_1->Append(wxID_EXIT, wxT("&Close\tAlt+F4"), wxT(""), wxITEM_NORMAL);
  frame_1_menubar->Append(wxglade_tmp_menu_1, wxT("&File"));
  wxMenu* wxglade_tmp_menu_2 = new wxMenu();
  wxglade_tmp_menu_2->Append(ID_SNDCARD, wxT("Audio &Interface Configuration..."), wxT(""),
                             wxITEM_NORMAL);
  frame_1_menubar->Append(wxglade_tmp_menu_2, wxT("&Tools"));
  wxMenu* wxglade_tmp_menu_3 = new wxMenu();
  wxglade_tmp_menu_3->Append(wxID_ABOUT, wxT("&About..."), wxT(""), wxITEM_NORMAL);
  frame_1_menubar->Append(wxglade_tmp_menu_3, wxT("&Help"));
  SetMenuBar(frame_1_menubar);

  frame_1_statusbar = CreateStatusBar(1, 0);

  /* generator panel */
  checkbox_l_en = new wxCheckBox(notebook_1_gen, ID_GENLENB, wxT("Enable Output"));
  label_1 = new wxStaticText(notebook_1_gen, -1, wxT("Waveform: "));
  const wxString choice_l_wav_choices[] = {wxT("Sine"), wxT("Rectangular"), wxT("Saw"),
                                           wxT("Triangle"), wxT("Wh-Noise")};
  choice_l_wav = new wxChoice(notebook_1_gen, ID_GENSHP_L, wxDefaultPosition, wxDefaultSize, 5,
                              choice_l_wav_choices, 0);
  label_2 = new wxStaticText(notebook_1_gen, -1, wxT("Frequency [20..20000Hz]: "));
  slide_l_fr = new wxSlider(notebook_1_gen, ID_GENLFREQ, 80, 0, 200);
  label_3 = new wxStaticText(notebook_1_gen, -1, wxT("Amplitude [0..-60dB]: "));
  slide_l_am = new wxSlider(notebook_1_gen, ID_GENLAMP, 0, -60, 0);

  checkbox_r_en = new wxCheckBox(notebook_1_gen, ID_GENRENB, wxT("Enable Output"));
  label_1_copy_1 = new wxStaticText(notebook_1_gen, -1, wxT("Waveform: "));
  const wxString choice_r_wav_choices[] = {wxT("Sine"), wxT("Rectangular"), wxT("Saw"),
                                           wxT("Triangle"), wxT("Wh-Noise")};
  choice_r_wav = new wxChoice(notebook_1_gen, ID_GENSHP_R, wxDefaultPosition, wxDefaultSize, 5,
                              choice_r_wav_choices, 0);
  label_2_copy_1 = new wxStaticText(notebook_1_gen, -1, wxT("Frequency [20..20000Hz]: "));
  slide_r_fr = new wxSlider(notebook_1_gen, ID_GENRFREQ, 80, 0, 200);
  label_3_copy_1 = new wxStaticText(notebook_1_gen, -1, wxT("Amplitude [0..-60dB]: "));
  slide_r_am = new wxSlider(notebook_1_gen, ID_GENRAMP, 0, -60, 0);
  button_gen_start = new wxToggleButton(notebook_1_gen, ID_GENSTART, wxT("Start"));

  checkbox_gen_sync = new wxCheckBox(notebook_1_gen, ID_GENSYNC, wxT("L and R are synchronized"));
  label_gen_sync =
      new wxStaticText(notebook_1_gen, -1, wxT("Phase between L and R [0..360 degrees]: "));
  text_gen_sync = new wxTextCtrl(notebook_1_gen, ID_GENPHASE, wxT("0"), wxDefaultPosition,
                                 wxDefaultSize, wxTE_PROCESS_ENTER);

  txt_freq_l = new wxTextCtrl(notebook_1_gen, ID_TXT_FREQ_L, wxT("315.0"), wxDefaultPosition,
                              wxDefaultSize, wxTE_PROCESS_ENTER);
  txt_freq_r = new wxTextCtrl(notebook_1_gen, ID_TXT_FREQ_R, wxT("315.0"), wxDefaultPosition,
                              wxDefaultSize, wxTE_PROCESS_ENTER);

  /* oscilloscope panel */
  window_1 = new CtrlOScope(notebook_1_osc, _T(""), _T(""), 1);
  label_5_copy = new wxStaticText(notebook_1_osc, -1, wxT("X Scale [samples/div]: "));
  const wxString choice_osc_l_swp_copy_choices[] = {
      wxT("10"),   wxT("20"),   wxT("50"),   wxT("100"),   wxT("200"),   wxT("500"),
      wxT("1000"), wxT("2000"), wxT("5000"), wxT("10000"), wxT("20000"), wxT("50000"),
  };
  choice_osc_l_swp_copy = new wxChoice(notebook_1_osc, ID_OSCXSCALE, wxDefaultPosition,
                                       wxDefaultSize, 12, choice_osc_l_swp_copy_choices, 0);
  label_6 = new wxStaticText(notebook_1_osc, -1, wxT("Res [V/div]: "));
  const wxString choice_osc_l_res_choices[] = {
      wxT("1"),    wxT("2"),    wxT("4"),     wxT("8"),    wxT("16"),   wxT("32"),
      wxT("64"),   wxT("128"),  wxT("256"),   wxT("512"),  wxT("1024"), wxT("2048"),
      wxT("4096"), wxT("8192"), wxT("16384"), wxT("32768")};
  choice_osc_l_res = new wxChoice(notebook_1_osc, -1, wxDefaultPosition, wxDefaultSize, 16,
                                  choice_osc_l_res_choices, 0);
  label_7 = new wxStaticText(notebook_1_osc, -1, wxT("Offset [V/div]: "));
  const wxString choice_osc_l_off_choices[] = {wxT("100"), wxT("80"),  wxT("60"),  wxT("40"),
                                               wxT("20"),  wxT("0"),   wxT("-20"), wxT("-40"),
                                               wxT("-60"), wxT("-80"), wxT("-100")};
  choice_osc_l_off = new wxChoice(notebook_1_osc, -1, wxDefaultPosition, wxDefaultSize, 11,
                                  choice_osc_l_off_choices, 0);

  label_6_copy = new wxStaticText(notebook_1_osc, -1, wxT("Res [V/div]: "));
  const wxString choice_osc_l_res_copy_choices[] = {
      wxT("1"),    wxT("2"),    wxT("4"),     wxT("8"),    wxT("16"),   wxT("32"),
      wxT("64"),   wxT("128"),  wxT("256"),   wxT("512"),  wxT("1024"), wxT("2048"),
      wxT("4096"), wxT("8192"), wxT("16384"), wxT("32768")};
  choice_osc_l_res_copy = new wxChoice(notebook_1_osc, -1, wxDefaultPosition, wxDefaultSize, 16,
                                       choice_osc_l_res_copy_choices, 0);
  label_7_copy = new wxStaticText(notebook_1_osc, -1, wxT("Offset [V/div]: "));
  const wxString choice_osc_l_off_copy_choices[] = {wxT("100"), wxT("80"),  wxT("60"),  wxT("40"),
                                                    wxT("20"),  wxT("0"),   wxT("-20"), wxT("-40"),
                                                    wxT("-60"), wxT("-80"), wxT("-100")};
  choice_osc_l_off_copy = new wxChoice(notebook_1_osc, -1, wxDefaultPosition, wxDefaultSize, 11,
                                       choice_osc_l_off_copy_choices, 0);

  button_osc_start = new wxToggleButton(notebook_1_osc, ID_OSCSTART, wxT("Start"));

  button_autocalibrate = new wxButton(notebook_1_osc, ID_AUTOCAL, wxT("Auto Cal"));
  /// triggering control
  label_8 = new wxStaticText(notebook_1_osc, -1, wxT("Trigger: "));
  const wxString choice_osc_trig_source_choices[] = {wxT("Off"), wxT("Left channel"),
                                                     wxT("Right channel")};
  choice_osc_trig_source = new wxChoice(notebook_1_osc, ID_OSCLTRIG, wxDefaultPosition,
                                        wxDefaultSize, 3, choice_osc_trig_source_choices, 0);
  label_8_copy = new wxStaticText(notebook_1_osc, -1, wxT("Trigger edge: "));
  const wxString choice_osc_trig_edge_choices[] = {wxT("Rising edge"), wxT("Falling edge")};
  choice_osc_trig_edge = new wxChoice(notebook_1_osc, ID_OSCRTRIG, wxDefaultPosition, wxDefaultSize,
                                      2, choice_osc_trig_edge_choices, 0);

  // Spectrum analyzer
  label_5 = new wxStaticText(notebook_1_spe, -1, wxT("FFT Window Type:"));
  const wxString choice_fft_choices[] = {wxT("Rect"), wxT("Hanning"), wxT("Blackman"),
                                         wxT("BlackHarr")};
  choice_fft = new wxChoice(notebook_1_spe, ID_FFTWINDOW, wxDefaultPosition, wxDefaultSize, 4,
                            choice_fft_choices, 0);

  label_9 = new wxStaticText(notebook_1_spe, -1, wxT("Number of samples:"));
  const wxString choice_fftlength_choices[] = {wxT("128"),   wxT("256"),  wxT("512"),  wxT("1024"),
                                               wxT("2048"),  wxT("4096"), wxT("8192"), wxT("16384"),
                                               wxT("32768"), wxT("65536")};
  choice_fftlength = new wxChoice(notebook_1_spe, ID_FFTLENGTH, wxDefaultPosition, wxDefaultSize,
                                  10, choice_fftlength_choices, 0);

  label_rx = new wxStaticText(notebook_1_spe, -1, wxT("Freq:"));
  const wxString choice_fftry_choices[] = {wxT("20-20k"), wxT("10-100k")};
  choice_fftrx = new wxChoice(notebook_1_spe, ID_FFTWINDOW, wxDefaultPosition, wxDefaultSize, 2,
                              choice_fftry_choices, 0);

  window_1_spe = new CtrlOScope(notebook_1_spe, _T("Hz"), _T("dB"), 1);
  button_spe_start = new wxToggleButton(notebook_1_spe, ID_SPANSTART, wxT("Start"));

  // Frequency response
  label_1_frm = new wxStaticText(notebook_1_frm, -1, wxT("Number of points (max 120):"));
  text_ctrl1_frm = new wxTextCtrl(notebook_1_frm, -1, wxT("24"));
  label_2_frm = new wxStaticText(notebook_1_frm, -1, wxT("-"));
  text_ctrl2_frm = new wxTextCtrl(notebook_1_frm, -1, wxT("-"));
  button_frm_start = new wxToggleButton(notebook_1_frm, ID_FRMSTART, wxT("Start"));
  window_1_frm = new CtrlOScope(notebook_1_frm, _T("Hz"), _T("dB"), 1);

  set_properties();
  do_layout();
  // end wxGlade
  set_custom_props();
}

void MainFrame::set_properties() {
  // begin wxGlade: MainFrame::set_properties
  SetTitle(wxT("AUDio MEasurement System"));
  int frame_1_statusbar_widths[] = {-1};
  frame_1_statusbar->SetStatusWidths(1, frame_1_statusbar_widths);
  frame_1_statusbar->SetStatusText("AUDio MEasurement System - version " AUDMES_VERSION_STRING);
  choice_l_wav->SetSelection(0);
  choice_r_wav->SetSelection(0);
  choice_osc_l_swp_copy->SetSelection(0);
  choice_osc_l_res->SetSelection(0);
  choice_osc_l_off->SetSelection(0);
  choice_osc_trig_source->SetSelection(0);
  choice_osc_l_res_copy->SetSelection(0);
  choice_osc_l_off_copy->SetSelection(0);
  choice_osc_trig_edge->SetSelection(0);
  choice_fft->SetSelection(1);
  choice_fftlength->SetSelection(4);
  choice_fftrx->SetSelection(1);
  // end wxGlade
}

void MainFrame::do_layout() {
  // begin wxGlade: MainFrame::do_layout
  wxBoxSizer* sizer_1 = new wxBoxSizer(wxVERTICAL);
  wxBoxSizer* sizer_9_copy = new wxBoxSizer(wxVERTICAL);
  wxBoxSizer* sizer_10_copy = new wxBoxSizer(wxVERTICAL);
  wxBoxSizer* sizer_17 = new wxBoxSizer(wxHORIZONTAL);
  wxBoxSizer* sizer_9 = new wxBoxSizer(wxVERTICAL);
  wxBoxSizer* sizer_10 = new wxBoxSizer(wxHORIZONTAL);
  wxBoxSizer* sizer_11 = new wxBoxSizer(wxVERTICAL);
  wxStaticBoxSizer* sizer_12_copy = new wxStaticBoxSizer(sizer_12_copy_staticbox, wxVERTICAL);
  wxBoxSizer* sizer_16_copy = new wxBoxSizer(wxHORIZONTAL);
  wxBoxSizer* sizer_15_copy = new wxBoxSizer(wxHORIZONTAL);
  wxBoxSizer* sizer_14_copy = new wxBoxSizer(wxHORIZONTAL);
  wxStaticBoxSizer* sizer_12 = new wxStaticBoxSizer(sizer_12_staticbox, wxVERTICAL);
  wxBoxSizer* sizer_16 = new wxBoxSizer(wxHORIZONTAL);
  wxBoxSizer* sizer_15 = new wxBoxSizer(wxHORIZONTAL);
  wxBoxSizer* sizer_14 = new wxBoxSizer(wxHORIZONTAL);
  wxBoxSizer* sizer_13 = new wxBoxSizer(wxHORIZONTAL);
  wxBoxSizer* sizer_2 = new wxBoxSizer(wxVERTICAL);
  wxBoxSizer* sizer_3 = new wxBoxSizer(wxHORIZONTAL);
  wxStaticBoxSizer* sizer_4_copy = new wxStaticBoxSizer(sizer_4_copy_staticbox, wxVERTICAL);
  wxStaticBoxSizer* sizer_4 = new wxStaticBoxSizer(sizer_4_staticbox, wxVERTICAL);
  wxBoxSizer* sizer_gen_sync = new wxBoxSizer(wxHORIZONTAL);
  wxBoxSizer* sizer_gen_sync2 = new wxBoxSizer(wxHORIZONTAL);

  wxBoxSizer* sizer_9_frm = new wxBoxSizer(wxVERTICAL);
  wxBoxSizer* sizer_10_frm = new wxBoxSizer(wxVERTICAL);
  wxBoxSizer* sizer_17_frm = new wxBoxSizer(wxHORIZONTAL);

  wxBoxSizer* sizer_txtfreql = new wxBoxSizer(wxVERTICAL);
  wxBoxSizer* sizer_txtfreqr = new wxBoxSizer(wxVERTICAL);

  wxFlexGridSizer* sizer_GenL = new wxFlexGridSizer(3, 2, 5, 5);
  wxFlexGridSizer* sizer_GenR = new wxFlexGridSizer(3, 2, 5, 5);

  // generator
  sizer_4->Add(checkbox_l_en, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_GenL->Add(label_1, 0, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 5);
  sizer_GenL->Add(choice_l_wav, 1, wxALL | wxEXPAND, 5);
  sizer_GenL->Add(label_2, 0, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 5);
  sizer_txtfreql->Add(slide_l_fr, 0, wxEXPAND, 5);
  sizer_txtfreql->Add(txt_freq_l, wxALL | wxEXPAND, 5);
  sizer_GenL->Add(sizer_txtfreql, 1, wxALL | wxEXPAND, 5);
  sizer_GenL->Add(label_3, 0, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 5);
  sizer_GenL->Add(slide_l_am, 1, wxEXPAND, 5);
  sizer_4->Add(sizer_GenL, 0, wxALL | wxEXPAND, 5);
  sizer_3->Add(sizer_4, 0, wxALL | wxEXPAND, 5);

  sizer_4_copy->Add(checkbox_r_en, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
                    5);
  sizer_GenR->Add(label_1_copy_1, 0, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 5);
  sizer_GenR->Add(choice_r_wav, 1, wxALL | wxEXPAND, 5);
  sizer_GenR->Add(label_2_copy_1, 0, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 5);
  sizer_txtfreqr->Add(slide_r_fr, 0, wxEXPAND, 5);
  sizer_txtfreqr->Add(txt_freq_r, wxALL | wxEXPAND, 5);
  sizer_GenR->Add(sizer_txtfreqr, 1, wxALL | wxEXPAND, 5);
  sizer_GenR->Add(label_3_copy_1, 0, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 5);
  sizer_GenR->Add(slide_r_am, 1, wxEXPAND, 5);
  sizer_4_copy->Add(sizer_GenR, 0, wxALL | wxEXPAND, 5);
  sizer_3->Add(sizer_4_copy, 0, wxALL | wxEXPAND, 5);

  sizer_2->Add(sizer_3, 0, wxALIGN_CENTER_HORIZONTAL, 0);
  sizer_gen_sync->Add(checkbox_gen_sync, 0,
                      wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_gen_sync2->Add(label_gen_sync, 0,
                       wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_gen_sync2->Add(text_gen_sync, 0,
                       wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_2->Add(sizer_gen_sync, 0, wxALIGN_CENTER_HORIZONTAL, 0);
  sizer_2->Add(sizer_gen_sync2, 0, wxALIGN_CENTER_HORIZONTAL, 0);
  sizer_2->Add(button_gen_start, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  notebook_1_gen->SetAutoLayout(true);
  notebook_1_gen->SetSizer(sizer_2);
  sizer_2->Fit(notebook_1_gen);
  sizer_2->SetSizeHints(notebook_1_gen);

  // oscilloscope
  sizer_10->Add(window_1, 1, wxEXPAND, 0);
  sizer_13->Add(label_5_copy, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_13->Add(choice_osc_l_swp_copy, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_11->Add(sizer_13, 0, wxALIGN_CENTER_HORIZONTAL, 0);

  sizer_14->Add(label_6, 0, wxLEFT | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_14->Add(5, 5, 1, 0, 0);
  sizer_14->Add(choice_osc_l_res, 0, wxALL, 5);
  sizer_12->Add(sizer_14, 1, wxEXPAND, 0);
  sizer_15->Add(label_7, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
                5);
  sizer_15->Add(5, 5, 1, 0, 0);
  sizer_15->Add(choice_osc_l_off, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  sizer_12->Add(sizer_15, 1, wxEXPAND, 0);
  sizer_11->Add(sizer_12, 0, wxALL | wxEXPAND, 5);

  sizer_11->Add(button_autocalibrate, 0,
                wxLEFT | wxRIGHT | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
                5);  // autocalibrate

  sizer_14_copy->Add(label_6_copy, 0, wxLEFT | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
                     5);
  sizer_14_copy->Add(5, 5, 1, 0, 0);
  sizer_14_copy->Add(choice_osc_l_res_copy, 0, wxALL, 5);
  sizer_12_copy->Add(sizer_14_copy, 1, wxEXPAND, 0);
  sizer_15_copy->Add(label_7_copy, 0,
                     wxLEFT | wxRIGHT | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_15_copy->Add(5, 5, 1, 0, 0);
  sizer_15_copy->Add(choice_osc_l_off_copy, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  sizer_12_copy->Add(sizer_15_copy, 1, wxEXPAND, 0);
  sizer_11->Add(sizer_12_copy, 0, wxALL | wxEXPAND, 5);

  sizer_16->Add(label_8, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_16->Add(5, 5, 1, 0, 0);
  sizer_16->Add(choice_osc_trig_source, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_11->Add(sizer_16, 1, wxEXPAND, 0);

  sizer_16_copy->Add(label_8_copy, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_16_copy->Add(5, 5, 1, 0, 0);
  sizer_16_copy->Add(choice_osc_trig_edge, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_11->Add(sizer_16_copy, 1, wxEXPAND, 0);

  sizer_10->Add(sizer_11, 0, 0, 0);
  sizer_9->Add(sizer_10, 1, wxEXPAND, 0);
  sizer_9->Add(button_osc_start, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  notebook_1_osc->SetAutoLayout(true);
  notebook_1_osc->SetSizer(sizer_9);
  sizer_9->Fit(notebook_1_osc);
  sizer_9->SetSizeHints(notebook_1_osc);

  // analyzer
  sizer_17->Add(label_5, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_17->Add(choice_fft, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_17->Add(20, 20, 0, 0, 0);
  sizer_17->Add(label_9, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_17->Add(choice_fftlength, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
                5);
  sizer_17->Add(20, 20, 0, 0, 0);
  sizer_17->Add(label_rx, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_17->Add(choice_fftrx, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_17->Add(20, 20, 0, 0, 0);
  sizer_10_copy->Add(sizer_17, 0, wxEXPAND, 0);
  sizer_10_copy->Add(window_1_spe, 1, wxEXPAND, 0);
  sizer_9_copy->Add(sizer_10_copy, 1, wxEXPAND, 0);
  sizer_9_copy->Add(button_spe_start, 0,
                    wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  notebook_1_spe->SetAutoLayout(true);
  notebook_1_spe->SetSizer(sizer_9_copy);
  sizer_9_copy->Fit(notebook_1_spe);
  sizer_9_copy->SetSizeHints(notebook_1_spe);

  // frequency response
  sizer_17_frm->Add(label_1_frm, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_17_frm->Add(text_ctrl1_frm, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
                    5);
  sizer_17_frm->Add(20, 20, 0, 0, 0);
  sizer_17_frm->Add(label_2_frm, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
  sizer_17_frm->Add(text_ctrl2_frm, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
                    5);
  sizer_17_frm->Add(20, 20, 0, 0, 0);
  sizer_10_frm->Add(sizer_17_frm, 0, wxEXPAND, 0);
  sizer_10_frm->Add(window_1_frm, 1, wxEXPAND, 0);
  sizer_9_frm->Add(sizer_10_frm, 1, wxEXPAND, 0);
  sizer_9_frm->Add(button_frm_start, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,
                   5);
  notebook_1_frm->SetAutoLayout(true);
  notebook_1_frm->SetSizer(sizer_9_frm);
  sizer_9_frm->Fit(notebook_1_frm);
  sizer_9_frm->SetSizeHints(notebook_1_frm);

  // main notebook
  notebook_1->AddPage(notebook_1_gen, wxT("Generator"));
  notebook_1->AddPage(notebook_1_osc, wxT("Oscilloscope"));
  notebook_1->AddPage(notebook_1_spe, wxT("Spectrum Analyzer"));
  notebook_1->AddPage(notebook_1_frm, wxT("Frequency Response"));
  sizer_1->Add(notebook_1, 1, wxEXPAND, 0);
  SetAutoLayout(true);
  SetSizer(sizer_1);
  sizer_1->Fit(this);
  sizer_1->SetSizeHints(this);
  Layout();
  // end wxGlade
}

void MainFrame::set_custom_props() {
  double sweep_div;

#ifdef __WXMSW__
  SetIcon(wxICON(AudMeSIcon));
#endif

#ifdef __LINUX__
#include "audmes.xpm"
  SetIcon(wxICON(audmes));
#endif

  m_SamplingFreq = 44100;

  choice_osc_l_res->SetSelection(15);
  choice_osc_l_res_copy->SetSelection(15);
  choice_osc_l_off->SetSelection(5);
  choice_osc_l_off_copy->SetSelection(5);

  choice_osc_l_swp_copy->GetString(choice_osc_l_swp_copy->GetCurrentSelection())
      .ToDouble(&sweep_div);
  m_OscBufferLength = (long)(10 * sweep_div);

  /* oscilloscope */
  window_1->SetXRange(0, sweep_div * 10, 0);
  window_1->SetYRange(-1, 1, 0, 1);
  window_1->SetNumOfVerticals(10);

  /* analyzer */
  window_1_spe->SetXRange(10, 100000, 1);
  window_1_spe->SetYRange(-100, 0, 0, 1);
  window_1_spe->SetFsample(m_SamplingFreq);

  /* freq response */
  window_1_frm->SetXRange(20, 20000, 1);
  window_1_frm->SetYRange(-80, 0, 0, 1);

  g_OscBufferChanged = 0;
  g_SpeBufferChanged = 0;

  frm_running = 0;
  frm_measure = 0;
  frm_istep = 0;

  m_configfilename = wxT("");

  m_timer = new wxTimer(this, ID_TIMERID);
  m_timer->Start(200);

  choice_fftlength->GetString(choice_fftlength->GetCurrentSelection()).ToDouble(&sweep_div);
  m_SpeBufferLength = (long)(sweep_div);

  m_RWAudio = new RWAudio();

  int ret = 0;

#ifdef XSCALEINTIME
  ret = m_RWAudio->InitSnd((long int)(1.5 * m_OscBufferLength * m_SamplingFreq / 10000 + 10),
                           m_SpeBufferLength, m_rtinfo);
#else
  ret = m_RWAudio->InitSnd((long int)(1.5 * m_OscBufferLength), m_SpeBufferLength, m_rtinfo);
#endif

  if (ret)
    wxMessageBox(_T("Sound card issue:\n\nPlease check\nTools -> Audio interface Configuration\n"),
                 _T("Alert"), wxICON_INFORMATION | wxOK);
}

void MainFrame::OnAboutClick(wxCommandEvent& WXUNUSED(event)) {
  wxString s;
  s << wxT("AUDio MEasurement System - version ") << AUDMES_VERSION_STRING
    << wxT("\nVaclav Peroutka - vaclavpe@seznam.cz\n\n")
    << wxT("Project page: https://sourceforge.net/projects/audmes/\n\n") << m_rtinfo;

  wxMessageBox(s, _T("About application"), wxICON_INFORMATION | wxOK);
}

void MainFrame::OnExitClick(wxCommandEvent& WXUNUSED(event)) { Close(); }

void MainFrame::OnClose(wxCloseEvent& WXUNUSED(event)) {
  m_timer->Stop();
  Destroy();
}

void MainFrame::OnOpenClick(wxCommandEvent& WXUNUSED(event)) {
  wxMessageBox(wxT("Not yet implemented"), _T("About application"), wxICON_INFORMATION | wxOK);
}

void MainFrame::OnSaveClick(wxCommandEvent& WXUNUSED(event)) {
  wxMessageBox(wxT("Not yet implemented"), _T("About application"), wxICON_INFORMATION | wxOK);
}

void MainFrame::OnSaveAsClick(wxCommandEvent& WXUNUSED(event)) {
  wxMessageBox(wxT("Not yet implemented"), _T("About application"), wxICON_INFORMATION | wxOK);
}

void MainFrame::OnSaveFRM(wxCommandEvent& WXUNUSED(event)) {
  wxFileDialog saveFileDialog(this, _("Save frequency response file"), "", "",
                              "CSV files (*.csv)|*.csv", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
  if (saveFileDialog.ShowModal() == wxID_CANCEL) return;

  std::ofstream frm;
  frm.open(saveFileDialog.GetPath().mb_str(), std::ios::trunc);
  if (!frm.is_open()) {
    wxLogError("Cannot save current contents in file '%s'.", saveFileDialog.GetPath());
    return;
  }
  frm << "Hz"
      << ","
      << "GainL"
      << ","
      << "GainR" << std::endl;
  for (unsigned int i = 0; i < m_frm_freqs.GetCount(); i++) {
    frm << m_frm_freqs[i] << "," << m_frm_lgains[i] << "," << m_frm_rgains[i] << std::endl;
  }
  frm.close();
}

void MainFrame::OnLoadFRM(wxCommandEvent& WXUNUSED(event)) {
  if (0 /* ...current content has not been saved... */) {
    if (wxMessageBox(_("Current content has not been saved! Proceed?"), _("Please confirm"),
                     wxICON_QUESTION | wxYES_NO, this) == wxNO)
      return;
  }

  wxFileDialog openFileDialog(this, _("Open frequency response file"), "", "",
                              "CSV files (*.csv)|*.csv", wxFD_OPEN | wxFD_FILE_MUST_EXIST);
  if (openFileDialog.ShowModal() == wxID_CANCEL) return;

  io::CSVReader<3> in(openFileDialog.GetPath());
  in.read_header(io::ignore_extra_column, "Hz", "GainL", "GainR");

  m_frm_freqs.Clear();
  m_frm_lgains.Clear();
  m_frm_rgains.Clear();
  double hz;
  double gainl, gainr;
  while (in.read_row(hz, gainl, gainr)) {
    m_frm_freqs.Add(hz);
    m_frm_lgains.Add(gainl);
    m_frm_rgains.Add(gainr);
  }
  DrawFreqResponse();
}

void MainFrame::OnAutoCalClick(wxCommandEvent& WXUNUSED(event)) {
  if (button_osc_start->GetValue()) {
    float minValueL = 1;
    float maxValueL = -1;
    float minValueR = 1;
    float maxValueR = -1;

    for (unsigned long int i = 0; i < m_OscBufferLength; i++) {
      if (minValueL > g_OscBuffer_Left[i]) minValueL = g_OscBuffer_Left[i];
      if (maxValueL < g_OscBuffer_Left[i]) maxValueL = g_OscBuffer_Left[i];
      if (minValueR > g_OscBuffer_Right[i]) minValueR = g_OscBuffer_Right[i];
      if (maxValueR < g_OscBuffer_Right[i]) maxValueR = g_OscBuffer_Right[i];
    }
    int diff = maxValueL - minValueL;
    float lgdiff = log(diff) / log(2);
    if (lgdiff > 15) lgdiff = 15;
    choice_osc_l_res->SetSelection((int)lgdiff);

    diff = maxValueR - minValueR;
    lgdiff = log(diff) / log(2);
    if (lgdiff > 15) lgdiff = 15;
    choice_osc_l_res_copy->SetSelection((int)lgdiff);

    // then center the wave - peaks must be located symetrically from the centre

  } else {
    wxMessageBox(wxT("Please start recording"), _T("Could not auto calibrate"),
                 wxICON_INFORMATION | wxOK);
  }
}

static const int frm_low = 20;

void MainFrame::CalcFreqResponse() {
  /* periodically called by OnTimer
   * delays for measuring work by waiting for the next call
   */
  if (frm_running) {
    if (frm_istep <= (int)frm_ipoints) {
      float freq = frm_low * pow(10.0, 3.0 * frm_istep / frm_ipoints);

      if (0 == frm_measure) {
        // play new frequency e.g. from 20Hz to 20kHz
        m_RWAudio->PlaySetGenerator(freq, freq, 0, 0, pow(10, slide_l_am->GetValue() / 20.0),
                                    pow(10, slide_r_am->GetValue() / 20.0));
        wxString bla;
        bla.Printf(wxT("Frequency : %.1f "), freq);
        window_1_frm->ShowUserText(bla, 100, 20);
      }
      if (1 == frm_measure) g_SpeBufferChanged = 0;  // now get audio data

      if (g_SpeBufferChanged && frm_measure > 1) {
        // new audio data has arrived
        double l_rms = 0;
        double r_rms = 0;
        // compute RMS value in the grabbed wave and store it as a result
        for (unsigned long int ii = 0; ii < m_SpeBufferLength; ii++) {
          l_rms += g_SpeBuffer_Left[ii] * g_SpeBuffer_Left[ii];
          r_rms += g_SpeBuffer_Right[ii] * g_SpeBuffer_Right[ii];
        }
        m_frm_freqs.Add(freq);
        m_frm_lgains.Add(sqrt(l_rms / m_SpeBufferLength));
        m_frm_rgains.Add(sqrt(r_rms / m_SpeBufferLength));
        frm_measure = -1;  // zero after increment
        frm_istep++;
      }
      frm_measure++;
    } else {
      frm_running = false;
      window_1_frm->ShowUserText(wxString(""), 0, 0);
      button_frm_start->SetValue(false);
      button_frm_start->SetLabel(_T("Start"));
      SendGenSettings();  // stop generator
    }
  }
}

void MainFrame::DrawFreqResponse(void) {
  wxArrayDouble left, right;

  left.Clear();
  right.Clear();
  /* make the linear interpolation of points for each 1Hz */
  /* initial values are the first step */
  double upfreq = frm_low - 1;
  double lupgain = 0.00000001;
  double rupgain = 0.00000001;
  double botfreq = 0;
  double lbotgain = 0.00000001;
  double rbotgain = 0.00000001;
  unsigned long int arrpointer = 0;
  for (unsigned long int i = 0; i < m_SamplingFreq / 2; i++) {
    if (i > (unsigned long int)upfreq) {
      /* next value from arrays */
      if ((arrpointer) >= m_frm_freqs.GetCount()) {
        lbotgain = lupgain;
        rbotgain = rupgain;
        lupgain = rupgain = 0.00000001;
      } else {
        botfreq = upfreq;
        lbotgain = lupgain;
        rbotgain = rupgain;
        upfreq = m_frm_freqs[arrpointer];
        lupgain = m_frm_lgains[arrpointer];
        rupgain = m_frm_rgains[arrpointer];
        arrpointer++;
      }
    }
    double tmpval = lbotgain + (lupgain - lbotgain) / (upfreq - botfreq) * (1.0 * i - botfreq);
    left.Add(20.0 * log10(tmpval));
    tmpval = rbotgain + (rupgain - rbotgain) / (upfreq - botfreq) * (1.0 * i - botfreq);
    right.Add(20.0 * log10(tmpval));
  }
  window_1_frm->SetTrack(left);
  window_1_frm->SetTrack2(right);
}

void MainFrame::DrawOscilloscope(void) {
  wxArrayDouble ardbl, ardbl2;
  unsigned long int i;
  double trigger_edge;
  double trigger_level;
  double hysteresis_level = 10;

  double range_div = pow(2, choice_osc_l_res->GetCurrentSelection() - 15);
  double shft_val = 20.0 * (choice_osc_l_off->GetCurrentSelection() - 5) / 128.0;
  double range_div2 = pow(2, choice_osc_l_res_copy->GetCurrentSelection() - 15);
  double shft_val2 = 20.0 * (choice_osc_l_off_copy->GetCurrentSelection() - 5) / 128.0;
  i = 0;

  // triggering - re-done a little bit, more or less ...
  trigger_edge = (0 == choice_osc_trig_edge->GetCurrentSelection()) ? 1.0 : -1.0;
  trigger_level = 0.0;  // later it will be maybe settable in the control
  switch (choice_osc_trig_source->GetCurrentSelection()) {
    case 1:
      // left channel - look for the value under hysteresis point and then over 0
      hysteresis_level =
          range_div / 10.0;  // later the hysteresis percent will be maybe settable in the control
      while (i < m_OscBufferLength) {
        if ((trigger_level - hysteresis_level) > (trigger_edge * g_OscBuffer_Left[i])) {
          break;
        }
        i++;
      }
      while (i < m_OscBufferLength) {
        if (trigger_level < (trigger_edge * g_OscBuffer_Left[i])) {
          break;
        }
        i++;
      }

      break;
    case 2:
      // right channel
      hysteresis_level =
          range_div2 / 10.0;  // later the hysteresis percent will be maybe settable in the control
      while (i < m_OscBufferLength) {
        if ((trigger_level - hysteresis_level) > (trigger_edge * g_OscBuffer_Right[i])) {
          break;
        }
        i++;
      }
      while (i < m_OscBufferLength) {
        if (trigger_level < (trigger_edge * g_OscBuffer_Right[i])) {
          break;
        }
        i++;
      }
      break;
    default:
      // no trigger
      break;
  }

  // Add the frequency measurement feature - FFT
  {
    int nsampl;

    m_OscBufferLength > 16384 ? nsampl = 16384 : nsampl = m_OscBufferLength;
    nsampl = (int)(pow(2, (int)(log10(nsampl) / log10(2))));

    if (nsampl > 0) {
      double* realin = (double*)malloc(nsampl * sizeof(double));
      double* realout = (double*)malloc(nsampl * sizeof(double));
      double* imagout = (double*)malloc(nsampl * sizeof(double));

      for (int i = 0; i < nsampl; i++) {
        realin[i] = g_OscBuffer_Left[i] / 2048.0;
      }
      if (fft_double(nsampl, 0, realin, NULL, realout, imagout)) {
        int i;
        double dmax = realout[0] * realout[0] + imagout[0] * imagout[0];
        int imax = 0;
        for (i = 1; i < nsampl / 2; i++) {
          double dval = realout[i] * realout[i] + imagout[i] * imagout[i];
          if (dval > dmax) {
            dmax = dval;
            imax = i;
          }
        }
#ifdef __WXMSW__
        if (wxTheClipboard->Open()) {
          wxString listOfData = "Frequency out of range";

          if ((imax > 9) && (imax + 9 < nsampl / 2)) {
            listOfData = "";
            for (int j = imax - 9; j < imax + 9; j++) {
              listOfData +=
                  wxString::Format("%.1f\t%f\n", 1.0 * j * m_SamplingFreq / nsampl,
                                   sqrt(realout[j] * realout[j] + imagout[j] * imagout[j]));
            }
          }
          wxTheClipboard->SetData(new wxTextDataObject(listOfData));
          wxTheClipboard->Close();
        }

#endif
        // recompute the frequency
        double freq = 1.0 * imax * m_SamplingFreq / nsampl;
        wxString bla;
        bla.Printf(wxT("Frequency : %.1f "), freq);
        window_1->ShowUserText(bla, 100, 20);
      }
      free(realin);
      free(realout);
      free(imagout);
    }
  }

  // here it is necessary to recompute the length of data packet to show just micro/miliseconds and
  // not samples
  unsigned long int finalBufferPoint =
      i + m_OscBufferLength;  // wrapped exactly for the OScopeCtrl X range
  if (finalBufferPoint > 1.5 * m_OscBufferLength) {
    finalBufferPoint = (unsigned long)(1.5 * m_OscBufferLength);
  }

#ifdef XSCALEINTIME
  // new copy function for microseconds/div XScale
  while (i < finalBufferPoint) {
    long int newpos = (long int)(i * m_SamplingFreq / 10000.f);
    ardbl.Add(g_OscBuffer_Left[newpos] / range_div - shft_val);
    ardbl2.Add(g_OscBuffer_Right[newpos] / range_div2 - shft_val2);
    i++;
  }
#else
  // old copy function for samples/div XScale
  while (i < finalBufferPoint) {
    ardbl.Add(g_OscBuffer_Left[i] / range_div - shft_val);
    ardbl2.Add(g_OscBuffer_Right[i] / range_div2 - shft_val2);
    i++;
  }
#endif

  window_1->SetTrack(ardbl);
  window_1->SetTrack2(ardbl2);
}

void MainFrame::DrawSpectrum(void) {
  double *realin, *realout, *imagout, *windowf;
  int nsampl = m_SpeBufferLength;
  realin = (double*)malloc(nsampl * sizeof(double));
  realout = (double*)malloc(nsampl * sizeof(double));
  imagout = (double*)malloc(nsampl * sizeof(double));
  windowf = (double*)malloc(nsampl * sizeof(double));
  wxArrayDouble ardbl;
  wxArrayDouble ardbl2;

  // calculate window
  const double multiplier = 2 * M_PI / nsampl;
  switch (choice_fft->GetCurrentSelection()) {
    case 1:  // Hanning
      for (int i = 0; i < nsampl; i++) {
        windowf[i] = 2 * (0.5 + -0.5 * cos(i * multiplier)) / (double)nsampl;
      }
      break;
    case 2:  // Blackman
      for (int i = 0; i < nsampl; i++) {
        windowf[i] = 2.4 * (0.42 - 0.5 * cos(multiplier * i) + 0.08 * cos(2 * multiplier * i)) /
                     (double)nsampl;
      }
      break;
    case 3:  // Blackman Harris minimum 4 term
      for (int i = 0; i < nsampl; i++) {
        windowf[i] = 2.63 *
                     (0.35875 + -0.48829 * cos(i * multiplier) + 0.14128 * cos(i * multiplier * 2) +
                      -0.01168 * cos(i * multiplier * 3)) /
                     (double)nsampl;
      }
      break;
    default:  // Rectangle
      for (int i = 0; i < nsampl; i++) {
        windowf[i] = 1.0 / (double)nsampl;
      }
      break;
  }

  /*
   * Scale max. amplitude to 1 which is 0 db.
   * We only use use nsampl/2 of the FFT.
   * Compensate with multiplying by 2 = 6dB.
   */
  const double dbscaler = 6;

  double dval = 0.0;
  double dmax = 0.0;
  int imax = 0;

  // left channel
  for (int i = 0; i < nsampl; i++) {
    // copy and apply window
    realin[i] = g_SpeBuffer_Left[i] * windowf[i];
  }

  if (fft_double(nsampl, 0, realin, NULL, realout, imagout)) {
    realout[0] = imagout[0] = 0;  // remove DC
    /* show only half FFT */
    for (int i = 0; i < nsampl / 2; i++) {
      dval = realout[i] * realout[i] + imagout[i] * imagout[i];
      if (dval > dmax) {
        dmax = dval;
        imax = i;
      }
      ardbl.Add(20.0 * log10(sqrt(dval)) + dbscaler);
    }
  } else {
    /* wrong computation */
    for (int i = 0; i < nsampl / 2; i++) {
      ardbl.Add(-150);
    }
  }

  if (imax > 0) {
    double freq = (double)imax * m_SamplingFreq / nsampl;
    double thdval[10];
    for (int i = 0; i < 10; i++) {
      int j = imax * (i + 1);
      if (j < nsampl / 2)
        thdval[i] = sqrt(realout[j] * realout[j] + imagout[j] * imagout[j]);
      else
        thdval[i] = 0.0;
    }
    double thd = 100 *
                 (thdval[1] + thdval[2] + thdval[3] + thdval[4] + thdval[5] + thdval[6] +
                  thdval[7] + thdval[8] + thdval[9]) /
                 thdval[0];
    wxString freqency;
    freqency.Printf(wxT("Frequency : %.1lf Hz, Magnitude: %.1lf dB, THD : %lf%%"), freq,
                    20.0 * log10(thdval[0]) + dbscaler, thd);
    frame_1_statusbar->SetStatusText(freqency);
  }

  // right channel
  for (int i = 0; i < nsampl; i++) {
    // copy and apply window
    realin[i] = g_SpeBuffer_Right[i] * windowf[i];
  }

  if (fft_double(nsampl, 0, realin, NULL, realout, imagout)) {
    realout[0] = imagout[0] = 0;  // remove DC
    /* show only half FFT */
    for (int i = 0; i < nsampl / 2; i++) {
      ardbl2.Add(20.0 * log10(sqrt(realout[i] * realout[i] + imagout[i] * imagout[i])) + dbscaler);
    }
  } else {
    /* wrong computation */
    for (int i = 0; i < nsampl / 2; i++) {
      ardbl2.Add(-150);
    }
  }

  window_1_spe->SetTrack(ardbl);
  window_1_spe->SetTrack2(ardbl2);
  switch (choice_fftrx->GetCurrentSelection()) {
    case 1:
      window_1_spe->SetXRange(10, 100000, 1);
      break;
    default:
      window_1_spe->SetXRange(20, 20000, 1);
      break;
  }
  free(realin);
  free(realout);
  free(imagout);
  free(windowf);
}

void MainFrame::OnTimer(wxTimerEvent& WXUNUSED(event)) {
  bool refresh = false;
  if (g_OscBufferChanged && button_osc_start->GetValue()) {
    DrawOscilloscope();
    g_OscBufferChanged = 0;
    refresh = true;
  }
  CalcFreqResponse();
  if (button_frm_start->GetValue()) {
    DrawFreqResponse();
    refresh = true;
  }
  if (g_SpeBufferChanged && button_spe_start->GetValue()) {
    DrawSpectrum();
    refresh = true;
    g_SpeBufferChanged = 0;
  }
  if (refresh) {
    Refresh();
    Update();
  }
}

void MainFrame::OnOscXScaleChanged(wxCommandEvent& WXUNUSED(event)) {
  double sweep_div;
  choice_osc_l_swp_copy->GetString(choice_osc_l_swp_copy->GetCurrentSelection())
      .ToDouble(&sweep_div);
  m_OscBufferLength = (long)(10 * sweep_div);
  window_1->SetXRange(0, sweep_div * 10, 0);

  choice_fftlength->GetString(choice_fftlength->GetCurrentSelection()).ToDouble(&sweep_div);
  m_SpeBufferLength = (long)(sweep_div);

#ifdef XSCALEINTIME
  m_RWAudio->ChangeBufLen((unsigned long)(1.5 * m_OscBufferLength * m_SamplingFreq / 10000 + 10),
                          m_SpeBufferLength);  // we need bigger buffer because of synchronization
#else
  m_RWAudio->ChangeBufLen((unsigned long)(1.5 * m_OscBufferLength),
                          m_SpeBufferLength);  // we need bigger buffer because of synchronization
#endif
}

void MainFrame::OnSpanStart(wxCommandEvent& WXUNUSED(event)) {
  //  int buf[4096];

  if (button_spe_start->GetValue()) {
    button_spe_start->SetLabel(_T("Stop"));
  } else {
    button_spe_start->SetLabel(_T("Start"));
  }
}

void MainFrame::OnGenStart(wxCommandEvent& WXUNUSED(event)) {
  if (button_gen_start->GetValue()) {
    button_gen_start->SetLabel(_T("Stop"));
  } else {
    button_gen_start->SetLabel(_T("Start"));
  }
  SendGenSettings();
}

void MainFrame::OnOscStart(wxCommandEvent& WXUNUSED(event)) {
  if (button_osc_start->GetValue()) {
    button_osc_start->SetLabel(_T("Stop"));
  } else {
    button_osc_start->SetLabel(_T("Start"));
  }
}

void MainFrame::OnFrmStart(wxCommandEvent& WXUNUSED(event)) {
  if (button_frm_start->GetValue()) {
    long ip;
    button_frm_start->SetLabel(_T("Stop"));
    wxString tpoints = text_ctrl1_frm->GetValue();
    tpoints.ToLong(&ip, 10);
    if (ip > 120) ip = 120;
    if (ip < 1) ip = 1;
    frm_ipoints = (int)ip;
    frm_istep = 0;

    m_frm_freqs.Clear();
    m_frm_lgains.Clear();
    m_frm_rgains.Clear();

    frm_measure = 0;
    frm_running = true;
  } else {
    frm_running = false;
    button_frm_start->SetValue(false);
    button_frm_start->SetLabel(_T("Start"));
    SendGenSettings();  // stop generator
  }
}

void MainFrame::OnGeneratorChanged(wxCommandEvent& WXUNUSED(event)) {
  if (checkbox_gen_sync->IsChecked()) {
    slide_r_fr->Enable(false);
    label_1_copy_1->Enable(false);
    txt_freq_r->Enable(false);
    choice_r_wav->Enable(false);
    label_2_copy_1->Enable(false);
  } else {
    slide_r_fr->Enable(true);
    choice_r_wav->Enable(true);
    txt_freq_r->Enable(true);
    label_1_copy_1->Enable(true);
    label_2_copy_1->Enable(true);
  }

  if (button_gen_start->GetValue()) {
    SendGenSettings();
  }
}

void MainFrame::OnOscChoiceChanged(wxCommandEvent& WXUNUSED(event)) {}

void MainFrame::OnGenScrollLChanged(wxScrollEvent& WXUNUSED(event)) {
  wxString bla;

  bla.Printf(wxT("%.1f"), floor(20.0 * pow(10.0, 3.0 * slide_l_fr->GetValue() / 200.0)));
  txt_freq_l->SetValue(bla);
  if (button_gen_start->GetValue()) {
    SendGenSettings();
  }
}

void MainFrame::OnGenScrollRChanged(wxScrollEvent& WXUNUSED(event)) {
  wxString bla;

  bla.Printf(wxT("%.1f"), floor(20.0 * pow(10.0, 3.0 * slide_r_fr->GetValue() / 200.0)));
  txt_freq_r->SetValue(bla);
  if (button_gen_start->GetValue()) {
    SendGenSettings();
  }
}

void MainFrame::OnGenScrollChanged(wxScrollEvent& WXUNUSED(event)) {
  wxString bla;

  bla.Printf(wxT("Amplitude: %d dB"), slide_l_am->GetValue());
  label_3->SetLabel(bla);

  bla.Printf(wxT("Amplitude: %d dB"), slide_r_am->GetValue());
  label_3_copy_1->SetLabel(bla);

  if (button_gen_start->GetValue()) {
    SendGenSettings();
  }
}

void MainFrame::SendGenSettings() {
  float freq_l, freq_r;
  double phas2;
  double doubleToFreq;
  float gain_l, gain_r;

  if ((checkbox_l_en->IsChecked()) && (button_gen_start->GetValue())) {
    gain_l = 1.0 * pow(10, slide_l_am->GetValue() / 20.0);
  } else {
    gain_l = 0.0;
  }
  if ((checkbox_r_en->IsChecked()) && (button_gen_start->GetValue())) {
    gain_r = 1.0 * pow(10, slide_r_am->GetValue() / 20.0);
  } else {
    gain_r = 0.0;
  }

  text_gen_sync->GetValue().ToDouble(&phas2);

  txt_freq_l->GetValue().ToDouble(&doubleToFreq);
  freq_l = (float)doubleToFreq;

  txt_freq_r->GetValue().ToDouble(&doubleToFreq);
  freq_r = (float)doubleToFreq;

  int shapeleft = choice_l_wav->GetCurrentSelection();
  int shaperight = choice_r_wav->GetCurrentSelection();

  if (checkbox_gen_sync->IsChecked()) {
    freq_r = freq_l;
    shaperight = shapeleft;
  } else {
    phas2 = 0.0;
  }

  m_RWAudio->PlaySetGenerator(freq_l, freq_r, shapeleft, shaperight, gain_l, gain_r);
  m_RWAudio->PlaySetPhaseDiff(phas2 * 3.14159 / 180.0);  // should be in degrees now
}

void MainFrame::OnSelectSndCard(wxCommandEvent& WXUNUSED(event)) {
  wxArrayString arrplstr, arrrecstr, freqsstr;
  wxString bla;
  unsigned int recdev, pldev;
  RWAudioDevList playDevList;
  RWAudioDevList recordDevList;
  unsigned long int newFrequency = m_SamplingFreq;

  m_RWAudio->GetRWAudioDevices(&playDevList, &recordDevList);

  //   bla = _T("Devices: ");
  //   for (i = 0; i < play.card_name.size(); i++) {
  //       wxString newstr(play.card_name[i].c_str(), wxConvUTF8);
  //       arrplstr.Add(newstr);
  //   }

  //   for (i = 0; i < record.card_name.size(); i++) {
  //       wxString newstr(record.card_name[i].c_str(), wxConvUTF8);
  //       arrrecstr.Add(newstr);
  //   }

  //   for (i = 0; i < freqs.size(); i++) {
  //     if ( 0 !=  freqs[i]) {
  //       freqsstr.Add(wxString::Format(wxT("%ld "),freqs[i]));
  //     }
  //   }

  AudioInterfaceDialog dlg(this);

  dlg.SetDevices(recordDevList, playDevList, m_SamplingFreq);
  if (wxID_OK == dlg.ShowModal()) {
    // poslat nastaveni do RW_AUDIO
    dlg.GetSelectedDevs(&recdev, &pldev, &newFrequency);
    m_RWAudio->SetSndDevices(recdev, pldev, newFrequency);
    // std::cerr << "Frequency changed from " << m_SamplingFreq << "Hz to " << newFrequency  <<
    // "\n";
    m_SamplingFreq = newFrequency;
    window_1_spe->SetFsample(m_SamplingFreq);
    window_1_frm->SetFsample(m_SamplingFreq);
  }
}

void MainFrame::OnTxtFreqLChanged(wxCommandEvent& WXUNUSED(event)) {
  if (button_gen_start->GetValue()) {
    SendGenSettings();
  }
}

void MainFrame::OnTxtFreqRChanged(wxCommandEvent& WXUNUSED(event)) {
  if (button_gen_start->GetValue()) {
    SendGenSettings();
  }
}

class AudMeSApp : public wxApp {
 public:
  bool OnInit();
};

IMPLEMENT_APP(AudMeSApp)

bool AudMeSApp::OnInit() {
  wxInitAllImageHandlers();
  MainFrame* frame_1 = new MainFrame(NULL, -1, wxT(""));
  SetTopWindow(frame_1);
  frame_1->Show();
  return true;
}
