1
0

CDSPRealFFT.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  1. //$ nobt
  2. //$ nocpp
  3. /**
  4. * @file CDSPRealFFT.h
  5. *
  6. * @brief Real-valued FFT transform class.
  7. *
  8. * This file includes FFT object implementation. All created FFT objects are
  9. * kept in a global list after use, for a future reusal. Such approach
  10. * minimizes time necessary to initialize the FFT object of the required
  11. * length.
  12. *
  13. * r8brain-free-src Copyright (c) 2013-2022 Aleksey Vaneev
  14. * See the "LICENSE" file for license.
  15. */
  16. #ifndef R8B_CDSPREALFFT_INCLUDED
  17. #define R8B_CDSPREALFFT_INCLUDED
  18. #include "r8bbase.h"
  19. #if !R8B_IPP && !R8B_PFFFT && !R8B_PFFFT_DOUBLE
  20. #include "fft4g.h"
  21. #endif // !R8B_IPP && !R8B_PFFFT && !R8B_PFFFT_DOUBLE
  22. #if R8B_PFFFT
  23. #include "pffft.h"
  24. #endif // R8B_PFFFT
  25. #if R8B_PFFFT_DOUBLE
  26. #include "pffft_double/pffft_double.h"
  27. #endif // R8B_PFFFT_DOUBLE
  28. namespace r8b {
  29. /**
  30. * @brief Real-valued FFT transform class.
  31. *
  32. * Class implements a wrapper for real-valued discrete fast Fourier transform
  33. * functions. The object of this class can only be obtained via the
  34. * CDSPRealFFTKeeper class.
  35. *
  36. * Uses functions from the FFT package by: Copyright(C) 1996-2001 Takuya OOURA
  37. * http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html
  38. *
  39. * Also uses Intel IPP library functions if available (if the R8B_IPP=1 macro
  40. * was defined). Note that IPP library's FFT functions are 2-3 times more
  41. * efficient on the modern Intel Core i7-3770K processor than Ooura's
  42. * functions. It may be worthwhile investing in IPP. Note, that FFT functions
  43. * take less than 20% of the overall sample rate conversion time. However,
  44. * when the "power of 2" resampling is used the performance of FFT functions
  45. * becomes "everything".
  46. */
  47. class CDSPRealFFT : public R8B_BASECLASS
  48. {
  49. R8BNOCTOR( CDSPRealFFT );
  50. friend class CDSPRealFFTKeeper;
  51. public:
  52. /**
  53. * @return A multiplication constant that should be used after inverse
  54. * transform to obtain a correct value scale.
  55. */
  56. double getInvMulConst() const
  57. {
  58. return( InvMulConst );
  59. }
  60. /**
  61. * @return The length (the number of real values in a transform) of *this
  62. * FFT object, expressed as Nth power of 2.
  63. */
  64. int getLenBits() const
  65. {
  66. return( LenBits );
  67. }
  68. /**
  69. * @return The length (the number of real values in a transform) of *this
  70. * FFT object.
  71. */
  72. int getLen() const
  73. {
  74. return( Len );
  75. }
  76. /**
  77. * Function performs in-place forward FFT.
  78. *
  79. * @param[in,out] p Pointer to data block to transform, length should be
  80. * equal to *this object's getLen().
  81. */
  82. void forward( double* const p ) const
  83. {
  84. #if R8B_FLOATFFT
  85. float* const op = (float*) p;
  86. int i;
  87. for( i = 0; i < Len; i++ )
  88. {
  89. op[ i ] = (float) p[ i ];
  90. }
  91. #endif // R8B_FLOATFFT
  92. #if R8B_IPP
  93. ippsFFTFwd_RToPerm_64f( p, p, SPtr, WorkBuffer );
  94. #elif R8B_PFFFT
  95. pffft_transform_ordered( setup, op, op, work, PFFFT_FORWARD );
  96. #elif R8B_PFFFT_DOUBLE
  97. pffftd_transform_ordered( setup, p, p, work, PFFFT_FORWARD );
  98. #else // R8B_PFFFT_DOUBLE
  99. ooura_fft :: rdft( Len, 1, p, wi.getPtr(), wd.getPtr() );
  100. #endif // R8B_IPP
  101. }
  102. /**
  103. * Function performs in-place inverse FFT.
  104. *
  105. * @param[in,out] p Pointer to data block to transform, length should be
  106. * equal to *this object's getLen().
  107. */
  108. void inverse( double* const p ) const
  109. {
  110. #if R8B_IPP
  111. ippsFFTInv_PermToR_64f( p, p, SPtr, WorkBuffer );
  112. #elif R8B_PFFFT
  113. pffft_transform_ordered( setup, (float*) p, (float*) p, work,
  114. PFFFT_BACKWARD );
  115. #elif R8B_PFFFT_DOUBLE
  116. pffftd_transform_ordered( setup, p, p, work, PFFFT_BACKWARD );
  117. #else // R8B_PFFFT_DOUBLE
  118. ooura_fft :: rdft( Len, -1, p, wi.getPtr(), wd.getPtr() );
  119. #endif // R8B_IPP
  120. #if R8B_FLOATFFT
  121. const float* const ip = (const float*) p;
  122. int i;
  123. for( i = Len - 1; i >= 0; i-- )
  124. {
  125. p[ i ] = ip[ i ];
  126. }
  127. #endif // R8B_FLOATFFT
  128. }
  129. /**
  130. * Function multiplies two complex-valued data blocks and places result in
  131. * a new data block. Length of all data blocks should be equal to *this
  132. * object's block length. Input blocks should have been produced with the
  133. * forward() function of *this object.
  134. *
  135. * @param aip1 Input data block 1.
  136. * @param aip2 Input data block 2.
  137. * @param[out] aop Output data block, should not be equal to aip1 nor
  138. * aip2.
  139. */
  140. void multiplyBlocks( const double* const aip1, const double* const aip2,
  141. double* const aop ) const
  142. {
  143. #if R8B_FLOATFFT
  144. const float* const ip1 = (const float*) aip1;
  145. const float* const ip2 = (const float*) aip2;
  146. float* const op = (float*) aop;
  147. #else // R8B_FLOATFFT
  148. const double* const ip1 = aip1;
  149. const double* const ip2 = aip2;
  150. double* const op = aop;
  151. #endif // R8B_FLOATFFT
  152. #if R8B_IPP
  153. ippsMulPerm_64f( (Ipp64f*) ip1, (Ipp64f*) ip2, (Ipp64f*) op, Len );
  154. #else // R8B_IPP
  155. op[ 0 ] = ip1[ 0 ] * ip2[ 0 ];
  156. op[ 1 ] = ip1[ 1 ] * ip2[ 1 ];
  157. int i = 2;
  158. while( i < Len )
  159. {
  160. op[ i ] = ip1[ i ] * ip2[ i ] - ip1[ i + 1 ] * ip2[ i + 1 ];
  161. op[ i + 1 ] = ip1[ i ] * ip2[ i + 1 ] + ip1[ i + 1 ] * ip2[ i ];
  162. i += 2;
  163. }
  164. #endif // R8B_IPP
  165. }
  166. /**
  167. * Function multiplies two complex-valued data blocks in-place. Length of
  168. * both data blocks should be equal to *this object's block length. Blocks
  169. * should have been produced with the forward() function of *this object.
  170. *
  171. * @param aip Input data block 1.
  172. * @param[in,out] aop Output/input data block 2.
  173. */
  174. void multiplyBlocks( const double* const aip, double* const aop ) const
  175. {
  176. #if R8B_FLOATFFT
  177. const float* const ip = (const float*) aip;
  178. float* const op = (float*) aop;
  179. float t;
  180. #else // R8B_FLOATFFT
  181. const double* const ip = aip;
  182. double* const op = aop;
  183. #if !R8B_IPP
  184. double t;
  185. #endif // !R8B_IPP
  186. #endif // R8B_FLOATFFT
  187. #if R8B_IPP
  188. ippsMulPerm_64f( (Ipp64f*) op, (Ipp64f*) ip, (Ipp64f*) op, Len );
  189. #else // R8B_IPP
  190. op[ 0 ] *= ip[ 0 ];
  191. op[ 1 ] *= ip[ 1 ];
  192. int i = 2;
  193. while( i < Len )
  194. {
  195. t = op[ i ] * ip[ i ] - op[ i + 1 ] * ip[ i + 1 ];
  196. op[ i + 1 ] = op[ i ] * ip[ i + 1 ] + op[ i + 1 ] * ip[ i ];
  197. op[ i ] = t;
  198. i += 2;
  199. }
  200. #endif // R8B_IPP
  201. }
  202. /**
  203. * Function multiplies two complex-valued data blocks in-place,
  204. * considering that the "ip" block contains "zero-phase" response. Length
  205. * of both data blocks should be equal to *this object's block length.
  206. * Blocks should have been produced with the forward() function of *this
  207. * object.
  208. *
  209. * @param aip Input data block 1, "zero-phase" response. This block should
  210. * be first transformed via the convertToZP() function.
  211. * @param[in,out] aop Output/input data block 2.
  212. */
  213. void multiplyBlocksZP( const double* const aip, double* const aop ) const
  214. {
  215. #if R8B_FLOATFFT
  216. const float* const ip = (const float*) aip;
  217. float* const op = (float*) aop;
  218. #else // R8B_FLOATFFT
  219. const double* ip = aip;
  220. double* op = aop;
  221. #endif // R8B_FLOATFFT
  222. // SIMD implementations assume that pointers are address-aligned.
  223. #if !R8B_FLOATFFT && defined( R8B_SSE2 )
  224. int c8 = Len >> 3;
  225. while( c8 != 0 )
  226. {
  227. const __m128d iv1 = _mm_load_pd( ip );
  228. const __m128d iv2 = _mm_load_pd( ip + 2 );
  229. const __m128d ov1 = _mm_load_pd( op );
  230. const __m128d ov2 = _mm_load_pd( op + 2 );
  231. _mm_store_pd( op, _mm_mul_pd( iv1, ov1 ));
  232. _mm_store_pd( op + 2, _mm_mul_pd( iv2, ov2 ));
  233. const __m128d iv3 = _mm_load_pd( ip + 4 );
  234. const __m128d ov3 = _mm_load_pd( op + 4 );
  235. const __m128d iv4 = _mm_load_pd( ip + 6 );
  236. const __m128d ov4 = _mm_load_pd( op + 6 );
  237. _mm_store_pd( op + 4, _mm_mul_pd( iv3, ov3 ));
  238. _mm_store_pd( op + 6, _mm_mul_pd( iv4, ov4 ));
  239. ip += 8;
  240. op += 8;
  241. c8--;
  242. }
  243. int c = Len & 7;
  244. while( c != 0 )
  245. {
  246. *op *= *ip;
  247. ip++;
  248. op++;
  249. c--;
  250. }
  251. #elif !R8B_FLOATFFT && defined( R8B_NEON )
  252. int c8 = Len >> 3;
  253. while( c8 != 0 )
  254. {
  255. const float64x2_t iv1 = vld1q_f64( ip );
  256. const float64x2_t iv2 = vld1q_f64( ip + 2 );
  257. const float64x2_t ov1 = vld1q_f64( op );
  258. const float64x2_t ov2 = vld1q_f64( op + 2 );
  259. vst1q_f64( op, vmulq_f64( iv1, ov1 ));
  260. vst1q_f64( op + 2, vmulq_f64( iv2, ov2 ));
  261. const float64x2_t iv3 = vld1q_f64( ip + 4 );
  262. const float64x2_t iv4 = vld1q_f64( ip + 6 );
  263. const float64x2_t ov3 = vld1q_f64( op + 4 );
  264. const float64x2_t ov4 = vld1q_f64( op + 6 );
  265. vst1q_f64( op + 4, vmulq_f64( iv3, ov3 ));
  266. vst1q_f64( op + 6, vmulq_f64( iv4, ov4 ));
  267. ip += 8;
  268. op += 8;
  269. c8--;
  270. }
  271. int c = Len & 7;
  272. while( c != 0 )
  273. {
  274. *op *= *ip;
  275. ip++;
  276. op++;
  277. c--;
  278. }
  279. #else // SIMD
  280. int i;
  281. for( i = 0; i < Len; i++ )
  282. {
  283. op[ i ] *= ip[ i ];
  284. }
  285. #endif // SIMD
  286. }
  287. /**
  288. * Function converts the specified forward-transformed block into
  289. * "zero-phase" form suitable for use with the multiplyBlocksZ() function.
  290. *
  291. * @param[in,out] ap Block to transform.
  292. */
  293. void convertToZP( double* const ap ) const
  294. {
  295. #if R8B_FLOATFFT
  296. float* const p = (float*) ap;
  297. #else // R8B_FLOATFFT
  298. double* const p = ap;
  299. #endif // R8B_FLOATFFT
  300. int i = 2;
  301. while( i < Len )
  302. {
  303. p[ i + 1 ] = p[ i ];
  304. i += 2;
  305. }
  306. }
  307. private:
  308. int LenBits; ///< Length of FFT block (expressed as Nth power of 2).
  309. ///<
  310. int Len; ///< Length of FFT block (number of real values).
  311. ///<
  312. double InvMulConst; ///< Inverse FFT multiply constant.
  313. ///<
  314. CDSPRealFFT* Next; ///< Next object in a singly-linked list.
  315. ///<
  316. #if R8B_IPP
  317. IppsFFTSpec_R_64f* SPtr; ///< Pointer to initialized data buffer
  318. ///< to be passed to IPP's FFT functions.
  319. ///<
  320. CFixedBuffer< unsigned char > SpecBuffer; ///< Working buffer.
  321. ///<
  322. CFixedBuffer< unsigned char > WorkBuffer; ///< Working buffer.
  323. ///<
  324. #elif R8B_PFFFT
  325. PFFFT_Setup* setup; ///< PFFFT setup object.
  326. ///<
  327. CFixedBuffer< float > work; ///< Working buffer.
  328. ///<
  329. #elif R8B_PFFFT_DOUBLE
  330. PFFFTD_Setup* setup; ///< PFFFTD setup object.
  331. ///<
  332. CFixedBuffer< double > work; ///< Working buffer.
  333. ///<
  334. #else // R8B_PFFFT_DOUBLE
  335. CFixedBuffer< int > wi; ///< Working buffer (ints).
  336. ///<
  337. CFixedBuffer< double > wd; ///< Working buffer (doubles).
  338. ///<
  339. #endif // R8B_IPP
  340. /**
  341. * A simple class that keeps the pointer to the object and deletes it
  342. * automatically.
  343. */
  344. class CObjKeeper
  345. {
  346. R8BNOCTOR( CObjKeeper );
  347. public:
  348. CObjKeeper()
  349. : Object( NULL )
  350. {
  351. }
  352. ~CObjKeeper()
  353. {
  354. delete Object;
  355. }
  356. CObjKeeper& operator = ( CDSPRealFFT* const aObject )
  357. {
  358. Object = aObject;
  359. return( *this );
  360. }
  361. operator CDSPRealFFT* () const
  362. {
  363. return( Object );
  364. }
  365. private:
  366. CDSPRealFFT* Object; ///< FFT object being kept.
  367. ///<
  368. };
  369. CDSPRealFFT()
  370. {
  371. }
  372. /**
  373. * Constructor initializes FFT object.
  374. *
  375. * @param aLenBits The length of FFT block (Nth power of 2), specifies the
  376. * number of real values in a block. Values from 1 to 30 inclusive are
  377. * supported.
  378. */
  379. CDSPRealFFT( const int aLenBits )
  380. : LenBits( aLenBits )
  381. , Len( 1 << aLenBits )
  382. #if R8B_IPP
  383. , InvMulConst( 1.0 / Len )
  384. #elif R8B_PFFFT
  385. , InvMulConst( 1.0 / Len )
  386. #elif R8B_PFFFT_DOUBLE
  387. , InvMulConst( 1.0 / Len )
  388. #else // R8B_PFFFT_DOUBLE
  389. , InvMulConst( 2.0 / Len )
  390. #endif // R8B_IPP
  391. {
  392. #if R8B_IPP
  393. int SpecSize;
  394. int SpecBufferSize;
  395. int BufferSize;
  396. ippsFFTGetSize_R_64f( LenBits, IPP_FFT_NODIV_BY_ANY,
  397. ippAlgHintFast, &SpecSize, &SpecBufferSize, &BufferSize );
  398. CFixedBuffer< unsigned char > InitBuffer( SpecBufferSize );
  399. SpecBuffer.alloc( SpecSize );
  400. WorkBuffer.alloc( BufferSize );
  401. ippsFFTInit_R_64f( &SPtr, LenBits, IPP_FFT_NODIV_BY_ANY,
  402. ippAlgHintFast, SpecBuffer, InitBuffer );
  403. #elif R8B_PFFFT
  404. setup = pffft_new_setup( Len, PFFFT_REAL );
  405. work.alloc( Len );
  406. #elif R8B_PFFFT_DOUBLE
  407. setup = pffftd_new_setup( Len, PFFFT_REAL );
  408. work.alloc( Len );
  409. #else // R8B_PFFFT_DOUBLE
  410. wi.alloc( (int) ceil( 2.0 + sqrt( (double) ( Len >> 1 ))));
  411. wi[ 0 ] = 0;
  412. wd.alloc( Len >> 1 );
  413. #endif // R8B_IPP
  414. }
  415. ~CDSPRealFFT()
  416. {
  417. #if R8B_PFFFT
  418. pffft_destroy_setup( setup );
  419. #elif R8B_PFFFT_DOUBLE
  420. pffftd_destroy_setup( setup );
  421. #endif // R8B_PFFFT_DOUBLE
  422. delete Next;
  423. }
  424. };
  425. /**
  426. * @brief A "keeper" class for real-valued FFT transform objects.
  427. *
  428. * Class implements "keeper" functionality for handling CDSPRealFFT objects.
  429. * The allocated FFT objects are placed on the global static list of objects
  430. * for future reuse instead of deallocation.
  431. */
  432. class CDSPRealFFTKeeper : public R8B_BASECLASS
  433. {
  434. R8BNOCTOR( CDSPRealFFTKeeper );
  435. public:
  436. CDSPRealFFTKeeper()
  437. : Object( NULL )
  438. {
  439. }
  440. /**
  441. * Function acquires FFT object with the specified block length.
  442. *
  443. * @param LenBits The length of FFT block (Nth power of 2), in the range
  444. * [1; 30] inclusive, specifies the number of real values in a FFT block.
  445. */
  446. CDSPRealFFTKeeper( const int LenBits )
  447. {
  448. Object = acquire( LenBits );
  449. }
  450. ~CDSPRealFFTKeeper()
  451. {
  452. if( Object != NULL )
  453. {
  454. release( Object );
  455. }
  456. }
  457. /**
  458. * @return Pointer to the acquired FFT object.
  459. */
  460. const CDSPRealFFT* operator -> () const
  461. {
  462. R8BASSERT( Object != NULL );
  463. return( Object );
  464. }
  465. /**
  466. * Function acquires FFT object with the specified block length. This
  467. * function can be called any number of times.
  468. *
  469. * @param LenBits The length of FFT block (Nth power of 2), in the range
  470. * [1; 30] inclusive, specifies the number of real values in a FFT block.
  471. */
  472. void init( const int LenBits )
  473. {
  474. if( Object != NULL )
  475. {
  476. if( Object -> LenBits == LenBits )
  477. {
  478. return;
  479. }
  480. release( Object );
  481. }
  482. Object = acquire( LenBits );
  483. }
  484. /**
  485. * Function releases a previously acquired FFT object.
  486. */
  487. void reset()
  488. {
  489. if( Object != NULL )
  490. {
  491. release( Object );
  492. Object = NULL;
  493. }
  494. }
  495. private:
  496. CDSPRealFFT* Object; ///< FFT object.
  497. ///<
  498. static CSyncObject StateSync; ///< FFTObjects synchronizer.
  499. ///<
  500. static CDSPRealFFT :: CObjKeeper FFTObjects[]; ///< Pool of FFT objects of
  501. ///< various lengths.
  502. ///<
  503. /**
  504. * Function acquires FFT object from the global pool.
  505. *
  506. * @param LenBits FFT block length (expressed as Nth power of 2).
  507. */
  508. CDSPRealFFT* acquire( const int LenBits )
  509. {
  510. R8BASSERT( LenBits > 0 && LenBits <= 30 );
  511. R8BSYNC( StateSync );
  512. if( FFTObjects[ LenBits ] == NULL )
  513. {
  514. return( new CDSPRealFFT( LenBits ));
  515. }
  516. CDSPRealFFT* ffto = FFTObjects[ LenBits ];
  517. FFTObjects[ LenBits ] = ffto -> Next;
  518. return( ffto );
  519. }
  520. /**
  521. * Function releases a previously acquired FFT object.
  522. *
  523. * @param ffto FFT object to release.
  524. */
  525. void release( CDSPRealFFT* const ffto )
  526. {
  527. R8BSYNC( StateSync );
  528. ffto -> Next = FFTObjects[ ffto -> LenBits ];
  529. FFTObjects[ ffto -> LenBits ] = ffto;
  530. }
  531. };
  532. /**
  533. * Function calculates the minimum-phase transform of the filter kernel, using
  534. * a discrete Hilbert transform in cepstrum domain.
  535. *
  536. * For more details, see part III.B of
  537. * http://www.hpl.hp.com/personal/Niranjan_Damera-Venkata/files/ComplexMinPhase.pdf
  538. *
  539. * @param[in,out] Kernel Filter kernel buffer.
  540. * @param KernelLen Filter kernel's length, in samples.
  541. * @param LenMult Kernel length multiplier. Used as a coefficient of the
  542. * "oversampling" in the frequency domain. Such oversampling is needed to
  543. * improve the precision of the minimum-phase transform. If the filter's
  544. * attenuation is high, this multiplier should be increased or otherwise the
  545. * required attenuation will not be reached due to "smoothing" effect of this
  546. * transform.
  547. * @param DoFinalMul "True" if the final multiplication after transform should
  548. * be performed or not. Such multiplication returns the gain of the signal to
  549. * its original value. This parameter can be set to "false" if normalization
  550. * of the resulting filter kernel is planned to be used.
  551. * @param[out] DCGroupDelay If not NULL, this variable receives group delay
  552. * at DC offset, in samples (can be a non-integer value).
  553. */
  554. inline void calcMinPhaseTransform( double* const Kernel, const int KernelLen,
  555. const int LenMult = 2, const bool DoFinalMul = true,
  556. double* const DCGroupDelay = NULL )
  557. {
  558. R8BASSERT( KernelLen > 0 );
  559. R8BASSERT( LenMult >= 2 );
  560. const int LenBits = getBitOccupancy(( KernelLen * LenMult ) - 1 );
  561. const int Len = 1 << LenBits;
  562. const int Len2 = Len >> 1;
  563. int i;
  564. CFixedBuffer< double > ip( Len );
  565. CFixedBuffer< double > ip2( Len2 + 1 );
  566. memcpy( &ip[ 0 ], Kernel, KernelLen * sizeof( ip[ 0 ]));
  567. memset( &ip[ KernelLen ], 0, ( Len - KernelLen ) * sizeof( ip[ 0 ]));
  568. CDSPRealFFTKeeper ffto( LenBits );
  569. ffto -> forward( ip );
  570. // Create the "log |c|" spectrum while saving the original power spectrum
  571. // in the "ip2" buffer.
  572. #if R8B_FLOATFFT
  573. float* const aip = (float*) &ip[ 0 ];
  574. float* const aip2 = (float*) &ip2[ 0 ];
  575. const float nzbias = 1e-35;
  576. #else // R8B_FLOATFFT
  577. double* const aip = &ip[ 0 ];
  578. double* const aip2 = &ip2[ 0 ];
  579. const double nzbias = 1e-300;
  580. #endif // R8B_FLOATFFT
  581. aip2[ 0 ] = aip[ 0 ];
  582. aip[ 0 ] = log( fabs( aip[ 0 ]) + nzbias );
  583. aip2[ Len2 ] = aip[ 1 ];
  584. aip[ 1 ] = log( fabs( aip[ 1 ]) + nzbias );
  585. for( i = 1; i < Len2; i++ )
  586. {
  587. aip2[ i ] = sqrt( aip[ i * 2 ] * aip[ i * 2 ] +
  588. aip[ i * 2 + 1 ] * aip[ i * 2 + 1 ]);
  589. aip[ i * 2 ] = log( aip2[ i ] + nzbias );
  590. aip[ i * 2 + 1 ] = 0.0;
  591. }
  592. // Convert to cepstrum and apply discrete Hilbert transform.
  593. ffto -> inverse( ip );
  594. const double m1 = ffto -> getInvMulConst();
  595. const double m2 = -m1;
  596. ip[ 0 ] = 0.0;
  597. for( i = 1; i < Len2; i++ )
  598. {
  599. ip[ i ] *= m1;
  600. }
  601. ip[ Len2 ] = 0.0;
  602. for( i = Len2 + 1; i < Len; i++ )
  603. {
  604. ip[ i ] *= m2;
  605. }
  606. // Convert Hilbert-transformed cepstrum back to the "log |c|" spectrum and
  607. // perform its exponentiation, multiplied by the power spectrum previously
  608. // saved in the "ip2" buffer.
  609. ffto -> forward( ip );
  610. aip[ 0 ] = aip2[ 0 ];
  611. aip[ 1 ] = aip2[ Len2 ];
  612. for( i = 1; i < Len2; i++ )
  613. {
  614. aip[ i * 2 + 0 ] = cos( aip[ i * 2 + 1 ]) * aip2[ i ];
  615. aip[ i * 2 + 1 ] = sin( aip[ i * 2 + 1 ]) * aip2[ i ];
  616. }
  617. ffto -> inverse( ip );
  618. if( DoFinalMul )
  619. {
  620. for( i = 0; i < KernelLen; i++ )
  621. {
  622. Kernel[ i ] = ip[ i ] * m1;
  623. }
  624. }
  625. else
  626. {
  627. memcpy( &Kernel[ 0 ], &ip[ 0 ], KernelLen * sizeof( Kernel[ 0 ]));
  628. }
  629. if( DCGroupDelay != NULL )
  630. {
  631. double tmp;
  632. calcFIRFilterResponseAndGroupDelay( Kernel, KernelLen, 0.0,
  633. tmp, tmp, *DCGroupDelay );
  634. }
  635. }
  636. } // namespace r8b
  637. #endif // VOX_CDSPREALFFT_INCLUDED