SIMD-accelerated strings effect
This commit is contained in:
parent
3aaf44b636
commit
026a5aceee
22 changed files with 1045 additions and 240 deletions
|
|
@ -2,8 +2,8 @@ project(sfizz)
|
||||||
|
|
||||||
# Check SIMD
|
# Check SIMD
|
||||||
include (SfizzSIMDSourceFiles)
|
include (SfizzSIMDSourceFiles)
|
||||||
set(BENCHMARK_SIMD_SOURCES ${SFIZZ_SIMD_SOURCES})
|
set(BENCHMARK_SIMD_SOURCES)
|
||||||
list(TRANSFORM BENCHMARK_SIMD_SOURCES PREPEND "../src/")
|
sfizz_add_simd_sources(BENCHMARK_SIMD_SOURCES "../src")
|
||||||
find_package(benchmark CONFIG REQUIRED)
|
find_package(benchmark CONFIG REQUIRED)
|
||||||
|
|
||||||
# Check libsamplerate
|
# Check libsamplerate
|
||||||
|
|
|
||||||
|
|
@ -15,22 +15,29 @@ if (WIN32)
|
||||||
add_compile_definitions(_WIN32_WINNT=0x601)
|
add_compile_definitions(_WIN32_WINNT=0x601)
|
||||||
endif()
|
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
|
# Add required flags for the builds
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||||
add_compile_options(-Wall)
|
add_compile_options(-Wall)
|
||||||
add_compile_options(-Wextra)
|
add_compile_options(-Wextra)
|
||||||
add_compile_options(-ffast-math)
|
add_compile_options(-ffast-math)
|
||||||
add_compile_options(-fno-omit-frame-pointer) # For debugging purposes
|
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")
|
elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
add_compile_options(/Zc:__cplusplus)
|
add_compile_options(/Zc:__cplusplus)
|
||||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||||
endif()
|
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)
|
add_library(sfizz-sndfile INTERFACE)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,21 @@
|
||||||
set (SFIZZ_SIMD_SOURCES
|
macro(sfizz_add_simd_sources SOURCES_VAR PREFIX)
|
||||||
sfizz/SIMDSSE.cpp
|
# It needs a macro, otherwise the source properties cannot take effect.
|
||||||
sfizz/SIMDNEON.cpp
|
|
||||||
sfizz/SIMDDummy.cpp)
|
|
||||||
|
|
||||||
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()
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,15 @@ set (SFIZZ_SOURCES
|
||||||
sfizz/effects/Rectify.cpp
|
sfizz/effects/Rectify.cpp
|
||||||
sfizz/effects/Gain.cpp
|
sfizz/effects/Gain.cpp
|
||||||
sfizz/effects/Width.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)
|
include (SfizzSIMDSourceFiles)
|
||||||
|
sfizz_add_simd_sources (SFIZZ_SOURCES ".")
|
||||||
|
|
||||||
# Parser core library
|
# Parser core library
|
||||||
add_library (sfizz_parser STATIC)
|
add_library (sfizz_parser STATIC)
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,7 @@ private:
|
||||||
static constexpr int TypeAlignment { Alignment / sizeof(value_type) };
|
static constexpr int TypeAlignment { Alignment / sizeof(value_type) };
|
||||||
static constexpr int TypeAlignmentMask { TypeAlignment - 1 };
|
static constexpr int TypeAlignmentMask { TypeAlignment - 1 };
|
||||||
static_assert(std::is_arithmetic<value_type>::value, "Type should be arithmetic");
|
static_assert(std::is_arithmetic<value_type>::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");
|
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 )
|
void* align(std::size_t alignment, std::size_t size, void *&ptr, std::size_t &space )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -67,3 +67,30 @@
|
||||||
# define SFIZZ_HAVE_NEON 0
|
# define SFIZZ_HAVE_NEON 0
|
||||||
# endif
|
# endif
|
||||||
#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
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,9 @@ Extensions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Strings.h"
|
#include "Strings.h"
|
||||||
#include "StringsPrivate.h"
|
#include "impl/ResonantArray.h"
|
||||||
|
#include "impl/ResonantArraySSE.h"
|
||||||
|
#include "impl/ResonantArrayAVX.h"
|
||||||
#include "Opcode.h"
|
#include "Opcode.h"
|
||||||
#include "MathHelpers.h"
|
#include "MathHelpers.h"
|
||||||
#include "SIMDHelpers.h"
|
#include "SIMDHelpers.h"
|
||||||
|
|
@ -26,13 +28,8 @@ Extensions
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
namespace fx {
|
namespace fx {
|
||||||
|
|
||||||
struct Strings::ResonantString {
|
|
||||||
Bw2BPF bpf;
|
|
||||||
WgResonator res;
|
|
||||||
};
|
|
||||||
|
|
||||||
Strings::Strings()
|
Strings::Strings()
|
||||||
: _strings(new ResonantString[MaximumNumStrings])
|
: _stringsArray(new ResonantArrayScalar)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,42 +39,48 @@ namespace fx {
|
||||||
|
|
||||||
void Strings::setSampleRate(double sampleRate)
|
void Strings::setSampleRate(double sampleRate)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0, n = _numStrings; i < n; ++i) {
|
const unsigned numStrings = _numStrings;
|
||||||
ResonantString& rs = _strings[i];
|
|
||||||
rs.bpf.init(sampleRate);
|
|
||||||
rs.res.init(sampleRate);
|
|
||||||
|
|
||||||
|
AudioBuffer<float, 4> 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;
|
int midiNote = i + 24;
|
||||||
double midiFrequency = 440.0 * std::exp2((midiNote - 69) * (1.0 / 12.0));
|
pitches[i] = 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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<float>(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)
|
void Strings::setSamplesPerBlock(int samplesPerBlock)
|
||||||
{
|
{
|
||||||
_tempBuffer.resize(samplesPerBlock);
|
_tempBuffer.resize(samplesPerBlock);
|
||||||
|
|
||||||
|
_stringsArray->setSamplesPerBlock(samplesPerBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Strings::clear()
|
void Strings::clear()
|
||||||
{
|
{
|
||||||
for (unsigned i = 0, n = _numStrings; i < n; ++i) {
|
_stringsArray->clear();
|
||||||
ResonantString& rs = _strings[i];
|
|
||||||
rs.bpf.clear();
|
|
||||||
rs.res.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Strings::process(const float* const inputs[], float* const outputs[], unsigned nframes)
|
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
|
// generate the strings summed into a common buffer
|
||||||
absl::Span<float> resOutput = _tempBuffer.getSpan(1).first(nframes);
|
absl::Span<float> resOutput = _tempBuffer.getSpan(1).first(nframes);
|
||||||
sfz::fill(resOutput, 0.0f);
|
|
||||||
|
|
||||||
for (unsigned is = 0, ns = _numStrings; is < ns; ++is) {
|
_stringsArray->process(resInput.data(), resOutput.data(), nframes);
|
||||||
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
|
|
||||||
|
|
||||||
// mix the resonator into the output
|
// mix the resonator into the output
|
||||||
auto outputL = absl::MakeSpan(outputs[0], nframes);
|
auto outputL = absl::MakeSpan(outputs[0], nframes);
|
||||||
auto outputR = absl::MakeSpan(outputs[1], nframes);
|
auto outputR = absl::MakeSpan(outputs[1], nframes);
|
||||||
|
|
||||||
constexpr float resAttenuate = 1e-3; // need significant attenuation, here -60dB
|
|
||||||
|
|
||||||
absl::Span<float> wet = _tempBuffer.getSpan(2).first(nframes);
|
absl::Span<float> 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(inputL, outputL);
|
||||||
sfz::copy(inputR, outputR);
|
sfz::copy(inputR, outputR);
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,7 @@
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
namespace fx {
|
namespace fx {
|
||||||
|
|
||||||
class Bw2BPF;
|
class ResonantArray;
|
||||||
class WgResonator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief String resonance effect
|
* @brief String resonance effect
|
||||||
|
|
@ -55,8 +54,7 @@ namespace fx {
|
||||||
unsigned _numStrings = MaximumNumStrings;
|
unsigned _numStrings = MaximumNumStrings;
|
||||||
float _wet = 0;
|
float _wet = 0;
|
||||||
|
|
||||||
struct ResonantString;
|
std::unique_ptr<ResonantArray> _stringsArray;
|
||||||
std::unique_ptr<ResonantString[]> _strings;
|
|
||||||
|
|
||||||
AudioBuffer<float, 3> _tempBuffer { 3, config::defaultSamplesPerBlock };
|
AudioBuffer<float, 3> _tempBuffer { 3, config::defaultSamplesPerBlock };
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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 <cmath>
|
|
||||||
|
|
||||||
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
|
|
||||||
8
src/sfizz/effects/dsp/resonant_string.dsp
Normal file
8
src/sfizz/effects/dsp/resonant_string.dsp
Normal file
|
|
@ -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);
|
||||||
70
src/sfizz/effects/impl/ResonantArray.cpp
Normal file
70
src/sfizz/effects/impl/ResonantArray.cpp
Normal file
|
|
@ -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
|
||||||
57
src/sfizz/effects/impl/ResonantArray.h
Normal file
57
src/sfizz/effects/impl/ResonantArray.h
Normal file
|
|
@ -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 <memory>
|
||||||
|
|
||||||
|
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<ResonantString[]> _strings;
|
||||||
|
unsigned _numStrings = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sfz
|
||||||
|
} // namespace fx
|
||||||
104
src/sfizz/effects/impl/ResonantArrayAVX.cpp
Normal file
104
src/sfizz/effects/impl/ResonantArrayAVX.cpp
Normal file
|
|
@ -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 <cstring>
|
||||||
|
|
||||||
|
#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
|
||||||
42
src/sfizz/effects/impl/ResonantArrayAVX.h
Normal file
42
src/sfizz/effects/impl/ResonantArrayAVX.h
Normal file
|
|
@ -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<ResonantStringAVX[]> _stringPacks;
|
||||||
|
unsigned _numStrings = 0;
|
||||||
|
Buffer<float, 32> _workBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sfz
|
||||||
|
} // namespace fx
|
||||||
|
#endif
|
||||||
107
src/sfizz/effects/impl/ResonantArraySSE.cpp
Normal file
107
src/sfizz/effects/impl/ResonantArraySSE.cpp
Normal file
|
|
@ -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 <cstring>
|
||||||
|
|
||||||
|
#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
|
||||||
42
src/sfizz/effects/impl/ResonantArraySSE.h
Normal file
42
src/sfizz/effects/impl/ResonantArraySSE.h
Normal file
|
|
@ -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<ResonantStringSSE[]> _stringPacks;
|
||||||
|
unsigned _numStrings = 0;
|
||||||
|
Buffer<float, 16> _workBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sfz
|
||||||
|
} // namespace fx
|
||||||
|
#endif
|
||||||
103
src/sfizz/effects/impl/ResonantString.cpp
Normal file
103
src/sfizz/effects/impl/ResonantString.cpp
Normal file
|
|
@ -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 <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
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
|
||||||
48
src/sfizz/effects/impl/ResonantString.h
Normal file
48
src/sfizz/effects/impl/ResonantString.h
Normal file
|
|
@ -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
|
||||||
125
src/sfizz/effects/impl/ResonantStringAVX.cpp
Normal file
125
src/sfizz/effects/impl/ResonantStringAVX.cpp
Normal file
|
|
@ -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 <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
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<const float *>(&x)[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void store_nth_v(__m256 &x, unsigned i, float v)
|
||||||
|
{
|
||||||
|
reinterpret_cast<float *>(&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
|
||||||
53
src/sfizz/effects/impl/ResonantStringAVX.h
Normal file
53
src/sfizz/effects/impl/ResonantStringAVX.h
Normal file
|
|
@ -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
|
||||||
125
src/sfizz/effects/impl/ResonantStringSSE.cpp
Normal file
125
src/sfizz/effects/impl/ResonantStringSSE.cpp
Normal file
|
|
@ -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 <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
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<const float *>(&x)[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void store_nth_v(__m128 &x, unsigned i, float v)
|
||||||
|
{
|
||||||
|
reinterpret_cast<float *>(&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
|
||||||
53
src/sfizz/effects/impl/ResonantStringSSE.h
Normal file
53
src/sfizz/effects/impl/ResonantStringSSE.h
Normal file
|
|
@ -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
|
||||||
Loading…
Add table
Reference in a new issue