diff --git a/gui/wxmain.cpp b/gui/wxmain.cpp index 1a10b84..64f36e4 100644 --- a/gui/wxmain.cpp +++ b/gui/wxmain.cpp @@ -10,6 +10,7 @@ */ #include "wxmain.hpp" +#include "wxsiggen.hpp" #include #include @@ -29,171 +30,7 @@ #include #include -static const std::array srateValues { - "8 kS/s", - "16 kS/s", - "20 kS/s", - "32 kS/s", - "48 kS/s", - "96 kS/s" -}; -static const std::array srateNums { - 8000, - 16000, - 20000, - 32000, - 48000, - 96000 -}; - -static const char *makefile_text_h7 = R"make( -all: - @arm-none-eabi-g++ -x c++ -Os -fno-exceptions -fno-rtti \ - -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 -mtune=cortex-m7 \ - -nostartfiles \ - -Wl,-Ttext-segment=0x00000000 -Wl,-zmax-page-size=512 -Wl,-eprocess_data_entry \ - $0 -o $0.o - @cp $0.o $0.orig.o - @arm-none-eabi-strip -s -S --strip-unneeded $0.o - @arm-none-eabi-objcopy --remove-section .ARM.attributes \ - --remove-section .comment \ - --remove-section .noinit \ - $0.o - arm-none-eabi-size $0.o -)make"; -static const char *makefile_text_l4 = R"make( -all: - @arm-none-eabi-g++ -x c++ -Os -fno-exceptions -fno-rtti \ - -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mtune=cortex-m4 \ - -nostartfiles \ - -Wl,-Ttext-segment=0x10000000 -Wl,-zmax-page-size=512 -Wl,-eprocess_data_entry \ - $0 -o $0.o - @cp $0.o $0.orig.o - @arm-none-eabi-strip -s -S --strip-unneeded $0.o - @arm-none-eabi-objcopy --remove-section .ARM.attributes \ - --remove-section .comment \ - --remove-section .noinit \ - $0.o - arm-none-eabi-size $0.o -)make"; - -static const char *file_header_h7 = R"cpp( -#include - -using adcsample_t = uint16_t; -constexpr unsigned int SIZE = $0; -adcsample_t *process_data(adcsample_t *samples, unsigned int size); -extern "C" void process_data_entry() -{ - ((void (*)())process_data)(); -} - -constexpr double PI = 3.14159265358979323846L; -__attribute__((naked)) -auto sin(double x) { -asm("vmov.f64 r1, r2, d0;" - "eor r0, r0;" - "svc 1;" - "vmov.f64 d0, r1, r2;" - "bx lr"); -return 0; -} -__attribute__((naked)) -auto cos(double x) { -asm("vmov.f64 r1, r2, d0;" - "mov r0, #1;" - "svc 1;" - "vmov.f64 d0, r1, r2;" - "bx lr"); -return 0; -} -__attribute__((naked)) -auto tan(double x) { -asm("vmov.f64 r1, r2, d0;" - "mov r0, #2;" - "svc 1;" - "vmov.f64 d0, r1, r2;" - "bx lr"); -return 0; -} -__attribute__((naked)) -auto sqrt(double x) { -asm("vsqrt.f64 d0, d0; bx lr"); -return 0; -} - -auto readalt() { -adcsample_t s; -asm("svc 3; mov %0, r0" : "=&r"(s)); -return s; -} - -// End stmdspgui header code - -)cpp"; -static const char *file_header_l4 = R"cpp( -#include - -using adcsample_t = uint16_t; -constexpr unsigned int SIZE = $0; -adcsample_t *process_data(adcsample_t *samples, unsigned int size); -extern "C" void process_data_entry() -{ - ((void (*)())process_data)(); -} - -constexpr float PI = 3.14159265358979L; -__attribute__((naked)) -auto sin(float x) { -asm("vmov.f32 r1, s0;" - "eor r0, r0;" - "svc 1;" - "vmov.f32 s0, r1;" - "bx lr"); -return 0; -} -__attribute__((naked)) -auto cos(float x) { -asm("vmov.f32 r1, s0;" - "mov r0, #1;" - "svc 1;" - "vmov.f32 s0, r1;" - "bx lr"); -return 0; -} -__attribute__((naked)) -auto tan(float x) { -asm("vmov.f32 r1, s0;" - "mov r0, #2;" - "svc 1;" - "vmov.f32 s0, r1;" - "bx lr"); -return 0; -} -__attribute__((naked)) -auto sqrt(float) { -asm("vsqrt.f32 s0, s0; bx lr"); -return 0; -} - -auto readalt() { -adcsample_t s; -asm("push {r4-r6}; svc 3; mov %0, r0; pop {r4-r6}" : "=&r"(s)); -return s; -} - -// End stmdspgui header code - -)cpp"; - - -static const char *file_content = -R"cpp(adcsample_t *process_data(adcsample_t *samples, unsigned int size) -{ - return samples; -} -)cpp"; - +#include "wxmain_devdata.h" enum Id { MeasureTimer = 1, @@ -218,7 +55,11 @@ enum Id { MCodeDisassemble }; -MainFrame::MainFrame() : wxFrame(nullptr, wxID_ANY, "stmdspgui", wxPoint(50, 50), wxSize(640, 800)) +#include "wxmain_mfile.h" +#include "wxmain_mrun.h" + +MainFrame::MainFrame() : + wxFrame(nullptr, wxID_ANY, "stmdspgui", wxDefaultPosition, wxSize(640, 800)) { // Main frame structure: // Begin with a main splitter for the code and terminal panes @@ -527,234 +368,6 @@ wxString MainFrame::compileEditorCode() } } -void MainFrame::onFileNew(wxCommandEvent&) -{ - m_open_file_path = ""; - m_text_editor->SetText(file_content); - m_text_editor->DiscardEdits(); - m_status_bar->SetStatusText("Ready."); -} - -void MainFrame::onFileOpen(wxCommandEvent&) -{ - wxFileDialog openDialog(this, "Open filter file", "", "", - "C++ source file (*.cpp)|*.cpp", - wxFD_OPEN | wxFD_FILE_MUST_EXIST); - - if (openDialog.ShowModal() != wxID_CANCEL) { - if (wxFileInputStream file_stream (openDialog.GetPath()); file_stream.IsOk()) { - auto size = file_stream.GetSize(); - auto buffer = new char[size + 1]; - buffer[size] = '\0'; - if (file_stream.ReadAll(buffer, size)) { - m_open_file_path = openDialog.GetPath(); - m_text_editor->SetText(buffer); - m_text_editor->DiscardEdits(); - m_compile_output->ChangeValue(""); - m_status_bar->SetStatusText("Ready."); - } else { - m_status_bar->SetStatusText("Failed to read file contents."); - } - delete[] buffer; - } else { - m_status_bar->SetStatusText("Failed to open file."); - } - } else { - m_status_bar->SetStatusText("Ready."); - } -} - -void MainFrame::onFileOpenTemplate(wxCommandEvent& event) -{ - auto file_path = wxGetCwd() + "/templates/" + m_menu_bar->GetLabel(event.GetId()); - - if (wxFileInputStream file_stream (file_path); file_stream.IsOk()) { - auto size = file_stream.GetSize(); - auto buffer = new char[size + 1]; - buffer[size] = '\0'; - if (file_stream.ReadAll(buffer, size)) { - m_open_file_path = ""; - m_text_editor->SetText(buffer); - //m_text_editor->DiscardEdits(); - m_status_bar->SetStatusText("Ready."); - } else { - m_status_bar->SetStatusText("Failed to read file contents."); - } - delete[] buffer; - } else { - m_status_bar->SetStatusText("Ready."); - } -} - - -void MainFrame::onFileSave(wxCommandEvent& ce) -{ - if (m_text_editor->IsModified()) { - if (m_open_file_path.IsEmpty()) { - onFileSaveAs(ce); - } else { - if (wxFile file (m_open_file_path, wxFile::write); file.IsOpened()) { - file.Write(m_text_editor->GetText()); - file.Close(); - m_text_editor->DiscardEdits(); - m_status_bar->SetStatusText("Saved."); - } else { - m_status_bar->SetStatusText("Save failed: couldn't open file."); - } - } - } else { - m_status_bar->SetStatusText("No modifications to save."); - } -} - -void MainFrame::onFileSaveAs(wxCommandEvent&) -{ - if (m_text_editor->IsModified()) { - wxFileDialog saveDialog(this, "Save filter file", "", "", - "C++ source file (*.cpp)|*.cpp", - wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - - if (saveDialog.ShowModal() != wxID_CANCEL) { - if (wxFile file (saveDialog.GetPath(), wxFile::write); file.IsOpened()) { - file.Write(m_text_editor->GetText()); - file.Close(); - m_text_editor->DiscardEdits(); - m_open_file_path = saveDialog.GetPath(); - m_status_bar->SetStatusText("Saved."); - } else { - m_status_bar->SetStatusText("Save failed: couldn't open file."); - } - } - } else { - m_status_bar->SetStatusText("No modifications to save."); - } -} - -void MainFrame::onFileQuit(wxCommandEvent&) -{ - Close(true); -} - -void MainFrame::onRunConnect(wxCommandEvent& ce) -{ - auto menuItem = dynamic_cast(ce.GetEventUserData()); - - if (!m_device) { - stmdsp::scanner scanner; - if (auto devices = scanner.scan(); devices.size() > 0) { - m_device = new stmdsp::device(devices.front()); - if (m_device->connected()) { - auto rate = m_device->get_sample_rate(); - m_rate_select->SetSelection(rate); - - updateMenuOptions(); - menuItem->SetItemLabel("&Disconnect"); - m_status_bar->SetStatusText("Connected."); - } else { - delete m_device; - m_device = nullptr; - - menuItem->SetItemLabel("&Connect"); - m_status_bar->SetStatusText("Failed to connect."); - } - } else { - m_status_bar->SetStatusText("No devices found."); - } - } else { - delete m_device; - m_device = nullptr; - updateMenuOptions(); - menuItem->SetItemLabel("&Connect"); - m_status_bar->SetStatusText("Disconnected."); - } -} - -void MainFrame::onRunStart(wxCommandEvent& ce) -{ - auto menuItem = dynamic_cast(ce.GetEventUserData()); - - if (!m_is_running) { - if (m_run_measure->IsChecked()) { - m_device->continuous_start_measure(); - m_measure_timer->StartOnce(1000); - } else { - if (m_device->is_siggening() && m_wav_clip) { - m_measure_timer->Start(m_device->get_buffer_size() * 500 / - srateNums[m_rate_select->GetSelection()]); - } else if (m_conv_result_log) { - m_measure_timer->Start(15); - } else if (m_run_draw_samples->IsChecked()) { - m_measure_timer->Start(300); - } - - m_device->continuous_start(); - } - - m_rate_select->Enable(false); - menuItem->SetItemLabel("&Stop"); - m_status_bar->SetStatusText("Running."); - m_is_running = true; - } else { - m_device->continuous_stop(); - m_measure_timer->Stop(); - - m_rate_select->Enable(true); - menuItem->SetItemLabel("&Start"); - m_status_bar->SetStatusText("Ready."); - m_is_running = false; - - if (m_run_draw_samples->IsChecked()) - m_compile_output->Refresh(); - } -} - -void MainFrame::onRunLogResults(wxCommandEvent& ce) -{ - auto menuItem = dynamic_cast(ce.GetEventUserData()); - if (menuItem->IsChecked()) { - wxFileDialog dialog (this, "Choose log file", "", "", "*.csv", - wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - - if (dialog.ShowModal() != wxID_CANCEL) { - if (m_conv_result_log) { - m_conv_result_log->Close(); - delete m_conv_result_log; - m_conv_result_log = nullptr; - } - - m_conv_result_log = new wxFileOutputStream(dialog.GetPath()); - } - - m_status_bar->SetStatusText("Ready."); - } else if (m_conv_result_log) { - m_conv_result_log->Close(); - delete m_conv_result_log; - m_conv_result_log = nullptr; - } -} - -void MainFrame::onRunEditBSize(wxCommandEvent&) -{ - wxTextEntryDialog dialog (this, "Enter new buffer size (100-4096)", "Set Buffer Size"); - if (dialog.ShowModal() == wxID_OK) { - if (wxString value = dialog.GetValue(); !value.IsEmpty()) { - if (unsigned long n; value.ToULong(&n)) { - if (n >= 100 && n <= stmdsp::SAMPLES_MAX) { - m_device->continuous_set_buffer_size(n); - } else { - m_status_bar->SetStatusText("Error: Invalid buffer size."); - } - } else { - m_status_bar->SetStatusText("Error: Invalid buffer size."); - } - } else { - m_status_bar->SetStatusText("Ready."); - } - } else { - m_status_bar->SetStatusText("Ready."); - } -} - void MainFrame::onToolbarSampleRate(wxCommandEvent& ce) { auto combo = dynamic_cast(ce.GetEventUserData()); @@ -762,99 +375,6 @@ void MainFrame::onToolbarSampleRate(wxCommandEvent& ce) m_status_bar->SetStatusText("Ready."); } -void MainFrame::onRunGenUpload(wxCommandEvent&) -{ - wxTextEntryDialog dialog (this, "Enter up to 8000 generator values below. " - "Values must be whole numbers between zero and 4095.", - "Enter Generator Values"); - if (dialog.ShowModal() == wxID_OK) { - if (wxString values = dialog.GetValue(); !values.IsEmpty()) { - if (values[0] == '/') { - m_wav_clip = new wav::clip(values.Mid(1)); - if (m_wav_clip->valid()) { - m_status_bar->SetStatusText("Generator ready."); - } else { - delete m_wav_clip; - m_wav_clip = nullptr; - m_status_bar->SetStatusText("Error: Bad WAV file."); - } - } else { - std::vector samples; - while (!values.IsEmpty() && samples.size() <= stmdsp::SAMPLES_MAX * 2) { - if (auto number_end = values.find_first_not_of("0123456789"); - number_end != wxString::npos && number_end > 0) - { - auto number = values.Left(number_end); - if (unsigned long n; number.ToULong(&n)) - samples.push_back(n & 4095); - - if (auto next = values.find_first_of("0123456789", number_end + 1); - next != wxString::npos) - { - values = values.Mid(next); - } else { - break; - } - } else { - break; - } - } - - if (samples.size() <= stmdsp::SAMPLES_MAX) { - m_device->siggen_upload(&samples[0], samples.size()); - m_status_bar->SetStatusText("Generator ready."); - } else { - m_status_bar->SetStatusText("Error: Too many samples (max is 8000)."); - } - } - } else { - m_status_bar->SetStatusText("Error: No samples given."); - } - } else { - m_status_bar->SetStatusText("Ready."); - } -} - -void MainFrame::onRunGenStart(wxCommandEvent& ce) -{ - auto menuItem = dynamic_cast(ce.GetEventUserData()); - if (menuItem->IsChecked()) { - m_device->siggen_start(); - menuItem->SetItemLabel("Stop &generator"); - m_status_bar->SetStatusText("Generator running."); - } else { - m_device->siggen_stop(); - menuItem->SetItemLabel("Start &generator"); - m_status_bar->SetStatusText("Ready."); - } -} - -void MainFrame::onRunUpload(wxCommandEvent&) -{ - if (auto file = compileEditorCode(); !file.IsEmpty()) { - if (wxFileInputStream file_stream (file); file_stream.IsOk()) { - auto size = file_stream.GetSize(); - auto buffer = new unsigned char[size]; - file_stream.ReadAll(buffer, size); - m_device->upload_filter(buffer, size); - m_status_bar->SetStatusText("Code uploaded."); - } else { - m_status_bar->SetStatusText("Couldn't load compiled code."); - } - } -} - -void MainFrame::onRunUnload(wxCommandEvent&) -{ - m_device->unload_filter(); - m_status_bar->SetStatusText("Unloaded code."); -} - -void MainFrame::onRunCompile(wxCommandEvent&) -{ - compileEditorCode(); -} - void MainFrame::onCodeDisassemble(wxCommandEvent&) { if (!m_temp_file_name.IsEmpty()) { diff --git a/gui/wxmain_devdata.h b/gui/wxmain_devdata.h new file mode 100644 index 0000000..4ca1eb2 --- /dev/null +++ b/gui/wxmain_devdata.h @@ -0,0 +1,165 @@ +static const std::array srateValues { + "8 kS/s", + "16 kS/s", + "20 kS/s", + "32 kS/s", + "48 kS/s", + "96 kS/s" +}; +static const std::array srateNums { + 8000, + 16000, + 20000, + 32000, + 48000, + 96000 +}; + +static const char *makefile_text_h7 = R"make( +all: + @arm-none-eabi-g++ -x c++ -Os -fno-exceptions -fno-rtti \ + -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 -mtune=cortex-m7 \ + -nostartfiles \ + -Wl,-Ttext-segment=0x00000000 -Wl,-zmax-page-size=512 -Wl,-eprocess_data_entry \ + $0 -o $0.o + @cp $0.o $0.orig.o + @arm-none-eabi-strip -s -S --strip-unneeded $0.o + @arm-none-eabi-objcopy --remove-section .ARM.attributes \ + --remove-section .comment \ + --remove-section .noinit \ + $0.o + arm-none-eabi-size $0.o +)make"; +static const char *makefile_text_l4 = R"make( +all: + @arm-none-eabi-g++ -x c++ -Os -fno-exceptions -fno-rtti \ + -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mtune=cortex-m4 \ + -nostartfiles \ + -Wl,-Ttext-segment=0x10000000 -Wl,-zmax-page-size=512 -Wl,-eprocess_data_entry \ + $0 -o $0.o + @cp $0.o $0.orig.o + @arm-none-eabi-strip -s -S --strip-unneeded $0.o + @arm-none-eabi-objcopy --remove-section .ARM.attributes \ + --remove-section .comment \ + --remove-section .noinit \ + $0.o + arm-none-eabi-size $0.o +)make"; + +static const char *file_header_h7 = R"cpp( +#include + +using adcsample_t = uint16_t; +constexpr unsigned int SIZE = $0; +adcsample_t *process_data(adcsample_t *samples, unsigned int size); +extern "C" void process_data_entry() +{ + ((void (*)())process_data)(); +} + +constexpr double PI = 3.14159265358979323846L; +__attribute__((naked)) +auto sin(double x) { +asm("vmov.f64 r1, r2, d0;" + "eor r0, r0;" + "svc 1;" + "vmov.f64 d0, r1, r2;" + "bx lr"); +return 0; +} +__attribute__((naked)) +auto cos(double x) { +asm("vmov.f64 r1, r2, d0;" + "mov r0, #1;" + "svc 1;" + "vmov.f64 d0, r1, r2;" + "bx lr"); +return 0; +} +__attribute__((naked)) +auto tan(double x) { +asm("vmov.f64 r1, r2, d0;" + "mov r0, #2;" + "svc 1;" + "vmov.f64 d0, r1, r2;" + "bx lr"); +return 0; +} +__attribute__((naked)) +auto sqrt(double x) { +asm("vsqrt.f64 d0, d0; bx lr"); +return 0; +} + +auto readalt() { +adcsample_t s; +asm("svc 3; mov %0, r0" : "=&r"(s)); +return s; +} + +// End stmdspgui header code + +)cpp"; +static const char *file_header_l4 = R"cpp( +#include + +using adcsample_t = uint16_t; +constexpr unsigned int SIZE = $0; +adcsample_t *process_data(adcsample_t *samples, unsigned int size); +extern "C" void process_data_entry() +{ + ((void (*)())process_data)(); +} + +constexpr float PI = 3.14159265358979L; +__attribute__((naked)) +auto sin(float x) { +asm("vmov.f32 r1, s0;" + "eor r0, r0;" + "svc 1;" + "vmov.f32 s0, r1;" + "bx lr"); +return 0; +} +__attribute__((naked)) +auto cos(float x) { +asm("vmov.f32 r1, s0;" + "mov r0, #1;" + "svc 1;" + "vmov.f32 s0, r1;" + "bx lr"); +return 0; +} +__attribute__((naked)) +auto tan(float x) { +asm("vmov.f32 r1, s0;" + "mov r0, #2;" + "svc 1;" + "vmov.f32 s0, r1;" + "bx lr"); +return 0; +} +__attribute__((naked)) +auto sqrt(float) { +asm("vsqrt.f32 s0, s0; bx lr"); +return 0; +} + +auto readalt() { +adcsample_t s; +asm("push {r4-r6}; svc 3; mov %0, r0; pop {r4-r6}" : "=&r"(s)); +return s; +} + +// End stmdspgui header code + +)cpp"; + + +static const char *file_content = +R"cpp(adcsample_t *process_data(adcsample_t *samples, unsigned int size) +{ + return samples; +} +)cpp"; + diff --git a/gui/wxmain_mfile.h b/gui/wxmain_mfile.h new file mode 100644 index 0000000..7cc9177 --- /dev/null +++ b/gui/wxmain_mfile.h @@ -0,0 +1,108 @@ +void MainFrame::onFileNew(wxCommandEvent&) +{ + m_open_file_path = ""; + m_text_editor->SetText(file_content); + m_text_editor->DiscardEdits(); + m_status_bar->SetStatusText("Ready."); +} + +void MainFrame::onFileOpen(wxCommandEvent&) +{ + wxFileDialog openDialog(this, "Open filter file", "", "", + "C++ source file (*.cpp)|*.cpp", + wxFD_OPEN | wxFD_FILE_MUST_EXIST); + + if (openDialog.ShowModal() != wxID_CANCEL) { + if (wxFileInputStream file_stream (openDialog.GetPath()); file_stream.IsOk()) { + auto size = file_stream.GetSize(); + auto buffer = new char[size + 1]; + buffer[size] = '\0'; + if (file_stream.ReadAll(buffer, size)) { + m_open_file_path = openDialog.GetPath(); + m_text_editor->SetText(buffer); + m_text_editor->DiscardEdits(); + m_compile_output->ChangeValue(""); + m_status_bar->SetStatusText("Ready."); + } else { + m_status_bar->SetStatusText("Failed to read file contents."); + } + delete[] buffer; + } else { + m_status_bar->SetStatusText("Failed to open file."); + } + } else { + m_status_bar->SetStatusText("Ready."); + } +} + +void MainFrame::onFileOpenTemplate(wxCommandEvent& event) +{ + auto file_path = wxGetCwd() + "/templates/" + m_menu_bar->GetLabel(event.GetId()); + + if (wxFileInputStream file_stream (file_path); file_stream.IsOk()) { + auto size = file_stream.GetSize(); + auto buffer = new char[size + 1]; + buffer[size] = '\0'; + if (file_stream.ReadAll(buffer, size)) { + m_open_file_path = ""; + m_text_editor->SetText(buffer); + //m_text_editor->DiscardEdits(); + m_status_bar->SetStatusText("Ready."); + } else { + m_status_bar->SetStatusText("Failed to read file contents."); + } + delete[] buffer; + } else { + m_status_bar->SetStatusText("Ready."); + } +} + + +void MainFrame::onFileSave(wxCommandEvent& ce) +{ + if (m_text_editor->IsModified()) { + if (m_open_file_path.IsEmpty()) { + onFileSaveAs(ce); + } else { + if (wxFile file (m_open_file_path, wxFile::write); file.IsOpened()) { + file.Write(m_text_editor->GetText()); + file.Close(); + m_text_editor->DiscardEdits(); + m_status_bar->SetStatusText("Saved."); + } else { + m_status_bar->SetStatusText("Save failed: couldn't open file."); + } + } + } else { + m_status_bar->SetStatusText("No modifications to save."); + } +} + +void MainFrame::onFileSaveAs(wxCommandEvent&) +{ + if (m_text_editor->IsModified()) { + wxFileDialog saveDialog(this, "Save filter file", "", "", + "C++ source file (*.cpp)|*.cpp", + wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + + if (saveDialog.ShowModal() != wxID_CANCEL) { + if (wxFile file (saveDialog.GetPath(), wxFile::write); file.IsOpened()) { + file.Write(m_text_editor->GetText()); + file.Close(); + m_text_editor->DiscardEdits(); + m_open_file_path = saveDialog.GetPath(); + m_status_bar->SetStatusText("Saved."); + } else { + m_status_bar->SetStatusText("Save failed: couldn't open file."); + } + } + } else { + m_status_bar->SetStatusText("No modifications to save."); + } +} + +void MainFrame::onFileQuit(wxCommandEvent&) +{ + Close(true); +} + diff --git a/gui/wxmain_mrun.h b/gui/wxmain_mrun.h new file mode 100644 index 0000000..b00898a --- /dev/null +++ b/gui/wxmain_mrun.h @@ -0,0 +1,228 @@ +void MainFrame::onRunConnect(wxCommandEvent& ce) +{ + auto menuItem = dynamic_cast(ce.GetEventUserData()); + + if (!m_device) { + stmdsp::scanner scanner; + if (auto devices = scanner.scan(); devices.size() > 0) { + m_device = new stmdsp::device(devices.front()); + if (m_device->connected()) { + auto rate = m_device->get_sample_rate(); + m_rate_select->SetSelection(rate); + + updateMenuOptions(); + menuItem->SetItemLabel("&Disconnect"); + m_status_bar->SetStatusText("Connected."); + } else { + delete m_device; + m_device = nullptr; + + menuItem->SetItemLabel("&Connect"); + m_status_bar->SetStatusText("Failed to connect."); + } + } else { + m_status_bar->SetStatusText("No devices found."); + } + } else { + delete m_device; + m_device = nullptr; + updateMenuOptions(); + menuItem->SetItemLabel("&Connect"); + m_status_bar->SetStatusText("Disconnected."); + } +} + +void MainFrame::onRunStart(wxCommandEvent& ce) +{ + auto menuItem = dynamic_cast(ce.GetEventUserData()); + + if (!m_is_running) { + if (m_run_measure->IsChecked()) { + m_device->continuous_start_measure(); + m_measure_timer->StartOnce(1000); + } else { + if (m_device->is_siggening() && m_wav_clip) { + m_measure_timer->Start(m_device->get_buffer_size() * 500 / + srateNums[m_rate_select->GetSelection()]); + } else if (m_conv_result_log) { + m_measure_timer->Start(15); + } else if (m_run_draw_samples->IsChecked()) { + m_measure_timer->Start(300); + } + + m_device->continuous_start(); + } + + m_rate_select->Enable(false); + menuItem->SetItemLabel("&Stop"); + m_status_bar->SetStatusText("Running."); + m_is_running = true; + } else { + m_device->continuous_stop(); + m_measure_timer->Stop(); + + m_rate_select->Enable(true); + menuItem->SetItemLabel("&Start"); + m_status_bar->SetStatusText("Ready."); + m_is_running = false; + + if (m_run_draw_samples->IsChecked()) + m_compile_output->Refresh(); + } +} + +void MainFrame::onRunLogResults(wxCommandEvent& ce) +{ + auto menuItem = dynamic_cast(ce.GetEventUserData()); + if (menuItem->IsChecked()) { + wxFileDialog dialog (this, "Choose log file", "", "", "*.csv", + wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + + if (dialog.ShowModal() != wxID_CANCEL) { + if (m_conv_result_log) { + m_conv_result_log->Close(); + delete m_conv_result_log; + m_conv_result_log = nullptr; + } + + m_conv_result_log = new wxFileOutputStream(dialog.GetPath()); + } + + m_status_bar->SetStatusText("Ready."); + } else if (m_conv_result_log) { + m_conv_result_log->Close(); + delete m_conv_result_log; + m_conv_result_log = nullptr; + } +} + +void MainFrame::onRunEditBSize(wxCommandEvent&) +{ + wxTextEntryDialog dialog (this, "Enter new buffer size (100-4096)", "Set Buffer Size"); + if (dialog.ShowModal() == wxID_OK) { + if (wxString value = dialog.GetValue(); !value.IsEmpty()) { + if (unsigned long n; value.ToULong(&n)) { + if (n >= 100 && n <= stmdsp::SAMPLES_MAX) { + m_device->continuous_set_buffer_size(n); + } else { + m_status_bar->SetStatusText("Error: Invalid buffer size."); + } + } else { + m_status_bar->SetStatusText("Error: Invalid buffer size."); + } + } else { + m_status_bar->SetStatusText("Ready."); + } + } else { + m_status_bar->SetStatusText("Ready."); + } +} + +void MainFrame::onRunGenUpload(wxCommandEvent&) +{ + if (SiggenDialog dialog (this); dialog.ShowModal() == wxID_OK) { + auto result = dialog.Result(); + if (result.Find(".wav") != wxNOT_FOUND) { + // Audio + m_wav_clip = new wav::clip(result/*.Mid(1)*/); + if (m_wav_clip->valid()) { + m_status_bar->SetStatusText("Generator ready."); + } else { + delete m_wav_clip; + m_wav_clip = nullptr; + m_status_bar->SetStatusText("Error: Bad WAV file."); + } + } else if (result.find_first_not_of("0123456789 \t\r\n") == wxString::npos) { + // List + std::vector samples; + while (!result.IsEmpty() && samples.size() <= stmdsp::SAMPLES_MAX * 2) { + if (auto number_end = result.find_first_not_of("0123456789"); + number_end != wxString::npos && number_end > 0) + { + auto number = result.Left(number_end); + if (unsigned long n; number.ToULong(&n)) + samples.push_back(n & 4095); + + if (auto next = result.find_first_of("0123456789", number_end + 1); + next != wxString::npos) + { + result = result.Mid(next); + } else { + break; + } + } else { + break; + } + } + + if (samples.size() <= stmdsp::SAMPLES_MAX * 2) { + m_device->siggen_upload(&samples[0], samples.size()); + m_status_bar->SetStatusText("Generator ready."); + } else { + m_status_bar->SetStatusText(wxString::Format("Error: Too many samples (max is %u).", + stmdsp::SAMPLES_MAX * 2)); + } + } else { + // Formula + m_status_bar->SetStatusText("Sorry, formulas not supported yet."); + } + // if (wxString values = dialog.GetValue(); !values.IsEmpty()) { + // if (values[0] == '/') { + // m_wav_clip = new wav::clip(values.Mid(1)); + // if (m_wav_clip->valid()) { + // m_status_bar->SetStatusText("Generator ready."); + // } else { + // delete m_wav_clip; + // m_wav_clip = nullptr; + // m_status_bar->SetStatusText("Error: Bad WAV file."); + // } + // } else { + // } + // } else { + // m_status_bar->SetStatusText("Error: No samples given."); + // } + } else { + m_status_bar->SetStatusText("Ready."); + } +} + +void MainFrame::onRunGenStart(wxCommandEvent& ce) +{ + auto menuItem = dynamic_cast(ce.GetEventUserData()); + if (menuItem->IsChecked()) { + m_device->siggen_start(); + menuItem->SetItemLabel("Stop &generator"); + m_status_bar->SetStatusText("Generator running."); + } else { + m_device->siggen_stop(); + menuItem->SetItemLabel("Start &generator"); + m_status_bar->SetStatusText("Ready."); + } +} + +void MainFrame::onRunUpload(wxCommandEvent&) +{ + if (auto file = compileEditorCode(); !file.IsEmpty()) { + if (wxFileInputStream file_stream (file); file_stream.IsOk()) { + auto size = file_stream.GetSize(); + auto buffer = new unsigned char[size]; + file_stream.ReadAll(buffer, size); + m_device->upload_filter(buffer, size); + m_status_bar->SetStatusText("Code uploaded."); + } else { + m_status_bar->SetStatusText("Couldn't load compiled code."); + } + } +} + +void MainFrame::onRunUnload(wxCommandEvent&) +{ + m_device->unload_filter(); + m_status_bar->SetStatusText("Unloaded code."); +} + +void MainFrame::onRunCompile(wxCommandEvent&) +{ + compileEditorCode(); +} + diff --git a/gui/wxsiggen.cpp b/gui/wxsiggen.cpp new file mode 100644 index 0000000..f6abce8 --- /dev/null +++ b/gui/wxsiggen.cpp @@ -0,0 +1,89 @@ +/** + * @file wxsiggen.cpp + * @brief Dialog prompt for providing signal generator input. + * + * Copyright (C) 2021 Clyne Sullivan + * + * Distributed under the GNU GPL v3 or later. You should have received a copy of + * the GNU General Public License along with this program. + * If not, see . + */ + +#include "wxsiggen.hpp" + +#include +#include + +static const std::array Sources {{ + "List", + "Formula", + "WAV audio" +}}; +static const std::array Instructions {{ + "Enter a list of numbers:", + "Enter a formula. f(x) = ", + wxEmptyString +}}; + +SiggenDialog::SiggenDialog(wxWindow *parent) : + wxDialog(parent, wxID_ANY, "stmdspgui signal generator", wxDefaultPosition, wxSize(300, 200)) +{ + m_instruction = new wxStaticText(this, wxID_ANY, wxEmptyString, wxPoint(10, 70)); + m_source_list = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxPoint(10, 100), wxSize(280, 30)); + m_source_math = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxPoint(10, 100), wxSize(280, 30)); + m_source_file = new wxButton(this, 42, "Choose file...", wxPoint(10, 75), wxSize(280, 50)); + + auto radio = new wxRadioBox(this, wxID_ANY, "Source", wxPoint(10, 10), wxSize(280, 50), + Sources.size(), Sources.data()); + auto save = new wxButton(this, 43, "Save", wxPoint(200, 150)); + + m_instruction->SetLabel(Instructions[0]); + m_source_math->Hide(); + m_source_file->Hide(); + + Bind(wxEVT_RADIOBOX, &SiggenDialog::onSourceChange, this, wxID_ANY, wxID_ANY, radio); + Bind(wxEVT_BUTTON, &SiggenDialog::onSourceFile, this, 42, 42, m_source_file); + Bind(wxEVT_BUTTON, &SiggenDialog::onSave, this, 43, 43, save); +} + +SiggenDialog::~SiggenDialog() +{ + Unbind(wxEVT_BUTTON, &SiggenDialog::onSave, this, 43, 43); + Unbind(wxEVT_BUTTON, &SiggenDialog::onSourceFile, this, 42, 42); + Unbind(wxEVT_RADIOBOX, &SiggenDialog::onSourceChange, this, wxID_ANY, wxID_ANY); +} + +void SiggenDialog::onSourceFile(wxCommandEvent&) +{ + wxFileDialog dialog (this, "Open audio file", "", "", + "Audio file (*.wav)|*.wav", + wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dialog.ShowModal() != wxID_CANCEL) + m_result = dialog.GetPath(); +} + +void SiggenDialog::onSave(wxCommandEvent&) +{ + if (m_source_list->IsShown()) + m_result = m_source_list->GetValue(); + else if (m_source_math->IsShown()) + m_result = m_source_math->GetValue(); + + EndModal(!m_result.IsEmpty() ? wxID_OK : wxID_CANCEL); +} + +void SiggenDialog::onSourceChange(wxCommandEvent& ce) +{ + auto radio = dynamic_cast(ce.GetEventObject()); + if (radio == nullptr) + return; + + m_result.Clear(); + if (int selection = radio->GetSelection(); selection >= 0 && selection < Sources.size()) { + m_instruction->SetLabel(Instructions[selection]); + m_source_list->Show(selection == 0); + m_source_math->Show(selection == 1); + m_source_file->Show(selection == 2); + } +} + diff --git a/gui/wxsiggen.hpp b/gui/wxsiggen.hpp new file mode 100644 index 0000000..acf479e --- /dev/null +++ b/gui/wxsiggen.hpp @@ -0,0 +1,42 @@ +/** + * @file wxsiggen.hpp + * @brief Dialog prompt for providing signal generator input. + * + * Copyright (C) 2021 Clyne Sullivan + * + * Distributed under the GNU GPL v3 or later. You should have received a copy of + * the GNU General Public License along with this program. + * If not, see . + */ + +#ifndef WXSIGGEN_HPP_ +#define WXSIGGEN_HPP_ + +#include +#include +#include +#include + +class SiggenDialog : public wxDialog { +public: + SiggenDialog(wxWindow *parent); + ~SiggenDialog(); + + auto Result() const { + return m_result; + } + + void onSourceChange(wxCommandEvent&); + void onSourceFile(wxCommandEvent&); + void onSave(wxCommandEvent&); + +private: + wxStaticText *m_instruction = nullptr; + wxTextCtrl *m_source_list = nullptr; + wxTextCtrl *m_source_math = nullptr; + wxButton *m_source_file = nullptr; + wxString m_result; +}; + +#endif // WXSIGGEN_HPP_ +