Merge pull request #149 from jpcima/strings-simd

SIMD-accelerated string resonator
This commit is contained in:
Paul Ferrand 2020-04-02 17:53:27 +02:00 committed by GitHub
commit e91b553f0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 1975 additions and 252 deletions

View file

@ -5,5 +5,7 @@ set -ex
mkdir -p build/${INSTALL_DIR} && cd build
buildenv mod-plugin-builder /usr/local/bin/cmake -DCMAKE_BUILD_TYPE=Release -DSFIZZ_JACK=OFF ..
buildenv mod-plugin-builder /usr/local/bin/cmake \
-DSFIZZ_SYSTEM_PROCESSOR=armv7-a \
-DCMAKE_BUILD_TYPE=Release -DSFIZZ_JACK=OFF ..
buildenv mod-plugin-builder make -j

View 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
#include "Macros.h"
#include "ScopedFTZ.h"
#include "MathHelpers.h"
#include "SIMDConfig.h"
#include "effects/impl/ResonantArray.h"
#include "effects/impl/ResonantArraySSE.h"
#include "effects/impl/ResonantArrayAVX.h"
#include <benchmark/benchmark.h>
#include <random>
#include <vector>
class StringResonator : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) {
std::random_device rd { };
std::mt19937 gen { rd() };
numStrings = state.range(0);
pitches.resize(numStrings);
bandwidths.resize(numStrings);
feedbacks.resize(numStrings);
gains.resize(numStrings);
std::generate(pitches.begin(), pitches.end(), [&]() {
std::uniform_int_distribution<> dist {0, 127};
return midiNoteFrequency(dist(gen));
});
std::fill(bandwidths.begin(), bandwidths.end(), 1.0f);
std::fill(feedbacks.begin(), feedbacks.end(), std::exp(-6.91 / (50e-3 * sampleRate)));
std::fill(gains.begin(), gains.end(), 1e-3f);
input.resize(numFrames);
output.resize(numFrames);
std::generate(input.begin(), input.end(), [&]() {
std::uniform_real_distribution<> dist {-1, 1};
return dist(gen);
});
}
void TearDown(const ::benchmark::State& state) {
UNUSED(state);
}
static constexpr float sampleRate = 44100;
static constexpr unsigned numFrames = sampleRate * 1.0;
std::vector<float> input;
std::vector<float> output;
unsigned numStrings = 0;
std::vector<float> pitches;
std::vector<float> bandwidths;
std::vector<float> feedbacks;
std::vector<float> gains;
};
BENCHMARK_DEFINE_F(StringResonator, StringResonator_Scalar)(benchmark::State& state) {
ScopedFTZ ftz;
sfz::fx::ResonantArrayScalar resonator;
resonator.setup(sampleRate, numStrings, pitches.data(), bandwidths.data(), feedbacks.data(), gains.data());
resonator.setSamplesPerBlock(numFrames);
for (auto _ : state)
{
resonator.process(input.data(), output.data(), numFrames);
}
}
#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386
BENCHMARK_DEFINE_F(StringResonator, StringResonator_SSE)(benchmark::State& state) {
ScopedFTZ ftz;
sfz::fx::ResonantArraySSE resonator;
resonator.setup(sampleRate, numStrings, pitches.data(), bandwidths.data(), feedbacks.data(), gains.data());
resonator.setSamplesPerBlock(numFrames);
for (auto _ : state)
{
resonator.process(input.data(), output.data(), numFrames);
}
}
BENCHMARK_DEFINE_F(StringResonator, StringResonator_AVX)(benchmark::State& state) {
ScopedFTZ ftz;
sfz::fx::ResonantArrayAVX resonator;
resonator.setup(sampleRate, numStrings, pitches.data(), bandwidths.data(), feedbacks.data(), gains.data());
resonator.setSamplesPerBlock(numFrames);
for (auto _ : state)
{
resonator.process(input.data(), output.data(), numFrames);
}
}
#endif
BENCHMARK_REGISTER_F(StringResonator, StringResonator_Scalar)->RangeMultiplier(4)->Range(1, 128);
#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386
BENCHMARK_REGISTER_F(StringResonator, StringResonator_SSE)->RangeMultiplier(4)->Range(1, 128);
BENCHMARK_REGISTER_F(StringResonator, StringResonator_AVX)->RangeMultiplier(4)->Range(1, 128);
#endif
BENCHMARK_MAIN();

