Restored recommendations based on acoustic similarities (using musicnn), fixes #301

This commit is contained in:
emeric
2026-06-02 08:32:43 +02:00
parent 1524106124
commit eb7f65878f
227 changed files with 10324 additions and 4673 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
pkg_check_modules(Config++ REQUIRED IMPORTED_TARGET libconfig++)
pkg_check_modules(Archive REQUIRED IMPORTED_TARGET libarchive)
pkg_check_modules(Config++ REQUIRED IMPORTED_TARGET libconfig++)
pkg_check_modules(XXHASH REQUIRED IMPORTED_TARGET libxxhash)
set(LMS_VERSION ${PROJECT_VERSION})
+1
View File
@@ -1,5 +1,6 @@
add_executable(bench-core
Core.cpp
TraceLoggerBench.cpp
)
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Emeric Poupon
* Copyright (C) 2026 Emeric Poupon
*
* This file is part of LMS.
*
@@ -17,10 +17,6 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gtest/gtest.h>
#include <benchmark/benchmark.h>
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
BENCHMARK_MAIN();
+2 -5
View File
@@ -24,7 +24,7 @@
#include "core/ILogger.hpp"
#include "core/ITraceLogger.hpp"
namespace lms::core
namespace lms::core::benchs
{
// The trace logger is meant to built/destroyed once
const Service<logging::ILogger> logger{ logging::createLogger() };
@@ -73,7 +73,4 @@ namespace lms::core
BENCHMARK(BM_TraceLogger_Overview_withArg)->Threads(1)->Threads(std::thread::hardware_concurrency());
BENCHMARK(BM_TraceLogger_Detailed)->Threads(1)->Threads(std::thread::hardware_concurrency());
BENCHMARK(BM_TraceLogger_Detailed_withArg)->Threads(1)->Threads(std::thread::hardware_concurrency());
} // namespace lms::core
BENCHMARK_MAIN();
} // namespace lms::core::benchs
+25 -1
View File
@@ -18,14 +18,38 @@
*/
#include "core/XxHash3.hpp"
#include "core/Exception.hpp"
#define XXH_INLINE_ALL
#include <xxhash.h>
namespace lms::core
{
std::uint64_t xxHash3_64(std::span<const std::byte> buf)
std::uint64_t XxHash3_64::hash(std::span<const std::byte> buf)
{
return XXH3_64bits(buf.data(), buf.size());
}
XxHash3_64::XxHash3_64()
: _state{ XXH3_createState() }
{
if (!_state)
throw LmsException{ "XXH3_createState failed: out of memory" };
XXH3_64bits_reset(static_cast<XXH3_state_t*>(_state));
}
XxHash3_64::~XxHash3_64()
{
XXH3_freeState(static_cast<XXH3_state_t*>(_state));
}
void XxHash3_64::update(std::span<const std::byte> buf)
{
XXH3_64bits_update(static_cast<XXH3_state_t*>(_state), buf.data(), buf.size());
}
std::uint64_t XxHash3_64::digest() const
{
return XXH3_64bits_digest(static_cast<const XXH3_state_t*>(_state));
}
} // namespace lms::core
@@ -0,0 +1,73 @@
/*
* Copyright (C) 2026 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstddef>
#include <cstdlib>
#include "core/Exception.hpp"
namespace lms::core
{
template<typename T, std::size_t Alignment>
class AlignedHeapArray
{
public:
using iterator = T*;
using const_iterator = const T*;
explicit AlignedHeapArray(std::size_t count)
: _count{ count }
, _values{ static_cast<T*>(std::aligned_alloc(Alignment, getStorageSize(count))) }
{
if (!_values)
throw LmsException{ "Allocation failed" };
}
~AlignedHeapArray()
{
std::free(_values);
}
AlignedHeapArray(const AlignedHeapArray&) = delete;
AlignedHeapArray& operator=(const AlignedHeapArray&) = delete;
std::size_t size() const { return _count; }
T* data() const { return _values; }
T& operator[](std::size_t index) const { return _values[index]; }
iterator begin() const { return _values; }
iterator end() const { return _values + _count; }
const_iterator cbegin() const { return _values; }
const_iterator cend() const { return _values + _count; }
private:
static std::size_t getStorageSize(std::size_t count)
{
const std::size_t bytes{ count * sizeof(T) };
const std::size_t alignedBytes{ ((bytes + Alignment - 1) / Alignment) * Alignment };
return alignedBytes;
}
const std::size_t _count;
T* _values;
};
} // namespace lms::core
@@ -19,6 +19,8 @@
#pragma once
#include <cstdint>
#include <boost/crc.hpp> // for boost::crc_32_type
namespace lms::core
+25
View File
@@ -21,6 +21,7 @@
#include <algorithm>
#include <random>
#include <type_traits>
namespace lms::core::random
{
@@ -43,12 +44,36 @@ namespace lms::core::random
return dist(getRandGenerator());
}
template<typename RandomEngine, typename Container>
requires std::is_floating_point_v<typename Container::value_type>
void fillContainer(RandomEngine& randomEngine, Container& container, typename Container::value_type min, typename Container::value_type max)
{
std::uniform_real_distribution<typename Container::value_type> distrib{ min, max };
for (auto& v : container)
v = distrib(randomEngine);
}
template<typename RandomEngine, typename Container>
requires std::is_integral_v<typename Container::value_type>
void fillContainer(RandomEngine& randomEngine, Container& container, typename Container::value_type min, typename Container::value_type max)
{
std::uniform_int_distribution<typename Container::value_type> distrib{ min, max };
for (auto& v : container)
v = distrib(randomEngine);
}
template<typename Container>
void shuffleContainer(Container& container)
{
std::shuffle(std::begin(container), std::end(container), getRandGenerator());
}
template<typename RandomEngine, typename Container>
void shuffleContainer(RandomEngine& randomEngine, Container& container)
{
std::shuffle(std::begin(container), std::end(container), randomEngine);
}
template<typename Container>
typename Container::const_iterator pickRandom(const Container& container)
{
-10
View File
@@ -19,18 +19,8 @@
#pragma once
#include <algorithm>
#include <functional>
namespace lms::core::utils
{
template<typename Container, typename T>
void push_back_if_not_present(Container& container, const T& val)
{
if (std::find(std::cbegin(container), std::cend(container), val) == std::cend(container))
container.push_back(val);
}
template<class... Ts>
struct overloads : Ts...
{
+16 -1
View File
@@ -25,5 +25,20 @@
namespace lms::core
{
std::uint64_t xxHash3_64(std::span<const std::byte> buf);
class XxHash3_64
{
public:
static std::uint64_t hash(std::span<const std::byte> buf);
XxHash3_64();
~XxHash3_64();
XxHash3_64(const XxHash3_64&) = delete;
XxHash3_64& operator=(const XxHash3_64&) = delete;
void update(std::span<const std::byte> buf);
std::uint64_t digest() const;
private:
void* _state{};
};
} // namespace lms::core
+2 -1
View File
@@ -10,15 +10,16 @@ add_executable(test-core
Service.cpp
String.cpp
TraceLogger.cpp
Utils.cpp
UUID.cpp
XxHash3.cpp
)
target_link_libraries(test-core PRIVATE
lmscore
lmsmath
Threads::Threads
GTest::GTest
GTest::gtest_main
)
if (NOT CMAKE_CROSSCOMPILING)
+21 -1
View File
@@ -31,7 +31,27 @@ namespace lms::core
for (std::size_t i{}; i < buffer.size(); ++i)
buffer[i] = static_cast<std::byte>(i);
const std::uint64_t hash{ xxHash3_64(buffer) };
const std::uint64_t hash{ XxHash3_64::hash(buffer) };
EXPECT_EQ(hash, 12137474952470826274ULL);
}
TEST(Xxhash3_64, streamingMatchesOneShot)
{
std::vector<std::byte> buffer;
buffer.resize(1024);
for (std::size_t i{}; i < buffer.size(); ++i)
buffer[i] = static_cast<std::byte>(i);
const std::uint64_t expected{ XxHash3_64::hash(buffer) };
// Feed the same data in three unequal chunks to exercise the streaming path
constexpr std::size_t chunk1{ 100 };
constexpr std::size_t chunk2{ 400 };
XxHash3_64 hasher;
hasher.update(std::span{ buffer }.subspan(0, chunk1));
hasher.update(std::span{ buffer }.subspan(chunk1, chunk2));
hasher.update(std::span{ buffer }.subspan(chunk1 + chunk2));
EXPECT_EQ(hasher.digest(), expected);
}
} // namespace lms::core