blob: d2ff1cf4262bedc76bf2c0ad3fdda892e3e4b57e [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 {
38#include "pffft.h"
39#include "pffft_double.h"
40}
41
42namespace pffft {
43
44// enum { PFFFT_REAL, PFFFT_COMPLEX }
45typedef pffft_transform_t TransformType;
46
hayati ayguenf913ef82020-04-04 12:31:36 +020047// define 'Scalar' and 'Complex' (in namespace pffft)
hayati ayguen55531192020-04-02 23:11:14 +020048template<typename T> struct Types {};
49template<> struct Types<float> { typedef float Scalar; typedef std::complex<Scalar> Complex; };
50template<> struct Types<double> { typedef double Scalar; typedef std::complex<Scalar> Complex; };
51template<> struct Types< std::complex<float> > { typedef float Scalar; typedef std::complex<float> Complex; };
52template<> struct Types< std::complex<double> > { typedef double Scalar; typedef std::complex<double> Complex; };
53
54// Allocator
55template<typename T> class PFAlloc;
dario mambrocb971842020-03-28 00:22:33 +010056
57namespace {
hayati ayguen55531192020-04-02 23:11:14 +020058 template<typename T> class Setup;
dario mambrocb971842020-03-28 00:22:33 +010059}
60
hayati ayguen55531192020-04-02 23:11:14 +020061#if (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900))
dario mambroa1cfad42020-03-30 05:50:26 +020062
hayati ayguenf913ef82020-04-04 12:31:36 +020063// define AlignedVector<T> utilizing 'using' in C++11
dario mambroa1cfad42020-03-30 05:50:26 +020064template<typename T>
hayati ayguen55531192020-04-02 23:11:14 +020065using AlignedVector = typename std::vector< T, PFAlloc<T> >;
66
67#else
68
hayati ayguenf913ef82020-04-04 12:31:36 +020069// define AlignedVector<T> having to derive std::vector<>
hayati ayguen55531192020-04-02 23:11:14 +020070template <typename T>
71struct AlignedVector : public std::vector< T, PFAlloc<T> > {
72 AlignedVector() : std::vector< T, PFAlloc<T> >() { }
73 AlignedVector(int N) : std::vector< T, PFAlloc<T> >(N) { }
74};
dario mambroa1cfad42020-03-30 05:50:26 +020075
76#endif
hayati ayguen1c193e92020-03-29 16:49:05 +020077
dario mambrocb971842020-03-28 00:22:33 +010078// T can be float, double, std::complex<float> or std::complex<double>
79template<typename T>
80class Fft
81{
82public:
hayati ayguenf913ef82020-04-04 12:31:36 +020083
84 // define types value_type, Scalar and Complex
hayati ayguen1c193e92020-03-29 16:49:05 +020085 typedef T value_type;
hayati ayguen55531192020-04-02 23:11:14 +020086 typedef typename Types<T>::Scalar Scalar;
87 typedef typename Types<T>::Complex Complex;
dario mambroa1cfad42020-03-30 05:50:26 +020088
hayati ayguen1c193e92020-03-29 16:49:05 +020089 // static retrospection functions
hayati ayguenf913ef82020-04-04 12:31:36 +020090 static bool isComplexTransform() { return sizeof(T) == sizeof(Complex); }
91 static bool isFloatScalar() { return sizeof(Scalar) == sizeof(float); }
hayati ayguen61ec6da2020-03-29 08:48:01 +020092 static bool isDoubleScalar() { return sizeof(Scalar) == sizeof(double); }
93
hayati ayguen1c193e92020-03-29 16:49:05 +020094 //////////////////
95
hayati ayguenf913ef82020-04-04 12:31:36 +020096 /*
97 * Contructor, with transformation length, preparing transforms.
98 *
99 * For length <= stackThresholdLen, the stack is used for the internal
100 * work memory. for bigger length', the heap is used.
101 *
102 * Using the stack is probably the best strategy for small
103 * FFTs, say for N <= 4096). Threads usually have a small stack, that
104 * there's no sufficient amount of memory, usually leading to a crash!
105 */
hayati ayguen1c193e92020-03-29 16:49:05 +0200106 Fft( int length, int stackThresholdLen = 4096 );
107
108 ~Fft();
109
hayati ayguenf913ef82020-04-04 12:31:36 +0200110 /*
111 * prepare for transformation length 'newLength'.
112 * length is identical to forward()'s input vector's size,
113 * and also equals inverse()'s output vector size.
114 * this function is no simple setter. it pre-calculates twiddle factors.
115 */
hayati ayguen1c193e92020-03-29 16:49:05 +0200116 void prepareLength(int newLength);
117
hayati ayguenf913ef82020-04-04 12:31:36 +0200118 /*
119 * retrieve the transformation length.
120 */
hayati ayguen1c193e92020-03-29 16:49:05 +0200121 int getLength() const { return length; }
122
hayati ayguenf913ef82020-04-04 12:31:36 +0200123 /*
124 * retrieve size of complex spectrum vector,
125 * the output of forward()
126 */
hayati ayguen1c193e92020-03-29 16:49:05 +0200127 int getSpectrumSize() const { return isComplexTransform() ? length : ( length / 2 ); }
128
hayati ayguenf913ef82020-04-04 12:31:36 +0200129 /*
130 * retrieve size of spectrum vector - in internal layout;
131 * the output of forwardToInternalLayout()
132 */
hayati ayguen1c193e92020-03-29 16:49:05 +0200133 int getInternalLayoutSize() const { return isComplexTransform() ? ( 2 * length ) : length; }
134
135
136 ////////////////////////////////////////////
137 ////
138 //// API 1, with std::vector<> based containers,
139 //// which free the allocated memory themselves (RAII).
140 ////
141 //// uses an Allocator for the alignment of SIMD data.
142 ////
143 ////////////////////////////////////////////
144
hayati ayguenf913ef82020-04-04 12:31:36 +0200145 // create suitably preallocated aligned vector for one FFT
hayati ayguencd3dad62020-04-03 00:02:34 +0200146 AlignedVector<T> valueVector() const;
147 AlignedVector<Complex> spectrumVector() const;
148 AlignedVector<Scalar> internalLayoutVector() const;
hayati ayguen1c193e92020-03-29 16:49:05 +0200149
150 ////////////////////////////////////////////
151 // although using Vectors for output ..
152 // they need to have resize() applied before!
153
154 // core API, having the spectrum in canonical order
155
hayati ayguenf913ef82020-04-04 12:31:36 +0200156 /*
157 * Perform the forward Fourier transform.
158 *
159 * Transforms are not scaled: inverse(forward(x)) = N*x.
160 * Typically you will want to scale the backward transform by 1/N.
161 *
162 * The output 'spectrum' is canonically ordered - as expected.
163 *
164 * a) for complex input isComplexTransform() == true,
165 * and transformation length N the output array is complex:
166 * index k in 0 .. N/2 -1 corresponds to frequency k * Samplerate / N
167 * index k in N/2 .. N -1 corresponds to frequency (k -N) * Samplerate / N,
168 * resulting in negative frequencies
169 *
170 * b) for real input isComplexTransform() == false,
171 * and transformation length N the output array is 'mostly' complex:
172 * index k in 1 .. N/2 -1 corresponds to frequency k * Samplerate / N
173 * index k == 0 is a special case:
174 * the real() part contains the result for the DC frequency 0,
175 * the imag() part contains the result for the Nyquist frequency Samplerate/2
176 * both 0-frequency and half frequency components, which are real,
177 * are assembled in the first entry as F(0)+i*F(N/2).
178 *
179 * input and output may alias - if you do nasty type conversion.
180 * return is just the given output parameter 'spectrum'.
181 */
hayati ayguencd3dad62020-04-03 00:02:34 +0200182 AlignedVector<Complex> & forward(const AlignedVector<T> & input, AlignedVector<Complex> & spectrum);
hayati ayguen1c193e92020-03-29 16:49:05 +0200183
hayati ayguenf913ef82020-04-04 12:31:36 +0200184 /*
185 * Perform the inverse Fourier transform, see forward().
186 * return is just the given output parameter 'output'.
187 */
hayati ayguencd3dad62020-04-03 00:02:34 +0200188 AlignedVector<T> & inverse(const AlignedVector<Complex> & spectrum, AlignedVector<T> & output);
hayati ayguen1c193e92020-03-29 16:49:05 +0200189
hayati ayguenf913ef82020-04-04 12:31:36 +0200190
hayati ayguen1c193e92020-03-29 16:49:05 +0200191 // provide additional functions with spectrum in some internal Layout.
192 // these are faster, cause the implementation omits the reordering.
193 // these are useful in special applications, like fast convolution,
194 // where inverse() is following anyway ..
195
hayati ayguenf913ef82020-04-04 12:31:36 +0200196 /*
197 * Perform the forward Fourier transform - similar to forward(), BUT:
198 *
199 * The z-domain data is stored in the most efficient order
200 * for transforming it back, or using it for convolution.
201 * If you need to have its content sorted in the "usual" canonical order,
202 * either use forward(), or call reorderSpectrum() after calling
203 * forwardToInternalLayout(), and before the backward fft
204 *
205 * return is just the given output parameter 'spectrum_internal_layout'.
206 */
hayati ayguen55531192020-04-02 23:11:14 +0200207 AlignedVector<Scalar> & forwardToInternalLayout(
208 const AlignedVector<T> & input,
hayati ayguencd3dad62020-04-03 00:02:34 +0200209 AlignedVector<Scalar> & spectrum_internal_layout );
hayati ayguen1c193e92020-03-29 16:49:05 +0200210
hayati ayguenf913ef82020-04-04 12:31:36 +0200211 /*
212 * Perform the inverse Fourier transform, see forwardToInternalLayout()
213 *
214 * return is just the given output parameter 'output'.
215 */
hayati ayguen55531192020-04-02 23:11:14 +0200216 AlignedVector<T> & inverseFromInternalLayout(
217 const AlignedVector<Scalar> & spectrum_internal_layout,
hayati ayguencd3dad62020-04-03 00:02:34 +0200218 AlignedVector<T> & output );
hayati ayguen1c193e92020-03-29 16:49:05 +0200219
hayati ayguenf913ef82020-04-04 12:31:36 +0200220 /*
221 * Reorder the spectrum from internal layout to have the
222 * frequency components in the correct "canonical" order.
223 * see forward() for a description of the canonical order.
224 *
225 * input and output should not alias.
226 */
hayati ayguen1c193e92020-03-29 16:49:05 +0200227 void reorderSpectrum(
hayati ayguen55531192020-04-02 23:11:14 +0200228 const AlignedVector<Scalar> & input,
hayati ayguencd3dad62020-04-03 00:02:34 +0200229 AlignedVector<Complex> & output );
hayati ayguen1c193e92020-03-29 16:49:05 +0200230
hayati ayguenf913ef82020-04-04 12:31:36 +0200231 /*
232 * Perform a multiplication of the frequency components of
233 * spectrum_internal_a and spectrum_internal_b
234 * into spectrum_internal_ab.
235 * The arrays should have been obtained with forwardToInternalLayout)
236 * and should *not* have been reordered with reorderSpectrum().
237 *
238 * the operation performed is:
239 * spectrum_internal_ab = (spectrum_internal_a * spectrum_internal_b)*scaling
240 *
241 * The spectrum_internal_[a][b], pointers may alias.
242 * return is just the given output parameter 'spectrum_internal_ab'.
243 */
244 AlignedVector<Scalar> & convolve(
hayati ayguen55531192020-04-02 23:11:14 +0200245 const AlignedVector<Scalar> & spectrum_internal_a,
246 const AlignedVector<Scalar> & spectrum_internal_b,
247 AlignedVector<Scalar> & spectrum_internal_ab,
hayati ayguencd3dad62020-04-03 00:02:34 +0200248 const Scalar scaling );
hayati ayguen1c193e92020-03-29 16:49:05 +0200249
hayati ayguenf913ef82020-04-04 12:31:36 +0200250 /*
251 * Perform a multiplication and accumulation of the frequency components
252 * - similar to convolve().
253 *
254 * the operation performed is:
255 * spectrum_internal_ab += (spectrum_internal_a * spectrum_internal_b)*scaling
256 *
257 * The spectrum_internal_[a][b], pointers may alias.
258 * return is just the given output parameter 'spectrum_internal_ab'.
259 */
260 AlignedVector<Scalar> & convolveAccumulate(
hayati ayguen55531192020-04-02 23:11:14 +0200261 const AlignedVector<Scalar> & spectrum_internal_a,
262 const AlignedVector<Scalar> & spectrum_internal_b,
263 AlignedVector<Scalar> & spectrum_internal_ab,
hayati ayguencd3dad62020-04-03 00:02:34 +0200264 const Scalar scaling );
hayati ayguen1c193e92020-03-29 16:49:05 +0200265
266
267 ////////////////////////////////////////////
268 ////
269 //// API 2, dealing with raw pointers,
270 //// which need to be deallocated using alignedFree()
271 ////
272 //// the special allocation is required cause SIMD
273 //// implementations require aligned memory
274 ////
hayati ayguenf913ef82020-04-04 12:31:36 +0200275 //// Method descriptions are equal to the methods above,
276 //// having AlignedVector<T> parameters - instead of raw pointers.
277 //// That is why following methods have no documentation.
278 ////
hayati ayguen1c193e92020-03-29 16:49:05 +0200279 ////////////////////////////////////////////
280
dario mambrocb971842020-03-28 00:22:33 +0100281 static void alignedFree(void* ptr);
282
hayati ayguen63794b22020-03-29 03:14:43 +0200283 static T * alignedAllocType(int length);
dario mambrocb971842020-03-28 00:22:33 +0100284 static Scalar* alignedAllocScalar(int length);
hayati ayguen55531192020-04-02 23:11:14 +0200285 static Complex* alignedAllocComplex(int length);
dario mambrocb971842020-03-28 00:22:33 +0100286
hayati ayguen1c193e92020-03-29 16:49:05 +0200287 // core API, having the spectrum in canonical order
288
hayati ayguen55531192020-04-02 23:11:14 +0200289 Complex* forward(const T* input, Complex* spectrum);
hayati ayguen1c193e92020-03-29 16:49:05 +0200290
hayati ayguen55531192020-04-02 23:11:14 +0200291 T* inverse(const Complex* spectrum, T* output);
hayati ayguen1c193e92020-03-29 16:49:05 +0200292
293
294 // provide additional functions with spectrum in some internal Layout.
295 // these are faster, cause the implementation omits the reordering.
296 // these are useful in special applications, like fast convolution,
297 // where inverse() is following anyway ..
298
299 Scalar* forwardToInternalLayout(const T* input,
300 Scalar* spectrum_internal_layout);
301
302 T* inverseFromInternalLayout(const Scalar* spectrum_internal_layout, T* output);
303
hayati ayguen55531192020-04-02 23:11:14 +0200304 void reorderSpectrum(const Scalar* input, Complex* output );
hayati ayguen1c193e92020-03-29 16:49:05 +0200305
hayati ayguen1c193e92020-03-29 16:49:05 +0200306 Scalar* convolve(const Scalar* spectrum_internal_a,
307 const Scalar* spectrum_internal_b,
308 Scalar* spectrum_internal_ab,
309 const Scalar scaling);
310
hayati ayguenf913ef82020-04-04 12:31:36 +0200311 Scalar* convolveAccumulate(const Scalar* spectrum_internal_a,
312 const Scalar* spectrum_internal_b,
313 Scalar* spectrum_internal_ab,
314 const Scalar scaling);
hayati ayguen1c193e92020-03-29 16:49:05 +0200315
dario mambrocb971842020-03-28 00:22:33 +0100316private:
317 Setup<T> setup;
318 Scalar* work;
319 int length;
320 int stackThresholdLen;
321};
322
dario mambrocb971842020-03-28 00:22:33 +0100323
hayati ayguen1c193e92020-03-29 16:49:05 +0200324////////////////////////////////////////////
325////
326//// PUBLIC HELPER FUNCTIONS
327////
328////////////////////////////////////////////
dario mambrocb971842020-03-28 00:22:33 +0100329
330/* simple helper to get minimum possible fft length */
331int
332minFFtsize(const TransformType transform)
333{
334 return pffft_min_fft_size(transform);
335}
336
337/* simple helper to determine next power of 2
338 - without inexact/rounding floating point operations
339*/
340int
341nextPowerOfTwo(const int N)
342{
343 return pffft_next_power_of_two(N);
344}
345
346/* simple helper to determine if power of 2 - returns bool */
347bool
348isPowerOfTwo(const int N)
349{
350 return pffft_is_power_of_two(N);
351}
352
hayati ayguen1c193e92020-03-29 16:49:05 +0200353void
354alignedFree(void* ptr)
355{
356 pffft_aligned_free(ptr);
357}
358
359
360////////////////////////////////////////////////////////////////////
361
dario mambrocb971842020-03-28 00:22:33 +0100362// implementation
363
364namespace {
365
366template<typename T>
hayati ayguen55531192020-04-02 23:11:14 +0200367class Setup
dario mambrocb971842020-03-28 00:22:33 +0100368{};
369
370template<>
371class Setup<float>
372{
373 PFFFT_Setup* self;
374
375public:
hayati ayguen1c193e92020-03-29 16:49:05 +0200376 typedef float value_type;
hayati ayguen55531192020-04-02 23:11:14 +0200377 typedef Types< value_type >::Scalar Scalar;
dario mambrocb971842020-03-28 00:22:33 +0100378
379 Setup()
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200380 : self(NULL)
dario mambrocb971842020-03-28 00:22:33 +0100381 {}
382
383 void prepareLength(int length)
384 {
385 if (self) {
386 pffft_destroy_setup(self);
387 }
388 self = pffft_new_setup(length, PFFFT_REAL);
389 }
390
391 ~Setup() { pffft_destroy_setup(self); }
392
393 void transform_ordered(const Scalar* input,
394 Scalar* output,
395 Scalar* work,
396 pffft_direction_t direction)
397 {
398 pffft_transform_ordered(self, input, output, work, direction);
399 }
400
401 void transform(const Scalar* input,
402 Scalar* output,
403 Scalar* work,
404 pffft_direction_t direction)
405 {
406 pffft_transform(self, input, output, work, direction);
407 }
408
409 void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
410 {
411 pffft_zreorder(self, input, output, direction);
412 }
413
414 void convolveAccumulate(const Scalar* dft_a,
415 const Scalar* dft_b,
416 Scalar* dft_ab,
417 const Scalar scaling)
418 {
419 pffft_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
420 }
421
422 void convolve(const Scalar* dft_a,
423 const Scalar* dft_b,
424 Scalar* dft_ab,
425 const Scalar scaling)
426 {
427 pffft_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
428 }
429
hayati ayguen1c193e92020-03-29 16:49:05 +0200430 static value_type* allocateType(int length)
dario mambrocb971842020-03-28 00:22:33 +0100431 {
hayati ayguen1c193e92020-03-29 16:49:05 +0200432 const int bytes = sizeof(value_type) * length;
433 return static_cast<value_type*>(pffft_aligned_malloc(bytes));
hayati ayguen63794b22020-03-29 03:14:43 +0200434 }
435
436 static Scalar* allocate(int length)
437 {
438 const int bytes = sizeof(Scalar) * length;
439 return static_cast<Scalar*>(pffft_aligned_malloc(bytes));
dario mambrocb971842020-03-28 00:22:33 +0100440 }
441};
442
443template<>
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200444class Setup< std::complex<float> >
dario mambrocb971842020-03-28 00:22:33 +0100445{
446 PFFFT_Setup* self;
447
448public:
hayati ayguen1c193e92020-03-29 16:49:05 +0200449 typedef std::complex<float> value_type;
hayati ayguen55531192020-04-02 23:11:14 +0200450 typedef Types< value_type >::Scalar Scalar;
dario mambrocb971842020-03-28 00:22:33 +0100451
452 Setup()
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200453 : self(NULL)
dario mambrocb971842020-03-28 00:22:33 +0100454 {}
455
456 ~Setup() { pffft_destroy_setup(self); }
457
458 void prepareLength(int length)
459 {
460 if (self) {
461 pffft_destroy_setup(self);
462 }
463 self = pffft_new_setup(length, PFFFT_COMPLEX);
464 }
465
466 void transform_ordered(const Scalar* input,
467 Scalar* output,
468 Scalar* work,
469 pffft_direction_t direction)
470 {
471 pffft_transform_ordered(self, input, output, work, direction);
472 }
473
474 void transform(const Scalar* input,
475 Scalar* output,
476 Scalar* work,
477 pffft_direction_t direction)
478 {
479 pffft_transform(self, input, output, work, direction);
480 }
481
482 void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
483 {
484 pffft_zreorder(self, input, output, direction);
485 }
486
487 void convolve(const Scalar* dft_a,
488 const Scalar* dft_b,
489 Scalar* dft_ab,
490 const Scalar scaling)
491 {
492 pffft_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
493 }
494
hayati ayguen1c193e92020-03-29 16:49:05 +0200495 static value_type* allocateType(int length)
dario mambrocb971842020-03-28 00:22:33 +0100496 {
hayati ayguen1c193e92020-03-29 16:49:05 +0200497 const int bytes = sizeof(value_type) * length;
498 return static_cast<value_type*>(pffft_aligned_malloc(bytes));
hayati ayguen63794b22020-03-29 03:14:43 +0200499 }
500
501 static Scalar* allocate(const int length)
502 {
503 const int bytes = sizeof(Scalar) * length;
504 return static_cast<Scalar*>(pffft_aligned_malloc(bytes));
dario mambrocb971842020-03-28 00:22:33 +0100505 }
506};
507
508template<>
509class Setup<double>
510{
511 PFFFTD_Setup* self;
512
513public:
hayati ayguen1c193e92020-03-29 16:49:05 +0200514 typedef double value_type;
hayati ayguen55531192020-04-02 23:11:14 +0200515 typedef Types< value_type >::Scalar Scalar;
dario mambrocb971842020-03-28 00:22:33 +0100516
517 Setup()
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200518 : self(NULL)
dario mambrocb971842020-03-28 00:22:33 +0100519 {}
520
521 ~Setup() { pffftd_destroy_setup(self); }
522
523 void prepareLength(int length)
524 {
525 if (self) {
526 pffftd_destroy_setup(self);
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200527 self = NULL;
dario mambrocb971842020-03-28 00:22:33 +0100528 }
529 if (length > 0) {
530 self = pffftd_new_setup(length, PFFFT_REAL);
531 }
532 }
533
534 void transform_ordered(const Scalar* input,
535 Scalar* output,
536 Scalar* work,
537 pffft_direction_t direction)
538 {
539 pffftd_transform_ordered(self, input, output, work, direction);
540 }
541
542 void transform(const Scalar* input,
543 Scalar* output,
544 Scalar* work,
545 pffft_direction_t direction)
546 {
547 pffftd_transform(self, input, output, work, direction);
548 }
549
550 void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
551 {
552 pffftd_zreorder(self, input, output, direction);
553 }
554
555 void convolveAccumulate(const Scalar* dft_a,
556 const Scalar* dft_b,
557 Scalar* dft_ab,
558 const Scalar scaling)
559 {
560 pffftd_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
561 }
562
563 void convolve(const Scalar* dft_a,
564 const Scalar* dft_b,
565 Scalar* dft_ab,
566 const Scalar scaling)
567 {
568 pffftd_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
569 }
570
hayati ayguen1c193e92020-03-29 16:49:05 +0200571 static value_type* allocateType(int length)
dario mambrocb971842020-03-28 00:22:33 +0100572 {
hayati ayguen1c193e92020-03-29 16:49:05 +0200573 const int bytes = sizeof(value_type) * length;
574 return static_cast<value_type*>(pffft_aligned_malloc(bytes));
hayati ayguen63794b22020-03-29 03:14:43 +0200575 }
576
577 static Scalar* allocate(int length)
578 {
579 const int bytes = sizeof(Scalar) * length;
580 return static_cast<Scalar*>(pffftd_aligned_malloc(bytes));
dario mambrocb971842020-03-28 00:22:33 +0100581 }
582};
583
584template<>
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200585class Setup< std::complex<double> >
dario mambrocb971842020-03-28 00:22:33 +0100586{
587 PFFFTD_Setup* self;
588
589public:
hayati ayguen1c193e92020-03-29 16:49:05 +0200590 typedef std::complex<double> value_type;
hayati ayguen55531192020-04-02 23:11:14 +0200591 typedef Types< value_type >::Scalar Scalar;
dario mambrocb971842020-03-28 00:22:33 +0100592
593 Setup()
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200594 : self(NULL)
dario mambrocb971842020-03-28 00:22:33 +0100595 {}
596
597 ~Setup() { pffftd_destroy_setup(self); }
598
599 void prepareLength(int length)
600 {
601 if (self) {
602 pffftd_destroy_setup(self);
603 }
604 self = pffftd_new_setup(length, PFFFT_COMPLEX);
605 }
606
607 void transform_ordered(const Scalar* input,
608 Scalar* output,
609 Scalar* work,
610 pffft_direction_t direction)
611 {
612 pffftd_transform_ordered(self, input, output, work, direction);
613 }
614
615 void transform(const Scalar* input,
616 Scalar* output,
617 Scalar* work,
618 pffft_direction_t direction)
619 {
620 pffftd_transform(self, input, output, work, direction);
621 }
622
623 void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
624 {
625 pffftd_zreorder(self, input, output, direction);
626 }
627
628 void convolveAccumulate(const Scalar* dft_a,
629 const Scalar* dft_b,
630 Scalar* dft_ab,
631 const Scalar scaling)
632 {
633 pffftd_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
634 }
635
636 void convolve(const Scalar* dft_a,
637 const Scalar* dft_b,
638 Scalar* dft_ab,
639 const Scalar scaling)
640 {
641 pffftd_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
642 }
643
hayati ayguen1c193e92020-03-29 16:49:05 +0200644 static value_type* allocateType(int length)
dario mambrocb971842020-03-28 00:22:33 +0100645 {
hayati ayguen1c193e92020-03-29 16:49:05 +0200646 const int bytes = sizeof(value_type) * length;
647 return static_cast<value_type*>(pffft_aligned_malloc(bytes));
hayati ayguen63794b22020-03-29 03:14:43 +0200648 }
649
650 static Scalar* allocate(int length)
651 {
652 const int bytes = sizeof(Scalar) * length;
653 return static_cast<Scalar*>(pffftd_aligned_malloc(bytes));
dario mambrocb971842020-03-28 00:22:33 +0100654 }
655};
656
hayati ayguencd3dad62020-04-03 00:02:34 +0200657} // end of anonymous namespace for Setup<>
658
dario mambrocb971842020-03-28 00:22:33 +0100659
660template<typename T>
661inline Fft<T>::Fft(int length, int stackThresholdLen)
662 : length(0)
663 , stackThresholdLen(stackThresholdLen)
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200664 , work(NULL)
dario mambrocb971842020-03-28 00:22:33 +0100665{
hayati ayguenf913ef82020-04-04 12:31:36 +0200666#if (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900))
667 static_assert( sizeof(Complex) == 2 * sizeof(Scalar), "pffft requires sizeof(std::complex<>) == 2 * sizeof(Scalar)" );
668#elif defined(__GNUC__)
669 char static_assert_like[(sizeof(Complex) == 2 * sizeof(Scalar)) ? 1 : -1]; // pffft requires sizeof(std::complex<>) == 2 * sizeof(Scalar)
670#endif
dario mambrocb971842020-03-28 00:22:33 +0100671 prepareLength(length);
672}
673
674template<typename T>
675inline Fft<T>::~Fft()
676{
677 pffft_aligned_free(work);
678}
679
680template<typename T>
681inline void
682Fft<T>::prepareLength(int newLength)
683{
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200684 const bool wasOnHeap = ( work != NULL );
dario mambrocb971842020-03-28 00:22:33 +0100685
686 const bool useHeap = newLength > stackThresholdLen;
687
688 if (useHeap == wasOnHeap && newLength == length) {
689 return;
690 }
691
692 length = newLength;
693
694 setup.prepareLength(length);
695
696 if (work) {
697 pffft_aligned_free(work);
hayati ayguen7b3ca7d2020-03-29 03:18:35 +0200698 work = NULL;
dario mambrocb971842020-03-28 00:22:33 +0100699 }
700
701 if (useHeap) {
702 int const bytesToAllocate = length * sizeof(T);
703 work = static_cast<Scalar*>(pffft_aligned_malloc(bytesToAllocate));
704 }
705}
706
hayati ayguencd3dad62020-04-03 00:02:34 +0200707
708template<typename T>
709inline AlignedVector<T>
710Fft<T>::valueVector() const
711{
712 return AlignedVector<T>(length);
713}
714
715template<typename T>
716inline AlignedVector< typename Fft<T>::Complex >
717Fft<T>::spectrumVector() const
718{
719 return AlignedVector<Complex>( getSpectrumSize() );
720}
721
722template<typename T>
723inline AlignedVector< typename Fft<T>::Scalar >
724Fft<T>::internalLayoutVector() const
725{
726 return AlignedVector<Scalar>( getInternalLayoutSize() );
727}
728
729
730template<typename T>
731inline AlignedVector< typename Fft<T>::Complex > &
732Fft<T>::forward(const AlignedVector<T> & input, AlignedVector<Complex> & spectrum)
733{
734 forward( input.data(), spectrum.data() );
735 return spectrum;
736}
737
738template<typename T>
739inline AlignedVector<T> &
740Fft<T>::inverse(const AlignedVector<Complex> & spectrum, AlignedVector<T> & output)
741{
742 inverse( spectrum.data(), output.data() );
743 return output;
744}
745
746
747template<typename T>
748inline AlignedVector< typename Fft<T>::Scalar > &
749Fft<T>::forwardToInternalLayout(
750 const AlignedVector<T> & input,
751 AlignedVector<Scalar> & spectrum_internal_layout )
752{
753 forwardToInternalLayout( input.data(), spectrum_internal_layout.data() );
754 return spectrum_internal_layout;
755}
756
757template<typename T>
758inline AlignedVector<T> &
759Fft<T>::inverseFromInternalLayout(
760 const AlignedVector<Scalar> & spectrum_internal_layout,
761 AlignedVector<T> & output )
762{
763 inverseFromInternalLayout( spectrum_internal_layout.data(), output.data() );
764 return output;
765}
766
767template<typename T>
768inline void
769Fft<T>::reorderSpectrum(
770 const AlignedVector<Scalar> & input,
771 AlignedVector<Complex> & output )
772{
773 reorderSpectrum( input.data(), output.data() );
774}
775
776template<typename T>
777inline AlignedVector< typename Fft<T>::Scalar > &
778Fft<T>::convolveAccumulate(
779 const AlignedVector<Scalar> & spectrum_internal_a,
780 const AlignedVector<Scalar> & spectrum_internal_b,
781 AlignedVector<Scalar> & spectrum_internal_ab,
782 const Scalar scaling )
783{
784 convolveAccumulate( spectrum_internal_a.data(), spectrum_internal_b.data(),
785 spectrum_internal_ab.data(), scaling );
786 return spectrum_internal_ab;
787}
788
789template<typename T>
790inline AlignedVector< typename Fft<T>::Scalar > &
791Fft<T>::convolve(
792 const AlignedVector<Scalar> & spectrum_internal_a,
793 const AlignedVector<Scalar> & spectrum_internal_b,
794 AlignedVector<Scalar> & spectrum_internal_ab,
795 const Scalar scaling )
796{
797 convolve( spectrum_internal_a.data(), spectrum_internal_b.data(),
798 spectrum_internal_ab.data(), scaling );
799 return spectrum_internal_ab;
800}
801
802
dario mambrocb971842020-03-28 00:22:33 +0100803template<typename T>
hayati ayguen55531192020-04-02 23:11:14 +0200804inline typename Fft<T>::Complex *
805Fft<T>::forward(const T* input, Complex * spectrum)
dario mambrocb971842020-03-28 00:22:33 +0100806{
807 setup.transform_ordered(reinterpret_cast<const Scalar*>(input),
808 reinterpret_cast<Scalar*>(spectrum),
809 work,
810 PFFFT_FORWARD);
811 return spectrum;
812}
813
814template<typename T>
815inline T*
hayati ayguen55531192020-04-02 23:11:14 +0200816Fft<T>::inverse(Complex const* spectrum, T* output)
dario mambrocb971842020-03-28 00:22:33 +0100817{
818 setup.transform_ordered(reinterpret_cast<const Scalar*>(spectrum),
819 reinterpret_cast<Scalar*>(output),
820 work,
821 PFFFT_BACKWARD);
822 return output;
823}
824
825template<typename T>
826inline typename pffft::Fft<T>::Scalar*
hayati ayguen1c193e92020-03-29 16:49:05 +0200827Fft<T>::forwardToInternalLayout(const T* input, Scalar* spectrum_internal_layout)
dario mambrocb971842020-03-28 00:22:33 +0100828{
829 setup.transform(reinterpret_cast<const Scalar*>(input),
830 spectrum_internal_layout,
831 work,
832 PFFFT_FORWARD);
833 return spectrum_internal_layout;
834}
835
836template<typename T>
837inline T*
hayati ayguen1c193e92020-03-29 16:49:05 +0200838Fft<T>::inverseFromInternalLayout(const Scalar* spectrum_internal_layout, T* output)
dario mambrocb971842020-03-28 00:22:33 +0100839{
840 setup.transform(spectrum_internal_layout,
841 reinterpret_cast<Scalar*>(output),
842 work,
843 PFFFT_BACKWARD);
844 return output;
845}
846
847template<typename T>
848inline void
hayati ayguen55531192020-04-02 23:11:14 +0200849Fft<T>::reorderSpectrum( const Scalar* input, Complex* output )
dario mambrocb971842020-03-28 00:22:33 +0100850{
hayati ayguen1c193e92020-03-29 16:49:05 +0200851 setup.reorder(input, reinterpret_cast<Scalar*>(output), PFFFT_FORWARD);
dario mambrocb971842020-03-28 00:22:33 +0100852}
853
854template<typename T>
855inline typename pffft::Fft<T>::Scalar*
856Fft<T>::convolveAccumulate(const Scalar* dft_a,
857 const Scalar* dft_b,
858 Scalar* dft_ab,
859 const Scalar scaling)
860{
861 setup.convolveAccumulate(dft_a, dft_b, dft_ab, scaling);
862 return dft_ab;
863}
864
865template<typename T>
866inline typename pffft::Fft<T>::Scalar*
867Fft<T>::convolve(const Scalar* dft_a,
868 const Scalar* dft_b,
869 Scalar* dft_ab,
870 const Scalar scaling)
871{
872 setup.convolve(dft_a, dft_b, dft_ab, scaling);
873 return dft_ab;
874}
875
876template<typename T>
877inline void
878Fft<T>::alignedFree(void* ptr)
879{
880 pffft_aligned_free(ptr);
881}
882
hayati ayguen63794b22020-03-29 03:14:43 +0200883
884template<typename T>
885inline T*
886pffft::Fft<T>::alignedAllocType(int length)
887{
888 return Setup<T>::allocateType(length);
889}
890
dario mambrocb971842020-03-28 00:22:33 +0100891template<typename T>
892inline typename pffft::Fft<T>::Scalar*
893pffft::Fft<T>::alignedAllocScalar(int length)
894{
hayati ayguen63794b22020-03-29 03:14:43 +0200895 return reinterpret_cast< Scalar* >( Setup<T>::allocate(length) );
dario mambrocb971842020-03-28 00:22:33 +0100896}
897
898template<typename T>
hayati ayguen55531192020-04-02 23:11:14 +0200899inline typename Fft<T>::Complex *
dario mambrocb971842020-03-28 00:22:33 +0100900Fft<T>::alignedAllocComplex(int length)
901{
hayati ayguen55531192020-04-02 23:11:14 +0200902 return reinterpret_cast< Complex* >( Setup<T>::allocate(2 * length) );
dario mambrocb971842020-03-28 00:22:33 +0100903}
904
hayati ayguen1c193e92020-03-29 16:49:05 +0200905
906
907////////////////////////////////////////////////////////////////////
908
909// Allocator - for std::vector<>:
910// origin: http://www.josuttis.com/cppcode/allocator.html
911// http://www.josuttis.com/cppcode/myalloc.hpp
912//
913// minor renaming and utilizing of pffft (de)allocation functions
914// are applied to Jossutis' allocator
915
916/* The following code example is taken from the book
917 * "The C++ Standard Library - A Tutorial and Reference"
918 * by Nicolai M. Josuttis, Addison-Wesley, 1999
919 *
920 * (C) Copyright Nicolai M. Josuttis 1999.
921 * Permission to copy, use, modify, sell and distribute this software
922 * is granted provided this copyright notice appears in all copies.
923 * This software is provided "as is" without express or implied
924 * warranty, and with no claim as to its suitability for any purpose.
925 */
926
927template <class T>
928class PFAlloc {
929 public:
930 // type definitions
931 typedef T value_type;
932 typedef T* pointer;
933 typedef const T* const_pointer;
934 typedef T& reference;
935 typedef const T& const_reference;
936 typedef std::size_t size_type;
937 typedef std::ptrdiff_t difference_type;
938
939 // rebind allocator to type U
940 template <class U>
941 struct rebind {
942 typedef PFAlloc<U> other;
943 };
944
945 // return address of values
946 pointer address (reference value) const {
947 return &value;
948 }
949 const_pointer address (const_reference value) const {
950 return &value;
951 }
952
953 /* constructors and destructor
954 * - nothing to do because the allocator has no state
955 */
956 PFAlloc() throw() {
957 }
958 PFAlloc(const PFAlloc&) throw() {
959 }
960 template <class U>
961 PFAlloc (const PFAlloc<U>&) throw() {
962 }
963 ~PFAlloc() throw() {
964 }
965
966 // return maximum number of elements that can be allocated
967 size_type max_size () const throw() {
968 return std::numeric_limits<std::size_t>::max() / sizeof(T);
969 }
970
971 // allocate but don't initialize num elements of type T
972 pointer allocate (size_type num, const void* = 0) {
973 pointer ret = (pointer)( pffft_aligned_malloc(num*sizeof(T)) );
974 return ret;
975 }
976
977 // initialize elements of allocated storage p with value value
978 void construct (pointer p, const T& value) {
979 // initialize memory with placement new
980 new((void*)p)T(value);
981 }
982
983 // destroy elements of initialized storage p
984 void destroy (pointer p) {
985 // destroy objects by calling their destructor
986 p->~T();
987 }
988
989 // deallocate storage p of deleted elements
990 void deallocate (pointer p, size_type num) {
991 // deallocate memory with pffft
992 pffft_aligned_free((void*)p);
993 }
994};
995
996// return that all specializations of this allocator are interchangeable
997template <class T1, class T2>
998bool operator== (const PFAlloc<T1>&,
999 const PFAlloc<T2>&) throw() {
1000 return true;
1001}
1002template <class T1, class T2>
1003bool operator!= (const PFAlloc<T1>&,
1004 const PFAlloc<T2>&) throw() {
1005 return false;
1006}
1007
1008
dario mambrocb971842020-03-28 00:22:33 +01001009} // namespace pffft
hayati ayguen63794b22020-03-29 03:14:43 +02001010