View file

@ -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
@ -93,6 +93,15 @@ target_link_libraries(bm_filterModulation PRIVATE sfizz-sndfile)
sfizz_add_benchmark(bm_filterStereoMono BM_filterStereoMono.cpp ../src/sfizz/SfzFilter.cpp)
target_link_libraries(bm_filterStereoMono PRIVATE sfizz-sndfile)
sfizz_add_benchmark(bm_stringResonator BM_stringResonator.cpp
../src/sfizz/effects/impl/ResonantArray.cpp
../src/sfizz/effects/impl/ResonantArraySSE.cpp
../src/sfizz/effects/impl/ResonantArrayAVX.cpp
../src/sfizz/effects/impl/ResonantString.cpp
../src/sfizz/effects/impl/ResonantStringSSE.cpp
../src/sfizz/effects/impl/ResonantStringAVX.cpp)
target_link_libraries(bm_stringResonator PRIVATE sfizz-sndfile)
add_custom_target(sfizz_benchmarks)
add_dependencies(sfizz_benchmarks
bm_opf_high_vs_low
@ -126,6 +135,7 @@ add_dependencies(sfizz_benchmarks
bm_flacfile
bm_filterModulation
bm_filterStereoMono
bm_stringResonator
)
if (TARGET bm_resample)

View file

@ -15,22 +15,31 @@ 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 (NOT SFIZZ_SYSTEM_PROCESSOR)
if(MSVC)
set(SFIZZ_SYSTEM_PROCESSOR "${MSVC_CXX_ARCHITECTURE_ID}")
else()
set(SFIZZ_SYSTEM_PROCESSOR "${CMAKE_SYSTEM_PROCESSOR}")
endif()
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)
@ -79,6 +88,7 @@ function (show_build_info_if_needed)
message (STATUS "
Project name: ${PROJECT_NAME}
Build type: ${CMAKE_BUILD_TYPE}
Build processor: ${SFIZZ_SYSTEM_PROCESSOR}
Build using LTO: ${ENABLE_LTO}
Build as shared library: ${SFIZZ_SHARED}
Build JACK stand-alone client: ${SFIZZ_JACK}

View file

@ -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()

View file

@ -11,13 +11,8 @@ else()
"Install destination for VST bundle [default: ${CMAKE_INSTALL_PREFIX}/lib/vst3}]")
endif()
# The variable CMAKE_SYSTEM_PROCESSOR is incorrect on Visual studio...
# see https://gitlab.kitware.com/cmake/cmake/issues/15170
if(MSVC)
set(VST3_SYSTEM_PROCESSOR "${MSVC_CXX_ARCHITECTURE_ID}")
else()
set(VST3_SYSTEM_PROCESSOR "${CMAKE_SYSTEM_PROCESSOR}")
if (NOT VST3_SYSTEM_PROCESSOR)
set(VST3_SYSTEM_PROCESSOR "${SFIZZ_SYSTEM_PROCESSOR}")
endif()
message(STATUS "The system architecture is: ${VST3_SYSTEM_PROCESSOR}")

View file

@ -1,6 +1,7 @@
include (GNUInstallDirs)
add_subdirectory(external/kiss_fft)
add_subdirectory(external/cpuid)
set (SFIZZ_SOURCES
sfizz/Synth.cpp
@ -29,8 +30,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)
@ -47,7 +55,7 @@ target_sources(sfizz_static PRIVATE ${SFIZZ_SOURCES} sfizz/sfizz_wrapper.cpp sfi
target_include_directories (sfizz_static PUBLIC .)
target_include_directories (sfizz_static PUBLIC external)
target_link_libraries (sfizz_static PUBLIC absl::strings absl::span)
target_link_libraries (sfizz_static PRIVATE sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-kissfft)
target_link_libraries (sfizz_static PRIVATE sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-kissfft sfizz-cpuid)
set_target_properties (sfizz_static PROPERTIES OUTPUT_NAME sfizz PUBLIC_HEADER "sfizz.h;sfizz.hpp")
if (WIN32)
target_compile_definitions (sfizz_static PRIVATE _USE_MATH_DEFINES)
@ -82,7 +90,7 @@ if (SFIZZ_SHARED)
target_sources(sfizz_shared PRIVATE ${SFIZZ_SOURCES} sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp)
target_include_directories (sfizz_shared PRIVATE .)
target_include_directories (sfizz_shared PRIVATE external)
target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-kissfft)
target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-kissfft sfizz-cpuid)
if (WIN32)
target_compile_definitions (sfizz_shared PRIVATE _USE_MATH_DEFINES)
endif()

