From 66ce190098ecf5597699a23a7b917b44e76d1245 Mon Sep 17 00:00:00 2001 From: apoorv569 Date: Fri, 16 Sep 2022 16:44:24 +0530 Subject: [PATCH] Add BPM detection using aubio. --- meson.build | 9 +++- src/Database/Database.cpp | 94 ++++++++++++++++++++++---------------- src/GUI/ListCtrl.cpp | 21 +++++++-- src/GUI/MainFrame.cpp | 7 +++ src/GUI/WaveformViewer.hpp | 2 + src/Utility/ControlIDs.hpp | 1 + src/Utility/Sample.cpp | 13 +++--- src/Utility/Sample.hpp | 11 +++-- src/Utility/Utils.cpp | 80 ++++++++++++++++++++++++++++++++ src/Utility/Utils.hpp | 3 ++ 10 files changed, 186 insertions(+), 55 deletions(-) diff --git a/meson.build b/meson.build index ed5aaf5..2a6ca89 100755 --- a/meson.build +++ b/meson.build @@ -277,6 +277,13 @@ if not spdlog.found() spdlog = spdlog_subproject.get_variable('spdlog_dep') endif +aubio = dependency('aubio', version: '>=0.4.9', required: false) + +if not aubio.found() + aubio_subproject = subproject('aubio') + aubio = aubio_subproject.get_variable('aubio_dep') +endif + # Create SampleHiveConfig.hpp based on configuration config = configure_file(output: 'SampleHiveConfig.hpp', configuration: config_data,) @@ -291,7 +298,7 @@ executable('SampleHive', cpp_args: [wx_cxx_flags], link_args: [wx_libs, link_args], include_directories : include_dirs, - dependencies: [wx, taglib, sqlite3, yaml, snd, spdlog], + dependencies: [wx, taglib, sqlite3, yaml, snd, spdlog, aubio], install: true, install_rpath: prefix / 'lib') diff --git a/src/Database/Database.cpp b/src/Database/Database.cpp index ad5c721..a08419f 100644 --- a/src/Database/Database.cpp +++ b/src/Database/Database.cpp @@ -106,6 +106,7 @@ void cDatabase::CreateTableSamples() "SAMPLEPACK TEXT NOT NULL," "TYPE TEXT NOT NULL," "CHANNELS INT NOT NULL," + "BPM INT NOT NULL," "LENGTH INT NOT NULL," "SAMPLERATE INT NOT NULL," "BITRATE INT NOT NULL," @@ -158,9 +159,9 @@ void cDatabase::InsertIntoSamples(const std::vector &samples) try { const auto sql = "INSERT INTO SAMPLES (FAVORITE, FILENAME, \ - EXTENSION, SAMPLEPACK, TYPE, CHANNELS, LENGTH, \ + EXTENSION, SAMPLEPACK, TYPE, CHANNELS, BPM, LENGTH, \ SAMPLERATE, BITRATE, PATH, TRASHED, HIVE) \ - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; Sqlite3Statement statement(m_pDatabase, sql); @@ -192,12 +193,13 @@ void cDatabase::InsertIntoSamples(const std::vector &samples) throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 4, sample_pack.c_str(), sample_pack.size(), SQLITE_STATIC)); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 5, type.c_str(), type.size(), SQLITE_STATIC)); throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 6, sample.GetChannels())); - throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 7, sample.GetLength())); - throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 8, sample.GetSampleRate())); - throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 9, sample.GetBitrate())); - throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 10, path.c_str(), path.size(), SQLITE_STATIC)); - throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 11, sample.GetTrashed())); - throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 12, hive.c_str(), hive.size(), SQLITE_STATIC)); + throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 7, sample.GetBPM())); + throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 8, sample.GetLength())); + throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 9, sample.GetSampleRate())); + throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 10, sample.GetBitrate())); + throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 11, path.c_str(), path.size(), SQLITE_STATIC)); + throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 12, sample.GetTrashed())); + throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 13, hive.c_str(), hive.size(), SQLITE_STATIC)); sqlite3_step(statement.stmt); @@ -591,7 +593,7 @@ wxVector> cDatabase::LoadSamplesDatabase(wxDataViewTreeCtrl } Sqlite3Statement statement(m_pDatabase, "SELECT FAVORITE, FILENAME, EXTENSION, SAMPLEPACK, \ - TYPE, CHANNELS, LENGTH, SAMPLERATE, BITRATE, PATH, \ + TYPE, CHANNELS, BPM, LENGTH, SAMPLERATE, BITRATE, PATH, \ TRASHED, HIVE FROM SAMPLES;"); int row = 0; @@ -604,14 +606,16 @@ wxVector> cDatabase::LoadSamplesDatabase(wxDataViewTreeCtrl wxString sample_pack = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 3))); wxString sample_type = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 4))); int channels = sqlite3_column_int(statement.stmt, 5); - int length = sqlite3_column_int(statement.stmt, 6); - int sample_rate = sqlite3_column_int(statement.stmt, 7); - int bitrate = sqlite3_column_int(statement.stmt, 8); - wxString path = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 9))); - int trashed = sqlite3_column_int(statement.stmt, 10); - wxString hive_name = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 11))); + int bpm = sqlite3_column_int(statement.stmt, 6); + int length = sqlite3_column_int(statement.stmt, 7); + int sample_rate = sqlite3_column_int(statement.stmt, 8); + int bitrate = sqlite3_column_int(statement.stmt, 9); + wxString path = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 10))); + int trashed = sqlite3_column_int(statement.stmt, 11); + wxString hive_name = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 12))); - wxString len = SampleHive::cUtils::Get().CalculateAndGetISOStandardTime(length); + wxString _length = SampleHive::cUtils::Get().CalculateAndGetISOStandardTime(length); + wxString _bpm = SampleHive::cUtils::Get().GetBPMString(bpm); wxVector vec; vec.reserve(12); @@ -684,7 +688,8 @@ wxVector> cDatabase::LoadSamplesDatabase(wxDataViewTreeCtrl vec.push_back(sample_pack); vec.push_back(sample_type); vec.push_back(wxString::Format("%d", channels)); - vec.push_back(len); + vec.push_back(_bpm); + vec.push_back(_length); vec.push_back(wxString::Format("%d", sample_rate)); vec.push_back(wxString::Format("%d", bitrate)); vec.push_back(path); @@ -716,7 +721,7 @@ wxVector>cDatabase::FilterDatabaseBySampleName(const std::st try { Sqlite3Statement statement(m_pDatabase, "SELECT FAVORITE, FILENAME, SAMPLEPACK, TYPE, \ - CHANNELS, LENGTH, SAMPLERATE, BITRATE, PATH \ + CHANNELS, BPM, LENGTH, SAMPLERATE, BITRATE, PATH \ FROM SAMPLES WHERE FILENAME LIKE '%' || ? || '%' ;"); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, sampleName.c_str(), sampleName.size(), SQLITE_STATIC)); @@ -732,12 +737,14 @@ wxVector>cDatabase::FilterDatabaseBySampleName(const std::st wxString sample_pack = wxString(std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 2)))); wxString sample_type = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 3))); int channels = sqlite3_column_int(statement.stmt, 4); - int length = sqlite3_column_int(statement.stmt, 5); - int sample_rate = sqlite3_column_int(statement.stmt, 6); - int bitrate = sqlite3_column_int(statement.stmt, 7); - wxString path = wxString(std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 8)))); + int bpm = sqlite3_column_int(statement.stmt, 5); + int length = sqlite3_column_int(statement.stmt, 6); + int sample_rate = sqlite3_column_int(statement.stmt, 7); + int bitrate = sqlite3_column_int(statement.stmt, 8); + wxString path = wxString(std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 9)))); - wxString len = SampleHive::cUtils::Get().CalculateAndGetISOStandardTime(length); + wxString _length = SampleHive::cUtils::Get().CalculateAndGetISOStandardTime(length); + wxString _bpm = SampleHive::cUtils::Get().GetBPMString(bpm); wxVector vec; @@ -758,7 +765,8 @@ wxVector>cDatabase::FilterDatabaseBySampleName(const std::st vec.push_back(sample_pack); vec.push_back(sample_type); vec.push_back(wxString::Format("%d", channels)); - vec.push_back(len); + vec.push_back(_bpm); + vec.push_back(_length); vec.push_back(wxString::Format("%d", sample_rate)); vec.push_back(wxString::Format("%d", bitrate)); vec.push_back(path); @@ -789,7 +797,7 @@ wxVector>cDatabase::FilterDatabaseByHiveName(const std::stri try { Sqlite3Statement statement(m_pDatabase, "SELECT FAVORITE, FILENAME, SAMPLEPACK, TYPE, \ - CHANNELS, LENGTH, SAMPLERATE, BITRATE, PATH \ + CHANNELS, BPM, LENGTH, SAMPLERATE, BITRATE, PATH \ FROM SAMPLES WHERE HIVE = ? AND FAVORITE = 1;"); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, hiveName.c_str(), hiveName.size(), SQLITE_STATIC)); @@ -805,12 +813,14 @@ wxVector>cDatabase::FilterDatabaseByHiveName(const std::stri wxString sample_pack = wxString(std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 2)))); wxString sample_type = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 3))); int channels = sqlite3_column_int(statement.stmt, 4); - int length = sqlite3_column_int(statement.stmt, 5); - int sample_rate = sqlite3_column_int(statement.stmt, 6); - int bitrate = sqlite3_column_int(statement.stmt, 7); - wxString path = wxString(std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 8)))); + int bpm = sqlite3_column_int(statement.stmt, 5); + int length = sqlite3_column_int(statement.stmt, 6); + int sample_rate = sqlite3_column_int(statement.stmt, 7); + int bitrate = sqlite3_column_int(statement.stmt, 8); + wxString path = wxString(std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 9)))); - wxString len = SampleHive::cUtils::Get().CalculateAndGetISOStandardTime(length); + wxString _length = SampleHive::cUtils::Get().CalculateAndGetISOStandardTime(length); + wxString _bpm = SampleHive::cUtils::Get().GetBPMString(bpm); wxVector vec; @@ -827,7 +837,8 @@ wxVector>cDatabase::FilterDatabaseByHiveName(const std::stri vec.push_back(sample_pack); vec.push_back(sample_type); vec.push_back(wxString::Format("%d", channels)); - vec.push_back(len); + vec.push_back(_bpm); + vec.push_back(_length); vec.push_back(wxString::Format("%d", sample_rate)); vec.push_back(wxString::Format("%d", bitrate)); vec.push_back(path); @@ -969,7 +980,7 @@ wxVector>cDatabase::RestoreFromTrashByFilename(const std::st try { const auto sql = "SELECT FAVORITE, FILENAME, EXTENSION, SAMPLEPACK, \ - TYPE, CHANNELS, LENGTH, SAMPLERATE, BITRATE, PATH, \ + TYPE, CHANNELS, BPM, LENGTH, SAMPLERATE, BITRATE, PATH, \ TRASHED, HIVE FROM SAMPLES WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); @@ -984,14 +995,16 @@ wxVector>cDatabase::RestoreFromTrashByFilename(const std::st wxString sample_pack = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 3))); wxString sample_type = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 4))); int channels = sqlite3_column_int(statement.stmt, 5); - int length = sqlite3_column_int(statement.stmt, 6); - int sample_rate = sqlite3_column_int(statement.stmt, 7); - int bitrate = sqlite3_column_int(statement.stmt, 8); - wxString path = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 9))); - int trashed = sqlite3_column_int(statement.stmt, 10); - wxString hive_name = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 11))); + int bpm = sqlite3_column_int(statement.stmt, 6); + int length = sqlite3_column_int(statement.stmt, 7); + int sample_rate = sqlite3_column_int(statement.stmt, 8); + int bitrate = sqlite3_column_int(statement.stmt, 9); + wxString path = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 10))); + int trashed = sqlite3_column_int(statement.stmt, 11); + wxString hive_name = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 12))); - wxString len = SampleHive::cUtils::Get().CalculateAndGetISOStandardTime(length); + wxString _length = SampleHive::cUtils::Get().CalculateAndGetISOStandardTime(length); + wxString _bpm = SampleHive::cUtils::Get().GetBPMString(bpm); wxVector vec; @@ -1010,7 +1023,8 @@ wxVector>cDatabase::RestoreFromTrashByFilename(const std::st vec.push_back(sample_pack); vec.push_back(sample_type); vec.push_back(wxString::Format("%d", channels)); - vec.push_back(len); + vec.push_back(_bpm); + vec.push_back(_length); vec.push_back(wxString::Format("%d", sample_rate)); vec.push_back(wxString::Format("%d", bitrate)); vec.push_back(path); diff --git a/src/GUI/ListCtrl.cpp b/src/GUI/ListCtrl.cpp index 3c27646..4d81627 100644 --- a/src/GUI/ListCtrl.cpp +++ b/src/GUI/ListCtrl.cpp @@ -76,6 +76,13 @@ cListCtrl::cListCtrl(wxWindow* window) wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_REORDERABLE); + AppendTextColumn(_("BPM"), + wxDATAVIEW_CELL_INERT, + 80, + wxALIGN_RIGHT, + wxDATAVIEW_COL_RESIZABLE | + wxDATAVIEW_COL_SORTABLE | + wxDATAVIEW_COL_REORDERABLE); AppendTextColumn(_("Length"), wxDATAVIEW_CELL_INERT, 80, @@ -679,10 +686,11 @@ void cListCtrl::OnShowLibraryColumnHeaderContextMenu(wxDataViewEvent& event) wxDataViewColumn* SamplePackColumn = this->GetColumn(2); wxDataViewColumn* TypeColumn = this->GetColumn(3); wxDataViewColumn* ChannelsColumn = this->GetColumn(4); - wxDataViewColumn* LengthColumn = this->GetColumn(5); - wxDataViewColumn* SampleRateColumn = this->GetColumn(6); - wxDataViewColumn* BitrateColumn = this->GetColumn(7); - wxDataViewColumn* PathColumn = this->GetColumn(8); + wxDataViewColumn* BpmColumn = this->GetColumn(5); + wxDataViewColumn* LengthColumn = this->GetColumn(6); + wxDataViewColumn* SampleRateColumn = this->GetColumn(7); + wxDataViewColumn* BitrateColumn = this->GetColumn(8); + wxDataViewColumn* PathColumn = this->GetColumn(9); menu.AppendCheckItem(SampleHive::ID::MN_ColumnFavorite, _("Favorites"), _("Toggle favorites column"))->Check(FavoriteColumn->IsShown()); @@ -694,6 +702,8 @@ void cListCtrl::OnShowLibraryColumnHeaderContextMenu(wxDataViewEvent& event) _("Toggle type column"))->Check(TypeColumn->IsShown()); menu.AppendCheckItem(SampleHive::ID::MN_ColumnChannels, _("Channels"), _("Toggle channels column"))->Check(ChannelsColumn->IsShown()); + menu.AppendCheckItem(SampleHive::ID::MN_ColumnBPM, _("BPM"), + _("Toggle length column"))->Check(BpmColumn->IsShown()); menu.AppendCheckItem(SampleHive::ID::MN_ColumnLength, _("Length"), _("Toggle length column"))->Check(LengthColumn->IsShown()); menu.AppendCheckItem(SampleHive::ID::MN_ColumnSampleRate, _("Sample Rate"), @@ -720,6 +730,9 @@ void cListCtrl::OnShowLibraryColumnHeaderContextMenu(wxDataViewEvent& event) case SampleHive::ID::MN_ColumnChannels: ChannelsColumn->SetHidden(!menu.IsChecked(SampleHive::ID::MN_ColumnChannels)); break; + case SampleHive::ID::MN_ColumnBPM: + BpmColumn->SetHidden(!menu.IsChecked(SampleHive::ID::MN_ColumnBPM)); + break; case SampleHive::ID::MN_ColumnLength: LengthColumn->SetHidden(!menu.IsChecked(SampleHive::ID::MN_ColumnLength)); break; diff --git a/src/GUI/MainFrame.cpp b/src/GUI/MainFrame.cpp index cb6a5d6..b2f1e24 100644 --- a/src/GUI/MainFrame.cpp +++ b/src/GUI/MainFrame.cpp @@ -914,6 +914,13 @@ void cMainFrame::PlaySample(const std::string& filepath, const std::string& samp if (!m_pMediaCtrl->Play()) SH_LOG_ERROR("Error! Cannot play sample."); + // ==================================================== + // TODO: Remove these lines (for debugging only) + float bpm = SampleHive::cUtils::Get().GetBPM(filepath); + wxString bpm_str = SampleHive::cUtils::Get().GetBPMString(bpm); + SH_LOG_DEBUG("BPM: {}, BPM_STR: {}", bpm, bpm_str); + // ==================================================== + PushStatusText(wxString::Format(_("Now playing: %s"), sample), 1); if (!m_pTimer->IsRunning()) diff --git a/src/GUI/WaveformViewer.hpp b/src/GUI/WaveformViewer.hpp index 8a88354..fb6630e 100644 --- a/src/GUI/WaveformViewer.hpp +++ b/src/GUI/WaveformViewer.hpp @@ -81,6 +81,8 @@ class cWaveformViewer : public wxPanel // ------------------------------------------------------------------- std::pair CalculateLoopPoints(); + float GetBPM(const std::string& path); + public: // ------------------------------------------------------------------- void ResetBitmapDC(); diff --git a/src/Utility/ControlIDs.hpp b/src/Utility/ControlIDs.hpp index 144ce1a..2d42a0d 100644 --- a/src/Utility/ControlIDs.hpp +++ b/src/Utility/ControlIDs.hpp @@ -90,6 +90,7 @@ namespace SampleHive { namespace ID { MN_ColumnSamplePack, MN_ColumnType, MN_ColumnChannels, + MN_ColumnBPM, MN_ColumnLength, MN_ColumnSampleRate, MN_ColumnBitrate, diff --git a/src/Utility/Sample.cpp b/src/Utility/Sample.cpp index 80cd259..21b80ab 100644 --- a/src/Utility/Sample.cpp +++ b/src/Utility/Sample.cpp @@ -36,11 +36,10 @@ Sample::Sample() ///Overloaded Constructor, Creates a sample profile with supplied data. @see Set() Sample::Sample(int favorite, const std::string& filename, const std::string& fileExtension, - const std::string& samplePack, const std::string& type, int channels, int length, - int sampleRate, int bitrate, const std::string& path, int trashed) + const std::string& samplePack, const std::string& type, int channels, int bpm, + int length, int sampleRate, int bitrate, const std::string& path, int trashed) { - Set(favorite, filename, fileExtension, samplePack, type, - channels, length, sampleRate, bitrate, path, trashed); + Set(favorite, filename, fileExtension, samplePack, type, channels, bpm, length, sampleRate, bitrate, path, trashed); } ///Clears all sample data @@ -48,6 +47,7 @@ void Sample::Clear() { m_Favorite = 0; m_Channels = 0; + m_BPM = 0; m_Length = 0; m_SampleRate = 0; m_Bitrate = 0; @@ -60,8 +60,8 @@ void Sample::Clear() } void Sample::Set(int favorite, const std::string& filename, const std::string& fileExtension, - const std::string& samplePack, const std::string& type, int channels, int length, - int sampleRate, int bitrate, const std::string& path, int trashed) + const std::string& samplePack, const std::string& type, int channels, int bpm, + int length, int sampleRate, int bitrate, const std::string& path, int trashed) { m_Favorite = favorite; m_Filename = filename; @@ -69,6 +69,7 @@ void Sample::Set(int favorite, const std::string& filename, const std::string& f m_SamplePack = samplePack; m_Type = type; m_Channels = channels; + m_BPM = bpm; m_Length = length; m_SampleRate = sampleRate; m_Bitrate = bitrate; diff --git a/src/Utility/Sample.hpp b/src/Utility/Sample.hpp index 1cffc60..ab12209 100644 --- a/src/Utility/Sample.hpp +++ b/src/Utility/Sample.hpp @@ -40,13 +40,14 @@ class Sample public: Sample(); Sample(int favorite, const std::string& filename, const std::string& fileExtension, - const std::string& samplePack, const std::string& type, int channels, int length, - int sampleRate, int bitrate, const std::string& path, int trashed); + const std::string& samplePack, const std::string& type, int channels, int bpm, + int length, int sampleRate, int bitrate, const std::string& path, int trashed); private: // ------------------------------------------------------------------- int m_Favorite = 0; int m_Channels = 0; + int m_BPM = 0; int m_Length = 0; int m_SampleRate = 0; int m_Bitrate = 0; @@ -62,6 +63,7 @@ class Sample // Getters int GetFavorite() const { return m_Favorite; } int GetChannels() const { return m_Channels; } + int GetBPM() const { return m_BPM; } int GetLength() const { return m_Length; } int GetSampleRate() const { return m_SampleRate; } int GetBitrate() const { return m_Bitrate; } @@ -75,11 +77,12 @@ class Sample // ------------------------------------------------------------------- // Setters void Set(int favorite, const std::string& filename, const std::string& fileExtension, - const std::string& samplePack, const std::string& type, int channels, int length, - int sampleRate, int bitrate, const std::string& path, int trashed); + const std::string& samplePack, const std::string& type, int channels, int bpm, + int length, int sampleRate, int bitrate, const std::string& path, int trashed); void SetFavorite(int favorite) { m_Favorite = favorite; } void SetChannels(int channels) { m_Channels = channels; } + void SetBPM(int bpm) { m_BPM = bpm; } void SetLength(int length) { m_Length = length; } void SetSampleRate(int sampleRate) { m_SampleRate = sampleRate; } void SetBitrate(int bitrate) { m_Bitrate = bitrate; } diff --git a/src/Utility/Utils.cpp b/src/Utility/Utils.cpp index aa91885..f9a8f2e 100644 --- a/src/Utility/Utils.cpp +++ b/src/Utility/Utils.cpp @@ -18,6 +18,8 @@ * along with this program. If not, see . */ +#include + #include "Database/Database.hpp" #include "Utility/HiveData.hpp" #include "Utility/Log.hpp" @@ -32,6 +34,8 @@ #include #include +#include + namespace SampleHive { SampleHive::cUtils::FileInfo SampleHive::cUtils::GetFilenamePathAndExtension(const wxString& selected, @@ -75,6 +79,9 @@ namespace SampleHive { std::string extension; std::string filename; + float bpm = 0.0f; + float key = 0.0f; + //Check All Files At Once wxArrayString sorted_files; @@ -110,6 +117,8 @@ namespace SampleHive { filename = serializer.DeserializeShowFileExtension() ? filename_with_extension : filename_without_extension; + bpm = GetBPM(path); + Sample sample; sample.SetPath(path); @@ -122,11 +131,13 @@ namespace SampleHive { sample.SetSamplePack(artist); sample.SetChannels(tags.GetAudioInfo().channels); + sample.SetBPM(static_cast(bpm)); sample.SetLength(tags.GetAudioInfo().length); sample.SetSampleRate(tags.GetAudioInfo().sample_rate); sample.SetBitrate(tags.GetAudioInfo().bitrate); wxString length = CalculateAndGetISOStandardTime(sample.GetLength()); + wxString bpm_str = GetBPMString(sample.GetBPM()); wxVector data; @@ -140,6 +151,7 @@ namespace SampleHive { data.push_back(sample.GetSamplePack()); data.push_back(""); data.push_back(wxString::Format("%d", sample.GetChannels())); + data.push_back(bpm_str); data.push_back(length); data.push_back(wxString::Format("%d", sample.GetSampleRate())); data.push_back(wxString::Format("%d", sample.GetBitrate())); @@ -249,4 +261,72 @@ namespace SampleHive { return iso_length; } + wxString cUtils::GetBPMString(float bpm) + { + return wxString::Format("%d", static_cast(bpm)); + } + + float cUtils::GetBPM(const std::string& path) + { + uint_t buff_size = 1024, hop_size = buff_size / 2, frames = 0, samplerate = 0, read = 0; + aubio_tempo_t* tempo; + fvec_t* in, *out; + aubio_source_t* source = new_aubio_source(path.c_str(), samplerate, hop_size); + + float bpm = 0.0f; + + if (!source) + return 0.0f; + else + { + try + { + if (samplerate == 0) + samplerate = aubio_source_get_samplerate(source); + + tempo = new_aubio_tempo("default", buff_size, hop_size, samplerate); + + if (!tempo) + return 0.0f; + + in = new_fvec(hop_size); + out = new_fvec(1); + + do + { + // put some fresh data in input vector + aubio_source_do(source, in, &read); + + // execute tempo + aubio_tempo_do(tempo, in, out); + + // do something with the beats + // if (out->data[0] != 0) + // { + // SH_LOG_DEBUG("Track: {} Beat at {}s, {}s, frame {}, {} bpm with confidence {}", + // path, aubio_tempo_get_last_ms(tempo), aubio_tempo_get_last_s(tempo), + // aubio_tempo_get_last(tempo), aubio_tempo_get_bpm(tempo), + // aubio_tempo_get_confidence(tempo)); + // } + + frames += read; + bpm = aubio_tempo_get_bpm(tempo); + } + while (read == hop_size); + + // clean up memory + del_aubio_tempo(tempo); + del_fvec(in); + del_fvec(out); + del_aubio_source(source); + aubio_cleanup(); + } + catch (std::exception& e) + { + SH_LOG_ERROR("Aubio Error! {}", e.what()); + } + } + + return bpm; + } } diff --git a/src/Utility/Utils.hpp b/src/Utility/Utils.hpp index 9fcf38a..2227fde 100644 --- a/src/Utility/Utils.hpp +++ b/src/Utility/Utils.hpp @@ -60,6 +60,9 @@ namespace SampleHive { void AddSamples(wxArrayString& files, wxWindow* parent); void OnAutoImportDir(const wxString& pathToDirectory, wxWindow* parent); wxString CalculateAndGetISOStandardTime(wxLongLong length); + wxString GetBPMString(float bpm); + + float GetBPM(const std::string& path); private: // -------------------------------------------------------------------