diff --git a/src/Makefile.am b/src/Makefile.am
index 2e541383..568fb488 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,9 +7,7 @@ lms_SOURCES = \
$(srcdir)/config/Config.cpp \
$(srcdir)/cover/CoverArtGrabber.cpp \
$(srcdir)/database/Artist.cpp \
- $(srcdir)/database/DatabaseFeatureExtractor.cpp \
$(srcdir)/database/DatabaseHandler.cpp \
- $(srcdir)/database/DatabaseUpdater.cpp \
$(srcdir)/database/MediaDirectory.cpp \
$(srcdir)/database/Playlist.cpp \
$(srcdir)/database/Release.cpp \
@@ -19,7 +17,9 @@ lms_SOURCES = \
$(srcdir)/database/Track.cpp \
$(srcdir)/database/User.cpp \
$(srcdir)/database/Video.cpp \
- $(srcdir)/database/cluster/DatabaseHighLevelCluster.cpp \
+ $(srcdir)/database/updater/DatabaseUpdater.cpp \
+ $(srcdir)/database/updater/DatabaseFeatureExtractor.cpp \
+ $(srcdir)/database/updater/DatabaseHighLevelCluster.cpp \
$(srcdir)/feature/FeatureExtractor.cpp \
$(srcdir)/feature/FeatureStore.cpp \
$(srcdir)/image/Image.cpp \
diff --git a/src/database/DatabaseClassifier.cpp b/src/database/DatabaseClassifier.cpp
deleted file mode 100644
index 5aff1b00..00000000
--- a/src/database/DatabaseClassifier.cpp
+++ /dev/null
@@ -1,878 +0,0 @@
-/*
- * Copyright (C) 2016 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 .
- */
-
-//#define BOOST_SPIRIT_THREADSAFE
-#include
-
-#include "logger/Logger.hpp"
-#include "feature/FeatureExtractor.hpp"
-
-#include "knnl/neural_net_headers.hpp"
-
-#include "DatabaseClassifier.hpp"
-
-namespace Database {
-
-Classifier::Classifier(Wt::Dbo::SqlConnectionPool& connectionPool)
-: _db(connectionPool)
-{}
-
-void
-Classifier::processTrackUpdate(bool added, Track::id_type trackId, std::string mbid, boost::filesystem::path path)
-{
- if (!added)
- return;
-
- if (mbid.empty())
- {
- // TODO compute from file
- LMS_LOG(DBUPDATER, INFO) << "File '" << path << "' has no MBID: skipping feature extraction";
- return;
- }
-
- boost::property_tree::ptree pt;
- if (::Feature::Extractor::getLowLevel(pt, mbid))
- {
- std::ostringstream oss;
- boost::property_tree::write_json(oss, pt);
-
- {
- Wt::Dbo::Transaction transaction(_db.getSession());
-
- Track::pointer track = Database::Track::getById(_db.getSession(), trackId);
- Feature::create( _db.getSession(), track, "low_level", oss.str());
- }
- }
-
- pt.clear();
- if (::Feature::Extractor::getHighLevel(pt, mbid))
- {
- std::ostringstream oss;
- boost::property_tree::write_json(oss, pt);
-
- {
- Wt::Dbo::Transaction transaction(_db.getSession());
-
- Track::pointer track = Database::Track::getById(_db.getSession(), trackId);
- Feature::create( _db.getSession(), track, "high_level", oss.str());
- }
- }
-}
-
-typedef std::vector entry_t;
-typedef std::vector Entries;
-
-static bool entryAddData(entry_t& entry, const boost::property_tree::ptree& pt, std::size_t nbDimensions)
-{
- if (pt.empty() && nbDimensions == 1)
- {
- double value = std::stod(pt.data());
- entry.push_back(value);
-
- return true;
- }
-
- if (pt.size() == nbDimensions)
- {
- for (auto it : pt)
- {
- double value = std::stod(it.second.data());
- entry.push_back(value);
- }
-
- return true;
- }
-
- LMS_LOG(DBUPDATER, DEBUG) << "Bad entry";
-
- return false;
-}
-
-struct FeatureDesc
-{
- std::string lowLevelName;
- std::size_t nbDimensions;
- double coeff;
-};
-
-static std::vector features =
-{
-// { "lowlevel.average_loudness", 1, 1.0 },
-// { "lowlevel.barkbands.dmean", 27, 1.0 },
-// { "lowlevel.barkbands.dmean2", 27, 1.0 },
-// { "lowlevel.barkbands.dvar", 27, 1.0 },
-// { "lowlevel.barkbands.dvar2", 27, 1.0 },
-// { "lowlevel.barkbands.max", 27, 1.0 },
-// { "lowlevel.barkbands.mean", 27, 1.0 },
-// { "lowlevel.barkbands.median", 27, 1.0 },
-// { "lowlevel.barkbands.min", 27, 1.0 },
-// { "lowlevel.barkbands.var", 27, 1.0 },
-// { "lowlevel.barkbands_crest.dmean", 1, 1.0 },
-// { "lowlevel.barkbands_crest.dmean2", 1, 1.0 },
-// { "lowlevel.barkbands_crest.dvar", 1, 1.0 },
-// { "lowlevel.barkbands_crest.dvar2", 1, 1.0 },
-// { "lowlevel.barkbands_crest.max", 1, 1.0 },
-// { "lowlevel.barkbands_crest.mean", 1, 1.0 },
-// { "lowlevel.barkbands_crest.median", 1, 1.0 },
-// { "lowlevel.barkbands_crest.min", 1, 1.0 },
-// { "lowlevel.barkbands_crest.var", 1, 1.0 },
-// { "lowlevel.barkbands_flatness_db.dmean", 1, 1.0 },
-// { "lowlevel.barkbands_flatness_db.dmean2", 1, 1.0 },
-// { "lowlevel.barkbands_flatness_db.dvar", 1, 1.0 },
-// { "lowlevel.barkbands_flatness_db.dvar2", 1, 1.0 },
-// { "lowlevel.barkbands_flatness_db.max", 1, 1.0 },
-// { "lowlevel.barkbands_flatness_db.mean", 1, 1.0 },
-// { "lowlevel.barkbands_flatness_db.median", 1, 1.0 },
-// { "lowlevel.barkbands_flatness_db.min", 1, 1.0 },
-// { "lowlevel.barkbands_flatness_db.var", 1, 1.0 },
-// { "lowlevel.barkbands_kurtosis.dmean", 1, 1.0 },
-// { "lowlevel.barkbands_kurtosis.dmean2", 1, 1.0 },
-// { "lowlevel.barkbands_kurtosis.dvar", 1, 1.0 },
-// { "lowlevel.barkbands_kurtosis.dvar2", 1, 1.0 },
-// { "lowlevel.barkbands_kurtosis.max", 1, 1.0 },
-// { "lowlevel.barkbands_kurtosis.mean", 1, 1.0 },
-// { "lowlevel.barkbands_kurtosis.median", 1, 1.0 },
-// { "lowlevel.barkbands_kurtosis.min", 1, 1.0 },
-// { "lowlevel.barkbands_kurtosis.var", 1, 1.0 },
-// { "lowlevel.barkbands_skewness.dmean", 1, 1.0 },
-// { "lowlevel.barkbands_skewness.dmean2", 1, 1.0 },
-// { "lowlevel.barkbands_skewness.dvar", 1, 1.0 },
-// { "lowlevel.barkbands_skewness.dvar2", 1, 1.0 },
-// { "lowlevel.barkbands_skewness.max", 1, 1.0 },
-// { "lowlevel.barkbands_skewness.mean", 1, 1.0 },
-// { "lowlevel.barkbands_skewness.median", 1, 1.0 },
-// { "lowlevel.barkbands_skewness.min", 1, 1.0 },
-// { "lowlevel.barkbands_skewness.var", 1, 1.0 },
-// { "lowlevel.barkbands_spread.dmean", 1, 1.0 },
-// { "lowlevel.barkbands_spread.dmean2", 1, 1.0 },
-// { "lowlevel.barkbands_spread.dvar", 1, 1.0 },
-// { "lowlevel.barkbands_spread.dvar2", 1, 1.0 },
-// { "lowlevel.barkbands_spread.max", 1, 1.0 },
-// { "lowlevel.barkbands_spread.mean", 1, 1.0 },
-// { "lowlevel.barkbands_spread.median", 1, 1.0 },
-// { "lowlevel.barkbands_spread.min", 1, 1.0 },
-// { "lowlevel.barkbands_spread.var", 1, 1.0 },
-// { "lowlevel.dissonance.dmean", 1, 1.0 },
-// { "lowlevel.dissonance.dmean2", 1, 1.0 },
-// { "lowlevel.dissonance.dvar", 1, 1.0 },
-// { "lowlevel.dissonance.dvar2", 1, 1.0 },
-// { "lowlevel.dissonance.max", 1, 1.0 },
-// { "lowlevel.dissonance.mean", 1, 1.0 },
-// { "lowlevel.dissonance.median", 1, 1.0 },
-// { "lowlevel.dissonance.min", 1, 1.0 },
-// { "lowlevel.dissonance.var", 1, 1.0 },
-// { "lowlevel.dynamic_complexity", 1, 1.0 },
-// { "lowlevel.erbbands.dmean", 40, 1.0 },
-// { "lowlevel.erbbands.dmean2", 40, 1.0 },
-// { "lowlevel.erbbands.dvar", 40, 1.0 },
-// { "lowlevel.erbbands.dvar2", 40, 1.0 },
-// { "lowlevel.erbbands.max", 40, 1.0 },
-// { "lowlevel.erbbands.mean", 40, 1.0 },
-// { "lowlevel.erbbands.median", 40, 1.0 },
-// { "lowlevel.erbbands.min", 40, 1.0 },
-// { "lowlevel.erbbands.var", 40, 1.0 },
-// { "lowlevel.erbbands_crest.dmean", 1, 1.0 },
-// { "lowlevel.erbbands_crest.dmean2", 1, 1.0 },
-// { "lowlevel.erbbands_crest.dvar", 1, 1.0 },
-// { "lowlevel.erbbands_crest.dvar2", 1, 1.0 },
-// { "lowlevel.erbbands_crest.max", 1, 1.0 },
-// { "lowlevel.erbbands_crest.mean", 1, 1.0 },
-// { "lowlevel.erbbands_crest.median", 1, 1.0 },
-// { "lowlevel.erbbands_crest.min", 1, 1.0 },
-// { "lowlevel.erbbands_crest.var", 1, 1.0 },
-// { "lowlevel.erbbands_flatness_db.dmean", 1, 1.0 },
-// { "lowlevel.erbbands_flatness_db.dmean2", 1, 1.0 },
-// { "lowlevel.erbbands_flatness_db.dvar", 1, 1.0 },
-// { "lowlevel.erbbands_flatness_db.dvar2", 1, 1.0 },
-// { "lowlevel.erbbands_flatness_db.max", 1, 1.0 },
-// { "lowlevel.erbbands_flatness_db.mean", 1, 1.0 },
-// { "lowlevel.erbbands_flatness_db.median", 1, 1.0 },
-// { "lowlevel.erbbands_flatness_db.min", 1, 1.0 },
-// { "lowlevel.erbbands_flatness_db.var", 1, 1.0 },
-// { "lowlevel.erbbands_kurtosis.dmean", 1, 1.0 },
-// { "lowlevel.erbbands_kurtosis.dmean2", 1, 1.0 },
-// { "lowlevel.erbbands_kurtosis.dvar", 1, 1.0 },
-// { "lowlevel.erbbands_kurtosis.dvar2", 1, 1.0 },
-// { "lowlevel.erbbands_kurtosis.max", 1, 1.0 },
-// { "lowlevel.erbbands_kurtosis.mean", 1, 1.0 },
-// { "lowlevel.erbbands_kurtosis.median", 1, 1.0 },
-// { "lowlevel.erbbands_kurtosis.min", 1, 1.0 },
-// { "lowlevel.erbbands_kurtosis.var", 1, 1.0 },
-// { "lowlevel.erbbands_skewness.dmean", 1, 1.0 },
-// { "lowlevel.erbbands_skewness.dmean2", 1, 1.0 },
-// { "lowlevel.erbbands_skewness.dvar", 1, 1.0 },
-// { "lowlevel.erbbands_skewness.dvar2", 1, 1.0 },
-// { "lowlevel.erbbands_skewness.max", 1, 1.0 },
-// { "lowlevel.erbbands_skewness.mean", 1, 1.0 },
-// { "lowlevel.erbbands_skewness.median", 1, 1.0 },
-// { "lowlevel.erbbands_skewness.min", 1, 1.0 },
-// { "lowlevel.erbbands_skewness.var", 1, 1.0 },
-// { "lowlevel.erbbands_spread.dmean", 1, 1.0 },
-// { "lowlevel.erbbands_spread.dmean2", 1, 1.0 },
-// { "lowlevel.erbbands_spread.dvar", 1, 1.0 },
-// { "lowlevel.erbbands_spread.dvar2", 1, 1.0 },
-// { "lowlevel.erbbands_spread.max", 1, 1.0 },
-// { "lowlevel.erbbands_spread.mean", 1, 1.0 },
-// { "lowlevel.erbbands_spread.median", 1, 1.0 },
-// { "lowlevel.erbbands_spread.min", 1, 1.0 },
-// { "lowlevel.erbbands_spread.var", 1, 1.0 },
-// { "lowlevel.gfcc.mean", 13, 1.0 },
-// { "lowlevel.hfc.dmean", 1, 1.0 },
-// { "lowlevel.hfc.dmean2", 1, 1.0 },
-// { "lowlevel.hfc.dvar", 1, 1.0 },
-// { "lowlevel.hfc.dvar2", 1, 1.0 },
-// { "lowlevel.hfc.max", 1, 1.0 },
-// { "lowlevel.hfc.mean", 1, 1.0 },
-// { "lowlevel.hfc.median", 1, 1.0 },
-// { "lowlevel.hfc.min", 1, 1.0 },
-// { "lowlevel.hfc.var", 1, 1.0 },
-// { "lowlevel.melbands.dmean", 40, 1.0 },
-// { "lowlevel.melbands.dmean2", 40, 1.0 },
-// { "lowlevel.melbands.dvar", 40, 1.0 },
-// { "lowlevel.melbands.dvar2", 40, 1.0 },
-// { "lowlevel.melbands.max", 40, 1.0 },
-// { "lowlevel.melbands.mean", 40, 1.0 },
-// { "lowlevel.melbands.median", 40, 1.0 },
-// { "lowlevel.melbands.min", 40, 1.0 },
-// { "lowlevel.melbands.var", 40, 1.0 },
-// { "lowlevel.melbands_crest.dmean", 1, 1.0 },
-// { "lowlevel.melbands_crest.dmean2", 1, 1.0 },
-// { "lowlevel.melbands_crest.dvar", 1, 1.0 },
-// { "lowlevel.melbands_crest.dvar2", 1, 1.0 },
-// { "lowlevel.melbands_crest.max", 1, 1.0 },
-// { "lowlevel.melbands_crest.mean", 1, 1.0 },
-// { "lowlevel.melbands_crest.median", 1, 1.0 },
-// { "lowlevel.melbands_crest.min", 1, 1.0 },
-// { "lowlevel.melbands_crest.var", 1, 1.0 },
-// { "lowlevel.melbands_flatness_db.dmean", 1, 1.0 },
-// { "lowlevel.melbands_flatness_db.dmean2", 1, 1.0 },
-// { "lowlevel.melbands_flatness_db.dvar", 1, 1.0 },
-// { "lowlevel.melbands_flatness_db.dvar2", 1, 1.0 },
-// { "lowlevel.melbands_flatness_db.max", 1, 1.0 },
-// { "lowlevel.melbands_flatness_db.mean", 1, 1.0 },
-// { "lowlevel.melbands_flatness_db.median", 1, 1.0 },
-// { "lowlevel.melbands_flatness_db.min", 1, 1.0 },
-// { "lowlevel.melbands_flatness_db.var", 1, 1.0 },
-// { "lowlevel.melbands_kurtosis.dmean", 1, 1.0 },
-// { "lowlevel.melbands_kurtosis.dmean2", 1, 1.0 },
-// { "lowlevel.melbands_kurtosis.dvar", 1, 1.0 },
-// { "lowlevel.melbands_kurtosis.dvar2", 1, 1.0 },
-// { "lowlevel.melbands_kurtosis.max", 1, 1.0 },
-// { "lowlevel.melbands_kurtosis.mean", 1, 1.0 },
-// { "lowlevel.melbands_kurtosis.median", 1, 1.0 },
-// { "lowlevel.melbands_kurtosis.min", 1, 1.0 },
-// { "lowlevel.melbands_kurtosis.var", 1, 1.0 },
-// { "lowlevel.melbands_spread.dmean", 1, 1.0 },
-// { "lowlevel.melbands_spread.dmean2", 1, 1.0 },
-// { "lowlevel.melbands_spread.dvar", 1, 1.0 },
-// { "lowlevel.melbands_spread.dvar2", 1, 1.0 },
-// { "lowlevel.melbands_spread.max", 1, 1.0 },
-// { "lowlevel.melbands_spread.mean", 1, 1.0 },
-// { "lowlevel.melbands_spread.median", 1, 1.0 },
-// { "lowlevel.melbands_spread.min", 1, 1.0 },
-// { "lowlevel.melbands_spread.var", 1, 1.0 },
-// { "lowlevel.mfcc.mean", 13, 1.0 },
-// { "lowlevel.pitch_salience.dmean", 1, 1.0 },
-// { "lowlevel.pitch_salience.dmean2", 1, 1.0 },
-// { "lowlevel.pitch_salience.dvar", 1, 1.0 },
-// { "lowlevel.pitch_salience.dvar2", 1, 1.0 },
-// { "lowlevel.pitch_salience.max", 1, 1.0 },
-// { "lowlevel.pitch_salience.mean", 1, 1.0 },
-// { "lowlevel.pitch_salience.median", 1, 1.0 },
-// { "lowlevel.pitch_salience.min", 1, 1.0 },
-// { "lowlevel.pitch_salience.var", 1, 1.0 },
-// { "lowlevel.silence_rate_20dB.dmean", 1, 1.0 },
-// { "lowlevel.silence_rate_20dB.dmean2", 1, 1.0 },
-// { "lowlevel.silence_rate_20dB.dvar", 1, 1.0 },
-// { "lowlevel.silence_rate_20dB.dvar2", 1, 1.0 },
-// { "lowlevel.silence_rate_20dB.max", 1, 1.0 },
-// { "lowlevel.silence_rate_20dB.mean", 1, 1.0 },
-// { "lowlevel.silence_rate_20dB.median", 1, 1.0 },
-// { "lowlevel.silence_rate_20dB.min", 1, 1.0 },
-// { "lowlevel.silence_rate_20dB.var", 1, 1.0 },
-// { "lowlevel.silence_rate_30dB.dmean", 1, 1.0 },
-// { "lowlevel.silence_rate_30dB.dmean2", 1, 1.0 },
-// { "lowlevel.silence_rate_30dB.dvar", 1, 1.0 },
-// { "lowlevel.silence_rate_30dB.dvar2", 1, 1.0 },
-// { "lowlevel.silence_rate_30dB.max", 1, 1.0 },
-// { "lowlevel.silence_rate_30dB.mean", 1, 1.0 },
-// { "lowlevel.silence_rate_30dB.median", 1, 1.0 },
-// { "lowlevel.silence_rate_30dB.min", 1, 1.0 },
-// { "lowlevel.silence_rate_30dB.var", 1, 1.0 },
-// { "lowlevel.silence_rate_60dB.dmean", 1, 1.0 },
-// { "lowlevel.silence_rate_60dB.dmean2", 1, 1.0 },
-// { "lowlevel.silence_rate_60dB.dvar", 1, 1.0 },
-// { "lowlevel.silence_rate_60dB.dvar2", 1, 1.0 },
-// { "lowlevel.silence_rate_60dB.max", 1, 1.0 },
-// { "lowlevel.silence_rate_60dB.mean", 1, 1.0 },
-// { "lowlevel.silence_rate_60dB.median", 1, 1.0 },
-// { "lowlevel.silence_rate_60dB.min", 1, 1.0 },
-// { "lowlevel.silence_rate_60dB.var", 1, 1.0 },
-// { "lowlevel.spectral_centroid.dmean", 1, 1.0 },
-// { "lowlevel.spectral_centroid.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_centroid.dvar", 1, 1.0 },
-// { "lowlevel.spectral_centroid.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_centroid.max", 1, 1.0 },
- { "lowlevel.spectral_centroid.mean", 1, 1.0 },
-// { "lowlevel.spectral_centroid.median", 1, 1.0 },
-// { "lowlevel.spectral_centroid.min", 1, 1.0 },
- { "lowlevel.spectral_centroid.var", 1, 1.0 },
-// { "lowlevel.spectral_complexity.dmean", 1, 1.0 },
-// { "lowlevel.spectral_complexity.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_complexity.dvar", 1, 1.0 },
-// { "lowlevel.spectral_complexity.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_complexity.max", 1, 1.0 },
- { "lowlevel.spectral_complexity.mean", 1, 1.0 },
-// { "lowlevel.spectral_complexity.median", 1, 1.0 },
-// { "lowlevel.spectral_complexity.min", 1, 1.0 },
- { "lowlevel.spectral_complexity.var", 1, 1.0 },
-// { "lowlevel.spectral_decrease.dmean", 1, 1.0 },
-// { "lowlevel.spectral_decrease.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_decrease.dvar", 1, 1.0 },
-// { "lowlevel.spectral_decrease.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_decrease.max", 1, 1.0 },
- { "lowlevel.spectral_decrease.mean", 1, 1.0 },
-// { "lowlevel.spectral_decrease.median", 1, 1.0 },
-// { "lowlevel.spectral_decrease.min", 1, 1.0 },
- { "lowlevel.spectral_decrease.var", 1, 1.0 },
-// { "lowlevel.spectral_energy.dmean", 1, 1.0 },
-// { "lowlevel.spectral_energy.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_energy.dvar", 1, 1.0 },
-// { "lowlevel.spectral_energy.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_energy.max", 1, 1.0 },
- { "lowlevel.spectral_energy.mean", 1, 1.0 },
-// { "lowlevel.spectral_energy.median", 1, 1.0 },
-// { "lowlevel.spectral_energy.min", 1, 1.0 },
- { "lowlevel.spectral_energy.var", 1, 1.0 },
-// { "lowlevel.spectral_energyband_low.dmean", 1, 1.0 },
-// { "lowlevel.spectral_energyband_low.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_energyband_low.dvar", 1, 1.0 },
-// { "lowlevel.spectral_energyband_low.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_energyband_low.max", 1, 1.0 },
- { "lowlevel.spectral_energyband_low.mean", 1, 1.0 },
-// { "lowlevel.spectral_energyband_low.median", 1, 1.0 },
-// { "lowlevel.spectral_energyband_low.min", 1, 1.0 },
- { "lowlevel.spectral_energyband_low.var", 1, 1.0 },
-// { "lowlevel.spectral_energyband_middle_high.dmean", 1, 1.0 },
-// { "lowlevel.spectral_energyband_middle_high.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_energyband_middle_high.dvar", 1, 1.0 },
-// { "lowlevel.spectral_energyband_middle_high.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_energyband_middle_high.max", 1, 1.0 },
- { "lowlevel.spectral_energyband_middle_high.mean", 1, 1.0 },
-// { "lowlevel.spectral_energyband_middle_high.median", 1, 1.0 },
-// { "lowlevel.spectral_energyband_middle_high.min", 1, 1.0 },
- { "lowlevel.spectral_energyband_middle_high.var", 1, 1.0 },
-// { "lowlevel.spectral_energyband_middle_low.dmean", 1, 1.0 },
-// { "lowlevel.spectral_energyband_middle_low.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_energyband_middle_low.dvar", 1, 1.0 },
-// { "lowlevel.spectral_energyband_middle_low.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_energyband_middle_low.max", 1, 1.0 },
- { "lowlevel.spectral_energyband_middle_low.mean", 1, 1.0 },
-// { "lowlevel.spectral_energyband_middle_low.median", 1, 1.0 },
-// { "lowlevel.spectral_energyband_middle_low.min", 1, 1.0 },
- { "lowlevel.spectral_energyband_middle_low.var", 1, 1.0 },
-// { "lowlevel.spectral_entropy.dmean", 1, 1.0 },
-// { "lowlevel.spectral_entropy.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_entropy.dvar", 1, 1.0 },
-// { "lowlevel.spectral_entropy.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_entropy.max", 1, 1.0 },
- { "lowlevel.spectral_entropy.mean", 1, 1.0 },
-// { "lowlevel.spectral_entropy.median", 1, 1.0 },
-// { "lowlevel.spectral_entropy.min", 1, 1.0 },
- { "lowlevel.spectral_entropy.var", 1, 1.0 },
-// { "lowlevel.spectral_flux.dmean", 1, 1.0 },
-// { "lowlevel.spectral_flux.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_flux.dvar", 1, 1.0 },
-// { "lowlevel.spectral_flux.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_flux.max", 1, 1.0 },
-// { "lowlevel.spectral_flux.mean", 1, 1.0 },
-// { "lowlevel.spectral_flux.median", 1, 1.0 },
-// { "lowlevel.spectral_flux.min", 1, 1.0 },
-// { "lowlevel.spectral_flux.var", 1, 1.0 },
-// { "lowlevel.spectral_kurtosis.dmean", 1, 1.0 },
-// { "lowlevel.spectral_kurtosis.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_kurtosis.dvar", 1, 1.0 },
-// { "lowlevel.spectral_kurtosis.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_kurtosis.max", 1, 1.0 },
-// { "lowlevel.spectral_kurtosis.mean", 1, 1.0 },
-// { "lowlevel.spectral_kurtosis.median", 1, 1.0 },
-// { "lowlevel.spectral_kurtosis.min", 1, 1.0 },
-// { "lowlevel.spectral_kurtosis.var", 1, 1.0 },
-// { "lowlevel.spectral_rms.dmean", 1, 1.0 },
-// { "lowlevel.spectral_rms.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_rms.dvar", 1, 1.0 },
-// { "lowlevel.spectral_rms.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_rms.max", 1, 1.0 },
- { "lowlevel.spectral_rms.mean", 1, 1.0 },
-// { "lowlevel.spectral_rms.median", 1, 1.0 },
-// { "lowlevel.spectral_rms.min", 1, 1.0 },
- { "lowlevel.spectral_rms.var", 1, 1.0 },
-// { "lowlevel.spectral_rolloff.dmean", 1, 1.0 },
-// { "lowlevel.spectral_rolloff.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_rolloff.dvar", 1, 1.0 },
-// { "lowlevel.spectral_rolloff.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_rolloff.max", 1, 1.0 },
-// { "lowlevel.spectral_rolloff.mean", 1, 1.0 },
-// { "lowlevel.spectral_rolloff.median", 1, 1.0 },
-// { "lowlevel.spectral_rolloff.min", 1, 1.0 },
-// { "lowlevel.spectral_rolloff.var", 1, 1.0 },
-// { "lowlevel.spectral_skewness.dmean", 1, 1.0 },
-// { "lowlevel.spectral_skewness.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_skewness.dvar", 1, 1.0 },
-// { "lowlevel.spectral_skewness.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_skewness.max", 1, 1.0 },
-// { "lowlevel.spectral_skewness.mean", 1, 1.0 },
-// { "lowlevel.spectral_skewness.median", 1, 1.0 },
-// { "lowlevel.spectral_skewness.min", 1, 1.0 },
-// { "lowlevel.spectral_skewness.var", 1, 1.0 },
-// { "lowlevel.spectral_spread.dmean", 1, 1.0 },
-// { "lowlevel.spectral_spread.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_spread.dvar", 1, 1.0 },
-// { "lowlevel.spectral_spread.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_spread.max", 1, 1.0 },
- { "lowlevel.spectral_spread.mean", 1, 1.0 },
-// { "lowlevel.spectral_spread.median", 1, 1.0 },
-// { "lowlevel.spectral_spread.min", 1, 1.0 },
- { "lowlevel.spectral_spread.var", 1, 1.0 },
-// { "lowlevel.spectral_strongpeak.dmean", 1, 1.0 },
-// { "lowlevel.spectral_strongpeak.dmean2", 1, 1.0 },
-// { "lowlevel.spectral_strongpeak.dvar", 1, 1.0 },
-// { "lowlevel.spectral_strongpeak.dvar2", 1, 1.0 },
-// { "lowlevel.spectral_strongpeak.max", 1, 1.0 },
- { "lowlevel.spectral_strongpeak.mean", 1, 1.0 },
-// { "lowlevel.spectral_strongpeak.median", 1, 1.0 },
-// { "lowlevel.spectral_strongpeak.min", 1, 1.0 },
- { "lowlevel.spectral_strongpeak.var", 1, 1.0 },
-// { "lowlevel.zerocrossingrate.dmean", 1, 1.0 },
-// { "lowlevel.zerocrossingrate.dmean2", 1, 1.0 },
-// { "lowlevel.zerocrossingrate.dvar", 1, 1.0 },
-// { "lowlevel.zerocrossingrate.dvar2", 1, 1.0 },
-// { "lowlevel.zerocrossingrate.max", 1, 1.0 },
- { "lowlevel.zerocrossingrate.mean", 1, 1.0 },
-// { "lowlevel.zerocrossingrate.median", 1, 1.0 },
-// { "lowlevel.zerocrossingrate.min", 1, 1.0 },
- { "lowlevel.zerocrossingrate.var", 1, 1.0 },
-// { "rhythm.beats_count", 1, 1.0 },
-// { "rhythm.beats_loudness.dmean", 1, 1.0 },
-// { "rhythm.beats_loudness.dmean2", 1, 1.0 },
-// { "rhythm.beats_loudness.dvar", 1, 1.0 },
-// { "rhythm.beats_loudness.dvar2", 1, 1.0 },
-// { "rhythm.beats_loudness.max", 1, 1.0 },
- { "rhythm.beats_loudness.mean", 1, 1.0 },
-// { "rhythm.beats_loudness.median", 1, 1.0 },
-// { "rhythm.beats_loudness.min", 1, 1.0 },
- { "rhythm.beats_loudness.var", 1, 1.0 },
-// { "rhythm.bpm", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_bpm.dmean", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_bpm.dmean2", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_bpm.dvar", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_bpm.dvar2", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_bpm.max", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_bpm.mean", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_bpm.median", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_bpm.min", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_bpm.var", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_spread.dmean", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_spread.dmean2", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_spread.dvar", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_spread.dvar2", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_spread.max", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_spread.mean", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_spread.median", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_spread.min", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_spread.var", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_weight.dmean", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_weight.dmean2", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_weight.dvar", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_weight.dvar2", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_weight.max", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_weight.mean", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_weight.median", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_weight.min", 1, 1.0 },
-// { "rhythm.bpm_histogram_first_peak_weight.var", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_bpm.dmean", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_bpm.dmean2", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_bpm.dvar", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_bpm.dvar2", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_bpm.max", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_bpm.mean", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_bpm.median", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_bpm.min", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_bpm.var", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_spread.dmean", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_spread.dmean2", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_spread.dvar", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_spread.dvar2", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_spread.max", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_spread.mean", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_spread.median", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_spread.min", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_spread.var", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_weight.dmean", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_weight.dmean2", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_weight.dvar", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_weight.dvar2", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_weight.max", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_weight.mean", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_weight.median", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_weight.min", 1, 1.0 },
-// { "rhythm.bpm_histogram_second_peak_weight.var", 1, 1.0 },
-// { "rhythm.danceability", 1, 1.0},
-// { "rhythm.onset_rate", 1, 1.0},
-//
-// { "tonal.chords_changes_rate", 1, 1.0 },
-// { "tonal.chords_histogram", 24, 1.0 },
-// { "tonal.chords_number_rate", 1, 1.0 },
-// { "tonal.hpcp_entropy.dmean", 1, 1.0 },
-// { "tonal.hpcp_entropy.dmean2", 1, 1.0 },
-// { "tonal.hpcp_entropy.dvar", 1, 1.0 },
-// { "tonal.hpcp_entropy.dvar2", 1, 1.0 },
-// { "tonal.hpcp_entropy.max", 1, 1.0 },
-// { "tonal.hpcp_entropy.mean", 1, 1.0 },
-// { "tonal.hpcp_entropy.median", 1, 1.0 },
-// { "tonal.hpcp_entropy.min", 1, 1.0 },
-// { "tonal.hpcp_entropy.var", 1, 1.0 },
-// { "tonal.tuning_frequency", 1, 1.0 },
-};
-
-static bool entryConstructFromJSON(entry_t& entry, const std::string& jsonData)
-{
- std::istringstream iss(jsonData);
- boost::property_tree::ptree pt;
- boost::property_tree::json_parser::read_json(iss, pt);
-
- for (auto& elem : features)
- {
- auto child = pt.get_child_optional(elem.lowLevelName);
-
- if (!child)
- {
- LMS_LOG(DBUPDATER, DEBUG) << "Cannot get '" << elem.lowLevelName << "'";
- return false;
- }
-
- if (!entryAddData(entry, *child, elem.nbDimensions))
- return false;
- }
-
- return true;
-}
-
-static entry_t computeWeightCoeffs(const Entries& entries)
-{
- std::vector expandedFeatures;
- std::vector userCoeffs;
-
- for (auto& feature : features)
- {
- for (std::size_t i = 0; i < feature.nbDimensions; ++i)
- {
- expandedFeatures.push_back( { feature.lowLevelName + std::to_string(i), 1, feature.coeff / (double)feature.nbDimensions} );
- }
- }
-
- entry_t coeffs;
-
- ::neural_net::Ranges ranges (*entries.begin());
- ranges( entries );
-
- entry_t::const_iterator pos_max = ranges.get_max().begin();
- entry_t::const_iterator pos_min = ranges.get_min().begin();
- for (std::size_t i = 0; pos_max != ranges.get_max().end(); ++pos_max, ++pos_min, ++i)
- {
- coeffs.push_back( expandedFeatures.at(i).coeff *
- ::operators::inverse (
- ( *pos_max - *pos_min ) * ( *pos_max - *pos_min )
- ) ); // weight for i-th axis
- std::cout << "Feature = " << expandedFeatures.at(i).lowLevelName << std::endl;
- std::cout << "min = " << *pos_min << ", max = " << *pos_max << std::endl;
- std::cout << "Coeff = " << coeffs.back() << std::endl;
- }
- return coeffs;
-}
- template
- using Matrix = std::array, ROW>;
-
-void
-Classifier::processDatabaseUpdate(Updater::Stats stats)
-{
- LMS_LOG(DBUPDATER, DEBUG) << "Database complete Called!";
-
-// typedef ::neural_net::Cauchy_function < entry_t::value_type, entry_t::value_type, ::boost::int32_t> CauchyFunction;
-
- typedef ::neural_net::Gauss_function < entry_t::value_type, entry_t::value_type, ::boost::int32_t > GaussFunction;
-
-// typedef ::distance::Euclidean_distance_function < entry_t > EuclideanDistFunction;
- typedef ::distance::Weighted_euclidean_distance_function < entry_t, entry_t > WeightedEuclideanDistFunction;
-
- typedef ::neural_net::Basic_neuron < GaussFunction, WeightedEuclideanDistFunction > KohonenNeuron;
- typedef ::neural_net::Rectangular_container < KohonenNeuron > KohonenNetwork;
-
-// typedef ::neural_net::Hexagonal_topology < ::boost::int32_t > HexagonalTopology;
- typedef ::neural_net::Max_topology < ::boost::int32_t > MaxTopology;
-
- typedef GaussFunction GaussFunctionSpace;
- typedef ::neural_net::Gauss_function < ::boost::int32_t, entry_t::value_type, ::boost::int32_t > GaussFunctionNet;
-// typedef ::neural_net::Constant_function < entry_t::value_type, entry_t::value_type > ConstFuncSpace;
-
- typedef ::neural_net::Classic_training_weight
- <
- entry_t,
- ::boost::int32_t,
- GaussFunctionNet,
- GaussFunctionSpace,
- MaxTopology,
- WeightedEuclideanDistFunction,
- ::boost::int32_t
- > ClassicWeight;
-
- typedef ::neural_net::Wtm_classical_training_functional
- <
- entry_t,
- double,
- ::boost::int32_t,
- ::boost::int32_t,
- ClassicWeight
- > WtmTrainingFunc;
-
- typedef ::neural_net::Wtm_training_algorithm
- <
- KohonenNetwork,
- entry_t,
- Entries::iterator,
- WtmTrainingFunc,
- ::boost::int32_t
- > WtmTrainingAlg;
-
- Entries entries;
-
- LMS_LOG(DBUPDATER, DEBUG) << "Getting track ids";
-
- std::vector trackIdsAll = Database::Track::getAllIds(_db.getSession());
-
- ::std::random_shuffle ( trackIdsAll.begin(), trackIdsAll.end() );
-
- LMS_LOG(DBUPDATER, DEBUG) << "Getting JSON data";
- std::vector trackIds;
- for (Track::id_type trackId : trackIdsAll)
- {
- std::string jsonData;
-
- {
- Wt::Dbo::Transaction transaction(_db.getSession());
-
- std::vector features = Feature::getByTrack(_db.getSession(), trackId, "low_level");
-
- if (features.empty())
- {
- LMS_LOG(DBUPDATER, DEBUG) << "No JSON data for track " << trackId;;
- continue;
- }
-
- jsonData = features.front()->getValue();
- }
-
- entry_t entry;
- if (entryConstructFromJSON(entry, jsonData))
- {
- trackIds.push_back(trackId);
- entries.push_back(entry);
- }
- else
- {
- LMS_LOG(DBUPDATER, ERROR) << "Skipping track " << trackId;
- continue;
- }
-
-// if (entries.size() > 100)
-// break;
- }
- std::cout << "Feature vector dimension = " << entries.front().size() << std::endl;
-
- if (entries.empty())
- return;
-
- // TODO compute rows / columns from the DBB
- const std::size_t nbRows = 32;
- const std::size_t nbColumns = 32;
-
- // CauchyFunction cauchyFunc(2.0, 1);
- GaussFunction gaussFunc(2.0, 1 );
-
- LMS_LOG(DBUPDATER, DEBUG) << "Computing coeffs";
- entry_t coeffs = computeWeightCoeffs(entries);
- LMS_LOG(DBUPDATER, DEBUG) << "Initializing distfunc";
- WeightedEuclideanDistFunction weightedEuclideanDistFunc (&coeffs);
-
- // prepare randomization policy
- ::neural_net::Internal_randomize internalRandomize;
-
- KohonenNetwork network;
-
- // generate networks initialized by data
- LMS_LOG(DBUPDATER, DEBUG) << "Generating network...";
- ::neural_net::generate_kohonen_network(nbRows, nbColumns, gaussFunc, weightedEuclideanDistFunc, entries, network, internalRandomize);
-
- ::std::cout << "Network weights:" << ::std::endl;
- ::neural_net::print_network_weights ( ::std::cout, network );
- ::std::cout << ::std::endl;
-
- GaussFunctionNet gaussFuncNetwork(10, 1);
- GaussFunctionSpace gaussFuncSpace(10, 1);
- MaxTopology maxTopology;
-// EuclideanDistFunction euclideanDistFunc;
-
- ClassicWeight classicWeight(gaussFuncNetwork, gaussFuncSpace, maxTopology, weightedEuclideanDistFunc);
- WtmTrainingFunc trainingFunc(classicWeight, 0.3);
-
- WtmTrainingAlg wtmTrainAlg( trainingFunc );
-
- LMS_LOG(DBUPDATER, DEBUG) << "Training...";
- // tricky training
- std::size_t nbPass = 20;
- for (std::size_t i = 0; i < nbPass; ++i )
- {
- LMS_LOG(DBUPDATER, DEBUG) << "Training pass " << i << " / " << nbPass;
- // train network using data
- auto entries_copy = entries;
- ::std::random_shuffle ( entries_copy.begin(), entries_copy.end() );
-
- wtmTrainAlg(entries_copy.begin(), entries_copy.end(), &network);
-
- // decrease sigma parameter in network will make training proces more sharpen with each epoch,
- // but it have to be done slowly :-)
- wtmTrainAlg.training_functional.generalized_training_weight.network_function.sigma *= 2.0/3.0;
-// wtmTrainAlg.training_functional.generalized_training_weight.network_function.sigma *= 9.0/10.0;
-
- // shuffle data
-// ::std::random_shuffle ( entries.begin(), entries.end() );
- }
- LMS_LOG(DBUPDATER, DEBUG) << "Training DONE...";
-
- ::std::cout << "Network weights:" << ::std::endl;
- ::neural_net::print_network_weights ( ::std::cout, network );
- ::std::cout << ::std::endl;
-
- struct ClusterEntry
- {
- Track::id_type trackId;
- entry_t entry;
- double distance;
- };
-
- Matrix, nbRows, nbColumns> trackClusters;
-
- for (std::size_t id = 0; id < entries.size(); ++id)
- {
- std::cout << "Entry " << id << " : " << std::endl;
- // Display input vector
- for (auto& value : entries[id])
- {
- std::cout << value << " ";
- }
- std::cout << std::endl;
-
- auto entry = entries[id];
- std::pair coordinates = {0,0};
- double maxValue = 0.0;
-
- for (std::size_t i = 0; i < network.objects.size(); ++i)
- {
- for (std::size_t j = 0; j < network.objects[0].size(); ++j)
- {
- double value = network.objects[i][j]( entry);
- if (value > maxValue)
- {
- coordinates = {i, j};
- maxValue = value;
- }
- }
- }
-
-/* ::std::cout << "Network for entry " << id << std::endl;
- ::neural_net::print_network ( ::std::cout, network, entry );
- ::std::cout << ::std::endl;
-
- std::cout << "Max is in {" << coordinates.first << ", " << coordinates.second << "}" << std::endl;
- */
- trackClusters[coordinates.first][coordinates.second].push_back({trackIds[id], entry, maxValue});
- }
-
- // Create clusters
- LMS_LOG(DBUPDATER, DEBUG) << "Erasing old clusters";
- {
- Wt::Dbo::Transaction transaction(_db.getSession());
- Cluster::removeByType(_db.getSession(), "similarity");
- }
-
- LMS_LOG(DBUPDATER, DEBUG) << "Creating new cluster...";
- for (std::size_t i = 0; i < nbRows; i++)
- {
- for (std::size_t j = 0; j < nbColumns; j++)
- {
- LMS_LOG(DBUPDATER, DEBUG) << "Creating cluster " << i << " " << j;
-
- Wt::Dbo::Transaction transaction(_db.getSession());
-
- Cluster::pointer cluster = Cluster::create(_db.getSession(), "similarity", "cluster_" + std::to_string(i) + "_" + std::to_string(j));
-
- for (auto track : trackClusters[i][j])
- {
- Track::pointer t = Database::Track::getById(_db.getSession(), track.trackId);
- cluster.modify()->addTrack(t);
- }
- }
- }
-
-
- for (std::size_t i = 0; i < nbRows; i++)
- {
- for (std::size_t j = 0; j < nbColumns; j++)
- {
-
-
- std::cout << "Cluster [" << i << "," << j << "] - ";
-
- // Display the neuron for this cluster
- for (auto& value : network.objects[i][j].weights)
- std::cout << value << " ";
- std::cout << std::endl;
-
- for (auto track : trackClusters[i][j])
- {
- Wt::Dbo::Transaction transaction(_db.getSession());
-
- std::cout << "- " << track.distance;
-
- Track::pointer t = Database::Track::getById(_db.getSession(), track.trackId);
- std::cout << " - " << track.trackId << " - " << t->getArtist()->getName() << " - " << t->getName() << " - (" ;
-
- // Display input vector
- for (auto& value : track.entry)
- std::cout << value << " ";
- std::cout << ")" << std::endl;
- }
- }
- }
-
-
-}
-
-} // namespace Database
-
diff --git a/src/database/DatabaseClassifier.hpp b/src/database/DatabaseClassifier.hpp
deleted file mode 100644
index c8e382b8..00000000
--- a/src/database/DatabaseClassifier.hpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2016 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
-
-#include "DatabaseHandler.hpp"
-#include "DatabaseUpdater.hpp"
-
-namespace Database {
-
-class ClusterClassifier
-{
- public:
- ClusterClassifier();
-
-
- private:
-
-};
-
-
-class Classifier
-{
- public:
- Classifier(Wt::Dbo::SqlConnectionPool& connectionPool);
-
- void processTrackUpdate(bool added, Track::id_type trackId, std::string mbid, boost::filesystem::path p);
- void processDatabaseUpdate(Updater::Stats stats);
-
- private:
-
- Database::Handler _db;
-};
-
-
-} // namespace Database
-
diff --git a/src/database/cluster/DatabaseSimilarityCluster.hpp b/src/database/cluster/DatabaseSimilarityCluster.hpp
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/database/tagger/GenreTagger.cpp b/src/database/tagger/GenreTagger.cpp
deleted file mode 100644
index ec40da6d..00000000
--- a/src/database/tagger/GenreTagger.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-
-/*
- * Copyright (C) 2016 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 "GenreTagger.hpp"
-
-namespace Database {
-
-void
-GenreTagger::processTrackUpdate(bool added, Track::id_type trackId, std::string mbid, boost::filesystem::path p)
-{
-
-
-}
-
-void
-GenreTagger::processDatabaseUpdate(Updater::Stats stats)
-{
-
- // TODO: if disabled, delete all the genre tags
- // TODO: if enabled, reconstruct all the genre tags from tracks
-}
-
-} // namespace Database
diff --git a/src/database/tagger/GenreTagger.hpp b/src/database/tagger/GenreTagger.hpp
deleted file mode 100644
index 16118c48..00000000
--- a/src/database/tagger/GenreTagger.hpp
+++ /dev/null
@@ -1,37 +0,0 @@
-
-/*
- * Copyright (C) 2016 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 "database/DatabaseUpdater.hpp"
-
-namespace Database {
-
-class GenreTagger
-{
- public:
- void processTrackUpdate(bool added, Track::id_type trackId, std::string mbid, boost::filesystem::path p);
- void processDatabaseUpdate(Updater::Stats stats);
-
- private:
-
-};
-
-} // namespace Database
diff --git a/src/database/DatabaseFeatureExtractor.cpp b/src/database/updater/DatabaseFeatureExtractor.cpp
similarity index 96%
rename from src/database/DatabaseFeatureExtractor.cpp
rename to src/database/updater/DatabaseFeatureExtractor.cpp
index c4239b39..8b44ecce 100644
--- a/src/database/DatabaseFeatureExtractor.cpp
+++ b/src/database/updater/DatabaseFeatureExtractor.cpp
@@ -21,8 +21,8 @@
#include "feature/FeatureExtractor.hpp"
#include "feature/FeatureStore.hpp"
+#include "database/Setting.hpp"
-#include "Setting.hpp"
#include "DatabaseFeatureExtractor.hpp"
namespace Database {
@@ -35,7 +35,7 @@ static std::string getMBID(Track::id_type trackId)
}
void
-FeatureExtractor::processDatabaseUpdate(Updater::Stats stats)
+FeatureExtractor::handleFilesUpdated(void)
{
bool fetchHighLevel = Setting::getBool(UpdaterDboSession(), "tags_highlevel_acousticbrainz");
bool fetchLowLevel = Setting::getBool(UpdaterDboSession(), "tags_similarity_acousticbrainz");
diff --git a/src/database/DatabaseFeatureExtractor.hpp b/src/database/updater/DatabaseFeatureExtractor.hpp
similarity index 86%
rename from src/database/DatabaseFeatureExtractor.hpp
rename to src/database/updater/DatabaseFeatureExtractor.hpp
index 2a7c268c..c0ff092a 100644
--- a/src/database/DatabaseFeatureExtractor.hpp
+++ b/src/database/updater/DatabaseFeatureExtractor.hpp
@@ -19,14 +19,14 @@
#pragma once
-#include "database/DatabaseUpdater.hpp"
+#include "DatabaseUpdater.hpp"
namespace Database {
-class FeatureExtractor
+class FeatureExtractor : public UpdaterEventHandler
{
public:
- void processDatabaseUpdate(Updater::Stats stats);
+ void handleFilesUpdated(void);
};
diff --git a/src/database/cluster/DatabaseHighLevelCluster.cpp b/src/database/updater/DatabaseHighLevelCluster.cpp
similarity index 98%
rename from src/database/cluster/DatabaseHighLevelCluster.cpp
rename to src/database/updater/DatabaseHighLevelCluster.cpp
index 84af0d28..412e354f 100644
--- a/src/database/cluster/DatabaseHighLevelCluster.cpp
+++ b/src/database/updater/DatabaseHighLevelCluster.cpp
@@ -165,7 +165,7 @@ static std::list getClustersFromFeature(Feature::Type& feature, dou
}
void
-HighLevelCluster::processDatabaseUpdate(Updater::Stats stats)
+HighLevelCluster::handleFilesUpdated(void)
{
bool createTags = Setting::getBool(UpdaterDboSession(), "tags_highlevel_acousticbrainz", false);
double minProb = Setting::getInt(UpdaterDboSession(), "tags_highlevel_acousticbrainz_min_probability", false) / 100.;
diff --git a/src/database/cluster/DatabaseHighLevelCluster.hpp b/src/database/updater/DatabaseHighLevelCluster.hpp
similarity index 86%
rename from src/database/cluster/DatabaseHighLevelCluster.hpp
rename to src/database/updater/DatabaseHighLevelCluster.hpp
index 25f91d86..20adb3a8 100644
--- a/src/database/cluster/DatabaseHighLevelCluster.hpp
+++ b/src/database/updater/DatabaseHighLevelCluster.hpp
@@ -19,14 +19,14 @@
#pragma once
-#include "database/DatabaseUpdater.hpp"
+#include "DatabaseUpdater.hpp"
namespace Database {
-class HighLevelCluster
+class HighLevelCluster : public UpdaterEventHandler
{
public:
- void processDatabaseUpdate(Updater::Stats stats);
+ void handleFilesUpdated(void);
};
diff --git a/src/database/DatabaseUpdater.cpp b/src/database/updater/DatabaseUpdater.cpp
similarity index 99%
rename from src/database/DatabaseUpdater.cpp
rename to src/database/updater/DatabaseUpdater.cpp
index 0ba96f85..7e5afb44 100644
--- a/src/database/DatabaseUpdater.cpp
+++ b/src/database/updater/DatabaseUpdater.cpp
@@ -28,8 +28,9 @@
#include "utils/Utils.hpp"
#include "utils/Path.hpp"
-#include "Setting.hpp"
-#include "Types.hpp"
+#include "database/Setting.hpp"
+#include "database/Types.hpp"
+
#include "DatabaseUpdater.hpp"
namespace {
diff --git a/src/database/DatabaseUpdater.hpp b/src/database/updater/DatabaseUpdater.hpp
similarity index 90%
rename from src/database/DatabaseUpdater.hpp
rename to src/database/updater/DatabaseUpdater.hpp
index 3d032c4e..5683b50c 100644
--- a/src/database/DatabaseUpdater.hpp
+++ b/src/database/updater/DatabaseUpdater.hpp
@@ -20,6 +20,7 @@
#pragma once
#include
+#include
#include
@@ -32,6 +33,9 @@
namespace Database {
+
+class UpdaterEventHandler;
+
class Updater
{
public:
@@ -60,7 +64,7 @@ class Updater
std::size_t nbChanges() const { return nbAdded + nbRemoved + nbModified;}
};
- // Emitted when the whole database has been scanned
+ // Emitted when the whole database has been scanned (and all the event handlers have been called)
Wt::Signal& scanComplete() { return _sigScanComplete; }
// Emitted when a track changed
@@ -76,6 +80,9 @@ class Updater
Database::Handler& getDb(void) { return *_db; }
bool quitRequested(void) const { return !_running;}
+
+ void registerEventHandler(std::shared_ptr handler) { _eventHandlers.push_back(handler); }
+
private:
Updater();
@@ -136,6 +143,8 @@ class Updater
MetaData::TagLibParser _metadataParser;
+ std::list > _eventHandlers;
+
}; // class Updater
// Helper to get the updater session data
@@ -149,5 +158,17 @@ static inline bool UpdaterQuitRequested()
return Updater::instance().quitRequested();
}
+
+class UpdaterEventHandler
+{
+ public:
+
+ // called when all the files have been scanned by the updater
+ virtual void handleFilesUpdated(void) = 0;
+
+ private:
+
+};
+
} // Database
diff --git a/src/main/main.cpp b/src/main/main.cpp
index 1c8eecc5..5e9f4082 100644
--- a/src/main/main.cpp
+++ b/src/main/main.cpp
@@ -28,9 +28,9 @@
#include "image/Image.hpp"
#include "feature/FeatureExtractor.hpp"
-#include "database/DatabaseUpdater.hpp"
-#include "database/DatabaseFeatureExtractor.hpp"
-#include "database/cluster/DatabaseHighLevelCluster.hpp"
+#include "database/updater/DatabaseUpdater.hpp"
+#include "database/updater/DatabaseFeatureExtractor.hpp"
+#include "database/updater/DatabaseHighLevelCluster.hpp"
#include "ui/LmsApplication.hpp"
@@ -106,14 +106,9 @@ int main(int argc, char* argv[])
Database::Updater& dbUpdater = Database::Updater::instance();
dbUpdater.setConnectionPool(*connectionPool);
- Database::FeatureExtractor dbFeatureExtractor;
- Database::HighLevelCluster dbHighLevelCluster;
-
- // Connect to the update events
- dbUpdater.scanComplete().connect(std::bind(&Database::HighLevelCluster::processDatabaseUpdate, &dbHighLevelCluster, std::placeholders::_1));
- dbUpdater.scanComplete().connect(std::bind(&Database::FeatureExtractor::processDatabaseUpdate, &dbFeatureExtractor, std::placeholders::_1));
-
-// dbHighLevelCluster.processDatabaseUpdate(Database::Updater::Stats());
+ // Instanciate the updater's event handler. Order is important
+ dbUpdater.registerEventHandler(std::make_shared());
+ dbUpdater.registerEventHandler(std::make_shared());
// bind entry point
server.addEntryPoint(Wt::Application, boost::bind(UserInterface::LmsApplication::create,
diff --git a/src/ui/settings/Settings.cpp b/src/ui/settings/Settings.cpp
index 308664c8..78d3838b 100644
--- a/src/ui/settings/Settings.cpp
+++ b/src/ui/settings/Settings.cpp
@@ -30,8 +30,8 @@
#include "SettingsUsers.hpp"
#include "logger/Logger.hpp"
-#include "database/DatabaseUpdater.hpp"
#include "database/Setting.hpp"
+#include "database/updater/DatabaseUpdater.hpp"
#include "LmsApplication.hpp"