6
src/external/cpuid/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,6 @@
cmake_minimum_required (VERSION 3.5)
project(sfizz-cpuid)
add_library(sfizz-cpuid STATIC src/cpuid/cpuinfo.cpp src/cpuid/version.cpp)
set_property(TARGET sfizz-cpuid PROPERTY CXX_STANDARD 11)
target_include_directories(sfizz-cpuid PUBLIC src PRIVATE platform/src)

31
src/external/cpuid/LICENSE.rst vendored Normal file
View file

@ -0,0 +1,31 @@
cpuid license
-------------
cpuid is provided under the "BSD (3-clause) License"::
Copyright (c) 2014, Steinwurf ApS
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Steinwurf ApS nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL Steinwurf ApS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

31
src/external/cpuid/platform/LICENSE.rst vendored Normal file
View file

@ -0,0 +1,31 @@
platform license
----------------
platform is provided under the "BSD (3-clause) License"::
Copyright (c) 2014, Steinwurf ApS
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Steinwurf ApS nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL Steinwurf ApS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,159 @@
// Copyright (c) 2014 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.
#pragma once
// Here we create a number of defines to make it easy to choose between
// different compilers, operatings systems and CPU architectures.
// Some information about the defines used can be found here:
// http://sourceforge.net/p/predef/wiki/Architectures/
// Detect operating systems
#if defined(__linux__)
#define PLATFORM_LINUX 1
#if defined(__ANDROID__)
#define PLATFORM_ANDROID 1
#endif
#elif defined(_WIN32)
#define PLATFORM_WINDOWS 1
#if defined(WINAPI_FAMILY)
#include <winapifamily.h>
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
#define PLATFORM_WINDOWS_PHONE 1
#endif
#endif
#elif defined(__APPLE__)
// Detect iOS before MacOSX (__MACH__ is also defined for iOS)
#if defined(IPHONE)
#define PLATFORM_IOS 1
#elif defined(__MACH__)
#define PLATFORM_MAC 1
#endif
#elif defined(__EMSCRIPTEN__)
#define PLATFORM_EMSCRIPTEN 1
#else
#error "Unable to determine operating system"
#endif
// Detect compilers and CPU architectures
// Note: clang also defines __GNUC__ since it aims to be compatible with GCC.
// Therefore we need to check for __clang__ or __llvm__ first.
#if defined(__clang__) || defined(__llvm__)
#define PLATFORM_CLANG 1
#define PLATFORM_GCC_COMPATIBLE 1
#if defined(__i386__) || defined(__x86_64__)
#define PLATFORM_X86 1
#define PLATFORM_CLANG_X86 1
#define PLATFORM_GCC_COMPATIBLE_X86 1
#elif defined(__arm__) || defined (__arm64__) || defined (__aarch64__)
#define PLATFORM_ARM 1
#define PLATFORM_CLANG_ARM 1
#define PLATFORM_GCC_COMPATIBLE_ARM 1
#elif defined(__mips__)
#define PLATFORM_MIPS 1
#define PLATFORM_CLANG_MIPS 1
#define PLATFORM_GCC_COMPATIBLE_MIPS 1
#elif defined(__asmjs__)
#define PLATFORM_ASMJS 1
#define PLATFORM_CLANG_ASMJS 1
#define PLATFORM_GCC_COMPATIBLE_ASMJS 1
#endif
#elif defined(__GNUC__)
#define PLATFORM_GCC 1
#define PLATFORM_GCC_COMPATIBLE 1
#if defined(__i386__) || defined(__x86_64__)
#define PLATFORM_X86 1
#define PLATFORM_GCC_X86 1
#define PLATFORM_GCC_COMPATIBLE_X86 1
#elif defined(__arm__) || defined (__arm64__) || defined (__aarch64__)
#define PLATFORM_ARM 1
#define PLATFORM_GCC_ARM 1
#define PLATFORM_GCC_COMPATIBLE_ARM 1
#elif defined(__mips__)
#define PLATFORM_MIPS 1
#define PLATFORM_GCC_MIPS 1
#define PLATFORM_GCC_COMPATIBLE_MIPS 1
#endif
#elif defined(_MSC_VER)
#define PLATFORM_MSVC 1
#if defined(_M_IX86) || defined(_M_X64)
#define PLATFORM_X86 1
#define PLATFORM_MSVC_X86 1
#elif defined(_M_ARM) || defined(_M_ARMT)
#define PLATFORM_ARM 1
#define PLATFORM_MSVC_ARM 1
#endif
#else
#error "Unable to determine compiler"
#endif
// Define macros for supported CPU instruction sets
#if defined(PLATFORM_GCC_COMPATIBLE)
#if defined(__MMX__)
#define PLATFORM_MMX 1
#endif
#if defined(__SSE__)
#define PLATFORM_SSE 1
#endif
#if defined(__SSE2__)
#define PLATFORM_SSE2 1
#endif
#if defined(__SSE3__)
#define PLATFORM_SSE3 1
#endif
#if defined(__SSSE3__)
#define PLATFORM_SSSE3 1
#endif
#if defined(__SSE4_1__)
#define PLATFORM_SSE41 1
#endif
#if defined(__SSE4_2__)
#define PLATFORM_SSE42 1
#endif
#if defined(__PCLMUL__)
#define PLATFORM_PCLMUL 1
#endif
#if defined(__AVX__)
#define PLATFORM_AVX 1
#endif
#if defined(__AVX2__)
#define PLATFORM_AVX2 1
#endif
#if defined(__ARM_NEON__) || defined (__ARM_NEON)
#define PLATFORM_NEON 1
#endif
// First, check the PLATFORM_WINDOWS_PHONE define, because
// the X86 instructions sets are not supported on the Windows Phone emulator
#elif defined(PLATFORM_WINDOWS_PHONE)
#if defined(PLATFORM_MSVC_ARM)
// NEON introduced in VS2012
#if (_MSC_VER >= 1700)
#define PLATFORM_NEON 1
#endif
#endif
#elif defined(PLATFORM_MSVC_X86)
// MMX, SSE and SSE2 introduced in VS2003
#if (_MSC_VER >= 1310)
#define PLATFORM_MMX 1
#define PLATFORM_SSE 1
#define PLATFORM_SSE2 1
#endif
// SSE3 introduced in VS2005
#if (_MSC_VER >= 1400)
#define PLATFORM_SSE3 1
#endif
// SSSE3, SSE4.1, SSE4.2, PCLMUL introduced in VS2008
#if (_MSC_VER >= 1500)
#define PLATFORM_SSSE3 1
#define PLATFORM_SSE41 1
#define PLATFORM_SSE42 1
#define PLATFORM_PCLMUL 1
#endif
// AVX and AVX2 introduced in VS2012
#if (_MSC_VER >= 1700)
#define PLATFORM_AVX 1
#define PLATFORM_AVX2 1
#endif
#endif

