From 8aac911f076527c84f541b8ed1c45138548a3d5d Mon Sep 17 00:00:00 2001 From: apoorv569 Date: Fri, 30 Jul 2021 23:40:45 +0530 Subject: [PATCH] Add loop point controls, draw waveform and playhead. --- meson.build | 3 +- src/ControlID_Enums.hpp | 2 + src/MainFrame.cpp | 345 ++++++++++++++++++++++++++++++++++++++-- src/MainFrame.hpp | 16 +- 4 files changed, 351 insertions(+), 15 deletions(-) diff --git a/meson.build b/meson.build index e81a9e8..45b9e71 100755 --- a/meson.build +++ b/meson.build @@ -75,6 +75,7 @@ wx = dependency('wxwidgets', version: '>=3.0.4') taglib = dependency('taglib', version: '>=1.11') sqlite3 = dependency('sqlite3') yaml = dependency('yaml-cpp') +snd = dependency('sndfile') install_subdir( 'assets', @@ -86,5 +87,5 @@ executable('SampleHive', sources: src, cpp_args: [wx_cxx_flags], link_args: [wx_libs], - dependencies: [wx, taglib, sqlite3, yaml], + dependencies: [wx, taglib, sqlite3, yaml, snd], install: true) diff --git a/src/ControlID_Enums.hpp b/src/ControlID_Enums.hpp index efb11dd..f393e79 100644 --- a/src/ControlID_Enums.hpp +++ b/src/ControlID_Enums.hpp @@ -35,6 +35,8 @@ enum ControlIDs BC_Settings, BC_Loop, BC_Stop, + BC_LoopPointButton, + BC_LoopPointText, BC_Mute, BC_Autoplay, BC_Volume, diff --git a/src/MainFrame.cpp b/src/MainFrame.cpp index 442dca8..bdf866b 100644 --- a/src/MainFrame.cpp +++ b/src/MainFrame.cpp @@ -20,8 +20,10 @@ #include #include +#include #include #include +#include #include #include @@ -29,6 +31,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -47,15 +52,20 @@ #include #include #include +#include #include #include #include +#include #include #include #include #include #include #include +#include + +#include #include "MainFrame.hpp" #include "ControlID_Enums.hpp" @@ -244,30 +254,50 @@ MainFrame::MainFrame() m_TopSplitter->SplitHorizontally(m_TopPanel, m_BottomSplitter); m_BottomSplitter->SplitVertically(m_BottomLeftPanel, m_BottomRightPanel); + m_TopWaveformPanel = new wxPanel(m_TopPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER); + m_TopControlsPanel = new wxPanel(m_TopPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER); + + // Looping region controls + m_LoopPointAButton = new wxToggleButton(m_TopControlsPanel, BC_LoopPointButton, _("A"), wxDefaultPosition, wxDefaultSize, 0); + m_LoopPointAButton->SetToolTip("Set Loop point A"); + m_LoopPointAButton->Disable(); + m_LoopPointAText = new wxTextCtrl(m_TopControlsPanel, BC_LoopPointText, _("00:00"), wxDefaultPosition, wxDefaultSize, wxTE_RIGHT | wxTE_PROCESS_ENTER); + m_LoopPointAText->Disable(); + m_LoopPointAText->SetMinSize(wxSize(60, -1)); + m_LoopPointAText->SetMaxSize(wxSize(60, -1)); + m_LoopPointBButton = new wxToggleButton(m_TopControlsPanel, BC_LoopPointButton, _("B"), wxDefaultPosition, wxDefaultSize, 0); + m_LoopPointBButton->SetToolTip("Set Loop point B"); + m_LoopPointBButton->Disable(); + m_LoopPointBText = new wxTextCtrl(m_TopControlsPanel, BC_LoopPointText, _("00:00"), wxDefaultPosition, wxDefaultSize, wxTE_RIGHT | wxTE_PROCESS_ENTER); + m_LoopPointBText->Disable(); + m_LoopPointBText->SetMinSize(wxSize(60, -1)); + m_LoopPointBText->SetMaxSize(wxSize(60, -1)); + // Initializing browser controls on top panel. - m_AutoPlayCheck = new wxCheckBox(m_TopPanel, BC_Autoplay, _("Autoplay"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); + m_AutoPlayCheck = new wxCheckBox(m_TopControlsPanel, BC_Autoplay, _("Autoplay"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); m_AutoPlayCheck->SetToolTip(_("Autoplay")); - m_VolumeSlider = new wxSlider(m_TopPanel, BC_Volume, 100, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL); + m_VolumeSlider = new wxSlider(m_TopControlsPanel, BC_Volume, 100, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL); m_VolumeSlider->SetToolTip(_("Volume")); m_VolumeSlider->SetMinSize(wxSize(120, -1)); m_VolumeSlider->SetMaxSize(wxSize(120, -1)); - m_SamplePosition = new wxStaticText(m_TopPanel, BC_SamplePosition, "--:--/--:--", + m_SamplePosition = new wxStaticText(m_TopControlsPanel, BC_SamplePosition, "--:--/--:--", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL); // Temporary widget to show a waveform sample image // TODO: Replace with actual waveform display - m_WaveformViewer = new wxStaticBitmap(m_TopPanel, wxID_ANY, wxBitmap(WAVEFORM_SVG)); + // m_WaveformBitmap.LoadFile(WAVEFORM_SVG); + // m_WaveformViewer = new wxStaticBitmap(m_TopWaveformPanel, wxID_ANY, m_WaveformBitmap); // Initialize browser control buttons - m_PlayButton = new wxButton(m_TopPanel, BC_Play, _("Play"), wxDefaultPosition, wxDefaultSize, 0); + m_PlayButton = new wxButton(m_TopControlsPanel, BC_Play, _("Play"), wxDefaultPosition, wxDefaultSize, 0); m_PlayButton->SetToolTip(_("Play")); - m_LoopButton = new wxToggleButton(m_TopPanel, BC_Loop, _("Loop"), wxDefaultPosition, wxDefaultSize, 0); + m_LoopButton = new wxToggleButton(m_TopControlsPanel, BC_Loop, _("Loop"), wxDefaultPosition, wxDefaultSize, 0); m_LoopButton->SetToolTip(_("Loop")); - m_StopButton = new wxButton(m_TopPanel, BC_Stop, _("Stop"), wxDefaultPosition, wxDefaultSize, 0); + m_StopButton = new wxButton(m_TopControlsPanel, BC_Stop, _("Stop"), wxDefaultPosition, wxDefaultSize, 0); m_StopButton->SetToolTip(_("Stop")); - m_SettingsButton = new wxButton(m_TopPanel, BC_Settings, _("Settings"), wxDefaultPosition, wxDefaultSize, 0); + m_SettingsButton = new wxButton(m_TopControlsPanel, BC_Settings, _("Settings"), wxDefaultPosition, wxDefaultSize, 0); m_SettingsButton->SetToolTip(_("Settings")); - m_MuteButton = new wxToggleButton(m_TopPanel, BC_Mute, _("Mute"), wxDefaultPosition, wxDefaultSize, 0); + m_MuteButton = new wxToggleButton(m_TopControlsPanel, BC_Mute, _("Mute"), wxDefaultPosition, wxDefaultSize, 0); m_MuteButton->SetToolTip(_("Mute")); // Initializing wxSearchCtrl on bottom panel. @@ -386,6 +416,10 @@ MainFrame::MainFrame() Bind(wxEVT_TOGGLEBUTTON, &MainFrame::OnClickMute, this, BC_Mute); Bind(wxEVT_MEDIA_FINISHED, &MainFrame::OnMediaFinished, this, BC_MediaCtrl); Bind(wxEVT_BUTTON, &MainFrame::OnClickSettings, this, BC_Settings); + Bind(wxEVT_TOGGLEBUTTON, &MainFrame::OnClickLoopPointsButton, this, BC_LoopPointButton); + Bind(wxEVT_TOGGLEBUTTON, &MainFrame::OnClickLoopPointsButton, this, BC_LoopPointButton); + Bind(wxEVT_TEXT_ENTER, &MainFrame::OnEnterLoopPoints, this, BC_LoopPointText); + Bind(wxEVT_TEXT_ENTER, &MainFrame::OnEnterLoopPoints, this, BC_LoopPointText); Bind(wxEVT_CHECKBOX, &MainFrame::OnCheckAutoplay, this, BC_Autoplay); Bind(wxEVT_SCROLL_THUMBTRACK, &MainFrame::OnSlideVolume, this, BC_Volume); Bind(wxEVT_SCROLL_THUMBRELEASE, &MainFrame::OnReleaseVolumeSlider, this, BC_Volume); @@ -412,6 +446,9 @@ MainFrame::MainFrame() Bind(wxEVT_BUTTON, &MainFrame::OnClickAddHive, this, BC_HiveAdd); Bind(wxEVT_BUTTON, &MainFrame::OnClickRemoveHive, this, BC_HiveRemove); + m_TopWaveformPanel->Bind(wxEVT_PAINT, &MainFrame::OnPaint, this); + m_TopWaveformPanel->Bind(wxEVT_MOTION, &MainFrame::OnHoverPlayhead, this); + // Adding widgets to their sizers m_MainSizer->Add(m_MainPanel, 1, wxALL | wxEXPAND, 0); @@ -424,14 +461,18 @@ MainFrame::MainFrame() m_BrowserControlSizer->Add(0,0,1, wxALL | wxEXPAND, 0); m_BrowserControlSizer->Add(m_SamplePosition, 0, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 2); m_BrowserControlSizer->Add(30,0,0, wxALL | wxEXPAND, 0); + m_BrowserControlSizer->Add(m_LoopPointAButton, 0, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 2); + m_BrowserControlSizer->Add(m_LoopPointAText, 0, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 2); + m_BrowserControlSizer->Add(m_LoopPointBButton, 0, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 2); + m_BrowserControlSizer->Add(m_LoopPointBText, 0, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 2); m_BrowserControlSizer->Add(m_MuteButton, 0, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 2); m_BrowserControlSizer->Add(m_VolumeSlider, 1, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 2); m_BrowserControlSizer->Add(m_AutoPlayCheck, 0, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 2); - m_WaveformDisplaySizer->Add(m_WaveformViewer, 1, wxALL | wxEXPAND, 2); + // m_WaveformDisplaySizer->Add(m_WaveformViewer, 1, wxALL | wxEXPAND, 2); - m_TopPanelMainSizer->Add(m_WaveformDisplaySizer, 1, wxALL | wxEXPAND, 2); - m_TopPanelMainSizer->Add(m_BrowserControlSizer, 0, wxALL | wxEXPAND, 2); + m_TopPanelMainSizer->Add(m_TopWaveformPanel, 1, wxALL | wxEXPAND, 2); + m_TopPanelMainSizer->Add(m_TopControlsPanel, 0, wxALL | wxEXPAND, 2); m_BottomLeftPanelMainSizer->Add(m_Notebook, 1, wxALL | wxEXPAND, 0); @@ -464,6 +505,16 @@ MainFrame::MainFrame() m_TopSizer->SetSizeHints(m_MainPanel); m_TopSizer->Layout(); + m_TopControlsPanel->SetSizer(m_BrowserControlSizer); + m_BrowserControlSizer->Fit(m_TopControlsPanel); + m_BrowserControlSizer->SetSizeHints(m_TopControlsPanel); + m_BrowserControlSizer->Layout(); + + m_TopWaveformPanel->SetSizer(m_WaveformDisplaySizer); + m_WaveformDisplaySizer->Fit(m_TopWaveformPanel); + m_WaveformDisplaySizer->SetSizeHints(m_TopWaveformPanel); + m_WaveformDisplaySizer->Layout(); + // Sizer for TopPanel m_TopPanel->SetSizer(m_TopPanelMainSizer); m_TopPanelMainSizer->Fit(m_TopPanel); @@ -885,7 +936,8 @@ void MainFrame::OnClickPlay(wxCommandEvent& event) int selected_row = m_Library->GetSelectedRow(); - if (selected_row < 0) return; + if (selected_row < 0) + return; wxString selection = m_Library->GetTextValue(selected_row, 1); @@ -915,10 +967,14 @@ void MainFrame::OnClickLoop(wxCommandEvent& event) if (m_LoopButton->GetValue()) { bLoop = true; + m_LoopPointAButton->Enable(true); + m_LoopPointBButton->Enable(true); } else { bLoop = false; + m_LoopPointAButton->Disable(); + m_LoopPointBButton->Disable(); } } @@ -988,6 +1044,8 @@ void MainFrame::UpdateElapsedTime(wxTimerEvent& event) position.Printf(wxT("%2i:%02i"), current_min, current_sec); m_SamplePosition->SetLabel(wxString::Format(wxT("%s/%s"), position.c_str(), duration.c_str())); + + Refresh(); } void MainFrame::OnCheckAutoplay(wxCommandEvent& event) @@ -2858,4 +2916,265 @@ void MainFrame::SetAfterFrameCreate() m_BottomSplitter->SetSashPosition(300); } +void MainFrame::OnClickLoopPointsButton(wxCommandEvent& event) +{ + wxLogDebug("Loop point button clicked"); + + if (bLoop) + { + m_LoopPointAButton->Enable(true); + m_LoopPointBButton->Enable(true); + + if (m_LoopPointAButton->GetValue()) + m_LoopPointAText->Enable(true); + else + m_LoopPointAText->Disable(); + + if (m_LoopPointBButton->GetValue()) + m_LoopPointBText->Enable(true); + else + m_LoopPointBText->Disable(); + } + else + { + m_LoopPointAButton->Disable(); + m_LoopPointBButton->Disable(); + } +} + +void MainFrame::OnEnterLoopPoints(wxCommandEvent& event) +{ + wxLogDebug("Loop point text changed"); +} + +void MainFrame::OnPaint(wxPaintEvent& event) +{ + wxPaintDC dc(m_TopWaveformPanel); + + const wxSize size = m_TopWaveformPanel->GetClientSize(); + + if(!m_WaveformBitmap.IsOk() + || m_WaveformBitmap.IsNull() + || m_WaveformBitmap.GetWidth() != size.x + || m_WaveformBitmap.GetHeight() != size.y) + { + m_WaveformBitmap = wxBitmap(wxImage(size.x, size.y), 32); + // m_WaveformBitmap.Create(size.x, size.y, 32); + wxLogDebug("Updating waveform bitmap.."); + UpdateWaveformBitmap(); + } + else + wxLogDebug("Cannot update waveform bitmap.."); + + dc.DrawBitmap(m_WaveformBitmap, 0, 0, false); + // m_WaveformBitmap.SaveFile("waveform.png", wxBITMAP_TYPE_PNG); + + RenderPlayhead(dc); +} + +void MainFrame::RenderPlayhead(wxDC& dc) +{ + int selected_row = m_Library->GetSelectedRow(); + + if (selected_row < 0) + return; + + wxString selected = m_Library->GetTextValue(selected_row, 1); + std::string path = GetFilenamePathAndExtension(selected).Path.ToStdString(); + + Tags tags(path); + + int length = tags.GetAudioInfo().length; + wxLogDebug("Len: %d", length); + + double position = m_MediaCtrl->Tell(); + wxLogDebug("Pos: %f", position); + + m_Timer->Start(1, wxTIMER_CONTINUOUS); + + int panel_width = m_TopWaveformPanel->GetSize().GetWidth(); + double line_pos = panel_width * (position / length); + + wxLogDebug("Drawing at: %f", line_pos); + + // Draw the triangle + dc.SetPen(wxPen(wxColor(255, 0, 0, 255), 8, wxPENSTYLE_SOLID)); + dc.DrawLine(line_pos - 5, m_TopWaveformPanel->GetSize().GetHeight() - (m_TopWaveformPanel->GetSize().GetHeight() - 1), + line_pos, m_TopWaveformPanel->GetSize().GetHeight() - (m_TopWaveformPanel->GetSize().GetHeight() - 1) + 5); + dc.DrawLine(line_pos + 5, m_TopWaveformPanel->GetSize().GetHeight() - (m_TopWaveformPanel->GetSize().GetHeight() - 1), + line_pos, m_TopWaveformPanel->GetSize().GetHeight() - (m_TopWaveformPanel->GetSize().GetHeight()- 1) + 5); + + // Draw the line + dc.SetPen(wxPen(wxColor(255, 0, 0, 255), 2, wxPENSTYLE_SOLID)); + dc.DrawLine(line_pos, m_TopWaveformPanel->GetSize().GetHeight() - (m_TopWaveformPanel->GetSize().GetHeight() - 1), + line_pos, m_TopWaveformPanel->GetSize().GetHeight() - 1); + + // For testing only + // dc.DrawLine(100 - 5, m_TopWaveformPanel->GetSize().GetHeight() - (m_TopWaveformPanel->GetSize().GetHeight() - 1), + // 100, m_TopWaveformPanel->GetSize().GetHeight() - (m_TopWaveformPanel->GetSize().GetHeight() - 1) + 5); + // dc.DrawLine(100 + 5, m_TopWaveformPanel->GetSize().GetHeight() - (m_TopWaveformPanel->GetSize().GetHeight() - 1), + // 100, m_TopWaveformPanel->GetSize().GetHeight() - (m_TopWaveformPanel->GetSize().GetHeight()- 1) + 5); + + // // Draw the line + // dc.SetPen(wxPen(wxColor(255, 0, 0, 255), 2, wxPENSTYLE_SOLID)); + // dc.DrawLine(100, m_TopWaveformPanel->GetSize().GetHeight() - (m_TopWaveformPanel->GetSize().GetHeight() - 1), + // 100, m_TopWaveformPanel->GetSize().GetHeight() - 1); +} + +void MainFrame::UpdateWaveformBitmap() +{ + Database db(*m_InfoBar); + Settings settings(this, m_ConfigFilepath, m_DatabaseFilepath); + + int selected_row = m_Library->GetSelectedRow(); + + if (selected_row < 0) + return; + + wxString selection = m_Library->GetTextValue(selected_row, 1); + + wxString filepath_with_extension = db.GetSamplePathByFilename(static_cast(DATABASE_FILEPATH), + selection.BeforeLast('.').ToStdString()); + wxString filepath_without_extension = db.GetSamplePathByFilename(static_cast(DATABASE_FILEPATH), + selection.ToStdString()); + + std::string extension = settings.ShouldShowFileExtension() ? + db.GetSampleFileExtension(static_cast(DATABASE_FILEPATH), selection.ToStdString()) : + db.GetSampleFileExtension(static_cast(DATABASE_FILEPATH), selection.BeforeLast('.').ToStdString()); + + wxString path = selection.Contains(wxString::Format(".%s", extension)) ? + filepath_with_extension : filepath_without_extension; + + SndfileHandle snd_file(path); + + int channels = snd_file.channels(); + double sample_rate = snd_file.samplerate(); + sf_count_t frames = snd_file.frames(); + + std::vector sample; + sample.resize(frames * channels); + + std::vector waveform; + + snd_file.read(&sample.at(0), frames * channels); + + float display_width = m_TopWaveformPanel->GetSize().GetWidth(); + float display_height = m_TopWaveformPanel->GetSize().GetHeight(); + + double max_value; + snd_file.command(SFC_CALC_NORM_SIGNAL_MAX, &max_value, sizeof(max_value)); + + float normalized_value = max_value > 1.0f ? 1.0f : 1.0f / max_value; + + float samples_per_pixel = static_cast(frames) / (float)display_width; + // float samples_per_pixel = waveform.size() / float(display_width); + + if (channels == 2) + { + for (int i = 0, j = 0 ; i < frames; i++) + { + float sum = (((sample[j] + sample[j + 1]) * 0.5f) * normalized_value) * float(display_height / 2.0f); + waveform.push_back(sum); + j += 2; + } + } + else + { + waveform.resize(frames); + + for (int i = 0; i < frames; i++) + { + waveform[i] = (sample[i] * normalized_value) * float(display_height / 2.0f); + } + } + + // float minimap_height = 0.0f; + // // make minimap + // std::vector miniMap; + // miniMap.resize(display_width); + // // float samples_per_pixel = static_cast(sampleLength) / (float)display_width; + // float fIndex; + // uint iIndex; + // for (uint16_t i = 0; i < display_width; i++) + // { + // fIndex = float(i) * samples_per_pixel; + // iIndex = fIndex; + // auto minmax = std::minmax_element(waveform.begin() + iIndex, waveform.begin() + iIndex + int(samples_per_pixel)); + // signed char min = std::abs(*minmax.first); + // signed char max = std::abs(*minmax.second); + // signed char maxValue = std::max(min, max); + // miniMap[i] = (float)maxValue / (float)(display_height / 2) * (float)minimap_height; + // } + + /*===========================Draw code============================*/ + wxMemoryDC mdc(m_WaveformBitmap); + + if (!mdc.IsOk()) + { + wxLogDebug("MDC NOT OK"); + return; + } + else + wxLogDebug("MDC OK"); + + mdc.SetPen(wxPen(wxColour(255, 255, 255, 255), 2, wxPENSTYLE_SOLID)); + + for(int i = 0; i < waveform.size() - 1; i++) + { + // dc.DrawPoint((display_width * static_cast(i)) / waveform.size(), + // (waveform[i] / samples_per_pixel) + float(display_height / 2.0f)); + + // dc.DrawLine((display_width * static_cast(i)) / waveform.size(), + // waveform[i] + float(display_height / 2.0f), + // (display_width * static_cast(i)) / waveform.size(), + // waveform[i] + float(display_height / 2.0f)); + + mdc.DrawLine((display_width * i) / waveform.size(), waveform[i] + display_height / 2.0f, + (display_width * i) / waveform.size(), (waveform[i] / samples_per_pixel) + display_height / 2.0f); + } + + // if (!mdc.IsOk()) + // return; + + // dc.Blit(m_TopWaveformPanel->GetSize().GetWidth() - (m_TopWaveformPanel->GetSize().GetWidth() - 1), + // m_TopWaveformPanel->GetSize().GetHeight() - (m_TopWaveformPanel->GetSize().GetHeight() - 1), + // m_WaveformBitmap.GetWidth(), m_WaveformBitmap.GetHeight(), &mdc, 0, 0); +} + +void MainFrame::OnHoverPlayhead(wxMouseEvent& event) +{ + int selected_row = m_Library->GetSelectedRow(); + + if (selected_row < 0) + return; + + wxString selected = m_Library->GetTextValue(selected_row, 1); + std::string path = GetFilenamePathAndExtension(selected).Path.ToStdString(); + + Tags tags(path); + + int length = tags.GetAudioInfo().length; + + double position = m_MediaCtrl->Tell(); + + int panel_width = m_TopWaveformPanel->GetSize().GetWidth(); + double line_pos = panel_width * (position / length); + + wxPoint pos = event.GetPosition(); + + if (pos.x >= line_pos - (line_pos - 5) && pos.x >= line_pos - (line_pos + 5) && pos.y <= line_pos + 5 && pos.y >= line_pos - 5) + { + this->SetCursor(wxCursor(wxCURSOR_CROSS)); + wxLogDebug("Capturing mouse.."); + // SetBackgroundColour("Green"); + } + else + { + SetCursor(wxCursor(wxCURSOR_ARROW)); + // SetBackgroundColour("Brown"); + } + + wxLogDebug("Mouse at: '(%d, %d)'", pos.x, pos.y); +} + MainFrame::~MainFrame(){} diff --git a/src/MainFrame.hpp b/src/MainFrame.hpp index dfa4bfa..846cfd1 100644 --- a/src/MainFrame.hpp +++ b/src/MainFrame.hpp @@ -106,16 +106,22 @@ class MainFrame : public wxFrame // ------------------------------------------------------------------- // Top panel controls wxPanel* m_TopPanel; + wxPanel* m_TopWaveformPanel; + wxPanel* m_TopControlsPanel; wxBoxSizer* m_TopSizer; wxBoxSizer* m_TopPanelMainSizer; wxBoxSizer* m_WaveformDisplaySizer; - wxStaticBitmap* m_WaveformViewer; + wxBitmap m_WaveformBitmap ; wxBoxSizer* m_BrowserControlSizer; wxButton* m_PlayButton; wxToggleButton* m_LoopButton; wxButton* m_StopButton; wxButton* m_SettingsButton; wxToggleButton* m_MuteButton; + wxToggleButton* m_LoopPointAButton; + wxToggleButton* m_LoopPointBButton; + wxTextCtrl* m_LoopPointAText; + wxTextCtrl* m_LoopPointBText; wxStaticText* m_SamplePosition; wxSlider* m_VolumeSlider; wxCheckBox* m_AutoPlayCheck; @@ -186,6 +192,8 @@ class MainFrame : public wxFrame void OnSlideVolume(wxScrollEvent& event); void OnReleaseVolumeSlider(wxScrollEvent& event); void OnClickSettings(wxCommandEvent& event); + void OnClickLoopPointsButton(wxCommandEvent& event); + void OnEnterLoopPoints(wxCommandEvent& event); // ------------------------------------------------------------------- // DirCtrl event handlers @@ -264,6 +272,12 @@ class MainFrame : public wxFrame // Call after frame creation void SetAfterFrameCreate(); + // Drawing playhead and waveform + void OnPaint(wxPaintEvent& event); + void RenderPlayhead(wxDC& dc); + void UpdateWaveformBitmap(); + void OnHoverPlayhead(wxMouseEvent& event); + // ------------------------------------------------------------------- friend class App; };