From 7dc65999a4dd367e0812ac1e243db98dd1efb1dc Mon Sep 17 00:00:00 2001 From: paul Date: Wed, 21 Aug 2019 01:00:07 +0200 Subject: [PATCH] SIMD refactoring --- CMakeLists.txt | 53 ++--- benchmarks/BM_fill.cpp | 53 +++++ benchmarks/BM_writeInterleaved.cpp | 59 ++++++ benchmarks/Cum_Prod.cpp | 100 +++++++++ benchmarks/Cum_Sum.cpp | 100 +++++++++ benchmarks/Looping_index_2.cpp | 2 +- benchmarks/Stereo_Fill.cpp | 39 ---- benchmarks/Stereo_Read_Interleaved.cpp | 35 ---- benchmarks/Stereo_Write_Interleaved.cpp | 53 ----- sources/ADSREnvelope.h | 40 ++++ sources/Buffer.h | 50 +++-- sources/FilePool.cpp | 5 +- sources/Globals.h | 31 +-- sources/LinearEnvelope.h | 13 ++ sources/SIMDDummy.cpp | 35 ++++ sources/SIMDHelpers.h | 67 ++++++ sources/SIMDSSE.cpp | 107 ++++++++++ sources/StereoBuffer.h | 46 +--- sources/StereoBufferSSE.cpp | 95 --------- sources/Synth.h | 3 +- tests/SIMDHelpersT.cpp | 267 ++++++++++++++++++++++++ tests/StereoBufferT.cpp | 208 ++---------------- 22 files changed, 944 insertions(+), 517 deletions(-) create mode 100644 benchmarks/BM_fill.cpp create mode 100644 benchmarks/BM_writeInterleaved.cpp create mode 100644 benchmarks/Cum_Prod.cpp create mode 100644 benchmarks/Cum_Sum.cpp delete mode 100644 benchmarks/Stereo_Fill.cpp delete mode 100644 benchmarks/Stereo_Read_Interleaved.cpp delete mode 100644 benchmarks/Stereo_Write_Interleaved.cpp create mode 100644 sources/ADSREnvelope.h create mode 100644 sources/LinearEnvelope.h create mode 100644 sources/SIMDDummy.cpp create mode 100644 sources/SIMDHelpers.h create mode 100644 sources/SIMDSSE.cpp delete mode 100644 sources/StereoBufferSSE.cpp create mode 100644 tests/SIMDHelpersT.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b63c750b..a7c242c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,22 +80,23 @@ set(COMMON_SOURCES ) set(SSE_SOURCES - sources/StereoBufferSSE.cpp + ) if (HAVE_X86INTRIN_H AND UNIX) add_compile_options(-DHAVE_X86INTRIN_H) add_compile_options(-DUSE_SIMD) - set(COMMON_SOURCES ${COMMON_SOURCES} ${SSE_SOURCES}) -endif() -if (HAVE_INTRIN_H AND WIN32) + set(COMMON_SOURCES ${COMMON_SOURCES} sources/SIMDSSE.cpp) +elseif (HAVE_INTRIN_H AND WIN32) add_compile_options(/DHAVE_INTRIN_H) add_compile_options(/DUSE_SIMD) - set(COMMON_SOURCES ${COMMON_SOURCES} ${SSE_SOURCES}) -endif() -if (HAVE_ARM_NEON_H AND UNIX) + set(COMMON_SOURCES ${COMMON_SOURCES} sources/SIMDSSE.cpp) +elseif (HAVE_ARM_NEON_H AND UNIX) add_compile_options(-DUSE_SIMD) add_compile_options(-DHAVE_ARM_NEON_H) + set(COMMON_SOURCES ${COMMON_SOURCES} sources/SIMDDummy.cpp) +else() + set(COMMON_SOURCES ${COMMON_SOURCES} sources/SIMDDummy.cpp) endif() set(TEST_SOURCES @@ -106,6 +107,7 @@ set(TEST_SOURCES tests/OpcodeT.cpp tests/BufferT.cpp tests/StereoBufferT.cpp + tests/SIMDHelpersT.cpp tests/FilesT.cpp tests/OnePoleFilterT.cpp tests/RegionActivationT.cpp @@ -124,9 +126,6 @@ target_link_libraries(sfzprint absl::strings cxxopts) ############################### # Basic command line program add_executable(sfizz sources/Main.cpp ${COMMON_SOURCES}) -if (HAVE_INTRIN_H OR HAVE_X86INTRIN_H) - target_sources(sfizz PRIVATE ${SSE_SOURCES}) -endif() # Per OS properties if(UNIX) target_compile_options(sfizz PRIVATE -fno-rtti) @@ -138,9 +137,6 @@ target_link_libraries(sfizz absl::strings cxxopts sndfile absl::flat_hash_map re # Test application add_executable(sfizz_tests ${TEST_SOURCES} ${COMMON_SOURCES}) # Per OS properties -if (HAVE_INTRIN_H OR HAVE_X86INTRIN_H) - target_sources(sfizz_tests PRIVATE ${SSE_SOURCES}) -endif() if(UNIX) target_link_libraries(sfizz_tests stdc++fs) endif(UNIX) @@ -148,36 +144,29 @@ target_link_libraries(sfizz_tests Catch2::Catch2 absl::strings absl::flat_hash_m target_include_directories(sfizz_tests SYSTEM PRIVATE sources) file(COPY "tests" DESTINATION ${CMAKE_BINARY_DIR}) -############################### -add_executable(bench_fill benchmarks/Stereo_Fill.cpp sources/StereoBufferSSE.cpp) -# Per OS properties -target_link_libraries(bench_fill benchmark) - -############################### -add_executable(bench_read benchmarks/Stereo_Read_Interleaved.cpp sources/StereoBufferSSE.cpp) -# Per OS properties -target_link_libraries(bench_read benchmark) - -############################### -add_executable(bench_write benchmarks/Stereo_Write_Interleaved.cpp sources/StereoBufferSSE.cpp) -# Per OS properties -target_link_libraries(bench_write benchmark) - ############################### add_executable(bench_span benchmarks/Spans.cpp) -# Per OS properties target_link_libraries(bench_span benchmark gsl::gsl-lite absl::span) ############################### add_executable(bench_opf_high_vs_low benchmarks/OPF_high_vs_low.cpp) -# Per OS properties target_link_libraries(bench_opf_high_vs_low benchmark gsl::gsl-lite) ############################### add_executable(bench_looping_index benchmarks/Looping_index.cpp) -# Per OS properties target_link_libraries(bench_looping_index benchmark) add_executable(bench_looping_index_2 benchmarks/Looping_index_2.cpp) -# Per OS properties target_link_libraries(bench_looping_index_2 benchmark) + +add_executable(bm_cum_prod benchmarks/Cum_Prod.cpp) +target_link_libraries(bm_cum_prod benchmark) + +add_executable(bm_cum_sum benchmarks/Cum_Sum.cpp) +target_link_libraries(bm_cum_sum benchmark) + +add_executable(bm_write benchmarks/BM_writeInterleaved.cpp sources/SIMDSSE.cpp) +target_link_libraries(bm_write benchmark gsl::gsl-lite) + +add_executable(bm_fill benchmarks/BM_fill.cpp sources/SIMDSSE.cpp) +target_link_libraries(bm_fill benchmark gsl::gsl-lite) \ No newline at end of file diff --git a/benchmarks/BM_fill.cpp b/benchmarks/BM_fill.cpp new file mode 100644 index 00000000..0b427086 --- /dev/null +++ b/benchmarks/BM_fill.cpp @@ -0,0 +1,53 @@ +#include +#include "../sources/SIMDHelpers.h" +#include "../sources/Buffer.h" +#include +#include +#include + +static void Dummy(benchmark::State& state) { + Buffer buffer (state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 1, 2 }; + for (auto _ : state) { + auto fillValue = dist(gen); + benchmark::DoNotOptimize(fillValue); + } +} + +static void Fill_float(benchmark::State& state) { + Buffer buffer (state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 1, 2 }; + for (auto _ : state) { + fill(buffer, dist(gen)); + } +} + +static void Fill_float_SSE(benchmark::State& state) { + Buffer buffer (state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 1, 2 }; + for (auto _ : state) { + fill(buffer, dist(gen)); + } +} + +static void Fill_double(benchmark::State& state) { + Buffer buffer (state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 1, 2 }; + for (auto _ : state) { + fill(buffer, dist(gen)); + } +} + +BENCHMARK(Dummy)->Range((2<<6), (2<<16)); +BENCHMARK(Fill_float)->Range((2<<6), (2<<16)); +BENCHMARK(Fill_float_SSE)->Range((2<<6), (2<<16)); +BENCHMARK(Fill_double)->Range((2<<6), (2<<16)); +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/BM_writeInterleaved.cpp b/benchmarks/BM_writeInterleaved.cpp new file mode 100644 index 00000000..ee32a78f --- /dev/null +++ b/benchmarks/BM_writeInterleaved.cpp @@ -0,0 +1,59 @@ +#include +#include "../sources/SIMDHelpers.h" +#include "../sources/Buffer.h" +#include +#include + +static void Interleaved_Write(benchmark::State& state) { + Buffer inputLeft (state.range(0)); + Buffer inputRight (state.range(0)); + Buffer output (state.range(0) * 2); + std::iota(inputLeft.begin(), inputLeft.end(), 1.0f); + std::iota(inputRight.begin(), inputRight.end(), 1.0f); + + for (auto _ : state) { + writeInterleaved(inputLeft, inputRight, output); + } +} + +static void Interleaved_Write_SSE(benchmark::State& state) { + Buffer inputLeft (state.range(0)); + Buffer inputRight (state.range(0)); + Buffer output (state.range(0) * 2); + std::iota(inputLeft.begin(), inputLeft.end(), 1.0f); + std::iota(inputRight.begin(), inputRight.end(), 1.0f); + for (auto _ : state) { + writeInterleaved(inputLeft, inputRight, output); + benchmark::DoNotOptimize(output); + } +} + +static void Unaligned_Interleaved_Write(benchmark::State& state) { + Buffer inputLeft (state.range(0)); + Buffer inputRight (state.range(0)); + Buffer output (state.range(0) * 2); + std::iota(inputLeft.begin(), inputLeft.end(), 1.0f); + std::iota(inputRight.begin(), inputRight.end(), 1.0f); + for (auto _ : state) { + writeInterleaved(gsl::span(inputLeft).subspan(1) , gsl::span(inputRight).subspan(1), gsl::span(output).subspan(1)); + benchmark::DoNotOptimize(output); + } +} + +static void Unaligned_Interleaved_Write_SSE(benchmark::State& state) { + Buffer inputLeft (state.range(0)); + Buffer inputRight (state.range(0)); + Buffer output (state.range(0) * 2); + std::iota(inputLeft.begin(), inputLeft.end(), 1.0f); + std::iota(inputRight.begin(), inputRight.end(), 1.0f); + for (auto _ : state) { + writeInterleaved(gsl::span(inputLeft).subspan(1) , gsl::span(inputRight).subspan(1), gsl::span(output).subspan(1)); + benchmark::DoNotOptimize(output); + } +} + +BENCHMARK(Interleaved_Write)->Range((8<<10), (8<<20)); +BENCHMARK(Interleaved_Write_SSE)->Range((8<<10), (8<<20)); +BENCHMARK(Unaligned_Interleaved_Write)->Range((8<<10), (8<<20)); +BENCHMARK(Unaligned_Interleaved_Write_SSE)->Range((8<<10), (8<<20)); +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/Cum_Prod.cpp b/benchmarks/Cum_Prod.cpp new file mode 100644 index 00000000..17c30438 --- /dev/null +++ b/benchmarks/Cum_Prod.cpp @@ -0,0 +1,100 @@ +#include +#include +#include "../sources/Intrinsics.h" +#include "../sources/Buffer.h" + + +static void Dummy(benchmark::State& state) { + Buffer output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 1, 2 }; + for (auto _ : state) + { + auto value = dist(gen); + benchmark::DoNotOptimize(value); + } +} + +static void Straight(benchmark::State& state) { + Buffer output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 1, 2 }; + for (auto _ : state) + { + auto value = dist(gen); + for (auto& out: output) + { + out = value; + value *= value; + } + } +} + +static void SIMD(benchmark::State& state) { + Buffer output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 1, 2 }; + for (auto _ : state) + { + auto value = dist(gen); + auto out = output.begin(); + const auto alignedEnd = state.range(0) - (state.range(0) & 3); + + auto baseReg = _mm_set_ps1(value); + auto prodReg = _mm_set_ps(value*value*value*value, value*value*value, value*value, value); + + while (out < output.data() + alignedEnd) + { + baseReg = _mm_mul_ps(baseReg, prodReg); + _mm_storeu_ps(out, baseReg); + baseReg = _mm_shuffle_ps(baseReg, baseReg, _MM_SHUFFLE(3, 3, 3, 3)); + out += 4; + } + + while (out < output.end()) + { + *out++ = value; + value *= value; + } + } +} + +static void SIMD_unaligned(benchmark::State& state) { + Buffer output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 1, 2 }; + for (auto _ : state) + { + auto value = dist(gen); + auto out = output.begin() + 1; + const auto alignedEnd = state.range(0) - (state.range(0) & 3); + + auto baseReg = _mm_set_ps1(value); + auto prodReg = _mm_set_ps(value*value*value*value, value*value*value, value*value, value); + + while (out < output.data() + alignedEnd) + { + baseReg = _mm_mul_ps(baseReg, prodReg); + _mm_storeu_ps(out, baseReg); + baseReg = _mm_shuffle_ps(baseReg, baseReg, _MM_SHUFFLE(3, 3, 3, 3)); + out += 4; + } + + while (out < output.end()) + { + *out++ = value; + value *= value; + } + } +} + +// Register the function as a benchmark +BENCHMARK(Dummy)->RangeMultiplier(2)->Range((1 << 2), (1 << 8)); +BENCHMARK(Straight)->RangeMultiplier(2)->Range((1 << 2), (1 << 8)); +BENCHMARK(SIMD)->RangeMultiplier(2)->Range((1 << 2), (1 << 8)); +BENCHMARK(SIMD_unaligned)->RangeMultiplier(2)->Range((1 << 2), (1 << 8)); +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/Cum_Sum.cpp b/benchmarks/Cum_Sum.cpp new file mode 100644 index 00000000..d4d8746f --- /dev/null +++ b/benchmarks/Cum_Sum.cpp @@ -0,0 +1,100 @@ +#include +#include +#include "../sources/Intrinsics.h" +#include "../sources/Buffer.h" + + +static void Dummy(benchmark::State& state) { + Buffer output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 0, 1 }; + for (auto _ : state) + { + auto value = dist(gen); + benchmark::DoNotOptimize(value); + } +} + +static void Straight(benchmark::State& state) { + Buffer output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 0, 1 }; + for (auto _ : state) + { + auto value = dist(gen); + for (auto& out: output) + { + out = value; + value *= value; + } + } +} + +static void SIMD(benchmark::State& state) { + Buffer output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 0, 1 }; + for (auto _ : state) + { + auto value = dist(gen); + auto out = output.begin(); + const auto alignedEnd = state.range(0) - (state.range(0) & 3); + + auto baseReg = _mm_set_ps1(value); + auto prodReg = _mm_set_ps(value+value+value+value, value+value+value, value+value, value); + + while (out < output.data() + alignedEnd) + { + baseReg = _mm_add_ps(baseReg, prodReg); + _mm_storeu_ps(out, baseReg); + baseReg = _mm_shuffle_ps(baseReg, baseReg, _MM_SHUFFLE(3, 3, 3, 3)); + out += 4; + } + + while (out < output.end()) + { + *out++ = value; + value *= value; + } + } +} + +static void SIMD_unaligned(benchmark::State& state) { + Buffer output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 0, 1 }; + for (auto _ : state) + { + auto value = dist(gen); + auto out = output.begin() + 1; + const auto alignedEnd = state.range(0) - (state.range(0) & 3); + + auto baseReg = _mm_set_ps1(value); + auto prodReg = _mm_set_ps(value+value+value+value, value+value+value, value+value, value); + + while (out < output.data() + alignedEnd) + { + baseReg = _mm_add_ps(baseReg, prodReg); + _mm_storeu_ps(out, baseReg); + baseReg = _mm_shuffle_ps(baseReg, baseReg, _MM_SHUFFLE(3, 3, 3, 3)); + out += 4; + } + + while (out < output.end()) + { + *out++ = value; + value *= value; + } + } +} + +// Register the function as a benchmark +BENCHMARK(Dummy)->RangeMultiplier(2)->Range((1 << 2), (1 << 8)); +BENCHMARK(Straight)->RangeMultiplier(2)->Range((1 << 2), (1 << 8)); +BENCHMARK(SIMD)->RangeMultiplier(2)->Range((1 << 2), (1 << 8)); +BENCHMARK(SIMD_unaligned)->RangeMultiplier(2)->Range((1 << 2), (1 << 8)); +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/Looping_index_2.cpp b/benchmarks/Looping_index_2.cpp index 69b39c3f..76883ec1 100644 --- a/benchmarks/Looping_index_2.cpp +++ b/benchmarks/Looping_index_2.cpp @@ -8,7 +8,7 @@ // In this one we have an array of indices constexpr int loopOffset { 5 }; -constexpr int loopPoint { 51 }; +constexpr int loopPoint { 1076 }; constexpr int loopBack { loopPoint - loopOffset }; constexpr float maxJump { 4 }; diff --git a/benchmarks/Stereo_Fill.cpp b/benchmarks/Stereo_Fill.cpp deleted file mode 100644 index 7f55cd0d..00000000 --- a/benchmarks/Stereo_Fill.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include "../sources/StereoBuffer.h" -#include -#include - -static void Fill_float(benchmark::State& state) { - StereoBuffer buffer (100001); - float fillValue = 0.0f; - for (auto _ : state) { - buffer.fill(fillValue); - fillValue += 1.0f; - } -} -// Register the function as a benchmark -BENCHMARK(Fill_float); - -static void Fill_float_SSE(benchmark::State& state) { - StereoBuffer buffer (100001); - float fillValue = 0.0f; - for (auto _ : state) { - buffer.fill(fillValue); - fillValue += 1.0f; - } -} -// Register the function as a benchmark -BENCHMARK(Fill_float_SSE); - -static void Fill_double(benchmark::State& state) { - StereoBuffer buffer (100001); - double fillValue = 0.0; - for (auto _ : state) { - buffer.fill(fillValue); - fillValue += 1.0; - } -} -// Register the function as a benchmark -BENCHMARK(Fill_double); - -BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/Stereo_Read_Interleaved.cpp b/benchmarks/Stereo_Read_Interleaved.cpp deleted file mode 100644 index 945393fd..00000000 --- a/benchmarks/Stereo_Read_Interleaved.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include "../sources/StereoBuffer.h" -#include -#include - -static void Interleaved_Read(benchmark::State& state) { - StereoBuffer buffer (state.range(0)); - std::vector input (state.range(0) * 2); - std::iota(input.begin(), input.end(), 1.0f); - for (auto _ : state) { - buffer.readInterleaved(input.data(), state.range(0)); - } -} -BENCHMARK(Interleaved_Read)->Range((8<<10) + 3, (8<<20) + 3); - -static void Interleaved_Read_SSE(benchmark::State& state) { - StereoBuffer buffer (state.range(0)); - std::vector input (state.range(0) * 2); - std::iota(input.begin(), input.end(), 1.0f); - for (auto _ : state) { - buffer.readInterleaved(input.data(), state.range(0)); - } -} -BENCHMARK(Interleaved_Read_SSE)->Range((8<<10) + 3, (8<<20) + 3); - -static void Unaligned_Interleaved_Read_SSE(benchmark::State& state) { - StereoBuffer buffer (state.range(0)); - std::vector input (state.range(0) * 2); - std::iota(input.begin(), input.end(), 1.0f); - for (auto _ : state) { - buffer.readInterleaved(input.data() + 1, state.range(0) - 1); - } -} -BENCHMARK(Unaligned_Interleaved_Read_SSE)->Range((8<<10) + 3, (8<<20) + 3); -BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/Stereo_Write_Interleaved.cpp b/benchmarks/Stereo_Write_Interleaved.cpp deleted file mode 100644 index 393d108c..00000000 --- a/benchmarks/Stereo_Write_Interleaved.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include "../sources/StereoBuffer.h" -#include -#include - -static void Interleaved_Write(benchmark::State& state) { - StereoBuffer buffer (state.range(0)); - std::vector input (state.range(0) * 2); - buffer.readInterleaved(input.data(), state.range(0)); - std::vector output (state.range(0) * 2); - std::iota(input.begin(), input.end(), 1.0f); - for (auto _ : state) { - buffer.writeInterleaved(output.data(), state.range(0)); - } -} -BENCHMARK(Interleaved_Write)->Range((8<<10) + 3, (8<<20) + 3); - -static void Interleaved_Write_SSE(benchmark::State& state) { - StereoBuffer buffer (state.range(0)); - std::vector input (state.range(0) * 2); - buffer.readInterleaved(input.data(), state.range(0)); - std::vector output (state.range(0) * 2); - std::iota(input.begin(), input.end(), 1.0f); - for (auto _ : state) { - buffer.writeInterleaved(output.data(), state.range(0)); - } -} -BENCHMARK(Interleaved_Write_SSE)->Range((8<<10) + 3, (8<<20) + 3); - -static void Unaligned_Interleaved_Write(benchmark::State& state) { - StereoBuffer buffer (state.range(0)); - std::vector input (state.range(0) * 2); - buffer.readInterleaved(input.data(), state.range(0)); - std::vector output (state.range(0) * 2 + 1); - std::iota(input.begin(), input.end(), 1.0f); - for (auto _ : state) { - buffer.writeInterleaved(output.data() + 1, state.range(0)); - } -} -BENCHMARK(Unaligned_Interleaved_Write)->Range((8<<10) + 3, (8<<20) + 3); - -static void Unaligned_Interleaved_Write_SSE(benchmark::State& state) { - StereoBuffer buffer (state.range(0)); - std::vector input (state.range(0) * 2); - buffer.readInterleaved(input.data(), state.range(0)); - std::vector output (state.range(0) * 2 + 1); - std::iota(input.begin(), input.end(), 1.0f); - for (auto _ : state) { - buffer.writeInterleaved(output.data() + 1, state.range(0)); - } -} -BENCHMARK(Unaligned_Interleaved_Write_SSE)->Range((8<<10) + 3, (8<<20) + 3); -BENCHMARK_MAIN(); \ No newline at end of file diff --git a/sources/ADSREnvelope.h b/sources/ADSREnvelope.h new file mode 100644 index 00000000..a5e1e8cd --- /dev/null +++ b/sources/ADSREnvelope.h @@ -0,0 +1,40 @@ +#include "Globals.h" + +namespace sfz +{ + +template +class ADSREnvelope +{ +public: + struct Description + { + Description() + : depth(1) {} + Descrition(Type depth) + : depth(depth) {} + + int delay { 0 }; + int attack { 0 }; + int decay { 0 }; + int release { 0 }; + int hold { 0 }; + float start { 0 }; + float sustain { 0 }; + Type depth; + }; + + ADSREnvelope() = default; + void reset(Description desc) + { + + } +private: + enum class State + { + Delay, Attack, Hold, Sustain, Release, Done + }; + State currentState { Done }; +}; + +} \ No newline at end of file diff --git a/sources/Buffer.h b/sources/Buffer.h index dbb1dcbd..2104c4da 100644 --- a/sources/Buffer.h +++ b/sources/Buffer.h @@ -8,7 +8,19 @@ template class Buffer { public: - Buffer() { } + using value_type = std::remove_cv_t; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using size_type = size_t; + using difference_type = ptrdiff_t; + + constexpr Buffer() { } Buffer(size_t size) { resize(size); @@ -22,14 +34,14 @@ public: } auto tempSize = newSize + 2 * AlignmentMask; // To ensure that we have leeway at the beginning and at the end - auto* newData = paddedData != nullptr ? std::realloc(paddedData, tempSize * sizeof(Type)) : std::malloc(tempSize * sizeof(Type)); + auto* newData = paddedData != nullptr ? std::realloc(paddedData, tempSize * sizeof(value_type)) : std::malloc(tempSize * sizeof(value_type)); if (newData == nullptr) return false; largerSize = tempSize; alignedSize = newSize; - paddedData = static_cast(newData); - normalData = static_cast(std::align(Alignment, alignedSize, newData, tempSize)); + paddedData = static_cast(newData); + normalData = static_cast(std::align(Alignment, alignedSize, newData, tempSize)); normalEnd = normalData + alignedSize; if (auto endMisalignment = (alignedSize & TypeAlignmentMask); endMisalignment != 0) _alignedEnd = normalEnd + Alignment - endMisalignment; @@ -38,7 +50,6 @@ public: return true; } - Type* data() { return normalData; } void clear() { largerSize = 0; @@ -54,22 +65,23 @@ public: } Type& operator[](int idx) { return *(normalData + idx); } - size_t size() const noexcept { return alignedSize; } - bool empty() const noexcept { return alignedSize == 0; } - Type* begin() noexcept { return data(); } - Type* end() noexcept { return normalEnd; } - Type* alignedEnd() noexcept { return _alignedEnd; } + constexpr pointer data() const noexcept { return normalData; } + constexpr size_type size() const noexcept { return alignedSize; } + constexpr bool empty() const noexcept { return alignedSize == 0; } + constexpr iterator begin() noexcept { return data(); } + constexpr iterator end() noexcept { return normalEnd; } + constexpr pointer alignedEnd() noexcept { return _alignedEnd; } private: static constexpr auto AlignmentMask { Alignment - 1 }; - static constexpr auto TypeAlignment { Alignment / sizeof(Type) }; + static constexpr auto TypeAlignment { Alignment / sizeof(value_type) }; static constexpr auto TypeAlignmentMask { TypeAlignment - 1 }; - static_assert(std::is_arithmetic::value, "Type should be arithmetic"); + static_assert(std::is_arithmetic::value, "Type should be arithmetic"); static_assert(Alignment == 0 || Alignment == 4 || Alignment == 8 || Alignment == 16, "Bad alignment value"); - static_assert(TypeAlignment * sizeof(Type) == Alignment, "The alignment does not appear to be divided by the size of the Type"); - size_t largerSize { 0 }; - size_t alignedSize { 0 }; - Type* normalData { nullptr }; - Type* paddedData { nullptr }; - Type* normalEnd { nullptr }; - Type* _alignedEnd { nullptr }; + static_assert(TypeAlignment * sizeof(value_type) == Alignment, "The alignment does not appear to be divided by the size of the Type"); + size_type largerSize { 0 }; + size_type alignedSize { 0 }; + pointer normalData { nullptr }; + pointer paddedData { nullptr }; + pointer normalEnd { nullptr }; + pointer _alignedEnd { nullptr }; }; \ No newline at end of file diff --git a/sources/FilePool.cpp b/sources/FilePool.cpp index 79e4e74d..1e36fc98 100644 --- a/sources/FilePool.cpp +++ b/sources/FilePool.cpp @@ -1,4 +1,5 @@ #include "FilePool.h" +#include "gsl/gsl-lite.hpp" #include using namespace std::chrono_literals; @@ -23,7 +24,7 @@ std::optional sfz::FilePool::getFileInformation( auto preloadedSize = std::min(returnedValue.end, static_cast(config::preloadSize)); returnedValue.preloadedData = std::make_shared>(preloadedSize); sndFile.readf(tempReadBuffer.data(), preloadedSize); - returnedValue.preloadedData->readInterleaved(tempReadBuffer.data(), preloadedSize); + returnedValue.preloadedData->readInterleaved(gsl::make_span(tempReadBuffer).first(preloadedSize)); preloadedData[filename] = returnedValue.preloadedData; // char buffer [2048] ; // sndFile.command(SFC_GET_LOG_INFO, buffer, sizeof(buffer)) ; @@ -65,7 +66,7 @@ void sfz::FilePool::loadingThread() auto fileLoaded = std::make_unique>(fileToLoad.numFrames); auto readBuffer = std::make_unique>(fileToLoad.numFrames * 2); sndFile.readf(readBuffer->data(), fileToLoad.numFrames); - fileLoaded->readInterleaved(readBuffer->data(), fileToLoad.numFrames); + fileLoaded->readInterleaved(*readBuffer); fileToLoad.voice->setFileData(std::move(fileLoaded)); } diff --git a/sources/Globals.h b/sources/Globals.h index 335ee7bb..8588bae2 100644 --- a/sources/Globals.h +++ b/sources/Globals.h @@ -5,27 +5,30 @@ namespace sfz namespace config { - inline constexpr double defaultSampleRate { 48000 }; - inline constexpr int defaultSamplesPerBlock { 1024 }; - inline constexpr int preloadSize { 32768 }; - inline constexpr int numChannels { 2 }; - inline constexpr int numVoices { 64 }; - inline constexpr int numLoadingThreads { 4 }; - inline constexpr int centPerSemitone { 100 }; - inline constexpr float virtuallyZero { 0.00005f }; - inline constexpr double fastReleaseDuration { 0.01 }; - inline constexpr char defineCharacter { '$' }; - inline constexpr int oversamplingFactor { 2 }; + constexpr double defaultSampleRate { 48000 }; + constexpr int defaultSamplesPerBlock { 1024 }; + constexpr int preloadSize { 32768 }; + constexpr int numChannels { 2 }; + constexpr int numVoices { 64 }; + constexpr int numLoadingThreads { 4 }; + constexpr int centPerSemitone { 100 }; + constexpr float virtuallyZero { 0.00005f }; + constexpr double fastReleaseDuration { 0.01 }; + constexpr char defineCharacter { '$' }; + constexpr int oversamplingFactor { 2 }; } // namespace config } // namespace sfz namespace SIMDConfig { - inline constexpr unsigned int defaultAlignment { 16 }; + constexpr unsigned int defaultAlignment { 16 }; + constexpr bool writeInterleaved { true }; + constexpr bool readInterleaved { true }; + constexpr bool fill { false }; #if USE_SIMD - inline constexpr bool useSIMD { true }; + constexpr bool useSIMD { true }; #else - inline constexpr bool useSIMD { false }; + constexpr bool useSIMD { false }; #endif } \ No newline at end of file diff --git a/sources/LinearEnvelope.h b/sources/LinearEnvelope.h new file mode 100644 index 00000000..8950ad0b --- /dev/null +++ b/sources/LinearEnvelope.h @@ -0,0 +1,13 @@ +#include "Globals.h" +namespace sfz +{ + +class LinearEnvelope +{ +public: + +private: + +}; + +} \ No newline at end of file diff --git a/sources/SIMDDummy.cpp b/sources/SIMDDummy.cpp new file mode 100644 index 00000000..6ca02329 --- /dev/null +++ b/sources/SIMDDummy.cpp @@ -0,0 +1,35 @@ +#include "SIMDHelpers.h" +#include "Helpers.h" + +template<> +void readInterleaved(gsl::span input, gsl::span outputLeft, gsl::span outputRight) noexcept +{ + readInterleaved(input, outputLeft, outputRight); +} + +template<> +void writeInterleaved(gsl::span inputLeft, gsl::span inputRight, gsl::span output) noexcept +{ + writeInterleaved(inputLeft, inputRight, output); +} + +// template +// void loopingSFZIndex(gsl::span inputLeft, gsl::span inputRight, gsl::span output); + +// template +// void linearRamp(gsl::span output, Type start, Type end); + +// template +// void exponentialRamp(gsl::span output, Type start, Type end); + +// template +// void applyGain(Type gain, gsl::span output); + +// template +// void applyGain(gsl::span output, gsl::span output); + +template<> +void fill(gsl::span output, float value) noexcept +{ + fill(output, value); +} \ No newline at end of file diff --git a/sources/SIMDHelpers.h b/sources/SIMDHelpers.h new file mode 100644 index 00000000..bf65424b --- /dev/null +++ b/sources/SIMDHelpers.h @@ -0,0 +1,67 @@ +#include "gsl/gsl-lite.hpp" +#include "Globals.h" +#include "Helpers.h" + +template +void readInterleaved(gsl::span input, gsl::span outputLeft, gsl::span outputRight) noexcept +{ + // The size of the output is not big enough for the input... + ASSERT(outputLeft.size() >= input.size() / 2); + ASSERT(outputRight.size() >= input.size() / 2); + + auto* in = input.begin(); + auto* lOut = outputLeft.begin(); + auto* rOut = outputRight.begin(); + while (in < (input.end() - 1) && lOut < outputLeft.end() && rOut < outputRight.end()) + { + *lOut++ = *in++; + *rOut++ = *in++; + } +} + +template +void writeInterleaved(gsl::span inputLeft, gsl::span inputRight, gsl::span output) noexcept +{ + ASSERT(inputLeft.size() <= output.size() / 2); + ASSERT(inputRight.size() <= output.size() / 2); + + auto* lIn = inputLeft.begin(); + auto* rIn = inputRight.begin(); + auto* out = output.begin(); + while (lIn < inputLeft.end() && rIn < inputRight.end() && out < (output.end() - 1)) + { + *out++ = *lIn++; + *out++ = *rIn++; + } +} + +// Specializations +template<> +void writeInterleaved(gsl::span inputLeft, gsl::span inputRight, gsl::span output) noexcept; +template<> +void readInterleaved(gsl::span input, gsl::span outputLeft, gsl::span outputRight) noexcept; + +template +void fill(gsl::span output, T value) noexcept +{ + std::fill(output.begin(), output.end(), value); +} + +template<> +void fill(gsl::span output, float value) noexcept; + +template +void loopingSFZIndex(gsl::span inputLeft, gsl::span inputRight, gsl::span output); + +template +void linearRamp(gsl::span output, T start, T end); + +template +void exponentialRamp(gsl::span output, T start, T end); + +template +void applyGain(T gain, gsl::span output); + +template +void applyGain(gsl::span gain, gsl::span output); + diff --git a/sources/SIMDSSE.cpp b/sources/SIMDSSE.cpp new file mode 100644 index 00000000..6bceb1e4 --- /dev/null +++ b/sources/SIMDSSE.cpp @@ -0,0 +1,107 @@ +#include "SIMDHelpers.h" +#include "Helpers.h" +#include "x86intrin.h" + +constexpr int TypeAlignment { 4 }; +using Type = float; + +template<> +void readInterleaved(gsl::span input, gsl::span outputLeft, gsl::span outputRight) noexcept +{ + // The size of the outputs is not big enough for the input... + ASSERT(outputLeft.size() >= input.size() / 2); + ASSERT(outputRight.size() >= input.size() / 2); + // Input is too small + ASSERT(input.size() > 1); + + auto* in = input.begin(); + auto* lOut = outputLeft.begin(); + auto* rOut = outputRight.begin(); + const int unalignedEnd = input.size() & (2 * TypeAlignment - 1); + const int lastAligned = input.size() - unalignedEnd; + auto* inputSentinel = in + lastAligned; + while (in < inputSentinel && lOut < outputLeft.end() && rOut < outputRight.end()) + { + auto register0 = _mm_loadu_ps(in); + in += TypeAlignment; + auto register1 = _mm_loadu_ps(in); + in += TypeAlignment; + auto register2 = register0; + // register 2 holds the copy of register 0 that is going to get erased by the first operation + // Remember that the bit mask reads from the end; 10 00 10 00 means + // "take 0 from a, take 2 from a, take 0 from b, take 2 from b" + register0 = _mm_shuffle_ps(register0, register1, 0b10001000); + register1 = _mm_shuffle_ps(register2, register1, 0b11011101); + _mm_storeu_ps(lOut, register0); + _mm_storeu_ps(rOut, register1); + lOut += TypeAlignment; + rOut += TypeAlignment; + } + + inputSentinel = input.end() - 1; + while (in < inputSentinel && lOut < outputLeft.end() && rOut < outputRight.end()) + { + *lOut++ = *in++; + *rOut++ = *in++; + } +} + +template<> +void writeInterleaved(gsl::span inputLeft, gsl::span inputRight, gsl::span output) noexcept +{ + // The size of the output is not big enough for the inputs... + ASSERT(inputLeft.size() <= output.size() / 2); + ASSERT(inputRight.size() <= output.size() / 2); + + auto* lIn = inputLeft.begin(); + auto* rIn = inputRight.begin(); + auto* out = output.begin(); + + const int residualLeft = inputLeft.size() & (TypeAlignment - 1); + const int residualRight = inputRight.size() & (TypeAlignment - 1); + const auto* leftSentinel = lIn + inputLeft.size() - residualLeft; + const auto* rightSentinel = rIn + inputRight.size() - residualRight; + const auto* outputSentinel = output.end() - 1; + + while (lIn < leftSentinel && rIn < rightSentinel && out < outputSentinel) + { + const auto lInRegister = _mm_loadu_ps(lIn); + const auto rInRegister = _mm_loadu_ps(rIn); + + const auto outRegister1 = _mm_unpacklo_ps(lInRegister, rInRegister); + _mm_storeu_ps(out, outRegister1); + out += TypeAlignment; + + const auto outRegister2 = _mm_unpackhi_ps(lInRegister, rInRegister); + _mm_storeu_ps(out, outRegister2); + out += TypeAlignment; + + lIn += TypeAlignment; + rIn += TypeAlignment; + } + + while (lIn < inputLeft.end() && rIn < inputRight.end() && out < outputSentinel) + { + + *out++ = *lIn++; + *out++ = *rIn++; + } +} + +template<> +void fill(gsl::span output, float value) noexcept +{ + const auto mmValue = _mm_set_ps1(value); + auto* out = output.begin(); + const int residual = output.size() & (TypeAlignment - 1); + const auto* sentinel = output.end() - residual; + + while (out < sentinel) // we should only need to test a single channel + { + _mm_storeu_ps(out, mmValue); + out += TypeAlignment; + } + + while (out < output.end()) + *out++ = value; +} \ No newline at end of file diff --git a/sources/StereoBuffer.h b/sources/StereoBuffer.h index 6aeec758..8979fc59 100644 --- a/sources/StereoBuffer.h +++ b/sources/StereoBuffer.h @@ -2,9 +2,11 @@ #include "Buffer.h" #include "Helpers.h" #include "Globals.h" +#include "gsl/gsl-lite.hpp" #include #include #include +#include "SIMDHelpers.h" enum class Channel {left, right}; @@ -46,42 +48,22 @@ public: } } - template void fill(Type value) noexcept { - std::fill(begin(Channel::left), end(Channel::left), value); - std::fill(begin(Channel::right), end(Channel::right), value); + ::fill(leftBuffer, value); + ::fill(rightBuffer, value); } - template - void readInterleaved(Type* input, int numFrames) noexcept + void readInterleaved(gsl::span input) noexcept { - auto* in = input; - auto* end = input + numChannels * numFrames; - auto [lOut, rOut] = getChannels(); - while (in < end) - { - *lOut = *in; - in++; - *rOut = *in; - in++; - lOut += 1; - rOut += 1; - } + ASSERT(input.size() <= numChannels * numFrames); + ::readInterleaved(input, leftBuffer, rightBuffer); } - template - void writeInterleaved(Type* output, int numFrames) noexcept + void writeInterleaved(gsl::span output) noexcept { - ASSERT(numFrames <= this->numFrames); - auto [lIn, rIn] = getChannels(); - auto* out = output; - auto* end = output + numChannels * numFrames; - while (out < end) - { - *out++ = *lIn++; - *out++ = *rIn++; - } + ASSERT(output.size() >= numChannels * numFrames); + ::writeInterleaved(leftBuffer, rightBuffer, output); } Type* getChannel(Channel channel) noexcept @@ -148,11 +130,3 @@ private: Buffer rightBuffer {}; Type trash { 0 }; }; - -template <> -template <> -void StereoBuffer::readInterleaved(float *input, int numFrames) noexcept; - -template <> -template <> -void StereoBuffer::fill(float value) noexcept; \ No newline at end of file diff --git a/sources/StereoBufferSSE.cpp b/sources/StereoBufferSSE.cpp deleted file mode 100644 index 099d47c2..00000000 --- a/sources/StereoBufferSSE.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "StereoBuffer.h" -#include "Intrinsics.h" - -template <> -template <> -void StereoBuffer::readInterleaved(float *input, int numFrames) noexcept -{ - // The number of frames to read has to fit! - ASSERT(this->numFrames >= numFrames); - const int residualFrames = numFrames & (2 * TypeAlignment - 1); - const int lastAligned = numFrames - residualFrames; - float *in = input; - auto [lOut, rOut] = getChannels(); - const float *end = input + 2 * lastAligned; - while (in < end) - { - auto register0 = _mm_loadu_ps(in); - in += 4; - auto register1 = _mm_loadu_ps(in); - in += 4; - auto register2 = register0; - // register 2 holds the copy of register 0 that is going to get erased by the first operation - // Remember that the bit mask reads from the end; 10 00 10 00 means - // "take 0 from a, take 2 from a, take 0 from b, take 2 from b" - register0 = _mm_shuffle_ps(register0, register1, 0b10001000); - register1 = _mm_shuffle_ps(register2, register1, 0b11011101); - _mm_store_ps(lOut, register0); - _mm_store_ps(rOut, register1); - lOut += 4; - rOut += 4; - } - end = input + numChannels * numFrames; - while (in < end) - { - *lOut = *in; - in++; - *rOut = *in; - in++; - lOut += 1; - rOut += 1; - } -} - -template<> -template<> -void StereoBuffer::fill(float value) noexcept -{ - const __m128 mmValue = _mm_set_ps1(value); - auto [lBegin, rBegin] = getChannels(); - auto [lEnd, rEnd] = alignedEnds(); - // Cast to m128 so that pointer arithmetics make sense - auto mmLeft = reinterpret_cast<__m128*>(lBegin); - auto mmRight = reinterpret_cast<__m128*>(rBegin); - while (mmLeft < reinterpret_cast<__m128*>(lEnd)) // we should only need to test a single channel - { - _mm_store_ps(reinterpret_cast(mmLeft), mmValue); - _mm_store_ps(reinterpret_cast(mmRight), mmValue); - mmLeft++; - mmRight++; - } -} - -template<> -template<> -void StereoBuffer::writeInterleaved(float* output, int numFrames) noexcept -{ - ASSERT(numFrames <= this->numFrames); - const int residualFrames = numFrames & (TypeAlignment - 1); - const int lastAligned = numFrames - residualFrames; - auto [lIn, rIn] = getChannels(); - auto* out = output; - const auto* sseEnd = lIn + lastAligned; - while (lIn < sseEnd) - { - const auto lInRegister = _mm_load_ps(lIn); - const auto rInRegister = _mm_load_ps(rIn); - - const auto outRegister1 = _mm_unpacklo_ps(lInRegister, rInRegister); - _mm_storeu_ps(out, outRegister1); - out += TypeAlignment; - const auto outRegister2 = _mm_unpackhi_ps(lInRegister, rInRegister); - _mm_storeu_ps(out, outRegister2); - out += TypeAlignment; - - lIn += TypeAlignment; - rIn += TypeAlignment; - } - - const auto* end = output + numChannels * numFrames; - while (out < end) - { - *out++ = *lIn++; - *out++ = *rIn++; - } -} \ No newline at end of file diff --git a/sources/Synth.h b/sources/Synth.h index 9e1b5598..7188ebb0 100644 --- a/sources/Synth.h +++ b/sources/Synth.h @@ -22,7 +22,8 @@ public: const Region* getRegionView(int idx) const noexcept { return (size_t)idx < regions.size() ? regions[idx].get() : nullptr; } auto getUnknownOpcodes() { return unknownOpcodes; } size_t getNumPreloadedSamples() { return filePool.getNumPreloadedSamples(); } - + void prepareToPlay(int samplesPerBlock, double sampleRate); + void renderBlock(StereoBuffer& buffer); protected: void callback(std::string_view header, std::vector members) final; private: diff --git a/tests/SIMDHelpersT.cpp b/tests/SIMDHelpersT.cpp new file mode 100644 index 00000000..42a08393 --- /dev/null +++ b/tests/SIMDHelpersT.cpp @@ -0,0 +1,267 @@ +#include "catch2/catch.hpp" +#include "../sources/SIMDHelpers.h" +#include +using namespace Catch::literals; + +constexpr int smallBufferSize { 3 }; +constexpr int bigBufferSize { 4095 }; +constexpr int medBufferSize { 127 }; +constexpr double fillValue { 1.3 }; + +TEST_CASE("[Helpers] fill() - Manual buffer") +{ + std::vector buffer (5); + std::vector expected { fillValue, fillValue, fillValue, fillValue, fillValue }; + fill(buffer, fillValue); + REQUIRE(buffer == expected); +} + +TEST_CASE("[Helpers] fill() - Small buffer") +{ + std::vector buffer (smallBufferSize); + std::vector expected (smallBufferSize); + std::fill(expected.begin(), expected.end(), fillValue); + + fill(buffer, fillValue); + REQUIRE(buffer == expected); +} + +TEST_CASE("[Helpers] fill() - Big buffer") +{ + std::vector buffer (bigBufferSize); + std::vector expected (bigBufferSize); + std::fill(expected.begin(), expected.end(), fillValue); + + fill(buffer, fillValue); + REQUIRE(buffer == expected); +} + +TEST_CASE("[Helpers] fill() - Small buffer -- SIMD") +{ + std::vector buffer (smallBufferSize); + std::vector expected (smallBufferSize); + std::fill(expected.begin(), expected.end(), fillValue); + + fill(buffer, fillValue); + REQUIRE(buffer == expected); +} + +TEST_CASE("[Helpers] fill() - Big buffer -- SIMD") +{ + std::vector buffer (bigBufferSize); + std::vector expected (bigBufferSize); + std::fill(expected.begin(), expected.end(), fillValue); + + fill(buffer, fillValue); + REQUIRE(buffer == expected); +} + +TEST_CASE("[Helpers] fill() - Small buffer -- doubles") +{ + std::vector buffer (smallBufferSize); + std::vector expected (smallBufferSize); + std::fill(expected.begin(), expected.end(), fillValue); + + fill(buffer, fillValue); + REQUIRE(buffer == expected); +} + +TEST_CASE("[Helpers] fill() - Big buffer -- doubles") +{ + std::vector buffer (bigBufferSize); + std::vector expected (bigBufferSize); + std::fill(expected.begin(), expected.end(), fillValue); + + fill(buffer, fillValue); + REQUIRE(buffer == expected); +} + + +TEST_CASE("[Helpers] Interleaved read") +{ + std::array input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f}; + std::array expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f }; + std::array leftOutput { 0.0f }; + std::array rightOutput { 0.0f }; + readInterleaved(input, leftOutput, rightOutput); + std::array real { 0.0f }; + + auto realIdx = 0; + for (auto value: leftOutput) + real[realIdx++] = value; + for (auto value: rightOutput) + real[realIdx++] = value; + REQUIRE( real == expected ); +} + +TEST_CASE("[Helpers] Interleaved read unaligned end") +{ + std::array input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f}; + std::array expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f}; + std::array leftOutput { 0.0f }; + std::array rightOutput { 0.0f }; + readInterleaved(input, leftOutput, rightOutput); + std::array real { 0.0f }; + + auto realIdx = 0; + for (auto value: leftOutput) + real[realIdx++] = value; + for (auto value: rightOutput) + real[realIdx++] = value; + REQUIRE( real == expected ); +} + +TEST_CASE("[Helpers] Small interleaved read unaligned end") +{ + std::array input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f}; + std::array expected = { 0.0f, 1.0f, 2.0f, 10.0f, 11.0f, 12.0f}; + std::array leftOutput { 0.0f }; + std::array rightOutput { 0.0f }; + readInterleaved(input, leftOutput, rightOutput); + std::array real { 0.0f }; + + auto realIdx = 0; + for (auto value: leftOutput) + real[realIdx++] = value; + for (auto value: rightOutput) + real[realIdx++] = value; + REQUIRE( real == expected ); +} + +TEST_CASE("[Helpers] Interleaved read -- SIMD") +{ + std::array input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f}; + std::array expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f }; + std::array leftOutput { 0.0f }; + std::array rightOutput { 0.0f }; + readInterleaved(input, leftOutput, rightOutput); + std::array real { 0.0f }; + + auto realIdx = 0; + for (auto value: leftOutput) + real[realIdx++] = value; + for (auto value: rightOutput) + real[realIdx++] = value; + REQUIRE( real == expected ); +} + +TEST_CASE("[Helpers] Interleaved read unaligned end -- SIMD") +{ + std::array input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f}; + std::array expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f}; + std::array leftOutput { 0.0f }; + std::array rightOutput { 0.0f }; + readInterleaved(input, leftOutput, rightOutput); + std::array real { 0.0f }; + + auto realIdx = 0; + for (auto value: leftOutput) + real[realIdx++] = value; + for (auto value: rightOutput) + real[realIdx++] = value; + REQUIRE( real == expected ); +} + +TEST_CASE("[Helpers] Small interleaved read unaligned end -- SIMD") +{ + std::array input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f}; + std::array expected = { 0.0f, 1.0f, 2.0f, 10.0f, 11.0f, 12.0f}; + std::array leftOutput { 0.0f }; + std::array rightOutput { 0.0f }; + readInterleaved(input, leftOutput, rightOutput); + std::array real { 0.0f }; + + auto realIdx = 0; + for (auto value: leftOutput) + real[realIdx++] = value; + for (auto value: rightOutput) + real[realIdx++] = value; + REQUIRE( real == expected ); +} + +TEST_CASE("[Helpers] Interleaved read SIMD vs Scalar") +{ + std::array input; + std::array leftOutputScalar; + std::array rightOutputScalar; + std::array leftOutputSIMD; + std::array rightOutputSIMD; + std::iota(input.begin(), input.end(), 0.0f); + readInterleaved(input, leftOutputScalar, rightOutputScalar); + readInterleaved(input, leftOutputSIMD, rightOutputSIMD); + REQUIRE( leftOutputScalar == leftOutputSIMD ); + REQUIRE( rightOutputScalar == rightOutputSIMD ); +} + +TEST_CASE("[Helpers] Interleaved write") +{ + std::array leftInput { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, }; + std::array rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f }; + std::array output; + std::array expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f}; + writeInterleaved(leftInput, rightInput, output); + REQUIRE( output == expected ); +} + +TEST_CASE("[Helpers] Interleaved write unaligned end") +{ + std::array leftInput { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f}; + std::array rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f }; + std::array output; + std::array expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f}; + writeInterleaved(leftInput, rightInput, output); + REQUIRE( output == expected ); +} + +TEST_CASE("[Helpers] Small interleaved write unaligned end") +{ + std::array leftInput { 0.0f, 1.0f, 2.0f}; + std::array rightInput { 10.0f, 11.0f, 12.0f }; + std::array output; + std::array expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f}; + writeInterleaved(leftInput, rightInput, output); + REQUIRE( output == expected ); +} + +TEST_CASE("[Helpers] Interleaved write -- SIMD") +{ + std::array leftInput { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, }; + std::array rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f }; + std::array output; + std::array expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f}; + writeInterleaved(leftInput, rightInput, output); + REQUIRE( output == expected ); +} + +TEST_CASE("[Helpers] Interleaved write unaligned end -- SIMD") +{ + std::array leftInput { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f}; + std::array rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f }; + std::array output; + std::array expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f}; + writeInterleaved(leftInput, rightInput, output); + REQUIRE( output == expected ); +} + +TEST_CASE("[Helpers] Small interleaved write unaligned end -- SIMD") +{ + std::array leftInput { 0.0f, 1.0f, 2.0f}; + std::array rightInput { 10.0f, 11.0f, 12.0f }; + std::array output; + std::array expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f}; + writeInterleaved(leftInput, rightInput, output); + REQUIRE( output == expected ); +} + +TEST_CASE("[Helpers] Interleaved write SIMD vs Scalar") +{ + std::array leftInput; + std::array rightInput; + std::array outputScalar; + std::array outputSIMD; + std::iota(leftInput.begin(), leftInput.end(), 0.0f); + std::iota(rightInput.begin(), rightInput.end(), medBufferSize); + writeInterleaved(leftInput, rightInput, outputScalar); + writeInterleaved(leftInput, rightInput, outputSIMD); + REQUIRE( outputScalar == outputSIMD ); +} \ No newline at end of file diff --git a/tests/StereoBufferT.cpp b/tests/StereoBufferT.cpp index b5c41923..63387aec 100644 --- a/tests/StereoBufferT.cpp +++ b/tests/StereoBufferT.cpp @@ -131,194 +131,22 @@ TEST_CASE("[StereoBuffer] Channel alignments (doubles)") } TEST_CASE("[AudioBuffer] fills") -{ - SECTION("Floats - 0.0") - { - StereoBuffer buffer(10); - buffer.fill(0.0f); - std::array expected; - std::fill(expected.begin(), expected.end(), 0.0f); - std::array real { 2.0f }; - - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[frameIdx] = buffer(Channel::left, frameIdx); - REQUIRE( real == expected ); - - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[frameIdx] = buffer(Channel::right, frameIdx); - REQUIRE( real == expected ); - } - - SECTION("Floats - 1.0") - { - StereoBuffer buffer(10); - buffer.fill(1.0f); - std::array expected; - std::fill(expected.begin(), expected.end(), 1.0f); - std::array real { 2.0f }; - - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[frameIdx] = buffer(Channel::left, frameIdx); - REQUIRE( real == expected ); - - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[frameIdx] = buffer(Channel::right, frameIdx); - REQUIRE( real == expected ); - } - - SECTION("Doubles - 0.0") - { - StereoBuffer buffer(10); - buffer.fill(0.0); - std::array expected; - std::fill(expected.begin(), expected.end(), 0.0); - std::array real { 2.0 }; - - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[frameIdx] = buffer(Channel::left, frameIdx); - REQUIRE( real == expected ); - - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[frameIdx] = buffer(Channel::right, frameIdx); - REQUIRE( real == expected ); - } - - SECTION("Doubles - 1.0") - { - StereoBuffer buffer(10); - buffer.fill(1.0); - std::array expected; - std::fill(expected.begin(), expected.end(), 1.0); - std::array real { 2.0 }; - - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[frameIdx] = buffer(Channel::left, frameIdx); - REQUIRE( real == expected ); - - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[frameIdx] = buffer(Channel::right, frameIdx); - REQUIRE( real == expected ); - } - - SECTION("Floats - 0.0 - SIMD") - { - StereoBuffer buffer(10); - buffer.fill(0.0f); - std::array expected; - std::fill(expected.begin(), expected.end(), 0.0f); - std::array real { 2.0f }; - - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[frameIdx] = buffer(Channel::left, frameIdx); - REQUIRE( real == expected ); - - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[frameIdx] = buffer(Channel::right, frameIdx); - REQUIRE( real == expected ); - } - - SECTION("Floats - 1.0 - SIMD") - { - StereoBuffer buffer(10); - buffer.fill(1.0f); - std::array expected { 1.0f }; - std::fill(expected.begin(), expected.end(), 1.0f); - std::array real { 2.0f }; - - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[frameIdx] = buffer(Channel::left, frameIdx); - REQUIRE( real == expected ); - - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[frameIdx] = buffer(Channel::right, frameIdx); - REQUIRE( real == expected ); - } -} - -TEST_CASE("[StereoBuffer] Interleave read") -{ - StereoBuffer buffer(8); - std::array input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f}; - std::array expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f }; - buffer.readInterleaved(input.data(), 8); - std::array real { 0.0f }; - auto realIdx = 0; - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[realIdx++] = buffer(Channel::left, frameIdx); - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[realIdx++] = buffer(Channel::right, frameIdx); - REQUIRE( real == expected ); -} - -TEST_CASE("[StereoBuffer] Interleave read -- SIMD") -{ - StereoBuffer buffer(8); - std::array input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f}; - std::array expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f }; - buffer.readInterleaved(input.data(), 8); - std::array real { 0.0f }; - auto realIdx = 0; - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[realIdx++] = buffer(Channel::left, frameIdx); - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[realIdx++] = buffer(Channel::right, frameIdx); - REQUIRE( real == expected ); -} - -TEST_CASE("[StereoBuffer] Interleave read unaligned end -- SIMD") { StereoBuffer buffer(10); - std::array input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f}; - std::array expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f}; - buffer.readInterleaved(input.data(), 10); - std::array real { 0.0f }; - auto realIdx = 0; + buffer.fill(1.3f); + std::array expected; + std::fill(expected.begin(), expected.end(), 1.3f); + std::array real { 0.0f }; + for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[realIdx++] = buffer(Channel::left, frameIdx); + real[frameIdx] = buffer(Channel::left, frameIdx); + REQUIRE( real == expected ); + for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[realIdx++] = buffer(Channel::right, frameIdx); + real[frameIdx] = buffer(Channel::right, frameIdx); REQUIRE( real == expected ); } -TEST_CASE("[StereoBuffer] small interleaved read unaligned end -- SIMD") -{ - StereoBuffer buffer(3); - std::array input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f}; - std::array expected = { 0.0f, 1.0f, 2.0f, 10.0f, 11.0f, 12.0f}; - buffer.readInterleaved(input.data(), 3); - std::array real { 0.0f }; - auto realIdx = 0; - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[realIdx++] = buffer(Channel::left, frameIdx); - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - real[realIdx++] = buffer(Channel::right, frameIdx); - REQUIRE( real == expected ); -} - -TEST_CASE("[StereoBuffer] SIMD vs scalar") -{ - constexpr int numFrames { 91 }; - StereoBuffer buffer(numFrames); - std::array input; - std::iota(input.begin(), input.end(), 0.0f); - StereoBuffer expectedBuffer (numFrames); - expectedBuffer.readInterleaved(input.data(), 10); - buffer.readInterleaved(input.data(), 10); - std::array expectedArray { 0.0f }; - std::array realArray { 0.0f }; - auto sampleIdx = 0; - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - { - expectedArray[sampleIdx] = buffer(Channel::left, frameIdx); - realArray[sampleIdx++] = buffer(Channel::left, frameIdx); - } - for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) - { - expectedArray[sampleIdx] = buffer(Channel::right, frameIdx); - realArray[sampleIdx++] = buffer(Channel::right, frameIdx); - } - REQUIRE( realArray == expectedArray ); -} TEST_CASE("[AudioBuffer] Fill a big Audiobuffer") { @@ -326,7 +154,7 @@ TEST_CASE("[AudioBuffer] Fill a big Audiobuffer") StereoBuffer buffer (size); std::vector input (2*size); std::iota(input.begin(), input.end(), 1.0f); - buffer.readInterleaved(input.data(), size); + buffer.readInterleaved(input); } TEST_CASE("[StereoBuffer] Interleaved write -- Scalar") @@ -334,8 +162,8 @@ TEST_CASE("[StereoBuffer] Interleaved write -- Scalar") StereoBuffer buffer(10); std::array input = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f}; std::array output { 0.0f }; - buffer.readInterleaved(input.data(), 10); - buffer.writeInterleaved(output.data(), 10); + buffer.readInterleaved(input); + buffer.writeInterleaved(output); REQUIRE( output == input ); } @@ -344,17 +172,17 @@ TEST_CASE("[StereoBuffer] Interleaved write -- SIMD") StereoBuffer buffer(10); std::array input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f}; std::array output { 0.0f }; - buffer.readInterleaved(input.data(), 10); - buffer.writeInterleaved(output.data(), 10); + buffer.readInterleaved(input); + buffer.writeInterleaved(output); REQUIRE( output == input ); } TEST_CASE("[StereoBuffer] Small interleaved write -- SIMD") { StereoBuffer buffer(3); - std::array input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f}; - std::array output { 0.0f }; - buffer.readInterleaved(input.data(), 3); - buffer.writeInterleaved(output.data(), 3); + std::array input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f}; + std::array output { 0.0f }; + buffer.readInterleaved(input); + buffer.writeInterleaved(output); REQUIRE( output == input ); } \ No newline at end of file