103
src/external/cpuid/src/cpuid/cpuinfo.cpp vendored Normal file
View file

@ -0,0 +1,103 @@
// Copyright (c) 2013 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.
#include <platform/config.hpp>
#include "cpuinfo.hpp"
#include "detail/cpuinfo_impl.hpp"
#if defined(PLATFORM_GCC_COMPATIBLE_X86)
#include "detail/init_gcc_x86.hpp"
#elif defined(PLATFORM_MSVC_X86) && !defined(PLATFORM_WINDOWS_PHONE)
#include "detail/init_msvc_x86.hpp"
#elif defined(PLATFORM_MSVC_ARM)
#include "detail/init_msvc_arm.hpp"
#elif defined(PLATFORM_CLANG_ARM) && defined(PLATFORM_IOS)
#include "detail/init_ios_clang_arm.hpp"
#elif defined(PLATFORM_GCC_COMPATIBLE_ARM) && defined(PLATFORM_LINUX)
#include "detail/init_linux_gcc_arm.hpp"
#else
#include "detail/init_unknown.hpp"
#endif
namespace cpuid
{
inline namespace STEINWURF_CPUID_VERSION
{
cpuinfo::cpuinfo() :
m_impl(new impl)
{
init_cpuinfo(*m_impl);
}
cpuinfo::~cpuinfo()
{
}
// x86 member functions
bool cpuinfo::has_fpu() const
{
return m_impl->m_has_fpu;
}
bool cpuinfo::has_mmx() const
{
return m_impl->m_has_mmx;
}
bool cpuinfo::has_sse() const
{
return m_impl->m_has_sse;
}
bool cpuinfo::has_sse2() const
{
return m_impl->m_has_sse2;
}
bool cpuinfo::has_sse3() const
{
return m_impl->m_has_sse3;
}
bool cpuinfo::has_ssse3() const
{
return m_impl->m_has_ssse3;
}
bool cpuinfo::has_sse4_1() const
{
return m_impl->m_has_sse4_1;
}
bool cpuinfo::has_sse4_2() const
{
return m_impl->m_has_sse4_2;
}
bool cpuinfo::has_pclmulqdq() const
{
return m_impl->m_has_pclmulqdq;
}
bool cpuinfo::has_avx() const
{
return m_impl->m_has_avx;
}
bool cpuinfo::has_avx2() const
{
return m_impl->m_has_avx2;
}
// ARM functions
bool cpuinfo::has_neon() const
{
return m_impl->m_has_neon;
}
}
}

