Restored recommendations based on acoustic similarities (using musicnn), fixes #301
This commit is contained in:
@@ -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,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();
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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...
|
||||
{
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user