You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1002 lines
28 KiB

/* Copyright (c) 2020 Dario Mambro ( dario.mambro@gmail.com )
Copyright (c) 2020 Hayati Ayguen ( h_ayguen@web.de )
Redistribution and use of the Software in source and binary forms,
with or without modification, is permitted provided that the
following conditions are met:
- Neither the names of PFFFT, nor the names of its
sponsors or contributors may be used to endorse or promote products
derived from this Software without specific prior written permission.
- Redistributions of source code must retain the above copyright
notices, this list of conditions, and the disclaimer below.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions, and the disclaimer below in the
documentation and/or other materials provided with the
distribution.
THIS 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 CONTRIBUTORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL 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 WITH THE
SOFTWARE.
*/
#pragma once
#include <complex>
#include <vector>
#include <limits>
namespace {
#if defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) )
#include "pffft.h"
#endif
#if defined(PFFFT_ENABLE_DOUBLE)
#include "pffft_double.h"
#endif
}
namespace pffft {
// enum { PFFFT_REAL, PFFFT_COMPLEX }
typedef pffft_transform_t TransformType;
// define 'Scalar' and 'Complex' (in namespace pffft) with template Types<>
// and other type specific helper functions
template<typename T> struct Types {};
#if defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) )
template<> struct Types<float> {
typedef float Scalar;
typedef std::complex<Scalar> Complex;
static int simd_size() { return pffft_simd_size(); }
static const char * simd_arch() { return pffft_simd_arch(); }
};
template<> struct Types< std::complex<float> > {
typedef float Scalar;
typedef std::complex<float> Complex;
static int simd_size() { return pffft_simd_size(); }
static const char * simd_arch() { return pffft_simd_arch(); }
};
#endif
#if defined(PFFFT_ENABLE_DOUBLE)
template<> struct Types<double> {
typedef double Scalar;
typedef std::complex<Scalar> Complex;
static int simd_size() { return pffftd_simd_size(); }
static const char * simd_arch() { return pffftd_simd_arch(); }
};
template<> struct Types< std::complex<double> > {
typedef double Scalar;
typedef std::complex<double> Complex;
static int simd_size() { return pffftd_simd_size(); }
static const char * simd_arch() { return pffftd_simd_arch(); }
};
#endif
// Allocator
template<typename T> class PFAlloc;
namespace {
template<typename T> class Setup;
}
#if (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900))
// define AlignedVector<T> utilizing 'using' in C++11
template<typename T>
using AlignedVector = typename std::vector< T, PFAlloc<T> >;
#else
// define AlignedVector<T> having to derive std::vector<>
template <typename T>
struct AlignedVector : public std::vector< T, PFAlloc<T> > {
AlignedVector() : std::vector< T, PFAlloc<T> >() { }
AlignedVector(int N) : std::vector< T, PFAlloc<T> >(N) { }
};
#endif
// T can be float, double, std::complex<float> or std::complex<double>
// define PFFFT_ENABLE_DOUBLE before include this file for double and std::complex<double>
template<typename T>
class Fft
{
public:
// define types value_type, Scalar and Complex
typedef T value_type;
typedef typename Types<T>::Scalar Scalar;
typedef typename Types<T>::Complex Complex;
// static retrospection functions
static bool isComplexTransform() { return sizeof(T) == sizeof(Complex); }
static bool isFloatScalar() { return sizeof(Scalar) == sizeof(float); }
static bool isDoubleScalar() { return sizeof(Scalar) == sizeof(double); }
// simple helper to get minimum possible fft length
static int minFFtsize() { return pffft_min_fft_size( isComplexTransform() ? PFFFT_COMPLEX : PFFFT_REAL ); }
// simple helper to determine next power of 2 - without inexact/rounding floating point operations
static int nextPowerOfTwo(int N) { return pffft_next_power_of_two(N); }
static bool isPowerOfTwo(int N) { return pffft_is_power_of_two(N) ? true : false; }
static int simd_size() { return Types<T>::simd_size(); }
static const char * simd_arch() { return Types<T>::simd_arch(); }
//////////////////
/*
* Contructor, with transformation length, preparing transforms.
*
* For length <= stackThresholdLen, the stack is used for the internal
* work memory. for bigger length', the heap is used.
*
* Using the stack is probably the best strategy for small
* FFTs, say for N <= 4096). Threads usually have a small stack, that
* there's no sufficient amount of memory, usually leading to a crash!
*/
Fft( int length, int stackThresholdLen = 4096 );
~Fft();
/*
* prepare for transformation length 'newLength'.
* length is identical to forward()'s input vector's size,
* and also equals inverse()'s output vector size.
* this function is no simple setter. it pre-calculates twiddle factors.
*/
void prepareLength(int newLength);
/*
* retrieve the transformation length.
*/
int getLength() const { return length; }
/*
* retrieve size of complex spectrum vector,
* the output of forward()
*/
int getSpectrumSize() const { return isComplexTransform() ? length : ( length / 2 ); }
/*
* retrieve size of spectrum vector - in internal layout;
* the output of forwardToInternalLayout()
*/
int getInternalLayoutSize() const { return isComplexTransform() ? ( 2 * length ) : length; }
////////////////////////////////////////////
////
//// API 1, with std::vector<> based containers,
//// which free the allocated memory themselves (RAII).
////
//// uses an Allocator for the alignment of SIMD data.
////
////////////////////////////////////////////
// create suitably preallocated aligned vector for one FFT
AlignedVector<T> valueVector() const;
AlignedVector<Complex> spectrumVector() const;
AlignedVector<Scalar> internalLayoutVector() const;
////////////////////////////////////////////
// although using Vectors for output ..
// they need to have resize() applied before!
// core API, having the spectrum in canonical order
/*
* Perform the forward Fourier transform.
*
* Transforms are not scaled: inverse(forward(x)) = N*x.
* Typically you will want to scale the backward transform by 1/N.
*
* The output 'spectrum' is canonically ordered - as expected.
*
* a) for complex input isComplexTransform() == true,
* and transformation length N the output array is complex:
* index k in 0 .. N/2 -1 corresponds to frequency k * Samplerate / N
* index k in N/2 .. N -1 corresponds to frequency (k -N) * Samplerate / N,
* resulting in negative frequencies
*
* b) for real input isComplexTransform() == false,
* and transformation length N the output array is 'mostly' complex:
* index k in 1 .. N/2 -1 corresponds to frequency k * Samplerate / N
* index k == 0 is a special case:
* the real() part contains the result for the DC frequency 0,
* the imag() part contains the result for the Nyquist frequency Samplerate/2
* both 0-frequency and half frequency components, which are real,
* are assembled in the first entry as F(0)+i*F(N/2).
*
* input and output may alias - if you do nasty type conversion.
* return is just the given output parameter 'spectrum'.
*/
AlignedVector<Complex> & forward(const AlignedVector<T> & input, AlignedVector<Complex> & spectrum);
/*
* Perform the inverse Fourier transform, see forward().
* return is just the given output parameter 'output'.
*/
AlignedVector<T> & inverse(const AlignedVector<Complex> & spectrum, AlignedVector<T> & output);
// provide additional functions with spectrum in some internal Layout.
// these are faster, cause the implementation omits the reordering.
// these are useful in special applications, like fast convolution,
// where inverse() is following anyway ..
/*
* Perform the forward Fourier transform - similar to forward(), BUT:
*
* The z-domain data is stored in the most efficient order
* for transforming it back, or using it for convolution.
* If you need to have its content sorted in the "usual" canonical order,
* either use forward(), or call reorderSpectrum() after calling
* forwardToInternalLayout(), and before the backward fft
*
* return is just the given output parameter 'spectrum_internal_layout'.
*/
AlignedVector<Scalar> & forwardToInternalLayout(
const AlignedVector<T> & input,
AlignedVector<Scalar> & spectrum_internal_layout );
/*
* Perform the inverse Fourier transform, see forwardToInternalLayout()
*
* return is just the given output parameter 'output'.
*/
AlignedVector<T> & inverseFromInternalLayout(
const AlignedVector<Scalar> & spectrum_internal_layout,
AlignedVector<T> & output );
/*
* Reorder the spectrum from internal layout to have the
* frequency components in the correct "canonical" order.
* see forward() for a description of the canonical order.
*
* input and output should not alias.
*/
void reorderSpectrum(
const AlignedVector<Scalar> & input,
AlignedVector<Complex> & output );
/*
* Perform a multiplication of the frequency components of
* spectrum_internal_a and spectrum_internal_b
* into spectrum_internal_ab.
* The arrays should have been obtained with forwardToInternalLayout)
* and should *not* have been reordered with reorderSpectrum().
*
* the operation performed is:
* spectrum_internal_ab = (spectrum_internal_a * spectrum_internal_b)*scaling
*
* The spectrum_internal_[a][b], pointers may alias.
* return is just the given output parameter 'spectrum_internal_ab'.
*/
AlignedVector<Scalar> & convolve(
const AlignedVector<Scalar> & spectrum_internal_a,
const AlignedVector<Scalar> & spectrum_internal_b,
AlignedVector<Scalar> & spectrum_internal_ab,
const Scalar scaling );
/*
* Perform a multiplication and accumulation of the frequency components
* - similar to convolve().
*
* the operation performed is:
* spectrum_internal_ab += (spectrum_internal_a * spectrum_internal_b)*scaling
*
* The spectrum_internal_[a][b], pointers may alias.
* return is just the given output parameter 'spectrum_internal_ab'.
*/
AlignedVector<Scalar> & convolveAccumulate(
const AlignedVector<Scalar> & spectrum_internal_a,
const AlignedVector<Scalar> & spectrum_internal_b,
AlignedVector<Scalar> & spectrum_internal_ab,
const Scalar scaling );
////////////////////////////////////////////
////
//// API 2, dealing with raw pointers,
//// which need to be deallocated using alignedFree()
////
//// the special allocation is required cause SIMD
//// implementations require aligned memory
////
//// Method descriptions are equal to the methods above,
//// having AlignedVector<T> parameters - instead of raw pointers.
//// That is why following methods have no documentation.
////
////////////////////////////////////////////
static void alignedFree(void* ptr);
static T * alignedAllocType(int length);
static Scalar* alignedAllocScalar(int length);
static Complex* alignedAllocComplex(int length);
// core API, having the spectrum in canonical order
Complex* forward(const T* input, Complex* spectrum);
T* inverse(const Complex* spectrum, T* output);
// provide additional functions with spectrum in some internal Layout.
// these are faster, cause the implementation omits the reordering.
// these are useful in special applications, like fast convolution,
// where inverse() is following anyway ..
Scalar* forwardToInternalLayout(const T* input,
Scalar* spectrum_internal_layout);
T* inverseFromInternalLayout(const Scalar* spectrum_internal_layout, T* output);
void reorderSpectrum(const Scalar* input, Complex* output );
Scalar* convolve(const Scalar* spectrum_internal_a,
const Scalar* spectrum_internal_b,
Scalar* spectrum_internal_ab,
const Scalar scaling);
Scalar* convolveAccumulate(const Scalar* spectrum_internal_a,
const Scalar* spectrum_internal_b,
Scalar* spectrum_internal_ab,
const Scalar scaling);
private:
Setup<T> setup;
Scalar* work;
int length;
int stackThresholdLen;
};
template<typename T>
inline T* alignedAlloc(int length) {
return (T*)pffft_aligned_malloc( length * sizeof(T) );
}
inline void alignedFree(void *ptr) {
pffft_aligned_free(ptr);
}
// simple helper to get minimum possible fft length
inline int minFFtsize(pffft_transform_t transform) {
return pffft_min_fft_size(transform);
}
// simple helper to determine next power of 2 - without inexact/rounding floating point operations
inline int nextPowerOfTwo(int N) {
return pffft_next_power_of_two(N);
}
inline bool isPowerOfTwo(int N) {
return pffft_is_power_of_two(N) ? true : false;
}
////////////////////////////////////////////////////////////////////
// implementation
namespace {
template<typename T>
class Setup
{};
#if defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) )
template<>
class Setup<float>
{
PFFFT_Setup* self;
public:
typedef float value_type;
typedef Types< value_type >::Scalar Scalar;
Setup()
: self(NULL)
{}
void prepareLength(int length)
{
if (self) {
pffft_destroy_setup(self);
}
self = pffft_new_setup(length, PFFFT_REAL);
}
~Setup() { pffft_destroy_setup(self); }
void transform_ordered(const Scalar* input,
Scalar* output,
Scalar* work,
pffft_direction_t direction)
{
pffft_transform_ordered(self, input, output, work, direction);
}
void transform(const Scalar* input,
Scalar* output,
Scalar* work,
pffft_direction_t direction)
{
pffft_transform(self, input, output, work, direction);
}
void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
{
pffft_zreorder(self, input, output, direction);
}
void convolveAccumulate(const Scalar* dft_a,
const Scalar* dft_b,
Scalar* dft_ab,
const Scalar scaling)
{
pffft_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
}
void convolve(const Scalar* dft_a,
const Scalar* dft_b,
Scalar* dft_ab,
const Scalar scaling)
{
pffft_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
}
};
template<>
class Setup< std::complex<float> >
{
PFFFT_Setup* self;
public:
typedef std::complex<float> value_type;
typedef Types< value_type >::Scalar Scalar;
Setup()
: self(NULL)
{}
~Setup() { pffft_destroy_setup(self); }
void prepareLength(int length)
{
if (self) {
pffft_destroy_setup(self);
}
self = pffft_new_setup(length, PFFFT_COMPLEX);
}
void transform_ordered(const Scalar* input,
Scalar* output,
Scalar* work,
pffft_direction_t direction)
{
pffft_transform_ordered(self, input, output, work, direction);
}
void transform(const Scalar* input,
Scalar* output,
Scalar* work,
pffft_direction_t direction)
{
pffft_transform(self, input, output, work, direction);
}
void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
{
pffft_zreorder(self, input, output, direction);
}
void convolve(const Scalar* dft_a,
const Scalar* dft_b,
Scalar* dft_ab,
const Scalar scaling)
{
pffft_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
}
};
#endif /* defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) ) */
#if defined(PFFFT_ENABLE_DOUBLE)
template<>
class Setup<double>
{
PFFFTD_Setup* self;
public:
typedef double value_type;
typedef Types< value_type >::Scalar Scalar;
Setup()
: self(NULL)
{}
~Setup() { pffftd_destroy_setup(self); }
void prepareLength(int length)
{
if (self) {
pffftd_destroy_setup(self);
self = NULL;
}
if (length > 0) {
self = pffftd_new_setup(length, PFFFT_REAL);
}
}
void transform_ordered(const Scalar* input,
Scalar* output,
Scalar* work,
pffft_direction_t direction)
{
pffftd_transform_ordered(self, input, output, work, direction);
}
void transform(const Scalar* input,
Scalar* output,
Scalar* work,
pffft_direction_t direction)
{
pffftd_transform(self, input, output, work, direction);
}
void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
{
pffftd_zreorder(self, input, output, direction);
}
void convolveAccumulate(const Scalar* dft_a,
const Scalar* dft_b,
Scalar* dft_ab,
const Scalar scaling)
{
pffftd_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
}
void convolve(const Scalar* dft_a,
const Scalar* dft_b,
Scalar* dft_ab,
const Scalar scaling)
{
pffftd_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
}
};
template<>
class Setup< std::complex<double> >
{
PFFFTD_Setup* self;
public:
typedef std::complex<double> value_type;
typedef Types< value_type >::Scalar Scalar;
Setup()
: self(NULL)
{}
~Setup() { pffftd_destroy_setup(self); }
void prepareLength(int length)
{
if (self) {
pffftd_destroy_setup(self);
}
self = pffftd_new_setup(length, PFFFT_COMPLEX);
}
void transform_ordered(const Scalar* input,
Scalar* output,
Scalar* work,
pffft_direction_t direction)
{
pffftd_transform_ordered(self, input, output, work, direction);
}
void transform(const Scalar* input,
Scalar* output,
Scalar* work,
pffft_direction_t direction)
{
pffftd_transform(self, input, output, work, direction);
}
void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
{
pffftd_zreorder(self, input, output, direction);
}
void convolveAccumulate(const Scalar* dft_a,
const Scalar* dft_b,
Scalar* dft_ab,
const Scalar scaling)
{
pffftd_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
}
void convolve(const Scalar* dft_a,
const Scalar* dft_b,
Scalar* dft_ab,
const Scalar scaling)
{
pffftd_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
}
};
#endif /* defined(PFFFT_ENABLE_DOUBLE) */
} // end of anonymous namespace for Setup<>
template<typename T>
inline Fft<T>::Fft(int length, int stackThresholdLen)
: length(0)
, stackThresholdLen(stackThresholdLen)
, work(NULL)
{
#if (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900))
static_assert( sizeof(Complex) == 2 * sizeof(Scalar), "pffft requires sizeof(std::complex<>) == 2 * sizeof(Scalar)" );
#elif defined(__GNUC__)
char static_assert_like[(sizeof(Complex) == 2 * sizeof(Scalar)) ? 1 : -1]; // pffft requires sizeof(std::complex<>) == 2 * sizeof(Scalar)
#endif
prepareLength(length);
}
template<typename T>
inline Fft<T>::~Fft()
{
alignedFree(work);
}
template<typename T>
inline void
Fft<T>::prepareLength(int newLength)
{
const bool wasOnHeap = ( work != NULL );
const bool useHeap = newLength > stackThresholdLen;
if (useHeap == wasOnHeap && newLength == length) {
return;
}
length = newLength;
setup.prepareLength(length);
if (work) {
alignedFree(work);
work = NULL;
}
if (useHeap) {
work = reinterpret_cast<Scalar*>( alignedAllocType(length) );
}
}
template<typename T>
inline AlignedVector<T>
Fft<T>::valueVector() const
{
return AlignedVector<T>(length);
}
template<typename T>
inline AlignedVector< typename Fft<T>::Complex >
Fft<T>::spectrumVector() const
{
return AlignedVector<Complex>( getSpectrumSize() );
}
template<typename T>
inline AlignedVector< typename Fft<T>::Scalar >
Fft<T>::internalLayoutVector() const
{
return AlignedVector<Scalar>( getInternalLayoutSize() );
}
template<typename T>
inline AlignedVector< typename Fft<T>::Complex > &
Fft<T>::forward(const AlignedVector<T> & input, AlignedVector<Complex> & spectrum)
{
forward( input.data(), spectrum.data() );
return spectrum;
}
template<typename T>
inline AlignedVector<T> &
Fft<T>::inverse(const AlignedVector<Complex> & spectrum, AlignedVector<T> & output)
{
inverse( spectrum.data(), output.data() );
return output;
}
template<typename T>
inline AlignedVector< typename Fft<T>::Scalar > &
Fft<T>::forwardToInternalLayout(
const AlignedVector<T> & input,
AlignedVector<Scalar> & spectrum_internal_layout )
{
forwardToInternalLayout( input.data(), spectrum_internal_layout.data() );
return spectrum_internal_layout;
}
template<typename T>
inline AlignedVector<T> &
Fft<T>::inverseFromInternalLayout(
const AlignedVector<Scalar> & spectrum_internal_layout,
AlignedVector<T> & output )
{
inverseFromInternalLayout( spectrum_internal_layout.data(), output.data() );
return output;
}
template<typename T>
inline void
Fft<T>::reorderSpectrum(
const AlignedVector<Scalar> & input,
AlignedVector<Complex> & output )
{
reorderSpectrum( input.data(), output.data() );
}
template<typename T>
inline AlignedVector< typename Fft<T>::Scalar > &
Fft<T>::convolveAccumulate(
const AlignedVector<Scalar> & spectrum_internal_a,
const AlignedVector<Scalar> & spectrum_internal_b,
AlignedVector<Scalar> & spectrum_internal_ab,
const Scalar scaling )
{
convolveAccumulate( spectrum_internal_a.data(), spectrum_internal_b.data(),
spectrum_internal_ab.data(), scaling );
return spectrum_internal_ab;
}
template<typename T>
inline AlignedVector< typename Fft<T>::Scalar > &
Fft<T>::convolve(
const AlignedVector<Scalar> & spectrum_internal_a,
const AlignedVector<Scalar> & spectrum_internal_b,
AlignedVector<Scalar> & spectrum_internal_ab,
const Scalar scaling )
{
convolve( spectrum_internal_a.data(), spectrum_internal_b.data(),
spectrum_internal_ab.data(), scaling );
return spectrum_internal_ab;
}
template<typename T>
inline typename Fft<T>::Complex *
Fft<T>::forward(const T* input, Complex * spectrum)
{
setup.transform_ordered(reinterpret_cast<const Scalar*>(input),
reinterpret_cast<Scalar*>(spectrum),
work,
PFFFT_FORWARD);
return spectrum;
}
template<typename T>
inline T*
Fft<T>::inverse(Complex const* spectrum, T* output)
{
setup.transform_ordered(reinterpret_cast<const Scalar*>(spectrum),
reinterpret_cast<Scalar*>(output),
work,
PFFFT_BACKWARD);
return output;
}
template<typename T>
inline typename pffft::Fft<T>::Scalar*
Fft<T>::forwardToInternalLayout(const T* input, Scalar* spectrum_internal_layout)
{
setup.transform(reinterpret_cast<const Scalar*>(input),
spectrum_internal_layout,
work,
PFFFT_FORWARD);
return spectrum_internal_layout;
}
template<typename T>
inline T*
Fft<T>::inverseFromInternalLayout(const Scalar* spectrum_internal_layout, T* output)
{
setup.transform(spectrum_internal_layout,
reinterpret_cast<Scalar*>(output),
work,
PFFFT_BACKWARD);
return output;
}
template<typename T>
inline void
Fft<T>::reorderSpectrum( const Scalar* input, Complex* output )
{
setup.reorder(input, reinterpret_cast<Scalar*>(output), PFFFT_FORWARD);
}
template<typename T>
inline typename pffft::Fft<T>::Scalar*
Fft<T>::convolveAccumulate(const Scalar* dft_a,
const Scalar* dft_b,
Scalar* dft_ab,
const Scalar scaling)
{
setup.convolveAccumulate(dft_a, dft_b, dft_ab, scaling);
return dft_ab;
}
template<typename T>
inline typename pffft::Fft<T>::Scalar*
Fft<T>::convolve(const Scalar* dft_a,
const Scalar* dft_b,
Scalar* dft_ab,
const Scalar scaling)
{
setup.convolve(dft_a, dft_b, dft_ab, scaling);
return dft_ab;
}
template<typename T>
inline void
Fft<T>::alignedFree(void* ptr)
{
pffft::alignedFree(ptr);
}
template<typename T>
inline T*
pffft::Fft<T>::alignedAllocType(int length)
{
return alignedAlloc<T>(length);
}
template<typename T>
inline typename pffft::Fft<T>::Scalar*
pffft::Fft<T>::alignedAllocScalar(int length)
{
return alignedAlloc<Scalar>(length);
}
template<typename T>
inline typename Fft<T>::Complex *
Fft<T>::alignedAllocComplex(int length)
{
return alignedAlloc<Complex>(length);
}
////////////////////////////////////////////////////////////////////
// Allocator - for std::vector<>:
// origin: http://www.josuttis.com/cppcode/allocator.html
// http://www.josuttis.com/cppcode/myalloc.hpp
//
// minor renaming and utilizing of pffft (de)allocation functions
// are applied to Jossutis' allocator
/* The following code example is taken from the book
* "The C++ Standard Library - A Tutorial and Reference"
* by Nicolai M. Josuttis, Addison-Wesley, 1999
*
* (C) Copyright Nicolai M. Josuttis 1999.
* Permission to copy, use, modify, sell and distribute this software
* is granted provided this copyright notice appears in all copies.
* This software is provided "as is" without express or implied
* warranty, and with no claim as to its suitability for any purpose.
*/
template <class T>
class PFAlloc {
public:
// type definitions
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
// rebind allocator to type U
template <class U>
struct rebind {
typedef PFAlloc<U> other;
};
// return address of values
pointer address (reference value) const {
return &value;
}
const_pointer address (const_reference value) const {
return &value;
}
/* constructors and destructor
* - nothing to do because the allocator has no state
*/
PFAlloc() throw() {
}
PFAlloc(const PFAlloc&) throw() {
}
template <class U>
PFAlloc (const PFAlloc<U>&) throw() {
}
~PFAlloc() throw() {
}
// return maximum number of elements that can be allocated
size_type max_size () const throw() {
return std::numeric_limits<std::size_t>::max() / sizeof(T);
}
// allocate but don't initialize num elements of type T
pointer allocate (size_type num, const void* = 0) {
pointer ret = (pointer)( alignedAlloc<T>(num) );
return ret;
}
// initialize elements of allocated storage p with value value
void construct (pointer p, const T& value) {
// initialize memory with placement new
new((void*)p)T(value);
}
// destroy elements of initialized storage p
void destroy (pointer p) {
// destroy objects by calling their destructor
p->~T();
}
// deallocate storage p of deleted elements
void deallocate (pointer p, size_type num) {
// deallocate memory with pffft
alignedFree( (void*)p );
}
};
// return that all specializations of this allocator are interchangeable
template <class T1, class T2>
bool operator== (const PFAlloc<T1>&,
const PFAlloc<T2>&) throw() {
return true;
}
template <class T1, class T2>
bool operator!= (const PFAlloc<T1>&,
const PFAlloc<T2>&) throw() {
return false;
}
} // namespace pffft