View file

@ -0,0 +1,72 @@
// Copyright (c) 2013 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.
#pragma once
#include <memory>
#include "version.hpp"
namespace cpuid
{
inline namespace STEINWURF_CPUID_VERSION
{
/// The cpuinfo object extract information about which, if any, additional
/// instructiions are supported by the CPU.
class cpuinfo
{
public:
/// Constructor for feature detection with default values
cpuinfo();
/// Destructor
~cpuinfo();
/// Has X87 FPU
bool has_fpu() const;
/// Return true if the CPU supports MMX
bool has_mmx() const;
/// Return true if the CPU supports SSE
bool has_sse() const;
/// Return true if the CPU supports SSE2
bool has_sse2() const;
/// Return true if the CPU supports SSE3
bool has_sse3() const;
/// Return true if the CPU supports SSSE3
bool has_ssse3() const;
/// Return true if the CPU supports SSE 4.1
bool has_sse4_1() const;
/// Return true if the CPU supports SSE 4.2
bool has_sse4_2() const;
/// Return true if the CPU supports pclmulqdq
bool has_pclmulqdq() const;
/// Return true if the CPU supports AVX
bool has_avx() const;
/// Return true if the CPU supports AVX2
bool has_avx2() const;
/// ARM member functions
bool has_neon() const;
public:
/// Private implementation
struct impl;
private:
/// Pimpl pointer
std::unique_ptr<impl> m_impl;
};
}
}

View file

@ -0,0 +1,39 @@
// Copyright (c) 2013 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.
#pragma once
#include "../cpuinfo.hpp"
namespace cpuid
{
inline namespace STEINWURF_CPUID_VERSION
{
struct cpuinfo::impl
{
impl() :
m_has_fpu(false), m_has_mmx(false), m_has_sse(false), m_has_sse2(false),
m_has_sse3(false), m_has_ssse3(false), m_has_sse4_1(false),
m_has_sse4_2(false), m_has_pclmulqdq(false), m_has_avx(false),
m_has_avx2(false), m_has_neon(false)
{
}
bool m_has_fpu;
bool m_has_mmx;
bool m_has_sse;
bool m_has_sse2;
bool m_has_sse3;
bool m_has_ssse3;
bool m_has_sse4_1;
bool m_has_sse4_2;
bool m_has_pclmulqdq;
bool m_has_avx;
bool m_has_avx2;
bool m_has_neon;
};
}
}

View file

