diff --git a/include/flucoma/algorithms/public/NMFMorph.hpp b/include/flucoma/algorithms/public/NMFMorph.hpp index ba8a8dd1..ab670743 100644 --- a/include/flucoma/algorithms/public/NMFMorph.hpp +++ b/include/flucoma/algorithms/public/NMFMorph.hpp @@ -102,7 +102,8 @@ class NMFMorph bool initialized() const { return mInitialized; } - void processFrame(ComplexVectorView v, double interpolation, Allocator& alloc) + void processFrame(ComplexVectorView v, double interpolation, index seed, + Allocator& alloc) { using namespace Eigen; using namespace _impl; @@ -120,7 +121,8 @@ class NMFMorph ScopedEigenMap frame(W.rows(), alloc); frame = W * hFrame; RealVectorView mag1 = asFluid(frame); - mRTPGHI.processFrame(mag1, v, mWindowSize, mFFTSize, mHopSize, 1e-6, alloc); + mRTPGHI.processFrame(mag1, v, mWindowSize, mFFTSize, mHopSize, 1e-6, seed, + alloc); mPos = (mPos + 1) % mH.cols(); } diff --git a/include/flucoma/algorithms/util/RTPGHI.hpp b/include/flucoma/algorithms/util/RTPGHI.hpp index 508407f9..8a6987ea 100644 --- a/include/flucoma/algorithms/util/RTPGHI.hpp +++ b/include/flucoma/algorithms/util/RTPGHI.hpp @@ -17,6 +17,7 @@ under the European Union’s Horizon 2020 research and innovation programme #include "AlgorithmUtils.hpp" #include "FluidEigenMappings.hpp" #include "../public/STFT.hpp" +#include "../util/EigenRandom.hpp" #include "../../data/FluidIndex.hpp" #include "../../data/FluidMemory.hpp" #include "../../data/TensorTypes.hpp" @@ -59,7 +60,7 @@ class RTPGHI } void processFrame(RealVectorView in, ComplexVectorView out, index winSize, - index fftSize, index hopSize, double tolerance, Allocator& alloc) + index fftSize, index hopSize, double tolerance, index seed, Allocator& alloc) { using namespace Eigen; using namespace _impl; @@ -88,8 +89,8 @@ class RTPGHI todo = (currentLogMag > absTol).cast(); index numTodo = static_cast(todo.sum()); ScopedEigenMap phaseEst(mBins, alloc); - phaseEst = pi + ArrayXd::Random(mBins) * pi; - + phaseEst = + EigenRandom(mBins, RandomSeed{seed}, Range{0.0, 2.0 * pi}); rt::vector> heap(alloc); heap.reserve(asUnsigned(mBins)); for (index i = 0; i < mBins; i++) diff --git a/include/flucoma/clients/rt/NMFMorphClient.hpp b/include/flucoma/clients/rt/NMFMorphClient.hpp index 9db20e61..4cc2318b 100644 --- a/include/flucoma/clients/rt/NMFMorphClient.hpp +++ b/include/flucoma/clients/rt/NMFMorphClient.hpp @@ -27,6 +27,7 @@ enum NMFFilterIndex { kActBuf, kAutoAssign, kInterpolation, + kRandomSeed, kFFT }; @@ -36,6 +37,7 @@ constexpr auto NMFMorphParams = defineParameters( InputBufferParam("activations", "Activations"), EnumParam("autoassign", "Automatic assign", 1, "No", "Yes"), FloatParam("interpolation", "Interpolation", 0, Min(0.0), Max(1.0)), + LongParam("seed", "Random Seed", -1), FFTParam("fftSettings", "FFT Settings", 1024, -1, -1)); class NMFMorphClient : public FluidBaseClient, public AudioOut @@ -116,7 +118,7 @@ class NMFMorphClient : public FluidBaseClient, public AudioOut mSTFTProcessor.processOutput( get(), output, c, [&](ComplexMatrixView out) { mNMFMorph.processFrame(out.row(0), get(), - c.allocator()); + get(), c.allocator()); }); } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9fd84ae5..1d6a50b8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -128,7 +128,7 @@ target_link_libraries(TestEnvelopeGate PRIVATE TestSignals) target_link_libraries(TestTransientSlice PRIVATE TestSignals) add_test_executable(TestEigenRandom algorithms/util/TestEigenRandom.cpp) - +add_test_executable(TestRTPGHI algorithms/util/TestRTPGHI.cpp) include(CTest) include(Catch) @@ -157,6 +157,7 @@ catch_discover_tests(TestBufferedProcess WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" catch_discover_tests(TestMLP WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") catch_discover_tests(TestKMeans WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) catch_discover_tests(TestEigenRandom WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +catch_discover_tests(TestRTPGHI WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") catch_discover_tests(TestUMAP WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") catch_discover_tests(TestDataSampler) diff --git a/tests/algorithms/util/TestRTPGHI.cpp b/tests/algorithms/util/TestRTPGHI.cpp new file mode 100644 index 00000000..d39ac104 --- /dev/null +++ b/tests/algorithms/util/TestRTPGHI.cpp @@ -0,0 +1,51 @@ +#define CATCH_CONFIG_MAIN +#include +#include +#include +#include +#include +#include + +namespace fluid { + + +TEST_CASE("RTPGHI is repeatable with manually set random seed") +{ + using Tensor = fluid::FluidTensor; + using ComplexTensor = fluid::FluidTensor, 1>; + using fluid::algorithm::RTPGHI; + + index win = 64; + index fft = 64; + index hop = 64; + index bins = fft / 2 + 1; + + double mag = 1.0; + // to stop algo converging, bypass loop by setting massive tolerence + double tol = 2.0 * mag; + + RTPGHI algo(fft, FluidDefaultAllocator()); + + Tensor input(bins); + input[index(bins / 2)] = mag; + std::vector results(3, ComplexTensor(bins)); + + // algo has memory, so re-init after each call to test repeatability, and call + // twice to actually generate some action + auto runit = [&](size_t run, index seed) { + algo.init(fft); + algo.processFrame(input, results[run], win, fft, hop, tol, seed, + FluidDefaultAllocator()); + algo.processFrame(input, results[run], win, fft, hop, 2.0, seed, + FluidDefaultAllocator()); + }; + + for (size_t run = 0; run < results.size(); ++run) + runit(run, run < 2 ? 42 : 8347); + + using Catch::Matchers::RangeEquals; + + REQUIRE_THAT(results[0], RangeEquals(results[1])); + REQUIRE_THAT(results[0], !RangeEquals(results[2])); +} +} // namespace fluid \ No newline at end of file