diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 34281ac68..c0f3e3f32 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -127,6 +127,9 @@ target_link_libraries(TestFluidTensorDeath PRIVATE add_test_executable(TestFluidTensorView data/TestFluidTensorView.cpp) add_test_executable(TestFluidTensorSupport data/TestFluidTensorSupport.cpp) add_test_executable(TestFluidDataSet data/TestFluidDataSet.cpp) +add_test_executable(TestFluidSource clients/common/TestFluidSource.cpp) +add_test_executable(TestFluidSink clients/common/TestFluidSink.cpp) +add_test_executable(TestBufferedProcess clients/common/TestBufferedProcess.cpp) include(CTest) include(Catch) @@ -142,4 +145,8 @@ catch_discover_tests(TestFluidTensorView WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" catch_discover_tests(TestFluidTensorSupport WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") catch_discover_tests(TestFluidDataSet WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") +catch_discover_tests(TestFluidSource WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") +catch_discover_tests(TestFluidSink WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") +catch_discover_tests(TestBufferedProcess WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") + add_compile_tests("FluidTensor Compilation Tests" data/compile_tests/TestFluidTensor_Compile.cpp) diff --git a/tests/clients/common/TestBufferedProcess.cpp b/tests/clients/common/TestBufferedProcess.cpp new file mode 100644 index 000000000..d91d6c40d --- /dev/null +++ b/tests/clients/common/TestBufferedProcess.cpp @@ -0,0 +1,72 @@ +#define CATCH_CONFIG_MAIN +#include +// #include +#include +#include +#include +#include +#include +#include +#include +#include + +using fluid::EqualsRange; +using fluid::FluidTensor; +using fluid::FluidTensorView; +using fluid::Slice; +using fluid::client::BufferedProcess; +using fluid::client::FluidContext; + +TEST_CASE("BufferedProcess will reconstruct windowed input properly under COLA conditions","[BufferedProcess]"){ + + BufferedProcess processor; + + constexpr int hostSize = 64; + FluidContext c; + + auto frameSize = GENERATE(32, 64, 256, 1024, 8192); + + auto hop = frameSize / 2; + + processor.maxSize(frameSize, frameSize, 1,1 ); + processor.hostSize(hostSize); // sigh, FIXME + + FluidTensor input(1,128 * frameSize); + + input(Slice(0),Slice(frameSize)).fill(1); + + FluidTensor output(1,hostSize); + FluidTensor window(1, frameSize); + + std::vector expected(hostSize); + std::vector actual(hostSize); + + window.apply([frameSize, i=0](double& x) mutable { + x = 0.5 - 0.5 * cos((2 * M_PI * i++) / frameSize); + }); //generate Hann window + + for(int i = frameSize; i < input.size() - hostSize; i+=hostSize) + { + processor.push(input(Slice(0),Slice(i,hostSize))); + + //Hann windowing with overlap of 2 should be COLA + processor.process(frameSize, frameSize,hop,c, + [&window](FluidTensorView in, FluidTensorView out) { + out = in; + out.apply(window, [](double& x, double w) { x *= w; }); + }); + + processor.pull(FluidTensorView(actual.data(),0,1,hostSize)); + + //we expect output to be input delayed by frameSize samples + auto expectedSlice= input(Slice(0), Slice(i - frameSize, hostSize)); + std::copy(expectedSlice.begin(), expectedSlice.end(),expected.begin()); + + auto matcher = Catch::Matchers::Approx(expected); + double epsilon = 1e-12; + matcher.epsilon(epsilon); + + CHECK_THAT(actual,matcher); + + } +} diff --git a/tests/clients/common/TestFluidSink.cpp b/tests/clients/common/TestFluidSink.cpp new file mode 100644 index 000000000..096353d29 --- /dev/null +++ b/tests/clients/common/TestFluidSink.cpp @@ -0,0 +1,74 @@ +#define CATCH_CONFIG_MAIN +#include +// #include +#include +#include +#include +#include +#include +#include + +using fluid::EqualsRange; +using fluid::FluidSink; +using fluid::FluidTensor; +using fluid::FluidTensorView; +using fluid::Slice; + + +TEMPLATE_TEST_CASE("FluidSink can overlap-add a varitety of window sizes and hops", + "[FluidSink][frames]", int, double) +{ + + constexpr int hostSize = 64; + constexpr int maxFrameSize = 1024; + + FluidSink olaBuffer(maxFrameSize, 1); + olaBuffer.setHostBufferSize(hostSize); + olaBuffer.reset(1); // sigh, FIXME + + std::array data; + data.fill(1); + + + std::array emptyFrame; + + emptyFrame.fill(0); +// std::iota(data.begin(), data.end(), 0); + + // run the test with each of these frame sizes + auto frameSize = GENERATE(32, 43, 64, 96, 128, 512); + + FluidTensor expected(data.size()); + expected.fill(0); + FluidTensor output(1, hostSize); + + + // and for each frame size above, we test with these overlaps + auto overlap = GENERATE(1,2,3,4); + + int hop = frameSize / overlap; + + //simulate ouptut data by building buffer for whole span + for(int i = 0; i < expected.size();i+=hop) + { + int chunkSize = std::min(frameSize, expected.size() - i); + expected(Slice(i,chunkSize)).apply([](TestType& x){ + x += 1; + }); + } + + + for (int i = 0, j = 0, k = 0; i < data.size() - hostSize; i += hostSize) + { + + for (; j < hostSize; j += hop, k += 1) + { + auto input = FluidTensorView{ data.data(), i, 1, frameSize }; + olaBuffer.push(input, j); + } + + j = j < hostSize ? j : j - hostSize; + olaBuffer.pull(FluidTensorView(output)); + CHECK_THAT(output,EqualsRange(expected(Slice(i,hostSize)))); + } +} diff --git a/tests/clients/common/TestFluidSource.cpp b/tests/clients/common/TestFluidSource.cpp new file mode 100644 index 000000000..1e59e360d --- /dev/null +++ b/tests/clients/common/TestFluidSource.cpp @@ -0,0 +1,63 @@ +#define CATCH_CONFIG_MAIN +#include +// #include +#include +#include +#include +#include +#include +#include + +using fluid::EqualsRange; +using fluid::FluidSource; +using fluid::FluidTensor; +using fluid::FluidTensorView; +using fluid::Slice; + +TEMPLATE_TEST_CASE( + "FluidSource can provide different window and hop sizes with a known delay", + "[FluidSource][frames]", int, double) +{ + + constexpr int hostSize = 64; + constexpr int maxFrameSize = 1024; + + FluidSource framer(maxFrameSize, 1); + framer.setHostBufferSize(hostSize); + framer.reset(1); // sigh, FIXME + + std::array data; + std::array output; +// std::array emptyFrame; + +// emptyFrame.fill(0); + std::iota(data.begin(), data.end(), 0); + + // run the test with each of these frame sizes + auto frameSize = GENERATE(32, 43, 64, 96, 128, 512); + + + // and for each frame size above, we test with these hops + auto hop = GENERATE_REF(int(frameSize / 4), int(frameSize / 3), + int(frameSize / 2), int(frameSize)); + + + FluidTensor expected(data.size() + frameSize); + expected(Slice(frameSize)) = FluidTensorView(data.data(),0,data.size()); + + for (int i = 0, j = 0, k = 0; i < data.size() - hostSize; i += hostSize) + { + auto input = FluidTensorView{ data.data(), i, 1, hostSize }; + framer.push(input); + auto outputView = FluidTensorView{ output.data(), 0, 1, frameSize }; + + + for (; j < hostSize; j += hop, k += hop) + { + framer.pull(outputView, j); + CHECK_THAT(outputView, EqualsRange(expected(Slice(k,frameSize)))); + } + + j = j < hostSize ? j : j - hostSize; + } +}