Merge pull request #149 from jpcima/strings-simd
SIMD-accelerated string resonator
This commit is contained in:
commit
e91b553f0e
41 changed files with 1975 additions and 252 deletions
|
|
@ -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
|
||||
|
|
|
|||
103
benchmarks/BM_stringResonator.cpp
Normal file
103
benchmarks/BM_stringResonator.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
|
||||
|
||||
#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();
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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}")
|
||||
|
|
|
|||
|
|
@ -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
6
src/external/cpuid/CMakeLists.txt
vendored
Normal 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
31
src/external/cpuid/LICENSE.rst
vendored
Normal 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
31
src/external/cpuid/platform/LICENSE.rst
vendored
Normal 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.
|
||||
159
src/external/cpuid/platform/src/platform/config.hpp
vendored
Normal file
159
src/external/cpuid/platform/src/platform/config.hpp
vendored
Normal 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
103
src/external/cpuid/src/cpuid/cpuinfo.cpp
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
72
src/external/cpuid/src/cpuid/cpuinfo.hpp
vendored
Normal file
72
src/external/cpuid/src/cpuid/cpuinfo.hpp
vendored
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
39
src/external/cpuid/src/cpuid/detail/cpuinfo_impl.hpp
vendored
Normal file
39
src/external/cpuid/src/cpuid/detail/cpuinfo_impl.hpp
vendored
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
39
src/external/cpuid/src/cpuid/detail/extract_x86_flags.hpp
vendored
Normal file
39
src/external/cpuid/src/cpuid/detail/extract_x86_flags.hpp
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
73
src/external/cpuid/src/cpuid/detail/init_gcc_x86.hpp
vendored
Normal file
73
src/external/cpuid/src/cpuid/detail/init_gcc_x86.hpp
vendored
Normal 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
src/external/cpuid/src/cpuid/detail/init_ios_clang_arm.hpp
vendored
Normal file
30
src/external/cpuid/src/cpuid/detail/init_ios_clang_arm.hpp
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
64
src/external/cpuid/src/cpuid/detail/init_linux_gcc_arm.hpp
vendored
Normal file
64
src/external/cpuid/src/cpuid/detail/init_linux_gcc_arm.hpp
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/external/cpuid/src/cpuid/detail/init_msvc_arm.hpp
vendored
Normal file
26
src/external/cpuid/src/cpuid/detail/init_msvc_arm.hpp
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
52
src/external/cpuid/src/cpuid/detail/init_msvc_x86.hpp
vendored
Normal file
52
src/external/cpuid/src/cpuid/detail/init_msvc_x86.hpp
vendored
Normal 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/external/cpuid/src/cpuid/detail/init_unknown.hpp
vendored
Normal file
20
src/external/cpuid/src/cpuid/detail/init_unknown.hpp
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/external/cpuid/src/cpuid/version.cpp
vendored
Normal file
18
src/external/cpuid/src/cpuid/version.cpp
vendored
Normal 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/external/cpuid/src/cpuid/version.hpp
vendored
Normal file
21
src/external/cpuid/src/cpuid/version.hpp
vendored
Normal 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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 );
|
||||
|
|
|
|||
|
|
@ -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,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);
|
||||
|
|
|
|||
|
|
@ -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 "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
|
||||
41
src/sfizz/effects/impl/ResonantArrayAVX.h
Normal file
41
src/sfizz/effects/impl/ResonantArrayAVX.h
Normal 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
|
||||
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 "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
|
||||
41
src/sfizz/effects/impl/ResonantArraySSE.h
Normal file
41
src/sfizz/effects/impl/ResonantArraySSE.h
Normal 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
|
||||
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
|
||||
129
src/sfizz/effects/impl/ResonantStringAVX.cpp
Normal file
129
src/sfizz/effects/impl/ResonantStringAVX.cpp
Normal 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
|
||||
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
|
||||
129
src/sfizz/effects/impl/ResonantStringSSE.cpp
Normal file
129
src/sfizz/effects/impl/ResonantStringSSE.cpp
Normal 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
|
||||
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