2 audio filters added, a volume-dependent-loudness and a convolution filter.

This commit is contained in:
Yann Pomarede
2017-02-27 22:43:23 +01:00
parent 64371bb85c
commit 7b9cd28e90
17 changed files with 2322 additions and 11 deletions
+1018
View File
File diff suppressed because it is too large Load Diff
+176
View File
@@ -0,0 +1,176 @@
// ==================================================================================
// Copyright (c) 2016 HiFi-LoFi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is furnished
// to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ==================================================================================
#ifndef _AUDIOFFT_H
#define _AUDIOFFT_H
/**
* AudioFFT provides real-to-complex/complex-to-real FFT routines.
*
* Features:
*
* - Real-complex FFT and complex-real inverse FFT for power-of-2-sized real data.
*
* - Uniform interface to different FFT implementations (currently Ooura, FFTW3 and Apple Accelerate).
*
* - Complex data is handled in "split-complex" format, i.e. there are separate
* arrays for the real and imaginary parts which can be useful for SIMD optimizations
* (split-complex arrays have to be of length (size/2+1) representing bins from DC
* to Nyquist frequency).
*
* - Output is "ready to use" (all scaling etc. is already handled internally).
*
* - No allocations/deallocations after the initialization which makes it usable
* for real-time audio applications (that's what I wrote it for and using it).
*
*
* How to use it in your project:
*
* - Add the .h and .cpp file to your project - that's all.
*
* - To get extra speed, you can link FFTW3 to your project and define
* AUDIOFFT_FFTW3 (however, please check whether your project suits the
* according license).
*
* - To get the best speed on Apple platforms, you can link the Apple
* Accelerate framework to your project and define
* AUDIOFFT_APPLE_ACCELERATE (however, please check whether your
* project suits the according license).
*
*
* Remarks:
*
* - AudioFFT is not intended to be the fastest FFT, but to be a fast-enough
* FFT suitable for most audio applications.
*
* - AudioFFT uses the quite liberal MIT license.
*
*
* Example usage:
* @code
* #include "AudioFFT.h"
*
* void Example()
* {
* const size_t fftSize = 1024; // Needs to be power of 2!
*
* std::vector<float> input(fftSize, 0.0f);
* std::vector<float> re(audiofft::AudioFFT::ComplexSize(fftSize));
* std::vector<float> im(audiofft::AudioFFT::ComplexSize(fftSize));
* std::vector<float> output(fftSize);
*
* audiofft::AudioFFT fft;
* fft.init(1024);
* fft.fft(input.data(), re.data(), im.data());
* fft.ifft(output.data(), re.data(), im.data());
* }
* @endcode
*/
#include <cstddef>
#include <memory>
namespace audiofft
{
namespace details
{
class AudioFFTImpl
{
public:
AudioFFTImpl() = default;
virtual ~AudioFFTImpl() = default;
virtual void init(size_t size) = 0;
virtual void fft(const float* data, float* re, float* im) = 0;
virtual void ifft(float* data, const float* re, const float* im) = 0;
private:
AudioFFTImpl(const AudioFFTImpl&) = delete;
AudioFFTImpl& operator=(const AudioFFTImpl&) = delete;
};
}
// ======================================================
/**
* @class AudioFFT
* @brief Performs 1D FFTs
*/
class AudioFFT
{
public:
/**
* @brief Constructor
*/
AudioFFT();
/**
* @brief Initializes the FFT object
* @param size Size of the real input (must be power 2)
*/
void init(size_t size);
/**
* @brief Performs the forward FFT
* @param data The real input data (has to be of the length as specified in init())
* @param re The real part of the complex output (has to be of length as returned by ComplexSize())
* @param im The imaginary part of the complex output (has to be of length as returned by ComplexSize())
*/
void fft(const float* data, float* re, float* im);
/**
* @brief Performs the inverse FFT
* @param data The real output data (has to be of the length as specified in init())
* @param re The real part of the complex input (has to be of length as returned by ComplexSize())
* @param im The imaginary part of the complex input (has to be of length as returned by ComplexSize())
*/
void ifft(float* data, const float* re, const float* im);
/**
* @brief Calculates the necessary size of the real/imaginary complex arrays
* @param size The size of the real data
* @return The size of the real/imaginary complex arrays
*/
static size_t ComplexSize(size_t size);
private:
std::unique_ptr<details::AudioFFTImpl> _impl;
AudioFFT(const AudioFFT&) = delete;
AudioFFT& operator=(const AudioFFT&) = delete;
};
/**
* @deprecated
* @brief Let's keep an AudioFFTBase type around for now because it has been here already in the 1st version in order to avoid breaking existing code.
*/
typedef AudioFFT AudioFFTBase;
} // End of namespace
#endif // Header guard
+202
View File
@@ -0,0 +1,202 @@
// ==================================================================================
// Copyright (c) 2012 HiFi-LoFi
//
// This 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.
//
// 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.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// ==================================================================================
#include "FFTConvolver.h"
#include <cassert>
#include <cmath>
#if defined (FFTCONVOLVER_USE_SSE)
#include <xmmintrin.h>
#endif
namespace fftconvolver
{
FFTConvolver::FFTConvolver() :
_blockSize(0),
_segSize(0),
_segCount(0),
_fftComplexSize(0),
_segments(),
_segmentsIR(),
_fftBuffer(),
_fft(),
_preMultiplied(),
_conv(),
_overlap(),
_current(0),
_inputBuffer(),
_inputBufferFill(0)
{
}
FFTConvolver::~FFTConvolver()
{
reset();
}
void FFTConvolver::reset()
{
for (size_t i=0; i<_segCount; ++i)
{
delete _segments[i];
delete _segmentsIR[i];
}
_blockSize = 0;
_segSize = 0;
_segCount = 0;
_fftComplexSize = 0;
_segments.clear();
_segmentsIR.clear();
_fftBuffer.clear();
_fft.init(0);
_preMultiplied.clear();
_conv.clear();
_overlap.clear();
_current = 0;
_inputBuffer.clear();
_inputBufferFill = 0;
}
bool FFTConvolver::init(size_t blockSize, const Sample* ir, size_t irLen)
{
reset();
if (blockSize == 0)
{
return false;
}
// Ignore zeros at the end of the impulse response because they only waste computation time
while (irLen > 0 && ::fabs(ir[irLen-1]) < 0.000001f)
{
--irLen;
}
if (irLen == 0)
{
return true;
}
_blockSize = NextPowerOf2(blockSize);
_segSize = 2 * _blockSize;
_segCount = static_cast<size_t>(::ceil(static_cast<float>(irLen) / static_cast<float>(_blockSize)));
_fftComplexSize = audiofft::AudioFFT::ComplexSize(_segSize);
// FFT
_fft.init(_segSize);
_fftBuffer.resize(_segSize);
// Prepare segments
for (size_t i=0; i<_segCount; ++i)
{
_segments.push_back(new SplitComplex(_fftComplexSize));
}
// Prepare IR
for (size_t i=0; i<_segCount; ++i)
{
SplitComplex* segment = new SplitComplex(_fftComplexSize);
const size_t remaining = irLen - (i * _blockSize);
const size_t sizeCopy = (remaining >= _blockSize) ? _blockSize : remaining;
CopyAndPad(_fftBuffer, &ir[i*_blockSize], sizeCopy);
_fft.fft(_fftBuffer.data(), segment->re(), segment->im());
_segmentsIR.push_back(segment);
}
// Prepare convolution buffers
_preMultiplied.resize(_fftComplexSize);
_conv.resize(_fftComplexSize);
_overlap.resize(_blockSize);
// Prepare input buffer
_inputBuffer.resize(_blockSize);
_inputBufferFill = 0;
// Reset current position
_current = 0;
return true;
}
void FFTConvolver::process(const Sample* input, Sample* output, size_t len)
{
if (_segCount == 0)
{
::memset(output, 0, len * sizeof(Sample));
return;
}
size_t processed = 0;
while (processed < len)
{
const bool inputBufferWasEmpty = (_inputBufferFill == 0);
const size_t processing = std::min(len-processed, _blockSize-_inputBufferFill);
const size_t inputBufferPos = _inputBufferFill;
::memcpy(_inputBuffer.data()+inputBufferPos, input+processed, processing * sizeof(Sample));
// Forward FFT
CopyAndPad(_fftBuffer, &_inputBuffer[0], _blockSize);
_fft.fft(_fftBuffer.data(), _segments[_current]->re(), _segments[_current]->im());
// Complex multiplication
if (inputBufferWasEmpty)
{
_preMultiplied.setZero();
for (size_t i=1; i<_segCount; ++i)
{
const size_t indexIr = i;
const size_t indexAudio = (_current + i) % _segCount;
ComplexMultiplyAccumulate(_preMultiplied, *_segmentsIR[indexIr], *_segments[indexAudio]);
}
}
_conv.copyFrom(_preMultiplied);
ComplexMultiplyAccumulate(_conv, *_segments[_current], *_segmentsIR[0]);
// Backward FFT
_fft.ifft(_fftBuffer.data(), _conv.re(), _conv.im());
// Add overlap
Sum(output+processed, _fftBuffer.data()+inputBufferPos, _overlap.data()+inputBufferPos, processing);
// Input buffer full => Next block
_inputBufferFill += processing;
if (_inputBufferFill == _blockSize)
{
// Input buffer is empty again now
_inputBuffer.setZero();
_inputBufferFill = 0;
// Save the overlap
::memcpy(_overlap.data(), _fftBuffer.data()+_blockSize, _blockSize * sizeof(Sample));
// Update current segment
_current = (_current > 0) ? (_current - 1) : (_segCount - 1);
}
processed += processing;
}
}
} // End of namespace fftconvolver
+100
View File
@@ -0,0 +1,100 @@
// ==================================================================================
// Copyright (c) 2012 HiFi-LoFi
//
// This 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.
//
// 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.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// ==================================================================================
#ifndef _FFTCONVOLVER_FFTCONVOLVER_H
#define _FFTCONVOLVER_FFTCONVOLVER_H
#include "AudioFFT.h"
#include "Utilities.h"
#include <vector>
namespace fftconvolver
{
/**
* @class FFTConvolver
* @brief Implementation of a partitioned FFT convolution algorithm with uniform block size
*
* Some notes on how to use it:
*
* - After initialization with an impulse response, subsequent data portions of
* arbitrary length can be convolved. The convolver internally can handle
* this by using appropriate buffering.
*
* - The convolver works without "latency" (except for the required
* processing time, of course), i.e. the output always is the convolved
* input for each processing call.
*
* - The convolver is suitable for real-time processing which means that no
* "unpredictable" operations like allocations, locking, API calls, etc. are
* performed during processing (all necessary allocations and preparations take
* place during initialization).
*/
class FFTConvolver
{
public:
FFTConvolver();
virtual ~FFTConvolver();
/**
* @brief Initializes the convolver
* @param blockSize Block size internally used by the convolver (partition size)
* @param ir The impulse response
* @param irLen Length of the impulse response
* @return true: Success - false: Failed
*/
bool init(size_t blockSize, const Sample* ir, size_t irLen);
/**
* @brief Convolves the the given input samples and immediately outputs the result
* @param input The input samples
* @param output The convolution result
* @param len Number of input/output samples
*/
void process(const Sample* input, Sample* output, size_t len);
/**
* @brief Resets the convolver and discards the set impulse response
*/
void reset();
private:
size_t _blockSize;
size_t _segSize;
size_t _segCount;
size_t _fftComplexSize;
std::vector<SplitComplex*> _segments;
std::vector<SplitComplex*> _segmentsIR;
SampleBuffer _fftBuffer;
audiofft::AudioFFT _fft;
SplitComplex _preMultiplied;
SplitComplex _conv;
SampleBuffer _overlap;
size_t _current;
SampleBuffer _inputBuffer;
size_t _inputBufferFill;
// Prevent uncontrolled usage
FFTConvolver(const FFTConvolver&);
FFTConvolver& operator=(const FFTConvolver&);
};
} // End of namespace fftconvolver
#endif // Header guard
+113
View File
@@ -0,0 +1,113 @@
// ==================================================================================
// Copyright (c) 2012 HiFi-LoFi
//
// This 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.
//
// 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.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// ==================================================================================
#include "Utilities.h"
namespace fftconvolver
{
bool SSEEnabled()
{
#if defined(FFTCONVOLVER_USE_SSE)
return true;
#else
return false;
#endif
}
void Sum(Sample* FFTCONVOLVER_RESTRICT result,
const Sample* FFTCONVOLVER_RESTRICT a,
const Sample* FFTCONVOLVER_RESTRICT b,
size_t len)
{
const size_t end4 = 4 * (len / 4);
for (size_t i=0; i<end4; i+=4)
{
result[i+0] = a[i+0] + b[i+0];
result[i+1] = a[i+1] + b[i+1];
result[i+2] = a[i+2] + b[i+2];
result[i+3] = a[i+3] + b[i+3];
}
for (size_t i=end4; i<len; ++i)
{
result[i] = a[i] + b[i];
}
}
void ComplexMultiplyAccumulate(SplitComplex& result, const SplitComplex& a, const SplitComplex& b)
{
assert(result.size() == a.size());
assert(result.size() == b.size());
ComplexMultiplyAccumulate(result.re(), result.im(), a.re(), a.im(), b.re(), b.im(), result.size());
}
void ComplexMultiplyAccumulate(Sample* FFTCONVOLVER_RESTRICT re,
Sample* FFTCONVOLVER_RESTRICT im,
const Sample* FFTCONVOLVER_RESTRICT reA,
const Sample* FFTCONVOLVER_RESTRICT imA,
const Sample* FFTCONVOLVER_RESTRICT reB,
const Sample* FFTCONVOLVER_RESTRICT imB,
const size_t len)
{
#if defined(FFTCONVOLVER_USE_SSE)
const size_t end4 = 4 * (len / 4);
for (size_t i=0; i<end4; i+=4)
{
const __m128 ra = _mm_load_ps(&reA[i]);
const __m128 rb = _mm_load_ps(&reB[i]);
const __m128 ia = _mm_load_ps(&imA[i]);
const __m128 ib = _mm_load_ps(&imB[i]);
__m128 real = _mm_load_ps(&re[i]);
__m128 imag = _mm_load_ps(&im[i]);
real = _mm_add_ps(real, _mm_mul_ps(ra, rb));
real = _mm_sub_ps(real, _mm_mul_ps(ia, ib));
_mm_store_ps(&re[i], real);
imag = _mm_add_ps(imag, _mm_mul_ps(ra, ib));
imag = _mm_add_ps(imag, _mm_mul_ps(ia, rb));
_mm_store_ps(&im[i], imag);
}
for (size_t i=end4; i<len; ++i)
{
re[i] += reA[i] * reB[i] - imA[i] * imB[i];
im[i] += reA[i] * imB[i] + imA[i] * reB[i];
}
#else
const size_t end4 = 4 * (len / 4);
for (size_t i=0; i<end4; i+=4)
{
re[i+0] += reA[i+0] * reB[i+0] - imA[i+0] * imB[i+0];
re[i+1] += reA[i+1] * reB[i+1] - imA[i+1] * imB[i+1];
re[i+2] += reA[i+2] * reB[i+2] - imA[i+2] * imB[i+2];
re[i+3] += reA[i+3] * reB[i+3] - imA[i+3] * imB[i+3];
im[i+0] += reA[i+0] * imB[i+0] + imA[i+0] * reB[i+0];
im[i+1] += reA[i+1] * imB[i+1] + imA[i+1] * reB[i+1];
im[i+2] += reA[i+2] * imB[i+2] + imA[i+2] * reB[i+2];
im[i+3] += reA[i+3] * imB[i+3] + imA[i+3] * reB[i+3];
}
for (size_t i=end4; i<len; ++i)
{
re[i] += reA[i] * reB[i] - imA[i] * imB[i];
im[i] += reA[i] * imB[i] + imA[i] * reB[i];
}
#endif
}
} // End of namespace fftconvolver
+351
View File
@@ -0,0 +1,351 @@
// ==================================================================================
// Copyright (c) 2012 HiFi-LoFi
//
// This 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.
//
// 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.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// ==================================================================================
#ifndef _FFTCONVOLVER_UTILITIES_H
#define _FFTCONVOLVER_UTILITIES_H
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstring>
#include <new>
namespace fftconvolver
{
#if defined(__SSE__) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
#if !defined(FFTCONVOLVER_USE_SSE) && !defined(FFTCONVOLVER_DONT_USE_SSE)
#define FFTCONVOLVER_USE_SSE
#endif
#endif
#if defined (FFTCONVOLVER_USE_SSE)
#include <xmmintrin.h>
#endif
#if defined(__GNUC__)
#define FFTCONVOLVER_RESTRICT __restrict__
#else
#define FFTCONVOLVER_RESTRICT
#endif
/**
* @brief Returns whether SSE optimization for the convolver is enabled
* @return true: Enabled - false: Disabled
*/
bool SSEEnabled();
/**
* @class Buffer
* @brief Simple buffer implementation (uses 16-byte alignment if SSE optimization is enabled)
*/
template<typename T>
class Buffer
{
public:
explicit Buffer(size_t initialSize = 0) :
_data(0),
_size(0)
{
resize(initialSize);
}
virtual ~Buffer()
{
clear();
}
void clear()
{
deallocate(_data);
_data = 0;
_size = 0;
}
void resize(size_t size)
{
if (_size != size)
{
clear();
if (size > 0)
{
assert(!_data && _size == 0);
_data = allocate(size);
_size = size;
}
}
setZero();
}
size_t size() const
{
return _size;
}
void setZero()
{
::memset(_data, 0, _size * sizeof(T));
}
void copyFrom(const Buffer<T>& other)
{
assert(_size == other._size);
if (this != &other)
{
::memcpy(_data, other._data, _size * sizeof(T));
}
}
T& operator[](size_t index)
{
assert(_data && index < _size);
return _data[index];
}
const T& operator[](size_t index) const
{
assert(_data && index < _size);
return _data[index];
}
operator bool() const
{
return (_data != 0 && _size > 0);
}
T* data()
{
return _data;
}
const T* data() const
{
return _data;
}
static void Swap(Buffer<T>& a, Buffer<T>& b)
{
std::swap(a._data, b._data);
std::swap(a._size, b._size);
}
private:
T* allocate(size_t size)
{
#if defined(FFTCONVOLVER_USE_SSE)
return static_cast<T*>(_mm_malloc(size * sizeof(T), 16));
#else
return new T[size];
#endif
}
void deallocate(T* ptr)
{
#if defined(FFTCONVOLVER_USE_SSE)
_mm_free(ptr);
#else
delete [] ptr;
#endif
}
T* _data;
size_t _size;
// Prevent uncontrolled usage
Buffer(const Buffer&);
Buffer& operator=(const Buffer&);
};
/**
* @brief Type of one sample
*/
typedef float Sample;
/**
* @brief Buffer for samples
*/
typedef Buffer<Sample> SampleBuffer;
/**
* @class SplitComplex
* @brief Buffer for split-complex representation of FFT results
*
* The split-complex representation stores the real and imaginary parts
* of FFT results in two different memory buffers which is useful e.g. for
* SIMD optimizations.
*/
class SplitComplex
{
public:
explicit SplitComplex(size_t initialSize = 0) :
_size(0),
_re(),
_im()
{
resize(initialSize);
}
~SplitComplex()
{
clear();
}
void clear()
{
_re.clear();
_im.clear();
_size = 0;
}
void resize(size_t newSize)
{
_re.resize(newSize);
_im.resize(newSize);
_size = newSize;
}
void setZero()
{
_re.setZero();
_im.setZero();
}
void copyFrom(const SplitComplex& other)
{
_re.copyFrom(other._re);
_im.copyFrom(other._im);
}
Sample* re()
{
return _re.data();
}
const Sample* re() const
{
return _re.data();
}
Sample* im()
{
return _im.data();
}
const Sample* im() const
{
return _im.data();
}
size_t size() const
{
return _size;
}
private:
size_t _size;
SampleBuffer _re;
SampleBuffer _im;
// Prevent uncontrolled usage
SplitComplex(const SplitComplex&);
SplitComplex& operator=(const SplitComplex&);
};
/**
* @brief Returns the next power of 2 of a given number
* @param val The number
* @return The next power of 2
*/
template<typename T>
T NextPowerOf2(const T& val)
{
T nextPowerOf2 = 1;
while (nextPowerOf2 < val)
{
nextPowerOf2 *= 2;
}
return nextPowerOf2;
}
/**
* @brief Sums two given sample arrays
* @param result The result array
* @param a The 1st array
* @param b The 2nd array
* @param len The length of the arrays
*/
void Sum(Sample* FFTCONVOLVER_RESTRICT result,
const Sample* FFTCONVOLVER_RESTRICT a,
const Sample* FFTCONVOLVER_RESTRICT b,
size_t len);
/**
* @brief Copies a source array into a destination buffer and pads the destination buffer with zeros
* @param dest The destination buffer
* @param src The source array
* @param srcSize The size of the source array
*/
template<typename T>
void CopyAndPad(Buffer<T>& dest, const T* src, size_t srcSize)
{
assert(dest.size() >= srcSize);
::memcpy(dest.data(), src, srcSize * sizeof(T));
::memset(dest.data() + srcSize, 0, (dest.size()-srcSize) * sizeof(T));
}
/**
* @brief Adds the complex product of two split-complex buffers to a result buffer
* @param result The result buffer
* @param a The 1st factor of the complex product
* @param b The 2nd factor of the complex product
*/
void ComplexMultiplyAccumulate(SplitComplex& result, const SplitComplex& a, const SplitComplex& b);
/**
* @brief Adds the complex product of two split-complex arrays to a result array
* @param re The real part of the result buffer
* @param im The imaginary part of the result buffer
* @param reA The real part of the 1st factor of the complex product
* @param imA The imaginary part of the 1st factor of the complex product
* @param reB The real part of the 2nd factor of the complex product
* @param imB The imaginary part of the 2nd factor of the complex product
*/
void ComplexMultiplyAccumulate(Sample* FFTCONVOLVER_RESTRICT re,
Sample* FFTCONVOLVER_RESTRICT im,
const Sample* FFTCONVOLVER_RESTRICT reA,
const Sample* FFTCONVOLVER_RESTRICT imA,
const Sample* FFTCONVOLVER_RESTRICT reB,
const Sample* FFTCONVOLVER_RESTRICT imB,
const size_t len);
} // End of namespace fftconvolver
#endif // Header guard
+67
View File
@@ -0,0 +1,67 @@
#include "convolver.h"
#include <sndfile.h>
#include "FFTConvolver.h"
#include "Utilities.h"
extern "C" {
#include "../common.h"
}
static fftconvolver::FFTConvolver convolver_l;
static fftconvolver::FFTConvolver convolver_r;
void convolver_init(const char* filename, int max_length)
{
SF_INFO info;
assert(filename);
SNDFILE* file = sf_open(filename, SFM_READ, &info);
assert(file);
if (info.samplerate != 44100)
die("Impulse file \"%s\" sample rate is %d Hz. Only 44100 Hz is supported", filename, info.samplerate);
if (info.channels != 1 && info.channels != 2)
die("Impulse file \"%s\" contains %d channels. Only 1 or 2 is supported.", filename, info.channels);
const size_t size = info.frames > max_length ? max_length : info.frames;
float buffer[size*info.channels];
size_t l = sf_readf_float(file, buffer, size);
assert(l == size);
if (info.channels == 1) {
convolver_l.init(352, buffer, size);
convolver_r.init(352, buffer, size);
} else {
// deinterleave
float buffer_l[size];
float buffer_r[size];
int i;
for (i=0; i<size; ++i)
{
buffer_l[i] = buffer[2*i+0];
buffer_r[i] = buffer[2*i+1];
}
convolver_l.init(352, buffer_l, size);
convolver_r.init(352, buffer_r, size);
}
debug(1, "IR initialized from \"%s\" with %d channels and %d samples", filename, info.channels, size);
sf_close(file);
}
void convolver_process_l(float* data, int length)
{
convolver_l.process(data, data, length);
}
void convolver_process_r(float* data, int length)
{
convolver_r.process(data, data, length);
}
+14
View File
@@ -0,0 +1,14 @@
// C wrapper to C++ FFTConvolver
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void convolver_init(const char* file, int max_length);
void convolver_process_l(float* data, int length);
void convolver_process_r(float* data, int length);
#ifdef __cplusplus
}
#endif