diff --git a/src/Makefile.am b/src/Makefile.am index 809fae14..c2c6534d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -40,10 +40,8 @@ lms_SOURCES = \ $(srcdir)/database/ScanSettings.hpp \ $(srcdir)/database/Session.cpp \ $(srcdir)/database/Session.hpp \ - $(srcdir)/database/SessionPool.cpp \ - $(srcdir)/database/SessionPool.hpp \ - $(srcdir)/database/SimilaritySettings.cpp \ - $(srcdir)/database/SimilaritySettings.hpp \ + $(srcdir)/database/SessionPool.cpp \ + $(srcdir)/database/SessionPool.hpp \ $(srcdir)/database/SqlQuery.cpp \ $(srcdir)/database/SqlQuery.hpp \ $(srcdir)/database/Track.cpp \ diff --git a/src/database/ScanSettings.hpp b/src/database/ScanSettings.hpp index bd908654..209e5829 100644 --- a/src/database/ScanSettings.hpp +++ b/src/database/ScanSettings.hpp @@ -34,6 +34,7 @@ class ScanSettings : public Wt::Dbo::Dbo public: using pointer = Wt::Dbo::ptr; + // Do not modify values (just add) enum class UpdatePeriod { Never = 0, Daily, @@ -41,6 +42,13 @@ class ScanSettings : public Wt::Dbo::Dbo Monthly }; + // Do not modify values (just add) + enum class SimilarityEngineType + { + Clusters = 0, + Features, + }; + static void init(Session& session); static pointer get(Session& session); @@ -52,12 +60,14 @@ class ScanSettings : public Wt::Dbo::Dbo UpdatePeriod getUpdatePeriod() const { return _updatePeriod; } std::vector> getClusterTypes() const; std::set getAudioFileExtensions() const; + SimilarityEngineType getSimilarityEngineType() const { return _similarityEngineType; } // Setters void setMediaDirectory(std::filesystem::path p); void setUpdateStartTime(Wt::WTime t) { _startTime = t; } void setUpdatePeriod(UpdatePeriod p) { _updatePeriod = p; } void setClusterTypes(Session& session, const std::set& clusterTypeNames); + void setSimilarityEngineType(SimilarityEngineType type) { _similarityEngineType = type; } void incScanVersion(); template @@ -68,6 +78,7 @@ class ScanSettings : public Wt::Dbo::Dbo Wt::Dbo::field(a, _startTime, "start_time"); Wt::Dbo::field(a, _updatePeriod, "update_period"); Wt::Dbo::field(a, _audioFileExtensions, "audio_file_extensions"); + Wt::Dbo::field(a, _similarityEngineType,"similarity_engine_type"); Wt::Dbo::hasMany(a, _clusterTypes, Wt::Dbo::ManyToOne, "scan_settings"); } @@ -77,6 +88,7 @@ class ScanSettings : public Wt::Dbo::Dbo std::string _mediaDirectory; Wt::WTime _startTime = Wt::WTime {0,0,0}; UpdatePeriod _updatePeriod {UpdatePeriod::Never}; + SimilarityEngineType _similarityEngineType {SimilarityEngineType::Clusters}; std::string _audioFileExtensions {".mp3 .ogg .oga .aac .m4a .flac .wav .wma .aif .aiff .ape .mpc .shn .opus"}; Wt::Dbo::collection> _clusterTypes; }; diff --git a/src/database/Session.cpp b/src/database/Session.cpp index 274185bd..2b881b88 100644 --- a/src/database/Session.cpp +++ b/src/database/Session.cpp @@ -31,7 +31,6 @@ #include "Db.hpp" #include "Release.hpp" #include "ScanSettings.hpp" -#include "SimilaritySettings.hpp" #include "Track.hpp" #include "TrackArtistLink.hpp" #include "TrackList.hpp" @@ -40,7 +39,7 @@ namespace Database { -#define LMS_DATABASE_VERSION 7 +#define LMS_DATABASE_VERSION 8 using Version = std::size_t; @@ -101,21 +100,32 @@ Session::doDatabaseMigrationIfNeeded() throw LmsException {outdatedMsg}; } - switch (version) + while (version < LMS_DATABASE_VERSION) { - case 5: - LMS_LOG(DB, INFO) << "Migrating database from version 5..."; + LMS_LOG(DB, INFO) << "Migrating database from version " << version << "..."; + + if (version == 5) + { _session.execute("DELETE FROM auth_token"); // format has changed - break; - case 6: - LMS_LOG(DB, INFO) << "Migrating database from version 6..."; + } + else if (version == 6) + { // Just increment the scan version of the settings to make the next scheduled scan rescan everything ScanSettings::get(*this).modify()->incScanVersion(); - break; - - default: + } + else if (version == 7) + { + _session.execute("DROP TABLE similarity_settings"); + _session.execute("DROP TABLE similarity_settings_feature"); + _session.execute("ALTER TABLE scan_settings ADD similarity_engine_type INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(ScanSettings::SimilarityEngineType::Clusters)) + ")"); + } + else + { LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration"; throw LmsException { LMS_DATABASE_VERSION > version ? outdatedMsg : "Server binary outdated, please upgrade it to handle this database"}; + } + + ++version; } VersionInfo::get(*this).modify()->setVersion(LMS_DATABASE_VERSION); @@ -133,8 +143,6 @@ Session::Session(Db& db) _session.mapClass("cluster_type"); _session.mapClass("release"); _session.mapClass("scan_settings"); - _session.mapClass("similarity_settings"); - _session.mapClass("similarity_settings_feature"); _session.mapClass("track"); _session.mapClass("track_artist_link"); _session.mapClass("track_features"); @@ -257,7 +265,6 @@ Session::prepareTables() auto uniqueTransaction {createUniqueTransaction()}; ScanSettings::init(*this); - SimilaritySettings::init(*this); } } diff --git a/src/database/SimilaritySettings.cpp b/src/database/SimilaritySettings.cpp deleted file mode 100644 index 14617512..00000000 --- a/src/database/SimilaritySettings.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2018 Emeric Poupon - * - * This file is part of LMS. - * - * LMS 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. - * - * LMS 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 LMS. If not, see . - */ - -#include "SimilaritySettings.hpp" - -#include "utils/Logger.hpp" -#include "utils/Utils.hpp" - -#include "Session.hpp" -#include "TrackFeatures.hpp" - -namespace Database { - -static const std::map defaultFeatures = -{ - { "lowlevel.spectral_contrast_coeffs.median", 1. }, - { "lowlevel.erbbands.median", 1. }, - { "tonal.hpcp.median", 1. }, - { "lowlevel.melbands.median", 1. }, - { "lowlevel.barkbands.median", 1. }, - { "lowlevel.mfcc.mean", 1. }, - { "lowlevel.gfcc.mean", 1. }, -}; - -SimilaritySettingsFeature::SimilaritySettingsFeature(Wt::Dbo::ptr settings, const std::string& name, double weight) -: _name {name}, -_weight {weight}, -_settings {settings} -{ -} - -SimilaritySettingsFeature::pointer -SimilaritySettingsFeature::create(Session& session, Wt::Dbo::ptr settings, const std::string& name, double weight) -{ - session.checkUniqueLocked(); - - SimilaritySettingsFeature::pointer res {session.getDboSession().add(std::make_unique(settings, name, weight))}; - session.getDboSession().flush(); - - return res; -} - -void -SimilaritySettings::init(Session& session) -{ - session.checkUniqueLocked(); - - pointer settings {session.getDboSession().find()}; - if (settings) - return; - - settings = session.getDboSession().add(std::make_unique()); - for (const auto& [feature, weight] : defaultFeatures) - SimilaritySettingsFeature::create(session, settings, feature, weight); -} - - -SimilaritySettings::pointer -SimilaritySettings::get(Session& session) -{ - session.checkSharedLocked(); - - return session.getDboSession().find(); -} - -std::vector> -SimilaritySettings::getFeatures() const -{ - return std::vector>(_features.begin(), _features.end()); -} - -} // namespace Database - diff --git a/src/database/SimilaritySettings.hpp b/src/database/SimilaritySettings.hpp deleted file mode 100644 index edac0744..00000000 --- a/src/database/SimilaritySettings.hpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2018 Emeric Poupon - * - * This file is part of LMS. - * - * LMS 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. - * - * LMS 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 LMS. If not, see . - */ - -#pragma once - -#include - -namespace Database { - -class Session; -class SimilaritySettings; - -class SimilaritySettingsFeature : public Wt::Dbo::Dbo -{ - public: - using pointer = Wt::Dbo::ptr; - - SimilaritySettingsFeature() = default; - SimilaritySettingsFeature(Wt::Dbo::ptr settings, const std::string& name, double weight); - - static pointer create(Session& session, Wt::Dbo::ptr settings, const std::string& name, double weight = 1); - - const std::string& getName() const { return _name; } ; - double getWeight() const { return _weight; } - - template - void persist(Action& a) - { - Wt::Dbo::field(a, _name, "name"); - Wt::Dbo::field(a, _weight, "weight"); - - Wt::Dbo::belongsTo(a, _settings, "similarity_settings", Wt::Dbo::OnDeleteCascade); - } - - private: - std::string _name; - double _weight {1}; - - Wt::Dbo::ptr _settings; -}; - -class SimilaritySettings : public Wt::Dbo::Dbo -{ - public: - - enum class EngineType - { - Features = 0, - Clusters = 1, - }; - - using pointer = Wt::Dbo::ptr; - - // Utils - static void init(Session& session); - static pointer get(Session& session); - - // Accessors Read - std::size_t getVersion() const { return _settingsVersion; } - EngineType getEngineType() const { return _engineType; } - std::vector> getFeatures() const; - - // Setters - void setEngineType(EngineType type) { _engineType = type; } - - template - void persist(Action& a) - { - Wt::Dbo::field(a, _settingsVersion, "settings_version"); - Wt::Dbo::field(a, _engineType, "engine_type"); - - Wt::Dbo::hasMany(a, _features, Wt::Dbo::ManyToOne, "similarity_settings"); - } - - private: - - int _settingsVersion {}; - EngineType _engineType {EngineType::Clusters}; - - Wt::Dbo::collection> _features; -}; - - -} // namespace Database - diff --git a/src/similarity/SimilaritySearcher.cpp b/src/similarity/SimilaritySearcher.cpp index 34e43835..cf2513d0 100644 --- a/src/similarity/SimilaritySearcher.cpp +++ b/src/similarity/SimilaritySearcher.cpp @@ -22,7 +22,7 @@ #include "features/SimilarityFeaturesScannerAddon.hpp" #include "cluster/SimilarityClusterSearcher.hpp" -#include "database/SimilaritySettings.hpp" +#include "database/ScanSettings.hpp" #include "database/TrackList.hpp" namespace Similarity { @@ -32,10 +32,11 @@ Searcher::Searcher(FeaturesScannerAddon& somAddon) {} static -Database::SimilaritySettings::EngineType getEngineType(Database::Session& dbSession) +Database::ScanSettings::SimilarityEngineType +getEngineType(Database::Session& dbSession) { auto transaction {dbSession.createSharedTransaction()}; - return Database::SimilaritySettings::get(dbSession)->getEngineType(); + return Database::ScanSettings::get(dbSession)->getSimilarityEngineType(); } std::vector @@ -58,7 +59,7 @@ Searcher::getSimilarTracksFromTrackList(Database::Session& session, Database::Id if (trackIds.empty()) return {}; - if (engineType == Database::SimilaritySettings::EngineType::Features + if (engineType == Database::ScanSettings::SimilarityEngineType::Features && somSearcher && std::any_of(std::cbegin(trackIds), std::cend(trackIds), [&](Database::IdType trackId) { return somSearcher->isTrackClassified(trackId); } )) { @@ -74,7 +75,7 @@ Searcher::getSimilarTracks(Database::Session& dbSession, const std::setisTrackClassified(trackId); } )) { @@ -90,7 +91,7 @@ Searcher::getSimilarReleases(Database::Session& dbSession, Database::IdType rele auto engineType {getEngineType(dbSession)}; auto somSearcher {_somAddon.getSearcher()}; - if (engineType == Database::SimilaritySettings::EngineType::Features + if (engineType == Database::ScanSettings::SimilarityEngineType::Features && somSearcher && somSearcher->isReleaseClassified(releaseId)) { @@ -106,7 +107,7 @@ Searcher::getSimilarArtists(Database::Session& dbSession, Database::IdType artis auto engineType {getEngineType(dbSession)}; auto somSearcher {_somAddon.getSearcher()}; - if (engineType == Database::SimilaritySettings::EngineType::Features + if (engineType == Database::ScanSettings::SimilarityEngineType::Features && somSearcher && somSearcher->isArtistClassified(artistId)) { diff --git a/src/similarity/features/SimilarityFeaturesDefs.cpp b/src/similarity/features/SimilarityFeaturesDefs.cpp index 2d99f7ca..a0aecc4d 100644 --- a/src/similarity/features/SimilarityFeaturesDefs.cpp +++ b/src/similarity/features/SimilarityFeaturesDefs.cpp @@ -122,7 +122,60 @@ static const std::unordered_map featureDefinitions { "lowlevel.hfc.min", {1}}, { "lowlevel.hfc.var", {1}}, { "tonal.hpcp.median", {36}}, + { "lowlevel.melbands.dmean", {40}}, + { "lowlevel.melbands.dmean2", {40}}, + { "lowlevel.melbands.dvar", {40}}, + { "lowlevel.melbands.dvar2", {40}}, + { "lowlevel.melbands.max", {40}}, + { "lowlevel.melbands.mean", {40}}, { "lowlevel.melbands.median", {40}}, + { "lowlevel.melbands.min", {40}}, + { "lowlevel.melbands.var", {40}}, + { "lowlevel.melbands_crest.dmean", {1}}, + { "lowlevel.melbands_crest.dmean2", {1}}, + { "lowlevel.melbands_crest.dvar", {1}}, + { "lowlevel.melbands_crest.dvar2", {1}}, + { "lowlevel.melbands_crest.max", {1}}, + { "lowlevel.melbands_crest.mean", {1}}, + { "lowlevel.melbands_crest.median", {1}}, + { "lowlevel.melbands_crest.min", {1}}, + { "lowlevel.melbands_crest.var", {1}}, + { "lowlevel.melbands_flatness_db.dmean", {1}}, + { "lowlevel.melbands_flatness_db.dmean2", {1}}, + { "lowlevel.melbands_flatness_db.dvar", {1}}, + { "lowlevel.melbands_flatness_db.dvar2", {1}}, + { "lowlevel.melbands_flatness_db.max", {1}}, + { "lowlevel.melbands_flatness_db.mean", {1}}, + { "lowlevel.melbands_flatness_db.median", {1}}, + { "lowlevel.melbands_flatness_db.min", {1}}, + { "lowlevel.melbands_flatness_db.var", {1}}, + { "lowlevel.melbands_kurtosis.dmean", {1}}, + { "lowlevel.melbands_kurtosis.dmean2", {1}}, + { "lowlevel.melbands_kurtosis.dvar", {1}}, + { "lowlevel.melbands_kurtosis.dvar2", {1}}, + { "lowlevel.melbands_kurtosis.max", {1}}, + { "lowlevel.melbands_kurtosis.mean", {1}}, + { "lowlevel.melbands_kurtosis.median", {1}}, + { "lowlevel.melbands_kurtosis.min", {1}}, + { "lowlevel.melbands_kurtosis.var", {1}}, + { "lowlevel.melbands_skewness.dmean", {1}}, + { "lowlevel.melbands_skewness.dmean2", {1}}, + { "lowlevel.melbands_skewness.dvar", {1}}, + { "lowlevel.melbands_skewness.dvar2", {1}}, + { "lowlevel.melbands_skewness.max", {1}}, + { "lowlevel.melbands_skewness.mean", {1}}, + { "lowlevel.melbands_skewness.median", {1}}, + { "lowlevel.melbands_skewness.min", {1}}, + { "lowlevel.melbands_skewness.var", {1}}, + { "lowlevel.melbands_spread.dmean", {1}}, + { "lowlevel.melbands_spread.dmean2", {1}}, + { "lowlevel.melbands_spread.dvar", {1}}, + { "lowlevel.melbands_spread.dvar2", {1}}, + { "lowlevel.melbands_spread.max", {1}}, + { "lowlevel.melbands_spread.mean", {1}}, + { "lowlevel.melbands_spread.median", {1}}, + { "lowlevel.melbands_spread.min", {1}}, + { "lowlevel.melbands_spread.var", {1}}, { "lowlevel.mfcc.mean", {13}}, { "lowlevel.pitch_salience.dmean", {1}}, { "lowlevel.pitch_salience.dmean2", {1}}, @@ -133,6 +186,24 @@ static const std::unordered_map featureDefinitions { "lowlevel.pitch_salience.median", {1}}, { "lowlevel.pitch_salience.min", {1}}, { "lowlevel.pitch_salience.var", {1}}, + { "lowlevel.silence_rate_30dB.dmean", {1}}, + { "lowlevel.silence_rate_30dB.dmean2", {1}}, + { "lowlevel.silence_rate_30dB.dvar", {1}}, + { "lowlevel.silence_rate_30dB.dvar2", {1}}, + { "lowlevel.silence_rate_30dB.max", {1}}, + { "lowlevel.silence_rate_30dB.mean", {1}}, + { "lowlevel.silence_rate_30dB.median", {1}}, + { "lowlevel.silence_rate_30dB.min", {1}}, + { "lowlevel.silence_rate_30dB.var", {1}}, + { "lowlevel.silence_rate_60dB.dmean", {1}}, + { "lowlevel.silence_rate_60dB.dmean2", {1}}, + { "lowlevel.silence_rate_60dB.dvar", {1}}, + { "lowlevel.silence_rate_60dB.dvar2", {1}}, + { "lowlevel.silence_rate_60dB.max", {1}}, + { "lowlevel.silence_rate_60dB.mean", {1}}, + { "lowlevel.silence_rate_60dB.median", {1}}, + { "lowlevel.silence_rate_60dB.min", {1}}, + { "lowlevel.silence_rate_60dB.var", {1}}, { "lowlevel.spectral_centroid.dmean", {1}}, { "lowlevel.spectral_centroid.dmean2", {1}}, { "lowlevel.spectral_centroid.dvar", {1}}, diff --git a/src/similarity/features/SimilarityFeaturesScannerAddon.cpp b/src/similarity/features/SimilarityFeaturesScannerAddon.cpp index 2dfc5dcb..c0f3107e 100644 --- a/src/similarity/features/SimilarityFeaturesScannerAddon.cpp +++ b/src/similarity/features/SimilarityFeaturesScannerAddon.cpp @@ -20,8 +20,8 @@ #include "SimilarityFeaturesScannerAddon.hpp" #include "AcousticBrainzUtils.hpp" +#include "database/ScanSettings.hpp" #include "database/Track.hpp" -#include "database/SimilaritySettings.hpp" #include "database/TrackFeatures.hpp" #include "similarity/features/SimilarityFeaturesCache.hpp" #include "utils/Config.hpp" @@ -29,26 +29,12 @@ namespace Similarity { -static -FeatureSettingsMap -getFeatureSettings(Database::Session& session) -{ - FeatureSettingsMap res; - - auto transaction {session.createSharedTransaction()}; - - for (const auto& feature : Database::SimilaritySettings::get(session)->getFeatures()) - res[feature->getName()] = {feature->getWeight()}; - - return res; -} - static bool hasAtLeastOneTrackWithFeatures(Database::Session& session) { auto transaction {session.createSharedTransaction()}; - return !Database::Track::getAllIdsWithFeatures(session).empty(); + return !Database::Track::getAllIdsWithFeatures(session, 1).empty(); } struct TrackInfo @@ -114,7 +100,7 @@ FeaturesScannerAddon::preScanComplete() { auto transaction {_dbSession.createSharedTransaction()}; - if (Database::SimilaritySettings::get(_dbSession)->getEngineType() != Database::SimilaritySettings::EngineType::Features) + if (Database::ScanSettings::get(_dbSession)->getSimilarityEngineType() != Database::ScanSettings::SimilarityEngineType::Features) { LMS_LOG(DBUPDATER, INFO) << "Do not fetch features since the engine type does not make use of them"; return; @@ -144,17 +130,17 @@ FeaturesScannerAddon::updateSearcher() { LMS_LOG(SIMILARITY, INFO) << "Updating searcher..."; - if (hasAtLeastOneTrackWithFeatures(_dbSession)) + if (!hasAtLeastOneTrackWithFeatures(_dbSession)) { - LMS_LOG(DBUPDATER, INFO) << "No track suitable for features similarity clustering"; + LMS_LOG(DBUPDATER, INFO) << "No track found with features!"; std::atomic_store(&_searcher, std::shared_ptr{}); return; } Similarity::FeaturesSearcher::TrainSettings trainSettings; - trainSettings.featureSettingsMap = getFeatureSettings(_dbSession); + trainSettings.featureSettingsMap = FeaturesSearcher::getDefaultTrainFeatureSettings(); - auto searcher {std::make_shared(_dbSession, trainSettings, [&]() { return _stopRequested; })}; + auto searcher {std::make_shared(_dbSession, trainSettings, [&]() { return _stopRequested; })}; if (searcher->isValid()) { std::atomic_store(&_searcher, searcher); diff --git a/src/similarity/features/SimilarityFeaturesSearcher.cpp b/src/similarity/features/SimilarityFeaturesSearcher.cpp index 236c0e1a..60414b31 100644 --- a/src/similarity/features/SimilarityFeaturesSearcher.cpp +++ b/src/similarity/features/SimilarityFeaturesSearcher.cpp @@ -23,7 +23,6 @@ #include #include "database/Artist.hpp" -#include "database/SimilaritySettings.hpp" #include "database/Release.hpp" #include "database/Session.hpp" #include "database/Track.hpp" @@ -35,6 +34,21 @@ namespace Similarity { +const FeatureSettingsMap& +FeaturesSearcher::getDefaultTrainFeatureSettings() +{ + static FeatureSettingsMap defaultTrainFeatureSettings + { + { "lowlevel.spectral_energyband_high.mean", {1}}, + { "lowlevel.spectral_rolloff.median", {1}}, + { "lowlevel.spectral_contrast_valleys.var", {1}}, + { "lowlevel.erbbands.mean", {1}}, + { "lowlevel.gfcc.mean", {1}}, + }; + + return defaultTrainFeatureSettings; +} + static std::optional getTrackFeatureValues(FeaturesSearcher::FeaturesFetchFunc func, Database::IdType trackId, const std::unordered_set& featureNames) @@ -128,7 +142,7 @@ FeaturesSearcher::FeaturesSearcher(Database::Session& session, LMS_LOG(SIMILARITY, DEBUG) << "Getting Tracks with features..."; trackIds = Database::Track::getAllIdsWithFeatures(session); - LMS_LOG(SIMILARITY, DEBUG) << "Getting Tracks with features DONE"; + LMS_LOG(SIMILARITY, DEBUG) << "Getting Tracks with features DONE (found " << trackIds.size() << " tracks)"; } std::vector samples; diff --git a/src/similarity/features/SimilarityFeaturesSearcher.hpp b/src/similarity/features/SimilarityFeaturesSearcher.hpp index cdbfb2df..625877c1 100644 --- a/src/similarity/features/SimilarityFeaturesSearcher.hpp +++ b/src/similarity/features/SimilarityFeaturesSearcher.hpp @@ -57,6 +57,8 @@ class FeaturesSearcher }; FeaturesSearcher(Database::Session& session, const TrainSettings& trainSettings, StopRequestedFunction stopRequested = {}); + static const FeatureSettingsMap& getDefaultTrainFeatureSettings(); + bool isValid() const; bool isTrackClassified(Database::IdType trackId) const; diff --git a/src/ui/admin/DatabaseSettingsView.cpp b/src/ui/admin/DatabaseSettingsView.cpp index 2d2e8613..d6e44833 100644 --- a/src/ui/admin/DatabaseSettingsView.cpp +++ b/src/ui/admin/DatabaseSettingsView.cpp @@ -27,7 +27,6 @@ #include #include "database/Cluster.hpp" -#include "database/SimilaritySettings.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" #include "utils/Utils.hpp" @@ -85,7 +84,6 @@ class DatabaseSettingsModel : public Wt::WFormModel auto transaction {LmsApp->getDbSession().createSharedTransaction()}; const ScanSettings::pointer scanSettings {ScanSettings::get(LmsApp->getDbSession())}; - const SimilaritySettings::pointer similaritySettings {SimilaritySettings::get(LmsApp->getDbSession())}; setValue(MediaDirectoryField, scanSettings->getMediaDirectory().string()); @@ -97,7 +95,7 @@ class DatabaseSettingsModel : public Wt::WFormModel if (startTimeRow) setValue(UpdateStartTimeField, _updateStartTimeModel->getString(*startTimeRow)); - auto similarityEngineTypeRow {_similarityEngineTypeModel->getRowFromValue(similaritySettings->getEngineType())}; + auto similarityEngineTypeRow {_similarityEngineTypeModel->getRowFromValue(scanSettings->getSimilarityEngineType())}; if (similarityEngineTypeRow) setValue(SimilarityEngineTypeField, _similarityEngineTypeModel->getString(*similarityEngineTypeRow)); @@ -115,7 +113,6 @@ class DatabaseSettingsModel : public Wt::WFormModel auto transaction {LmsApp->getDbSession().createUniqueTransaction()}; ScanSettings::pointer scanSettings {ScanSettings::get(LmsApp->getDbSession())}; - SimilaritySettings::pointer similaritySettings {SimilaritySettings::get(LmsApp->getDbSession())}; scanSettings.modify()->setMediaDirectory(valueText(MediaDirectoryField).toUTF8()); @@ -129,7 +126,7 @@ class DatabaseSettingsModel : public Wt::WFormModel auto similarityEngineTypeRow {_similarityEngineTypeModel->getRowFromString(valueText(SimilarityEngineTypeField))}; if (similarityEngineTypeRow) - similaritySettings.modify()->setEngineType(_similarityEngineTypeModel->getValue(*similarityEngineTypeRow)); + scanSettings.modify()->setSimilarityEngineType(_similarityEngineTypeModel->getValue(*similarityEngineTypeRow)); auto clusterTypes {splitString(valueText(TagsField).toUTF8(), " ")}; scanSettings.modify()->setClusterTypes(LmsApp->getDbSession(), std::set(clusterTypes.begin(), clusterTypes.end())); @@ -158,14 +155,14 @@ class DatabaseSettingsModel : public Wt::WFormModel _updateStartTimeModel->add(time.toString(), time); } - _similarityEngineTypeModel = std::make_shared>(); - _similarityEngineTypeModel->add(Wt::WString::tr("Lms.Admin.Database.similarity-engine-type.clusters"), SimilaritySettings::EngineType::Clusters); - _similarityEngineTypeModel->add(Wt::WString::tr("Lms.Admin.Database.similarity-engine-type.features"), SimilaritySettings::EngineType::Features); + _similarityEngineTypeModel = std::make_shared>(); + _similarityEngineTypeModel->add(Wt::WString::tr("Lms.Admin.Database.similarity-engine-type.clusters"), ScanSettings::SimilarityEngineType::Clusters); + _similarityEngineTypeModel->add(Wt::WString::tr("Lms.Admin.Database.similarity-engine-type.features"), ScanSettings::SimilarityEngineType::Features); } std::shared_ptr> _updatePeriodModel; std::shared_ptr> _updateStartTimeModel; - std::shared_ptr> _similarityEngineTypeModel; + std::shared_ptr> _similarityEngineTypeModel; }; diff --git a/test/Makefile.am b/test/Makefile.am index 7235da83..eefde4b8 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -22,7 +22,6 @@ test_database_SOURCES = \ $(top_srcdir)/src/database/Release.cpp \ $(top_srcdir)/src/database/ScanSettings.cpp \ $(top_srcdir)/src/database/Session.cpp \ - $(top_srcdir)/src/database/SimilaritySettings.cpp \ $(top_srcdir)/src/database/SqlQuery.cpp \ $(top_srcdir)/src/database/Track.cpp \ $(top_srcdir)/src/database/User.cpp \ diff --git a/tools/similarity-parameters/GeneticAlgorithm.hpp b/tools/similarity-parameters/GeneticAlgorithm.hpp index 8c9ffef7..d4e0e402 100644 --- a/tools/similarity-parameters/GeneticAlgorithm.hpp +++ b/tools/similarity-parameters/GeneticAlgorithm.hpp @@ -58,7 +58,7 @@ class GeneticAlgorithm void scoreAndSortPopulation(std::vector& population); Score getTotalScore(const std::vector& population) const; - typename std::vector::const_iterator pickRandomRouletteWheel(const std::vector& population); + typename std::vector::const_iterator pickRandomRouletteWheel(const std::vector& population, Score totalScore); Params _params; }; @@ -90,23 +90,22 @@ GeneticAlgorithm::simulate(const std::vector& initialPop { assert(scoredPopulation.size() == initialPopulation.size()); std::cout << "Processing generation " << currentGeneration << "..." << std::endl; + std::cout << "Need to create " << childrenCountPerGeneration << " new children" << std::endl; // breed + const Score populationTotalScore {getTotalScore(scoredPopulation)}; std::vector children; children.reserve(childrenCountPerGeneration); while (children.size() < childrenCountPerGeneration) { // Select two random parents using their score as weight - const auto itParent1 {pickRandomRouletteWheel(scoredPopulation)}; - const auto itParent2 {pickRandomRouletteWheel(scoredPopulation)}; + const auto itParent1 {pickRandomRouletteWheel(scoredPopulation, populationTotalScore)}; + const auto itParent2 {pickRandomRouletteWheel(scoredPopulation, populationTotalScore)}; if (itParent1 == itParent2) continue; - std::cout << "Parent1 = " << std::distance(std::cbegin(scoredPopulation), itParent1) << std::endl; - std::cout << "Parent2 = " << std::distance(std::cbegin(scoredPopulation), itParent2) << std::endl; - ScoredIndividual child {_params.breedFunction(itParent1->individual, itParent2->individual)}; if (getRealRandom(float {}, float {1}) <= _params.mutationProbability) @@ -155,11 +154,9 @@ GeneticAlgorithm::getTotalScore(const std::vector& template typename std::vector::ScoredIndividual>::const_iterator -GeneticAlgorithm::pickRandomRouletteWheel(const std::vector& population) +GeneticAlgorithm::pickRandomRouletteWheel(const std::vector& population, Score totalScore) { - const Score randomScore {getRealRandom(Score {}, getTotalScore(population))}; - - std::cout << "Random = " << randomScore << ", total = " << getTotalScore(population) << std::endl; + const Score randomScore {getRealRandom(Score {}, totalScore)}; Score curScore{}; for (auto itScoredIndividual {std::cbegin(population)}; itScoredIndividual != std::cend(population); ++itScoredIndividual ) diff --git a/tools/similarity-parameters/LmsSimilarityParameters.cpp b/tools/similarity-parameters/LmsSimilarityParameters.cpp index 18f335e4..ed5b1f66 100644 --- a/tools/similarity-parameters/LmsSimilarityParameters.cpp +++ b/tools/similarity-parameters/LmsSimilarityParameters.cpp @@ -76,11 +76,34 @@ const FeatureSettingsMap featuresSettings { "lowlevel.hfc.median", {1}}, { "lowlevel.hfc.var", {1}}, { "tonal.hpcp.median", {1}}, + { "lowlevel.melbands.mean", {1}}, { "lowlevel.melbands.median", {1}}, + { "lowlevel.melbands.var", {1}}, + { "lowlevel.melbands_crest.mean", {1}}, + { "lowlevel.melbands_crest.median", {1}}, + { "lowlevel.melbands_crest.var", {1}}, + { "lowlevel.melbands_flatness_db.mean", {1}}, + { "lowlevel.melbands_flatness_db.median", {1}}, + { "lowlevel.melbands_flatness_db.var", {1}}, + { "lowlevel.melbands_kurtosis.mean", {1}}, + { "lowlevel.melbands_kurtosis.median", {1}}, + { "lowlevel.melbands_kurtosis.var", {1}}, + { "lowlevel.melbands_skewness.mean", {1}}, + { "lowlevel.melbands_skewness.median", {1}}, + { "lowlevel.melbands_skewness.var", {1}}, + { "lowlevel.melbands_spread.mean", {1}}, + { "lowlevel.melbands_spread.median", {1}}, + { "lowlevel.melbands_spread.var", {1}}, { "lowlevel.mfcc.mean", {1}}, { "lowlevel.pitch_salience.mean", {1}}, { "lowlevel.pitch_salience.median", {1}}, { "lowlevel.pitch_salience.var", {1}}, + { "lowlevel.silence_rate_30dB.mean", {1}}, + { "lowlevel.silence_rate_30dB.median", {1}}, + { "lowlevel.silence_rate_30dB.var", {1}}, + { "lowlevel.silence_rate_60dB.mean", {1}}, + { "lowlevel.silence_rate_60dB.median", {1}}, + { "lowlevel.silence_rate_60dB.var", {1}}, { "lowlevel.spectral_centroid.mean", {1}}, { "lowlevel.spectral_centroid.median", {1}}, { "lowlevel.spectral_centroid.var", {1}}, @@ -261,10 +284,10 @@ computeTrackScore(Database::Session& session, Database::IdType track1Id, Databas static SimilarityScore -computeSimilarityScore(Database::Session& session, FeaturesSearcher::TrainSettings trainSettings, const FeatureSettingsMap& featuresSettings) +computeSimilarityScore(Database::Session& session, FeaturesSearcher::TrainSettings trainSettings) { std::cout << "Compute score of: "; - printFeatureSettingsMap(featuresSettings); + printFeatureSettingsMap(trainSettings.featureSettingsMap); std::cout << std::endl; FeaturesSearcher searcher {session, trainSettings}; @@ -272,7 +295,7 @@ computeSimilarityScore(Database::Session& session, FeaturesSearcher::TrainSettin const std::vector trackIds = std::invoke([&]() { auto transaction {session.createSharedTransaction()}; - return Database::Track::getAllIds(session); + return Database::Track::getAllIdsWithFeatures(session); }); SimilarityScore score {}; @@ -297,6 +320,32 @@ computeSimilarityScore(Database::Session& session, FeaturesSearcher::TrainSettin return score; } +static +void +printBadlyClassifiedTracks(Database::Session& session, FeaturesSearcher::TrainSettings trainSettings) +{ + + FeaturesSearcher searcher {session, trainSettings}; + + const std::vector trackIds = std::invoke([&]() + { + auto transaction {session.createSharedTransaction()}; + return Database::Track::getAllIdsWithFeatures(session); + }); + + for (Database::IdType trackId : trackIds) + { + constexpr std::size_t nbSimilarTracks {3}; + for (Database::IdType similarTrackId : searcher.getSimilarTracks({trackId}, nbSimilarTracks)) + { + SimilarityScore trackScore {computeTrackScore(session, trackId, similarTrackId)}; + if (trackScore == 0) + std::cout << "Badly classified tracks: '" << trackToString(session, trackId) << "'\n\twith track '" << trackToString(session, similarTrackId) << "'" < initialPopulation; - constexpr std::size_t populationSize {10}; + constexpr std::size_t populationSize {200}; constexpr std::size_t nbFeatures {5}; for (std::size_t i {}; i < populationSize; ++i) @@ -383,29 +432,31 @@ int main(int argc, char *argv[]) initialPopulation.emplace_back(std::move(settings)); } + FeaturesSearcher::TrainSettings trainSettings; + trainSettings.iterationCount = 8; + trainSettings.sampleCountPerNeuron = 1.5; GeneticAlgorithm::Params params; params.nbWorkers = nbWorkers; - params.nbGenerations = 5; + params.nbGenerations = 1; params.crossoverRatio = 0.78; params.mutationProbability = 0.2; params.breedFunction = breedFeatureSettingsMap; params.mutateFunction = mutateFeatureSettingsMap; params.scoreFunction = - [&](const FeatureSettingsMap& settings) + [&](const FeatureSettingsMap& featureSettings) { - FeaturesSearcher::TrainSettings trainSettings; - trainSettings.iterationCount = 8; - trainSettings.sampleCountPerNeuron = 1.5; - trainSettings.featureSettingsMap = settings; + FeaturesSearcher::TrainSettings settings {trainSettings}; + settings.featureSettingsMap = featureSettings; Database::SessionPool::ScopedSession scopedSession {sessionPool}; - return computeSimilarityScore(scopedSession.get(), trainSettings, settings); + return computeSimilarityScore(scopedSession.get(), settings); }; GeneticAlgorithm geneticAlgorithm {params}; std::cout << "Parameters:\n" + << "\tnb total settings = "<< featuresSettings.size() << "\n" << "\tnb generations = " << params.nbGenerations << "\n" << "\tpopulationSize = " << populationSize << "\n" << "\tnbFeatures = " << nbFeatures << "\n" @@ -417,6 +468,15 @@ int main(int argc, char *argv[]) const FeatureSettingsMap selectedSettings {geneticAlgorithm.simulate(initialPopulation)}; std::cout << "Simulation complete! Best result:" << std::endl; printFeatureSettingsMap(selectedSettings); + + // print all badly classified tracks + { + FeaturesSearcher::TrainSettings settings {trainSettings}; + settings.featureSettingsMap = selectedSettings; + + Database::SessionPool::ScopedSession scopedSession {sessionPool}; + printBadlyClassifiedTracks(scopedSession.get(), settings); + } } catch (std::exception& e) { diff --git a/tools/similarity-parameters/Makefile.am b/tools/similarity-parameters/Makefile.am index 68cce62c..1d47f224 100644 --- a/tools/similarity-parameters/Makefile.am +++ b/tools/similarity-parameters/Makefile.am @@ -11,7 +11,6 @@ lms_similarity_parameters_SOURCES = \ $(top_srcdir)/src/database/ScanSettings.cpp \ $(top_srcdir)/src/database/Session.cpp \ $(top_srcdir)/src/database/SessionPool.cpp \ - $(top_srcdir)/src/database/SimilaritySettings.cpp \ $(top_srcdir)/src/database/SqlQuery.cpp \ $(top_srcdir)/src/database/Track.cpp \ $(top_srcdir)/src/database/User.cpp \ diff --git a/tools/similarity/LmsSimilarity.cpp b/tools/similarity/LmsSimilarity.cpp index 7f82a692..522b6558 100644 --- a/tools/similarity/LmsSimilarity.cpp +++ b/tools/similarity/LmsSimilarity.cpp @@ -42,54 +42,6 @@ int main(int argc, char *argv[]) // log to stdout ServiceProvider::create(std::cout); - const FeatureSettingsMap featuresSettings - { -/* { "lowlevel.average_loudness", 1 }, - { "lowlevel.dynamic_complexity", 1 }, - { "lowlevel.spectral_contrast_coeffs.median", {1} }, - { "lowlevel.erbbands.median", {1} }, - { "tonal.hpcp.median", {1} }, - { "lowlevel.melbands.median", {1} }, - { "lowlevel.barkbands.median", {1} }, - { "lowlevel.mfcc.mean", {1} }, - { "lowlevel.gfcc.mean", {1} }, -*/ - { "lowlevel.spectral_kurtosis.median", {1}}, - { "lowlevel.spectral_kurtosis.mean", {1}}, - { "lowlevel.spectral_complexity.var", {1}}, - { "lowlevel.barkbands.median", {1}}, - { "lowlevel.barkbands_kurtosis.mean", {1}}, - /* - {"lowlevel.spectral_centroid.dvar2", {1} }, - {"lowlevel.barkbands.median", {1} }, - { "lowlevel.barkbands.dvar", {1} }, - { "lowlevel.spectral_complexity.min", {1} }, - { "lowlevel.pitch_salience.dmean2", {1} }, - { "lowlevel.spectral_contrast_valleys.dmean", {1} }, - { "lowlevel.pitch_salience.max", {1} }, - { "lowlevel.barkbands.mean", {1} }, - { "lowlevel.spectral_complexity.mean", {1} }, - { "lowlevel.dissonance.dvar", {1} }, - */ -/* - { "lowlevel.spectral_energy.dvar", {1} }, - { "lowlevel.barkbands.min", {1} }, - { "lowlevel.spectral_centroid.median", {1} }, - {"lowlevel.barkbands_kurtosis.median", {1} }, - {"lowlevel.spectral_energy.median", {1} }, - {"lowlevel.barkbands.max", {1} }, - {"lowlevel.barkbands_spread.var", {1} }, - {"lowlevel.spectral_decrease.var", {1} }, - {"lowlevel.spectral_contrast_valleys.dmean", {1} }, - {"lowlevel.barkbands_crest.mean", {1} }, - {"lowlevel.spectral_entropy.var", {1} }, - {"lowlevel.barkbands_crest.max", {1} }, - {"lowlevel.hfc.dvar", {1} }, - {"lowlevel.barkbands_skewness.dvar2", {1} }, - {"lowlevel.spectral_centroid.max", {1} }, - */ - }; - std::filesystem::path configFilePath {"/etc/lms.conf"}; if (argc >= 2) configFilePath = std::string(argv[1], 0, 256); @@ -102,17 +54,17 @@ int main(int argc, char *argv[]) std::cout << "Classifying tracks..." << std::endl; // may be long... struct FeaturesSearcher::TrainSettings trainSettings; - trainSettings.featureSettingsMap = featuresSettings; + trainSettings.featureSettingsMap = FeaturesSearcher::getDefaultTrainFeatureSettings(); FeaturesSearcher searcher {session, trainSettings}; std::cout << "Classifying tracks DONE" << std::endl; const std::vector trackIds = std::invoke([&]() { auto transaction {session.createSharedTransaction()}; - return Database::Track::getAllIds(session); + return Database::Track::getAllIdsWithFeatures(session); }); - std::cout << "*** Tracks ***" << std::endl; + std::cout << "*** Tracks (" << trackIds.size() << ") ***" << std::endl; for (Database::IdType trackId : trackIds) { auto trackToString = [&](Database::IdType trackId) diff --git a/tools/similarity/Makefile.am b/tools/similarity/Makefile.am index 02117e5f..11657a5e 100644 --- a/tools/similarity/Makefile.am +++ b/tools/similarity/Makefile.am @@ -10,7 +10,6 @@ lms_similarity_SOURCES = \ $(top_srcdir)/src/database/Release.cpp \ $(top_srcdir)/src/database/ScanSettings.cpp \ $(top_srcdir)/src/database/Session.cpp \ - $(top_srcdir)/src/database/SimilaritySettings.cpp \ $(top_srcdir)/src/database/SqlQuery.cpp \ $(top_srcdir)/src/database/Track.cpp \ $(top_srcdir)/src/database/User.cpp \