blob: 8437e0510b7b3365a97fba751b10431df203e2b4 [file] [log] [blame]
hayati ayguen63794b22020-03-29 03:14:43 +02001/* Copyright (c) 2020 Dario Mambro ( dario.mambro@gmail.com )
dario mambrocb971842020-03-28 00:22:33 +01002 Copyright (c) 2020 Hayati Ayguen ( h_ayguen@web.de )
hayati ayguen63794b22020-03-29 03:14:43 +02003
4 Redistribution and use of the Software in source and binary forms,
5 with or without modification, is permitted provided that the
6 following conditions are met:
7
8 - Neither the names of PFFFT, nor the names of its
9 sponsors or contributors may be used to endorse or promote products
10 derived from this Software without specific prior written permission.
11
12 - Redistributions of source code must retain the above copyright
13 notices, this list of conditions, and the disclaimer below.
14
15 - Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions, and the disclaimer below in the
17 documentation and/or other materials provided with the
18 distribution.
19
20 THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
24 HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
25 EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
26 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
28 SOFTWARE.
dario mambrocb971842020-03-28 00:22:33 +010029*/
30
31#pragma once
32
33#include <complex>
hayati ayguen1c193e92020-03-29 16:49:05 +020034#include <vector>
35#include <limits>
dario mambrocb971842020-03-28 00:22:33 +010036
37namespace {
hayati ayguenca112412020-04-13 00:19:40 +020038#if defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) )
dario mambrocb971842020-03-28 00:22:33 +010039#include "pffft.h"
hayati ayguenca112412020-04-13 00:19:40 +020040#endif
41#if defined(PFFFT_ENABLE_DOUBLE)
dario mambrocb971842020-03-28 00:22:33 +010042#include "pffft_double.h"
hayati ayguenca112412020-04-13 00:19:40 +020043#endif
dario mambrocb971842020-03-28 00:22:33 +010044}
45
46namespace pffft {
47
48// enum { PFFFT_REAL, PFFFT_COMPLEX }
49typedef pffft_transform_t TransformType;
50
hayati ayguenca112412020-04-13 00:19:40 +020051// define 'Scalar' and 'Complex' (in namespace pffft) with template Types<>
52// and other type specific helper functions
hayati ayguen55531192020-04-02 23:11:14 +020053template<typename T> struct Types {};
hayati ayguenca112412020-04-13 00:19:40 +020054#if defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) )
55template<> struct Types<float> {
56 typedef float Scalar;
57 typedef std::complex<Scalar> Complex;
hayati ayguenca112412020-04-13 00:19:40 +020058 static int simd_size() { return pffft_simd_size(); }
59 static const char * simd_arch() { return pffft_simd_arch(); }
60};
61template<> struct Types< std::complex<float> > {
62 typedef float Scalar;
63 typedef std::complex<float> Complex;
hayati ayguenca112412020-04-13 00:19:40 +020064 static int simd_size() { return pffft_simd_size(); }
65 static const char * simd_arch() { return pffft_simd_arch(); }
66};
67#endif
68#if defined(PFFFT_ENABLE_DOUBLE)
69template<> struct Types<double> {
70 typedef double Scalar;
71 typedef std::complex<Scalar> Complex;
hayati ayguenca112412020-04-13 00:19:40 +020072 static int simd_size() { return pffftd_simd_size(); }
73 static const char * simd_arch() { return pffftd_simd_arch(); }
74};
75template<> struct Types< std::complex<double> > {
76 typedef double Scalar;
77 typedef std::complex<double> Complex;
hayati ayguenca112412020-04-13 00:19:40 +020078 static int simd_size() { return pffftd_simd_size(); }
79 static const char * simd_arch() { return pffftd_simd_arch(); }
80};
81#endif
hayati ayguen55531192020-04-02 23:11:14 +020082
83// Allocator
84template<typename T> class PFAlloc;
dario mambrocb971842020-03-28 00:22:33 +010085
86namespace {
hayati ayguen55531192020-04-02 23:11:14 +020087 template<typename T> class Setup;
dario mambrocb971842020-03-28 00:22:33 +010088}
89
hayati ayguen55531192020-04-02 23:11:14 +020090#if (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900))
dario mambroa1cfad42020-03-30 05:50:26 +020091
hayati ayguenf913ef82020-04-04 12:31:36 +020092// define AlignedVector<T> utilizing 'using' in C++11
dario mambroa1cfad42020-03-30 05:50:26 +020093template<typename T>
hayati ayguen55531192020-04-02 23:11:14 +020094using AlignedVector = typename std::vector< T, PFAlloc<T> >;
95
96#else
97
hayati ayguenf913ef82020-04-04 12:31:36 +020098// define AlignedVector<T> having to derive std::vector<>
hayati ayguen55531192020-04-02 23:11:14 +020099template <typename T>
100struct AlignedVector : public std::vector< T, PFAlloc<T> > {
101 AlignedVector() : std::vector< T, PFAlloc<T> >() { }
102 AlignedVector(int N) : std::vector< T, PFAlloc<T> >(N) { }
103};
dario mambroa1cfad42020-03-30 05:50:26 +0200104
105#endif
hayati ayguen1c193e92020-03-29 16:49:05 +0200106
hayati ayguenca112412020-04-13 00:19:40 +0200107
dario mambrocb971842020-03-28 00:22:33 +0100108// T can be float, double, std::complex<float> or std::complex<double>
hayati ayguenca112412020-04-13 00:19:40 +0200109// define PFFFT_ENABLE_DOUBLE before include this file for double and std::complex<double>
dario mambrocb971842020-03-28 00:22:33 +0100110template<typename T>
111class Fft
112{
113public:
hayati ayguenf913ef82020-04-04 12:31:36 +0200114
115 // define types value_type, Scalar and Complex
hayati ayguen1c193e92020-03-29 16:49:05 +0200116 typedef T value_type;
hayati ayguen55531192020-04-02 23:11:14 +0200117 typedef typename Types<T>::Scalar Scalar;
118 typedef typename Types<T>::Complex Complex;
dario mambroa1cfad42020-03-30 05:50:26 +0200119
hayati ayguen1c193e92020-03-29 16:49:05 +0200120 // static retrospection functions
hayati ayguenf913ef82020-04-04 12:31:36 +0200121 static bool isComplexTransform() { return sizeof(T) == sizeof(Complex); }
122 static bool isFloatScalar() { return sizeof(Scalar) == sizeof(float); }
hayati ayguen61ec6da2020-03-29 08:48:01 +0200123 static bool isDoubleScalar() { return sizeof(Scalar) == sizeof(double); }
124
hayati ayguenca112412020-04-13 00:19:40 +0200125 // simple helper to get minimum possible fft length
hayati aygueneeb17fc2020-04-13 04:02:07 +0200126 static int minFFtsize() { return pffft_min_fft_size( isComplexTransform() ? PFFFT_COMPLEX : PFFFT_REAL ); }
127
hayati ayguenca112412020-04-13 00:19:40 +0200128 // simple helper to determine next power of 2 - without inexact/rounding floating point operations
hayati aygueneeb17fc2020-04-13 04:02:07 +0200129 static int nextPowerOfTwo(int N) { return pffft_next_power_of_two(N); }
hayati ayguenee17cb02020-08-28 04:47:22 +0200130 static bool isPowerOfTwo(int N) { return pffft_is_power_of_two(N) ? true : false; }
hayati aygueneeb17fc2020-04-13 04:02:07 +0200131
hayati ayguenca112412020-04-13 00:19:40 +0200132 static int simd_size() { return Types<T>::simd_size(); }
133 static const char * simd_arch() { return Types<T>::simd_arch(); }
134
hayati ayguen1c193e92020-03-29 16:49:05 +0200135 //////////////////
136
hayati ayguenf913ef82020-04-04 12:31:36 +0200137 /*
138 * Contructor, with transformation length, preparing transforms.
139 *
140 * For length <= stackThresholdLen, the stack is used for the internal
141 * work memory. for bigger length', the heap is used.
142 *
143 * Using the stack is probably the best strategy for small
144 * FFTs, say for N <= 4096). Threads usually have a small stack, that
145 * there's no sufficient amount of memory, usually leading to a crash!
146 */
hayati ayguen1c193e92020-03-29 16:49:05 +0200147 Fft( int length, int stackThresholdLen = 4096 );
148
149 ~Fft();
150
hayati ayguenf913ef82020-04-04 12:31:36 +0200151 /*
152 * prepare for transformation length 'newLength'.
153 * length is identical to forward()'s input vector's size,
154 * and also equals inverse()'s output vector size.
155 * this function is no simple setter. it pre-calculates twiddle factors.
156 */
hayati ayguen1c193e92020-03-29 16:49:05 +0200157 void prepareLength(int newLength);
158
hayati ayguenf913ef82020-04-04 12:31:36 +0200159 /*
160 * retrieve the transformation length.
161 */
hayati ayguen1c193e92020-03-29 16:49:05 +0200162 int getLength() const { return length; }
163
hayati ayguenf913ef82020-04-04 12:31:36 +0200164 /*
165 * retrieve size of complex spectrum vector,
166 * the output of forward()
167 */
hayati ayguen1c193e92020-03-29 16:49:05 +0200168 int getSpectrumSize() const { return isComplexTransform() ? length : ( length / 2 ); }
169
hayati ayguenf913ef82020-04-04 12:31:36 +0200170 /*
171 * retrieve size of spectrum vector - in internal layout;
172 * the output of forwardToInternalLayout()
173 */
hayati ayguen1c193e92020-03-29 16:49:05 +0200174 int getInternalLayoutSize() const { return isComplexTransform() ? ( 2 * length ) : length; }
175
176
177 ////////////////////////////////////////////
178 ////
179 //// API 1, with std::vector<> based containers,
180 //// which free the allocated memory themselves (RAII).
181 ////
182 //// uses an Allocator for the alignment of SIMD data.
183 ////
184 ////////////////////////////////////////////
185
hayati ayguenf913ef82020-04-04 12:31:36 +0200186 // create suitably preallocated aligned vector for one FFT
hayati ayguencd3dad62020-04-03 00:02:34 +0200187 AlignedVector<T> valueVector() const;
188 AlignedVector<Complex> spectrumVector() const;
189 AlignedVector<Scalar> internalLayoutVector() const;
hayati ayguen1c193e92020-03-29 16:49:05 +0200190
191 ////////////////////////////////////////////
192 // although using Vectors for output ..
193 // they need to have resize() applied before!
194
195 // core API, having the spectrum in canonical order
196
hayati ayguenf913ef82020-04-04 12:31:36 +0200197 /*
198 * Perform the forward Fourier transform.
199 *
200 * Transforms are not scaled: inverse(forward(x)) = N*x.
201 * Typically you will want to scale the backward transform by 1/N.
202 *
203 * The output 'spectrum' is canonically ordered - as expected.
204 *
205 * a) for complex input isComplexTransform() == true,
206 * and transformation length N the output array is complex:
207 * index k in 0 .. N/2 -1 corresponds to frequency k * Samplerate / N
208 * index k in N/2 .. N -1 corresponds to frequency (k -N) * Samplerate / N,
209 * resulting in negative frequencies
210 *
211 * b) for real input isComplexTransform() == false,
212 * and transformation length N the output array is 'mostly' complex:
213 * index k in 1 .. N/2 -1 corresponds to frequency k * Samplerate / N
214 * index k == 0 is a special case:
215 * the real() part contains the result for the DC frequency 0,
216 * the imag() part contains the result for the Nyquist frequency Samplerate/2
217 * both 0-frequency and half frequency components, which are real,
218 * are assembled in the first entry as F(0)+i*F(N/2).
219 *
220 * input and output may alias - if you do nasty type conversion.
221 * return is just the given output parameter 'spectrum'.
222 */
hayati ayguencd3dad62020-04-03 00:02:34 +0200223 AlignedVector<Complex> & forward(const AlignedVector<T> & input, AlignedVector<Complex> & spectrum);
hayati ayguen1c193e92020-03-29 16:49:05 +0200224
hayati ayguenf913ef82020-04-04 12:31:36 +0200225 /*
226 * Perform the inverse Fourier transform, see forward().
227 * return is just the given output parameter 'output'.
228 */
hayati ayguencd3dad62020-04-03 00:02:34 +0200229 AlignedVector<T> & inverse(const AlignedVector<Complex> & spectrum, AlignedVector<T> & output);
hayati ayguen1c193e92020-03-29 16:49:05 +0200230
hayati ayguenf913ef82020-04-04 12:31:36 +0200231
hayati ayguen1c193e92020-03-29 16:49:05 +0200232 // provide additional functions with spectrum in some internal Layout.
233 // these are faster, cause the implementation omits the reordering.
234 // these are useful in special applications, like fast convolution,
235 // where inverse() is following anyway ..
236
hayati ayguenf913ef82020-04-04 12:31:36 +0200237 /*
238 * Perform the forward Fourier transform - similar to forward(), BUT:
239 *
240 * The z-domain data is stored in the most efficient order
241 * for transforming it back, or using it for convolution.
242 * If you need to have its content sorted in the "usual" canonical order,
243 * either use forward(), or call reorderSpectrum() after calling
244 * forwardToInternalLayout(), and before the backward fft
245 *
246 * return is just the given output parameter 'spectrum_internal_layout'.
247 */
hayati ayguen55531192020-04-02 23:11:14 +0200248 AlignedVector<Scalar> & forwardToInternalLayout(
249 const AlignedVector<T> & input,
hayati ayguencd3dad62020-04-03 00:02:34 +0200250 AlignedVector<Scalar> & spectrum_internal_layout );
hayati ayguen1c193e92020-03-29 16:49:05 +0200251
hayati ayguenf913ef82020-04-04 12:31:36 +0200252 /*
253 * Perform the inverse Fourier transform, see forwardToInternalLayout()
254 *
255 * return is just the given output parameter 'output'.
256 */
hayati ayguen55531192020-04-02 23:11:14 +0200257 AlignedVector<T> & inverseFromInternalLayout(
258 const AlignedVector<Scalar> & spectrum_internal_layout,
hayati ayguencd3dad62020-04-03 00:02:34 +0200259 AlignedVector<T> & output );
hayati ayguen1c193e92020-03-29 16:49:05 +0200260
hayati ayguenf913ef82020-04-04 12:31:36 +0200261 /*
262 * Reorder the spectrum from internal layout to have the
263 * frequency components in the correct "canonical" order.
264 * see forward() for a description of the canonical order.
265 *
266 * input and output should not alias.
267 */
hayati ayguen1c193e92020-03-29 16:49:05 +0200268 void reorderSpectrum(
hayati ayguen55531192020-04-02 23:11:14 +0200269 const AlignedVector<Scalar> & input,
hayati ayguencd3dad62020-04-03 00:02:34 +0200270 AlignedVector<Complex> & output );
hayati ayguen1c193e92020-03-29 16:49:05 +0200271
hayati ayguenf913ef82020-04-04 12:31:36 +0200272 /*
273 * Perform a multiplication of the frequency components of
274 * spectrum_internal_a and spectrum_internal_b
275 * into spectrum_internal_ab.
276 * The arrays should have been obtained with forwardToInternalLayout)
277 * and should *not* have been reordered with reorderSpectrum().
278 *
279 * the operation performed is:
280 * spectrum_internal_ab = (spectrum_internal_a * spectrum_internal_b)*scaling
281 *
282 * The spectrum_internal_[a][b], pointers may alias.
283 * return is just the given output parameter 'spectrum_internal_ab'.
284 */
285 AlignedVector<Scalar> & convolve(
hayati ayguen55531192020-04-02 23:11:14 +0200286 const AlignedVector<Scalar> & spectrum_internal_a,
287 const AlignedVector<Scalar> & spectrum_internal_b,
288 AlignedVector<Scalar> & spectrum_internal_ab,
hayati ayguencd3dad62020-04-03 00:02:34 +0200289 const Scalar scaling );
hayati ayguen1c193e92020-03-29 16:49:05 +0200290
hayati ayguenf913ef82020-04-04 12:31:36 +0200291 /*
292 * Perform a multiplication and accumulation of the frequency components
293 * - similar to convolve().
294 *
295 * the operation performed is:
296 * spectrum_internal_ab += (spectrum_internal_a * spectrum_internal_b)*scaling
297 *
298 * The spectrum_internal_[a][b], pointers may alias.
299 * return is just the given output parameter 'spectrum_internal_ab'.
300 */
301 AlignedVector<Scalar> & convolveAccumulate(
hayati ayguen55531192020-04-02 23:11:14 +0200302 const AlignedVector<Scalar> & spectrum_internal_a,
303 const AlignedVector<Scalar> & spectrum_internal_b,
304 AlignedVector<Scalar> & spectrum_internal_ab,
hayati ayguencd3dad62020-04-03 00:02:34 +0200305 const Scalar scaling );
hayati ayguen1c193e92020-03-29 16:49:05 +0200306
307
308 ////////////////////////////////////////////
309 ////
310 //// API 2, dealing with raw pointers,
311 //// which need to be deallocated using alignedFree()
312 ////
313 //// the special allocation is required cause SIMD
314 //// implementations require aligned memory
315 ////
hayati ayguenf913ef82020-04-04 12:31:36 +0200316 //// Method descriptions are equal to the methods above,
317 //// having AlignedVector<T> parameters - instead of raw pointers.
318 //// That is why following methods have no documentation.
319 ////
hayati ayguen1c193e92020-03-29 16:49:05 +0200320 ////////////////////////////////////////////
321
dario mambrocb971842020-03-28 00:22:33 +0100322 static void alignedFree(void* ptr);
323
hayati ayguen63794b22020-03-29 03:14:43 +0200324 static T * alignedAllocType(int length);
dario mambrocb971842020-03-28 00:22:33 +0100325 static Scalar* alignedAllocScalar(int length);
hayati ayguen55531192020-04-02 23:11:14 +0200326 static Complex* alignedAllocComplex(int length);
dario mambrocb971842020-03-28 00:22:33 +0100327
hayati ayguen1c193e92020-03-29 16:49:05 +0200328 // core API, having the spectrum in canonical order
329
hayati ayguen55531192020-04-02 23:11:14 +0200330 Complex* forward(const T* input, Complex* spectrum);
hayati ayguen1c193e92020-03-29 16:49:05 +0200331
hayati ayguen55531192020-04-02 23:11:14 +0200332 T* inverse(const Complex* spectrum, T* output);
hayati ayguen1c193e92020-03-29 16:49:05 +0200333
334
335 // provide additional functions with spectrum in some internal Layout.
336 // these are faster, cause the implementation omits the reordering.
337 // these are useful in special applications, like fast convolution,
338 // where inverse() is following anyway ..
339
340 Scalar* forwardToInternalLayout(const T* input,
341 Scalar* spectrum_internal_layout);
342
343 T* inverseFromInternalLayout(const Scalar* spectrum_internal_layout, T* output);
344
hayati ayguen55531192020-04-02 23:11:14 +0200345 void reorderSpectrum(const Scalar* input, Complex* output );
hayati ayguen1c193e92020-03-29 16:49:05 +0200346
hayati ayguen1c193e92020-03-29 16:49:05 +0200347 Scalar* convolve(const Scalar* spectrum_internal_a,
348 const Scalar* spectrum_internal_b,
349 Scalar* spectrum_internal_ab,
350 const Scalar scaling);
351
hayati ayguenf913ef82020-04-04 12:31:36 +0200352 Scalar* convolveAccumulate(const Scalar* spectrum_internal_a,
353 const Scalar* spectrum_internal_b,
354 Scalar* spectrum_internal_ab,
355 const Scalar scaling);
hayati ayguen1c193e92020-03-29 16:49:05 +0200356
dario mambrocb971842020-03-28 00:22:33 +0100357private:
358 Setup<T> setup;
359 Scalar* work;
360 int length;
361 int stackThresholdLen;
362};
363
dario mambrocb971842020-03-28 00:22:33 +0100364
hayati aygueneeb17fc2020-04-13 04:02:07 +0200365template<typename T>
366inline T* alignedAlloc(int length) {
367 return (T*)pffft_aligned_malloc( length * sizeof(T) );
368}
369
370inline void alignedFree(void *ptr) {
371 pffft_aligned_free(ptr);
372}
373
374
375// simple helper to get minimum possible fft length
376inline int minFFtsize(pffft_transform_t transform) {
377 return pffft_min_fft_size(transform);
378}
379
380// simple helper to determine next power of 2 - without inexact/rounding floating point operations
381inline int nextPowerOfTwo(int N) {
382 return pffft_next_power_of_two(N);
383}
384
385inline bool isPowerOfTwo(int N) {
hayati ayguenee17cb02020-08-28 04:47:22 +0200386 return pffft_is_power_of_two(N) ? true : false;
hayati aygueneeb17fc2020-04-13 04:02:07 +0200387}
388
389
hayati ayguen1c193e92020-03-29 16:49:05 +0200390
391////////////////////////////////////////////////////////////////////
392
dario mambrocb971842020-03-28 00:22:33 +0100393// implementation
394
395namespace {
396
397template<typename T>
hayati ayguen55531192020-04-02 23:11:14 +0200398class Setup
dario mambrocb971842020-03-28 00:22:33 +0100399{};
400
hayati ayguenca112412020-04-13 00:19:40 +0200401#if defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) )
402
dario mambrocb971842020-03-28 00:22:33 +0100403template<>
404class Setup<float>
405{
406 PFFFT_Setup* self;
407
408public:
hayati ayguen1c193e92020-03-29 16:49:05 +0200409 typedef float value_type;
hayati ayguen55531192020-04-02 23:11:14 +0200410 typedef Types< value_type >::Scalar Scalar;
dario mambrocb971842020-03-28 00:22:33 +0100411
412 Setup()
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200413 : self(NULL)
dario mambrocb971842020-03-28 00:22:33 +0100414 {}
415
416 void prepareLength(int length)
417 {
418 if (self) {
419 pffft_destroy_setup(self);
420 }
421 self = pffft_new_setup(length, PFFFT_REAL);
422 }
423
424 ~Setup() { pffft_destroy_setup(self); }
425
426 void transform_ordered(const Scalar* input,
427 Scalar* output,
428 Scalar* work,
429 pffft_direction_t direction)
430 {
431 pffft_transform_ordered(self, input, output, work, direction);
432 }
433
434 void transform(const Scalar* input,
435 Scalar* output,
436 Scalar* work,
437 pffft_direction_t direction)
438 {
439 pffft_transform(self, input, output, work, direction);
440 }
441
442 void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
443 {
444 pffft_zreorder(self, input, output, direction);
445 }
446
447 void convolveAccumulate(const Scalar* dft_a,
448 const Scalar* dft_b,
449 Scalar* dft_ab,
450 const Scalar scaling)
451 {
452 pffft_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
453 }
454
455 void convolve(const Scalar* dft_a,
456 const Scalar* dft_b,
457 Scalar* dft_ab,
458 const Scalar scaling)
459 {
460 pffft_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
461 }
dario mambrocb971842020-03-28 00:22:33 +0100462};
463
464template<>
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200465class Setup< std::complex<float> >
dario mambrocb971842020-03-28 00:22:33 +0100466{
467 PFFFT_Setup* self;
468
469public:
hayati ayguen1c193e92020-03-29 16:49:05 +0200470 typedef std::complex<float> value_type;
hayati ayguen55531192020-04-02 23:11:14 +0200471 typedef Types< value_type >::Scalar Scalar;
dario mambrocb971842020-03-28 00:22:33 +0100472
473 Setup()
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200474 : self(NULL)
dario mambrocb971842020-03-28 00:22:33 +0100475 {}
476
477 ~Setup() { pffft_destroy_setup(self); }
478
479 void prepareLength(int length)
480 {
481 if (self) {
482 pffft_destroy_setup(self);
483 }
484 self = pffft_new_setup(length, PFFFT_COMPLEX);
485 }
486
487 void transform_ordered(const Scalar* input,
488 Scalar* output,
489 Scalar* work,
490 pffft_direction_t direction)
491 {
492 pffft_transform_ordered(self, input, output, work, direction);
493 }
494
495 void transform(const Scalar* input,
496 Scalar* output,
497 Scalar* work,
498 pffft_direction_t direction)
499 {
500 pffft_transform(self, input, output, work, direction);
501 }
502
503 void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
504 {
505 pffft_zreorder(self, input, output, direction);
506 }
507
508 void convolve(const Scalar* dft_a,
509 const Scalar* dft_b,
510 Scalar* dft_ab,
511 const Scalar scaling)
512 {
513 pffft_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
514 }
dario mambrocb971842020-03-28 00:22:33 +0100515};
516
hayati ayguenca112412020-04-13 00:19:40 +0200517#endif /* defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) ) */
518
519
520#if defined(PFFFT_ENABLE_DOUBLE)
521
dario mambrocb971842020-03-28 00:22:33 +0100522template<>
523class Setup<double>
524{
525 PFFFTD_Setup* self;
526
527public:
hayati ayguen1c193e92020-03-29 16:49:05 +0200528 typedef double value_type;
hayati ayguen55531192020-04-02 23:11:14 +0200529 typedef Types< value_type >::Scalar Scalar;
dario mambrocb971842020-03-28 00:22:33 +0100530
531 Setup()
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200532 : self(NULL)
dario mambrocb971842020-03-28 00:22:33 +0100533 {}
534
535 ~Setup() { pffftd_destroy_setup(self); }
536
537 void prepareLength(int length)
538 {
539 if (self) {
540 pffftd_destroy_setup(self);
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200541 self = NULL;
dario mambrocb971842020-03-28 00:22:33 +0100542 }
543 if (length > 0) {
544 self = pffftd_new_setup(length, PFFFT_REAL);
545 }
546 }
547
548 void transform_ordered(const Scalar* input,
549 Scalar* output,
550 Scalar* work,
551 pffft_direction_t direction)
552 {
553 pffftd_transform_ordered(self, input, output, work, direction);
554 }
555
556 void transform(const Scalar* input,
557 Scalar* output,
558 Scalar* work,
559 pffft_direction_t direction)
560 {
561 pffftd_transform(self, input, output, work, direction);
562 }
563
564 void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
565 {
566 pffftd_zreorder(self, input, output, direction);
567 }
568
569 void convolveAccumulate(const Scalar* dft_a,
570 const Scalar* dft_b,
571 Scalar* dft_ab,
572 const Scalar scaling)
573 {
574 pffftd_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
575 }
576
577 void convolve(const Scalar* dft_a,
578 const Scalar* dft_b,
579 Scalar* dft_ab,
580 const Scalar scaling)
581 {
582 pffftd_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
583 }
dario mambrocb971842020-03-28 00:22:33 +0100584};
585
586template<>
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200587class Setup< std::complex<double> >
dario mambrocb971842020-03-28 00:22:33 +0100588{
589 PFFFTD_Setup* self;
590
591public:
hayati ayguen1c193e92020-03-29 16:49:05 +0200592 typedef std::complex<double> value_type;
hayati ayguen55531192020-04-02 23:11:14 +0200593 typedef Types< value_type >::Scalar Scalar;
dario mambrocb971842020-03-28 00:22:33 +0100594
595 Setup()
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200596 : self(NULL)
dario mambrocb971842020-03-28 00:22:33 +0100597 {}
598
599 ~Setup() { pffftd_destroy_setup(self); }
600
601 void prepareLength(int length)
602 {
603 if (self) {
604 pffftd_destroy_setup(self);
605 }
606 self = pffftd_new_setup(length, PFFFT_COMPLEX);
607 }
608
609 void transform_ordered(const Scalar* input,
610 Scalar* output,
611 Scalar* work,
612 pffft_direction_t direction)
613 {
614 pffftd_transform_ordered(self, input, output, work, direction);
615 }
616
617 void transform(const Scalar* input,
618 Scalar* output,
619 Scalar* work,
620 pffft_direction_t direction)
621 {
622 pffftd_transform(self, input, output, work, direction);
623 }
624
625 void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
626 {
627 pffftd_zreorder(self, input, output, direction);
628 }
629
630 void convolveAccumulate(const Scalar* dft_a,
631 const Scalar* dft_b,
632 Scalar* dft_ab,
633 const Scalar scaling)
634 {
635 pffftd_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
636 }
637
638 void convolve(const Scalar* dft_a,
639 const Scalar* dft_b,
640 Scalar* dft_ab,
641 const Scalar scaling)
642 {
643 pffftd_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
644 }
dario mambrocb971842020-03-28 00:22:33 +0100645};
646
hayati ayguenca112412020-04-13 00:19:40 +0200647#endif /* defined(PFFFT_ENABLE_DOUBLE) */
648
hayati ayguencd3dad62020-04-03 00:02:34 +0200649} // end of anonymous namespace for Setup<>
650
dario mambrocb971842020-03-28 00:22:33 +0100651
652template<typename T>
653inline Fft<T>::Fft(int length, int stackThresholdLen)
654 : length(0)
655 , stackThresholdLen(stackThresholdLen)
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200656 , work(NULL)
dario mambrocb971842020-03-28 00:22:33 +0100657{
hayati ayguenf913ef82020-04-04 12:31:36 +0200658#if (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900))
659 static_assert( sizeof(Complex) == 2 * sizeof(Scalar), "pffft requires sizeof(std::complex<>) == 2 * sizeof(Scalar)" );
660#elif defined(__GNUC__)
661 char static_assert_like[(sizeof(Complex) == 2 * sizeof(Scalar)) ? 1 : -1]; // pffft requires sizeof(std::complex<>) == 2 * sizeof(Scalar)
662#endif
dario mambrocb971842020-03-28 00:22:33 +0100663 prepareLength(length);
664}
665
666template<typename T>
667inline Fft<T>::~Fft()
668{
hayati aygueneeb17fc2020-04-13 04:02:07 +0200669 alignedFree(work);
dario mambrocb971842020-03-28 00:22:33 +0100670}
671
672template<typename T>
673inline void
674Fft<T>::prepareLength(int newLength)
675{
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200676 const bool wasOnHeap = ( work != NULL );
dario mambrocb971842020-03-28 00:22:33 +0100677
678 const bool useHeap = newLength > stackThresholdLen;
679
680 if (useHeap == wasOnHeap && newLength == length) {
681 return;
682 }
683
684 length = newLength;
685
686 setup.prepareLength(length);
687
688 if (work) {
hayati aygueneeb17fc2020-04-13 04:02:07 +0200689 alignedFree(work);
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200690 work = NULL;
dario mambrocb971842020-03-28 00:22:33 +0100691 }
692
693 if (useHeap) {
hayati aygueneeb17fc2020-04-13 04:02:07 +0200694 work = reinterpret_cast<Scalar*>( alignedAllocType(length) );
dario mambrocb971842020-03-28 00:22:33 +0100695 }
696}
697
hayati ayguencd3dad62020-04-03 00:02:34 +0200698
699template<typename T>
700inline AlignedVector<T>
701Fft<T>::valueVector() const
702{
703 return AlignedVector<T>(length);
704}
705
706template<typename T>
707inline AlignedVector< typename Fft<T>::Complex >
708Fft<T>::spectrumVector() const
709{
710 return AlignedVector<Complex>( getSpectrumSize() );
711}
712
713template<typename T>
714inline AlignedVector< typename Fft<T>::Scalar >
715Fft<T>::internalLayoutVector() const
716{
717 return AlignedVector<Scalar>( getInternalLayoutSize() );
718}
719
720
721template<typename T>
722inline AlignedVector< typename Fft<T>::Complex > &
723Fft<T>::forward(const AlignedVector<T> & input, AlignedVector<Complex> & spectrum)
724{
725 forward( input.data(), spectrum.data() );
726 return spectrum;
727}
728
729template<typename T>
730inline AlignedVector<T> &
731Fft<T>::inverse(const AlignedVector<Complex> & spectrum, AlignedVector<T> & output)
732{
733 inverse( spectrum.data(), output.data() );
734 return output;
735}
736
737
738template<typename T>
739inline AlignedVector< typename Fft<T>::Scalar > &
740Fft<T>::forwardToInternalLayout(
741 const AlignedVector<T> & input,
742 AlignedVector<Scalar> & spectrum_internal_layout )
743{
744 forwardToInternalLayout( input.data(), spectrum_internal_layout.data() );
745 return spectrum_internal_layout;
746}
747
748template<typename T>
749inline AlignedVector<T> &
750Fft<T>::inverseFromInternalLayout(
751 const AlignedVector<Scalar> & spectrum_internal_layout,
752 AlignedVector<T> & output )
753{
754 inverseFromInternalLayout( spectrum_internal_layout.data(), output.data() );
755 return output;
756}
757
758template<typename T>
759inline void
760Fft<T>::reorderSpectrum(
761 const AlignedVector<Scalar> & input,
762 AlignedVector<Complex> & output )
763{
764 reorderSpectrum( input.data(), output.data() );
765}
766
767template<typename T>
768inline AlignedVector< typename Fft<T>::Scalar > &
769Fft<T>::convolveAccumulate(
770 const AlignedVector<Scalar> & spectrum_internal_a,
771 const AlignedVector<Scalar> & spectrum_internal_b,
772 AlignedVector<Scalar> & spectrum_internal_ab,
773 const Scalar scaling )
774{
775 convolveAccumulate( spectrum_internal_a.data(), spectrum_internal_b.data(),
776 spectrum_internal_ab.data(), scaling );
777 return spectrum_internal_ab;
778}
779
780template<typename T>
781inline AlignedVector< typename Fft<T>::Scalar > &
782Fft<T>::convolve(
783 const AlignedVector<Scalar> & spectrum_internal_a,
784 const AlignedVector<Scalar> & spectrum_internal_b,
785 AlignedVector<Scalar> & spectrum_internal_ab,
786 const Scalar scaling )
787{
788 convolve( spectrum_internal_a.data(), spectrum_internal_b.data(),
789 spectrum_internal_ab.data(), scaling );
790 return spectrum_internal_ab;
791}
792
793
dario mambrocb971842020-03-28 00:22:33 +0100794template<typename T>
hayati ayguen55531192020-04-02 23:11:14 +0200795inline typename Fft<T>::Complex *
796Fft<T>::forward(const T* input, Complex * spectrum)
dario mambrocb971842020-03-28 00:22:33 +0100797{
798 setup.transform_ordered(reinterpret_cast<const Scalar*>(input),
799 reinterpret_cast<Scalar*>(spectrum),
800 work,
801 PFFFT_FORWARD);
802 return spectrum;
803}
804
805template<typename T>
806inline T*
hayati ayguen55531192020-04-02 23:11:14 +0200807Fft<T>::inverse(Complex const* spectrum, T* output)
dario mambrocb971842020-03-28 00:22:33 +0100808{
809 setup.transform_ordered(reinterpret_cast<const Scalar*>(spectrum),
810 reinterpret_cast<Scalar*>(output),
811 work,
812 PFFFT_BACKWARD);
813 return output;
814}
815
816template<typename T>
817inline typename pffft::Fft<T>::Scalar*
hayati ayguen1c193e92020-03-29 16:49:05 +0200818Fft<T>::forwardToInternalLayout(const T* input, Scalar* spectrum_internal_layout)
dario mambrocb971842020-03-28 00:22:33 +0100819{
820 setup.transform(reinterpret_cast<const Scalar*>(input),
821 spectrum_internal_layout,
822 work,
823 PFFFT_FORWARD);
824 return spectrum_internal_layout;
825}
826
827template<typename T>
828inline T*
hayati ayguen1c193e92020-03-29 16:49:05 +0200829Fft<T>::inverseFromInternalLayout(const Scalar* spectrum_internal_layout, T* output)
dario mambrocb971842020-03-28 00:22:33 +0100830{
831 setup.transform(spectrum_internal_layout,
832 reinterpret_cast<Scalar*>(output),
833 work,
834 PFFFT_BACKWARD);
835 return output;
836}
837
838template<typename T>
839inline void
hayati ayguen55531192020-04-02 23:11:14 +0200840Fft<T>::reorderSpectrum( const Scalar* input, Complex* output )
dario mambrocb971842020-03-28 00:22:33 +0100841{
hayati ayguen1c193e92020-03-29 16:49:05 +0200842 setup.reorder(input, reinterpret_cast<Scalar*>(output), PFFFT_FORWARD);
dario mambrocb971842020-03-28 00:22:33 +0100843}
844
845template<typename T>
846inline typename pffft::Fft<T>::Scalar*
847Fft<T>::convolveAccumulate(const Scalar* dft_a,
848 const Scalar* dft_b,
849 Scalar* dft_ab,
850 const Scalar scaling)
851{
852 setup.convolveAccumulate(dft_a, dft_b, dft_ab, scaling);
853 return dft_ab;
854}
855
856template<typename T>
857inline typename pffft::Fft<T>::Scalar*
858Fft<T>::convolve(const Scalar* dft_a,
859 const Scalar* dft_b,
860 Scalar* dft_ab,
861 const Scalar scaling)
862{
863 setup.convolve(dft_a, dft_b, dft_ab, scaling);
864 return dft_ab;
865}
866
867template<typename T>
868inline void
869Fft<T>::alignedFree(void* ptr)
870{
hayati aygueneeb17fc2020-04-13 04:02:07 +0200871 pffft::alignedFree(ptr);
dario mambrocb971842020-03-28 00:22:33 +0100872}
873
hayati ayguen63794b22020-03-29 03:14:43 +0200874
875template<typename T>
876inline T*
877pffft::Fft<T>::alignedAllocType(int length)
878{
hayati aygueneeb17fc2020-04-13 04:02:07 +0200879 return alignedAlloc<T>(length);
hayati ayguen63794b22020-03-29 03:14:43 +0200880}
881
dario mambrocb971842020-03-28 00:22:33 +0100882template<typename T>
883inline typename pffft::Fft<T>::Scalar*
884pffft::Fft<T>::alignedAllocScalar(int length)
885{
hayati aygueneeb17fc2020-04-13 04:02:07 +0200886 return alignedAlloc<Scalar>(length);
dario mambrocb971842020-03-28 00:22:33 +0100887}
888
889template<typename T>
hayati ayguen55531192020-04-02 23:11:14 +0200890inline typename Fft<T>::Complex *
dario mambrocb971842020-03-28 00:22:33 +0100891Fft<T>::alignedAllocComplex(int length)
892{
hayati aygueneeb17fc2020-04-13 04:02:07 +0200893 return alignedAlloc<Complex>(length);
dario mambrocb971842020-03-28 00:22:33 +0100894}
895
hayati ayguen1c193e92020-03-29 16:49:05 +0200896
897
898////////////////////////////////////////////////////////////////////
899
900// Allocator - for std::vector<>:
901// origin: http://www.josuttis.com/cppcode/allocator.html
902// http://www.josuttis.com/cppcode/myalloc.hpp
903//
904// minor renaming and utilizing of pffft (de)allocation functions
905// are applied to Jossutis' allocator
906
907/* The following code example is taken from the book
908 * "The C++ Standard Library - A Tutorial and Reference"
909 * by Nicolai M. Josuttis, Addison-Wesley, 1999
910 *
911 * (C) Copyright Nicolai M. Josuttis 1999.
912 * Permission to copy, use, modify, sell and distribute this software
913 * is granted provided this copyright notice appears in all copies.
914 * This software is provided "as is" without express or implied
915 * warranty, and with no claim as to its suitability for any purpose.
916 */
917
918template <class T>
919class PFAlloc {
920 public:
921 // type definitions
922 typedef T value_type;
923 typedef T* pointer;
924 typedef const T* const_pointer;
925 typedef T& reference;
926 typedef const T& const_reference;
927 typedef std::size_t size_type;
928 typedef std::ptrdiff_t difference_type;
929
930 // rebind allocator to type U
931 template <class U>
932 struct rebind {
933 typedef PFAlloc<U> other;
934 };
935
936 // return address of values
937 pointer address (reference value) const {
938 return &value;
939 }
940 const_pointer address (const_reference value) const {
941 return &value;
942 }
943
944 /* constructors and destructor
945 * - nothing to do because the allocator has no state
946 */
947 PFAlloc() throw() {
948 }
949 PFAlloc(const PFAlloc&) throw() {
950 }
951 template <class U>
952 PFAlloc (const PFAlloc<U>&) throw() {
953 }
954 ~PFAlloc() throw() {
955 }
956
957 // return maximum number of elements that can be allocated
958 size_type max_size () const throw() {
959 return std::numeric_limits<std::size_t>::max() / sizeof(T);
960 }
961
962 // allocate but don't initialize num elements of type T
963 pointer allocate (size_type num, const void* = 0) {
hayati aygueneeb17fc2020-04-13 04:02:07 +0200964 pointer ret = (pointer)( alignedAlloc<T>(num) );
hayati ayguen1c193e92020-03-29 16:49:05 +0200965 return ret;
966 }
967
968 // initialize elements of allocated storage p with value value
969 void construct (pointer p, const T& value) {
970 // initialize memory with placement new
971 new((void*)p)T(value);
972 }
973
974 // destroy elements of initialized storage p
975 void destroy (pointer p) {
976 // destroy objects by calling their destructor
977 p->~T();
978 }
979
980 // deallocate storage p of deleted elements
981 void deallocate (pointer p, size_type num) {
982 // deallocate memory with pffft
hayati aygueneeb17fc2020-04-13 04:02:07 +0200983 alignedFree( (void*)p );
hayati ayguen1c193e92020-03-29 16:49:05 +0200984 }
985};
986
987// return that all specializations of this allocator are interchangeable
988template <class T1, class T2>
989bool operator== (const PFAlloc<T1>&,
990 const PFAlloc<T2>&) throw() {
991 return true;
992}
993template <class T1, class T2>
994bool operator!= (const PFAlloc<T1>&,
995 const PFAlloc<T2>&) throw() {
996 return false;
997}
998
999
dario mambrocb971842020-03-28 00:22:33 +01001000} // namespace pffft
hayati ayguen63794b22020-03-29 03:14:43 +02001001