Waveform panel moved to its own class, add ability to change waveform color and can seek the playhead now.
This commit is contained in:
parent
8aac911f07
commit
af929cecc2
|
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 122.88 95.13" style="enable-background:new 0 0 122.88 95.13" xml:space="preserve"><g><path d="M100.95,23.32c0-2.09,1.69-3.78,3.78-3.78c2.09,0,3.78,1.69,3.78,3.78v48.5c0,2.09-1.69,3.78-3.78,3.78 c-2.09,0-3.78-1.69-3.78-3.78V23.32L100.95,23.32z M0,31.82c0-2.09,1.69-3.78,3.78-3.78c2.09,0,3.78,1.69,3.78,3.78v31.49 c0,2.09-1.69,3.78-3.78,3.78C1.69,67.09,0,65.4,0,63.31V31.82L0,31.82z M14.42,23.32c0-2.09,1.69-3.78,3.78-3.78 c2.09,0,3.78,1.69,3.78,3.78v48.5c0,2.09-1.69,3.78-3.78,3.78c-2.09,0-3.78-1.69-3.78-3.78V23.32L14.42,23.32z M28.9,13.9 c0-2.08,1.67-3.76,3.72-3.76c2.06,0,3.72,1.68,3.72,3.76v67.34c0,2.08-1.67,3.76-3.72,3.76c-2.06,0-3.72-1.68-3.72-3.76V13.9 L28.9,13.9z M43.26,3.78c0-2.09,1.69-3.78,3.78-3.78c2.09,0,3.78,1.69,3.78,3.78v87.57c0,2.09-1.69,3.78-3.78,3.78 c-2.09,0-3.78-1.69-3.78-3.78V3.78L43.26,3.78z M86.53,31.82c0-2.09,1.69-3.78,3.78-3.78c2.09,0,3.78,1.69,3.78,3.78v31.49 c0,2.09-1.69,3.78-3.78,3.78c-2.09,0-3.78-1.69-3.78-3.78V31.82L86.53,31.82z M72.11,23.32c0-2.09,1.69-3.78,3.78-3.78 c2.09,0,3.78,1.69,3.78,3.78v48.5c0,2.09-1.69,3.78-3.78,3.78c-2.09,0-3.78-1.69-3.78-3.78V23.32L72.11,23.32z M57.74,13.9 c0-2.08,1.67-3.76,3.72-3.76c2.06,0,3.72,1.68,3.72,3.76v67.34c0,2.08-1.67,3.76-3.72,3.76c-2.06,0-3.72-1.68-3.72-3.76V13.9 L57.74,13.9z M115.43,13.9c0-2.08,1.67-3.76,3.72-3.76c2.06,0,3.72,1.68,3.72,3.76v67.34c0,2.08-1.67,3.76-3.72,3.76 c-2.06,0-3.72-1.68-3.72-3.76V13.9L115.43,13.9z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
|
|
@ -57,6 +57,7 @@ src = [
|
|||
'src/Sample.cpp',
|
||||
'src/Serialize.cpp',
|
||||
'src/Tags.cpp',
|
||||
'src/WaveformViewer.cpp',
|
||||
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ enum ControlIDs
|
|||
SD_FontType,
|
||||
SD_FontSize,
|
||||
SD_FontBrowseButton,
|
||||
SD_WaveformColourPickerCtrl,
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// App Menu items
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
#include <wx/variant.h>
|
||||
|
||||
#include "Database.hpp"
|
||||
#include "SettingsDialog.hpp"
|
||||
|
||||
Database::Database(wxInfoBar& infoBar)
|
||||
: m_InfoBar(infoBar)
|
||||
|
|
|
|||
|
|
@ -31,9 +31,6 @@
|
|||
#include <wx/artprov.h>
|
||||
#include <wx/busyinfo.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/dcclient.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
#include <wx/dc.h>
|
||||
#include <wx/debug.h>
|
||||
#include <wx/defs.h>
|
||||
#include <wx/dir.h>
|
||||
|
|
@ -52,20 +49,15 @@
|
|||
#include <wx/menu.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/object.h>
|
||||
#include <wx/pen.h>
|
||||
#include <wx/progdlg.h>
|
||||
#include <wx/stdpaths.h>
|
||||
#include <wx/stringimpl.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/textdlg.h>
|
||||
#include <wx/valtext.h>
|
||||
#include <wx/variant.h>
|
||||
#include <wx/vector.h>
|
||||
#include <wx/utils.h>
|
||||
#include <wx/unix/stdpaths.h>
|
||||
#include <wx/wxcrt.h>
|
||||
|
||||
#include <sndfile.hh>
|
||||
|
||||
#include "MainFrame.hpp"
|
||||
#include "ControlID_Enums.hpp"
|
||||
|
|
@ -86,7 +78,6 @@
|
|||
#define ICON_HIVE_256px SAMPLEHIVE_DATADIR "/assets/icons/icon-hive_256x256.png"
|
||||
#define ICON_STAR_FILLED_16px SAMPLEHIVE_DATADIR "/assets/icons/icon-star_filled_16x16.png"
|
||||
#define ICON_STAR_EMPTY_16px SAMPLEHIVE_DATADIR "/assets/icons/icon-star_empty_16x16.png"
|
||||
#define WAVEFORM_SVG SAMPLEHIVE_DATADIR "/assets/waveform.svg"
|
||||
#define APP_CONFIG_DIR wxStandardPaths::Get().GetUserConfigDir() + "/.config/SampleHive"
|
||||
#define APP_DATA_DIR wxStandardPaths::Get().GetDocumentsDir() + "/.local/share/SampleHive"
|
||||
#define CONFIG_FILEPATH APP_CONFIG_DIR + "/config.yaml"
|
||||
|
|
@ -254,7 +245,6 @@ 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
|
||||
|
|
@ -283,11 +273,6 @@ MainFrame::MainFrame()
|
|||
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_WaveformBitmap.LoadFile(WAVEFORM_SVG);
|
||||
// m_WaveformViewer = new wxStaticBitmap(m_TopWaveformPanel, wxID_ANY, m_WaveformBitmap);
|
||||
|
||||
// Initialize browser control buttons
|
||||
m_PlayButton = new wxButton(m_TopControlsPanel, BC_Play, _("Play"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_PlayButton->SetToolTip(_("Play"));
|
||||
|
|
@ -394,6 +379,8 @@ MainFrame::MainFrame()
|
|||
// Intializing wxTimer
|
||||
m_Timer = new wxTimer(this);
|
||||
|
||||
m_TopWaveformPanel = new WaveformViewer(this, m_TopPanel, *m_Library, *m_MediaCtrl, *m_Timer, *m_InfoBar, m_ConfigFilepath, m_DatabaseFilepath);
|
||||
|
||||
// Binding events.
|
||||
Bind(wxEVT_MENU, &MainFrame::OnSelectAddFile, this, MN_AddFile);
|
||||
Bind(wxEVT_MENU, &MainFrame::OnSelectAddDirectory, this, MN_AddDirectory);
|
||||
|
|
@ -446,9 +433,6 @@ 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);
|
||||
|
||||
|
|
@ -469,8 +453,6 @@ MainFrame::MainFrame()
|
|||
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_TopPanelMainSizer->Add(m_TopWaveformPanel, 1, wxALL | wxEXPAND, 2);
|
||||
m_TopPanelMainSizer->Add(m_TopControlsPanel, 0, wxALL | wxEXPAND, 2);
|
||||
|
||||
|
|
@ -569,6 +551,11 @@ void MainFrame::OnClickSettings(wxCommandEvent& event)
|
|||
OnAutoImportDir(settings->GetImportDirPath());
|
||||
RefreshDatabase();
|
||||
}
|
||||
if (settings->IsWaveformColourChanged())
|
||||
{
|
||||
m_TopWaveformPanel->SetBitmapDirty(true);
|
||||
m_TopWaveformPanel->Refresh();
|
||||
}
|
||||
break;
|
||||
case wxID_CANCEL:
|
||||
break;
|
||||
|
|
@ -957,6 +944,10 @@ void MainFrame::OnClickPlay(wxCommandEvent& event)
|
|||
|
||||
PushStatusText(wxString::Format(_("Now playing: %s"), selection), 1);
|
||||
|
||||
// Update waveform bitmap
|
||||
m_TopWaveformPanel->SetBitmapDirty(true);
|
||||
m_TopWaveformPanel->Refresh();
|
||||
|
||||
m_MediaCtrl->Play();
|
||||
|
||||
m_Timer->Start(100, wxTIMER_CONTINUOUS);
|
||||
|
|
@ -1045,7 +1036,7 @@ void MainFrame::UpdateElapsedTime(wxTimerEvent& event)
|
|||
|
||||
m_SamplePosition->SetLabel(wxString::Format(wxT("%s/%s"), position.c_str(), duration.c_str()));
|
||||
|
||||
Refresh();
|
||||
m_TopWaveformPanel->Refresh();
|
||||
}
|
||||
|
||||
void MainFrame::OnCheckAutoplay(wxCommandEvent& event)
|
||||
|
|
@ -1105,6 +1096,10 @@ void MainFrame::OnClickLibrary(wxDataViewEvent& event)
|
|||
return;
|
||||
}
|
||||
|
||||
// Update the waveform bitmap
|
||||
m_TopWaveformPanel->SetBitmapDirty(true);
|
||||
m_TopWaveformPanel->Refresh();
|
||||
|
||||
wxString selection = m_Library->GetTextValue(selected_row, 1);
|
||||
|
||||
// Get curremt column
|
||||
|
|
@ -2947,234 +2942,4 @@ 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<std::string>(DATABASE_FILEPATH),
|
||||
selection.BeforeLast('.').ToStdString());
|
||||
wxString filepath_without_extension = db.GetSamplePathByFilename(static_cast<std::string>(DATABASE_FILEPATH),
|
||||
selection.ToStdString());
|
||||
|
||||
std::string extension = settings.ShouldShowFileExtension() ?
|
||||
db.GetSampleFileExtension(static_cast<std::string>(DATABASE_FILEPATH), selection.ToStdString()) :
|
||||
db.GetSampleFileExtension(static_cast<std::string>(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<float> sample;
|
||||
sample.resize(frames * channels);
|
||||
|
||||
std::vector<signed char> 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<float>(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<char> miniMap;
|
||||
// miniMap.resize(display_width);
|
||||
// // float samples_per_pixel = static_cast<float>(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<float>(i)) / waveform.size(),
|
||||
// (waveform[i] / samples_per_pixel) + float(display_height / 2.0f));
|
||||
|
||||
// dc.DrawLine((display_width * static_cast<float>(i)) / waveform.size(),
|
||||
// waveform[i] + float(display_height / 2.0f),
|
||||
// (display_width * static_cast<float>(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(){}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@
|
|||
#include <taglib/fileref.h>
|
||||
#include <taglib/tstring.h>
|
||||
|
||||
#include "WaveformViewer.hpp"
|
||||
|
||||
struct FileInfo
|
||||
{
|
||||
wxString Path;
|
||||
|
|
@ -106,12 +108,11 @@ class MainFrame : public wxFrame
|
|||
// -------------------------------------------------------------------
|
||||
// Top panel controls
|
||||
wxPanel* m_TopPanel;
|
||||
wxPanel* m_TopWaveformPanel;
|
||||
WaveformViewer* m_TopWaveformPanel;
|
||||
wxPanel* m_TopControlsPanel;
|
||||
wxBoxSizer* m_TopSizer;
|
||||
wxBoxSizer* m_TopPanelMainSizer;
|
||||
wxBoxSizer* m_WaveformDisplaySizer;
|
||||
wxBitmap m_WaveformBitmap ;
|
||||
wxBoxSizer* m_BrowserControlSizer;
|
||||
wxButton* m_PlayButton;
|
||||
wxToggleButton* m_LoopButton;
|
||||
|
|
@ -272,12 +273,6 @@ 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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include <wx/colour.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/stdpaths.h>
|
||||
#include <wx/filename.h>
|
||||
|
|
@ -39,6 +40,8 @@ Serializer::Serializer(const std::string& filepath)
|
|||
std::string system_font_face = font.GetFaceName().ToStdString();
|
||||
int system_font_size = font.GetPointSize();
|
||||
|
||||
wxColour colour = "#FE9647";
|
||||
|
||||
std::string dir = wxStandardPaths::Get().GetDocumentsDir().ToStdString();
|
||||
|
||||
if (!ifstrm)
|
||||
|
|
@ -73,6 +76,11 @@ Serializer::Serializer(const std::string& filepath)
|
|||
m_Emitter << YAML::EndMap;
|
||||
m_Emitter << YAML::EndMap << YAML::Newline;
|
||||
|
||||
m_Emitter << YAML::Newline << YAML::Key << "Waveform";
|
||||
m_Emitter << YAML::BeginMap;
|
||||
m_Emitter << YAML::Key << "Colour" << YAML::Value << colour.GetAsString().ToStdString();
|
||||
m_Emitter << YAML::EndMap << YAML::Newline;
|
||||
|
||||
m_Emitter << YAML::Newline << YAML::Key << "Collection";
|
||||
m_Emitter << YAML::BeginMap;
|
||||
m_Emitter << YAML::Key << "AutoImport" << YAML::Value << false;
|
||||
|
|
@ -249,6 +257,64 @@ FontType Serializer::DeserializeDisplaySettings() const
|
|||
return { face, size };
|
||||
}
|
||||
|
||||
void Serializer::SerializeWaveformColour(wxColour& colour)
|
||||
{
|
||||
YAML::Emitter out;
|
||||
|
||||
std::string colour_string = colour.GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
|
||||
|
||||
try
|
||||
{
|
||||
YAML::Node config = YAML::LoadFile(m_Filepath);
|
||||
|
||||
if (auto waveform = config["Waveform"])
|
||||
{
|
||||
wxLogDebug("Changing waveform colour");
|
||||
wxLogDebug("Waveform colour: %s", colour_string);
|
||||
|
||||
waveform["Colour"] = colour_string;
|
||||
|
||||
out << config;
|
||||
|
||||
std::ofstream ofstrm(m_Filepath);
|
||||
ofstrm << out.c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogDebug("Error! Cannot store waveform colour.");
|
||||
}
|
||||
}
|
||||
catch(const YAML::ParserException& ex)
|
||||
{
|
||||
std::cout << ex.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
wxColour Serializer::DeserializeWaveformColour() const
|
||||
{
|
||||
std::string colour;
|
||||
|
||||
try
|
||||
{
|
||||
YAML::Node config = YAML::LoadFile(m_Filepath);
|
||||
|
||||
if (auto waveform = config["Waveform"])
|
||||
{
|
||||
colour = waveform["Colour"].as<std::string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogDebug("Error! Cannot fetch waveform colour.");
|
||||
}
|
||||
}
|
||||
catch(const YAML::ParserException& ex)
|
||||
{
|
||||
std::cout << ex.what() << std::endl;
|
||||
}
|
||||
|
||||
return static_cast<wxString>(colour);
|
||||
}
|
||||
|
||||
void Serializer::SerializeAutoImportSettings(wxTextCtrl& textCtrl, wxCheckBox& checkBox)
|
||||
{
|
||||
YAML::Emitter out;
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ class Serializer
|
|||
// Display settings
|
||||
void SerializeDisplaySettings(wxFont& font);
|
||||
FontType DeserializeDisplaySettings() const;
|
||||
void SerializeWaveformColour(wxColour& colour);
|
||||
wxColour DeserializeWaveformColour() const;
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Auto import settings
|
||||
|
|
|
|||
|
|
@ -51,8 +51,10 @@ Settings::Settings(wxWindow* window, const std::string& configFilepath, const st
|
|||
|
||||
m_DisplayTopSizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_DisplayFontSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_WaveformColourSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
wxString fontChoices[] = {"System default"};
|
||||
Serializer serializer(m_ConfigFilepath);
|
||||
// m_RowHeightText = new wxStaticText();
|
||||
m_FontTypeText = new wxStaticText(m_DisplaySettingPanel, wxID_ANY, "Font", wxDefaultPosition, wxDefaultSize, 0);
|
||||
// m_RowHeight = new wxChoice();
|
||||
|
|
@ -61,6 +63,9 @@ Settings::Settings(wxWindow* window, const std::string& configFilepath, const st
|
|||
m_FontSize = new wxSpinCtrl(m_DisplaySettingPanel, SD_FontSize, "Default", wxDefaultPosition, wxDefaultSize);
|
||||
m_FontSize->SetValue(window->GetFont().GetPointSize());
|
||||
m_FontBrowseButton = new wxButton(m_DisplaySettingPanel, SD_FontBrowseButton, "Select font", wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_WaveformColourLabel = new wxStaticText(m_DisplaySettingPanel, wxID_ANY, "Waveform colour", wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_WaveformColourPickerCtrl = new wxColourPickerCtrl(m_DisplaySettingPanel, SD_WaveformColourPickerCtrl, serializer.DeserializeWaveformColour(),
|
||||
wxDefaultPosition, wxDefaultSize, wxCLRP_DEFAULT_STYLE);
|
||||
|
||||
m_CollectionSettingPanel = new wxPanel(m_Notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
|
||||
|
|
@ -108,6 +113,7 @@ Settings::Settings(wxWindow* window, const std::string& configFilepath, const st
|
|||
Bind(wxEVT_BUTTON, &Settings::OnClickBrowseAutoImportDir, this, SD_BrowseAutoImportDir);
|
||||
Bind(wxEVT_BUTTON, &Settings::OnClickConfigBrowse, this, SD_BrowseConfigDir);
|
||||
Bind(wxEVT_BUTTON, &Settings::OnClickDatabaseBrowse, this, SD_BrowseDatabaseDir);
|
||||
Bind(wxEVT_COLOURPICKER_CHANGED, &Settings::OnChangeWaveformColour, this, SD_WaveformColourPickerCtrl);
|
||||
|
||||
// Adding controls to sizers
|
||||
m_NotebookSizer->Add(m_Notebook, 1, wxALL | wxEXPAND, 2);
|
||||
|
|
@ -124,8 +130,11 @@ Settings::Settings(wxWindow* window, const std::string& configFilepath, const st
|
|||
m_DisplayFontSizer->Add(m_FontType, 1, wxALL | wxALIGN_CENTER_VERTICAL, 2);
|
||||
m_DisplayFontSizer->Add(m_FontSize, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
|
||||
m_DisplayFontSizer->Add(m_FontBrowseButton, 0, wxALL | wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 2);
|
||||
m_WaveformColourSizer->Add(m_WaveformColourLabel, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
|
||||
m_WaveformColourSizer->Add(m_WaveformColourPickerCtrl, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
|
||||
|
||||
m_DisplayTopSizer->Add(m_DisplayFontSizer, 1, wxALL | wxEXPAND, 2);
|
||||
m_DisplayTopSizer->Add(m_DisplayFontSizer, 0, wxALL | wxEXPAND, 2);
|
||||
m_DisplayTopSizer->Add(m_WaveformColourSizer, 0, wxALL | wxEXPAND, 2);
|
||||
|
||||
m_CollectionImportDirSizer->Add(m_AutoImportCheck, 0, wxALL | wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT, 2);
|
||||
m_CollectionImportDirSizer->Add(m_ImportDirLocation, 1, wxALL | wxALIGN_CENTER_VERTICAL, 2);
|
||||
|
|
@ -426,4 +435,25 @@ wxString Settings::GetImportDirPath()
|
|||
return dir;
|
||||
}
|
||||
|
||||
void Settings::OnChangeWaveformColour(wxColourPickerEvent& event)
|
||||
{
|
||||
Serializer serializer(m_ConfigFilepath);
|
||||
wxColour colour = m_WaveformColourPickerCtrl->GetColour();
|
||||
|
||||
wxColour wave_colour = serializer.DeserializeWaveformColour();
|
||||
|
||||
if (colour != wave_colour)
|
||||
{
|
||||
wxLogDebug("Waveform colour changed.");
|
||||
bWaveformColourChanged = true;
|
||||
|
||||
serializer.SerializeWaveformColour(colour);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogDebug("Waveform colour not changed.");
|
||||
bWaveformColourChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
Settings::~Settings(){}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <string>
|
||||
|
||||
#include <wx/button.h>
|
||||
#include <wx/clrpicker.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/dialog.h>
|
||||
|
|
@ -86,6 +87,9 @@ class Settings : public wxDialog
|
|||
wxFontDialog* m_FontDialog;
|
||||
wxButton* m_FontBrowseButton;
|
||||
wxSpinCtrl* m_FontSize;
|
||||
wxBoxSizer* m_WaveformColourSizer;
|
||||
wxStaticText* m_WaveformColourLabel;
|
||||
wxColourPickerCtrl* m_WaveformColourPickerCtrl;
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Collection page
|
||||
|
|
@ -121,6 +125,7 @@ class Settings : public wxDialog
|
|||
// -------------------------------------------------------------------
|
||||
bool bAutoImport = false;
|
||||
bool bShowExtension = true;
|
||||
bool bWaveformColourChanged = false;
|
||||
|
||||
private:
|
||||
// -------------------------------------------------------------------
|
||||
|
|
@ -131,6 +136,7 @@ class Settings : public wxDialog
|
|||
void OnClickBrowseAutoImportDir(wxCommandEvent& event);
|
||||
void OnChangeFontSize(wxSpinEvent& event);
|
||||
void OnSelectFont(wxCommandEvent& event);
|
||||
void OnChangeWaveformColour(wxColourPickerEvent& event);
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
void SetCustomFont();
|
||||
|
|
@ -147,4 +153,6 @@ class Settings : public wxDialog
|
|||
inline wxFont GetFontType() { return m_Font; };
|
||||
inline bool CanAutoImport() { return bAutoImport; };
|
||||
inline bool ShouldShowFileExtension() { return bShowExtension; };
|
||||
inline bool IsWaveformColourChanged() { return bWaveformColourChanged; }
|
||||
inline wxColour GetWaveformColour() { return m_WaveformColourPickerCtrl->GetColour(); }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,320 @@
|
|||
/* SampleHive
|
||||
* Copyright (C) 2021 Apoorv Singh
|
||||
* A simple, modern audio sample browser/manager for GNU/Linux.
|
||||
*
|
||||
* This file is a part of SampleHive
|
||||
*
|
||||
* SampleHive 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* SampleHive 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 <vector>
|
||||
|
||||
#include <wx/dcclient.h>
|
||||
#include <wx/dcmemory.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include <sndfile.hh>
|
||||
|
||||
#include "WaveformViewer.hpp"
|
||||
#include "Database.hpp"
|
||||
#include "SettingsDialog.hpp"
|
||||
#include "Serialize.hpp"
|
||||
#include "Tags.hpp"
|
||||
#include "wx/filefn.h"
|
||||
|
||||
WaveformViewer::WaveformViewer(wxWindow* parentFrame, wxWindow* window, wxDataViewListCtrl& library, wxMediaCtrl& mediaCtrl,
|
||||
wxTimer& timer, wxInfoBar& infoBar, const std::string& configFilepath, const std::string& databaseFilepath)
|
||||
: wxPanel(window, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER | wxFULL_REPAINT_ON_RESIZE),
|
||||
m_ParentFrame(parentFrame), m_Window(window), m_Library(library), m_MediaCtrl(mediaCtrl), m_Timer(timer),
|
||||
m_InfoBar(infoBar), m_ConfigFilepath(configFilepath), m_DatabaseFilepath(databaseFilepath)
|
||||
{
|
||||
Bind(wxEVT_PAINT, &WaveformViewer::OnPaint, this);
|
||||
Bind(wxEVT_MOTION, &WaveformViewer::OnHoverPlayhead, this);
|
||||
Bind(wxEVT_LEFT_DOWN, &WaveformViewer::OnGrabPlayhead, this);
|
||||
Bind(wxEVT_LEFT_UP, &WaveformViewer::OnReleasePlayhead, this);
|
||||
}
|
||||
|
||||
WaveformViewer::~WaveformViewer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void WaveformViewer::OnPaint(wxPaintEvent& event)
|
||||
{
|
||||
wxPaintDC dc(this);
|
||||
|
||||
const wxSize& size = m_Window->GetClientSize();
|
||||
|
||||
if(!m_WaveformBitmap.IsOk()
|
||||
|| m_WaveformBitmap.GetWidth() != size.x
|
||||
|| m_WaveformBitmap.GetHeight() != size.y
|
||||
|| bBitmapDirty)
|
||||
{
|
||||
wxLogDebug("Updating waveform bitmap..");
|
||||
|
||||
m_WaveformBitmap = wxBitmap(wxImage(size.x, size.y), 32);
|
||||
// m_WaveformBitmap.Create(size.x, size.y, 32);
|
||||
|
||||
UpdateWaveformBitmap();
|
||||
|
||||
bBitmapDirty = false;
|
||||
}
|
||||
|
||||
dc.DrawBitmap(m_WaveformBitmap, 0, 0, false);
|
||||
// m_WaveformBitmap.SaveFile("waveform.png", wxBITMAP_TYPE_PNG);
|
||||
|
||||
RenderPlayhead(dc);
|
||||
}
|
||||
|
||||
void WaveformViewer::RenderPlayhead(wxDC& dc)
|
||||
{
|
||||
Database db(m_InfoBar);
|
||||
|
||||
int selected_row = m_Library.GetSelectedRow();
|
||||
|
||||
if (selected_row < 0)
|
||||
return;
|
||||
|
||||
wxString selected = m_Library.GetTextValue(selected_row, 1);
|
||||
std::string path = db.GetSamplePathByFilename(m_DatabaseFilepath, selected.BeforeLast('.').ToStdString());
|
||||
|
||||
Tags tags(path);
|
||||
|
||||
int length = tags.GetAudioInfo().length;
|
||||
// wxLogDebug("Sample length: %d", length);
|
||||
|
||||
double position = m_MediaCtrl.Tell();
|
||||
// wxLogDebug("Current Sample Position: %f", position);
|
||||
|
||||
m_Timer.Start(5, wxTIMER_CONTINUOUS);
|
||||
|
||||
int panel_width = this->GetSize().GetWidth();
|
||||
double line_pos = panel_width * (position / length);
|
||||
|
||||
// wxLogDebug("Drawing playhead at: %f", line_pos);
|
||||
|
||||
m_PlayheadColour = wxColor(255, 0, 0, 255);
|
||||
|
||||
// Draw the triangle
|
||||
dc.SetPen(wxPen(m_PlayheadColour, 8, wxPENSTYLE_SOLID));
|
||||
dc.DrawLine(line_pos - 5, this->GetSize().GetHeight() - (this->GetSize().GetHeight() - 1),
|
||||
line_pos, this->GetSize().GetHeight() - (this->GetSize().GetHeight() - 1) + 5);
|
||||
dc.DrawLine(line_pos + 5, this->GetSize().GetHeight() - (this->GetSize().GetHeight() - 1),
|
||||
line_pos, this->GetSize().GetHeight() - (this->GetSize().GetHeight()- 1) + 5);
|
||||
|
||||
// Draw the line
|
||||
dc.SetPen(wxPen(m_PlayheadColour, 2, wxPENSTYLE_SOLID));
|
||||
dc.DrawLine(line_pos, this->GetSize().GetHeight() - (this->GetSize().GetHeight() - 1),
|
||||
line_pos, this->GetSize().GetHeight() - 1);
|
||||
}
|
||||
|
||||
void WaveformViewer::UpdateWaveformBitmap()
|
||||
{
|
||||
Database db(m_InfoBar);
|
||||
Settings settings(m_ParentFrame, m_ConfigFilepath, m_DatabaseFilepath);
|
||||
Serializer serializer(m_ConfigFilepath);
|
||||
|
||||
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(m_DatabaseFilepath, selection.BeforeLast('.').ToStdString());
|
||||
wxString filepath_without_extension = db.GetSamplePathByFilename(m_DatabaseFilepath, selection.ToStdString());
|
||||
|
||||
std::string extension = settings.ShouldShowFileExtension() ?
|
||||
db.GetSampleFileExtension(m_DatabaseFilepath, selection.ToStdString()) :
|
||||
db.GetSampleFileExtension(m_DatabaseFilepath, 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<float> sample;
|
||||
sample.resize(frames * channels);
|
||||
|
||||
std::vector<float> waveform;
|
||||
|
||||
snd_file.read(&sample.at(0), frames * channels);
|
||||
|
||||
float display_width = this->GetSize().GetWidth();
|
||||
float display_height = this->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<float>(frames) / (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);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw code
|
||||
wxMemoryDC mdc(m_WaveformBitmap);
|
||||
|
||||
mdc.SetBrush(*wxBLACK);
|
||||
mdc.Clear();
|
||||
|
||||
m_WaveformColour = serializer.DeserializeWaveformColour();
|
||||
|
||||
wxLogDebug("Changing waveform colour to: %s", m_WaveformColour.GetAsString());
|
||||
mdc.SetPen(wxPen(wxColour(m_WaveformColour), 2, wxPENSTYLE_SOLID));
|
||||
|
||||
wxLogDebug("Drawing bitmap..");
|
||||
|
||||
for(int i = 0; i < waveform.size() - 1; i++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
wxLogDebug("Done drawing bitmap..");
|
||||
}
|
||||
|
||||
void WaveformViewer::OnHoverPlayhead(wxMouseEvent& event)
|
||||
{
|
||||
Database db(m_InfoBar);
|
||||
|
||||
int selected_row = m_Library.GetSelectedRow();
|
||||
|
||||
if (selected_row < 0)
|
||||
return;
|
||||
|
||||
wxString selected = m_Library.GetTextValue(selected_row, 1);
|
||||
std::string path = db.GetSamplePathByFilename(m_DatabaseFilepath, selected.BeforeLast('.').ToStdString());
|
||||
|
||||
Tags tags(path);
|
||||
|
||||
int length = tags.GetAudioInfo().length;
|
||||
// wxLogDebug("Sample length: %d", length);
|
||||
|
||||
double position = m_MediaCtrl.Tell();
|
||||
// wxLogDebug("Current Sample Position: %f", position);
|
||||
|
||||
int panel_width = this->GetSize().GetWidth();
|
||||
double line_pos = panel_width * (position / length);
|
||||
|
||||
wxPoint pos = event.GetPosition();
|
||||
// wxLogDebug("PosX: %d", pos.x);
|
||||
|
||||
double seek_to = ((double)pos.x / panel_width) * length;
|
||||
// wxLogDebug("Seeking to: %f", seek_to);
|
||||
|
||||
if (abs(pos.x - line_pos) <= 5 && pos.y <= 5)
|
||||
{
|
||||
SetCursor(wxCursor(wxCURSOR_CLOSED_HAND));
|
||||
wxLogDebug("Cursor on playhead..");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCursor(wxCURSOR_ARROW);
|
||||
return;
|
||||
}
|
||||
|
||||
wxLogDebug("Mouse at: '(%d, %d)'", pos.x, pos.y);
|
||||
}
|
||||
|
||||
void WaveformViewer::OnGrabPlayhead(wxMouseEvent& event)
|
||||
{
|
||||
Database db(m_InfoBar);
|
||||
int selected_row = m_Library.GetSelectedRow();
|
||||
|
||||
if (selected_row < 0)
|
||||
return;
|
||||
|
||||
wxString selected = m_Library.GetTextValue(selected_row, 1);
|
||||
std::string path = db.GetSamplePathByFilename(m_DatabaseFilepath, selected.BeforeLast('.').ToStdString());
|
||||
|
||||
Tags tags(path);
|
||||
|
||||
int length = tags.GetAudioInfo().length;
|
||||
|
||||
double position = m_MediaCtrl.Tell();
|
||||
|
||||
int panel_width = this->GetSize().GetWidth();
|
||||
double line_pos = panel_width * (position / length);
|
||||
|
||||
wxPoint pos = event.GetPosition();
|
||||
|
||||
if (abs(pos.x - line_pos) <= 5 && pos.y <= 5)
|
||||
{
|
||||
wxWindow::CaptureMouse();
|
||||
|
||||
SetCursor(wxCURSOR_CLOSED_HAND);
|
||||
wxLogDebug("Mouse Captured playhead..");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCursor(wxCURSOR_ARROW);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void WaveformViewer::OnReleasePlayhead(wxMouseEvent& event)
|
||||
{
|
||||
Database db(m_InfoBar);
|
||||
|
||||
int selected_row = m_Library.GetSelectedRow();
|
||||
|
||||
if (selected_row < 0)
|
||||
return;
|
||||
|
||||
wxString selected = m_Library.GetTextValue(selected_row, 1);
|
||||
std::string path = db.GetSamplePathByFilename(m_DatabaseFilepath, selected.BeforeLast('.').ToStdString());
|
||||
|
||||
Tags tags(path);
|
||||
|
||||
int length = tags.GetAudioInfo().length;
|
||||
|
||||
double position = m_MediaCtrl.Tell();
|
||||
|
||||
int panel_width = this->GetSize().GetWidth();
|
||||
double line_pos = panel_width * (position / length);
|
||||
|
||||
wxPoint pos = event.GetPosition();
|
||||
|
||||
double seek_to = ((double)pos.x / panel_width) * length;
|
||||
|
||||
wxWindow::ReleaseMouse();
|
||||
SetCursor(wxCURSOR_ARROW);
|
||||
wxLogDebug("Mouse released playhead..");
|
||||
|
||||
m_MediaCtrl.Seek(seek_to, wxFromCurrent);
|
||||
// m_MediaCtrl.Play();
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/* SampleHive
|
||||
* Copyright (C) 2021 Apoorv Singh
|
||||
* A simple, modern audio sample browser/manager for GNU/Linux.
|
||||
*
|
||||
* This file is a part of SampleHive
|
||||
*
|
||||
* SampleHive 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* SampleHive 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/dc.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/infobar.h>
|
||||
#include <wx/mediactrl.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/window.h>
|
||||
|
||||
class WaveformViewer : public wxPanel
|
||||
{
|
||||
public:
|
||||
WaveformViewer(wxWindow* parentFrame, wxWindow* window, wxDataViewListCtrl& library, wxMediaCtrl& mediaCtrl,
|
||||
wxTimer& timer, wxInfoBar& infoBar, const std::string& configFilepath, const std::string& databaseFilepath);
|
||||
~WaveformViewer();
|
||||
|
||||
private:
|
||||
// -------------------------------------------------------------------
|
||||
wxWindow* m_ParentFrame;
|
||||
wxWindow* m_Window;
|
||||
|
||||
wxDataViewListCtrl& m_Library;
|
||||
wxInfoBar& m_InfoBar;
|
||||
wxMediaCtrl& m_MediaCtrl;
|
||||
wxTimer& m_Timer;
|
||||
|
||||
const std::string& m_ConfigFilepath;
|
||||
const std::string& m_DatabaseFilepath;
|
||||
|
||||
private:
|
||||
// -------------------------------------------------------------------
|
||||
wxBitmap m_WaveformBitmap;
|
||||
wxColour m_PlayheadColour;
|
||||
wxColour m_WaveformColour;
|
||||
|
||||
private:
|
||||
// -------------------------------------------------------------------
|
||||
bool bBitmapDirty;
|
||||
|
||||
private:
|
||||
// -------------------------------------------------------------------
|
||||
void OnPaint(wxPaintEvent& event);
|
||||
void RenderPlayhead(wxDC& dc);
|
||||
void UpdateWaveformBitmap();
|
||||
void OnHoverPlayhead(wxMouseEvent& event);
|
||||
void OnGrabPlayhead(wxMouseEvent& event);
|
||||
void OnReleasePlayhead(wxMouseEvent& event);
|
||||
|
||||
public:
|
||||
inline bool IsBitmapDirty() { return bBitmapDirty; }
|
||||
inline void SetBitmapDirty(bool dirty) { bBitmapDirty = dirty; }
|
||||
};
|
||||
Loading…
Reference in New Issue