Merge branch 'experimental-draw-waveform' into 'testing'

Show waveform for the selected sample.

See merge request samplehive/sample-hive!9
This commit is contained in:
Apoorv 2021-10-04 09:01:55 +00:00
commit 2e656b057e
31 changed files with 1277 additions and 141 deletions

View File

@ -48,18 +48,18 @@ SampleHive let's you manage your audio samples in a nice and simple way, just ad
On Arch based distributions, On Arch based distributions,
#+begin_example #+begin_example
sudo pacman -S wxgtk3 wxsvg sqlite taglib yaml-cpp sudo pacman -S wxgtk3 wxsvg sqlite taglib yaml-cpp libsndfile
#+end_example #+end_example
On Debian, Ubuntu and distributions based the on two, On Debian, Ubuntu and distributions based the on two,
#+begin_example #+begin_example
sudo apt install libwxbase3.0-dev libwxgtk-media3.0-gtk3-dev libwxgtk3.0-gtk3-dev wx3.0-headers libsqlite3-dev libyaml-cpp-dev libtagc0-dev libtag1-dev libtagc0 libexif-dev libpango1.0-dev sudo apt install libwxbase3.0-dev libwxgtk-media3.0-gtk3-dev libwxgtk3.0-gtk3-dev wx3.0-headers libsqlite3-dev libyaml-cpp-dev libtagc0-dev libtag1-dev libtagc0 libexif-dev libpango1.0-dev libsndfile1-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev
#+end_example #+end_example
You might also need to install =git=, =meson= and =g++= as well, if you don't already have them installed in order to build SampleHive. You might also need to install =git=, =cmake=, =meson= and =g++= as well, if you don't already have them installed in order to build SampleHive.
/NOTE:/ On Debian and Debian based distributions you also have to install =libwxgtk-media3.0-dev= *NOTE:* On Debian and Debian based distributions you also have to install =libwxgtk-media3.0-dev=
** How to build SampleHive? ** How to build SampleHive?
:PROPERTIES: :PROPERTIES:

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 KiB

After

Width:  |  Height:  |  Size: 176 KiB

View File

@ -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

View File

