From 9187e0db415553a101abd9b32a8ce8d02b25ff2b Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Sun, 22 Dec 2019 23:36:34 +0100 Subject: [PATCH] Refactor the oversampling helpers --- cmake/SfizzSIMDSourceFilesCheck.cmake | 6 +- src/CMakeLists.txt | 1 + src/sfizz/Oversampler.cpp | 145 ++++++++++++++++++++++++++ src/sfizz/Oversampler.h | 111 +++----------------- src/sfizz/OversamplerDummy.cpp | 40 ------- src/sfizz/OversamplerSSE.cpp | 52 --------- 6 files changed, 161 insertions(+), 194 deletions(-) create mode 100644 src/sfizz/Oversampler.cpp delete mode 100644 src/sfizz/OversamplerDummy.cpp delete mode 100644 src/sfizz/OversamplerSSE.cpp diff --git a/cmake/SfizzSIMDSourceFilesCheck.cmake b/cmake/SfizzSIMDSourceFilesCheck.cmake index 3c88f5b2..1237b3bc 100644 --- a/cmake/SfizzSIMDSourceFilesCheck.cmake +++ b/cmake/SfizzSIMDSourceFilesCheck.cmake @@ -10,7 +10,7 @@ endif() # SIMD checks if (HAVE_X86INTRIN_H AND UNIX) add_compile_options (-DHAVE_X86INTRIN_H) - set (SFIZZ_SIMD_SOURCES sfizz/SIMDSSE.cpp sfizz/OversamplerSSE.cpp) + set (SFIZZ_SIMD_SOURCES sfizz/SIMDSSE.cpp) elseif (HAVE_INTRIN_H AND WIN32) add_compile_options (/DHAVE_INTRIN_H) set (SFIZZ_SIMD_SOURCES sfizz/SIMDSSE.cpp) @@ -20,9 +20,9 @@ elseif (HAVE_ARM_NEON_H AND UNIX) add_compile_options (-march=native) add_compile_options (-mtune=cortex-a53) add_compile_options (-funsafe-math-optimizations) - set (SFIZZ_SIMD_SOURCES sfizz/SIMDDummy.cpp sfizz/OversamplerDummy.cpp) + set (SFIZZ_SIMD_SOURCES sfizz/SIMDDummy.cpp) else() - set (SFIZZ_SIMD_SOURCES sfizz/SIMDDummy.cpp sfizz/OversamplerDummy.cpp) + set (SFIZZ_SIMD_SOURCES sfizz/SIMDDummy.cpp) endif() set (SFIZZ_SOURCES ${SFIZZ_SOURCES} ${SFIZZ_SIMD_SOURCES}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eb19231a..39f2555f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,6 +8,7 @@ set (SFIZZ_SOURCES sfizz/ScopedFTZ.cpp sfizz/MidiState.cpp sfizz/SfzHelpers.cpp + sfizz/Oversampler.cpp sfizz/FloatEnvelopes.cpp ) include (SfizzSIMDSourceFilesCheck) diff --git a/src/sfizz/Oversampler.cpp b/src/sfizz/Oversampler.cpp new file mode 100644 index 00000000..dd3c8fbb --- /dev/null +++ b/src/sfizz/Oversampler.cpp @@ -0,0 +1,145 @@ +// Copyright (c) 2019, Paul Ferrand +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "Oversampler.h" +#include "Buffer.h" +#include "AudioSpan.h" + +constexpr std::array coeffsStage2x { + 0.036681502163648017, + 0.13654762463195771, + 0.27463175937945411, + 0.42313861743656667, + 0.56109869787919475, + 0.67754004997416162, + 0.76974183386322659, + 0.83988962484963803, + 0.89226081800387891, + 0.9315419599631839, + 0.96209454837808395, + 0.98781637073289708 +}; + +constexpr std::array coeffsStage4x { + 0.042448989488488006, + 0.17072114107630679, + 0.39329183835224008, + 0.74569514831986694 +}; + +constexpr std::array coeffsStage8x { + 0.055748680811302048, + 0.24305119574153092, + 0.6466991311926823 +}; + + +#if defined(__x86_64__) || defined(__i386__) +#include "hiir/Upsampler2xSse.h" +using Upsampler2x = hiir::Upsampler2xSse; +using Upsampler4x = hiir::Upsampler2xSse; +using Upsampler8x = hiir::Upsampler2xSse; +#elif defined(__arm__) || defined(__aarch64__) +#include "hiir/Upsampler2xNeon.h" +using Upsampler2x = hiir::Upsampler2xNeon; +using Upsampler4x = hiir::Upsampler2xNeon; +using Upsampler8x = hiir::Upsampler2xNeon; +#else +#include "hiir/Upsampler2xFpu.h" +using Upsampler2x = hiir::Upsampler2xFpu; +using Upsampler4x = hiir::Upsampler2xFpu; +using Upsampler8x = hiir::Upsampler2xFpu; +#endif + +sfz::Oversampler::Oversampler(sfz::Oversampling factor, size_t chunkSize) +: factor(factor), chunkSize(chunkSize) +{ + +} + +void sfz::Oversampler::stream(const sfz::AudioBuffer& input, sfz::AudioBuffer& output, std::atomic* framesReady) +{ + ASSERT(output.getNumFrames() >= input.getNumFrames() * static_cast(factor)); + ASSERT(output.getNumChannels() == input.getNumChannels()); + + const auto numFrames = input.getNumFrames(); + const auto numChannels = input.getNumChannels(); + + std::vector upsampler2x (numChannels); + std::vector upsampler4x (numChannels); + std::vector upsampler8x (numChannels); + + switch(factor) + { + case Oversampling::x8: + for (auto& upsampler: upsampler8x) + upsampler.set_coefs(coeffsStage8x.data()); + [[fallthrough]]; + case Oversampling::x4: + for (auto& upsampler: upsampler4x) + upsampler.set_coefs(coeffsStage4x.data()); + [[fallthrough]]; + case Oversampling::x2: + for (auto& upsampler: upsampler2x) + upsampler.set_coefs(coeffsStage2x.data()); + [[fallthrough]]; + case Oversampling::x1: + for (int i = 0; i < numChannels; ++i) + copy(input.getConstSpan(i), output.getSpan(i)); + return; + } + + // Intermediate buffers + sfz::Buffer buffer1 { chunkSize * 2 }; + sfz::Buffer buffer2 { chunkSize * 4 }; + auto span1 = absl::MakeSpan(buffer1); + auto span2 = absl::MakeSpan(buffer2); + + size_t inputFrameCounter { 0 }; + size_t outputFrameCounter { 0 }; + while(inputFrameCounter < numFrames) + { + // std::cout << "Input frames: " << inputFrameCounter << "/" << numFrames << '\n'; + const auto thisChunkSize = std::min(chunkSize, numFrames - inputFrameCounter); + const auto outputChunkSize { thisChunkSize * static_cast(factor) }; + for (int chanIdx = 0; chanIdx < numChannels; chanIdx++) { + const auto inputChunk = input.getSpan(chanIdx).subspan(inputFrameCounter, chunkSize); + const auto outputChunk = output.getSpan(chanIdx).subspan(outputFrameCounter, outputChunkSize); + upsampler2x[chanIdx].process_block(span1.data(), inputChunk.data(), thisChunkSize); + + if (factor == Oversampling::x4) { + upsampler4x[chanIdx].process_block(outputChunk.data(), span1.data(), thisChunkSize * 2); + } + else if (factor == Oversampling::x8) { + upsampler4x[chanIdx].process_block(span2.data(), span1.data(), thisChunkSize * 2); + upsampler8x[chanIdx].process_block(outputChunk.data(), span1.data(), thisChunkSize * 4); + } + } + inputFrameCounter += thisChunkSize; + outputFrameCounter += outputChunkSize; + + if (framesReady != nullptr) + framesReady->fetch_add(outputChunkSize); + } + +} diff --git a/src/sfizz/Oversampler.h b/src/sfizz/Oversampler.h index 66c419aa..40135d8f 100644 --- a/src/sfizz/Oversampler.h +++ b/src/sfizz/Oversampler.h @@ -26,110 +26,23 @@ #include #include "absl/types/span.h" #include "Debug.h" +#include "Buffer.h" #include "AudioBuffer.h" #include "Config.h" -#include "hiir/Upsampler2xFpu.h" namespace sfz { -constexpr std::array coeffsStage2x { - 0.036681502163648017, - 0.13654762463195771, - 0.27463175937945411, - 0.42313861743656667, - 0.56109869787919475, - 0.67754004997416162, - 0.76974183386322659, - 0.83988962484963803, - 0.89226081800387891, - 0.9315419599631839, - 0.96209454837808395, - 0.98781637073289708 +class Oversampler +{ +public: + Oversampler() = delete; + Oversampler(Oversampling factor = Oversampling::x1, size_t chunkSize = config::chunkSize); + Oversampler(const Oversampler&) = delete; + Oversampler(Oversampler&&) = delete; + void stream(const AudioBuffer& input, AudioBuffer& output, std::atomic* framesReady = nullptr); +private: + Oversampling factor; + size_t chunkSize; }; -constexpr std::array coeffsStage4x { - 0.042448989488488006, - 0.17072114107630679, - 0.39329183835224008, - 0.74569514831986694 -}; - -constexpr std::array coeffsStage8x { - 0.055748680811302048, - 0.24305119574153092, - 0.6466991311926823 -}; - -template -void upsample2xStage(absl::Span input, absl::Span output) -{ - ASSERT(output.size() >= 2 * input.size()); - hiir::Upsampler2xFpu upsampler; - upsampler.set_coefs(coeffsStage2x.data()); - upsampler.process_block(output.data(), input.data(), input.size()); -} - - -template -void upsample4xStage(absl::Span input, absl::Span output) -{ - ASSERT(output.size() >= 2 * input.size()); - hiir::Upsampler2xFpu upsampler; - upsampler.set_coefs(coeffsStage4x.data()); - upsampler.process_block(output.data(), input.data(), input.size()); -} - -template -void upsample8xStage(absl::Span input, absl::Span output) -{ - ASSERT(output.size() >= 2 * input.size()); - hiir::Upsampler2xFpu upsampler; - upsampler.set_coefs(coeffsStage8x.data()); - upsampler.process_block(output.data(), input.data(), input.size()); -} - -template<> -void upsample2xStage(absl::Span input, absl::Span output); -template<> -void upsample4xStage(absl::Span input, absl::Span output); -template<> -void upsample8xStage(absl::Span input, absl::Span output); - -template -std::unique_ptr> upsample2x(const sfz::AudioBuffer& buffer) -{ - // auto tempBuffer = std::make_unique>(buffer.getNumFrames() * 2); - auto outputBuffer = std::make_unique>(buffer.getNumChannels(), buffer.getNumFrames() * 2); - for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) { - sfz::upsample2xStage(buffer.getConstSpan(channelIdx), outputBuffer->getSpan(channelIdx)); - } - return outputBuffer; -} - -template -std::unique_ptr> upsample4x(const sfz::AudioBuffer& buffer) -{ - auto tempBuffer = std::make_unique>(buffer.getNumFrames() * 2); - auto outputBuffer = std::make_unique>(buffer.getNumChannels(), buffer.getNumFrames() * 4); - for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) { - sfz::upsample2xStage(buffer.getConstSpan(channelIdx), absl::MakeSpan(*tempBuffer)); - sfz::upsample4xStage(absl::MakeConstSpan(*tempBuffer), outputBuffer->getSpan(channelIdx)); - } - return outputBuffer; -} - -template -std::unique_ptr> upsample8x(const sfz::AudioBuffer& buffer) -{ - auto tempBuffer2x = std::make_unique>(buffer.getNumFrames() * 2); - auto tempBuffer4x = std::make_unique>(buffer.getNumFrames() * 4); - auto outputBuffer = std::make_unique>(buffer.getNumChannels(), buffer.getNumFrames() * 8); - for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) { - sfz::upsample2xStage(buffer.getConstSpan(channelIdx), absl::MakeSpan(*tempBuffer2x)); - sfz::upsample4xStage(absl::MakeConstSpan(*tempBuffer2x), absl::MakeSpan(*tempBuffer4x)); - sfz::upsample8xStage(absl::MakeConstSpan(*tempBuffer4x), outputBuffer->getSpan(channelIdx)); - } - return outputBuffer; -} - } diff --git a/src/sfizz/OversamplerDummy.cpp b/src/sfizz/OversamplerDummy.cpp deleted file mode 100644 index b3080a4a..00000000 --- a/src/sfizz/OversamplerDummy.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2019, Paul Ferrand -// All rights reserved. - -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: - -// 1. Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. - -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "Oversampler.h" - -template<> -void sfz::upsample2xStage(absl::Span input, absl::Span output) -{ - sfz::upsample2xStage(input, output); -} -template<> -void sfz::upsample4xStage(absl::Span input, absl::Span output) -{ - sfz::upsample4xStage(input, output); -} -template<> -void sfz::upsample8xStage(absl::Span input, absl::Span output) -{ - sfz::upsample8xStage(input, output); -} diff --git a/src/sfizz/OversamplerSSE.cpp b/src/sfizz/OversamplerSSE.cpp deleted file mode 100644 index 6d233943..00000000 --- a/src/sfizz/OversamplerSSE.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2019, Paul Ferrand -// All rights reserved. - -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: - -// 1. Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. - -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "Oversampler.h" -#include "hiir/Upsampler2xSse.h" - -template<> -void sfz::upsample2xStage(absl::Span input, absl::Span output) -{ - ASSERT(output.size() >= 2 * input.size()); - hiir::Upsampler2xSse upsampler; - upsampler.set_coefs(sfz::coeffsStage2x.data()); - upsampler.process_block(output.data(), input.data(), input.size()); -} - -template<> -void sfz::upsample4xStage(absl::Span input, absl::Span output) -{ - ASSERT(output.size() >= 2 * input.size()); - hiir::Upsampler2xSse upsampler; - upsampler.set_coefs(sfz::coeffsStage4x.data()); - upsampler.process_block(output.data(), input.data(), input.size()); -} - -template<> -void sfz::upsample8xStage(absl::Span input, absl::Span output) -{ - ASSERT(output.size() >= 2 * input.size()); - hiir::Upsampler2xSse upsampler; - upsampler.set_coefs(sfz::coeffsStage8x.data()); - upsampler.process_block(output.data(), input.data(), input.size()); -}