@ -0,0 +1,39 @@
// Copyright (c) 2013 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.
#pragma once
#include <cstdint>
#include "cpuinfo_impl.hpp"
namespace cpuid
{
inline namespace STEINWURF_CPUID_VERSION
{
void extract_x86_flags(cpuinfo::impl& info, uint32_t ecx, uint32_t edx)
{
// Instruction set flags
info.m_has_fpu = (edx & (1 << 0)) != 0;
info.m_has_mmx = (edx & (1 << 23)) != 0;
info.m_has_sse = (edx & (1 << 25)) != 0;
info.m_has_sse2 = (edx & (1 << 26)) != 0;
info.m_has_sse3 = (ecx & (1 << 0)) != 0;
info.m_has_ssse3 = (ecx & (1 << 9)) != 0;
info.m_has_sse4_1 = (ecx & (1 << 19)) != 0;
info.m_has_sse4_2 = (ecx & (1 << 20)) != 0;
info.m_has_pclmulqdq = (ecx & (1 << 1)) != 0;
info.m_has_avx = (ecx & (1 << 28)) != 0;
}
void extract_x86_extended_flags(cpuinfo::impl& info, uint32_t ebx)
{
// Extended instruction set flags
info.m_has_avx2 = (ebx & (1 << 5)) != 0;
}
}
}

View file

@ -0,0 +1,73 @@
// Copyright (c) 2013 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.
#pragma once
#include <cstdint>
#include "cpuinfo_impl.hpp"
#include "extract_x86_flags.hpp"
namespace cpuid
{
inline namespace STEINWURF_CPUID_VERSION
{
// Reference for this code is Intel's recommendation for detecting AVX2
// on Haswell located here: http://goo.gl/c6IkGX
void run_cpuid(uint32_t eax, uint32_t ecx, uint32_t* abcd)
{
uint32_t ebx = 0, edx = 0;
#if defined(__i386__) && defined(__PIC__)
// If PIC used under 32-bit, EBX cannot be clobbered
// EBX is saved to EDI and later restored
__asm__("movl %%ebx, %%edi;"
"cpuid;"
"xchgl %%ebx, %%edi;"
: "=D"(ebx), "+a"(eax), "+c"(ecx), "=d"(edx));
#else
__asm__("cpuid;" : "+b"(ebx), "+a"(eax), "+c"(ecx), "=d"(edx));
#endif
abcd[0] = eax;
abcd[1] = ebx;
abcd[2] = ecx;
abcd[3] = edx;
}
/// @todo Document
void init_cpuinfo(cpuinfo::impl& info)
{
// Note: We need to capture these 4 registers, otherwise we get
// a segmentation fault on 32-bit Linux
uint32_t output[4];
// The register information per input can be extracted from here:
// http://en.wikipedia.org/wiki/CPUID
// CPUID should be called with EAX=0 first, as this will return the
// maximum supported EAX input value for future calls
run_cpuid(0, 0, output);
uint32_t maximum_index = output[0];
// Set registers for basic flag extraction
// All CPUs should support index=1
if (maximum_index >= 1U)
{
run_cpuid(1, 0, output);
extract_x86_flags(info, output[2], output[3]);
}
// Set registers for extended flags extraction using index=7
// This operation is not supported on older CPUs, so it should be skipped
// to avoid incorrect results
if (maximum_index >= 7U)
{
run_cpuid(7, 0, output);
extract_x86_extended_flags(info, output[1]);
}
}
}
}

View file

@ -0,0 +1,30 @@
// Copyright (c) 2013 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.
#pragma once
#include "cpuinfo_impl.hpp"
namespace cpuid
{
inline namespace STEINWURF_CPUID_VERSION
{
/// @todo docs
void init_cpuinfo(cpuinfo::impl& info)
{
// The __ARM_NEON__ macro will be defined by the Apple Clang compiler
// when targeting ARMv7 processors that have NEON.
// The compiler guarantees this capability, so there is no benefit
// in doing a runtime check. More info in this SO answer:
// http://stackoverflow.com/a/1601234
#if defined __ARM_NEON__
info.m_has_neon = true;
#else
info.m_has_neon = false;
#endif
}
}
}

View file

