diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 2a217244..3da1d309 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -2,8 +2,8 @@ project(sfizz) # Check SIMD include (SfizzSIMDSourceFiles) -set(BENCHMARK_SIMD_SOURCES ${SFIZZ_SIMD_SOURCES}) -list(TRANSFORM BENCHMARK_SIMD_SOURCES PREPEND "../src/") +set(BENCHMARK_SIMD_SOURCES) +sfizz_add_simd_sources(BENCHMARK_SIMD_SOURCES "../src") find_package(benchmark CONFIG REQUIRED) # Check libsamplerate diff --git a/cmake/SfizzConfig.cmake b/cmake/SfizzConfig.cmake index 68c02078..76aea885 100644 --- a/cmake/SfizzConfig.cmake +++ b/cmake/SfizzConfig.cmake @@ -15,22 +15,29 @@ if (WIN32) add_compile_definitions(_WIN32_WINNT=0x601) endif() +# The variable CMAKE_SYSTEM_PROCESSOR is incorrect on Visual studio... +# see https://gitlab.kitware.com/cmake/cmake/issues/15170 + +if(MSVC) + set(SFIZZ_SYSTEM_PROCESSOR "${MSVC_CXX_ARCHITECTURE_ID}") +else() + set(SFIZZ_SYSTEM_PROCESSOR "${CMAKE_SYSTEM_PROCESSOR}") +endif() + # Add required flags for the builds if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") add_compile_options(-Wall) add_compile_options(-Wextra) add_compile_options(-ffast-math) add_compile_options(-fno-omit-frame-pointer) # For debugging purposes + if (SFIZZ_SYSTEM_PROCESSOR MATCHES "^i.86$") + add_compile_options(-msse2) + endif() elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(CMAKE_CXX_STANDARD 17) add_compile_options(/Zc:__cplusplus) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") endif() -if (CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$") - if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - add_compile_options(-msse2) - endif() -endif() add_library(sfizz-sndfile INTERFACE) diff --git a/cmake/SfizzSIMDSourceFiles.cmake b/cmake/SfizzSIMDSourceFiles.cmake index e518dadd..ef0e1d1b 100644 --- a/cmake/SfizzSIMDSourceFiles.cmake +++ b/cmake/SfizzSIMDSourceFiles.cmake @@ -1,6 +1,21 @@ -set (SFIZZ_SIMD_SOURCES - sfizz/SIMDSSE.cpp - sfizz/SIMDNEON.cpp - sfizz/SIMDDummy.cpp) +macro(sfizz_add_simd_sources SOURCES_VAR PREFIX) + # It needs a macro, otherwise the source properties cannot take effect. -list (APPEND SFIZZ_SOURCES ${SFIZZ_SIMD_SOURCES}) + list (APPEND ${SOURCES_VAR} + ${PREFIX}/sfizz/SIMDSSE.cpp + ${PREFIX}/sfizz/SIMDNEON.cpp + ${PREFIX}/sfizz/SIMDDummy.cpp) + + # For CPU-dispatched X86 sources + # Always build them for all X86 targets. + if (SFIZZ_SYSTEM_PROCESSOR MATCHES "^(x86_64|amd64|AMD64|x64|X64|i.86|x86|X86)$") + # on GCC, it requires to set ISA support flags on individual files + # to be able to use the intrinsics + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + set_source_files_properties( + ${PREFIX}/sfizz/effects/impl/ResonantStringAVX.cpp + ${PREFIX}/sfizz/effects/impl/ResonantArrayAVX.cpp + PROPERTIES COMPILE_FLAGS "-mavx") + endif() + endif() +endmacro() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d24083a9..1655147b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,8 +29,15 @@ set (SFIZZ_SOURCES sfizz/effects/Rectify.cpp sfizz/effects/Gain.cpp sfizz/effects/Width.cpp -) + sfizz/effects/impl/ResonantString.cpp + sfizz/effects/impl/ResonantStringSSE.cpp + sfizz/effects/impl/ResonantStringAVX.cpp + sfizz/effects/impl/ResonantArray.cpp + sfizz/effects/impl/ResonantArraySSE.cpp + sfizz/effects/impl/ResonantArrayAVX.cpp) + include (SfizzSIMDSourceFiles) +sfizz_add_simd_sources (SFIZZ_SOURCES ".") # Parser core library add_library (sfizz_parser STATIC) diff --git a/src/sfizz/Buffer.h b/src/sfizz/Buffer.h index b7b0ce8d..35779eab 100644 --- a/src/sfizz/Buffer.h +++ b/src/sfizz/Buffer.h @@ -282,7 +282,7 @@ private: static constexpr int TypeAlignment { Alignment / sizeof(value_type) }; static constexpr int TypeAlignmentMask { TypeAlignment - 1 }; 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(Alignment == 0 || Alignment == 4 || Alignment == 8 || Alignment == 16 || Alignment == 32, "Bad alignment value"); static_assert(TypeAlignment * sizeof(value_type) == Alignment, "The alignment does not appear to be divided by the size of the Type"); void* align(std::size_t alignment, std::size_t size, void *&ptr, std::size_t &space ) { diff --git a/src/sfizz/SIMDConfig.h b/src/sfizz/SIMDConfig.h index 8b7f427f..c4f77d48 100644 --- a/src/sfizz/SIMDConfig.h +++ b/src/sfizz/SIMDConfig.h @@ -67,3 +67,30 @@ # define SFIZZ_HAVE_NEON 0 # endif #endif + +/** + Detect one of the following the processor families. + + - SFIZZ_CPU_FAMILY_X86_64 + - SFIZZ_CPU_FAMILY_I386 + - SFIZZ_CPU_FAMILY_AARCH64 + - SFIZZ_CPU_FAMILY_ARM + */ + +#if defined(_MSC_VER) && defined(_M_AMD64) +# define SFIZZ_CPU_FAMILY_X86_64 1 +#elif defined(_MSC_VER) && defined(_M_IX86) +# define SFIZZ_CPU_FAMILY_I386 1 +#elif defined(_MSC_VER) && defined(_M_ARM64) +# define SFIZZ_CPU_FAMILY_AARCH64 1 +#elif defined(_MSC_VER) && defined(_M_ARM) +# define SFIZZ_CPU_FAMILY_ARM 1 +#elif defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) || defined(__amd64) +# define SFIZZ_CPU_FAMILY_X86_64 1 +#elif defined(__i386__) || defined(__i386) +# define SFIZZ_CPU_FAMILY_I386 1 +#elif defined(__aarch64__) +# define SFIZZ_CPU_FAMILY_AARCH64 1 +#elif defined(__arm__) || defined(__arm) +# define SFIZZ_CPU_FAMILY_ARM 1 +#endif diff --git a/src/sfizz/effects/Strings.cpp b/src/sfizz/effects/Strings.cpp index 6073e753..d739c593 100644 --- a/src/sfizz/effects/Strings.cpp +++ b/src/sfizz/effects/Strings.cpp @@ -16,7 +16,9 @@ Extensions */ #include "Strings.h" -#include "StringsPrivate.h" +#include "impl/ResonantArray.h" +#include "impl/ResonantArraySSE.h" +#include "impl/ResonantArrayAVX.h" #include "Opcode.h" #include "MathHelpers.h" #include "SIMDHelpers.h" @@ -26,13 +28,8 @@ Extensions namespace sfz { namespace fx { - struct Strings::ResonantString { - Bw2BPF bpf; - WgResonator res; - }; - Strings::Strings() - : _strings(new ResonantString[MaximumNumStrings]) + : _stringsArray(new ResonantArrayScalar) { } @@ -42,42 +39,48 @@ namespace fx { void Strings::setSampleRate(double sampleRate) { - for (unsigned i = 0, n = _numStrings; i < n; ++i) { - ResonantString& rs = _strings[i]; - rs.bpf.init(sampleRate); - rs.res.init(sampleRate); + const unsigned numStrings = _numStrings; + AudioBuffer parameterBuffers { 4, numStrings }; + + auto pitches = parameterBuffers.getSpan(0); + auto bandwidths = parameterBuffers.getSpan(1); + auto feedbacks = parameterBuffers.getSpan(2); + auto gains = parameterBuffers.getSpan(3); + + for (unsigned i = 0; i < numStrings; ++i) { int midiNote = i + 24; - double midiFrequency = 440.0 * std::exp2((midiNote - 69) * (1.0 / 12.0)); - - // 1 Hz works decently as compromise of selectivity/speed - double bpfBandwidth = 1.0; - rs.bpf.setCutoff( - midiFrequency - 0.5 * bpfBandwidth, - midiFrequency + 0.5 * bpfBandwidth); - - rs.res.setFrequency(midiFrequency); - - // TODO(jpc) find how to adjust the string feedbacks - // for now set a fixed release time for all strings - double releaseTime = 50e-3; - double releaseFeedback = std::exp(-6.91 / (releaseTime * sampleRate)); - rs.res.setFeedback(releaseFeedback); + pitches[i] = 440.0 * std::exp2((midiNote - 69) * (1.0 / 12.0)); } + + // 1 Hz works decently as compromise of selectivity/speed + sfz::fill(bandwidths, 1.0f); + + // TODO(jpc) find how to adjust the string feedbacks + // for now set a fixed release time for all strings + const double releaseTime = 50e-3; + const double releaseFeedback = std::exp(-6.91 / (releaseTime * sampleRate)); + sfz::fill(feedbacks, releaseFeedback); + + // TODO(jpc) damping of the high frequencies + // fixed gains for now + sfz::fill(gains, 1e-3f); + + _stringsArray->setup( + sampleRate, numStrings, + pitches.data(), bandwidths.data(), feedbacks.data(), gains.data()); } void Strings::setSamplesPerBlock(int samplesPerBlock) { _tempBuffer.resize(samplesPerBlock); + + _stringsArray->setSamplesPerBlock(samplesPerBlock); } void Strings::clear() { - for (unsigned i = 0, n = _numStrings; i < n; ++i) { - ResonantString& rs = _strings[i]; - rs.bpf.clear(); - rs.res.clear(); - } + _stringsArray->clear(); } void Strings::process(const float* const inputs[], float* const outputs[], unsigned nframes) @@ -92,30 +95,15 @@ namespace fx { // generate the strings summed into a common buffer absl::Span resOutput = _tempBuffer.getSpan(1).first(nframes); - sfz::fill(resOutput, 0.0f); - for (unsigned is = 0, ns = _numStrings; is < ns; ++is) { - ResonantString& rs = _strings[is]; - for (unsigned i = 0; i < nframes; ++i) { - float sample = resInput[i]; - sample = rs.bpf.process(sample); - sample = rs.res.process(sample); - resOutput[i] += sample; - } - } - - // TODO(jpc) damping of the high frequencies - // it's easiest apply individual gains to resonating strings - // or pass resonator output through LPF + _stringsArray->process(resInput.data(), resOutput.data(), nframes); // mix the resonator into the output auto outputL = absl::MakeSpan(outputs[0], nframes); auto outputR = absl::MakeSpan(outputs[1], nframes); - constexpr float resAttenuate = 1e-3; // need significant attenuation, here -60dB - absl::Span wet = _tempBuffer.getSpan(2).first(nframes); - sfz::fill(wet, 0.01f * resAttenuate *_wet); // TOD strings_wet_oncc modulation... + sfz::fill(wet, 0.01f *_wet); // TOD strings_wet_oncc modulation... sfz::copy(inputL, outputL); sfz::copy(inputR, outputR); diff --git a/src/sfizz/effects/Strings.h b/src/sfizz/effects/Strings.h index a628960a..2f46254b 100644 --- a/src/sfizz/effects/Strings.h +++ b/src/sfizz/effects/Strings.h @@ -12,8 +12,7 @@ namespace sfz { namespace fx { - class Bw2BPF; - class WgResonator; + class ResonantArray; /** * @brief String resonance effect @@ -55,8 +54,7 @@ namespace fx { unsigned _numStrings = MaximumNumStrings; float _wet = 0; - struct ResonantString; - std::unique_ptr _strings; + std::unique_ptr _stringsArray; AudioBuffer _tempBuffer { 3, config::defaultSamplesPerBlock }; }; diff --git a/src/sfizz/effects/StringsPrivate.h b/src/sfizz/effects/StringsPrivate.h deleted file mode 100644 index e17a8b59..00000000 --- a/src/sfizz/effects/StringsPrivate.h +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause - -// This code is part of the sfizz library and is licensed under a BSD 2-clause -// license. You should have receive a LICENSE.md file along with the code. -// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz - -#pragma once -#include "MathHelpers.h" -#include - -namespace sfz { -namespace fx { - - // Butterworth 2nd order bandpass (faust -double -os) - /* - import("stdfaust.lib"); - process = fi.bandpass(1, loF, hiF) with { - loF = hslider("[1] Lo frequency [unit:Hz]", 1, 0, 1000, 1); - hiF = hslider("[2] Hi frequency [unit:Hz]", 1, 0, 1000, 1); - }; - */ - class Bw2BPF { - private: - typedef float FAUSTFLOAT; - - public: - /** - * @brief Initialize. - */ - void init(double sampleRate) - { - fConst0 = sampleRate; - fConst1 = (2.0 / fConst0); - fConst2 = (2.0 * fConst0); - fConst3 = (3.1415926535897931 / fConst0); - fConst4 = (0.5 / fConst0); - fConst5 = (4.0 * power2(fConst0)); - fConst6 = power2((1.0 / fConst0)); - fConst7 = (2.0 * fConst6); - clear(); - } - - /** - * @brief Clear the memory of the filter. - */ - void clear() - { - for (int l0 = 0; (l0 < 3); l0 = (l0 + 1)) { - fRec0[l0] = 0.0; - } - } - - /** - * @brief Set the BPF low and high frequencies for -3dB response. - * - * The center frequency is (loF+hiF)/2. - */ - void setCutoff(double loF, double hiF) - { - fControl[0] = std::tan((fConst3 * double(hiF))); - fControl[1] = power2(std::sqrt((fConst5 * (fControl[0] * std::tan((fConst3 * double(loF))))))); - fControl[2] = ((fConst2 * fControl[0]) - (fConst4 * (fControl[1] / fControl[0]))); - fControl[3] = (fConst6 * fControl[1]); - fControl[4] = (fConst1 * fControl[2]); - fControl[5] = ((fControl[3] + fControl[4]) + 4.0); - fControl[6] = (fConst1 * (fControl[2] / fControl[5])); - fControl[7] = (1.0 / fControl[5]); - fControl[8] = ((fConst7 * fControl[1]) + -8.0); - fControl[9] = (fControl[3] + (4.0 - fControl[4])); - fControl[10] = (0.0 - fControl[6]); - } - - /** - * @brief Process the next filtered sample. - */ - FAUSTFLOAT process(FAUSTFLOAT input) - { - fRec0[0] = (double(input) - (fControl[7] * ((fControl[8] * fRec0[1]) + (fControl[9] * fRec0[2])))); - FAUSTFLOAT output = FAUSTFLOAT(((fControl[6] * fRec0[0]) + (fControl[10] * fRec0[2]))); - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; - return output; - } - - private: - double fRec0[3] {}; - double fControl[11] {}; - double fConst0 {}; - double fConst1 {}; - double fConst2 {}; - double fConst3 {}; - double fConst4 {}; - double fConst5 {}; - double fConst6 {}; - double fConst7 {}; - }; - - //-------------------------------------------------------------------------- - - // Waveguide resonator (faust -os) - /* - import("stdfaust.lib"); - process = fi.nlf2(f, r) : (_,!) with { - f = hslider("[1] Resonance frequency [unit:Hz]", 1, 0, 1000, 1); - r = hslider("[2] Resonance feedback", 0, 0, 1, 1e-3); - }; - */ - class WgResonator { - private: - typedef float FAUSTFLOAT; - - public: - /** - * @brief Initialize. - */ - void init(float sampleRate) - { - fConst0 = (6.28318548f / sampleRate); - clear(); - } - - /** - * @brief Clear the memory of the resonator. - */ - void clear() - { - for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec0[l0] = 0.0f; - } - for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec1[l1] = 0.0f; - } - } - - /** - * @brief Set the resonance frequency. - */ - void setFrequency(float frequency) - { - fControl[1] = (fConst0 * float(frequency)); - fControl[2] = std::sin(fControl[1]); - fControl[3] = std::cos(fControl[1]); - } - - /** - * @brief Set the resonance feedback. - */ - void setFeedback(float feedback) - { - fControl[0] = float(feedback); - } - - /** - * @brief Process the next resonance sample. - */ - FAUSTFLOAT process(FAUSTFLOAT input) - { - fRec0[0] = (fControl[0] * ((fControl[2] * fRec1[1]) + (fControl[3] * fRec0[1]))); - fRec1[0] = ((float(input) + (fControl[3] * fRec1[1])) - (fControl[2] * fRec0[1])); - FAUSTFLOAT output = FAUSTFLOAT(fRec0[0]); - fRec0[1] = fRec0[0]; - fRec1[1] = fRec1[0]; - return output; - } - - private: - float fRec0[2] {}; - float fRec1[2] {}; - float fControl[4]; - float fConst0 {}; - }; - -} // namespace sfz -} // namespace fx diff --git a/src/sfizz/effects/dsp/resonant_string.dsp b/src/sfizz/effects/dsp/resonant_string.dsp new file mode 100644 index 00000000..8a6d896c --- /dev/null +++ b/src/sfizz/effects/dsp/resonant_string.dsp @@ -0,0 +1,8 @@ +import("stdfaust.lib"); + +f = hslider("[1] Resonance frequency [unit:Hz]", 1, 0, 22000, 1); +r = hslider("[2] Resonance feedback", 0, 0, 1, 0.001); +b = hslider("[3] Bandwidth [unit:Hz]", 1, 0, 10, 0.01); +g = hslider("[4] Gain", 0, 0, 1, 0.01); + +process = fi.bandpass(1, f-0.5*b, f+0.5*b) : fi.nlf2(f, r) : (_,!) : *(g); diff --git a/src/sfizz/effects/impl/ResonantArray.cpp b/src/sfizz/effects/impl/ResonantArray.cpp new file mode 100644 index 00000000..59204916 --- /dev/null +++ b/src/sfizz/effects/impl/ResonantArray.cpp @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "ResonantArray.h" +#include "ResonantString.h" +#include "SIMDHelpers.h" + +namespace sfz { +namespace fx { + +ResonantArrayScalar::ResonantArrayScalar() +{ +} + +ResonantArrayScalar::~ResonantArrayScalar() +{ +} + +void ResonantArrayScalar::setup( + float sampleRate, unsigned numStrings, + const float pitches[], const float bandwidths[], + const float feedbacks[], const float gains[]) +{ + ResonantString* strings = new ResonantString[numStrings]; + + _strings.reset(strings); + _numStrings = numStrings; + + for (unsigned i = 0; i < numStrings; ++i) { + ResonantString& rs = strings[i]; + rs.init(sampleRate); + rs.setResonanceFrequency(pitches[i], bandwidths[i]); + rs.setResonanceFeedback(feedbacks[i]); + rs.setGain(gains[i]); + } +} + +void ResonantArrayScalar::clear() +{ + ResonantString* strings = _strings.get(); + const unsigned numStrings = _numStrings; + + for (unsigned i = 0; i < numStrings; ++i) { + ResonantString& rs = strings[i]; + rs.clear(); + } +} + +void ResonantArrayScalar::process(const float *inPtr, float *outPtr, unsigned numFrames) +{ + ResonantString* strings = _strings.get(); + const unsigned numStrings = _numStrings; + + auto input = absl::MakeSpan(inPtr, numFrames); + auto output = absl::MakeSpan(outPtr, numFrames); + + sfz::fill(output, 0.0f); + + for (unsigned is = 0; is < numStrings; ++is) { + ResonantString& rs = strings[is]; + for (unsigned i = 0; i < numFrames; ++i) + output[i] += rs.process(input[i]); + } +} + +} // namespace sfz +} // namespace fx diff --git a/src/sfizz/effects/impl/ResonantArray.h b/src/sfizz/effects/impl/ResonantArray.h new file mode 100644 index 00000000..e0c6f662 --- /dev/null +++ b/src/sfizz/effects/impl/ResonantArray.h @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include + +namespace sfz { +namespace fx { + +class ResonantString; + +//------------------------------------------------------------------------------ + +class ResonantArray { +public: + virtual ~ResonantArray() {} + + virtual void setup( + float sampleRate, unsigned numStrings, + const float pitches[], const float bandwidths[], + const float feedbacks[], const float gains[]) = 0; + + virtual void setSamplesPerBlock(unsigned samplesPerBlock) = 0; + + virtual void clear() = 0; + + virtual void process(const float *input, float *output, unsigned numFrames) = 0; +}; + +//------------------------------------------------------------------------------ + +class ResonantArrayScalar final : public ResonantArray { +public: + ResonantArrayScalar(); + ~ResonantArrayScalar(); + + void setup( + float sampleRate, unsigned numStrings, + const float pitches[], const float bandwidths[], + const float feedbacks[], const float gains[]) override; + + void setSamplesPerBlock(unsigned) override {} + + void clear() override; + + void process(const float *inPtr, float *outPtr, unsigned numFrames) override; + +private: + std::unique_ptr _strings; + unsigned _numStrings = 0; +}; + +} // namespace sfz +} // namespace fx diff --git a/src/sfizz/effects/impl/ResonantArrayAVX.cpp b/src/sfizz/effects/impl/ResonantArrayAVX.cpp new file mode 100644 index 00000000..73cbe86c --- /dev/null +++ b/src/sfizz/effects/impl/ResonantArrayAVX.cpp @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "ResonantArrayAVX.h" +#include "ResonantStringAVX.h" +#include "Config.h" +#include + +#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386 +namespace sfz { +namespace fx { + +static constexpr unsigned avxVectorSize = sizeof(__m256) / sizeof(float); + +ResonantArrayAVX::ResonantArrayAVX() +{ + setSamplesPerBlock(config::defaultSamplesPerBlock); +} + +ResonantArrayAVX::~ResonantArrayAVX() +{ +} + +void ResonantArrayAVX::setup( + float sampleRate, unsigned numStrings, + const float pitches[], const float bandwidths[], + const float feedbacks[], const float gains[]) +{ + const unsigned numStringPacks = (numStrings + avxVectorSize - 1) / avxVectorSize; + ResonantStringAVX* stringPacks = new ResonantStringAVX[numStringPacks]; + + _stringPacks.reset(stringPacks); + _numStrings = numStrings; + + for (unsigned p = 0; p < numStringPacks; ++p) { + ResonantStringAVX& rs = stringPacks[p]; + rs.init(sampleRate); + + __m256 pitchAVX = _mm256_set1_ps(0.0f); + __m256 bandwidthAVX = _mm256_set1_ps(0.0f); + __m256 feedbackAVX = _mm256_set1_ps(0.0f); + __m256 gainAVX = _mm256_set1_ps(0.0f); + + // copy 8 string parameters, or less if not enough remaining in buffer + unsigned numCopy = std::min(avxVectorSize, numStrings - (p * avxVectorSize)); + + std::memcpy(&pitchAVX, &pitches[p * avxVectorSize], numCopy * sizeof(float)); + std::memcpy(&bandwidthAVX, &bandwidths[p * avxVectorSize], numCopy * sizeof(float)); + std::memcpy(&feedbackAVX, &feedbacks[p * avxVectorSize], numCopy * sizeof(float)); + std::memcpy(&gainAVX, &gains[p * avxVectorSize], numCopy * sizeof(float)); + + rs.setResonanceFrequency(pitchAVX, bandwidthAVX); + rs.setResonanceFeedback(feedbackAVX); + rs.setGain(gainAVX); + } +} + +void ResonantArrayAVX::setSamplesPerBlock(unsigned samplesPerBlock) +{ + _workBuffer.resize(avxVectorSize * samplesPerBlock); +} + +void ResonantArrayAVX::clear() +{ + ResonantStringAVX* stringPacks = _stringPacks.get(); + const unsigned numStringPacks = (_numStrings + avxVectorSize - 1) / avxVectorSize; + + for (unsigned p = 0; p < numStringPacks; ++p) { + ResonantStringAVX& rs = stringPacks[p]; + rs.clear(); + } +} + +void ResonantArrayAVX::process(const float *inPtr, float *outPtr, unsigned numFrames) +{ + ResonantStringAVX* stringPacks = _stringPacks.get(); + const unsigned numStringPacks = (_numStrings + avxVectorSize - 1) / avxVectorSize; + + // receive 4 resonator outputs per pack + __m256* outputs8 = reinterpret_cast<__m256*>(_workBuffer.data()); + std::memset(outputs8, 0, numFrames * sizeof(__m256)); + + for (unsigned p = 0; p < numStringPacks; ++p) { + ResonantStringAVX& rs = stringPacks[p]; + for (unsigned i = 0; i < numFrames; ++i) + outputs8[i] += rs.process(_mm256_broadcast_ss(&inPtr[i])); + } + + // sum resonator outputs 8 to 1 + for (unsigned i = 0; i < numFrames; ++i) { + __m256 x = outputs8[i]; + const __m128 x128 = _mm_add_ps(_mm256_extractf128_ps(x, 1), _mm256_castps256_ps128(x)); + const __m128 x64 = _mm_add_ps(x128, _mm_movehl_ps(x128, x128)); + const __m128 x32 = _mm_add_ss(x64, _mm_shuffle_ps(x64, x64, 0x55)); + outPtr[i] = _mm_cvtss_f32(x32); + } +} + +} // namespace sfz +} // namespace fx +#endif diff --git a/src/sfizz/effects/impl/ResonantArrayAVX.h b/src/sfizz/effects/impl/ResonantArrayAVX.h new file mode 100644 index 00000000..93dca2e2 --- /dev/null +++ b/src/sfizz/effects/impl/ResonantArrayAVX.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "ResonantArray.h" +#include "Buffer.h" +#include "SIMDConfig.h" + +#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386 +namespace sfz { +namespace fx { + +class ResonantStringAVX; + +class ResonantArrayAVX final : public ResonantArray { +public: + ResonantArrayAVX(); + ~ResonantArrayAVX(); + + void setup( + float sampleRate, unsigned numStrings, + const float pitches[], const float bandwidths[], + const float feedbacks[], const float gains[]) override; + + void setSamplesPerBlock(unsigned samplesPerBlock) override; + + void clear() override; + + void process(const float *inPtr, float *outPtr, unsigned numFrames) override; + +private: + std::unique_ptr _stringPacks; + unsigned _numStrings = 0; + Buffer _workBuffer; +}; + +} // namespace sfz +} // namespace fx +#endif diff --git a/src/sfizz/effects/impl/ResonantArraySSE.cpp b/src/sfizz/effects/impl/ResonantArraySSE.cpp new file mode 100644 index 00000000..75051664 --- /dev/null +++ b/src/sfizz/effects/impl/ResonantArraySSE.cpp @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "ResonantArraySSE.h" +#include "ResonantStringSSE.h" +#include "Config.h" +#include + +#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386 +namespace sfz { +namespace fx { + +static constexpr unsigned sseVectorSize = sizeof(__m128) / sizeof(float); + +ResonantArraySSE::ResonantArraySSE() +{ + setSamplesPerBlock(config::defaultSamplesPerBlock); +} + +ResonantArraySSE::~ResonantArraySSE() +{ +} + +void ResonantArraySSE::setup( + float sampleRate, unsigned numStrings, + const float pitches[], const float bandwidths[], + const float feedbacks[], const float gains[]) +{ + const unsigned numStringPacks = (numStrings + sseVectorSize - 1) / sseVectorSize; + ResonantStringSSE* stringPacks = new ResonantStringSSE[numStringPacks]; + + _stringPacks.reset(stringPacks); + _numStrings = numStrings; + + for (unsigned p = 0; p < numStringPacks; ++p) { + ResonantStringSSE& rs = stringPacks[p]; + rs.init(sampleRate); + + __m128 pitchSSE = _mm_set1_ps(0.0f); + __m128 bandwidthSSE = _mm_set1_ps(0.0f); + __m128 feedbackSSE = _mm_set1_ps(0.0f); + __m128 gainSSE = _mm_set1_ps(0.0f); + + // copy 4 string parameters, or less if not enough remaining in buffer + unsigned numCopy = std::min(sseVectorSize, numStrings - (p * sseVectorSize)); + + std::memcpy(&pitchSSE, &pitches[p * sseVectorSize], numCopy * sizeof(float)); + std::memcpy(&bandwidthSSE, &bandwidths[p * sseVectorSize], numCopy * sizeof(float)); + std::memcpy(&feedbackSSE, &feedbacks[p * sseVectorSize], numCopy * sizeof(float)); + std::memcpy(&gainSSE, &gains[p * sseVectorSize], numCopy * sizeof(float)); + + rs.setResonanceFrequency(pitchSSE, bandwidthSSE); + rs.setResonanceFeedback(feedbackSSE); + rs.setGain(gainSSE); + } +} + +void ResonantArraySSE::setSamplesPerBlock(unsigned samplesPerBlock) +{ + _workBuffer.resize(sseVectorSize * samplesPerBlock); +} + +void ResonantArraySSE::clear() +{ + ResonantStringSSE* stringPacks = _stringPacks.get(); + const unsigned numStringPacks = (_numStrings + sseVectorSize - 1) / sseVectorSize; + + for (unsigned p = 0; p < numStringPacks; ++p) { + ResonantStringSSE& rs = stringPacks[p]; + rs.clear(); + } +} + +void ResonantArraySSE::process(const float *inPtr, float *outPtr, unsigned numFrames) +{ + ResonantStringSSE* stringPacks = _stringPacks.get(); + const unsigned numStringPacks = (_numStrings + sseVectorSize - 1) / sseVectorSize; + + // receive 4 resonator outputs per pack + __m128* outputs4 = reinterpret_cast<__m128*>(_workBuffer.data()); + std::memset(outputs4, 0, numFrames * sizeof(__m128)); + + for (unsigned p = 0; p < numStringPacks; ++p) { + ResonantStringSSE& rs = stringPacks[p]; + for (unsigned i = 0; i < numFrames; ++i) + outputs4[i] += rs.process(_mm_load1_ps(&inPtr[i])); + } + + // sum resonator outputs 4 to 1 + for (unsigned i = 0; i < numFrames; ++i) { + __m128 xmm0 = outputs4[i]; + __m128 xmm1 = _mm_shuffle_ps(xmm0, xmm0, 0xe5); + __m128 xmm2 = _mm_movehl_ps(xmm0, xmm0); + xmm1 = _mm_add_ss(xmm1, xmm0); + xmm0 = _mm_shuffle_ps(xmm0, xmm0, 0xe7); + xmm2 = _mm_add_ss(xmm2, xmm1); + xmm0 = _mm_add_ss(xmm0, xmm2); + outPtr[i] = _mm_cvtss_f32(xmm0); + } +} + +} // namespace sfz +} // namespace fx +#endif diff --git a/src/sfizz/effects/impl/ResonantArraySSE.h b/src/sfizz/effects/impl/ResonantArraySSE.h new file mode 100644 index 00000000..36a67579 --- /dev/null +++ b/src/sfizz/effects/impl/ResonantArraySSE.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "ResonantArray.h" +#include "Buffer.h" +#include "SIMDConfig.h" + +#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386 +namespace sfz { +namespace fx { + +class ResonantStringSSE; + +class ResonantArraySSE final : public ResonantArray { +public: + ResonantArraySSE(); + ~ResonantArraySSE(); + + void setup( + float sampleRate, unsigned numStrings, + const float pitches[], const float bandwidths[], + const float feedbacks[], const float gains[]) override; + + void setSamplesPerBlock(unsigned samplesPerBlock) override; + + void clear() override; + + void process(const float *inPtr, float *outPtr, unsigned numFrames) override; + +private: + std::unique_ptr _stringPacks; + unsigned _numStrings = 0; + Buffer _workBuffer; +}; + +} // namespace sfz +} // namespace fx +#endif diff --git a/src/sfizz/effects/impl/ResonantString.cpp b/src/sfizz/effects/impl/ResonantString.cpp new file mode 100644 index 00000000..7e7aaf40 --- /dev/null +++ b/src/sfizz/effects/impl/ResonantString.cpp @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +/** + Note(jpc): generated with faust and edited + */ + +/* ------------------------------------------------------------ +name: "resonant_string" +Code generated with Faust 2.20.2 (https://faust.grame.fr) +Compilation options: -lang cpp -inpl -os -scal -ftz 0 +------------------------------------------------------------ */ + +#include "ResonantString.h" +#include +#include +#include + +namespace sfz { +namespace fx { + +static float faustpower2_f(float value) +{ + return (value * value); +} + +void ResonantString::init(float sample_rate) +{ + fConst0 = sample_rate; + fConst1 = (6.28318548f / fConst0); + fConst2 = (2.0f / fConst0); + fConst3 = (2.0f * fConst0); + fConst4 = (3.14159274f / fConst0); + fConst5 = (0.5f / fConst0); + fConst6 = (4.0f * faustpower2_f(fConst0)); + fConst7 = faustpower2_f((1.0f / fConst0)); + fConst8 = (2.0f * fConst7); + + clear(); +} + +void ResonantString::clear() +{ + for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { + fRec0[l0] = 0.0f; + } + for (int l1 = 0; (l1 < 3); l1 = (l1 + 1)) { + fRec2[l1] = 0.0f; + } + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec1[l2] = 0.0f; + } +} + +void ResonantString::setGain(float gain) +{ + fControl[0] = gain; +} + +void ResonantString::setResonanceFeedback(float feedback) +{ + fControl[1] = feedback; +} + +void ResonantString::setResonanceFrequency(float frequency, float bandwidth) +{ + fControl[2] = frequency; + fControl[3] = (fConst1 * fControl[2]); + fControl[4] = std::sin(fControl[3]); + fControl[5] = std::cos(fControl[3]); + fControl[6] = (0.5f * bandwidth); + fControl[7] = std::tan((fConst4 * (fControl[6] + fControl[2]))); + fControl[8] = faustpower2_f(std::sqrt((fConst6 * (fControl[7] * std::tan((fConst4 * (fControl[2] - fControl[6]))))))); + fControl[9] = ((fConst3 * fControl[7]) - (fConst5 * (fControl[8] / fControl[7]))); + fControl[10] = (fConst7 * fControl[8]); + fControl[11] = (fConst2 * fControl[9]); + fControl[12] = ((fControl[10] + fControl[11]) + 4.0f); + fControl[13] = (fConst2 * (fControl[9] / fControl[12])); + fControl[14] = (0.0f - fControl[13]); + fControl[15] = (1.0f / fControl[12]); + fControl[16] = ((fConst8 * fControl[8]) + -8.0f); + fControl[17] = (fControl[10] + (4.0f - fControl[11])); +} + +float ResonantString::process(float input) +{ + fRec0[0] = (fControl[1] * ((fControl[4] * fRec1[1]) + (fControl[5] * fRec0[1]))); + float fTemp0 = input; + fRec2[0] = (fTemp0 - (fControl[15] * ((fControl[16] * fRec2[1]) + (fControl[17] * fRec2[2])))); + fRec1[0] = (((fControl[14] * fRec2[2]) + ((fControl[5] * fRec1[1]) + (fControl[13] * fRec2[0]))) - (fControl[4] * fRec0[1])); + float output = float((fControl[0] * fRec0[0])); + fRec0[1] = fRec0[0]; + fRec2[2] = fRec2[1]; + fRec2[1] = fRec2[0]; + fRec1[1] = fRec1[0]; + return output; +} + +} // namespace sfz +} // namespace fx diff --git a/src/sfizz/effects/impl/ResonantString.h b/src/sfizz/effects/impl/ResonantString.h new file mode 100644 index 00000000..df45a8ac --- /dev/null +++ b/src/sfizz/effects/impl/ResonantString.h @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +/** + Note(jpc): generated with faust and edited + */ + +/* ------------------------------------------------------------ +name: "resonant_string" +Code generated with Faust 2.20.2 (https://faust.grame.fr) +Compilation options: -lang cpp -inpl -os -scal -ftz 0 +------------------------------------------------------------ */ + +#pragma once + +namespace sfz { +namespace fx { + +class ResonantString { +public: + void init(float sample_rate); + void clear(); + void setGain(float gain); + void setResonanceFeedback(float feedback); + void setResonanceFrequency(float frequency, float bandwidth); + float process(float input); + +private: + float fConst0 {}; + float fConst1 {}; + float fRec0[2] {}; + float fConst2 {}; + float fConst3 {}; + float fConst4 {}; + float fConst5 {}; + float fConst6 {}; + float fConst7 {}; + float fConst8 {}; + float fRec2[3] {}; + float fRec1[2] {}; + float fControl[18] {}; +}; + +} // namespace sfz +} // namespace fx diff --git a/src/sfizz/effects/impl/ResonantStringAVX.cpp b/src/sfizz/effects/impl/ResonantStringAVX.cpp new file mode 100644 index 00000000..e1819b52 --- /dev/null +++ b/src/sfizz/effects/impl/ResonantStringAVX.cpp @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +/** + Note(jpc): generated with faust and edited + */ + +/* ------------------------------------------------------------ +name: "resonant_string" +Code generated with Faust 2.20.2 (https://faust.grame.fr) +Compilation options: -lang cpp -inpl -os -scal -ftz 0 +------------------------------------------------------------ */ + +#include "ResonantStringAVX.h" + +#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386 +#include +#include +#include + +namespace sfz { +namespace fx { + +static float faustpower2_f(float value) +{ + return (value * value); +} + +static __m256 faustpower2_v(__m256 value) +{ + return _mm256_mul_ps(value, value); +} + +static float load_nth_v(const __m256 &x, unsigned i) +{ + return reinterpret_cast(&x)[i]; +} + +static void store_nth_v(__m256 &x, unsigned i, float v) +{ + reinterpret_cast(&x)[i] = v; +} + +void ResonantStringAVX::init(float sample_rate) +{ + fConst0 = _mm256_set1_ps(sample_rate); + fConst1 = _mm256_div_ps(_mm256_set1_ps(6.28318548f), fConst0); + fConst2 = _mm256_div_ps(_mm256_set1_ps(2.0f), fConst0); + fConst3 = _mm256_mul_ps(_mm256_set1_ps(2.0f), fConst0); + fConst4 = _mm256_div_ps(_mm256_set1_ps(3.14159274f), fConst0); + fConst5 = _mm256_div_ps(_mm256_set1_ps(0.5f), fConst0); + fConst6 = _mm256_mul_ps(_mm256_set1_ps(4.0f), faustpower2_v(fConst0)); + fConst7 = faustpower2_v(_mm256_div_ps(_mm256_set1_ps(1.0f), fConst0)); + fConst8 = _mm256_mul_ps(_mm256_set1_ps(2.0f), fConst7); + + clear(); +} + +void ResonantStringAVX::clear() +{ + for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { + fRec0[l0] = _mm256_set1_ps(0.0f); + } + for (int l1 = 0; (l1 < 3); l1 = (l1 + 1)) { + fRec2[l1] = _mm256_set1_ps(0.0f); + } + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec1[l2] = _mm256_set1_ps(0.0f); + } +} + +void ResonantStringAVX::setGain(__m256 gain) +{ + fControl[0] = gain; +} + +void ResonantStringAVX::setResonanceFeedback(__m256 feedback) +{ + fControl[1] = feedback; +} + +void ResonantStringAVX::setResonanceFrequency(__m256 frequency, __m256 bandwidth) +{ + fControl[2] = frequency; + fControl[3] = _mm256_mul_ps(fConst1, fControl[2]); + for (int i = 0; i < int(sizeof(__m256) / sizeof(float)); ++i) { + store_nth_v(fControl[4], i, std::sin(load_nth_v(fControl[3], i))); + store_nth_v(fControl[5], i, std::cos(load_nth_v(fControl[3], i))); + } + fControl[6] = _mm256_mul_ps(_mm256_set1_ps(0.5f), bandwidth); + for (int i = 0; i < int(sizeof(__m256) / sizeof(float)); ++i) { + store_nth_v(fControl[7], i, std::tan((load_nth_v(fConst4, i) * (load_nth_v(fControl[6], i) + load_nth_v(fControl[2], i))))); + store_nth_v(fControl[8], i, faustpower2_f(std::sqrt((load_nth_v(fConst6, i) * (load_nth_v(fControl[7], i) * std::tan((load_nth_v(fConst4, i) * (load_nth_v(fControl[2], i) - load_nth_v(fControl[6], i))))))))); + } + fControl[9] = _mm256_sub_ps(_mm256_mul_ps(fConst3, fControl[7]), _mm256_mul_ps(fConst5, _mm256_div_ps(fControl[8], fControl[7]))); + fControl[10] = _mm256_mul_ps(fConst7, fControl[8]); + fControl[11] = _mm256_mul_ps(fConst2, fControl[9]); + fControl[12] = _mm256_add_ps(_mm256_add_ps(fControl[10], fControl[11]), _mm256_set1_ps(4.0f)); + fControl[13] = _mm256_mul_ps(fConst2, _mm256_div_ps(fControl[9], fControl[12])); + fControl[14] = _mm256_sub_ps(_mm256_set1_ps(0.0f), fControl[13]); + fControl[15] = _mm256_div_ps(_mm256_set1_ps(1.0f), fControl[12]); + fControl[16] = _mm256_add_ps(_mm256_mul_ps(fConst8, fControl[8]), _mm256_set1_ps(-8.0f)); + fControl[17] = _mm256_add_ps(fControl[10], _mm256_sub_ps(_mm256_set1_ps(4.0f), fControl[11])); +} + +__m256 ResonantStringAVX::process(__m256 input) +{ + fRec0[0] = _mm256_mul_ps(fControl[1], _mm256_add_ps(_mm256_mul_ps(fControl[4], fRec1[1]), _mm256_mul_ps(fControl[5], fRec0[1]))); + __m256 fTemp0 = input; + fRec2[0] = _mm256_sub_ps(fTemp0, _mm256_mul_ps(fControl[15], _mm256_add_ps(_mm256_mul_ps(fControl[16], fRec2[1]), _mm256_mul_ps(fControl[17], fRec2[2])))); + fRec1[0] = _mm256_sub_ps(_mm256_add_ps(_mm256_mul_ps(fControl[14], fRec2[2]), _mm256_add_ps(_mm256_mul_ps(fControl[5], fRec1[1]), _mm256_mul_ps(fControl[13], fRec2[0]))),_mm256_mul_ps(fControl[4], fRec0[1])); + __m256 output = _mm256_mul_ps(fControl[0], fRec0[0]); + fRec0[1] = fRec0[0]; + fRec2[2] = fRec2[1]; + fRec2[1] = fRec2[0]; + fRec1[1] = fRec1[0]; + return output; +} + +} // namespace sfz +} // namespace fx +#endif diff --git a/src/sfizz/effects/impl/ResonantStringAVX.h b/src/sfizz/effects/impl/ResonantStringAVX.h new file mode 100644 index 00000000..4bcc1ec7 --- /dev/null +++ b/src/sfizz/effects/impl/ResonantStringAVX.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +/** + Note(jpc): generated with faust and edited + */ + +/* ------------------------------------------------------------ +name: "resonant_string" +Code generated with Faust 2.20.2 (https://faust.grame.fr) +Compilation options: -lang cpp -inpl -os -scal -ftz 0 +------------------------------------------------------------ */ + +#pragma once +#include "SIMDConfig.h" + +#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386 +#include "immintrin.h" + +namespace sfz { +namespace fx { + +class alignas(32) ResonantStringAVX { +public: + void init(float sample_rate); + void clear(); + void setGain(__m256 gain); + void setResonanceFeedback(__m256 feedback); + void setResonanceFrequency(__m256 frequency, __m256 bandwidth); + __m256 process(__m256 input); + +private: + __m256 fConst0 {}; + __m256 fConst1 {}; + __m256 fRec0[2] {}; + __m256 fConst2 {}; + __m256 fConst3 {}; + __m256 fConst4 {}; + __m256 fConst5 {}; + __m256 fConst6 {}; + __m256 fConst7 {}; + __m256 fConst8 {}; + __m256 fRec2[3] {}; + __m256 fRec1[2] {}; + __m256 fControl[18] {}; +}; + +} // namespace sfz +} // namespace fx +#endif diff --git a/src/sfizz/effects/impl/ResonantStringSSE.cpp b/src/sfizz/effects/impl/ResonantStringSSE.cpp new file mode 100644 index 00000000..df802d48 --- /dev/null +++ b/src/sfizz/effects/impl/ResonantStringSSE.cpp @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +/** + Note(jpc): generated with faust and edited + */ + +/* ------------------------------------------------------------ +name: "resonant_string" +Code generated with Faust 2.20.2 (https://faust.grame.fr) +Compilation options: -lang cpp -inpl -os -scal -ftz 0 +------------------------------------------------------------ */ + +#include "ResonantStringSSE.h" + +#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386 +#include +#include +#include + +namespace sfz { +namespace fx { + +static float faustpower2_f(float value) +{ + return (value * value); +} + +static __m128 faustpower2_v(__m128 value) +{ + return _mm_mul_ps(value, value); +} + +static float load_nth_v(const __m128 &x, unsigned i) +{ + return reinterpret_cast(&x)[i]; +} + +static void store_nth_v(__m128 &x, unsigned i, float v) +{ + reinterpret_cast(&x)[i] = v; +} + +void ResonantStringSSE::init(float sample_rate) +{ + fConst0 = _mm_set1_ps(sample_rate); + fConst1 = _mm_div_ps(_mm_set1_ps(6.28318548f), fConst0); + fConst2 = _mm_div_ps(_mm_set1_ps(2.0f), fConst0); + fConst3 = _mm_mul_ps(_mm_set1_ps(2.0f), fConst0); + fConst4 = _mm_div_ps(_mm_set1_ps(3.14159274f), fConst0); + fConst5 = _mm_div_ps(_mm_set1_ps(0.5f), fConst0); + fConst6 = _mm_mul_ps(_mm_set1_ps(4.0f), faustpower2_v(fConst0)); + fConst7 = faustpower2_v(_mm_div_ps(_mm_set1_ps(1.0f), fConst0)); + fConst8 = _mm_mul_ps(_mm_set1_ps(2.0f), fConst7); + + clear(); +} + +void ResonantStringSSE::clear() +{ + for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { + fRec0[l0] = _mm_set1_ps(0.0f); + } + for (int l1 = 0; (l1 < 3); l1 = (l1 + 1)) { + fRec2[l1] = _mm_set1_ps(0.0f); + } + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec1[l2] = _mm_set1_ps(0.0f); + } +} + +void ResonantStringSSE::setGain(__m128 gain) +{ + fControl[0] = gain; +} + +void ResonantStringSSE::setResonanceFeedback(__m128 feedback) +{ + fControl[1] = feedback; +} + +void ResonantStringSSE::setResonanceFrequency(__m128 frequency, __m128 bandwidth) +{ + fControl[2] = frequency; + fControl[3] = _mm_mul_ps(fConst1, fControl[2]); + for (int i = 0; i < int(sizeof(__m128) / sizeof(float)); ++i) { + store_nth_v(fControl[4], i, std::sin(load_nth_v(fControl[3], i))); + store_nth_v(fControl[5], i, std::cos(load_nth_v(fControl[3], i))); + } + fControl[6] = _mm_mul_ps(_mm_set1_ps(0.5f), bandwidth); + for (int i = 0; i < int(sizeof(__m128) / sizeof(float)); ++i) { + store_nth_v(fControl[7], i, std::tan((load_nth_v(fConst4, i) * (load_nth_v(fControl[6], i) + load_nth_v(fControl[2], i))))); + store_nth_v(fControl[8], i, faustpower2_f(std::sqrt((load_nth_v(fConst6, i) * (load_nth_v(fControl[7], i) * std::tan((load_nth_v(fConst4, i) * (load_nth_v(fControl[2], i) - load_nth_v(fControl[6], i))))))))); + } + fControl[9] = _mm_sub_ps(_mm_mul_ps(fConst3, fControl[7]), _mm_mul_ps(fConst5, _mm_div_ps(fControl[8], fControl[7]))); + fControl[10] = _mm_mul_ps(fConst7, fControl[8]); + fControl[11] = _mm_mul_ps(fConst2, fControl[9]); + fControl[12] = _mm_add_ps(_mm_add_ps(fControl[10], fControl[11]), _mm_set1_ps(4.0f)); + fControl[13] = _mm_mul_ps(fConst2, _mm_div_ps(fControl[9], fControl[12])); + fControl[14] = _mm_sub_ps(_mm_set1_ps(0.0f), fControl[13]); + fControl[15] = _mm_div_ps(_mm_set1_ps(1.0f), fControl[12]); + fControl[16] = _mm_add_ps(_mm_mul_ps(fConst8, fControl[8]), _mm_set1_ps(-8.0f)); + fControl[17] = _mm_add_ps(fControl[10], _mm_sub_ps(_mm_set1_ps(4.0f), fControl[11])); +} + +__m128 ResonantStringSSE::process(__m128 input) +{ + fRec0[0] = _mm_mul_ps(fControl[1], _mm_add_ps(_mm_mul_ps(fControl[4], fRec1[1]), _mm_mul_ps(fControl[5], fRec0[1]))); + __m128 fTemp0 = input; + fRec2[0] = _mm_sub_ps(fTemp0, _mm_mul_ps(fControl[15], _mm_add_ps(_mm_mul_ps(fControl[16], fRec2[1]), _mm_mul_ps(fControl[17], fRec2[2])))); + fRec1[0] = _mm_sub_ps(_mm_add_ps(_mm_mul_ps(fControl[14], fRec2[2]), _mm_add_ps(_mm_mul_ps(fControl[5], fRec1[1]), _mm_mul_ps(fControl[13], fRec2[0]))),_mm_mul_ps(fControl[4], fRec0[1])); + __m128 output = _mm_mul_ps(fControl[0], fRec0[0]); + fRec0[1] = fRec0[0]; + fRec2[2] = fRec2[1]; + fRec2[1] = fRec2[0]; + fRec1[1] = fRec1[0]; + return output; +} + +} // namespace sfz +} // namespace fx +#endif diff --git a/src/sfizz/effects/impl/ResonantStringSSE.h b/src/sfizz/effects/impl/ResonantStringSSE.h new file mode 100644 index 00000000..ca32b5e2 --- /dev/null +++ b/src/sfizz/effects/impl/ResonantStringSSE.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +/** + Note(jpc): generated with faust and edited + */ + +/* ------------------------------------------------------------ +name: "resonant_string" +Code generated with Faust 2.20.2 (https://faust.grame.fr) +Compilation options: -lang cpp -inpl -os -scal -ftz 0 +------------------------------------------------------------ */ + +#pragma once +#include "SIMDConfig.h" + +#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386 +#include "xmmintrin.h" + +namespace sfz { +namespace fx { + +class alignas(16) ResonantStringSSE { +public: + void init(float sample_rate); + void clear(); + void setGain(__m128 gain); + void setResonanceFeedback(__m128 feedback); + void setResonanceFrequency(__m128 frequency, __m128 bandwidth); + __m128 process(__m128 input); + +private: + __m128 fConst0 {}; + __m128 fConst1 {}; + __m128 fRec0[2] {}; + __m128 fConst2 {}; + __m128 fConst3 {}; + __m128 fConst4 {}; + __m128 fConst5 {}; + __m128 fConst6 {}; + __m128 fConst7 {}; + __m128 fConst8 {}; + __m128 fRec2[3] {}; + __m128 fRec1[2] {}; + __m128 fControl[18] {}; +}; + +} // namespace sfz +} // namespace fx +#endif