@ -17,13 +17,15 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
project('SampleHive', 'cpp', project('SampleHive',
'cpp',
version : 'v0.8.4_alpha.1', version : 'v0.8.4_alpha.1',
license : 'GPL v3', license : 'GPL v3',
default_options : ['warning_level=1', default_options : ['warning_level=1',
'cpp_std=c++11']) 'cpp_std=c++11'])
meson_src_root = meson.current_source_dir() meson_src_root = meson.current_source_dir()
meson_build_root = meson.current_build_dir()
# Save important directories # Save important directories
prefix = get_option('prefix') prefix = get_option('prefix')
@ -40,11 +42,39 @@ config_data.set_quoted('LIBDIR', libdir)
config_data.set_quoted('DATADIR', datadir) config_data.set_quoted('DATADIR', datadir)
config_data.set_quoted('SAMPLEHIVE_DATADIR', samplehive_datadir) config_data.set_quoted('SAMPLEHIVE_DATADIR', samplehive_datadir)
# Create samplehive-config.h based on configuration # Import CMake
config_h = configure_file( cmake = import('cmake')
output: 'SampleHiveConfig.hpp',
configuration: config_data, wx_opts = cmake.subproject_options()
) wx_opts.add_cmake_defines({'CMAKE_POSITION_INDEPENDENT_CODE': 'ON',
'CMAKE_INSTALL_PREFIX': prefix,
'CMAKE_BUILD_TYPE': 'Release',
'CMAKE_CXX_COMPILER': 'g++',
'wxBUILD_SHARED': 'ON',
'wxBUILD_TESTS': 'OFF',
'wxBUILD_SAMPLES': 'OFF',
'wxBUILD_DEMOS': 'OFF',
'wxBUILD_COMPATIBILITY': '3.0',
'wxUSE_UNICODE': 'ON',
'wxUSE_AUI': 'OFF',
'wxUSE_XML': 'OFF',
'wxUSE_XRC': 'ON',
'wxUSE_HTML': 'ON',
'wxUSE_QA': 'ON',
'wxUSE_PROPGRID': 'OFF',
'wxUSE_RIBBON': 'OFF',
'wxUSE_MDI': 'OFF',
'wxUSE_MDI_ARCHITECTURE': 'OFF',
'wxUSE_RICHTEXT': 'OFF',
'wxUSE_WEBVIEW': 'OFF',
'wxUSE_LIBSDL': 'OFF',
'wxUSE_MEDIACTRL': 'ON'})
taglib_opts = cmake.subproject_options()
taglib_opts.add_cmake_defines({'CMAKE_POSITION_INDEPENDENT_CODE': 'ON',
'CMAKE_INSTALL_PREFIX': prefix,
'CMAKE_BUILD_TYPE': 'Release',
'CMAKE_CXX_COMPILER': 'g++'})
# Source files to be compiled # Source files to be compiled
src = [ src = [
@ -57,34 +87,88 @@ src = [
'src/Sample.cpp', 'src/Sample.cpp',
'src/Serialize.cpp', 'src/Serialize.cpp',
'src/Tags.cpp', 'src/Tags.cpp',
'src/WaveformViewer.cpp',
'src/SH_Event.cpp',
] ]
wxconfig = find_program(['wx-config-gtk3', 'wx-config'])
wx_modules = ['media', 'std']
wx_cxx_flags = []
wx_libs = []
foreach module : wx_modules
wx_cxx_flags += run_command(wxconfig, '--cxxflags', module).stdout().strip().split()
wx_libs += run_command(wxconfig, '--libs', module).stdout().strip().split()
endforeach
# Dependencies # Dependencies
wx = dependency('wxwidgets', version: '>=3.0.4') wx = dependency('wxwidgets', version: '>=3.1.5', required: false)
taglib = dependency('taglib', version: '>=1.11')
sqlite3 = dependency('sqlite3')
yaml = dependency('yaml-cpp')
install_subdir( wx_found = false
'assets',
install_dir: samplehive_datadir,
exclude_directories: 'screenshots'
)
executable('SampleHive', if not wx.found()
sources: src, wx_found = false
cpp_args: [wx_cxx_flags],
link_args: [wx_libs], wx_subproject = cmake.subproject('wxwidgets', options: wx_opts)
dependencies: [wx, taglib, sqlite3, yaml], wx_base = wx_subproject.dependency('wxbase')
install: true) wx_core = wx_subproject.dependency('wxcore')
wx_media = wx_subproject.dependency('wxmedia')
wx = [wx_core, wx_base, wx_media]
else
wx_found = true
wxconfig = find_program(['wx-config-gtk3', 'wx-config'])
wx_modules = ['media', 'std']
wx_cxx_flags = []
wx_libs = []
foreach module : wx_modules
wx_cxx_flags += run_command(wxconfig, '--cxxflags', module).stdout().strip().split()
wx_libs += run_command(wxconfig, '--libs', module).stdout().strip().split()
endforeach
endif
taglib = dependency('taglib', version: '>=1.12', required: false)
if not taglib.found()
taglib_subproject = cmake.subproject('taglib', options: taglib_opts)
taglib = taglib_subproject.dependency('tag')
else
config_data.set('USE_SYSTEM_INCLUDE_PATH', 1)
endif
sqlite3 = dependency('sqlite3', required: true)
yaml = dependency('yaml-cpp', required: true)
snd = dependency('sndfile', required: true)
# Create samplehive-config.h based on configuration
config_h = configure_file(output: 'SampleHiveConfig.hpp',
configuration: config_data,)
install_subdir('assets',
install_dir: samplehive_datadir,
exclude_directories: 'screenshots')
if wx_found
executable('SampleHive',
sources: src,
cpp_args: [wx_cxx_flags],
link_args: [wx_libs],
dependencies: [wx, taglib, sqlite3, yaml, snd],
install: true,
install_rpath: prefix / 'lib')
else
executable('SampleHive',
sources: src,
dependencies: [wx, taglib, sqlite3, yaml, snd],
install: true,
install_rpath: prefix / 'lib')
endif
summary(
{
'Debug': get_option('debug'),
'Optimization': get_option('optimization'),
},
section: 'General')
summary(
{
'prefix': prefix,
'bindir': bindir,
'libdir': libdir,
'datadir': datadir,
'samplehive_datadir': samplehive_datadir,
},
section: 'Directories')

View File

@ -79,7 +79,7 @@ bool App::OnCmdLineParsed(wxCmdLineParser& parser)
if (parser.Found("version")) if (parser.Found("version"))
{ {
std::cout << "SampleHive v0.8.4_alpha.1" << std::endl; std::cout << "SampleHive v0.9.0_alpha.1" << std::endl;
return false; return false;
} }

View File

@ -35,6 +35,7 @@ enum ControlIDs
BC_Settings, BC_Settings,
BC_Loop, BC_Loop,
BC_Stop, BC_Stop,
BC_LoopABButton,
BC_Mute, BC_Mute,
BC_Autoplay, BC_Autoplay,
BC_Volume, BC_Volume,
@ -59,6 +60,7 @@ enum ControlIDs
SD_FontType, SD_FontType,
SD_FontSize, SD_FontSize,
SD_FontBrowseButton, SD_FontBrowseButton,
SD_WaveformColourPickerCtrl,
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// App Menu items // App Menu items

View File

@ -30,7 +30,6 @@
#include <wx/variant.h> #include <wx/variant.h>
#include "Database.hpp" #include "Database.hpp"
#include "SettingsDialog.hpp"
Database::Database(wxInfoBar& infoBar) Database::Database(wxInfoBar& infoBar)
: m_InfoBar(infoBar) : m_InfoBar(infoBar)
@ -383,7 +382,7 @@ void Database::UpdateHive(const std::string& dbPath, const std::string& hiveOldN
} }
else else
{ {
wxLogInfo("Hive updated successfully. %s", m_ErrMsg); wxLogDebug("Hive updated successfully. %s", m_ErrMsg);
} }
sqlite3_close(m_Database); sqlite3_close(m_Database);
@ -566,7 +565,7 @@ std::string Database::GetSampleType(const std::string& dbPath, const std::string
if (sqlite3_step(m_Stmt) == SQLITE_ROW) if (sqlite3_step(m_Stmt) == SQLITE_ROW)
{ {
wxLogInfo("Record found, fetching.."); wxLogDebug("Record found, fetching..");
type = std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 0))); type = std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 0)));
} }
@ -582,7 +581,7 @@ std::string Database::GetSampleType(const std::string& dbPath, const std::string
} }
else else
{ {
wxLogInfo("Selected data from table successfully."); wxLogDebug("Selected data from table successfully.");
} }
sqlite3_close(m_Database); sqlite3_close(m_Database);
@ -611,7 +610,7 @@ int Database::GetFavoriteColumnValueByFilename(const std::string& dbPath, const
if (sqlite3_step(m_Stmt) == SQLITE_ROW) if (sqlite3_step(m_Stmt) == SQLITE_ROW)
{ {
wxLogInfo("Record found, fetching.."); wxLogDebug("Record found, fetching..");
value = sqlite3_column_int(m_Stmt, 0); value = sqlite3_column_int(m_Stmt, 0);
} }
@ -626,7 +625,7 @@ int Database::GetFavoriteColumnValueByFilename(const std::string& dbPath, const
} }
else else
{ {
wxLogInfo("Selected data from table successfully."); wxLogDebug("Selected data from table successfully.");
} }
sqlite3_close(m_Database); sqlite3_close(m_Database);
@ -655,7 +654,7 @@ std::string Database::GetHiveByFilename(const std::string& dbPath, const std::st
if (sqlite3_step(m_Stmt) == SQLITE_ROW) if (sqlite3_step(m_Stmt) == SQLITE_ROW)
{ {
wxLogInfo("Record found, fetching.."); wxLogDebug("Record found, fetching..");
hive = std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 0))); hive = std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 0)));
} }
@ -671,7 +670,7 @@ std::string Database::GetHiveByFilename(const std::string& dbPath, const std::st
} }
else else
{ {
wxLogInfo("Selected data from table successfully."); wxLogDebug("Selected data from table successfully.");
} }
sqlite3_close(m_Database); sqlite3_close(m_Database);
@ -778,7 +777,7 @@ std::string Database::GetSamplePathByFilename(const std::string& dbPath, const s
if (sqlite3_step(m_Stmt) == SQLITE_ROW) if (sqlite3_step(m_Stmt) == SQLITE_ROW)
{ {
wxLogInfo("Record found, fetching.."); wxLogDebug("Record found, fetching..");
path = std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 0))); path = std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 0)));
} }
@ -793,7 +792,7 @@ std::string Database::GetSamplePathByFilename(const std::string& dbPath, const s
} }
else else
{ {
wxLogInfo("Selected data from table successfully."); wxLogDebug("Selected data from table successfully.");
} }
sqlite3_close(m_Database); sqlite3_close(m_Database);
@ -822,7 +821,7 @@ std::string Database::GetSampleFileExtension(const std::string& dbPath, const st
if (sqlite3_step(m_Stmt) == SQLITE_ROW) if (sqlite3_step(m_Stmt) == SQLITE_ROW)
{ {
wxLogInfo("Record found, fetching.."); wxLogDebug("Record found, fetching..");
extension = std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 0))); extension = std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 0)));
} }
@ -837,7 +836,7 @@ std::string Database::GetSampleFileExtension(const std::string& dbPath, const st
} }
else else
{ {
wxLogInfo("Selected data from table successfully."); wxLogDebug("Selected data from table successfully.");
} }
sqlite3_close(m_Database); sqlite3_close(m_Database);
@ -1039,7 +1038,7 @@ Database::FilterDatabaseBySampleName(const std::string& dbPath, wxVector<wxVecto
while (SQLITE_ROW == sqlite3_step(m_Stmt)) while (SQLITE_ROW == sqlite3_step(m_Stmt))
{ {
wxLogInfo("Record found, fetching.."); wxLogDebug("Record found, fetching..");
int favorite = sqlite3_column_int(m_Stmt, 0); int favorite = sqlite3_column_int(m_Stmt, 0);
wxString filename = wxString(std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 1)))); wxString filename = wxString(std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 1))));
wxString sample_pack = wxString(std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 2)))); wxString sample_pack = wxString(std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 2))));
@ -1141,7 +1140,7 @@ Database::FilterDatabaseByHiveName(const std::string& dbPath, wxVector<wxVector<
while (SQLITE_ROW == sqlite3_step(m_Stmt)) while (SQLITE_ROW == sqlite3_step(m_Stmt))
{ {
wxLogInfo("Record found, fetching.."); wxLogDebug("Record found, fetching..");
int favorite = sqlite3_column_int(m_Stmt, 0); int favorite = sqlite3_column_int(m_Stmt, 0);
wxString filename = wxString(std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 1)))); wxString filename = wxString(std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 1))));
wxString sample_pack = wxString(std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 2)))); wxString sample_pack = wxString(std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 2))));
@ -1234,7 +1233,7 @@ void Database::LoadHivesDatabase(const std::string& dbPath, wxDataViewTreeCtrl&
{ {
while (SQLITE_ROW == sqlite3_step(m_Stmt)) while (SQLITE_ROW == sqlite3_step(m_Stmt))
{ {
wxLogInfo("Record found, fetching.."); wxLogDebug("Record found, fetching..");
wxString hive = wxString(std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 0)))); wxString hive = wxString(std::string(reinterpret_cast<const char*>(sqlite3_column_text(m_Stmt, 0))));
treeCtrl.AppendContainer(wxDataViewItem(wxNullPtr), hive); treeCtrl.AppendContainer(wxDataViewItem(wxNullPtr), hive);
@ -1314,7 +1313,7 @@ bool Database::IsTrashed(const std::string& dbPath, const std::string& filename)
if (sqlite3_step(m_Stmt) == SQLITE_ROW) if (sqlite3_step(m_Stmt) == SQLITE_ROW)
{ {
wxLogInfo("Record found, fetching.."); wxLogDebug("Record found, fetching..");
if (sqlite3_column_int(m_Stmt, 0) == 1) if (sqlite3_column_int(m_Stmt, 0) == 1)
return true; return true;
@ -1331,7 +1330,7 @@ bool Database::IsTrashed(const std::string& dbPath, const std::string& filename)
} }
else else
{ {
wxLogInfo("Selected data from table successfully."); wxLogDebug("Selected data from table successfully.");
} }
sqlite3_close(m_Database); sqlite3_close(m_Database);

View File

@ -20,8 +20,10 @@
#include <algorithm> #include <algorithm>
#include <cstddef> #include <cstddef>
#include <cstdlib>
#include <deque> #include <deque>
#include <exception> #include <exception>
#include <ios>
#include <wx/aboutdlg.h> #include <wx/aboutdlg.h>
#include <wx/accel.h> #include <wx/accel.h>
@ -48,14 +50,12 @@
#include <wx/msgdlg.h> #include <wx/msgdlg.h>
#include <wx/object.h> #include <wx/object.h>
#include <wx/progdlg.h> #include <wx/progdlg.h>
#include <wx/stdpaths.h>
#include <wx/stringimpl.h> #include <wx/stringimpl.h>
#include <wx/textdlg.h> #include <wx/textdlg.h>
#include <wx/valtext.h> #include <wx/valtext.h>
#include <wx/variant.h> #include <wx/variant.h>
#include <wx/vector.h> #include <wx/vector.h>
#include <wx/utils.h> #include <wx/utils.h>
#include <wx/unix/stdpaths.h>
#include "MainFrame.hpp" #include "MainFrame.hpp"
#include "ControlID_Enums.hpp" #include "ControlID_Enums.hpp"
@ -76,9 +76,18 @@
#define ICON_HIVE_256px SAMPLEHIVE_DATADIR "/assets/icons/icon-hive_256x256.png" #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_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 ICON_STAR_EMPTY_16px SAMPLEHIVE_DATADIR "/assets/icons/icon-star_empty_16x16.png"
#define WAVEFORM_SVG SAMPLEHIVE_DATADIR "/assets/waveform.svg" #define ICON_PLAY_DARK_16px SAMPLEHIVE_DATADIR "/assets/icons/icon-play-dark_16x16.png"
#define APP_CONFIG_DIR wxStandardPaths::Get().GetUserConfigDir() + "/.config/SampleHive" #define ICON_STOP_DARK_16px SAMPLEHIVE_DATADIR "/assets/icons/icon-stop-dark_16x16.png"
#define APP_DATA_DIR wxStandardPaths::Get().GetDocumentsDir() + "/.local/share/SampleHive" #define ICON_AB_DARK_16px SAMPLEHIVE_DATADIR "/assets/icons/icon-ab-dark_16x16.png"
#define ICON_LOOP_DARK_16px SAMPLEHIVE_DATADIR "/assets/icons/icon-loop-dark_16x16.png"
#define ICON_MUTE_DARK_16px SAMPLEHIVE_DATADIR "/assets/icons/icon-mute-dark_16x16.png"
#define ICON_PLAY_LIGHT_16px SAMPLEHIVE_DATADIR "/assets/icons/icon-play-light_16x16.png"
#define ICON_STOP_LIGHT_16px SAMPLEHIVE_DATADIR "/assets/icons/icon-stop-light_16x16.png"
#define ICON_AB_LIGHT_16px SAMPLEHIVE_DATADIR "/assets/icons/icon-ab-light_16x16.png"
#define ICON_LOOP_LIGHT_16px SAMPLEHIVE_DATADIR "/assets/icons/icon-loop-light_16x16.png"
#define ICON_MUTE_LIGHT_16px SAMPLEHIVE_DATADIR "/assets/icons/icon-mute-light_16x16.png"
#define APP_CONFIG_DIR wxGetHomeDir() + "/.config/SampleHive"
#define APP_DATA_DIR wxGetHomeDir() + "/.local/share/SampleHive"
#define CONFIG_FILEPATH APP_CONFIG_DIR + "/config.yaml" #define CONFIG_FILEPATH APP_CONFIG_DIR + "/config.yaml"
#define DATABASE_FILEPATH APP_DATA_DIR "/sample.hive" #define DATABASE_FILEPATH APP_DATA_DIR "/sample.hive"
@ -196,7 +205,7 @@ MainFrame::MainFrame()
_("All files|*|Ogg files (*.ogg)|*.ogg|Wav files (*.wav)|*.wav|" _("All files|*|Ogg files (*.ogg)|*.ogg|Wav files (*.wav)|*.wav|"
"Flac files (*.flac)|*.flac"), 0); "Flac files (*.flac)|*.flac"), 0);
wxString path = wxStandardPaths::Get().GetDocumentsDir(); wxString path = wxGetHomeDir();
m_DirCtrl->SetPath(path); m_DirCtrl->SetPath(path);
// This panel will hold 2nd page of wxNotebook // This panel will hold 2nd page of wxNotebook
@ -244,32 +253,58 @@ MainFrame::MainFrame()
m_TopSplitter->SplitHorizontally(m_TopPanel, m_BottomSplitter); m_TopSplitter->SplitHorizontally(m_TopPanel, m_BottomSplitter);
m_BottomSplitter->SplitVertically(m_BottomLeftPanel, m_BottomRightPanel); m_BottomSplitter->SplitVertically(m_BottomLeftPanel, m_BottomRightPanel);
m_TopControlsPanel = new wxPanel(m_TopPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER);
// Looping region controls
if (m_Theme.IsDark())
m_LoopABButton = new wxBitmapToggleButton(m_TopControlsPanel, BC_LoopABButton, static_cast<wxString>(ICON_AB_LIGHT_16px), wxDefaultPosition, wxDefaultSize, 0);
else
m_LoopABButton = new wxBitmapToggleButton(m_TopControlsPanel, BC_LoopABButton, static_cast<wxString>(ICON_AB_DARK_16px), wxDefaultPosition, wxDefaultSize, 0);
m_LoopABButton->SetToolTip(_("Loop selected region"));
// Initializing browser controls on top panel. // 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_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->SetToolTip(_("Volume"));
m_VolumeSlider->SetMinSize(wxSize(120, -1)); m_VolumeSlider->SetMinSize(wxSize(120, -1));
m_VolumeSlider->SetMaxSize(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); 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));
// Initialize browser control buttons // Initialize browser control buttons
m_PlayButton = new wxButton(m_TopPanel, BC_Play, _("Play"), wxDefaultPosition, wxDefaultSize, 0); if (m_Theme.IsDark())
{
m_PlayButton = new wxBitmapButton(m_TopControlsPanel, BC_Play, static_cast<wxString>(ICON_PLAY_LIGHT_16px),
wxDefaultPosition, wxDefaultSize, 0);
m_LoopButton = new wxBitmapToggleButton(m_TopControlsPanel, BC_Loop, static_cast<wxString>(ICON_LOOP_LIGHT_16px),
wxDefaultPosition, wxDefaultSize, 0);
m_StopButton = new wxBitmapButton(m_TopControlsPanel, BC_Stop, static_cast<wxString>(ICON_STOP_LIGHT_16px),
wxDefaultPosition, wxDefaultSize, 0);
m_MuteButton = new wxBitmapToggleButton(m_TopControlsPanel, BC_Mute, static_cast<wxString>(ICON_MUTE_LIGHT_16px),
wxDefaultPosition, wxDefaultSize, 0);
}
else
{
m_PlayButton = new wxBitmapButton(m_TopControlsPanel, BC_Play, static_cast<wxString>(ICON_PLAY_DARK_16px),
wxDefaultPosition, wxDefaultSize, 0);
m_LoopButton = new wxBitmapToggleButton(m_TopControlsPanel, BC_Loop, static_cast<wxString>(ICON_LOOP_DARK_16px),
wxDefaultPosition, wxDefaultSize, 0);
m_StopButton = new wxBitmapButton(m_TopControlsPanel, BC_Stop, static_cast<wxString>(ICON_STOP_DARK_16px),
wxDefaultPosition, wxDefaultSize, 0);
m_MuteButton = new wxBitmapToggleButton(m_TopControlsPanel, BC_Mute, static_cast<wxString>(ICON_MUTE_DARK_16px),
wxDefaultPosition, wxDefaultSize, 0);
}
m_PlayButton->SetToolTip(_("Play")); m_PlayButton->SetToolTip(_("Play"));
m_LoopButton = new wxToggleButton(m_TopPanel, BC_Loop, _("Loop"), wxDefaultPosition, wxDefaultSize, 0);
m_LoopButton->SetToolTip(_("Loop")); m_LoopButton->SetToolTip(_("Loop"));
m_StopButton = new wxButton(m_TopPanel, BC_Stop, _("Stop"), wxDefaultPosition, wxDefaultSize, 0);
m_StopButton->SetToolTip(_("Stop")); m_StopButton->SetToolTip(_("Stop"));
m_SettingsButton = new wxButton(m_TopPanel, BC_Settings, _("Settings"), wxDefaultPosition, wxDefaultSize, 0);
m_SettingsButton->SetToolTip(_("Settings"));
m_MuteButton = new wxToggleButton(m_TopPanel, BC_Mute, _("Mute"), wxDefaultPosition, wxDefaultSize, 0);
m_MuteButton->SetToolTip(_("Mute")); m_MuteButton->SetToolTip(_("Mute"));
m_SettingsButton = new wxButton(m_TopControlsPanel, BC_Settings, _("Settings"), wxDefaultPosition, wxDefaultSize, 0);
m_SettingsButton->SetToolTip(_("Settings"));
// Initializing wxSearchCtrl on bottom panel. // Initializing wxSearchCtrl on bottom panel.
m_SearchBox = new wxSearchCtrl(m_BottomRightPanel, BC_Search, _("Search for samples.."), wxDefaultPosition, m_SearchBox = new wxSearchCtrl(m_BottomRightPanel, BC_Search, _("Search for samples.."), wxDefaultPosition,
wxDefaultSize, wxTE_PROCESS_ENTER); wxDefaultSize, wxTE_PROCESS_ENTER);
@ -364,6 +399,9 @@ MainFrame::MainFrame()
// Intializing wxTimer // Intializing wxTimer
m_Timer = new wxTimer(this); m_Timer = new wxTimer(this);
m_TopWaveformPanel = new WaveformViewer(this, m_TopPanel, *m_Library, *m_MediaCtrl, *m_InfoBar,
m_ConfigFilepath, m_DatabaseFilepath);
// Binding events. // Binding events.
Bind(wxEVT_MENU, &MainFrame::OnSelectAddFile, this, MN_AddFile); Bind(wxEVT_MENU, &MainFrame::OnSelectAddFile, this, MN_AddFile);
Bind(wxEVT_MENU, &MainFrame::OnSelectAddDirectory, this, MN_AddDirectory); Bind(wxEVT_MENU, &MainFrame::OnSelectAddDirectory, this, MN_AddDirectory);
@ -412,26 +450,28 @@ MainFrame::MainFrame()
Bind(wxEVT_BUTTON, &MainFrame::OnClickAddHive, this, BC_HiveAdd); Bind(wxEVT_BUTTON, &MainFrame::OnClickAddHive, this, BC_HiveAdd);
Bind(wxEVT_BUTTON, &MainFrame::OnClickRemoveHive, this, BC_HiveRemove); Bind(wxEVT_BUTTON, &MainFrame::OnClickRemoveHive, this, BC_HiveRemove);
Bind(SampleHive::SH_EVT_LOOP_POINTS_UPDATED, &MainFrame::OnRecieveLoopPoints, this);
Bind(SampleHive::SH_EVT_STATUSBAR_MESSAGE_UPDATED, &MainFrame::OnRecieveStatusBarStatus, this);
// Adding widgets to their sizers // Adding widgets to their sizers
m_MainSizer->Add(m_MainPanel, 1, wxALL | wxEXPAND, 0); m_MainSizer->Add(m_MainPanel, 1, wxALL | wxEXPAND, 0);
m_TopSizer->Add(m_TopSplitter, 1, wxALL | wxEXPAND, 0); m_TopSizer->Add(m_TopSplitter, 1, wxALL | wxEXPAND, 0);
m_BrowserControlSizer->Add(m_PlayButton, 0, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 2); m_BrowserControlSizer->Add(m_PlayButton, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_BrowserControlSizer->Add(m_LoopButton, 0, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 2); m_BrowserControlSizer->Add(m_StopButton, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_BrowserControlSizer->Add(m_StopButton, 0, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 2); m_BrowserControlSizer->Add(m_LoopButton, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_BrowserControlSizer->Add(m_SettingsButton, 0, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 2); m_BrowserControlSizer->Add(m_LoopABButton, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_BrowserControlSizer->Add(m_SettingsButton, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_BrowserControlSizer->Add(0,0,1, wxALL | wxEXPAND, 0); 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(m_SamplePosition, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_BrowserControlSizer->Add(30,0,0, wxALL | wxEXPAND, 0); m_BrowserControlSizer->Add(30,0,0, wxALL | wxEXPAND, 0);
m_BrowserControlSizer->Add(m_MuteButton, 0, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 2); m_BrowserControlSizer->Add(m_MuteButton, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_BrowserControlSizer->Add(m_VolumeSlider, 1, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 2); m_BrowserControlSizer->Add(m_VolumeSlider, 1, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_BrowserControlSizer->Add(m_AutoPlayCheck, 0, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 2); m_BrowserControlSizer->Add(m_AutoPlayCheck, 0, wxALL | 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);
m_TopPanelMainSizer->Add(m_WaveformDisplaySizer, 1, wxALL | wxEXPAND, 2);
m_TopPanelMainSizer->Add(m_BrowserControlSizer, 0, wxALL | wxEXPAND, 2);
m_BottomLeftPanelMainSizer->Add(m_Notebook, 1, wxALL | wxEXPAND, 0); m_BottomLeftPanelMainSizer->Add(m_Notebook, 1, wxALL | wxEXPAND, 0);
@ -464,6 +504,16 @@ MainFrame::MainFrame()
m_TopSizer->SetSizeHints(m_MainPanel); m_TopSizer->SetSizeHints(m_MainPanel);
m_TopSizer->Layout(); 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 // Sizer for TopPanel
m_TopPanel->SetSizer(m_TopPanelMainSizer); m_TopPanel->SetSizer(m_TopPanelMainSizer);
m_TopPanelMainSizer->Fit(m_TopPanel); m_TopPanelMainSizer->Fit(m_TopPanel);
@ -518,6 +568,10 @@ void MainFrame::OnClickSettings(wxCommandEvent& event)
OnAutoImportDir(settings->GetImportDirPath()); OnAutoImportDir(settings->GetImportDirPath());
RefreshDatabase(); RefreshDatabase();
} }
if (settings->IsWaveformColourChanged())
{
m_TopWaveformPanel->ResetDC();
}
break; break;
case wxID_CANCEL: case wxID_CANCEL:
break; break;
@ -885,7 +939,8 @@ void MainFrame::OnClickPlay(wxCommandEvent& event)
int selected_row = m_Library->GetSelectedRow(); int selected_row = m_Library->GetSelectedRow();
if (selected_row < 0) return; if (selected_row < 0)
return;
wxString selection = m_Library->GetTextValue(selected_row, 1); wxString selection = m_Library->GetTextValue(selected_row, 1);
@ -901,25 +956,18 @@ void MainFrame::OnClickPlay(wxCommandEvent& event)
wxString sample_path = GetFilenamePathAndExtension(selection).Path; wxString sample_path = GetFilenamePathAndExtension(selection).Path;
m_MediaCtrl->Load(sample_path); if (bLoopPointsSet && m_LoopABButton->GetValue())
PlaySample(sample_path.ToStdString(), selection.ToStdString(), true, m_LoopA.ToDouble(), wxFromStart);
PushStatusText(wxString::Format(_("Now playing: %s"), selection), 1); else
PlaySample(sample_path.ToStdString(), selection.ToStdString());
m_MediaCtrl->Play();
m_Timer->Start(100, wxTIMER_CONTINUOUS);
} }
void MainFrame::OnClickLoop(wxCommandEvent& event) void MainFrame::OnClickLoop(wxCommandEvent& event)
{ {
if (m_LoopButton->GetValue()) if (m_LoopButton->GetValue())
{
bLoop = true; bLoop = true;
}
else else
{
bLoop = false; bLoop = false;
}
} }
void MainFrame::OnClickStop(wxCommandEvent& event) void MainFrame::OnClickStop(wxCommandEvent& event)
@ -927,7 +975,9 @@ void MainFrame::OnClickStop(wxCommandEvent& event)
m_MediaCtrl->Stop(); m_MediaCtrl->Stop();
bStopped = true; bStopped = true;
m_Timer->Stop(); if (m_Timer->IsRunning())
m_Timer->Stop();
m_SamplePosition->SetLabel("--:--/--:--"); m_SamplePosition->SetLabel("--:--/--:--");
this->SetStatusText(_("Stopped"), 1); this->SetStatusText(_("Stopped"), 1);
@ -957,14 +1007,16 @@ void MainFrame::OnMediaFinished(wxMediaEvent& event)
msgDialog.ShowModal(); msgDialog.ShowModal();
} }
else else
{
m_MediaCtrl->Play(); m_MediaCtrl->Play();
m_Timer->Start(100, wxTIMER_CONTINUOUS);
}
} }
else else
{ {
m_Timer->Stop(); if (m_Timer->IsRunning())
{
m_Timer->Stop();
wxLogDebug("TIMER STOPPED");
}
m_SamplePosition->SetLabel("--:--/--:--"); m_SamplePosition->SetLabel("--:--/--:--");
PopStatusText(1); PopStatusText(1);
this->SetStatusText(_("Stopped"), 1); this->SetStatusText(_("Stopped"), 1);
@ -973,6 +1025,8 @@ void MainFrame::OnMediaFinished(wxMediaEvent& event)
void MainFrame::UpdateElapsedTime(wxTimerEvent& event) void MainFrame::UpdateElapsedTime(wxTimerEvent& event)
{ {
wxLogDebug("TIMER IS RUNNING..");
wxString duration, position; wxString duration, position;
wxLongLong llLength, llTell; wxLongLong llLength, llTell;
@ -988,6 +1042,13 @@ void MainFrame::UpdateElapsedTime(wxTimerEvent& event)
position.Printf(wxT("%2i:%02i"), current_min, current_sec); position.Printf(wxT("%2i:%02i"), current_min, current_sec);
m_SamplePosition->SetLabel(wxString::Format(wxT("%s/%s"), position.c_str(), duration.c_str())); m_SamplePosition->SetLabel(wxString::Format(wxT("%s/%s"), position.c_str(), duration.c_str()));
m_TopControlsPanel->Refresh();
m_TopWaveformPanel->Refresh();
if (bLoopPointsSet && m_LoopABButton->GetValue())
if (static_cast<double>(m_MediaCtrl->Tell()) >= m_LoopB.ToDouble())
m_MediaCtrl->Seek(m_LoopA.ToDouble(), wxFromStart);
} }
void MainFrame::OnCheckAutoplay(wxCommandEvent& event) void MainFrame::OnCheckAutoplay(wxCommandEvent& event)
@ -1047,6 +1108,17 @@ void MainFrame::OnClickLibrary(wxDataViewEvent& event)
return; return;
} }
// Update the waveform bitmap
m_TopWaveformPanel->ResetDC();
m_LoopABButton->SetValue(false);
if (m_Timer->IsRunning())
{
m_Timer->Stop();
wxLogDebug("TIMER STOPPED");
}
wxString selection = m_Library->GetTextValue(selected_row, 1); wxString selection = m_Library->GetTextValue(selected_row, 1);
// Get curremt column // Get curremt column
@ -1084,15 +1156,17 @@ void MainFrame::OnClickLibrary(wxDataViewEvent& event)
if (CurrentColumn != FavoriteColumn) if (CurrentColumn != FavoriteColumn)
{ {
m_MediaCtrl->Load(sample_path); ClearLoopPoints();
if (bAutoplay) if (bAutoplay)
{ {
PushStatusText(wxString::Format(_("Now playing: %s"), selection), 1); if (bLoopPointsSet && m_LoopABButton->GetValue())
PlaySample(sample_path.ToStdString(), selection.ToStdString(), true, m_LoopA.ToDouble(), wxFromStart);
m_MediaCtrl->Play(); else
m_Timer->Start(100, wxTIMER_CONTINUOUS); PlaySample(sample_path.ToStdString(), selection.ToStdString());
} }
else
m_MediaCtrl->Stop();
} }
else else
{ {
@ -2568,7 +2642,7 @@ void MainFrame::LoadConfigFile()
this->CenterOnScreen(wxBOTH); this->CenterOnScreen(wxBOTH);
this->SetIcon(wxIcon(ICON_HIVE_256px, wxICON_DEFAULT_TYPE, -1, -1)); this->SetIcon(wxIcon(ICON_HIVE_256px, wxICON_DEFAULT_TYPE, -1, -1));
this->SetTitle("SampleHive"); this->SetTitle("SampleHive");
this->SetStatusText("SampleHive v0.8.4_alpha.1", 3); this->SetStatusText("SampleHive v0.9.0_alpha.1", 3);
this->SetStatusText(_("Stopped"), 1); this->SetStatusText(_("Stopped"), 1);
} }
@ -2646,7 +2720,7 @@ void MainFrame::OnHiveStartEditing(wxDataViewEvent &event)
void MainFrame::OnSelectAddFile(wxCommandEvent& event) void MainFrame::OnSelectAddFile(wxCommandEvent& event)
{ {
wxFileDialog file_dialog(this, wxFileSelectorPromptStr, wxStandardPaths::Get().GetDocumentsDir(), wxFileDialog file_dialog(this, wxFileSelectorPromptStr, wxGetHomeDir(),
wxEmptyString, wxFileSelectorDefaultWildcardStr, wxEmptyString, wxFileSelectorDefaultWildcardStr,
wxFD_DEFAULT_STYLE | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE | wxFD_PREVIEW, wxFD_DEFAULT_STYLE | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE | wxFD_PREVIEW,
wxDefaultPosition, wxDefaultSize); wxDefaultPosition, wxDefaultSize);
@ -2669,7 +2743,7 @@ void MainFrame::OnSelectAddFile(wxCommandEvent& event)
void MainFrame::OnSelectAddDirectory(wxCommandEvent& event) void MainFrame::OnSelectAddDirectory(wxCommandEvent& event)
{ {
wxDirDialog dir_dialog(this, wxDirSelectorPromptStr, wxStandardPaths::Get().GetDocumentsDir(), wxDirDialog dir_dialog(this, wxDirSelectorPromptStr, wxGetHomeDir(),
wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST, wxDefaultPosition, wxDefaultSize); wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST, wxDefaultPosition, wxDefaultSize);
switch (dir_dialog.ShowModal()) switch (dir_dialog.ShowModal())
@ -2813,21 +2887,21 @@ void MainFrame::OnSelectAbout(wxCommandEvent& event)
aboutInfo.SetName("SampleHive"); aboutInfo.SetName("SampleHive");
aboutInfo.SetIcon(wxIcon(ICON_HIVE_64px)); aboutInfo.SetIcon(wxIcon(ICON_HIVE_64px));
aboutInfo.AddArtist("Apoorv"); aboutInfo.AddArtist("Apoorv");
aboutInfo.SetVersion("v0.8.4_alpha.1", _("Version 0.8.4_alpha.1")); aboutInfo.SetVersion("v0.9.0_alpha.1", _("Version 0.9.0_alpha.1"));
aboutInfo.SetDescription(_("A simple, modern audio sample browser/manager for GNU/Linux.")); aboutInfo.SetDescription(_("A simple, modern audio sample browser/manager for GNU/Linux."));
aboutInfo.SetCopyright("(C) 2020-2021"); aboutInfo.SetCopyright("(C) 2020-2021");
aboutInfo.SetWebSite("http://samplehive.gitlab.io"); aboutInfo.SetWebSite("http://samplehive.gitlab.io");
aboutInfo.AddDeveloper("Apoorv"); aboutInfo.AddDeveloper("Apoorv");
aboutInfo.SetLicence(wxString::FromAscii( aboutInfo.SetLicence(wxString::FromAscii(
"SampleHive v0.8.4_alpha.1\n" "SampleHive v0.9.0_alpha.1\n"
"Copyright (C) 2021 Apoorv Singh\n" "Copyright (C) 2021 Apoorv Singh\n"
"\n" "\n"
"This program is free software: you can redistribute it and/or modify\n" "SampleHive is free software: you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License as published by\n" "it under the terms of the GNU General Public License as published by\n"
"the Free Software Foundation, either version 3 of the License, or\n" "the Free Software Foundation, either version 3 of the License, or\n"
"(at your option) any later version.\n" "(at your option) any later version.\n"
"\n" "\n"
"This program is distributed in the hope that it will be useful,\n" "SampleHive is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public License for more details.\n" "GNU General Public License for more details.\n"
@ -2858,4 +2932,64 @@ void MainFrame::SetAfterFrameCreate()
m_BottomSplitter->SetSashPosition(300); m_BottomSplitter->SetSashPosition(300);
} }
void MainFrame::OnRecieveLoopPoints(SampleHive::SH_LoopPointsEvent& event)
{
wxLogDebug("%s called and recieved loop points", __FUNCTION__);
std::pair<double, double> loop_points = event.GetLoopPoints();
m_LoopA = wxLongLong(loop_points.first);
m_LoopB = wxLongLong(loop_points.second);
int loopA_min = static_cast<int>((m_LoopA / 60000).GetValue());
int loopA_sec = static_cast<int>(((m_LoopA % 60000) / 1000).GetValue());
int loopB_min = static_cast<int>((m_LoopB / 60000).GetValue());
int loopB_sec = static_cast<int>(((m_LoopB % 60000) / 1000).GetValue());
wxLogDebug(wxString::Format("LoopA: %2i:%02i, LoopB: %2i:%02i",
loopA_min, loopA_sec, loopB_min, loopB_sec));
m_LoopABButton->SetValue(true);
bLoopPointsSet = true;
wxLogDebug("%s Event processed successfully..", __FUNCTION__);
}
void MainFrame::OnRecieveStatusBarStatus(SampleHive::SH_SetStatusBarMessageEvent& event)
{
std::pair<wxString, int> status = event.GetMessageAndSection();
m_StatusBar->PushStatusText(status.first, status.second);
}
void MainFrame::ClearLoopPoints()
{
m_LoopA = 0;
m_LoopB = 0;
bLoopPointsSet = false;
}
void MainFrame::PlaySample(const std::string& filepath, const std::string& sample, bool seek, wxFileOffset where, wxSeekMode mode)
{
wxLogDebug("TIMER STARTING FROM %s", __FUNCTION__);
if (m_MediaCtrl->Load(filepath))
{
if (seek)
m_MediaCtrl->Seek(where, mode);
if (!m_MediaCtrl->Play())
wxLogDebug(_("Error! Cannot play sample."));
PushStatusText(wxString::Format(_("Now playing: %s"), sample), 1);
if (!m_Timer->IsRunning())
m_Timer->Start(20, wxTIMER_CONTINUOUS);
}
else
wxLogDebug(_("Error! Cannot load sample."));
}
MainFrame::~MainFrame(){} MainFrame::~MainFrame(){}

View File

@ -20,9 +20,14 @@
#pragma once #pragma once
#include "WaveformViewer.hpp"
#include "SampleHiveConfig.hpp"
#include "SH_Event.hpp"
#include <string> #include <string>
#include <wx/button.h> #include <wx/button.h>
#include <wx/bmpbuttn.h>
#include <wx/checkbox.h> #include <wx/checkbox.h>
#include <wx/collpane.h> #include <wx/collpane.h>
#include <wx/dataview.h> #include <wx/dataview.h>
@ -40,6 +45,7 @@
#include <wx/sizer.h> #include <wx/sizer.h>
#include <wx/slider.h> #include <wx/slider.h>
#include <wx/splitter.h> #include <wx/splitter.h>
#include <wx/settings.h>
#include <wx/statbmp.h> #include <wx/statbmp.h>
#include <wx/statusbr.h> #include <wx/statusbr.h>
#include <wx/string.h> #include <wx/string.h>
@ -51,9 +57,14 @@
#include <wx/treectrl.h> #include <wx/treectrl.h>
#include <wx/window.h> #include <wx/window.h>
#include <taglib/taglib.h> #include <taglib/tag.h>
#include <taglib/fileref.h> #include <taglib/fileref.h>
#include <taglib/tstring.h>
#ifndef USE_SYSTEM_INCLUDE_PATH
#include <taglib/toolkit/tstring.h>
#else
#include <taglib/tstring.h>
#endif
struct FileInfo struct FileInfo
{ {
@ -106,16 +117,18 @@ class MainFrame : public wxFrame
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Top panel controls // Top panel controls
wxPanel* m_TopPanel; wxPanel* m_TopPanel;
WaveformViewer* m_TopWaveformPanel;
wxPanel* m_TopControlsPanel;
wxBoxSizer* m_TopSizer; wxBoxSizer* m_TopSizer;
wxBoxSizer* m_TopPanelMainSizer; wxBoxSizer* m_TopPanelMainSizer;
wxBoxSizer* m_WaveformDisplaySizer; wxBoxSizer* m_WaveformDisplaySizer;
wxStaticBitmap* m_WaveformViewer;
wxBoxSizer* m_BrowserControlSizer; wxBoxSizer* m_BrowserControlSizer;
wxButton* m_PlayButton; wxBitmapButton* m_PlayButton;
wxToggleButton* m_LoopButton; wxBitmapToggleButton* m_LoopButton;
wxButton* m_StopButton; wxBitmapButton* m_StopButton;
wxButton* m_SettingsButton; wxButton* m_SettingsButton;
wxToggleButton* m_MuteButton; wxBitmapToggleButton* m_MuteButton;
wxBitmapToggleButton* m_LoopABButton;
wxStaticText* m_SamplePosition; wxStaticText* m_SamplePosition;
wxSlider* m_VolumeSlider; wxSlider* m_VolumeSlider;
wxCheckBox* m_AutoPlayCheck; wxCheckBox* m_AutoPlayCheck;
@ -162,6 +175,12 @@ class MainFrame : public wxFrame
// FileSystemWatcher // FileSystemWatcher
wxFileSystemWatcher* m_FsWatcher; wxFileSystemWatcher* m_FsWatcher;
// -------------------------------------------------------------------
wxLongLong m_LoopA, m_LoopB;
// -------------------------------------------------------------------
wxSystemAppearance m_Theme = wxSystemSettings::GetAppearance();
private: private:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
bool bAutoplay = false; bool bAutoplay = false;
@ -169,6 +188,7 @@ class MainFrame : public wxFrame
bool bMuted = false; bool bMuted = false;
bool bStopped = false; bool bStopped = false;
bool bFiltered = false; bool bFiltered = false;
bool bLoopPointsSet = false;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
const std::string m_ConfigFilepath; const std::string m_ConfigFilepath;
@ -243,6 +263,15 @@ class MainFrame : public wxFrame
void AddSamples(wxArrayString& files); void AddSamples(wxArrayString& files);
void OnAutoImportDir(const wxString& pathToDirectory); void OnAutoImportDir(const wxString& pathToDirectory);
// -------------------------------------------------------------------
void PlaySample(const std::string& filepath, const std::string& sample, bool seek = false,
wxFileOffset where = NULL, wxSeekMode mode = wxFromStart);
// Recieve custom events
// -------------------------------------------------------------------
void OnRecieveLoopPoints(SampleHive::SH_LoopPointsEvent& event);
void OnRecieveStatusBarStatus(SampleHive::SH_SetStatusBarMessageEvent& event);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
void LoadDatabase(); void LoadDatabase();
void RefreshDatabase(); void RefreshDatabase();
@ -264,6 +293,9 @@ class MainFrame : public wxFrame
// Call after frame creation // Call after frame creation
void SetAfterFrameCreate(); void SetAfterFrameCreate();
// -------------------------------------------------------------------
void ClearLoopPoints();
// ------------------------------------------------------------------- // -------------------------------------------------------------------
friend class App; friend class App;
}; };

69
src/SH_Event.cpp Normal file
View File

@ -0,0 +1,69 @@
#include "SH_Event.hpp"
namespace SampleHive
{
SH_LoopPointsEvent::SH_LoopPointsEvent(wxEventType eventType, int winId)
: wxCommandEvent(eventType, winId)
{
}
SH_LoopPointsEvent::~SH_LoopPointsEvent()
{
}
wxDEFINE_EVENT(SH_EVT_LOOP_POINTS_UPDATED, SH_LoopPointsEvent);
// SH_AddSampleEvent::SH_AddSampleEvent(wxEventType eventType, int winId)
// : wxCommandEvent(eventType, winId)
// {
// }
// SH_AddSampleEvent::~SH_AddSampleEvent()
// {
// }
// wxDEFINE_EVENT(SH_EVT_STATUS_ADD_SAMPLE, SH_AddSampleEvent);
// SH_MediaEvent::SH_MediaEvent(wxEventType eventType, int winId)
// : wxCommandEvent(eventType, winId)
// {
// }
// SH_MediaEvent::~SH_MediaEvent()
// {
// }
// wxDEFINE_EVENT(SH_EVT_MEDIA_STATUS_UPDATED, SH_MediaEvent);
SH_SetStatusBarMessageEvent::SH_SetStatusBarMessageEvent(wxEventType eventType, int winId)
: wxCommandEvent(eventType, winId)
{
}
SH_SetStatusBarMessageEvent::~SH_SetStatusBarMessageEvent()
{
}
wxDEFINE_EVENT(SH_EVT_STATUSBAR_MESSAGE_UPDATED, SH_SetStatusBarMessageEvent);
// SH_TimerEvent::SH_TimerEvent(wxEventType eventType, int winId)
// : wxCommandEvent(eventType, winId)
// {
// }
// SH_TimerEvent::~SH_TimerEvent()
// {
// }
// wxDEFINE_EVENT(SH_EVT_TIMER_STATUS_UPDATED, SH_TimerEvent);
}

106
src/SH_Event.hpp Normal file
View File

@ -0,0 +1,106 @@
#pragma once
#include <utility>
#include <wx/event.h>
namespace SampleHive
{
class SH_LoopPointsEvent : public wxCommandEvent
{
public:
SH_LoopPointsEvent(wxEventType eventType, int winId);
~SH_LoopPointsEvent();
public:
virtual wxEvent* Clone() const { return new SH_LoopPointsEvent(*this); }
public:
std::pair<double, double> GetLoopPoints() const { return { m_LoopA, m_LoopB }; };
void SetLoopPoints(std::pair<double&, double&> loopPoints)
{ m_LoopA = loopPoints.first; m_LoopB = loopPoints.second; };
private:
double m_LoopA, m_LoopB;
};
wxDECLARE_EVENT(SH_EVT_LOOP_POINTS_UPDATED, SH_LoopPointsEvent);
// class SH_AddSampleEvent : public wxCommandEvent
// {
// public:
// SH_AddSampleEvent(wxEventType eventType, int winId);
// ~SH_AddSampleEvent();
// public:
// virtual wxEvent* Clone() const { return new SH_AddSampleEvent(*this); }
// public:
// wxArrayString GetArrayString() const { return m_Files; };
// void SetArrayString(const wxArrayString& files) { m_Files = files; };
// private:
// wxArrayString m_Files;
// };
// wxDECLARE_EVENT(SH_EVT_STATUS_ADD_SAMPLE, SH_AddSampleEvent);
// class SH_MediaEvent : public wxCommandEvent
// {
// public:
// SH_MediaEvent(wxEventType eventType, int winId);
// ~SH_MediaEvent();
// public:
// virtual wxEvent* Clone() const { return new SH_MediaEvent(*this); }
// public:
// void SetPath(const wxString& path) { m_Path = path; }
// wxString GetPath() const { return m_Path; }
// private:
// wxString m_Path;
// };
// wxDECLARE_EVENT(SH_EVT_MEDIA_STATUS_UPDATED, SH_MediaEvent);
class SH_SetStatusBarMessageEvent : public wxCommandEvent
{
public:
SH_SetStatusBarMessageEvent(wxEventType eventType, int winId);
~SH_SetStatusBarMessageEvent();
public:
virtual wxEvent* Clone() const { return new SH_SetStatusBarMessageEvent(*this); }
public:
std::pair<wxString, int> GetMessageAndSection() const { return { m_Msg, m_Section }; }
void SetMessageAndSection(std::pair<const wxString&, int> status) { m_Msg = status.first; m_Section = status.second; }
private:
wxString m_Msg;
int m_Section;
};
wxDECLARE_EVENT(SH_EVT_STATUSBAR_MESSAGE_UPDATED, SH_SetStatusBarMessageEvent);
// class SH_TimerEvent : public wxCommandEvent
// {
// public:
// SH_TimerEvent(wxEventType eventType, int winId);
// ~SH_TimerEvent();
// public:
// virtual wxEvent* Clone() const { return new SH_TimerEvent(*this); }
// public:
// std::pair<int, bool> GetSecondsAndMode() const { return { m_Seconds, m_Mode }; }
// void SetSecondsAndMode(std::pair<int, bool> timerStatus) { m_Seconds = timerStatus.first; m_Mode = timerStatus.second; }
// private:
// int m_Seconds;
// bool m_Mode;
// };
// wxDECLARE_EVENT(SH_EVT_TIMER_STATUS_UPDATED, SH_TimerEvent);
}

View File

@ -21,8 +21,9 @@
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <wx/colour.h>
#include <wx/log.h> #include <wx/log.h>
#include <wx/stdpaths.h> // #include <wx/stdpaths.h>
#include <wx/filename.h> #include <wx/filename.h>
#include <yaml-cpp/emittermanip.h> #include <yaml-cpp/emittermanip.h>
@ -39,7 +40,9 @@ Serializer::Serializer(const std::string& filepath)
std::string system_font_face = font.GetFaceName().ToStdString(); std::string system_font_face = font.GetFaceName().ToStdString();
int system_font_size = font.GetPointSize(); int system_font_size = font.GetPointSize();
std::string dir = wxStandardPaths::Get().GetDocumentsDir().ToStdString(); wxColour colour = "#FE9647";
std::string dir = wxGetHomeDir().ToStdString();
if (!ifstrm) if (!ifstrm)
{ {
@ -73,6 +76,11 @@ Serializer::Serializer(const std::string& filepath)
m_Emitter << YAML::EndMap; m_Emitter << YAML::EndMap;
m_Emitter << YAML::EndMap << YAML::Newline; 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::Newline << YAML::Key << "Collection";
m_Emitter << YAML::BeginMap; m_Emitter << YAML::BeginMap;
m_Emitter << YAML::Key << "AutoImport" << YAML::Value << false; m_Emitter << YAML::Key << "AutoImport" << YAML::Value << false;
@ -249,6 +257,64 @@ FontType Serializer::DeserializeDisplaySettings() const
return { face, size }; 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) void Serializer::SerializeAutoImportSettings(wxTextCtrl& textCtrl, wxCheckBox& checkBox)
{ {
YAML::Emitter out; YAML::Emitter out;

View File

@ -76,6 +76,8 @@ class Serializer
// Display settings // Display settings
void SerializeDisplaySettings(wxFont& font); void SerializeDisplaySettings(wxFont& font);
FontType DeserializeDisplaySettings() const; FontType DeserializeDisplaySettings() const;
void SerializeWaveformColour(wxColour& colour);
wxColour DeserializeWaveformColour() const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Auto import settings // Auto import settings

View File

@ -21,7 +21,7 @@
#include <wx/defs.h> #include <wx/defs.h>
#include <wx/gdicmn.h> #include <wx/gdicmn.h>
#include <wx/log.h> #include <wx/log.h>
#include <wx/stdpaths.h> // #include <wx/stdpaths.h>
#include <wx/stringimpl.h> #include <wx/stringimpl.h>
#include "ControlID_Enums.hpp" #include "ControlID_Enums.hpp"
@ -51,8 +51,10 @@ Settings::Settings(wxWindow* window, const std::string& configFilepath, const st
m_DisplayTopSizer = new wxBoxSizer(wxVERTICAL); m_DisplayTopSizer = new wxBoxSizer(wxVERTICAL);
m_DisplayFontSizer = new wxBoxSizer(wxHORIZONTAL); m_DisplayFontSizer = new wxBoxSizer(wxHORIZONTAL);
m_WaveformColourSizer = new wxBoxSizer(wxHORIZONTAL);
wxString fontChoices[] = {"System default"}; wxString fontChoices[] = {"System default"};
Serializer serializer(m_ConfigFilepath);
// m_RowHeightText = new wxStaticText(); // m_RowHeightText = new wxStaticText();
m_FontTypeText = new wxStaticText(m_DisplaySettingPanel, wxID_ANY, "Font", wxDefaultPosition, wxDefaultSize, 0); m_FontTypeText = new wxStaticText(m_DisplaySettingPanel, wxID_ANY, "Font", wxDefaultPosition, wxDefaultSize, 0);
// m_RowHeight = new wxChoice(); // 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 = new wxSpinCtrl(m_DisplaySettingPanel, SD_FontSize, "Default", wxDefaultPosition, wxDefaultSize);
m_FontSize->SetValue(window->GetFont().GetPointSize()); m_FontSize->SetValue(window->GetFont().GetPointSize());
m_FontBrowseButton = new wxButton(m_DisplaySettingPanel, SD_FontBrowseButton, "Select font", wxDefaultPosition, wxDefaultSize, 0); 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); m_CollectionSettingPanel = new wxPanel(m_Notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
@ -68,7 +73,7 @@ Settings::Settings(wxWindow* window, const std::string& configFilepath, const st
m_CollectionImportDirSizer = new wxBoxSizer(wxHORIZONTAL); m_CollectionImportDirSizer = new wxBoxSizer(wxHORIZONTAL);
m_ShowFileExtensionSizer = new wxBoxSizer(wxHORIZONTAL); m_ShowFileExtensionSizer = new wxBoxSizer(wxHORIZONTAL);
wxString defaultDir = wxStandardPaths::Get().GetDocumentsDir(); wxString defaultDir = wxGetHomeDir();
m_AutoImportCheck = new wxCheckBox(m_CollectionSettingPanel, SD_AutoImport, "Auto import", wxDefaultPosition, wxDefaultSize, 0); m_AutoImportCheck = new wxCheckBox(m_CollectionSettingPanel, SD_AutoImport, "Auto import", wxDefaultPosition, wxDefaultSize, 0);
m_ImportDirLocation = new wxTextCtrl(m_CollectionSettingPanel, wxID_ANY, defaultDir, wxDefaultPosition, wxDefaultSize, 0); m_ImportDirLocation = new wxTextCtrl(m_CollectionSettingPanel, wxID_ANY, defaultDir, wxDefaultPosition, wxDefaultSize, 0);
@ -108,30 +113,34 @@ Settings::Settings(wxWindow* window, const std::string& configFilepath, const st
Bind(wxEVT_BUTTON, &Settings::OnClickBrowseAutoImportDir, this, SD_BrowseAutoImportDir); Bind(wxEVT_BUTTON, &Settings::OnClickBrowseAutoImportDir, this, SD_BrowseAutoImportDir);
Bind(wxEVT_BUTTON, &Settings::OnClickConfigBrowse, this, SD_BrowseConfigDir); Bind(wxEVT_BUTTON, &Settings::OnClickConfigBrowse, this, SD_BrowseConfigDir);
Bind(wxEVT_BUTTON, &Settings::OnClickDatabaseBrowse, this, SD_BrowseDatabaseDir); Bind(wxEVT_BUTTON, &Settings::OnClickDatabaseBrowse, this, SD_BrowseDatabaseDir);
Bind(wxEVT_COLOURPICKER_CHANGED, &Settings::OnChangeWaveformColour, this, SD_WaveformColourPickerCtrl);
// Adding controls to sizers // Adding controls to sizers
m_NotebookSizer->Add(m_Notebook, 1, wxALL | wxEXPAND, 2); m_NotebookSizer->Add(m_Notebook, 1, wxALL | wxEXPAND, 2);
m_GeneralMainSizer->Add(m_ConfigLabel, 0, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 2); m_GeneralMainSizer->Add(m_ConfigLabel, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_GeneralMainSizer->Add(m_ConfigText, 1, wxALL | wxALIGN_CENTER_VERTICAL | wxEXPAND, 2); m_GeneralMainSizer->Add(m_ConfigText, 1, wxALL | wxALIGN_CENTER_VERTICAL | wxEXPAND, 2);
m_GeneralMainSizer->Add(m_ConfigBrowse, 0, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 2); m_GeneralMainSizer->Add(m_ConfigBrowse, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_GeneralMainSizer->Add(m_DatabaseLabel, 0, wxALL | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 2); m_GeneralMainSizer->Add(m_DatabaseLabel, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_GeneralMainSizer->Add(m_DatabaseText, 1, wxALL | wxALIGN_CENTER_VERTICAL | wxEXPAND, 2); m_GeneralMainSizer->Add(m_DatabaseText, 1, wxALL | wxALIGN_CENTER_VERTICAL | wxEXPAND, 2);
m_GeneralMainSizer->Add(m_DatabaseBrowse, 0, wxALL | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 2); m_GeneralMainSizer->Add(m_DatabaseBrowse, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_DisplayFontSizer->Add(m_FontTypeText, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); m_DisplayFontSizer->Add(m_FontTypeText, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_DisplayFontSizer->Add(m_FontType, 1, wxALL | wxALIGN_CENTER_VERTICAL, 2); 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_FontSize, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_DisplayFontSizer->Add(m_FontBrowseButton, 0, wxALL | wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 2); m_DisplayFontSizer->Add(m_FontBrowseButton, 0, wxALL | wxALIGN_CENTER_VERTICAL, 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_AutoImportCheck, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_CollectionImportDirSizer->Add(m_ImportDirLocation, 1, wxALL | wxALIGN_CENTER_VERTICAL, 2); m_CollectionImportDirSizer->Add(m_ImportDirLocation, 1, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_CollectionImportDirSizer->Add(m_BrowseAutoImportDirButton, 0, wxALL | wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 2); m_CollectionImportDirSizer->Add(m_BrowseAutoImportDirButton, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
m_ShowFileExtensionSizer->Add(m_ShowFileExtensionCheck, 0, wxALL | wxALIGN_LEFT, 2); m_ShowFileExtensionSizer->Add(m_ShowFileExtensionCheck, 0, wxALL, 2);
m_CollectionTopSizer->Add(m_CollectionImportDirSizer, 0, wxALL | wxEXPAND, 2); m_CollectionTopSizer->Add(m_CollectionImportDirSizer, 0, wxALL | wxEXPAND, 2);
m_CollectionTopSizer->Add(m_ShowFileExtensionSizer, 0, wxALL | wxEXPAND, 2); m_CollectionTopSizer->Add(m_ShowFileExtensionSizer, 0, wxALL | wxEXPAND, 2);
@ -169,7 +178,7 @@ Settings::Settings(wxWindow* window, const std::string& configFilepath, const st
void Settings::OnClickConfigBrowse(wxCommandEvent& event) void Settings::OnClickConfigBrowse(wxCommandEvent& event)
{ {
wxString initial_dir = wxStandardPaths::Get().GetDocumentsDir(); wxString initial_dir = wxGetHomeDir();
m_DirDialog = new wxDirDialog(this, "Select a directory..", initial_dir, m_DirDialog = new wxDirDialog(this, "Select a directory..", initial_dir,
wxDD_DEFAULT_STYLE | wxDD_DEFAULT_STYLE |
@ -192,7 +201,7 @@ void Settings::OnClickConfigBrowse(wxCommandEvent& event)
void Settings::OnClickDatabaseBrowse(wxCommandEvent& event) void Settings::OnClickDatabaseBrowse(wxCommandEvent& event)
{ {
wxString initial_dir = wxStandardPaths::Get().GetDocumentsDir(); wxString initial_dir = wxGetHomeDir();
m_DirDialog = new wxDirDialog(this, "Select a directory..", initial_dir, m_DirDialog = new wxDirDialog(this, "Select a directory..", initial_dir,
wxDD_DEFAULT_STYLE | wxDD_DEFAULT_STYLE |
@ -255,7 +264,7 @@ void Settings::OnClickBrowseAutoImportDir(wxCommandEvent& event)
{ {
Serializer serializer(m_ConfigFilepath); Serializer serializer(m_ConfigFilepath);
wxString initial_dir = wxStandardPaths::Get().GetDocumentsDir(); wxString initial_dir = wxGetHomeDir();
m_DirDialog = new wxDirDialog(this, "Select a directory..", initial_dir, m_DirDialog = new wxDirDialog(this, "Select a directory..", initial_dir,
wxDD_DEFAULT_STYLE | wxDD_DEFAULT_STYLE |
@ -426,4 +435,25 @@ wxString Settings::GetImportDirPath()
return dir; 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(){} Settings::~Settings(){}

View File

@ -23,6 +23,7 @@
#include <string> #include <string>
#include <wx/button.h> #include <wx/button.h>
#include <wx/clrpicker.h>
#include <wx/checkbox.h> #include <wx/checkbox.h>
#include <wx/choice.h> #include <wx/choice.h>
#include <wx/dialog.h> #include <wx/dialog.h>
@ -86,6 +87,9 @@ class Settings : public wxDialog
wxFontDialog* m_FontDialog; wxFontDialog* m_FontDialog;
wxButton* m_FontBrowseButton; wxButton* m_FontBrowseButton;
wxSpinCtrl* m_FontSize; wxSpinCtrl* m_FontSize;
wxBoxSizer* m_WaveformColourSizer;
wxStaticText* m_WaveformColourLabel;
wxColourPickerCtrl* m_WaveformColourPickerCtrl;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Collection page // Collection page
@ -121,6 +125,7 @@ class Settings : public wxDialog
// ------------------------------------------------------------------- // -------------------------------------------------------------------
bool bAutoImport = false; bool bAutoImport = false;
bool bShowExtension = true; bool bShowExtension = true;
bool bWaveformColourChanged = false;
private: private:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
@ -131,6 +136,7 @@ class Settings : public wxDialog
void OnClickBrowseAutoImportDir(wxCommandEvent& event); void OnClickBrowseAutoImportDir(wxCommandEvent& event);
void OnChangeFontSize(wxSpinEvent& event); void OnChangeFontSize(wxSpinEvent& event);
void OnSelectFont(wxCommandEvent& event); void OnSelectFont(wxCommandEvent& event);
void OnChangeWaveformColour(wxColourPickerEvent& event);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
void SetCustomFont(); void SetCustomFont();
@ -147,4 +153,6 @@ class Settings : public wxDialog
inline wxFont GetFontType() { return m_Font; }; inline wxFont GetFontType() { return m_Font; };
inline bool CanAutoImport() { return bAutoImport; }; inline bool CanAutoImport() { return bAutoImport; };
inline bool ShouldShowFileExtension() { return bShowExtension; }; inline bool ShouldShowFileExtension() { return bShowExtension; };
inline bool IsWaveformColourChanged() { return bWaveformColourChanged; }
inline wxColour GetWaveformColour() { return m_WaveformColourPickerCtrl->GetColour(); }
}; };

View File

@ -18,11 +18,17 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <taglib/taglib.h>
#include <taglib/fileref.h>
#include <taglib/tstring.h>
#include "Tags.hpp" #include "Tags.hpp"
#include "SampleHiveConfig.hpp"
#include <taglib/tag.h>
#include <taglib/fileref.h>
#ifndef USE_SYSTEM_INCLUDE_PATH
#include <taglib/toolkit/tstring.h>
#else
#include <taglib/tstring.h>
#endif
Tags::Tags(const std::string& filename) Tags::Tags(const std::string& filename)
: m_Filepath(filename) : m_Filepath(filename)

496
src/WaveformViewer.cpp Normal file
View File

@ -0,0 +1,496 @@
/* 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/brush.h>
#include <wx/dcclient.h>
#include <wx/defs.h>
#include <wx/dcmemory.h>
#include <wx/filefn.h>
#include <wx/gdicmn.h>
#include <wx/log.h>
#include <wx/pen.h>
#include <sndfile.hh>
#include "WaveformViewer.hpp"
#include "Database.hpp"
#include "SettingsDialog.hpp"
#include "Serialize.hpp"
#include "Tags.hpp"
#include "SH_Event.hpp"
WaveformViewer::WaveformViewer(wxWindow* parentFrame, wxWindow* window, wxDataViewListCtrl& library,
wxMediaCtrl& mediaCtrl, 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_InfoBar(infoBar), m_MediaCtrl(mediaCtrl),
m_ConfigFilepath(configFilepath), m_DatabaseFilepath(databaseFilepath)
{
this->SetDoubleBuffered(true);
Bind(wxEVT_PAINT, &WaveformViewer::OnPaint, this);
Bind(wxEVT_MOTION, &WaveformViewer::OnMouseMotion, this);
Bind(wxEVT_LEFT_DOWN, &WaveformViewer::OnMouseLeftButtonDown, this);
Bind(wxEVT_LEFT_UP, &WaveformViewer::OnMouseLeftButtonUp, this);
// Bind(wxEVT_KEY_DOWN, &WaveformViewer::OnControlKeyDown, this);
Bind(wxEVT_KEY_UP, &WaveformViewer::OnControlKeyUp, 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);
UpdateWaveformBitmap();
bBitmapDirty = false;
}
dc.DrawBitmap(m_WaveformBitmap, 0, 0, false);
RenderPlayhead(dc);
// Draw selection range
if (bSelectRange)
{
wxRect rect(m_CurrentPoint, m_AnchorPoint);
dc.SetPen(wxPen(wxColour(200, 200, 200), 2, wxPENSTYLE_SOLID));
dc.SetBrush(wxBrush(wxColour(200, 200, 200, 80), wxBRUSHSTYLE_SOLID));
dc.DrawRectangle(rect);
}
// Draw selected area
if (!bSelectRange && bDrawSelectedArea && !bBitmapDirty)
{
dc.SetPen(wxPen(wxColour(200, 200, 200, 255), 4, wxPENSTYLE_SOLID));
dc.SetBrush(wxBrush(wxColour(200, 200, 200, 80), wxBRUSHSTYLE_SOLID));
dc.DrawRectangle(wxRect(m_AnchorPoint.x, -2, m_CurrentPoint.x - m_AnchorPoint.x, this->GetSize().GetHeight() + 5));
bAreaSelected = true;
SendLoopPoints();
}
else
bAreaSelected = false;
}
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);
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;
// TODO, FIXME: Don't reload file on every window resize
snd_file.read(&sample.at(0), frames * channels);
float display_width = this->GetSize().GetWidth();
float display_height = this->GetSize().GetHeight();
wxLogDebug("Calculating Waveform bars RMS..");
float chunk_size = (float)(frames) / (float)display_width;
int number_of_chunks = static_cast<int>(static_cast<float>(frames) / chunk_size);
// Start with low non-zero value
float normalize = 0.00001;
for (int i = 0; i < number_of_chunks; i++)
{
double sum = 0, mono = 0;
int start_point = static_cast<int>(i * chunk_size * channels);
// Iterate on the chunk, get the square of sum of monos
for (int j = 0; j < chunk_size; j++)
{
if (channels == 2)
mono = 0.5f * (sample[start_point + (2 * j)] + sample[start_point + (2 * j) + 1]);
else
mono = sample[start_point + j];
sum += mono * mono; // Square
}
sum /= chunk_size; // Mean
sum = pow(sum, 0.5); // Root
// We might bleed a bit on the end and get some near infs, dunno
// what is causing astronomically big numbers from sample[]
if ((sum < 200.0) && (sum > normalize))
normalize = sum;
waveform.push_back(sum);
}
// Actually normalize
for (int i = 0; i < waveform.size(); i++)
waveform[i] /= normalize;
// Draw code
wxMemoryDC mdc(m_WaveformBitmap);
mdc.SetBackground(wxBrush(wxColour(0, 0, 0, 150), wxBRUSHSTYLE_SOLID));
mdc.Clear();
m_WaveformColour = serializer.DeserializeWaveformColour();
mdc.SetPen(wxPen(wxColour(m_WaveformColour), 2, wxPENSTYLE_SOLID));
wxLogDebug("Drawing bitmap..");
for (int i = 0; i < waveform.size() - 1; i++)
{
float half_display_height = static_cast<float>(display_height) / 2.0f;
// X is percentage of i relative to waveform.size() multiplied by
// the width, Y is the half height times the value up or down
float X = display_width * ((float)i / waveform.size());
float Y = waveform[i] * half_display_height;
mdc.DrawLine(X, half_display_height + Y, X, half_display_height - Y);
}
wxLogDebug("Done drawing bitmap..");
}
void WaveformViewer::OnControlKeyDown(wxKeyEvent &event)
{
switch (event.GetKeyCode())
{
case WXK_CONTROL:
SetCursor(wxCURSOR_IBEAM);
break;
default:
SetCursor(wxCURSOR_ARROW);
break;
}
event.Skip();
}
void WaveformViewer::OnControlKeyUp(wxKeyEvent &event)
{
switch (event.GetKeyCode())
{
case WXK_CONTROL:
if (bSelectRange)
{
SetCursor(wxCURSOR_ARROW);
bSelectRange = false;
bDrawSelectedArea = false;
ReleaseMouse();
return;
}
break;
default:
break;
}
event.Skip();
}
void WaveformViewer::OnMouseMotion(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;
if (abs(pos.x - line_pos) <= 5 && pos.y <= 5)
{
SetCursor(wxCursor(wxCURSOR_HAND));
wxLogDebug("Cursor on playhead..");
}
else if (bSelectRange)
{
m_CurrentPoint = wxPoint(pos.x , pos.y);
Refresh();
wxLogDebug("CTRL pressed, pressing LMB will draw selection range at %d, %d", pos.x, pos.y);
}
else
return;
}
void WaveformViewer::OnMouseLeftButtonDown(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)
{
SetCursor(wxCURSOR_CLOSED_HAND);
CaptureMouse();
wxLogDebug("Mouse Captured playhead..");
}
else if (event.ControlDown())
{
wxLogDebug("LMB pressed");
SetCursor(wxCURSOR_CLOSED_HAND);
CaptureMouse();
bSelectRange = true;
m_AnchorPoint = wxPoint(pos.x, pos.y);
m_CurrentPoint = m_AnchorPoint;
}
else
{
SetCursor(wxCURSOR_ARROW);
return;
}
event.Skip();
}
void WaveformViewer::OnMouseLeftButtonUp(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;
if (!wxWindow::HasCapture())
{
wxLogDebug("Window doesn't have capture skipping..");
return;
}
if (bSelectRange)
{
wxLogDebug("LMB released");
m_CurrentPoint = wxPoint(pos.x, pos.y);
ReleaseMouse();
SetCursor(wxCURSOR_ARROW);
Refresh();
bSelectRange = false;
if (!bSelectRange)
bDrawSelectedArea = true;
}
else
{
ReleaseMouse();
SetCursor(wxCURSOR_ARROW);
m_MediaCtrl.Seek(seek_to, wxFromStart);
SendStatusBarStatus(wxString::Format(_("Now playing: %s"), selected), 1);
m_MediaCtrl.Play();
}
}
void WaveformViewer::ResetDC()
{
bBitmapDirty = true;
bSelectRange = false;
bDrawSelectedArea = false;
Refresh();
}
void WaveformViewer::SendLoopPoints()
{
wxLogDebug("%s Called", __FUNCTION__);
SampleHive::SH_LoopPointsEvent event(SampleHive::SH_EVT_LOOP_POINTS_UPDATED, this->GetId());
event.SetEventObject(this);
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;
int panel_width = this->GetSize().GetWidth();
int a = m_AnchorPoint.x, b = m_CurrentPoint.x;
double loopA = ((double)a / panel_width) * length;
double loopB = ((double)b / panel_width) * length;
event.SetLoopPoints({ loopA, loopB });
HandleWindowEvent(event);
wxLogDebug("%s processed event, sending loop points..", __FUNCTION__);
}
void WaveformViewer::SendStatusBarStatus(const wxString& msg, int section)
{
SampleHive::SH_SetStatusBarMessageEvent event(SampleHive::SH_EVT_STATUSBAR_MESSAGE_UPDATED, this->GetId());
event.SetEventObject(this);
event.SetMessageAndSection({ msg, section });
HandleWindowEvent(event);
}

97
src/WaveformViewer.hpp Normal file
View File

@ -0,0 +1,97 @@
/* 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/statusbr.h>
#include <wx/timer.h>
#include <wx/window.h>
class WaveformViewer : public wxPanel
{
public:
WaveformViewer(wxWindow* parentFrame, wxWindow* window, wxDataViewListCtrl& library,
wxMediaCtrl& mediaCtrl, 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;
const std::string& m_ConfigFilepath;
const std::string& m_DatabaseFilepath;
private:
// -------------------------------------------------------------------
wxBitmap m_WaveformBitmap;
wxColour m_PlayheadColour;
wxColour m_WaveformColour;
// -------------------------------------------------------------------
// Selection area coordinates
wxPoint m_AnchorPoint;
wxPoint m_CurrentPoint;
private:
// -------------------------------------------------------------------
bool bBitmapDirty = false;
bool bSelectRange = false;
bool bDrawSelectedArea = false;
bool bAreaSelected = false;
private:
// -------------------------------------------------------------------
void OnPaint(wxPaintEvent& event);
void RenderPlayhead(wxDC& dc);
void UpdateWaveformBitmap();
// -------------------------------------------------------------------
void OnMouseMotion(wxMouseEvent& event);
void OnMouseLeftButtonDown(wxMouseEvent& event);
void OnMouseLeftButtonUp(wxMouseEvent& event);
// -------------------------------------------------------------------
void OnControlKeyUp(wxKeyEvent& event);
void OnControlKeyDown(wxKeyEvent& event);
// -------------------------------------------------------------------
// Send custom events
void SendLoopPoints();
void SendStatusBarStatus(const wxString& msg, int section);
public:
// -------------------------------------------------------------------
void ResetDC();
};

3
subprojects/taglib.wrap Normal file
View File

@ -0,0 +1,3 @@
[wrap-git]
url = https://github.com/taglib/taglib.git
revision = master

View File

@ -0,0 +1,3 @@
[wrap-git]
url = https://github.com/wxWidgets/wxWidgets
revision = master