Enhancements
Enable the AirPlay 2 build to operate an optional classic-AirPlay-only service or to gracefully degrade to classic AirPlay, as follows:
1. Add a new command-line option '-—service-type=<type>' and an equivalent configuration entry 'service_type = "<type>"' in the 'general' section of the configuration file,
where <type> can be "auto", "classic" or "airplay2":
1. "auto" (default) means that the service will be AirPlay 2 if NQPTP is running. If NQPTP is not running, classic AirPlay service will be provided instead, and in that case,
"(Classic)" will be appended to the default AirPlay service name visible to AirPlay clients like Apple Music, for example "RaspberryPi3B (Classic)".
2. "classic" means the service will be classic AirPlay (aka AirPlay 1).
3. "airplay2" means the service will be the modern AirPlay 2. In this case, as distinct from "auto", if NQPTP is not running, Shairport Sync will log an error and terminate.
2. In the systemd service file, NQPTP is now a "Want" rather than a "Require". If it's present, then it will be launched before Shairport Sync. If it's absent, Shairport Sync will launch anyway.
3. Improve the delivery of input format changes and emit 'sdsc' metadata when changes occur.
4. Emit new format information in the log if statistics is enabled.
Docker Changes
1. Support for linux/arm/v6 has been dropped, as Docker is no longer supported.
2. NQPTP is not started in the AirPlay 2 Docker image if '--service-type=classic' or '--service-type=airplay1' is in the command line options at the end of the docker run command.
The purpose is to ensure that ports 319 and 320 are left alone when the AirPlay 2 image is set to provide Classic service only.
Note that setting the configuration file 'service_type' to 'classic' will not prevent NQPTP from starting up -- you must use the command line option.
3. A new "dev" target has been added. It is a large image containing the custom-built FFmpeg library, NQPTP, Avahi and D-Bus along with the Shairport Sync source and
all necessary development tools. When started, Avahi, D-Bus and NQPTP are all installed and running. The bash shell has also been added and is entered.
Stability Improvements
Reorganise session preemption to fully terminate the existing session before starting a new one.
Don't delay closing the event port to wait for it to be closed at the client end.
Add a safe_socket_close() function to ensure sockets are fully closed. Use -1 to designate closed rather than 0, to prevent attempts to reclose sockets, causing mayhem.
Re-order FFmpeg decommissioning during teardown.
This commit is contained in:
Executable → Regular
+734
-866
File diff suppressed because it is too large
Load Diff
Executable → Regular
+122
-126
@@ -22,147 +22,143 @@
|
||||
#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
|
||||
*/
|
||||
|
||||
* 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 audiofft
|
||||
{
|
||||
namespace detail {
|
||||
class AudioFFTImpl;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
class AudioFFTImpl;
|
||||
}
|
||||
// =============================================================
|
||||
|
||||
/**
|
||||
* @class AudioFFT
|
||||
* @brief Performs 1D FFTs
|
||||
*/
|
||||
class AudioFFT {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
AudioFFT();
|
||||
|
||||
// =============================================================
|
||||
|
||||
AudioFFT(const AudioFFT &) = delete;
|
||||
AudioFFT &operator=(const AudioFFT &) = delete;
|
||||
|
||||
/**
|
||||
* @class AudioFFT
|
||||
* @brief Performs 1D FFTs
|
||||
* @brief Destructor
|
||||
*/
|
||||
class AudioFFT
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
AudioFFT();
|
||||
|
||||
AudioFFT(const AudioFFT&) = delete;
|
||||
AudioFFT& operator=(const AudioFFT&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~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<detail::AudioFFTImpl> _impl;
|
||||
};
|
||||
|
||||
~AudioFFT();
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @brief Initializes the FFT object
|
||||
* @param size Size of the real input (must be power 2)
|
||||
*/
|
||||
typedef AudioFFT AudioFFTBase;
|
||||
void init(size_t size);
|
||||
|
||||
} // End of namespace
|
||||
/**
|
||||
* @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<detail::AudioFFTImpl> _impl;
|
||||
};
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
} // namespace audiofft
|
||||
|
||||
#endif // Header guard
|
||||
|
||||
@@ -140,8 +140,11 @@ void ConvolverThreadPool::clearState(size_t convolverId) {
|
||||
if (convolverId < _convolvers.size()) {
|
||||
waitForAll();
|
||||
} else {
|
||||
debug(1, "assert(convolverId < _convolvers.size()) failed, with convolverId: %u and _convolvers.size(): %u.", convolverId, _convolvers.size());
|
||||
}
|
||||
debug(1,
|
||||
"assert(convolverId < _convolvers.size()) failed, with convolverId: %u and "
|
||||
"_convolvers.size(): %u.",
|
||||
convolverId, _convolvers.size());
|
||||
}
|
||||
}
|
||||
|
||||
/* this is the old version
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
|
||||
// Get the number of threads
|
||||
size_t getNumThreads() const { return _threads.size(); }
|
||||
|
||||
|
||||
void shutdown();
|
||||
|
||||
private:
|
||||
|
||||
Executable → Regular
+51
-87
@@ -20,47 +20,25 @@
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
#if defined (FFTCONVOLVER_USE_SSE)
|
||||
#include <xmmintrin.h>
|
||||
#if defined(FFTCONVOLVER_USE_SSE)
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
namespace fftconvolver {
|
||||
|
||||
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() :
|
||||
_blockSize(0),
|
||||
_segSize(0),
|
||||
_segCount(0),
|
||||
_fftComplexSize(0),
|
||||
_segments(),
|
||||
_segmentsIR(),
|
||||
_fftBuffer(),
|
||||
_fft(),
|
||||
_preMultiplied(),
|
||||
_conv(),
|
||||
_overlap(),
|
||||
_current(0),
|
||||
_inputBuffer(),
|
||||
_inputBufferFill(0)
|
||||
{
|
||||
}
|
||||
FFTConvolver::~FFTConvolver() { reset(); }
|
||||
|
||||
|
||||
FFTConvolver::~FFTConvolver()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
void FFTConvolver::reset()
|
||||
{
|
||||
for (size_t i=0; i<_segCount; ++i)
|
||||
{
|
||||
void FFTConvolver::reset() {
|
||||
for (size_t i = 0; i < _segCount; ++i) {
|
||||
delete _segments[i];
|
||||
delete _segmentsIR[i];
|
||||
}
|
||||
|
||||
|
||||
_blockSize = 0;
|
||||
_segSize = 0;
|
||||
_segCount = 0;
|
||||
@@ -77,115 +55,101 @@ void FFTConvolver::reset()
|
||||
_inputBufferFill = 0;
|
||||
}
|
||||
|
||||
void FFTConvolver::clearState()
|
||||
{
|
||||
if (_segCount == 0)
|
||||
{
|
||||
void FFTConvolver::clearState() {
|
||||
if (_segCount == 0) {
|
||||
return; // Not initialized
|
||||
}
|
||||
|
||||
|
||||
_inputBuffer.setZero();
|
||||
_inputBufferFill = 0;
|
||||
_overlap.setZero();
|
||||
|
||||
for (size_t i = 0; i < _segCount; ++i)
|
||||
{
|
||||
|
||||
for (size_t i = 0; i < _segCount; ++i) {
|
||||
_segments[i]->setZero();
|
||||
}
|
||||
|
||||
|
||||
_preMultiplied.setZero();
|
||||
_conv.setZero();
|
||||
_current = 0;
|
||||
}
|
||||
|
||||
bool FFTConvolver::init(size_t blockSize, const Sample* ir, size_t irLen)
|
||||
{
|
||||
|
||||
bool FFTConvolver::init(size_t blockSize, const Sample *ir, size_t irLen) {
|
||||
reset();
|
||||
|
||||
if (blockSize == 0)
|
||||
{
|
||||
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)
|
||||
{
|
||||
while (irLen > 0 && ::fabs(ir[irLen - 1]) < 0.000001f) {
|
||||
--irLen;
|
||||
}
|
||||
|
||||
if (irLen == 0)
|
||||
{
|
||||
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)));
|
||||
_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));
|
||||
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);
|
||||
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);
|
||||
CopyAndPad(_fftBuffer, &ir[i * _blockSize], sizeCopy);
|
||||
_fft.fft(_fftBuffer.data(), segment->re(), segment->im());
|
||||
_segmentsIR.push_back(segment);
|
||||
}
|
||||
|
||||
// Prepare convolution buffers
|
||||
|
||||
// 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
while (processed < len) {
|
||||
const bool inputBufferWasEmpty = (_inputBufferFill == 0);
|
||||
const size_t processing = std::min(len-processed, _blockSize-_inputBufferFill);
|
||||
const size_t processing = std::min(len - processed, _blockSize - _inputBufferFill);
|
||||
const size_t inputBufferPos = _inputBufferFill;
|
||||
::memcpy(_inputBuffer.data()+inputBufferPos, input+processed, processing * sizeof(Sample));
|
||||
::memcpy(_inputBuffer.data() + inputBufferPos, input + processed, processing * sizeof(Sample));
|
||||
|
||||
// Forward FFT
|
||||
CopyAndPad(_fftBuffer, &_inputBuffer[0], _blockSize);
|
||||
CopyAndPad(_fftBuffer, &_inputBuffer[0], _blockSize);
|
||||
_fft.fft(_fftBuffer.data(), _segments[_current]->re(), _segments[_current]->im());
|
||||
|
||||
// Complex multiplication
|
||||
if (inputBufferWasEmpty)
|
||||
{
|
||||
if (inputBufferWasEmpty) {
|
||||
_preMultiplied.setZero();
|
||||
for (size_t i=1; i<_segCount; ++i)
|
||||
{
|
||||
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]);
|
||||
@@ -198,18 +162,18 @@ void FFTConvolver::process(const Sample* input, Sample* output, size_t len)
|
||||
_fft.ifft(_fftBuffer.data(), _conv.re(), _conv.im());
|
||||
|
||||
// Add overlap
|
||||
Sum(output+processed, _fftBuffer.data()+inputBufferPos, _overlap.data()+inputBufferPos, processing);
|
||||
Sum(output + processed, _fftBuffer.data() + inputBufferPos, _overlap.data() + inputBufferPos,
|
||||
processing);
|
||||
|
||||
// Input buffer full => Next block
|
||||
_inputBufferFill += processing;
|
||||
if (_inputBufferFill == _blockSize)
|
||||
{
|
||||
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));
|
||||
::memcpy(_overlap.data(), _fftBuffer.data() + _blockSize, _blockSize * sizeof(Sample));
|
||||
|
||||
// Update current segment
|
||||
_current = (_current > 0) ? (_current - 1) : (_segCount - 1);
|
||||
@@ -218,5 +182,5 @@ void FFTConvolver::process(const Sample* input, Sample* output, size_t len)
|
||||
processed += processing;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace fftconvolver
|
||||
|
||||
Executable → Regular
+47
-50
@@ -27,61 +27,58 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace fftconvolver
|
||||
{
|
||||
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
|
||||
{
|
||||
* @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();
|
||||
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 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 Resets the convolver and discards the set impulse response
|
||||
*/
|
||||
* @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();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Clears audio history
|
||||
*/
|
||||
|
||||
* @brief Clears audio history
|
||||
*/
|
||||
|
||||
void clearState();
|
||||
|
||||
private:
|
||||
@@ -89,8 +86,8 @@ private:
|
||||
size_t _segSize;
|
||||
size_t _segCount;
|
||||
size_t _fftComplexSize;
|
||||
std::vector<SplitComplex*> _segments;
|
||||
std::vector<SplitComplex*> _segmentsIR;
|
||||
std::vector<SplitComplex *> _segments;
|
||||
std::vector<SplitComplex *> _segmentsIR;
|
||||
SampleBuffer _fftBuffer;
|
||||
audiofft::AudioFFT _fft;
|
||||
SplitComplex _preMultiplied;
|
||||
@@ -101,10 +98,10 @@ private:
|
||||
size_t _inputBufferFill;
|
||||
|
||||
// Prevent uncontrolled usage
|
||||
FFTConvolver(const FFTConvolver&);
|
||||
FFTConvolver& operator=(const FFTConvolver&);
|
||||
FFTConvolver(const FFTConvolver &);
|
||||
FFTConvolver &operator=(const FFTConvolver &);
|
||||
};
|
||||
|
||||
|
||||
} // End of namespace fftconvolver
|
||||
|
||||
#endif // Header guard
|
||||
|
||||
+30
-48
@@ -21,12 +21,9 @@
|
||||
|
||||
#include "Utilities.h"
|
||||
|
||||
namespace fftconvolver {
|
||||
|
||||
namespace fftconvolver
|
||||
{
|
||||
|
||||
bool SSEEnabled()
|
||||
{
|
||||
bool SSEEnabled() {
|
||||
#if defined(FFTCONVOLVER_USE_SSE)
|
||||
return true;
|
||||
#else
|
||||
@@ -34,47 +31,35 @@ bool SSEEnabled()
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Sum(Sample* FFTCONVOLVER_RESTRICT result,
|
||||
const Sample* FFTCONVOLVER_RESTRICT a,
|
||||
const Sample* FFTCONVOLVER_RESTRICT b,
|
||||
size_t len)
|
||||
{
|
||||
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 = 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)
|
||||
{
|
||||
for (size_t i = end4; i < len; ++i) {
|
||||
result[i] = a[i] + b[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ComplexMultiplyAccumulate(SplitComplex& result, const SplitComplex& a, const SplitComplex& b)
|
||||
{
|
||||
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());
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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]);
|
||||
@@ -88,26 +73,23 @@ void ComplexMultiplyAccumulate(Sample* FFTCONVOLVER_RESTRICT re,
|
||||
imag = _mm_add_ps(imag, _mm_mul_ps(ia, rb));
|
||||
_mm_store_ps(&im[i], imag);
|
||||
}
|
||||
for (size_t i=end4; i<len; ++i)
|
||||
{
|
||||
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 = 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)
|
||||
{
|
||||
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];
|
||||
}
|
||||
|
||||
+106
-194
@@ -28,71 +28,51 @@
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
|
||||
|
||||
namespace fftconvolver
|
||||
{
|
||||
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
|
||||
#if !defined(FFTCONVOLVER_USE_SSE) && !defined(FFTCONVOLVER_DONT_USE_SSE)
|
||||
#define FFTCONVOLVER_USE_SSE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#if defined (FFTCONVOLVER_USE_SSE)
|
||||
#include <xmmintrin.h>
|
||||
#if defined(FFTCONVOLVER_USE_SSE)
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define FFTCONVOLVER_RESTRICT __restrict__
|
||||
#define FFTCONVOLVER_RESTRICT __restrict__
|
||||
#else
|
||||
#define FFTCONVOLVER_RESTRICT
|
||||
#define FFTCONVOLVER_RESTRICT
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns whether SSE optimization for the convolver is enabled
|
||||
* @return true: Enabled - false: Disabled
|
||||
*/
|
||||
* @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);
|
||||
}
|
||||
* @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();
|
||||
}
|
||||
virtual ~Buffer() { clear(); }
|
||||
|
||||
void clear()
|
||||
{
|
||||
void clear() {
|
||||
deallocate(_data);
|
||||
_data = 0;
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
void resize(size_t size)
|
||||
{
|
||||
if (_size != size)
|
||||
{
|
||||
void resize(size_t size) {
|
||||
if (_size != size) {
|
||||
clear();
|
||||
|
||||
if (size > 0)
|
||||
{
|
||||
if (size > 0) {
|
||||
assert(!_data && _size == 0);
|
||||
_data = allocate(size);
|
||||
_size = size;
|
||||
@@ -101,172 +81,118 @@ public:
|
||||
setZero();
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
size_t size() const { return _size; }
|
||||
|
||||
void setZero()
|
||||
{
|
||||
::memset(_data, 0, _size * sizeof(T));
|
||||
}
|
||||
void setZero() { ::memset(_data, 0, _size * sizeof(T)); }
|
||||
|
||||
void copyFrom(const Buffer<T>& other)
|
||||
{
|
||||
void copyFrom(const Buffer<T> &other) {
|
||||
assert(_size == other._size);
|
||||
if (this != &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
::memcpy(_data, other._data, _size * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
T& operator[](size_t index)
|
||||
{
|
||||
T &operator[](size_t index) {
|
||||
assert(_data && index < _size);
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
const T& operator[](size_t index) const
|
||||
{
|
||||
const T &operator[](size_t index) const {
|
||||
assert(_data && index < _size);
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return (_data != 0 && _size > 0);
|
||||
}
|
||||
operator bool() const { return (_data != 0 && _size > 0); }
|
||||
|
||||
T* data()
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
T *data() { return _data; }
|
||||
|
||||
const T* data() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
const T *data() const { return _data; }
|
||||
|
||||
static void Swap(Buffer<T>& a, Buffer<T>& b)
|
||||
{
|
||||
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)
|
||||
{
|
||||
T *allocate(size_t size) {
|
||||
#if defined(FFTCONVOLVER_USE_SSE)
|
||||
return static_cast<T*>(_mm_malloc(size * sizeof(T), 16));
|
||||
return static_cast<T *>(_mm_malloc(size * sizeof(T), 16));
|
||||
#else
|
||||
return new T[size];
|
||||
#endif
|
||||
}
|
||||
|
||||
void deallocate(T* ptr)
|
||||
{
|
||||
|
||||
void deallocate(T *ptr) {
|
||||
#if defined(FFTCONVOLVER_USE_SSE)
|
||||
_mm_free(ptr);
|
||||
#else
|
||||
delete [] ptr;
|
||||
delete[] ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
T* _data;
|
||||
T *_data;
|
||||
size_t _size;
|
||||
|
||||
// Prevent uncontrolled usage
|
||||
Buffer(const Buffer&);
|
||||
Buffer& operator=(const Buffer&);
|
||||
Buffer(const Buffer &);
|
||||
Buffer &operator=(const Buffer &);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Type of one sample
|
||||
*/
|
||||
* @brief Type of one sample
|
||||
*/
|
||||
typedef float Sample;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Buffer for samples
|
||||
*/
|
||||
* @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
|
||||
{
|
||||
* @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);
|
||||
}
|
||||
explicit SplitComplex(size_t initialSize = 0) : _size(0), _re(), _im() { resize(initialSize); }
|
||||
|
||||
~SplitComplex()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
~SplitComplex() { clear(); }
|
||||
|
||||
void clear()
|
||||
{
|
||||
void clear() {
|
||||
_re.clear();
|
||||
_im.clear();
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
void resize(size_t newSize)
|
||||
{
|
||||
void resize(size_t newSize) {
|
||||
_re.resize(newSize);
|
||||
_im.resize(newSize);
|
||||
_size = newSize;
|
||||
}
|
||||
|
||||
void setZero()
|
||||
{
|
||||
void setZero() {
|
||||
_re.setZero();
|
||||
_im.setZero();
|
||||
}
|
||||
|
||||
void copyFrom(const SplitComplex& other)
|
||||
{
|
||||
void copyFrom(const SplitComplex &other) {
|
||||
_re.copyFrom(other._re);
|
||||
_im.copyFrom(other._im);
|
||||
}
|
||||
|
||||
Sample* re()
|
||||
{
|
||||
return _re.data();
|
||||
}
|
||||
Sample *re() { return _re.data(); }
|
||||
|
||||
const Sample* re() const
|
||||
{
|
||||
return _re.data();
|
||||
}
|
||||
const Sample *re() const { return _re.data(); }
|
||||
|
||||
Sample* im()
|
||||
{
|
||||
return _im.data();
|
||||
}
|
||||
Sample *im() { return _im.data(); }
|
||||
|
||||
const Sample* im() const
|
||||
{
|
||||
return _im.data();
|
||||
}
|
||||
const Sample *im() const { return _im.data(); }
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
size_t size() const { return _size; }
|
||||
|
||||
private:
|
||||
size_t _size;
|
||||
@@ -274,82 +200,68 @@ private:
|
||||
SampleBuffer _im;
|
||||
|
||||
// Prevent uncontrolled usage
|
||||
SplitComplex(const SplitComplex&);
|
||||
SplitComplex& operator=(const SplitComplex&);
|
||||
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)
|
||||
{
|
||||
* @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)
|
||||
{
|
||||
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 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)
|
||||
{
|
||||
* @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));
|
||||
::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 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);
|
||||
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
||||
+10
-12
@@ -46,8 +46,8 @@ void convolver_pool_init(size_t numThreads, size_t numConvolvers) {
|
||||
if (!pool.init(numThreads, numConvolvers)) {
|
||||
debug(1, "failed to initialize thread pool!");
|
||||
} else {
|
||||
debug(1, "thread pool initialized with %u thread%s and %u convolver%s.", numThreads, numThreads == 1 ? "" : "s",
|
||||
numConvolvers, numConvolvers == 1 ? "" : "s");
|
||||
debug(1, "thread pool initialized with %u thread%s and %u convolver%s.", numThreads,
|
||||
numThreads == 1 ? "" : "s", numConvolvers, numConvolvers == 1 ? "" : "s");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +56,8 @@ void convolver_pool_closedown() {
|
||||
debug(3, "thread pool shut down");
|
||||
}
|
||||
|
||||
int convolver_init(const char *filename, unsigned char channel_count,
|
||||
double max_length_in_seconds, size_t block_size) {
|
||||
int convolver_init(const char *filename, unsigned char channel_count, double max_length_in_seconds,
|
||||
size_t block_size) {
|
||||
debug(3, "convolver_init");
|
||||
int success = 0;
|
||||
SF_INFO info = {}; // Zero everything, including format
|
||||
@@ -67,10 +67,10 @@ int convolver_init(const char *filename, unsigned char channel_count,
|
||||
size_t max_length = (size_t)(max_length_in_seconds * info.samplerate);
|
||||
const size_t size =
|
||||
(unsigned int)info.frames > max_length ? max_length : (unsigned int)info.frames;
|
||||
float *buffer = (float*)malloc(sizeof(float) * size * info.channels);
|
||||
float *buffer = (float *)malloc(sizeof(float) * size * info.channels);
|
||||
if (buffer != NULL) {
|
||||
// float buffer[size * info.channels];
|
||||
float *abuffer = (float*)malloc(sizeof(float) * size);
|
||||
float *abuffer = (float *)malloc(sizeof(float) * size);
|
||||
if (abuffer != NULL) {
|
||||
size_t l = sf_readf_float(file, buffer, size);
|
||||
if (l != 0) {
|
||||
@@ -91,7 +91,7 @@ int convolver_init(const char *filename, unsigned char channel_count,
|
||||
}
|
||||
if (!pool.initConvolver(cc, block_size, abuffer, size)) {
|
||||
debug(1, "new convolver failed to initialize convolver %u ", cc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
success = 1;
|
||||
@@ -101,11 +101,11 @@ int convolver_init(const char *filename, unsigned char channel_count,
|
||||
"%d samples",
|
||||
filename, info.channels, info.channels == 1 ? "" : "s", size);
|
||||
sf_close(file);
|
||||
free((void*)abuffer);
|
||||
free((void *)abuffer);
|
||||
} else {
|
||||
debug(1, "failed to init convolvers because insufficient memory was available");
|
||||
}
|
||||
free((void*)buffer);
|
||||
free((void *)buffer);
|
||||
} else {
|
||||
warn("failed to init convolvers because insufficient memory was available");
|
||||
}
|
||||
@@ -124,9 +124,7 @@ void convolver_process(unsigned int channel, float *data, int length) {
|
||||
|
||||
void convolver_wait_for_all() { pool.waitForAll(); }
|
||||
|
||||
void convolver_clear_state() {
|
||||
pool.clearAllStates();
|
||||
}
|
||||
void convolver_clear_state() { pool.clearAllStates(); }
|
||||
|
||||
const unsigned int max_channels = 8;
|
||||
fftconvolver::FFTConvolver convolvers[max_channels];
|
||||
|
||||
@@ -5,22 +5,24 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
// int convolver_init(const char* file, unsigned char channel_count, double max_length_in_seconds, size_t block_size);
|
||||
#include <stddef.h>
|
||||
|
||||
// int convolver_init(const char* file, unsigned char channel_count, double max_length_in_seconds,
|
||||
// size_t block_size);
|
||||
void convolver_reset();
|
||||
//void convolver_clear_state();
|
||||
// void convolver_process(unsigned int channel, float *data, int length);
|
||||
// void convolver_process_l(float* data, int length);
|
||||
// void convolver_process_r(float* data, int length);
|
||||
// void convolver_clear_state();
|
||||
// void convolver_process(unsigned int channel, float *data, int length);
|
||||
// void convolver_process_l(float* data, int length);
|
||||
// void convolver_process_r(float* data, int length);
|
||||
|
||||
void convolver_pool_init(size_t numThreads, size_t numConvolvers);
|
||||
void convolver_pool_closedown();
|
||||
int convolver_init(const char* file, unsigned char channel_count, double max_length_in_seconds, size_t block_size);
|
||||
int convolver_init(const char *file, unsigned char channel_count, double max_length_in_seconds,
|
||||
size_t block_size);
|
||||
void convolver_process(unsigned int channel, float *data, int length);
|
||||
void convolver_clear_state();
|
||||
void convolver_wait_for_all();
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user