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
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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$<$<CONFIG:Debug>: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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>::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 )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<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;
|
||||
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<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)
|
||||
{
|
||||
_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<float> 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<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(inputR, outputR);
|
||||
|
|
|
|||
|
|
@ -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<ResonantString[]> _strings;
|
||||
std::unique_ptr<ResonantArray> _stringsArray;
|
||||
|
||||
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