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#ifndef R8B_CDSPREALFFT_INCLUDED
19#define R8B_CDSPREALFFT_INCLUDED
20
21#include "r8bbase.h"
22
23#if R8B_PFFFT_DOUBLE
24 #include "pffft_double/pffft_double.h"
25#elif R8B_PFFFT
26 #include "pffft.h"
27#elif !R8B_IPP
28 #include "fft4g.h"
29#endif // !R8B_IPP
30
31namespace r8b {
32
53{
55
56 friend class CDSPRealFFTKeeper;
57
58public:
64 double getInvMulConst() const
65 {
66 return( InvMulConst );
67 }
68
74 int getLenBits() const
75 {
76 return( LenBits );
77 }
78
84 int getLen() const
85 {
86 return( Len );
87 }
88
96 void forward( double* const p ) const
97 {
98 #if R8B_FLOATFFT
99
100 float* const op = (float*) p;
101 int i;
102
103 for( i = 0; i < Len; i++ )
104 {
105 op[ i ] = (float) p[ i ];
106 }
107
108 #endif // R8B_FLOATFFT
109
110 #if R8B_IPP
111
112 ippsFFTFwd_RToPerm_64f( p, p, SPtr, WorkBuffer );
113
114 #elif R8B_PFFFT
115
116 pffft_transform_ordered( setup, op, op, work, PFFFT_FORWARD );
117
118 #elif R8B_PFFFT_DOUBLE
119
120 pffftd_transform_ordered( setup, p, p, work, PFFFT_FORWARD );
121
122 #else // R8B_PFFFT_DOUBLE
123
124 ooura_fft :: rdft( Len, 1, p, wi, wd );
125
126 #endif // R8B_IPP
127 }
128
136 void inverse( double* const p ) const
137 {
138 #if R8B_IPP
139
140 ippsFFTInv_PermToR_64f( p, p, SPtr, WorkBuffer );
141
142 #elif R8B_PFFFT
143
144 pffft_transform_ordered( setup, (float*) p, (float*) p, work,
145 PFFFT_BACKWARD );
146
147 #elif R8B_PFFFT_DOUBLE
148
149 pffftd_transform_ordered( setup, p, p, work, PFFFT_BACKWARD );
150
151 #else // R8B_PFFFT_DOUBLE
152
153 ooura_fft :: rdft( Len, -1, p, wi, wd );
154
155 #endif // R8B_IPP
156
157 #if R8B_FLOATFFT
158
159 const float* const ip = (const float*) p;
160 int i;
161
162 for( i = Len - 1; i >= 0; i-- )
163 {
164 p[ i ] = ip[ i ];
165 }
166
167 #endif // R8B_FLOATFFT
168 }
169
182 void multiplyBlocks( const double* const aip1, const double* const aip2,
183 double* const aop ) const
184 {
185 #if R8B_FLOATFFT
186
187 const float* const ip1 = (const float*) aip1;
188 const float* const ip2 = (const float*) aip2;
189 float* const op = (float*) aop;
190
191 #else // R8B_FLOATFFT
192
193 const double* const ip1 = aip1;
194 const double* const ip2 = aip2;
195 double* const op = aop;
196
197 #endif // R8B_FLOATFFT
198
199 #if R8B_IPP
200
201 ippsMulPerm_64f( (Ipp64f*) ip1, (Ipp64f*) ip2, (Ipp64f*) op, Len );
202
203 #else // R8B_IPP
204
205 op[ 0 ] = ip1[ 0 ] * ip2[ 0 ];
206 op[ 1 ] = ip1[ 1 ] * ip2[ 1 ];
207
208 int i = 2;
209
210 while( i < Len )
211 {
212 op[ i ] = ip1[ i ] * ip2[ i ] - ip1[ i + 1 ] * ip2[ i + 1 ];
213 op[ i + 1 ] = ip1[ i ] * ip2[ i + 1 ] + ip1[ i + 1 ] * ip2[ i ];
214 i += 2;
215 }
216
217 #endif // R8B_IPP
218 }
219
229 void multiplyBlocks( const double* const aip, double* const aop ) const
230 {
231 #if R8B_FLOATFFT
232
233 const float* const ip = (const float*) aip;
234 float* const op = (float*) aop;
235 float t;
236
237 #else // R8B_FLOATFFT
238
239 const double* const ip = aip;
240 double* const op = aop;
241
242 #if !R8B_IPP
243 double t;
244 #endif // !R8B_IPP
245
246 #endif // R8B_FLOATFFT
247
248 #if R8B_IPP
249
250 ippsMulPerm_64f( (Ipp64f*) op, (Ipp64f*) ip, (Ipp64f*) op, Len );
251
252 #else // R8B_IPP
253
254 op[ 0 ] *= ip[ 0 ];
255 op[ 1 ] *= ip[ 1 ];
256
257 int i = 2;
258
259 while( i < Len )
260 {
261 t = op[ i ] * ip[ i ] - op[ i + 1 ] * ip[ i + 1 ];
262 op[ i + 1 ] = op[ i ] * ip[ i + 1 ] + op[ i + 1 ] * ip[ i ];
263 op[ i ] = t;
264 i += 2;
265 }
266
267 #endif // R8B_IPP
268 }
269
282 void multiplyBlocksZP( const double* const aip, double* const aop ) const
283 {
284 #if R8B_FLOATFFT
285
286 const float* const ip = (const float*) aip;
287 float* const op = (float*) aop;
288
289 #else // R8B_FLOATFFT
290
291 const double* ip = aip;
292 double* op = aop;
293
294 #endif // R8B_FLOATFFT
295
296 // SIMD implementations assume that pointers are address-aligned.
297
298 #if !R8B_FLOATFFT && defined( R8B_SSE2 )
299
300 int c8 = Len >> 3;
301
302 while( c8 != 0 )
303 {
304 const __m128d iv1 = _mm_load_pd( ip );
305 const __m128d iv2 = _mm_load_pd( ip + 2 );
306 const __m128d ov1 = _mm_load_pd( op );
307 const __m128d ov2 = _mm_load_pd( op + 2 );
308 _mm_store_pd( op, _mm_mul_pd( iv1, ov1 ));
309 _mm_store_pd( op + 2, _mm_mul_pd( iv2, ov2 ));
310
311 const __m128d iv3 = _mm_load_pd( ip + 4 );
312 const __m128d ov3 = _mm_load_pd( op + 4 );
313 const __m128d iv4 = _mm_load_pd( ip + 6 );
314 const __m128d ov4 = _mm_load_pd( op + 6 );
315 _mm_store_pd( op + 4, _mm_mul_pd( iv3, ov3 ));
316 _mm_store_pd( op + 6, _mm_mul_pd( iv4, ov4 ));
317
318 ip += 8;
319 op += 8;
320 c8--;
321 }
322
323 int c = Len & 7;
324
325 while( c != 0 )
326 {
327 *op *= *ip;
328 ip++;
329 op++;
330 c--;
331 }
332
333 #elif !R8B_FLOATFFT && defined( R8B_NEON )
334
335 int c8 = Len >> 3;
336
337 while( c8 != 0 )
338 {
339 const float64x2_t iv1 = vld1q_f64( ip );
340 const float64x2_t iv2 = vld1q_f64( ip + 2 );
341 const float64x2_t ov1 = vld1q_f64( op );
342 const float64x2_t ov2 = vld1q_f64( op + 2 );
343 vst1q_f64( op, vmulq_f64( iv1, ov1 ));
344 vst1q_f64( op + 2, vmulq_f64( iv2, ov2 ));
345
346 const float64x2_t iv3 = vld1q_f64( ip + 4 );
347 const float64x2_t iv4 = vld1q_f64( ip + 6 );
348 const float64x2_t ov3 = vld1q_f64( op + 4 );
349 const float64x2_t ov4 = vld1q_f64( op + 6 );
350 vst1q_f64( op + 4, vmulq_f64( iv3, ov3 ));
351 vst1q_f64( op + 6, vmulq_f64( iv4, ov4 ));
352
353 ip += 8;
354 op += 8;
355 c8--;
356 }
357
358 int c = Len & 7;
359
360 while( c != 0 )
361 {
362 *op *= *ip;
363 ip++;
364 op++;
365 c--;
366 }
367
368 #else // SIMD
369
370 int i;
371
372 for( i = 0; i < Len; i++ )
373 {
374 op[ i ] *= ip[ i ];
375 }
376
377 #endif // SIMD
378 }
379
388 void convertToZP( double* const ap ) const
389 {
390 #if R8B_FLOATFFT
391
392 float* const p = (float*) ap;
393
394 #else // R8B_FLOATFFT
395
396 double* const p = ap;
397
398 #endif // R8B_FLOATFFT
399
400 int i = 2;
401
402 while( i < Len )
403 {
404 p[ i + 1 ] = p[ i ];
405 i += 2;
406 }
407 }
408
409private:
410 int LenBits;
411 int Len;
412 double InvMulConst;
413 CDSPRealFFT* Next;
414
415 #if R8B_IPP
416 IppsFFTSpec_R_64f* SPtr;
420 #elif R8B_PFFFT
421 PFFFT_Setup* setup;
423 #elif R8B_PFFFT_DOUBLE
424 PFFFTD_Setup* setup;
426 #else // R8B_PFFFT_DOUBLE
429 #endif // R8B_IPP
430
436 class CObjKeeper
437 {
438 R8BNOCTOR( CObjKeeper );
439
440 public:
441 CObjKeeper()
442 : Object( NULL )
443 {
444 }
445
446 ~CObjKeeper()
447 {
448 delete Object;
449 }
450
451 CObjKeeper& operator = ( CDSPRealFFT* const aObject )
452 {
453 Object = aObject;
454 return( *this );
455 }
456
457 operator CDSPRealFFT* () const
458 {
459 return( Object );
460 }
461
462 private:
463 CDSPRealFFT* Object;
464 };
465
466 CDSPRealFFT()
467 {
468 }
469
478 CDSPRealFFT( const int aLenBits )
479 : LenBits( aLenBits )
480 , Len( 1 << aLenBits )
481 #if R8B_IPP
482 , InvMulConst( 1.0 / Len )
483 #elif R8B_PFFFT
484 , InvMulConst( 1.0 / Len )
485 #elif R8B_PFFFT_DOUBLE
486 , InvMulConst( 1.0 / Len )
487 #else // R8B_PFFFT_DOUBLE
488 , InvMulConst( 2.0 / Len )
489 #endif // R8B_IPP
490 {
491 #if R8B_IPP
492
493 int SpecSize;
494 int SpecBufferSize;
495 int BufferSize;
496
497 ippsFFTGetSize_R_64f( LenBits, IPP_FFT_NODIV_BY_ANY,
498 ippAlgHintFast, &SpecSize, &SpecBufferSize, &BufferSize );
499
500 CFixedBuffer< unsigned char > InitBuffer( SpecBufferSize );
501 SpecBuffer.alloc( SpecSize );
502 WorkBuffer.alloc( BufferSize );
503
504 ippsFFTInit_R_64f( &SPtr, LenBits, IPP_FFT_NODIV_BY_ANY,
505 ippAlgHintFast, SpecBuffer, InitBuffer );
506
507 #elif R8B_PFFFT
508
509 setup = pffft_new_setup( Len, PFFFT_REAL );
510 work.alloc( Len );
511
512 #elif R8B_PFFFT_DOUBLE
513
514 setup = pffftd_new_setup( Len, PFFFT_REAL );
515 work.alloc( Len );
516
517 #else // R8B_PFFFT_DOUBLE
518
519 wi.alloc( (int) ceil( 2.0 + sqrt( (double) ( Len >> 1 ))));
520 wi[ 0 ] = 0;
521 wd.alloc( Len >> 1 );
522
523 #endif // R8B_IPP
524 }
525
526 ~CDSPRealFFT()
527 {
528 #if R8B_PFFFT
529 pffft_destroy_setup( setup );
530 #elif R8B_PFFFT_DOUBLE
531 pffftd_destroy_setup( setup );
532 #endif // R8B_PFFFT_DOUBLE
533
534 delete Next;
535 }
536};
537
547{
549
550public:
552 : Object( NULL )
553 {
554 }
555
563 CDSPRealFFTKeeper( const int LenBits )
564 {
565 Object = acquire( LenBits );
566 }
567
569 {
570 if( Object != NULL )
571 {
572 release( Object );
573 }
574 }
575
581 {
582 R8BASSERT( Object != NULL );
583
584 return( Object );
585 }
586
595 void init( const int LenBits )
596 {
597 if( Object != NULL )
598 {
599 if( Object -> LenBits == LenBits )
600 {
601 return;
602 }
603
604 release( Object );
605 }
606
607 Object = acquire( LenBits );
608 }
609
614 void reset()
615 {
616 if( Object != NULL )
617 {
618 release( Object );
619 Object = NULL;
620 }
621 }
622
623private:
624 CDSPRealFFT* Object;
625
626 static CSyncObject StateSync;
627 static CDSPRealFFT :: CObjKeeper FFTObjects[];
629
636 CDSPRealFFT* acquire( const int LenBits )
637 {
638 R8BASSERT( LenBits > 0 && LenBits <= 30 );
639
640 R8BSYNC( StateSync );
641
642 if( FFTObjects[ LenBits ] == NULL )
643 {
644 return( new CDSPRealFFT( LenBits ));
645 }
646
647 CDSPRealFFT* ffto = FFTObjects[ LenBits ];
648 FFTObjects[ LenBits ] = ffto -> Next;
649
650 return( ffto );
651 }
652
659 void release( CDSPRealFFT* const ffto )
660 {
661 R8BSYNC( StateSync );
662
663 ffto -> Next = FFTObjects[ ffto -> LenBits ];
664 FFTObjects[ ffto -> LenBits ] = ffto;
665 }
666};
667
691inline void calcMinPhaseTransform( double* const Kernel, const int KernelLen,
692 const int LenMult = 2, const bool DoFinalMul = true,
693 double* const DCGroupDelay = NULL )
694{
695 R8BASSERT( KernelLen > 0 );
696 R8BASSERT( LenMult >= 2 );
697
698 const int LenBits = getBitOccupancy(( KernelLen * LenMult ) - 1 );
699 const int Len = 1 << LenBits;
700 const int Len2 = Len >> 1;
701 int i;
702
703 CFixedBuffer< double > ip( Len );
704 CFixedBuffer< double > ip2( Len2 + 1 );
705
706 memcpy( &ip[ 0 ], Kernel, KernelLen * sizeof( ip[ 0 ]));
707 memset( &ip[ KernelLen ], 0, ( Len - KernelLen ) * sizeof( ip[ 0 ]));
708
709 CDSPRealFFTKeeper ffto( LenBits );
710 ffto -> forward( ip );
711
712 // Create the "log |c|" spectrum while saving the original power spectrum
713 // in the "ip2" buffer.
714
715 #if R8B_FLOATFFT
716 float* const aip = (float*) &ip[ 0 ];
717 float* const aip2 = (float*) &ip2[ 0 ];
718 const float nzbias = 1e-35;
719 #else // R8B_FLOATFFT
720 double* const aip = &ip[ 0 ];
721 double* const aip2 = &ip2[ 0 ];
722 const double nzbias = 1e-300;
723 #endif // R8B_FLOATFFT
724
725 aip2[ 0 ] = aip[ 0 ];
726 aip[ 0 ] = log( fabs( aip[ 0 ]) + nzbias );
727 aip2[ Len2 ] = aip[ 1 ];
728 aip[ 1 ] = log( fabs( aip[ 1 ]) + nzbias );
729
730 for( i = 1; i < Len2; i++ )
731 {
732 aip2[ i ] = sqrt( aip[ i * 2 ] * aip[ i * 2 ] +
733 aip[ i * 2 + 1 ] * aip[ i * 2 + 1 ]);
734
735 aip[ i * 2 ] = log( aip2[ i ] + nzbias );
736 aip[ i * 2 + 1 ] = 0.0;
737 }
738
739 // Convert to cepstrum and apply discrete Hilbert transform.
740
741 ffto -> inverse( ip );
742
743 const double m1 = ffto -> getInvMulConst();
744 const double m2 = -m1;
745
746 ip[ 0 ] = 0.0;
747
748 for( i = 1; i < Len2; i++ )
749 {
750 ip[ i ] *= m1;
751 }
752
753 ip[ Len2 ] = 0.0;
754
755 for( i = Len2 + 1; i < Len; i++ )
756 {
757 ip[ i ] *= m2;
758 }
759
760 // Convert Hilbert-transformed cepstrum back to the "log |c|" spectrum and
761 // perform its exponentiation, multiplied by the power spectrum previously
762 // saved in the "ip2" buffer.
763
764 ffto -> forward( ip );
765
766 aip[ 0 ] = aip2[ 0 ];
767 aip[ 1 ] = aip2[ Len2 ];
768
769 for( i = 1; i < Len2; i++ )
770 {
771 aip[ i * 2 + 0 ] = cos( aip[ i * 2 + 1 ]) * aip2[ i ];
772 aip[ i * 2 + 1 ] = sin( aip[ i * 2 + 1 ]) * aip2[ i ];
773 }
774
775 ffto -> inverse( ip );
776
777 if( DoFinalMul )
778 {
779 for( i = 0; i < KernelLen; i++ )
780 {
781 Kernel[ i ] = ip[ i ] * m1;
782 }
783 }
784 else
785 {
786 memcpy( &Kernel[ 0 ], &ip[ 0 ], KernelLen * sizeof( Kernel[ 0 ]));
787 }
788
789 if( DCGroupDelay != NULL )
790 {
791 *DCGroupDelay = calcFIRFilterGroupDelay( Kernel, KernelLen, 0.0 );
792 }
793}
794
795} // namespace r8b
796
797#endif // VOX_CDSPREALFFT_INCLUDED
Wrapper class for Takuya OOURA's FFT functions.
The "base" inclusion file with basic classes and functions.
#define R8BSYNC(SyncObject)
Definition: r8bbase.h:660
#define R8BNOCTOR(ClassName)
Definition: r8bbase.h:154
#define R8BASSERT(e)
Definition: r8bconf.h:27
#define R8B_PFFFT
Definition: r8bconf.h:170
#define R8B_BASECLASS
Definition: r8bconf.h:54
#define R8B_PFFFT_DOUBLE
Definition: r8bconf.h:159
#define R8B_IPP
Definition: r8bconf.h:147
The "r8brain-free-src" library namespace.
Definition: CDSPBlockConvolver.h:21
double calcFIRFilterGroupDelay(const double *const flt, const int fltlen, const double th)
Definition: r8bbase.h:872
int getBitOccupancy(const int v)
Definition: r8bbase.h:766
void calcMinPhaseTransform(double *const Kernel, const int KernelLen, const int LenMult=2, const bool DoFinalMul=true, double *const DCGroupDelay=NULL)
Definition: CDSPRealFFT.h:691
Real-valued FFT transform class.
Definition: CDSPRealFFT.h:53
int getLenBits() const
Definition: CDSPRealFFT.h:74
void convertToZP(double *const ap) const
Definition: CDSPRealFFT.h:388
void multiplyBlocks(const double *const aip, double *const aop) const
Definition: CDSPRealFFT.h:229
double getInvMulConst() const
Definition: CDSPRealFFT.h:64
void inverse(double *const p) const
Definition: CDSPRealFFT.h:136
void multiplyBlocks(const double *const aip1, const double *const aip2, double *const aop) const
Definition: CDSPRealFFT.h:182
void forward(double *const p) const
Definition: CDSPRealFFT.h:96
int getLen() const
Definition: CDSPRealFFT.h:84
void multiplyBlocksZP(const double *const aip, double *const aop) const
Definition: CDSPRealFFT.h:282
A "keeper" class for real-valued FFT transform objects.
Definition: CDSPRealFFT.h:547
void init(const int LenBits)
Definition: CDSPRealFFT.h:595
void reset()
Definition: CDSPRealFFT.h:614
CDSPRealFFTKeeper(const int LenBits)
Definition: CDSPRealFFT.h:563
const CDSPRealFFT * operator->() const
Definition: CDSPRealFFT.h:580
Templated memory buffer class for element buffers of fixed capacity.
Definition: r8bbase.h:304
void alloc(const int Capacity)
Definition: r8bbase.h:343
Multi-threaded synchronization object class.
Definition: r8bbase.h:526