Force 32bit alignment for vectorized operations, fixes 32bit build
Signed-off-by: falkTX <falktx@falktx.com>
This commit is contained in:
parent
6d9633b7fb
commit
2dc12fb1ca
|
@ -1 +1 @@
|
||||||
Subproject commit b22a46cfb0291d5523099daf2d9facb7af9836c3
|
Subproject commit 9fa141a1050cfd81577f068135218723441b8ac5
|
|
@ -0,0 +1,164 @@
|
||||||
|
/*
|
||||||
|
* DISTRHO Cardinal Plugin
|
||||||
|
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 3 of
|
||||||
|
* the License, or any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* For a full copy of the GNU General Public License see the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is an edited version of VCVRack's dsp/fir.hpp
|
||||||
|
* Copyright (C) 2016-2021 VCV.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pffft.h>
|
||||||
|
|
||||||
|
#include <dsp/common.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace rack {
|
||||||
|
namespace dsp {
|
||||||
|
|
||||||
|
|
||||||
|
/** Performs a direct sum convolution */
|
||||||
|
inline float convolveNaive(const float* in, const float* kernel, int len) {
|
||||||
|
float y = 0.f;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
y += in[len - 1 - i] * kernel[i];
|
||||||
|
}
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Computes the impulse response of a boxcar lowpass filter */
|
||||||
|
inline void boxcarLowpassIR(float* out, int len, float cutoff = 0.5f) {
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
float t = i - (len - 1) / 2.f;
|
||||||
|
out[i] = 2 * cutoff * sinc(2 * cutoff * t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct RealTimeConvolver {
|
||||||
|
// `kernelBlocks` number of contiguous FFT blocks of size `blockSize`
|
||||||
|
// indexed by [i * blockSize*2 + j]
|
||||||
|
float* kernelFfts = NULL;
|
||||||
|
float* inputFfts = NULL;
|
||||||
|
float* outputTail = NULL;
|
||||||
|
float* tmpBlock = NULL;
|
||||||
|
size_t blockSize;
|
||||||
|
size_t kernelBlocks = 0;
|
||||||
|
size_t inputPos = 0;
|
||||||
|
PFFFT_Setup* pffft;
|
||||||
|
|
||||||
|
/** `blockSize` is the size of each FFT block. It should be >=32 and a power of 2. */
|
||||||
|
RealTimeConvolver(size_t blockSize) {
|
||||||
|
this->blockSize = blockSize;
|
||||||
|
pffft = pffft_new_setup(blockSize * 2, PFFFT_REAL);
|
||||||
|
outputTail = (float*) pffft_aligned_malloc(sizeof(float) * blockSize);
|
||||||
|
std::memset(outputTail, 0, blockSize * sizeof(float));
|
||||||
|
tmpBlock = (float*) pffft_aligned_malloc(sizeof(float) * blockSize * 2);
|
||||||
|
std::memset(tmpBlock, 0, blockSize * 2 * sizeof(float));
|
||||||
|
}
|
||||||
|
|
||||||
|
~RealTimeConvolver() {
|
||||||
|
setKernel(NULL, 0);
|
||||||
|
pffft_aligned_free(outputTail);
|
||||||
|
pffft_aligned_free(tmpBlock);
|
||||||
|
pffft_destroy_setup(pffft);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setKernel(const float* kernel, size_t length) {
|
||||||
|
// Clear existing kernel
|
||||||
|
if (kernelFfts) {
|
||||||
|
pffft_aligned_free(kernelFfts);
|
||||||
|
kernelFfts = NULL;
|
||||||
|
}
|
||||||
|
if (inputFfts) {
|
||||||
|
pffft_aligned_free(inputFfts);
|
||||||
|
inputFfts = NULL;
|
||||||
|
}
|
||||||
|
kernelBlocks = 0;
|
||||||
|
inputPos = 0;
|
||||||
|
|
||||||
|
if (kernel && length > 0) {
|
||||||
|
// Round up to the nearest factor of `blockSize`
|
||||||
|
kernelBlocks = (length - 1) / blockSize + 1;
|
||||||
|
|
||||||
|
// Allocate blocks
|
||||||
|
kernelFfts = (float*) pffft_aligned_malloc(sizeof(float) * blockSize * 2 * kernelBlocks);
|
||||||
|
inputFfts = (float*) pffft_aligned_malloc(sizeof(float) * blockSize * 2 * kernelBlocks);
|
||||||
|
std::memset(inputFfts, 0, sizeof(float) * blockSize * 2 * kernelBlocks);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < kernelBlocks; i++) {
|
||||||
|
// Pad each block with zeros
|
||||||
|
std::memset(tmpBlock, 0, sizeof(float) * blockSize * 2);
|
||||||
|
size_t len = std::min((int) blockSize, (int)(length - i * blockSize));
|
||||||
|
std::memcpy(tmpBlock, &kernel[i * blockSize], sizeof(float)*len);
|
||||||
|
// Compute fft
|
||||||
|
pffft_transform(pffft, tmpBlock, &kernelFfts[blockSize * 2 * i], NULL, PFFFT_FORWARD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Applies reverb to input
|
||||||
|
input and output must be of size `blockSize`
|
||||||
|
*/
|
||||||
|
void processBlock(const float* input, float* output) {
|
||||||
|
if (kernelBlocks == 0) {
|
||||||
|
std::memset(output, 0, sizeof(float) * blockSize);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step input position
|
||||||
|
inputPos = (inputPos + 1) % kernelBlocks;
|
||||||
|
// Pad block with zeros
|
||||||
|
std::memset(tmpBlock, 0, sizeof(float) * blockSize * 2);
|
||||||
|
std::memcpy(tmpBlock, input, sizeof(float) * blockSize);
|
||||||
|
// Compute input fft
|
||||||
|
pffft_transform(pffft, tmpBlock, &inputFfts[blockSize * 2 * inputPos], NULL, PFFFT_FORWARD);
|
||||||
|
// Create output fft
|
||||||
|
std::memset(tmpBlock, 0, sizeof(float) * blockSize * 2);
|
||||||
|
// convolve input fft by kernel fft
|
||||||
|
// Note: This is the CPU bottleneck loop
|
||||||
|
for (size_t i = 0; i < kernelBlocks; i++) {
|
||||||
|
size_t pos = (inputPos - i + kernelBlocks) % kernelBlocks;
|
||||||
|
pffft_zconvolve_accumulate(pffft, &kernelFfts[blockSize * 2 * i], &inputFfts[blockSize * 2 * pos], tmpBlock, 1.f);
|
||||||
|
}
|
||||||
|
// Compute output
|
||||||
|
pffft_transform(pffft, tmpBlock, tmpBlock, NULL, PFFFT_BACKWARD);
|
||||||
|
// Add block tail from last output block
|
||||||
|
for (size_t i = 0; i < blockSize; i++) {
|
||||||
|
tmpBlock[i] += outputTail[i];
|
||||||
|
}
|
||||||
|
// Copy output block to output
|
||||||
|
float scale = 1.f / (blockSize * 2);
|
||||||
|
for (size_t i = 0; i < blockSize; i++) {
|
||||||
|
// Scale based on FFT
|
||||||
|
output[i] = tmpBlock[i] * scale;
|
||||||
|
}
|
||||||
|
// Set tail
|
||||||
|
for (size_t i = 0; i < blockSize; i++) {
|
||||||
|
outputTail[i] = tmpBlock[i + blockSize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace dsp
|
||||||
|
} // namespace rack
|
|
@ -48,7 +48,7 @@ struct Port {
|
||||||
/** Voltage of the port. */
|
/** Voltage of the port. */
|
||||||
/** NOTE alignas is required in order to allow SSE usage.
|
/** NOTE alignas is required in order to allow SSE usage.
|
||||||
Consecutive data (like in a vector) would otherwise pack Ports in a way that breaks SSE. */
|
Consecutive data (like in a vector) would otherwise pack Ports in a way that breaks SSE. */
|
||||||
union alignas(PORT_MAX_CHANNELS) {
|
union alignas(32) {
|
||||||
/** Unstable API. Use getVoltage() and setVoltage() instead. */
|
/** Unstable API. Use getVoltage() and setVoltage() instead. */
|
||||||
float voltages[PORT_MAX_CHANNELS] = {};
|
float voltages[PORT_MAX_CHANNELS] = {};
|
||||||
/** DEPRECATED. Unstable API. Use getVoltage() and setVoltage() instead. */
|
/** DEPRECATED. Unstable API. Use getVoltage() and setVoltage() instead. */
|
||||||
|
|
|
@ -0,0 +1,374 @@
|
||||||
|
/*
|
||||||
|
* DISTRHO Cardinal Plugin
|
||||||
|
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 3 of
|
||||||
|
* the License, or any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* For a full copy of the GNU General Public License see the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is an edited version of VCVRack's simd/Vector.hpp
|
||||||
|
* Copyright (C) 2016-2021 VCV.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <pmmintrin.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace rack {
|
||||||
|
|
||||||
|
|
||||||
|
/** Abstraction of aligned types for SIMD computation
|
||||||
|
*/
|
||||||
|
namespace simd {
|
||||||
|
|
||||||
|
|
||||||
|
/** Generic class for vector types.
|
||||||
|
|
||||||
|
This class is designed to be used just like you use scalars, with extra features for handling bitwise logic, conditions, loading, and storing.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
float a[4], b[4];
|
||||||
|
float_4 a = float_4::load(in);
|
||||||
|
float_4 b = 2.f * a / (1 - a);
|
||||||
|
b *= sin(2 * M_PI * a);
|
||||||
|
b.store(out);
|
||||||
|
*/
|
||||||
|
template <typename TYPE, int SIZE>
|
||||||
|
struct Vector;
|
||||||
|
|
||||||
|
|
||||||
|
/** Wrapper for `__m128` representing an aligned vector of 4 single-precision float values.
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
struct Vector<float, 4> {
|
||||||
|
using type = float;
|
||||||
|
constexpr static int size = 4;
|
||||||
|
|
||||||
|
/** NOTE alignas is required in order to allow SSE usage. */
|
||||||
|
union alignas(32) {
|
||||||
|
__m128 v;
|
||||||
|
/** Accessing this array of scalars is slow and defeats the purpose of vectorizing.
|
||||||
|
*/
|
||||||
|
float s[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Constructs an uninitialized vector. */
|
||||||
|
Vector() = default;
|
||||||
|
|
||||||
|
/** Constructs a vector from a native `__m128` type. */
|
||||||
|
Vector(__m128 v) : v(v) {}
|
||||||
|
|
||||||
|
/** Constructs a vector with all elements set to `x`. */
|
||||||
|
Vector(float x) {
|
||||||
|
v = _mm_set1_ps(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructs a vector from four scalars. */
|
||||||
|
Vector(float x1, float x2, float x3, float x4) {
|
||||||
|
v = _mm_setr_ps(x1, x2, x3, x4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a vector with all 0 bits. */
|
||||||
|
static Vector zero() {
|
||||||
|
return Vector(_mm_setzero_ps());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a vector with all 1 bits. */
|
||||||
|
static Vector mask() {
|
||||||
|
return Vector(_mm_castsi128_ps(_mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reads an array of 4 values.
|
||||||
|
On little-endian machines (e.g. x86_64), the order is reversed, so `x[0]` corresponds to `vector.s[3]`.
|
||||||
|
*/
|
||||||
|
static Vector load(const float* x) {
|
||||||
|
/*
|
||||||
|
My benchmarks show that _mm_loadu_ps() performs equally as fast as _mm_load_ps() when data is actually aligned.
|
||||||
|
This post seems to agree. https://stackoverflow.com/a/20265193/272642
|
||||||
|
I therefore use _mm_loadu_ps() for generality, so you can load unaligned arrays using the same function (although load aligned arrays if you can for best performance).
|
||||||
|
*/
|
||||||
|
return Vector(_mm_loadu_ps(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Writes an array of 4 values.
|
||||||
|
On little-endian machines (e.g. x86_64), the order is reversed, so `x[0]` corresponds to `vector.s[3]`.
|
||||||
|
*/
|
||||||
|
void store(float* x) {
|
||||||
|
_mm_storeu_ps(x, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Accessing vector elements individually is slow and defeats the purpose of vectorizing.
|
||||||
|
However, this operator is convenient when writing simple serial code in a non-bottlenecked section.
|
||||||
|
*/
|
||||||
|
float& operator[](int i) {
|
||||||
|
return s[i];
|
||||||
|
}
|
||||||
|
const float& operator[](int i) const {
|
||||||
|
return s[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversions
|
||||||
|
Vector(Vector<int32_t, 4> a);
|
||||||
|
// Casts
|
||||||
|
static Vector cast(Vector<int32_t, 4> a);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Vector<int32_t, 4> {
|
||||||
|
using type = int32_t;
|
||||||
|
constexpr static int size = 4;
|
||||||
|
|
||||||
|
/** NOTE alignas is required in order to allow SSE usage. */
|
||||||
|
union alignas(32) {
|
||||||
|
__m128i v;
|
||||||
|
int32_t s[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector() = default;
|
||||||
|
Vector(__m128i v) : v(v) {}
|
||||||
|
Vector(int32_t x) {
|
||||||
|
v = _mm_set1_epi32(x);
|
||||||
|
}
|
||||||
|
Vector(int32_t x1, int32_t x2, int32_t x3, int32_t x4) {
|
||||||
|
v = _mm_setr_epi32(x1, x2, x3, x4);
|
||||||
|
}
|
||||||
|
static Vector zero() {
|
||||||
|
return Vector(_mm_setzero_si128());
|
||||||
|
}
|
||||||
|
static Vector mask() {
|
||||||
|
return Vector(_mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128()));
|
||||||
|
}
|
||||||
|
static Vector load(const int32_t* x) {
|
||||||
|
// HACK
|
||||||
|
// Use _mm_loadu_si128() because GCC doesn't support _mm_loadu_si32()
|
||||||
|
return Vector(_mm_loadu_si128((const __m128i*) x));
|
||||||
|
}
|
||||||
|
void store(int32_t* x) {
|
||||||
|
// HACK
|
||||||
|
// Use _mm_storeu_si128() because GCC doesn't support _mm_storeu_si32()
|
||||||
|
_mm_storeu_si128((__m128i*) x, v);
|
||||||
|
}
|
||||||
|
int32_t& operator[](int i) {
|
||||||
|
return s[i];
|
||||||
|
}
|
||||||
|
const int32_t& operator[](int i) const {
|
||||||
|
return s[i];
|
||||||
|
}
|
||||||
|
Vector(Vector<float, 4> a);
|
||||||
|
static Vector cast(Vector<float, 4> a);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Conversions and casts
|
||||||
|
|
||||||
|
|
||||||
|
inline Vector<float, 4>::Vector(Vector<int32_t, 4> a) {
|
||||||
|
v = _mm_cvtepi32_ps(a.v);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vector<int32_t, 4>::Vector(Vector<float, 4> a) {
|
||||||
|
v = _mm_cvttps_epi32(a.v);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vector<float, 4> Vector<float, 4>::cast(Vector<int32_t, 4> a) {
|
||||||
|
return Vector(_mm_castsi128_ps(a.v));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vector<int32_t, 4> Vector<int32_t, 4>::cast(Vector<float, 4> a) {
|
||||||
|
return Vector(_mm_castps_si128(a.v));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Operator overloads
|
||||||
|
|
||||||
|
|
||||||
|
/** `a @ b` */
|
||||||
|
#define DECLARE_VECTOR_OPERATOR_INFIX(t, s, operator, func) \
|
||||||
|
inline Vector<t, s> operator(const Vector<t, s>& a, const Vector<t, s>& b) { \
|
||||||
|
return Vector<t, s>(func(a.v, b.v)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `a @= b` */
|
||||||
|
#define DECLARE_VECTOR_OPERATOR_INCREMENT(t, s, operator, opfunc) \
|
||||||
|
inline Vector<t, s>& operator(Vector<t, s>& a, const Vector<t, s>& b) { \
|
||||||
|
return a = opfunc(a, b); \
|
||||||
|
}
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator+, _mm_add_ps)
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator+, _mm_add_epi32)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator-, _mm_sub_ps)
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator-, _mm_sub_epi32)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator*, _mm_mul_ps)
|
||||||
|
// DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator*, NOT AVAILABLE IN SSE3)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator/, _mm_div_ps)
|
||||||
|
// DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator/, NOT AVAILABLE IN SSE3)
|
||||||
|
|
||||||
|
/* Use these to apply logic, bit masks, and conditions to elements.
|
||||||
|
Boolean operators on vectors give 0x00000000 for false and 0xffffffff for true, for each vector element.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
Subtract 1 from value if greater than or equal to 1.
|
||||||
|
|
||||||
|
x -= (x >= 1.f) & 1.f;
|
||||||
|
*/
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator^, _mm_xor_ps)
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator^, _mm_xor_si128)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator&, _mm_and_ps)
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator&, _mm_and_si128)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator|, _mm_or_ps)
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator|, _mm_or_si128)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INCREMENT(float, 4, operator+=, operator+)
|
||||||
|
DECLARE_VECTOR_OPERATOR_INCREMENT(int32_t, 4, operator+=, operator+)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INCREMENT(float, 4, operator-=, operator-)
|
||||||
|
DECLARE_VECTOR_OPERATOR_INCREMENT(int32_t, 4, operator-=, operator-)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INCREMENT(float, 4, operator*=, operator*)
|
||||||
|
// DECLARE_VECTOR_OPERATOR_INCREMENT(int32_t, 4, operator*=, NOT AVAILABLE IN SSE3)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INCREMENT(float, 4, operator/=, operator/)
|
||||||
|
// DECLARE_VECTOR_OPERATOR_INCREMENT(int32_t, 4, operator/=, NOT AVAILABLE IN SSE3)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INCREMENT(float, 4, operator^=, operator^)
|
||||||
|
DECLARE_VECTOR_OPERATOR_INCREMENT(int32_t, 4, operator^=, operator^)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INCREMENT(float, 4, operator&=, operator&)
|
||||||
|
DECLARE_VECTOR_OPERATOR_INCREMENT(int32_t, 4, operator&=, operator&)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INCREMENT(float, 4, operator|=, operator|)
|
||||||
|
DECLARE_VECTOR_OPERATOR_INCREMENT(int32_t, 4, operator|=, operator|)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator==, _mm_cmpeq_ps)
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator==, _mm_cmpeq_epi32)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator>=, _mm_cmpge_ps)
|
||||||
|
inline Vector<int32_t, 4> operator>=(const Vector<int32_t, 4>& a, const Vector<int32_t, 4>& b) {
|
||||||
|
return Vector<int32_t, 4>(_mm_cmpgt_epi32(a.v, b.v)) ^ Vector<int32_t, 4>::mask();
|
||||||
|
}
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator>, _mm_cmpgt_ps)
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator>, _mm_cmpgt_epi32)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator<=, _mm_cmple_ps)
|
||||||
|
inline Vector<int32_t, 4> operator<=(const Vector<int32_t, 4>& a, const Vector<int32_t, 4>& b) {
|
||||||
|
return Vector<int32_t, 4>(_mm_cmplt_epi32(a.v, b.v)) ^ Vector<int32_t, 4>::mask();
|
||||||
|
}
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator<, _mm_cmplt_ps)
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator<, _mm_cmplt_epi32)
|
||||||
|
|
||||||
|
DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator!=, _mm_cmpneq_ps)
|
||||||
|
inline Vector<int32_t, 4> operator!=(const Vector<int32_t, 4>& a, const Vector<int32_t, 4>& b) {
|
||||||
|
return Vector<int32_t, 4>(_mm_cmpeq_epi32(a.v, b.v)) ^ Vector<int32_t, 4>::mask();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `+a` */
|
||||||
|
inline Vector<float, 4> operator+(const Vector<float, 4>& a) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
inline Vector<int32_t, 4> operator+(const Vector<int32_t, 4>& a) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `-a` */
|
||||||
|
inline Vector<float, 4> operator-(const Vector<float, 4>& a) {
|
||||||
|
return 0.f - a;
|
||||||
|
}
|
||||||
|
inline Vector<int32_t, 4> operator-(const Vector<int32_t, 4>& a) {
|
||||||
|
return 0 - a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `++a` */
|
||||||
|
inline Vector<float, 4>& operator++(Vector<float, 4>& a) {
|
||||||
|
return a += 1.f;
|
||||||
|
}
|
||||||
|
inline Vector<int32_t, 4>& operator++(Vector<int32_t, 4>& a) {
|
||||||
|
return a += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `--a` */
|
||||||
|
inline Vector<float, 4>& operator--(Vector<float, 4>& a) {
|
||||||
|
return a -= 1.f;
|
||||||
|
}
|
||||||
|
inline Vector<int32_t, 4>& operator--(Vector<int32_t, 4>& a) {
|
||||||
|
return a -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `a++` */
|
||||||
|
inline Vector<float, 4> operator++(Vector<float, 4>& a, int) {
|
||||||
|
Vector<float, 4> b = a;
|
||||||
|
++a;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
inline Vector<int32_t, 4> operator++(Vector<int32_t, 4>& a, int) {
|
||||||
|
Vector<int32_t, 4> b = a;
|
||||||
|
++a;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `a--` */
|
||||||
|
inline Vector<float, 4> operator--(Vector<float, 4>& a, int) {
|
||||||
|
Vector<float, 4> b = a;
|
||||||
|
--a;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
inline Vector<int32_t, 4> operator--(Vector<int32_t, 4>& a, int) {
|
||||||
|
Vector<int32_t, 4> b = a;
|
||||||
|
--a;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `~a` */
|
||||||
|
inline Vector<float, 4> operator~(const Vector<float, 4>& a) {
|
||||||
|
return a ^ Vector<float, 4>::mask();
|
||||||
|
}
|
||||||
|
inline Vector<int32_t, 4> operator~(const Vector<int32_t, 4>& a) {
|
||||||
|
return a ^ Vector<int32_t, 4>::mask();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `a << b` */
|
||||||
|
inline Vector<int32_t, 4> operator<<(const Vector<int32_t, 4>& a, const int& b) {
|
||||||
|
return Vector<int32_t, 4>(_mm_slli_epi32(a.v, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `a >> b` */
|
||||||
|
inline Vector<int32_t, 4> operator>>(const Vector<int32_t, 4>& a, const int& b) {
|
||||||
|
return Vector<int32_t, 4>(_mm_srli_epi32(a.v, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Typedefs
|
||||||
|
|
||||||
|
|
||||||
|
using float_4 = Vector<float, 4>;
|
||||||
|
using int32_4 = Vector<int32_t, 4>;
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace simd
|
||||||
|
} // namespace rack
|
|
@ -954,7 +954,7 @@ endif
|
||||||
|
|
||||||
BUILD_C_FLAGS += -std=gnu11
|
BUILD_C_FLAGS += -std=gnu11
|
||||||
BUILD_C_FLAGS += -fno-finite-math-only -fno-strict-aliasing
|
BUILD_C_FLAGS += -fno-finite-math-only -fno-strict-aliasing
|
||||||
BUILD_CXX_FLAGS += -fno-finite-math-only -fno-strict-aliasing
|
BUILD_CXX_FLAGS += -fno-finite-math-only -fno-strict-aliasing -faligned-new
|
||||||
|
|
||||||
# Rack code is not tested for this flag, unset it
|
# Rack code is not tested for this flag, unset it
|
||||||
BUILD_CXX_FLAGS += -U_GLIBCXX_ASSERTIONS -Wp,-U_GLIBCXX_ASSERTIONS
|
BUILD_CXX_FLAGS += -U_GLIBCXX_ASSERTIONS -Wp,-U_GLIBCXX_ASSERTIONS
|
||||||
|
|
|
@ -95,7 +95,7 @@ endif
|
||||||
|
|
||||||
BUILD_C_FLAGS += -std=gnu11
|
BUILD_C_FLAGS += -std=gnu11
|
||||||
BUILD_C_FLAGS += -fno-finite-math-only -fno-strict-aliasing
|
BUILD_C_FLAGS += -fno-finite-math-only -fno-strict-aliasing
|
||||||
BUILD_CXX_FLAGS += -fno-finite-math-only -fno-strict-aliasing
|
BUILD_CXX_FLAGS += -fno-finite-math-only -fno-strict-aliasing -faligned-new
|
||||||
|
|
||||||
# use our custom function to invert some colors
|
# use our custom function to invert some colors
|
||||||
BUILD_CXX_FLAGS += -DnsvgParseFromFile=nsvgParseFromFileCardinal
|
BUILD_CXX_FLAGS += -DnsvgParseFromFile=nsvgParseFromFileCardinal
|
||||||
|
@ -115,6 +115,7 @@ RACK_FILES += custom/network.cpp
|
||||||
RACK_FILES += custom/osdialog.cpp
|
RACK_FILES += custom/osdialog.cpp
|
||||||
RACK_FILES += override/blendish.c
|
RACK_FILES += override/blendish.c
|
||||||
RACK_FILES += override/context.cpp
|
RACK_FILES += override/context.cpp
|
||||||
|
RACK_FILES += override/minblep.cpp
|
||||||
RACK_FILES += override/plugin.cpp
|
RACK_FILES += override/plugin.cpp
|
||||||
RACK_FILES += override/Engine.cpp
|
RACK_FILES += override/Engine.cpp
|
||||||
RACK_FILES += override/MenuBar.cpp
|
RACK_FILES += override/MenuBar.cpp
|
||||||
|
@ -144,6 +145,7 @@ IGNORED_FILES += Rack/src/app/MenuBar.cpp
|
||||||
IGNORED_FILES += Rack/src/app/MidiDisplay.cpp
|
IGNORED_FILES += Rack/src/app/MidiDisplay.cpp
|
||||||
IGNORED_FILES += Rack/src/app/Scene.cpp
|
IGNORED_FILES += Rack/src/app/Scene.cpp
|
||||||
IGNORED_FILES += Rack/src/app/TipWindow.cpp
|
IGNORED_FILES += Rack/src/app/TipWindow.cpp
|
||||||
|
IGNORED_FILES += Rack/src/dsp/minblep.cpp
|
||||||
IGNORED_FILES += Rack/src/engine/Engine.cpp
|
IGNORED_FILES += Rack/src/engine/Engine.cpp
|
||||||
IGNORED_FILES += Rack/src/plugin/Model.cpp
|
IGNORED_FILES += Rack/src/plugin/Model.cpp
|
||||||
IGNORED_FILES += Rack/src/window/Window.cpp
|
IGNORED_FILES += Rack/src/window/Window.cpp
|
||||||
|
|
|
@ -170,7 +170,7 @@ endif
|
||||||
|
|
||||||
BUILD_C_FLAGS += -std=gnu11
|
BUILD_C_FLAGS += -std=gnu11
|
||||||
BUILD_C_FLAGS += -fno-finite-math-only -fno-strict-aliasing
|
BUILD_C_FLAGS += -fno-finite-math-only -fno-strict-aliasing
|
||||||
BUILD_CXX_FLAGS += -fno-finite-math-only -fno-strict-aliasing
|
BUILD_CXX_FLAGS += -fno-finite-math-only -fno-strict-aliasing -faligned-new
|
||||||
|
|
||||||
# Rack code is not tested for this flag, unset it
|
# Rack code is not tested for this flag, unset it
|
||||||
BUILD_CXX_FLAGS += -U_GLIBCXX_ASSERTIONS -Wp,-U_GLIBCXX_ASSERTIONS
|
BUILD_CXX_FLAGS += -U_GLIBCXX_ASSERTIONS -Wp,-U_GLIBCXX_ASSERTIONS
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* DISTRHO Cardinal Plugin
|
||||||
|
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 3 of
|
||||||
|
* the License, or any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* For a full copy of the GNU General Public License see the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is an edited version of VCVRack's dsp/minblep.cpp
|
||||||
|
* Copyright (C) 2016-2021 VCV.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <dsp/minblep.hpp>
|
||||||
|
#include <dsp/fft.hpp>
|
||||||
|
#include <dsp/window.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace rack {
|
||||||
|
namespace dsp {
|
||||||
|
|
||||||
|
|
||||||
|
void minBlepImpulse(int z, int o, float* output) {
|
||||||
|
// Symmetric sinc array with `z` zero-crossings on each side
|
||||||
|
int n = 2 * z * o;
|
||||||
|
float* x = (float*) pffft_aligned_malloc(sizeof(float) * n);
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
float p = math::rescale((float) i, 0.f, (float)(n - 1), (float) - z, (float) z);
|
||||||
|
x[i] = sinc(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply window
|
||||||
|
blackmanHarrisWindow(x, n);
|
||||||
|
|
||||||
|
// Real cepstrum
|
||||||
|
float* fx = (float*) pffft_aligned_malloc(sizeof(float) * 2 * n);
|
||||||
|
// Valgrind complains that the array is uninitialized for some reason, unless we clear it.
|
||||||
|
std::memset(fx, 0, sizeof(float) * 2 * n);
|
||||||
|
RealFFT rfft(n);
|
||||||
|
rfft.rfft(x, fx);
|
||||||
|
// fx = log(abs(fx))
|
||||||
|
fx[0] = std::log(std::fabs(fx[0]));
|
||||||
|
for (int i = 1; i < n; i++) {
|
||||||
|
fx[2 * i] = std::log(std::hypot(fx[2 * i], fx[2 * i + 1]));
|
||||||
|
fx[2 * i + 1] = 0.f;
|
||||||
|
}
|
||||||
|
fx[1] = std::log(std::fabs(fx[1]));
|
||||||
|
// Clamp values in case we have -inf
|
||||||
|
for (int i = 0; i < 2 * n; i++) {
|
||||||
|
fx[i] = std::fmax(-30.f, fx[i]);
|
||||||
|
}
|
||||||
|
rfft.irfft(fx, x);
|
||||||
|
rfft.scale(x);
|
||||||
|
|
||||||
|
// Minimum-phase reconstruction
|
||||||
|
for (int i = 1; i < n / 2; i++) {
|
||||||
|
x[i] *= 2.f;
|
||||||
|
}
|
||||||
|
for (int i = (n + 1) / 2; i < n; i++) {
|
||||||
|
x[i] = 0.f;
|
||||||
|
}
|
||||||
|
rfft.rfft(x, fx);
|
||||||
|
// fx = exp(fx)
|
||||||
|
fx[0] = std::exp(fx[0]);
|
||||||
|
for (int i = 1; i < n; i++) {
|
||||||
|
float re = std::exp(fx[2 * i]);
|
||||||
|
float im = fx[2 * i + 1];
|
||||||
|
fx[2 * i] = re * std::cos(im);
|
||||||
|
fx[2 * i + 1] = re * std::sin(im);
|
||||||
|
}
|
||||||
|
fx[1] = std::exp(fx[1]);
|
||||||
|
rfft.irfft(fx, x);
|
||||||
|
rfft.scale(x);
|
||||||
|
|
||||||
|
// Integrate
|
||||||
|
float total = 0.f;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
total += x[i];
|
||||||
|
x[i] = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize
|
||||||
|
float norm = 1.f / x[n - 1];
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
x[i] *= norm;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(output, x, n * sizeof(float));
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
pffft_aligned_free(x);
|
||||||
|
pffft_aligned_free(fx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace dsp
|
||||||
|
} // namespace rack
|
Loading…
Reference in New Issue