@ -0,0 +1,64 @@
// Copyright (c) 2013 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.
#pragma once
#include <cassert>
#include <cstdio>
#include <cstring>
#include <elf.h>
#include <fcntl.h>
#include <linux/auxvec.h>
#include <unistd.h>
#include "cpuinfo_impl.hpp"
namespace cpuid
{
inline namespace STEINWURF_CPUID_VERSION
{
/// @todo docs
void init_cpuinfo(cpuinfo::impl& info)
{
#if defined(__aarch64__)
// The Advanced SIMD (NEON) instruction set is required on AArch64
// (64-bit ARM). Note that /proc/cpuinfo will display "asimd" instead of
// "neon" in the Features list on a 64-bit ARM CPU.
info.m_has_neon = true;
#else
// Runtime detection of NEON is necessary on 32-bit ARM CPUs
//
// Follow recommendation from Cortex-A Series Programmer's guide
// in Section 20.1.7 Detecting NEON. The guide is available at
// Steinwurf's Google drive: steinwurf/technical/experimental/cpuid
auto cpufile = open("/proc/self/auxv", O_RDONLY);
assert(cpufile);
Elf32_auxv_t auxv;
if (cpufile >= 0)
{
const auto size_auxv_t = sizeof(Elf32_auxv_t);
while (read(cpufile, &auxv, size_auxv_t) == size_auxv_t)
{
if (auxv.a_type == AT_HWCAP)
{
info.m_has_neon = (auxv.a_un.a_val & 4096) != 0;
break;
}
}
close(cpufile);
}
else
{
info.m_has_neon = false;
}
#endif
}
}
}

View file

@ -0,0 +1,26 @@
// Copyright (c) 2013 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.
#pragma once
#include "cpuinfo_impl.hpp"
namespace cpuid
{
inline namespace STEINWURF_CPUID_VERSION
{
void init_cpuinfo(cpuinfo::impl& info)
{
// Visual Studio 2012 (and above) guarantees the NEON capability when
// compiling for Windows Phone 8 (and above)
#if defined(PLATFORM_WINDOWS_PHONE)
info.m_has_neon = true;
#else
info.m_has_neon = false;
#endif
}
}
}

View file

@ -0,0 +1,52 @@
// Copyright (c) 2013 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.
#pragma once
#include <intrin.h>
#include "cpuinfo_impl.hpp"
#include "extract_x86_flags.hpp"
namespace cpuid
{
inline namespace STEINWURF_CPUID_VERSION
{
/// @todo docs
void init_cpuinfo(cpuinfo::impl& info)
{
int registers[4];
/// According to the msvc docs eax, ebx, ecx and edx are
/// stored (in that order) in the array passed to the __cpuid
/// function.
// The register information per input can be extracted from here:
// http://en.wikipedia.org/wiki/CPUID
// CPUID should be called with EAX=0 first, as this will return the
// maximum supported EAX input value for future calls
__cpuid(registers, 0);
uint32_t maximum_eax = registers[0];
// Set registers for basic flag extraction, eax=1
// All CPUs should support index=1
if (maximum_eax >= 1U)
{
__cpuid(registers, 1);
extract_x86_flags(info, registers[2], registers[3]);
}
// Set registers for extended flags extraction, eax=7 and ecx=0
// This operation is not supported on older CPUs, so it should be skipped
// to avoid incorrect results
if (maximum_eax >= 7U)
{
__cpuidex(registers, 7, 0);
extract_x86_extended_flags(info, registers[1]);
}
}
}
}

View file

@ -0,0 +1,20 @@
// Copyright (c) 2013 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.
#pragma once
#include "cpuinfo_impl.hpp"
namespace cpuid
{
inline namespace STEINWURF_CPUID_VERSION
{
/// @todo docs
void init_cpuinfo(cpuinfo::impl& info)
{
(void)info;
}
}
}

View file

@ -0,0 +1,18 @@
// Copyright (c) 2013 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.
#include "version.hpp"
namespace cpuid
{
inline namespace STEINWURF_CPUID_VERSION
{
std::string version()
{
return "6.3.1";
}
}
}

View file

@ -0,0 +1,21 @@
// Copyright (c) 2013 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.
#pragma once
#include <string>
namespace cpuid
{
/// Here we define the STEINWURF_CPUID_VERSION this should be updated on each
/// release
#define STEINWURF_CPUID_VERSION v6_3_1
inline namespace STEINWURF_CPUID_VERSION
{
/// @return The version of the library as string
std::string version();
}
}

