1 /* Copyright (c) 2020 Dario Mambro ( dario.mambro@gmail.com )
2 Copyright (c) 2020 Hayati Ayguen ( h_ayguen@web.de )
3
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.
29 */
30
31 #pragma once
32
33 #include <complex>
34 #include <vector>
35 #include <limits>
36 #include <cassert>
37
38 namespace pffft {
39 namespace detail {
40 #if defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) )
41 #include "pffft.h"
42 #endif
43 #if defined(PFFFT_ENABLE_DOUBLE)
44 #include "pffft_double.h"
45 #endif
46 }
47 }
48
49 namespace pffft {
50
51 // enum { PFFFT_REAL, PFFFT_COMPLEX }
52 typedef detail::pffft_transform_t TransformType;
53
54 // define 'Scalar' and 'Complex' (in namespace pffft) with template Types<>
55 // and other type specific helper functions
56 template<typename T> struct Types {};
57 #if defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) )
58 template<> struct Types<float> {
59 typedef float Scalar;
60 typedef std::complex<Scalar> Complex;
simd_sizepffft::Types61 static int simd_size() { return detail::pffft_simd_size(); }
simd_archpffft::Types62 static const char * simd_arch() { return detail::pffft_simd_arch(); }
minFFtsizepffft::Types63 static int minFFtsize() { return pffft_min_fft_size(detail::PFFFT_REAL); }
isValidSizepffft::Types64 static bool isValidSize(int N) { return pffft_is_valid_size(N, detail::PFFFT_REAL); }
nearestTransformSizepffft::Types65 static int nearestTransformSize(int N, bool higher) { return pffft_nearest_transform_size(N, detail::PFFFT_REAL, higher ? 1 : 0); }
66 };
67 template<> struct Types< std::complex<float> > {
68 typedef float Scalar;
69 typedef std::complex<float> Complex;
simd_sizepffft::Types70 static int simd_size() { return detail::pffft_simd_size(); }
simd_archpffft::Types71 static const char * simd_arch() { return detail::pffft_simd_arch(); }
minFFtsizepffft::Types72 static int minFFtsize() { return pffft_min_fft_size(detail::PFFFT_COMPLEX); }
isValidSizepffft::Types73 static bool isValidSize(int N) { return pffft_is_valid_size(N, detail::PFFFT_COMPLEX); }
nearestTransformSizepffft::Types74 static int nearestTransformSize(int N, bool higher) { return pffft_nearest_transform_size(N, detail::PFFFT_COMPLEX, higher ? 1 : 0); }
75 };
76 #endif
77 #if defined(PFFFT_ENABLE_DOUBLE)
78 template<> struct Types<double> {
79 typedef double Scalar;
80 typedef std::complex<Scalar> Complex;
simd_sizepffft::Types81 static int simd_size() { return detail::pffftd_simd_size(); }
simd_archpffft::Types82 static const char * simd_arch() { return detail::pffftd_simd_arch(); }
minFFtsizepffft::Types83 static int minFFtsize() { return pffftd_min_fft_size(detail::PFFFT_REAL); }
isValidSizepffft::Types84 static bool isValidSize(int N) { return pffftd_is_valid_size(N, detail::PFFFT_REAL); }
nearestTransformSizepffft::Types85 static int nearestTransformSize(int N, bool higher) { return pffftd_nearest_transform_size(N, detail::PFFFT_REAL, higher ? 1 : 0); }
86 };
87 template<> struct Types< std::complex<double> > {
88 typedef double Scalar;
89 typedef std::complex<double> Complex;
simd_sizepffft::Types90 static int simd_size() { return detail::pffftd_simd_size(); }
simd_archpffft::Types91 static const char * simd_arch() { return detail::pffftd_simd_arch(); }
minFFtsizepffft::Types92 static int minFFtsize() { return pffftd_min_fft_size(detail::PFFFT_COMPLEX); }
isValidSizepffft::Types93 static bool isValidSize(int N) { return pffftd_is_valid_size(N, detail::PFFFT_COMPLEX); }
nearestTransformSizepffft::Types94 static int nearestTransformSize(int N, bool higher) { return pffftd_nearest_transform_size(N, detail::PFFFT_COMPLEX, higher ? 1 : 0); }
95 };
96 #endif
97
98 // Allocator
99 template<typename T> class PFAlloc;
100
101 namespace detail {
102 template<typename T> class Setup;
103 }
104
105 #if (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900))
106
107 // define AlignedVector<T> utilizing 'using' in C++11
108 template<typename T>
109 using AlignedVector = typename std::vector< T, PFAlloc<T> >;
110
111 #else
112
113 // define AlignedVector<T> having to derive std::vector<>
114 template <typename T>
115 struct AlignedVector : public std::vector< T, PFAlloc<T> > {
AlignedVectorpffft::AlignedVector116 AlignedVector() : std::vector< T, PFAlloc<T> >() { }
AlignedVectorpffft::AlignedVector117 AlignedVector(int N) : std::vector< T, PFAlloc<T> >(N) { }
118 };
119
120 #endif
121
122
123 // T can be float, double, std::complex<float> or std::complex<double>
124 // define PFFFT_ENABLE_DOUBLE before include this file for double and std::complex<double>
125 template<typename T>
126 class Fft
127 {
128 public:
129
130 // define types value_type, Scalar and Complex
131 typedef T value_type;
132 typedef typename Types<T>::Scalar Scalar;
133 typedef typename Types<T>::Complex Complex;
134
135 // static retrospection functions
isComplexTransform()136 static bool isComplexTransform() { return sizeof(T) == sizeof(Complex); }
isFloatScalar()137 static bool isFloatScalar() { return sizeof(Scalar) == sizeof(float); }
isDoubleScalar()138 static bool isDoubleScalar() { return sizeof(Scalar) == sizeof(double); }
139
140 // simple helper to determine next power of 2 - without inexact/rounding floating point operations
nextPowerOfTwo(int N)141 static int nextPowerOfTwo(int N) { return detail::pffft_next_power_of_two(N); }
isPowerOfTwo(int N)142 static bool isPowerOfTwo(int N) { return detail::pffft_is_power_of_two(N) ? true : false; }
143
144
simd_size()145 static int simd_size() { return Types<T>::simd_size(); }
simd_arch()146 static const char * simd_arch() { return Types<T>::simd_arch(); }
147
148 // simple helper to get minimum possible fft length
minFFtsize()149 static int minFFtsize() { return Types<T>::minFFtsize(); }
150
151 // helper to determine nearest transform size - factorizable to minFFtsize() with factors 2, 3, 5
isValidSize(int N)152 static bool isValidSize(int N) { return Types<T>::isValidSize(N); }
nearestTransformSize(int N,bool higher=true)153 static int nearestTransformSize(int N, bool higher=true) { return Types<T>::nearestTransformSize(N, higher); }
154
155
156 //////////////////
157
158 /*
159 * Contructor, with transformation length, preparing transforms.
160 *
161 * For length <= stackThresholdLen, the stack is used for the internal
162 * work memory. for bigger length', the heap is used.
163 *
164 * Using the stack is probably the best strategy for small
165 * FFTs, say for N <= 4096). Threads usually have a small stack, that
166 * there's no sufficient amount of memory, usually leading to a crash!
167 */
168 Fft( int length, int stackThresholdLen = 4096 );
169
170
171 /*
172 * constructor or prepareLength() produced a valid FFT instance?
173 * delivers false for invalid FFT sizes
174 */
175 bool isValid() const;
176
177
178 ~Fft();
179
180 /*
181 * prepare for transformation length 'newLength'.
182 * length is identical to forward()'s input vector's size,
183 * and also equals inverse()'s output vector size.
184 * this function is no simple setter. it pre-calculates twiddle factors.
185 * returns true if newLength is >= minFFtsize, false otherwise
186 */
187 bool prepareLength(int newLength);
188
189 /*
190 * retrieve the transformation length.
191 */
getLength() const192 int getLength() const { return length; }
193
194 /*
195 * retrieve size of complex spectrum vector,
196 * the output of forward()
197 */
getSpectrumSize() const198 int getSpectrumSize() const { return isComplexTransform() ? length : ( length / 2 ); }
199
200 /*
201 * retrieve size of spectrum vector - in internal layout;
202 * the output of forwardToInternalLayout()
203 */
getInternalLayoutSize() const204 int getInternalLayoutSize() const { return isComplexTransform() ? ( 2 * length ) : length; }
205
206
207 ////////////////////////////////////////////
208 ////
209 //// API 1, with std::vector<> based containers,
210 //// which free the allocated memory themselves (RAII).
211 ////
212 //// uses an Allocator for the alignment of SIMD data.
213 ////
214 ////////////////////////////////////////////
215
216 // create suitably preallocated aligned vector for one FFT
217 AlignedVector<T> valueVector() const;
218 AlignedVector<Complex> spectrumVector() const;
219 AlignedVector<Scalar> internalLayoutVector() const;
220
221 ////////////////////////////////////////////
222 // although using Vectors for output ..
223 // they need to have resize() applied before!
224
225 // core API, having the spectrum in canonical order
226
227 /*
228 * Perform the forward Fourier transform.
229 *
230 * Transforms are not scaled: inverse(forward(x)) = N*x.
231 * Typically you will want to scale the backward transform by 1/N.
232 *
233 * The output 'spectrum' is canonically ordered - as expected.
234 *
235 * a) for complex input isComplexTransform() == true,
236 * and transformation length N the output array is complex:
237 * index k in 0 .. N/2 -1 corresponds to frequency k * Samplerate / N
238 * index k in N/2 .. N -1 corresponds to frequency (k -N) * Samplerate / N,
239 * resulting in negative frequencies
240 *
241 * b) for real input isComplexTransform() == false,
242 * and transformation length N the output array is 'mostly' complex:
243 * index k in 1 .. N/2 -1 corresponds to frequency k * Samplerate / N
244 * index k == 0 is a special case:
245 * the real() part contains the result for the DC frequency 0,
246 * the imag() part contains the result for the Nyquist frequency Samplerate/2
247 * both 0-frequency and half frequency components, which are real,
248 * are assembled in the first entry as F(0)+i*F(N/2).
249 * with the output size N/2 complex values, it is obvious, that the
250 * result for negative frequencies are not output, cause of symmetry.
251 *
252 * input and output may alias - if you do nasty type conversion.
253 * return is just the given output parameter 'spectrum'.
254 */
255 AlignedVector<Complex> & forward(const AlignedVector<T> & input, AlignedVector<Complex> & spectrum);
256
257 /*
258 * Perform the inverse Fourier transform, see forward().
259 * return is just the given output parameter 'output'.
260 */
261 AlignedVector<T> & inverse(const AlignedVector<Complex> & spectrum, AlignedVector<T> & output);
262
263
264 // provide additional functions with spectrum in some internal Layout.
265 // these are faster, cause the implementation omits the reordering.
266 // these are useful in special applications, like fast convolution,
267 // where inverse() is following anyway ..
268
269 /*
270 * Perform the forward Fourier transform - similar to forward(), BUT:
271 *
272 * The z-domain data is stored in the most efficient order
273 * for transforming it back, or using it for convolution.
274 * If you need to have its content sorted in the "usual" canonical order,
275 * either use forward(), or call reorderSpectrum() after calling
276 * forwardToInternalLayout(), and before the backward fft
277 *
278 * return is just the given output parameter 'spectrum_internal_layout'.
279 */
280 AlignedVector<Scalar> & forwardToInternalLayout(
281 const AlignedVector<T> & input,
282 AlignedVector<Scalar> & spectrum_internal_layout );
283
284 /*
285 * Perform the inverse Fourier transform, see forwardToInternalLayout()
286 *
287 * return is just the given output parameter 'output'.
288 */
289 AlignedVector<T> & inverseFromInternalLayout(
290 const AlignedVector<Scalar> & spectrum_internal_layout,
291 AlignedVector<T> & output );
292
293 /*
294 * Reorder the spectrum from internal layout to have the
295 * frequency components in the correct "canonical" order.
296 * see forward() for a description of the canonical order.
297 *
298 * input and output should not alias.
299 */
300 void reorderSpectrum(
301 const AlignedVector<Scalar> & input,
302 AlignedVector<Complex> & output );
303
304 /*
305 * Perform a multiplication of the frequency components of
306 * spectrum_internal_a and spectrum_internal_b
307 * into spectrum_internal_ab.
308 * The arrays should have been obtained with forwardToInternalLayout)
309 * and should *not* have been reordered with reorderSpectrum().
310 *
311 * the operation performed is:
312 * spectrum_internal_ab = (spectrum_internal_a * spectrum_internal_b)*scaling
313 *
314 * The spectrum_internal_[a][b], pointers may alias.
315 * return is just the given output parameter 'spectrum_internal_ab'.
316 */
317 AlignedVector<Scalar> & convolve(
318 const AlignedVector<Scalar> & spectrum_internal_a,
319 const AlignedVector<Scalar> & spectrum_internal_b,
320 AlignedVector<Scalar> & spectrum_internal_ab,
321 const Scalar scaling );
322
323 /*
324 * Perform a multiplication and accumulation of the frequency components
325 * - similar to convolve().
326 *
327 * the operation performed is:
328 * spectrum_internal_ab += (spectrum_internal_a * spectrum_internal_b)*scaling
329 *
330 * The spectrum_internal_[a][b], pointers may alias.
331 * return is just the given output parameter 'spectrum_internal_ab'.
332 */
333 AlignedVector<Scalar> & convolveAccumulate(
334 const AlignedVector<Scalar> & spectrum_internal_a,
335 const AlignedVector<Scalar> & spectrum_internal_b,
336 AlignedVector<Scalar> & spectrum_internal_ab,
337 const Scalar scaling );
338
339
340 ////////////////////////////////////////////
341 ////
342 //// API 2, dealing with raw pointers,
343 //// which need to be deallocated using alignedFree()
344 ////
345 //// the special allocation is required cause SIMD
346 //// implementations require aligned memory
347 ////
348 //// Method descriptions are equal to the methods above,
349 //// having AlignedVector<T> parameters - instead of raw pointers.
350 //// That is why following methods have no documentation.
351 ////
352 ////////////////////////////////////////////
353
354 static void alignedFree(void* ptr);
355
356 static T * alignedAllocType(int length);
357 static Scalar* alignedAllocScalar(int length);
358 static Complex* alignedAllocComplex(int length);
359
360 // core API, having the spectrum in canonical order
361
362 Complex* forward(const T* input, Complex* spectrum);
363
364 T* inverse(const Complex* spectrum, T* output);
365
366
367 // provide additional functions with spectrum in some internal Layout.
368 // these are faster, cause the implementation omits the reordering.
369 // these are useful in special applications, like fast convolution,
370 // where inverse() is following anyway ..
371
372 Scalar* forwardToInternalLayout(const T* input,
373 Scalar* spectrum_internal_layout);
374
375 T* inverseFromInternalLayout(const Scalar* spectrum_internal_layout, T* output);
376
377 void reorderSpectrum(const Scalar* input, Complex* output );
378
379 Scalar* convolve(const Scalar* spectrum_internal_a,
380 const Scalar* spectrum_internal_b,
381 Scalar* spectrum_internal_ab,
382 const Scalar scaling);
383
384 Scalar* convolveAccumulate(const Scalar* spectrum_internal_a,
385 const Scalar* spectrum_internal_b,
386 Scalar* spectrum_internal_ab,
387 const Scalar scaling);
388
389 private:
390 detail::Setup<T> setup;
391 Scalar* work;
392 int length;
393 int stackThresholdLen;
394 };
395
396
397 template<typename T>
alignedAlloc(int length)398 inline T* alignedAlloc(int length) {
399 return (T*)detail::pffft_aligned_malloc( length * sizeof(T) );
400 }
401
alignedFree(void * ptr)402 inline void alignedFree(void *ptr) {
403 detail::pffft_aligned_free(ptr);
404 }
405
406
407 // simple helper to determine next power of 2 - without inexact/rounding floating point operations
nextPowerOfTwo(int N)408 inline int nextPowerOfTwo(int N) {
409 return detail::pffft_next_power_of_two(N);
410 }
411
isPowerOfTwo(int N)412 inline bool isPowerOfTwo(int N) {
413 return detail::pffft_is_power_of_two(N) ? true : false;
414 }
415
416
417
418 ////////////////////////////////////////////////////////////////////
419
420 // implementation
421
422 namespace detail {
423
424 template<typename T>
425 class Setup
426 {};
427
428 #if defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) )
429
430 template<>
431 class Setup<float>
432 {
433 PFFFT_Setup* self;
434
435 public:
436 typedef float value_type;
437 typedef Types< value_type >::Scalar Scalar;
438
Setup()439 Setup()
440 : self(NULL)
441 {}
442
~Setup()443 ~Setup() { pffft_destroy_setup(self); }
444
prepareLength(int length)445 void prepareLength(int length)
446 {
447 if (self) {
448 pffft_destroy_setup(self);
449 }
450 self = pffft_new_setup(length, PFFFT_REAL);
451 }
452
isValid() const453 bool isValid() const { return (self); }
454
transform_ordered(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)455 void transform_ordered(const Scalar* input,
456 Scalar* output,
457 Scalar* work,
458 pffft_direction_t direction)
459 {
460 pffft_transform_ordered(self, input, output, work, direction);
461 }
462
transform(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)463 void transform(const Scalar* input,
464 Scalar* output,
465 Scalar* work,
466 pffft_direction_t direction)
467 {
468 pffft_transform(self, input, output, work, direction);
469 }
470
reorder(const Scalar * input,Scalar * output,pffft_direction_t direction)471 void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
472 {
473 pffft_zreorder(self, input, output, direction);
474 }
475
convolveAccumulate(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)476 void convolveAccumulate(const Scalar* dft_a,
477 const Scalar* dft_b,
478 Scalar* dft_ab,
479 const Scalar scaling)
480 {
481 pffft_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
482 }
483
convolve(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)484 void convolve(const Scalar* dft_a,
485 const Scalar* dft_b,
486 Scalar* dft_ab,
487 const Scalar scaling)
488 {
489 pffft_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
490 }
491 };
492
493
494 template<>
495 class Setup< std::complex<float> >
496 {
497 PFFFT_Setup* self;
498
499 public:
500 typedef std::complex<float> value_type;
501 typedef Types< value_type >::Scalar Scalar;
502
Setup()503 Setup()
504 : self(NULL)
505 {}
506
~Setup()507 ~Setup() { pffft_destroy_setup(self); }
508
prepareLength(int length)509 void prepareLength(int length)
510 {
511 if (self) {
512 pffft_destroy_setup(self);
513 }
514 self = pffft_new_setup(length, PFFFT_COMPLEX);
515 }
516
isValid() const517 bool isValid() const { return (self); }
518
transform_ordered(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)519 void transform_ordered(const Scalar* input,
520 Scalar* output,
521 Scalar* work,
522 pffft_direction_t direction)
523 {
524 pffft_transform_ordered(self, input, output, work, direction);
525 }
526
transform(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)527 void transform(const Scalar* input,
528 Scalar* output,
529 Scalar* work,
530 pffft_direction_t direction)
531 {
532 pffft_transform(self, input, output, work, direction);
533 }
534
reorder(const Scalar * input,Scalar * output,pffft_direction_t direction)535 void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
536 {
537 pffft_zreorder(self, input, output, direction);
538 }
539
convolve(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)540 void convolve(const Scalar* dft_a,
541 const Scalar* dft_b,
542 Scalar* dft_ab,
543 const Scalar scaling)
544 {
545 pffft_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
546 }
547 };
548
549 #endif /* defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) ) */
550
551
552 #if defined(PFFFT_ENABLE_DOUBLE)
553
554 template<>
555 class Setup<double>
556 {
557 PFFFTD_Setup* self;
558
559 public:
560 typedef double value_type;
561 typedef Types< value_type >::Scalar Scalar;
562
Setup()563 Setup()
564 : self(NULL)
565 {}
566
~Setup()567 ~Setup() { pffftd_destroy_setup(self); }
568
prepareLength(int length)569 void prepareLength(int length)
570 {
571 if (self) {
572 pffftd_destroy_setup(self);
573 self = NULL;
574 }
575 if (length > 0) {
576 self = pffftd_new_setup(length, PFFFT_REAL);
577 }
578 }
579
isValid() const580 bool isValid() const { return (self); }
581
transform_ordered(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)582 void transform_ordered(const Scalar* input,
583 Scalar* output,
584 Scalar* work,
585 pffft_direction_t direction)
586 {
587 pffftd_transform_ordered(self, input, output, work, direction);
588 }
589
transform(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)590 void transform(const Scalar* input,
591 Scalar* output,
592 Scalar* work,
593 pffft_direction_t direction)
594 {
595 pffftd_transform(self, input, output, work, direction);
596 }
597
reorder(const Scalar * input,Scalar * output,pffft_direction_t direction)598 void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
599 {
600 pffftd_zreorder(self, input, output, direction);
601 }
602
convolveAccumulate(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)603 void convolveAccumulate(const Scalar* dft_a,
604 const Scalar* dft_b,
605 Scalar* dft_ab,
606 const Scalar scaling)
607 {
608 pffftd_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
609 }
610
convolve(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)611 void convolve(const Scalar* dft_a,
612 const Scalar* dft_b,
613 Scalar* dft_ab,
614 const Scalar scaling)
615 {
616 pffftd_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
617 }
618 };
619
620 template<>
621 class Setup< std::complex<double> >
622 {
623 PFFFTD_Setup* self;
624
625 public:
626 typedef std::complex<double> value_type;
627 typedef Types< value_type >::Scalar Scalar;
628
Setup()629 Setup()
630 : self(NULL)
631 {}
632
~Setup()633 ~Setup() { pffftd_destroy_setup(self); }
634
prepareLength(int length)635 void prepareLength(int length)
636 {
637 if (self) {
638 pffftd_destroy_setup(self);
639 }
640 self = pffftd_new_setup(length, PFFFT_COMPLEX);
641 }
642
isValid() const643 bool isValid() const { return (self); }
644
transform_ordered(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)645 void transform_ordered(const Scalar* input,
646 Scalar* output,
647 Scalar* work,
648 pffft_direction_t direction)
649 {
650 pffftd_transform_ordered(self, input, output, work, direction);
651 }
652
transform(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)653 void transform(const Scalar* input,
654 Scalar* output,
655 Scalar* work,
656 pffft_direction_t direction)
657 {
658 pffftd_transform(self, input, output, work, direction);
659 }
660
reorder(const Scalar * input,Scalar * output,pffft_direction_t direction)661 void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
662 {
663 pffftd_zreorder(self, input, output, direction);
664 }
665
convolveAccumulate(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)666 void convolveAccumulate(const Scalar* dft_a,
667 const Scalar* dft_b,
668 Scalar* dft_ab,
669 const Scalar scaling)
670 {
671 pffftd_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
672 }
673
convolve(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)674 void convolve(const Scalar* dft_a,
675 const Scalar* dft_b,
676 Scalar* dft_ab,
677 const Scalar scaling)
678 {
679 pffftd_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
680 }
681 };
682
683 #endif /* defined(PFFFT_ENABLE_DOUBLE) */
684
685 } // end of anonymous namespace for Setup<>
686
687
688 template<typename T>
Fft(int length,int stackThresholdLen)689 inline Fft<T>::Fft(int length, int stackThresholdLen)
690 : work(NULL)
691 , length(0)
692 , stackThresholdLen(stackThresholdLen)
693 {
694 #if (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900))
695 static_assert( sizeof(Complex) == 2 * sizeof(Scalar), "pffft requires sizeof(std::complex<>) == 2 * sizeof(Scalar)" );
696 #elif defined(__GNUC__)
697 char static_assert_like[(sizeof(Complex) == 2 * sizeof(Scalar)) ? 1 : -1]; // pffft requires sizeof(std::complex<>) == 2 * sizeof(Scalar)
698 #endif
699 prepareLength(length);
700 }
701
702 template<typename T>
~Fft()703 inline Fft<T>::~Fft()
704 {
705 alignedFree(work);
706 }
707
708 template<typename T>
709 inline bool
isValid() const710 Fft<T>::isValid() const
711 {
712 return setup.isValid();
713 }
714
715 template<typename T>
716 inline bool
prepareLength(int newLength)717 Fft<T>::prepareLength(int newLength)
718 {
719 if(newLength < minFFtsize())
720 return false;
721
722 const bool wasOnHeap = ( work != NULL );
723
724 const bool useHeap = newLength > stackThresholdLen;
725
726 if (useHeap == wasOnHeap && newLength == length) {
727 return true;
728 }
729
730 length = 0;
731
732 setup.prepareLength(newLength);
733 if (!setup.isValid())
734 return false;
735
736 length = newLength;
737
738 if (work) {
739 alignedFree(work);
740 work = NULL;
741 }
742
743 if (useHeap) {
744 work = reinterpret_cast<Scalar*>( alignedAllocType(length) );
745 }
746
747 return true;
748 }
749
750
751 template<typename T>
752 inline AlignedVector<T>
valueVector() const753 Fft<T>::valueVector() const
754 {
755 return AlignedVector<T>(length);
756 }
757
758 template<typename T>
759 inline AlignedVector< typename Fft<T>::Complex >
spectrumVector() const760 Fft<T>::spectrumVector() const
761 {
762 return AlignedVector<Complex>( getSpectrumSize() );
763 }
764
765 template<typename T>
766 inline AlignedVector< typename Fft<T>::Scalar >
internalLayoutVector() const767 Fft<T>::internalLayoutVector() const
768 {
769 return AlignedVector<Scalar>( getInternalLayoutSize() );
770 }
771
772
773 template<typename T>
774 inline AlignedVector< typename Fft<T>::Complex > &
forward(const AlignedVector<T> & input,AlignedVector<Complex> & spectrum)775 Fft<T>::forward(const AlignedVector<T> & input, AlignedVector<Complex> & spectrum)
776 {
777 forward( input.data(), spectrum.data() );
778 return spectrum;
779 }
780
781 template<typename T>
782 inline AlignedVector<T> &
inverse(const AlignedVector<Complex> & spectrum,AlignedVector<T> & output)783 Fft<T>::inverse(const AlignedVector<Complex> & spectrum, AlignedVector<T> & output)
784 {
785 inverse( spectrum.data(), output.data() );
786 return output;
787 }
788
789
790 template<typename T>
791 inline AlignedVector< typename Fft<T>::Scalar > &
forwardToInternalLayout(const AlignedVector<T> & input,AlignedVector<Scalar> & spectrum_internal_layout)792 Fft<T>::forwardToInternalLayout(
793 const AlignedVector<T> & input,
794 AlignedVector<Scalar> & spectrum_internal_layout )
795 {
796 forwardToInternalLayout( input.data(), spectrum_internal_layout.data() );
797 return spectrum_internal_layout;
798 }
799
800 template<typename T>
801 inline AlignedVector<T> &
inverseFromInternalLayout(const AlignedVector<Scalar> & spectrum_internal_layout,AlignedVector<T> & output)802 Fft<T>::inverseFromInternalLayout(
803 const AlignedVector<Scalar> & spectrum_internal_layout,
804 AlignedVector<T> & output )
805 {
806 inverseFromInternalLayout( spectrum_internal_layout.data(), output.data() );
807 return output;
808 }
809
810 template<typename T>
811 inline void
reorderSpectrum(const AlignedVector<Scalar> & input,AlignedVector<Complex> & output)812 Fft<T>::reorderSpectrum(
813 const AlignedVector<Scalar> & input,
814 AlignedVector<Complex> & output )
815 {
816 reorderSpectrum( input.data(), output.data() );
817 }
818
819 template<typename T>
820 inline AlignedVector< typename Fft<T>::Scalar > &
convolveAccumulate(const AlignedVector<Scalar> & spectrum_internal_a,const AlignedVector<Scalar> & spectrum_internal_b,AlignedVector<Scalar> & spectrum_internal_ab,const Scalar scaling)821 Fft<T>::convolveAccumulate(
822 const AlignedVector<Scalar> & spectrum_internal_a,
823 const AlignedVector<Scalar> & spectrum_internal_b,
824 AlignedVector<Scalar> & spectrum_internal_ab,
825 const Scalar scaling )
826 {
827 convolveAccumulate( spectrum_internal_a.data(), spectrum_internal_b.data(),
828 spectrum_internal_ab.data(), scaling );
829 return spectrum_internal_ab;
830 }
831
832 template<typename T>
833 inline AlignedVector< typename Fft<T>::Scalar > &
convolve(const AlignedVector<Scalar> & spectrum_internal_a,const AlignedVector<Scalar> & spectrum_internal_b,AlignedVector<Scalar> & spectrum_internal_ab,const Scalar scaling)834 Fft<T>::convolve(
835 const AlignedVector<Scalar> & spectrum_internal_a,
836 const AlignedVector<Scalar> & spectrum_internal_b,
837 AlignedVector<Scalar> & spectrum_internal_ab,
838 const Scalar scaling )
839 {
840 convolve( spectrum_internal_a.data(), spectrum_internal_b.data(),
841 spectrum_internal_ab.data(), scaling );
842 return spectrum_internal_ab;
843 }
844
845
846 template<typename T>
847 inline typename Fft<T>::Complex *
forward(const T * input,Complex * spectrum)848 Fft<T>::forward(const T* input, Complex * spectrum)
849 {
850 assert(isValid());
851 setup.transform_ordered(reinterpret_cast<const Scalar*>(input),
852 reinterpret_cast<Scalar*>(spectrum),
853 work,
854 detail::PFFFT_FORWARD);
855 return spectrum;
856 }
857
858 template<typename T>
859 inline T*
inverse(Complex const * spectrum,T * output)860 Fft<T>::inverse(Complex const* spectrum, T* output)
861 {
862 assert(isValid());
863 setup.transform_ordered(reinterpret_cast<const Scalar*>(spectrum),
864 reinterpret_cast<Scalar*>(output),
865 work,
866 detail::PFFFT_BACKWARD);
867 return output;
868 }
869
870 template<typename T>
871 inline typename pffft::Fft<T>::Scalar*
forwardToInternalLayout(const T * input,Scalar * spectrum_internal_layout)872 Fft<T>::forwardToInternalLayout(const T* input, Scalar* spectrum_internal_layout)
873 {
874 assert(isValid());
875 setup.transform(reinterpret_cast<const Scalar*>(input),
876 spectrum_internal_layout,
877 work,
878 detail::PFFFT_FORWARD);
879 return spectrum_internal_layout;
880 }
881
882 template<typename T>
883 inline T*
inverseFromInternalLayout(const Scalar * spectrum_internal_layout,T * output)884 Fft<T>::inverseFromInternalLayout(const Scalar* spectrum_internal_layout, T* output)
885 {
886 assert(isValid());
887 setup.transform(spectrum_internal_layout,
888 reinterpret_cast<Scalar*>(output),
889 work,
890 detail::PFFFT_BACKWARD);
891 return output;
892 }
893
894 template<typename T>
895 inline void
reorderSpectrum(const Scalar * input,Complex * output)896 Fft<T>::reorderSpectrum( const Scalar* input, Complex* output )
897 {
898 assert(isValid());
899 setup.reorder(input, reinterpret_cast<Scalar*>(output), detail::PFFFT_FORWARD);
900 }
901
902 template<typename T>
903 inline typename pffft::Fft<T>::Scalar*
convolveAccumulate(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)904 Fft<T>::convolveAccumulate(const Scalar* dft_a,
905 const Scalar* dft_b,
906 Scalar* dft_ab,
907 const Scalar scaling)
908 {
909 assert(isValid());
910 setup.convolveAccumulate(dft_a, dft_b, dft_ab, scaling);
911 return dft_ab;
912 }
913
914 template<typename T>
915 inline typename pffft::Fft<T>::Scalar*
convolve(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)916 Fft<T>::convolve(const Scalar* dft_a,
917 const Scalar* dft_b,
918 Scalar* dft_ab,
919 const Scalar scaling)
920 {
921 assert(isValid());
922 setup.convolve(dft_a, dft_b, dft_ab, scaling);
923 return dft_ab;
924 }
925
926 template<typename T>
927 inline void
alignedFree(void * ptr)928 Fft<T>::alignedFree(void* ptr)
929 {
930 pffft::alignedFree(ptr);
931 }
932
933
934 template<typename T>
935 inline T*
alignedAllocType(int length)936 pffft::Fft<T>::alignedAllocType(int length)
937 {
938 return alignedAlloc<T>(length);
939 }
940
941 template<typename T>
942 inline typename pffft::Fft<T>::Scalar*
alignedAllocScalar(int length)943 pffft::Fft<T>::alignedAllocScalar(int length)
944 {
945 return alignedAlloc<Scalar>(length);
946 }
947
948 template<typename T>
949 inline typename Fft<T>::Complex *
alignedAllocComplex(int length)950 Fft<T>::alignedAllocComplex(int length)
951 {
952 return alignedAlloc<Complex>(length);
953 }
954
955
956
957 ////////////////////////////////////////////////////////////////////
958
959 // Allocator - for std::vector<>:
960 // origin: http://www.josuttis.com/cppcode/allocator.html
961 // http://www.josuttis.com/cppcode/myalloc.hpp
962 //
963 // minor renaming and utilizing of pffft (de)allocation functions
964 // are applied to Jossutis' allocator
965
966 /* The following code example is taken from the book
967 * "The C++ Standard Library - A Tutorial and Reference"
968 * by Nicolai M. Josuttis, Addison-Wesley, 1999
969 *
970 * (C) Copyright Nicolai M. Josuttis 1999.
971 * Permission to copy, use, modify, sell and distribute this software
972 * is granted provided this copyright notice appears in all copies.
973 * This software is provided "as is" without express or implied
974 * warranty, and with no claim as to its suitability for any purpose.
975 */
976
977 template <class T>
978 class PFAlloc {
979 public:
980 // type definitions
981 typedef T value_type;
982 typedef T* pointer;
983 typedef const T* const_pointer;
984 typedef T& reference;
985 typedef const T& const_reference;
986 typedef std::size_t size_type;
987 typedef std::ptrdiff_t difference_type;
988
989 // rebind allocator to type U
990 template <class U>
991 struct rebind {
992 typedef PFAlloc<U> other;
993 };
994
995 // return address of values
address(reference value) const996 pointer address (reference value) const {
997 return &value;
998 }
address(const_reference value) const999 const_pointer address (const_reference value) const {
1000 return &value;
1001 }
1002
1003 /* constructors and destructor
1004 * - nothing to do because the allocator has no state
1005 */
PFAlloc()1006 PFAlloc() throw() {
1007 }
PFAlloc(const PFAlloc &)1008 PFAlloc(const PFAlloc&) throw() {
1009 }
1010 template <class U>
PFAlloc(const PFAlloc<U> &)1011 PFAlloc (const PFAlloc<U>&) throw() {
1012 }
~PFAlloc()1013 ~PFAlloc() throw() {
1014 }
1015
1016 // return maximum number of elements that can be allocated
max_size() const1017 size_type max_size () const throw() {
1018 return std::numeric_limits<std::size_t>::max() / sizeof(T);
1019 }
1020
1021 // allocate but don't initialize num elements of type T
allocate(size_type num,const void * =0)1022 pointer allocate (size_type num, const void* = 0) {
1023 pointer ret = (pointer)( alignedAlloc<T>(int(num)) );
1024 return ret;
1025 }
1026
1027 // initialize elements of allocated storage p with value value
construct(pointer p,const T & value)1028 void construct (pointer p, const T& value) {
1029 // initialize memory with placement new
1030 new((void*)p)T(value);
1031 }
1032
1033 // destroy elements of initialized storage p
destroy(pointer p)1034 void destroy (pointer p) {
1035 // destroy objects by calling their destructor
1036 p->~T();
1037 }
1038
1039 // deallocate storage p of deleted elements
deallocate(pointer p,size_type num)1040 void deallocate (pointer p, size_type num) {
1041 // deallocate memory with pffft
1042 alignedFree( (void*)p );
1043 }
1044 };
1045
1046 // return that all specializations of this allocator are interchangeable
1047 template <class T1, class T2>
operator ==(const PFAlloc<T1> &,const PFAlloc<T2> &)1048 bool operator== (const PFAlloc<T1>&,
1049 const PFAlloc<T2>&) throw() {
1050 return true;
1051 }
1052 template <class T1, class T2>
operator !=(const PFAlloc<T1> &,const PFAlloc<T2> &)1053 bool operator!= (const PFAlloc<T1>&,
1054 const PFAlloc<T2>&) throw() {
1055 return false;
1056 }
1057
1058
1059 } // namespace pffft
1060
1061