r8brain-free-src
High-quality pro audio sample rate converter library
 
Loading...
Searching...
No Matches
CDSPRealFFT.h
Go to the documentation of this file.
1//$ nobt
2//$ nocpp
3
18
19#ifndef R8B_CDSPREALFFT_INCLUDED
20#define R8B_CDSPREALFFT_INCLUDED
21
22#include "r8bbase.h"
23
24#if R8B_PFFFT_DOUBLE
25 #include "fft/pffft_double.h"
26#elif R8B_PFFFT
27 #include "fft/pffft.h"
28#elif !R8B_IPP
29 #include "fft/fft4g.h"
30#endif // !R8B_IPP
31
32namespace r8b {
33
52
53class CDSPRealFFT : public R8B_BASECLASS
54{
55 R8BNOCTOR( CDSPRealFFT )
56
57 friend class CPtrKeeper< CDSPRealFFT >;
58 friend class CDSPRealFFTKeeper;
59
60public:
65
66 double getInvMulConst() const
67 {
68 return( InvMulConst );
69 }
70
75
76 int getLenBits() const
77 {
78 return( LenBits );
79 }
80
85
86 int getLen() const
87 {
88 return( Len );
89 }
90
97
98 void forward( double* const p ) const
99 {
100 #if R8B_FLOATFFT
101
102 float* const op = (float*) p;
103 int i;
104
105 for( i = 0; i < Len; i++ )
106 {
107 op[ i ] = (float) p[ i ];
108 }
109
110 #endif // R8B_FLOATFFT
111
112 #if R8B_IPP
113
114 ippsFFTFwd_RToPerm_64f( p, p, SPtr, WorkBuffer );
115
116 #elif R8B_PFFFT
117
118 pffft_transform_ordered( setup, op, op, work, PFFFT_FORWARD );
119
120 #elif R8B_PFFFT_DOUBLE
121
122 pffftd_transform_ordered( setup, p, p, work, PFFFT_FORWARD );
123
124 #else // R8B_PFFFT_DOUBLE
125
126 ooura_fft :: rdft( Len, 1, p, wi, wd );
127
128 #endif // R8B_IPP
129 }
130
137
138 void inverse( double* const p ) const
139 {
140 #if R8B_IPP
141
142 ippsFFTInv_PermToR_64f( p, p, SPtr, WorkBuffer );
143
144 #elif R8B_PFFFT
145
146 pffft_transform_ordered( setup, (float*) p, (float*) p, work,
147 PFFFT_BACKWARD );
148
149 #elif R8B_PFFFT_DOUBLE
150
151 pffftd_transform_ordered( setup, p, p, work, PFFFT_BACKWARD );
152
153 #else // R8B_PFFFT_DOUBLE
154
155 ooura_fft :: rdft( Len, -1, p, wi, wd );
156
157 #endif // R8B_IPP
158
159 #if R8B_FLOATFFT
160
161 const float* const ip = (const float*) p;
162 int i;
163
164 for( i = Len - 1; i >= 0; i-- )
165 {
166 p[ i ] = ip[ i ];
167 }
168
169 #endif // R8B_FLOATFFT
170 }
171
185
186 void multiplyBlocks( const double* const aip1, const double* const aip2,
187 double* const aop ) const
188 {
189 #if R8B_FLOATFFT
190
191 const float* const ip1 = (const float*) aip1;
192 const float* const ip2 = (const float*) aip2;
193 float* const op = (float*) aop;
194
195 #else // R8B_FLOATFFT
196
197 const double* const ip1 = aip1;
198 const double* const ip2 = aip2;
199 double* const op = aop;
200
201 #endif // R8B_FLOATFFT
202
203 #if R8B_IPP
204
205 ippsMulPerm_64f( (Ipp64f*) ip1, (Ipp64f*) ip2, (Ipp64f*) op, Len );
206
207 #else // R8B_IPP
208
209 op[ 0 ] = ip1[ 0 ] * ip2[ 0 ];
210 op[ 1 ] = ip1[ 1 ] * ip2[ 1 ];
211
212 int i = 2;
213
214 while( i < Len )
215 {
216 op[ i ] = ip1[ i ] * ip2[ i ] - ip1[ i + 1 ] * ip2[ i + 1 ];
217 op[ i + 1 ] = ip1[ i ] * ip2[ i + 1 ] + ip1[ i + 1 ] * ip2[ i ];
218 i += 2;
219 }
220
221 #endif // R8B_IPP
222 }
223
234
235 void multiplyBlocks( const double* const aip, double* const aop ) const
236 {
237 #if R8B_FLOATFFT
238
239 const float* const ip = (const float*) aip;
240 float* const op = (float*) aop;
241 float t;
242
243 #else // R8B_FLOATFFT
244
245 const double* const ip = aip;
246 double* const op = aop;
247
248 #if !R8B_IPP
249 double t;
250 #endif // !R8B_IPP
251
252 #endif // R8B_FLOATFFT
253
254 #if R8B_IPP
255
256 ippsMulPerm_64f( (Ipp64f*) op, (Ipp64f*) ip, (Ipp64f*) op, Len );
257
258 #else // R8B_IPP
259
260 op[ 0 ] *= ip[ 0 ];
261 op[ 1 ] *= ip[ 1 ];
262
263 int i = 2;
264
265 while( i < Len )
266 {
267 t = op[ i ] * ip[ i ] - op[ i + 1 ] * ip[ i + 1 ];
268 op[ i + 1 ] = op[ i ] * ip[ i + 1 ] + op[ i + 1 ] * ip[ i ];
269 op[ i ] = t;
270 i += 2;
271 }
272
273 #endif // R8B_IPP
274 }
275
288
289 void multiplyBlocksZP( const double* const aip, double* const aop ) const
290 {
291 #if R8B_FLOATFFT
292
293 const float* const ip = (const float*) aip;
294 float* const op = (float*) aop;
295
296 #else // R8B_FLOATFFT
297
298 const double* ip = aip;
299 double* op = aop;
300
301 #endif // R8B_FLOATFFT
302
303 // SIMD implementations assume that pointers are address-aligned.
304
305 #if !R8B_FLOATFFT && defined( R8B_SSE2 )
306
307 int c8 = Len >> 3;
308
309 while( c8 != 0 )
310 {
311 const __m128d iv1 = _mm_load_pd( ip );
312 const __m128d iv2 = _mm_load_pd( ip + 2 );
313 const __m128d ov1 = _mm_load_pd( op );
314 const __m128d ov2 = _mm_load_pd( op + 2 );
315 _mm_store_pd( op, _mm_mul_pd( iv1, ov1 ));
316 _mm_store_pd( op + 2, _mm_mul_pd( iv2, ov2 ));
317
318 const __m128d iv3 = _mm_load_pd( ip + 4 );
319 const __m128d ov3 = _mm_load_pd( op + 4 );
320 const __m128d iv4 = _mm_load_pd( ip + 6 );
321 const __m128d ov4 = _mm_load_pd( op + 6 );
322 _mm_store_pd( op + 4, _mm_mul_pd( iv3, ov3 ));
323 _mm_store_pd( op + 6, _mm_mul_pd( iv4, ov4 ));
324
325 ip += 8;
326 op += 8;
327 c8--;
328 }
329
330 int c = Len & 7;
331
332 while( c != 0 )
333 {
334 *op *= *ip;
335 ip++;
336 op++;
337 c--;
338 }
339
340 #elif !R8B_FLOATFFT && defined( R8B_NEON )
341
342 int c8 = Len >> 3;
343
344 while( c8 != 0 )
345 {
346 const float64x2_t iv1 = vld1q_f64( ip );
347 const float64x2_t iv2 = vld1q_f64( ip + 2 );
348 const float64x2_t ov1 = vld1q_f64( op );
349 const float64x2_t ov2 = vld1q_f64( op + 2 );
350 vst1q_f64( op, vmulq_f64( iv1, ov1 ));
351 vst1q_f64( op + 2, vmulq_f64( iv2, ov2 ));
352
353 const float64x2_t iv3 = vld1q_f64( ip + 4 );
354 const float64x2_t iv4 = vld1q_f64( ip + 6 );
355 const float64x2_t ov3 = vld1q_f64( op + 4 );
356 const float64x2_t ov4 = vld1q_f64( op + 6 );
357 vst1q_f64( op + 4, vmulq_f64( iv3, ov3 ));
358 vst1q_f64( op + 6, vmulq_f64( iv4, ov4 ));
359
360 ip += 8;
361 op += 8;
362 c8--;
363 }
364
365 int c = Len & 7;
366
367 while( c != 0 )
368 {
369 *op *= *ip;
370 ip++;
371 op++;
372 c--;
373 }
374
375 #else // SIMD
376
377 int i;
378
379 for( i = 0; i < Len; i++ )
380 {
381 op[ i ] *= ip[ i ];
382 }
383
384 #endif // SIMD
385 }
386
394
395 void convertToZP( double* const ap ) const
396 {
397 #if R8B_FLOATFFT
398
399 float* const p = (float*) ap;
400
401 #else // R8B_FLOATFFT
402
403 double* const p = ap;
404
405 #endif // R8B_FLOATFFT
406
407 int i = 2;
408
409 while( i < Len )
410 {
411 p[ i + 1 ] = p[ i ];
412 i += 2;
413 }
414 }
415
416private:
417 int LenBits;
418 int Len;
419 double InvMulConst;
420 CDSPRealFFT* Next;
421
422 #if R8B_IPP
423 IppsFFTSpec_R_64f* SPtr;
427 #elif R8B_PFFFT
428 PFFFT_Setup* setup;
430 #elif R8B_PFFFT_DOUBLE
431 PFFFTD_Setup* setup;
433 #else // R8B_PFFFT_DOUBLE
436 #endif // R8B_IPP
437
439 {
440 }
441
449
450 CDSPRealFFT( const int aLenBits )
451 : LenBits( aLenBits )
452 , Len( 1 << aLenBits )
453 #if R8B_IPP
454 , InvMulConst( 1.0 / Len )
455 #elif R8B_PFFFT
456 , InvMulConst( 1.0 / Len )
457 #elif R8B_PFFFT_DOUBLE
458 , InvMulConst( 1.0 / Len )
459 #else // R8B_PFFFT_DOUBLE
460 , InvMulConst( 2.0 / Len )
461 #endif // R8B_IPP
462 {
463 #if R8B_IPP
464
465 int SpecSize;
466 int SpecBufferSize;
467 int BufferSize;
468
469 ippsFFTGetSize_R_64f( LenBits, IPP_FFT_NODIV_BY_ANY,
470 ippAlgHintFast, &SpecSize, &SpecBufferSize, &BufferSize );
471
472 CFixedBuffer< unsigned char > InitBuffer( SpecBufferSize );
473 SpecBuffer.alloc( SpecSize );
474 WorkBuffer.alloc( BufferSize );
475
476 ippsFFTInit_R_64f( &SPtr, LenBits, IPP_FFT_NODIV_BY_ANY,
477 ippAlgHintFast, SpecBuffer, InitBuffer );
478
479 #elif R8B_PFFFT
480
481 setup = pffft_new_setup( Len, PFFFT_REAL );
482 work.alloc( Len );
483
484 #elif R8B_PFFFT_DOUBLE
485
486 setup = pffftd_new_setup( Len, PFFFT_REAL );
487 work.alloc( Len );
488
489 #else // R8B_PFFFT_DOUBLE
490
491 wi.alloc( (int) ceil( 2.0 + sqrt( (double) ( Len >> 1 ))));
492 wi[ 0 ] = 0;
493 wd.alloc( Len >> 1 );
494
495 #endif // R8B_IPP
496 }
497
498 ~CDSPRealFFT()
499 {
500 #if R8B_PFFFT
501 pffft_destroy_setup( setup );
502 #elif R8B_PFFFT_DOUBLE
503 pffftd_destroy_setup( setup );
504 #endif // R8B_PFFFT_DOUBLE
505
506 delete Next;
507 }
508};
509
517
518class CDSPRealFFTKeeper : public R8B_BASECLASS
519{
520 R8BNOCTOR( CDSPRealFFTKeeper )
521
522public:
523 CDSPRealFFTKeeper()
524 : Object( R8B_NULL )
525 {
526 }
527
534
535 CDSPRealFFTKeeper( const int LenBits )
536 {
537 Object = acquire( LenBits );
538 }
539
541 {
542 if( Object != R8B_NULL )
543 {
544 release( Object );
545 }
546 }
547
551
553 {
554 R8BASSERT( Object != R8B_NULL );
555
556 return( Object );
557 }
558
566
567 void init( const int LenBits )
568 {
569 if( Object != R8B_NULL )
570 {
571 if( Object -> LenBits == LenBits )
572 {
573 return;
574 }
575
576 release( Object );
577 }
578
579 Object = acquire( LenBits );
580 }
581
585
586 void reset()
587 {
588 if( Object != R8B_NULL )
589 {
590 release( Object );
591 Object = R8B_NULL;
592 }
593 }
594
595private:
596 CDSPRealFFT* Object;
597
603
604 CDSPRealFFT* acquire( const int LenBits )
605 {
606 R8BASSERT( LenBits > 0 && LenBits <= 30 );
607
608 R8BSYNC( getStateSync() );
609
610 if( getFFTObjects()[ LenBits ] == R8B_NULL )
611 {
612 return( new CDSPRealFFT( LenBits ));
613 }
614
615 CDSPRealFFT* ffto = getFFTObjects()[ LenBits ].unkeep();
616 getFFTObjects()[ LenBits ] = ffto -> Next;
617
618 return( ffto );
619 }
620
626
627 void release( CDSPRealFFT* const ffto )
628 {
629 R8BSYNC( getStateSync() );
630
631 ffto -> Next = getFFTObjects()[ ffto -> LenBits ].unkeep();
632 getFFTObjects()[ ffto -> LenBits ] = ffto;
633 }
634
638
639 static CPtrKeeper< CDSPRealFFT >* getFFTObjects()
640 {
641 R8B_EXITDTOR static CPtrKeeper< CDSPRealFFT > FFTObjects[ 31 ];
642
643 return( FFTObjects );
644 }
645
649
650 static CSyncObject& getStateSync()
651 {
652 R8B_EXITDTOR static CSyncObject StateSync;
653
654 return( StateSync );
655 }
656};
657
680
681inline void calcMinPhaseTransform( double* const Kernel, const int KernelLen,
682 const int LenMult = 2, const bool DoFinalMul = true,
683 double* const DCGroupDelay = R8B_NULL )
684{
685 R8BASSERT( KernelLen > 0 );
686 R8BASSERT( LenMult >= 2 );
687
688 const int LenBits = getBitOccupancy(( KernelLen * LenMult ) - 1 );
689 const int Len = 1 << LenBits;
690 const int Len2 = Len >> 1;
691 int i;
692
693 CFixedBuffer< double > ip( Len );
694 CFixedBuffer< double > ip2( Len2 + 1 );
695
696 memcpy( &ip[ 0 ], Kernel, (size_t) KernelLen * sizeof( ip[ 0 ]));
697 memset( &ip[ KernelLen ], 0,
698 (size_t) ( Len - KernelLen ) * sizeof( ip[ 0 ]));
699
700 CDSPRealFFTKeeper ffto( LenBits );
701 ffto -> forward( ip );
702
703 // Create the "log |c|" spectrum while saving the original power spectrum
704 // in the "ip2" buffer.
705
706 #if R8B_FLOATFFT
707 float* const aip = (float*) &ip[ 0 ];
708 float* const aip2 = (float*) &ip2[ 0 ];
709 const float nzbias = 1e-35;
710 #else // R8B_FLOATFFT
711 double* const aip = &ip[ 0 ];
712 double* const aip2 = &ip2[ 0 ];
713 const double nzbias = 1e-300;
714 #endif // R8B_FLOATFFT
715
716 aip2[ 0 ] = aip[ 0 ];
717 aip[ 0 ] = log( fabs( aip[ 0 ]) + nzbias );
718 aip2[ Len2 ] = aip[ 1 ];
719 aip[ 1 ] = log( fabs( aip[ 1 ]) + nzbias );
720
721 for( i = 1; i < Len2; i++ )
722 {
723 aip2[ i ] = sqrt( aip[ i * 2 ] * aip[ i * 2 ] +
724 aip[ i * 2 + 1 ] * aip[ i * 2 + 1 ]);
725
726 aip[ i * 2 ] = log( aip2[ i ] + nzbias );
727 aip[ i * 2 + 1 ] = 0.0;
728 }
729
730 // Convert to cepstrum and apply discrete Hilbert transform.
731
732 ffto -> inverse( ip );
733
734 const double m1 = ffto -> getInvMulConst();
735 const double m2 = -m1;
736
737 ip[ 0 ] = 0.0;
738
739 for( i = 1; i < Len2; i++ )
740 {
741 ip[ i ] *= m1;
742 }
743
744 ip[ Len2 ] = 0.0;
745
746 for( i = Len2 + 1; i < Len; i++ )
747 {
748 ip[ i ] *= m2;
749 }
750
751 // Convert Hilbert-transformed cepstrum back to the "log |c|" spectrum and
752 // perform its exponentiation, multiplied by the power spectrum previously
753 // saved in the "ip2" buffer.
754
755 ffto -> forward( ip );
756
757 aip[ 0 ] = aip2[ 0 ];
758 aip[ 1 ] = aip2[ Len2 ];
759
760 for( i = 1; i < Len2; i++ )
761 {
762 aip[ i * 2 + 0 ] = cos( aip[ i * 2 + 1 ]) * aip2[ i ];
763 aip[ i * 2 + 1 ] = sin( aip[ i * 2 + 1 ]) * aip2[ i ];
764 }
765
766 ffto -> inverse( ip );
767
768 if( DoFinalMul )
769 {
770 for( i = 0; i < KernelLen; i++ )
771 {
772 Kernel[ i ] = ip[ i ] * m1;
773 }
774 }
775 else
776 {
777 memcpy( &Kernel[ 0 ], &ip[ 0 ],
778 (size_t) KernelLen * sizeof( Kernel[ 0 ]));
779 }
780
781 if( DCGroupDelay != R8B_NULL )
782 {
783 *DCGroupDelay = calcFIRFilterGroupDelay( Kernel, KernelLen, 0.0 );
784 }
785}
786
787} // namespace r8b
788
789#endif // VOX_CDSPREALFFT_INCLUDED
The "base" header file with basic classes and functions.
#define R8BSYNC(SyncObject)
Thread synchronization macro.
Definition r8bbase.h:655
#define R8B_EXITDTOR
Macro that defines the attribute specifying that the exit-time destructor should be called for a stat...
Definition r8bbase.h:149
#define R8B_NULL
The "null pointer" value, portable between C++11 and earlier C++ versions.
Definition r8bbase.h:101
#define R8BNOCTOR(ClassName)
Macro that defines empty copy-constructor and copy operator with the "private:" prefix.
Definition r8bbase.h:198
#define R8BASSERT(e)
Assertion macro used to check for certain run-time conditions. By default, no action is taken if asse...
Definition r8bconf.h:28
#define R8B_PFFFT
When defined as 1, enables PFFFT routines which are fast, but which are limited to 24-bit precision.
Definition r8bconf.h:183
#define R8B_BASECLASS
Macro defines the name of the class from which all classes that are designed to be created on heap ar...
Definition r8bconf.h:56
#define R8B_PFFFT_DOUBLE
When defined as 1, enables PFFFT "double" routines which are fast, and which provide the highest prec...
Definition r8bconf.h:171
#define R8B_IPP
Set this macro definition to 1 to enable the use of Intel IPP's fast Fourier transform functions....
Definition r8bconf.h:159
The "r8brain-free-src" library namespace.
Definition CDSPBlockConvolver.h:22
double calcFIRFilterGroupDelay(const double *const flt, const int fltlen, const double th)
FIR filter's group delay calculation function.
Definition r8bbase.h:876
int getBitOccupancy(const int v)
Calculate the exact number of bits a value needs for representation.
Definition r8bbase.h:766
void calcMinPhaseTransform(double *const Kernel, const int KernelLen, const int LenMult=2, const bool DoFinalMul=true, double *const DCGroupDelay=R8B_NULL)
Calculates the minimum-phase transform of the filter kernel, using a discrete Hilbert transform in ce...
Definition CDSPRealFFT.h:681
Real-valued FFT transform class.
Definition CDSPRealFFT.h:54
int getLenBits() const
Returns the length (the number of real values in a transform) of this FFT object, expressed as Nth po...
Definition CDSPRealFFT.h:76
void convertToZP(double *const ap) const
Converts the specified forward-transformed block into "zero-phase" form, suitable for use with the mu...
Definition CDSPRealFFT.h:395
void multiplyBlocks(const double *const aip, double *const aop) const
Nultiplies two complex-valued data blocks in-place.
Definition CDSPRealFFT.h:235
double getInvMulConst() const
Return a multiplication constant that should be used after inverse transform to obtain a correct valu...
Definition CDSPRealFFT.h:66
void inverse(double *const p) const
Performs in-place inverse FFT.
Definition CDSPRealFFT.h:138
void multiplyBlocks(const double *const aip1, const double *const aip2, double *const aop) const
Multiplies two complex-valued data blocks and places result in a new data block.
Definition CDSPRealFFT.h:186
void forward(double *const p) const
Performs in-place forward FFT.
Definition CDSPRealFFT.h:98
int getLen() const
Returns the length (the number of real values in a transform) of this FFT object.
Definition CDSPRealFFT.h:86
void multiplyBlocksZP(const double *const aip, double *const aop) const
Multiplies two complex-valued data blocks in-place, considering that the aip block contains "zero-pha...
Definition CDSPRealFFT.h:289
A "keeper" class for real-valued FFT transform objects.
Definition CDSPRealFFT.h:519
void init(const int LenBits)
Acquires FFT object with the specified block length. This function can be called any number of times.
Definition CDSPRealFFT.h:567
void reset()
Releases a previously acquired FFT object.
Definition CDSPRealFFT.h:586
CDSPRealFFTKeeper(const int LenBits)
Acquires FFT object with the specified block length.
Definition CDSPRealFFT.h:535
const CDSPRealFFT * operator->() const
Returns pointer to the acquired FFT object.
Definition CDSPRealFFT.h:552
Templated memory buffer class for element buffers of fixed capacity.
Definition r8bbase.h:281
void alloc(const int Capacity)
Allocates memory so that the specified number of elements of type T can be stored in this buffer obje...
Definition r8bbase.h:320
Pointer-to-object "keeper" class with automatic deletion.
Definition r8bbase.h:405