View file

@ -281,9 +281,10 @@ private:
static constexpr int AlignmentMask { Alignment - 1 };
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(TypeAlignment * sizeof(value_type) == Alignment, "The alignment does not appear to be divided by the size of the Type");
static_assert(std::is_trivial<value_type>::value, "Type should be trivial");
static_assert(Alignment == 0 || Alignment == 4 || Alignment == 8 || Alignment == 16 || Alignment == 32, "Bad alignment value");
static_assert(TypeAlignment * sizeof(value_type) == Alignment || !std::is_arithmetic<value_type>::value,
"The alignment does not appear to be divided by the size of the arithmetic Type");
void* align(std::size_t alignment, std::size_t size, void *&ptr, std::size_t &space )
{
std::uintptr_t pn = reinterpret_cast< std::uintptr_t>( ptr );

View file

@ -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

View file

@ -16,24 +16,34 @@ 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"
#include "cpuid/cpuinfo.hpp"
#include "absl/memory/memory.h"
#include <cmath>
namespace sfz {
namespace fx {
struct Strings::ResonantString {
Bw2BPF bpf;
WgResonator res;
};
Strings::Strings()
: _strings(new ResonantString[MaximumNumStrings])
{
ResonantArray* array = nullptr;
#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386
cpuid::cpuinfo cpuInfo;
if (cpuInfo.has_avx())
array = new ResonantArrayAVX;
else if (cpuInfo.has_sse())
array = new ResonantArraySSE;
#endif
if (!array)
array = new ResonantArrayScalar;
_stringsArray.reset(array);
}
Strings::~Strings()
@ -42,42 +52,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 +108,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);

View file

@ -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 };
};

View file

@ -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

View 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);

View 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

View 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

View 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 "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;
_stringPacks.resize(numStringPacks);
ResonantStringAVX* stringPacks = _stringPacks.data();
_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.data();
const unsigned numStringPacks = (_numStrings + avxVectorSize - 1) / avxVectorSize;
for (unsigned p = 0; p < numStringPacks; ++p) {
ResonantStringAVX& rs = reinterpret_cast<ResonantStringAVX&>(stringPacks[p]);
rs.clear();
}
}
void ResonantArrayAVX::process(const float *inPtr, float *outPtr, unsigned numFrames)
{
ResonantStringAVX* stringPacks = _stringPacks.data();
const unsigned numStringPacks = (_numStrings + avxVectorSize - 1) / avxVectorSize;
// receive 8 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 = reinterpret_cast<ResonantStringAVX&>(stringPacks[p]);
for (unsigned i = 0; i < numFrames; ++i)
outputs8[i] = _mm256_add_ps(
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

View file

@ -0,0 +1,41 @@
// 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 "ResonantStringAVX.h"
#include "Buffer.h"
#include "SIMDConfig.h"
#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386
namespace sfz {
namespace fx {
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:
Buffer<ResonantStringAVX, 32> _stringPacks;
unsigned _numStrings = 0;
Buffer<float, 32> _workBuffer;
};
} // namespace sfz
} // namespace fx
#endif

View 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 "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;
_stringPacks.resize(numStringPacks);
ResonantStringSSE* stringPacks = _stringPacks.data();
_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.data();
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.data();
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] = _mm_add_ps(
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

View file

@ -0,0 +1,41 @@
// 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 "ResonantStringSSE.h"
#include "Buffer.h"
#include "SIMDConfig.h"
#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386
namespace sfz {
namespace fx {
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:
Buffer<ResonantStringSSE, 16> _stringPacks;
unsigned _numStrings = 0;
Buffer<float, 16> _workBuffer;
};
} // namespace sfz
} // namespace fx
#endif

View 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

View 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

View file

@ -0,0 +1,129 @@
// 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 <stdexcept>
#include <cstdint>
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)
{
if (reinterpret_cast<uintptr_t>(this) & 31)
throw std::runtime_error("The resonant string is misaligned for AVX");
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

View 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

View file

@ -0,0 +1,129 @@
// 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 <stdexcept>
#include <cstdint>
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)
{
if (reinterpret_cast<uintptr_t>(this) & 15)
throw std::runtime_error("The resonant string is misaligned for SSE");
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

View 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