CDSPFracInterpolator.h 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180
  1. //$ nobt
  2. //$ nocpp
  3. /**
  4. * @file CDSPFracInterpolator.h
  5. *
  6. * @brief Fractional delay interpolator and filter bank classes.
  7. *
  8. * This file includes fractional delay interpolator class.
  9. *
  10. * r8brain-free-src Copyright (c) 2013-2022 Aleksey Vaneev
  11. * See the "LICENSE" file for license.
  12. */
  13. #ifndef R8B_CDSPFRACINTERPOLATOR_INCLUDED
  14. #define R8B_CDSPFRACINTERPOLATOR_INCLUDED
  15. #include "CDSPSincFilterGen.h"
  16. #include "CDSPProcessor.h"
  17. namespace r8b {
  18. #if R8B_FLTTEST
  19. extern int InterpFilterFracs; ///< Force this number of fractional filter
  20. ///< positions. -1 - use default.
  21. ///<
  22. #endif // R8B_FLTTEST
  23. /**
  24. * @brief Sinc function-based fractional delay filter bank class.
  25. *
  26. * Class implements storage and initialization of a bank of sinc-based
  27. * fractional delay filters, expressed as 0th, 1st, 2nd or 3rd order
  28. * polynomial interpolation coefficients. The filters are windowed by the
  29. * "Kaiser" power-raised window function.
  30. */
  31. class CDSPFracDelayFilterBank : public R8B_BASECLASS
  32. {
  33. R8BNOCTOR( CDSPFracDelayFilterBank );
  34. friend class CDSPFracDelayFilterBankCache;
  35. public:
  36. /**
  37. * Constructor.
  38. *
  39. * @param aFilterFracs The number of fractional delay positions to sample,
  40. * -1 - use default.
  41. * @param aElementSize The size of each filter's tap, in "double" values.
  42. * This parameter corresponds to the complexity of interpolation. 4 should
  43. * be set for 3rd order, 3 for 2nd order, 2 for linear interpolation, 1
  44. * for whole-numbered stepping.
  45. * @param aInterpPoints The number of points the interpolation is based
  46. * on. This value should not be confused with the ElementSize. Set to 2
  47. * for linear or no interpolation.
  48. * @param aReqAtten Required filter attentuation.
  49. * @param aIsThird "True" if one-third filter is required.
  50. */
  51. CDSPFracDelayFilterBank( const int aFilterFracs, const int aElementSize,
  52. const int aInterpPoints, const double aReqAtten, const bool aIsThird )
  53. : InitFilterFracs( aFilterFracs )
  54. , ElementSize( aElementSize )
  55. , InterpPoints( aInterpPoints )
  56. , ReqAtten( aReqAtten )
  57. , IsThird( aIsThird )
  58. , Next( NULL )
  59. , RefCount( 1 )
  60. {
  61. R8BASSERT( ElementSize >= 1 && ElementSize <= 4 );
  62. // Kaiser window function Params, for half and third-band.
  63. const double* const Params = getWinParams( ReqAtten, IsThird,
  64. FilterLen );
  65. FilterSize = FilterLen * ElementSize;
  66. if( InitFilterFracs == -1 )
  67. {
  68. FilterFracs = (int) ceil( pow( 6.4, ReqAtten / 50.0 ));
  69. #if R8B_FLTTEST
  70. if( InterpFilterFracs != -1 )
  71. {
  72. FilterFracs = InterpFilterFracs;
  73. }
  74. #endif // R8B_FLTTEST
  75. }
  76. else
  77. {
  78. FilterFracs = InitFilterFracs;
  79. }
  80. Table.alloc( FilterSize * ( FilterFracs + InterpPoints ));
  81. CDSPSincFilterGen sinc;
  82. sinc.Len2 = FilterLen / 2;
  83. double* p = Table;
  84. const int pc2 = InterpPoints / 2;
  85. int i;
  86. for( i = -pc2 + 1; i <= FilterFracs + pc2; i++ )
  87. {
  88. sinc.FracDelay = (double) ( FilterFracs - i ) / FilterFracs;
  89. sinc.initFrac( CDSPSincFilterGen :: wftKaiser, Params, true );
  90. sinc.generateFrac( p, &CDSPSincFilterGen :: calcWindowKaiser,
  91. ElementSize );
  92. normalizeFIRFilter( p, FilterLen, 1.0, ElementSize );
  93. p += FilterSize;
  94. }
  95. const int TablePos2 = FilterSize;
  96. const int TablePos3 = FilterSize * 2;
  97. const int TablePos4 = FilterSize * 3;
  98. const int TablePos5 = FilterSize * 4;
  99. const int TablePos6 = FilterSize * 5;
  100. const int TablePos7 = FilterSize * 6;
  101. const int TablePos8 = FilterSize * 7;
  102. double* const TableEnd = Table + ( FilterFracs + 1 ) * FilterSize;
  103. p = Table;
  104. if( InterpPoints == 8 )
  105. {
  106. if( ElementSize == 3 )
  107. {
  108. // Calculate 2nd order spline (polynomial) interpolation
  109. // coefficients using 8 points.
  110. while( p < TableEnd )
  111. {
  112. calcSpline2p8Coeffs( p, p[ 0 ], p[ TablePos2 ],
  113. p[ TablePos3 ], p[ TablePos4 ], p[ TablePos5 ],
  114. p[ TablePos6 ], p[ TablePos7 ], p[ TablePos8 ]);
  115. p += ElementSize;
  116. }
  117. #if defined( R8B_SIMD_ISH )
  118. shuffle2_3( Table, TableEnd );
  119. #endif // SIMD
  120. }
  121. else
  122. if( ElementSize == 4 )
  123. {
  124. // Calculate 3rd order spline (polynomial) interpolation
  125. // coefficients using 8 points.
  126. while( p < TableEnd )
  127. {
  128. calcSpline3p8Coeffs( p, p[ 0 ], p[ TablePos2 ],
  129. p[ TablePos3 ], p[ TablePos4 ], p[ TablePos5 ],
  130. p[ TablePos6 ], p[ TablePos7 ], p[ TablePos8 ]);
  131. p += ElementSize;
  132. }
  133. #if defined( R8B_SIMD_ISH )
  134. shuffle2_4( Table, TableEnd );
  135. #endif // SIMD
  136. }
  137. }
  138. else
  139. {
  140. if( ElementSize == 2 )
  141. {
  142. // Calculate linear interpolation coefficients.
  143. while( p < TableEnd )
  144. {
  145. p[ 1 ] = p[ TablePos2 ] - p[ 0 ];
  146. p += ElementSize;
  147. }
  148. #if defined( R8B_SIMD_ISH )
  149. shuffle2_2( Table, TableEnd );
  150. #endif // SIMD
  151. }
  152. }
  153. R8BCONSOLE( "CDSPFracDelayFilterBank: fracs=%i order=%i taps=%i "
  154. "att=%.1f third=%i\n", FilterFracs, ElementSize - 1, FilterLen,
  155. ReqAtten, (int) IsThird );
  156. }
  157. ~CDSPFracDelayFilterBank()
  158. {
  159. delete Next;
  160. }
  161. /**
  162. * Function "rounds" the specified attenuation to the nearest effective
  163. * value.
  164. *
  165. * @param[in,out] att Required filter attentuation. Will be rounded to the
  166. * nearest value.
  167. * @param aIsThird "True" if one-third filter is required.
  168. */
  169. static void roundReqAtten( double& att, const bool aIsThird )
  170. {
  171. int tmp;
  172. getWinParams( att, aIsThird, tmp );
  173. }
  174. /**
  175. * Function returns the length of the filter. Always an even number, not
  176. * less than 6.
  177. */
  178. int getFilterLen() const
  179. {
  180. return( FilterLen );
  181. }
  182. /**
  183. * Function returns the number of fractional positions sampled by the
  184. * bank.
  185. */
  186. int getFilterFracs() const
  187. {
  188. return( FilterFracs );
  189. }
  190. /**
  191. * @param i Filter index, in the range 0 to FilterFracs, inclusive.
  192. * @return Reference to the filter.
  193. */
  194. const double& operator []( const int i ) const
  195. {
  196. R8BASSERT( i >= 0 && i <= FilterFracs );
  197. return( Table[ i * FilterSize ]);
  198. }
  199. /**
  200. * This function should be called when the filter bank obtained via the
  201. * filter bank cache is no longer needed.
  202. */
  203. void unref();
  204. private:
  205. int FilterLen; ///< Filter length. Always an even number, not less than 6.
  206. ///<
  207. int FilterFracs; ///< Fractional position count.
  208. ///<
  209. int InitFilterFracs; ///< Fractional position count as supplied to the
  210. ///< constructor, may equal -1.
  211. ///<
  212. int ElementSize; ///< Filter element size.
  213. ///<
  214. int InterpPoints; ///< Interpolation points to use.
  215. ///<
  216. double ReqAtten; ///< Filter's attentuation.
  217. ///<
  218. bool IsThird; ///< "True" if one-third filter is in use.
  219. ///<
  220. int FilterSize; ///< This constant specifies the "size" of a single filter
  221. ///< in "double" elements.
  222. ///<
  223. CFixedBuffer< double > Table; ///< The table of fractional delay filters
  224. ///< for all discrete fractional x = 0..1 sample positions, and
  225. ///< interpolation coefficients.
  226. ///<
  227. CDSPFracDelayFilterBank* Next; ///< Next filter bank in cache's list.
  228. ///<
  229. int RefCount; ///< The number of references made to *this filter bank.
  230. ///< Not considered for "static" filter bank objects.
  231. ///<
  232. /**
  233. * Function returns windowing function parameters for the specified
  234. * attenuation and filter type.
  235. *
  236. * @param[in,out] att Required filter attentuation. Will be rounded to the
  237. * nearest value.
  238. * @param aIsThird "True" if one-third filter is required.
  239. * @param[out] fltlen Resulting filter length.
  240. */
  241. static const double* getWinParams( double& att, const bool aIsThird,
  242. int& fltlen )
  243. {
  244. static const int Coeffs2Base = 8;
  245. static const int Coeffs2Count = 12;
  246. static const double Coeffs2[ Coeffs2Count ][ 3 ] = {
  247. { 4.1308468534586913, 1.1752580009977263, 55.5446 }, // 0.0256
  248. { 4.4241520324148826, 1.8004881791443044, 81.4191 }, // 0.0886
  249. { 5.2615232289173663, 1.8133318236025469, 96.3392 }, // 0.0481
  250. { 5.9433751227216174, 1.8730186391986436, 111.1315 }, // 0.0264
  251. { 6.8308658290513815, 1.8549555110340281, 125.4653 }, // 0.0146
  252. { 7.6648458290312904, 1.8565766090828464, 139.7379 }, // 0.0081
  253. { 8.2038728664307605, 1.9269521820570166, 154.0532 }, // 0.0045
  254. { 8.7865150946655142, 1.9775307667441668, 168.2101 }, // 0.0025
  255. { 9.5945017884101773, 1.9718456992078597, 182.1076 }, // 0.0014
  256. { 10.5163141145985240, 1.9504067820201083, 195.5668 }, // 0.0008
  257. { 10.2382465206362470, 2.1608923446870087, 209.0610 }, // 0.0004
  258. { 10.9976060250714000, 2.1536533525688935, 222.5010 }, // 0.0003
  259. };
  260. static const int Coeffs3Base = 6;
  261. static const int Coeffs3Count = 10;
  262. static const double Coeffs3[ Coeffs3Count ][ 3 ] = {
  263. { 3.9888564562781847, 1.5869927184268915, 66.5701 }, // 0.0467
  264. { 4.6986694038145007, 1.8086068597928262, 86.4715 }, // 0.0136
  265. { 5.5995071329337822, 1.8930163360942349, 106.1195 }, // 0.0040
  266. { 6.3627287800257228, 1.9945748322093975, 125.2307 }, // 0.0012
  267. { 7.4299550711428308, 1.9893400572347544, 144.3469 }, // 0.0004
  268. { 8.0667715944075642, 2.0928201458699909, 163.4099 }, // 0.0001
  269. { 8.7469970226288822, 2.1640279784268355, 181.0694 }, // 0.0000
  270. { 10.0823430069835230, 2.0896678025321922, 199.2880 }, // 0.0000
  271. { 10.9222206090489510, 2.1221681162186004, 216.6865 }, // 0.0000
  272. { 21.2017743894772010, 1.1856768080118900, 233.9188 }, // 0.0000
  273. };
  274. const double* Params;
  275. int i = 0;
  276. if( aIsThird )
  277. {
  278. while( i != Coeffs3Count - 1 && Coeffs3[ i ][ 2 ] < att )
  279. {
  280. i++;
  281. }
  282. Params = &Coeffs3[ i ][ 0 ];
  283. att = Coeffs3[ i ][ 2 ];
  284. fltlen = Coeffs3Base + i * 2;
  285. }
  286. else
  287. {
  288. while( i != Coeffs2Count - 1 && Coeffs2[ i ][ 2 ] < att )
  289. {
  290. i++;
  291. }
  292. Params = &Coeffs2[ i ][ 0 ];
  293. att = Coeffs2[ i ][ 2 ];
  294. fltlen = Coeffs2Base + i * 2;
  295. }
  296. return( Params );
  297. }
  298. /**
  299. * Function shuffles 2 order-2 filter points for SIMD operation.
  300. *
  301. * @param p Filter table start pointer.
  302. * @param pe Filter table end pointer.
  303. */
  304. static void shuffle2_2( double* p, double* const pe )
  305. {
  306. while( p != pe )
  307. {
  308. const double t = p[ 2 ];
  309. p[ 2 ] = p[ 1 ];
  310. p[ 1 ] = t;
  311. p += 4;
  312. }
  313. }
  314. /**
  315. * Function shuffles 2 order-3 filter points for SIMD operation.
  316. *
  317. * @param p Filter table start pointer.
  318. * @param pe Filter table end pointer.
  319. */
  320. static void shuffle2_3( double* p, double* const pe )
  321. {
  322. while( p != pe )
  323. {
  324. const double t1 = p[ 1 ];
  325. const double t2 = p[ 2 ];
  326. const double t3 = p[ 3 ];
  327. const double t4 = p[ 4 ];
  328. p[ 1 ] = t3;
  329. p[ 2 ] = t1;
  330. p[ 3 ] = t4;
  331. p[ 4 ] = t2;
  332. p += 6;
  333. }
  334. }
  335. /**
  336. * Function shuffles 2 order-4 filter points for SIMD operation.
  337. *
  338. * @param p Filter table start pointer.
  339. * @param pe Filter table end pointer.
  340. */
  341. static void shuffle2_4( double* p, double* const pe )
  342. {
  343. while( p != pe )
  344. {
  345. const double t1 = p[ 1 ];
  346. const double t2 = p[ 2 ];
  347. const double t3 = p[ 3 ];
  348. const double t4 = p[ 4 ];
  349. const double t5 = p[ 5 ];
  350. const double t6 = p[ 6 ];
  351. p[ 1 ] = t4;
  352. p[ 2 ] = t1;
  353. p[ 3 ] = t5;
  354. p[ 4 ] = t2;
  355. p[ 5 ] = t6;
  356. p[ 6 ] = t3;
  357. p += 8;
  358. }
  359. }
  360. };
  361. /**
  362. * @brief Fractional delay filter cache class.
  363. *
  364. * Class implements cache storage of fractional delay filter banks.
  365. */
  366. class CDSPFracDelayFilterBankCache : public R8B_BASECLASS
  367. {
  368. R8BNOCTOR( CDSPFracDelayFilterBankCache );
  369. friend class CDSPFracDelayFilterBank;
  370. public:
  371. /**
  372. * @return The number of filters present in the cache now. This value can
  373. * be monitored for debugging "forgotten" filters.
  374. */
  375. static int getObjCount()
  376. {
  377. R8BSYNC( StateSync );
  378. return( ObjCount );
  379. }
  380. /**
  381. * Function calculates or returns reference to a previously calculated
  382. * (cached) fractional delay filter bank.
  383. *
  384. * @param aFilterFracs The number of fractional delay positions to sample,
  385. * -1 - use default.
  386. * @param aElementSize The size of each filter's tap, in "double" values.
  387. * @param aInterpPoints The number of points the interpolation is based
  388. * on.
  389. * @param ReqAtten Required filter attentuation.
  390. * @param IsThird "True" if one-third filter is required.
  391. * @param IsStatic "True" if a permanent static filter should be returned
  392. * that is never removed from the cache until application terminates.
  393. */
  394. static CDSPFracDelayFilterBank& getFilterBank( const int aFilterFracs,
  395. const int aElementSize, const int aInterpPoints,
  396. double ReqAtten, const bool IsThird, const bool IsStatic )
  397. {
  398. CDSPFracDelayFilterBank :: roundReqAtten( ReqAtten, IsThird );
  399. R8BSYNC( StateSync );
  400. if( IsStatic )
  401. {
  402. CDSPFracDelayFilterBank* CurObj = StaticObjects;
  403. while( CurObj != NULL )
  404. {
  405. if( CurObj -> InitFilterFracs == aFilterFracs &&
  406. CurObj -> ElementSize == aElementSize &&
  407. CurObj -> InterpPoints == aInterpPoints &&
  408. CurObj -> ReqAtten == ReqAtten &&
  409. CurObj -> IsThird == IsThird )
  410. {
  411. return( *CurObj );
  412. }
  413. CurObj = CurObj -> Next;
  414. }
  415. // Create a new filter bank and build it.
  416. CurObj = new CDSPFracDelayFilterBank( aFilterFracs, aElementSize,
  417. aInterpPoints, ReqAtten, IsThird );
  418. // Insert the bank at the start of the list.
  419. CurObj -> Next = StaticObjects.unkeep();
  420. StaticObjects = CurObj;
  421. return( *CurObj );
  422. }
  423. CDSPFracDelayFilterBank* PrevObj = NULL;
  424. CDSPFracDelayFilterBank* CurObj = Objects;
  425. while( CurObj != NULL )
  426. {
  427. if( CurObj -> InitFilterFracs == aFilterFracs &&
  428. CurObj -> ElementSize == aElementSize &&
  429. CurObj -> InterpPoints == aInterpPoints &&
  430. CurObj -> ReqAtten == ReqAtten &&
  431. CurObj -> IsThird == IsThird )
  432. {
  433. break;
  434. }
  435. if( CurObj -> Next == NULL && ObjCount >= R8B_FRACBANK_CACHE_MAX )
  436. {
  437. if( CurObj -> RefCount == 0 )
  438. {
  439. // Delete the last bank which is not used.
  440. PrevObj -> Next = NULL;
  441. delete CurObj;
  442. ObjCount--;
  443. }
  444. else
  445. {
  446. // Move the last bank to the top of the list since it
  447. // seems to be in use for a long time.
  448. PrevObj -> Next = NULL;
  449. CurObj -> Next = Objects.unkeep();
  450. Objects = CurObj;
  451. }
  452. CurObj = NULL;
  453. break;
  454. }
  455. PrevObj = CurObj;
  456. CurObj = CurObj -> Next;
  457. }
  458. if( CurObj != NULL )
  459. {
  460. CurObj -> RefCount++;
  461. if( PrevObj == NULL )
  462. {
  463. return( *CurObj );
  464. }
  465. // Remove the bank from the list temporarily.
  466. PrevObj -> Next = CurObj -> Next;
  467. }
  468. else
  469. {
  470. // Create a new filter bank (with RefCount == 1) and build it.
  471. CurObj = new CDSPFracDelayFilterBank( aFilterFracs, aElementSize,
  472. aInterpPoints, ReqAtten, IsThird );
  473. ObjCount++;
  474. }
  475. // Insert the bank at the start of the list.
  476. CurObj -> Next = Objects.unkeep();
  477. Objects = CurObj;
  478. return( *CurObj );
  479. }
  480. private:
  481. static CSyncObject StateSync; ///< Cache state synchronizer.
  482. ///<
  483. static CPtrKeeper< CDSPFracDelayFilterBank* > Objects; ///< The chain of
  484. ///< cached objects.
  485. ///<
  486. static CPtrKeeper< CDSPFracDelayFilterBank* > StaticObjects; ///< The
  487. ///< chain of static objects.
  488. ///<
  489. static int ObjCount; ///< The number of objects currently preset in the
  490. ///< Objects cache.
  491. ///<
  492. };
  493. // ---------------------------------------------------------------------------
  494. // CDSPFracDelayFilterBank PUBLIC
  495. // ---------------------------------------------------------------------------
  496. inline void CDSPFracDelayFilterBank :: unref()
  497. {
  498. R8BSYNC( CDSPFracDelayFilterBankCache :: StateSync );
  499. RefCount--;
  500. }
  501. /**
  502. * @param l Number 1.
  503. * @param s Number 2.
  504. * @param[out] GCD Resulting GCD.
  505. * @return "True" if the greatest common denominator of 2 numbers was
  506. * found.
  507. */
  508. inline bool findGCD( double l, double s, double& GCD )
  509. {
  510. int it = 0;
  511. while( it < 50 )
  512. {
  513. if( s <= 0.0 )
  514. {
  515. GCD = l;
  516. return( true );
  517. }
  518. const double r = l - s;
  519. l = s;
  520. s = ( r < 0.0 ? -r : r );
  521. it++;
  522. }
  523. return( false );
  524. }
  525. /**
  526. * Function evaluates source and destination sample rate ratio and returns
  527. * the required input and output stepping. Function returns "false" if
  528. * whole stepping cannot be used to perform interpolation using these sample
  529. * rates.
  530. *
  531. * @param SSampleRate Source sample rate.
  532. * @param DSampleRate Destination sample rate.
  533. * @param[out] ResInStep Resulting input step.
  534. * @param[out] ResOutStep Resulting output step.
  535. * @return "True" if stepping was acquired.
  536. */
  537. inline bool getWholeStepping( const double SSampleRate,
  538. const double DSampleRate, int& ResInStep, int& ResOutStep )
  539. {
  540. double GCD;
  541. if( !findGCD( SSampleRate, DSampleRate, GCD ) || GCD < 1.0 )
  542. {
  543. return( false );
  544. }
  545. const double InStep0 = SSampleRate / GCD;
  546. ResInStep = (int) InStep0;
  547. const double OutStep0 = DSampleRate / GCD;
  548. ResOutStep = (int) OutStep0;
  549. if( InStep0 != ResInStep || OutStep0 != ResOutStep )
  550. {
  551. return( false );
  552. }
  553. if( ResOutStep > 1500 )
  554. {
  555. // Do not allow large output stepping due to low cache
  556. // performance of large filter banks.
  557. return( false );
  558. }
  559. return( true );
  560. }
  561. /**
  562. * @brief Fractional delay filter bank-based interpolator class.
  563. *
  564. * Class implements the fractional delay interpolator. This implementation at
  565. * first puts the input signal into a ring buffer and then performs
  566. * interpolation. The interpolation is performed using sinc-based fractional
  567. * delay filters. These filters are contained in a bank, and for higher
  568. * precision they are interpolated between adjacent filters.
  569. *
  570. * To increase sample timing precision, this class uses "resettable counter"
  571. * approach. This gives zero overall sample timing error. With the
  572. * R8B_FASTTIMING configuration option enabled, the sample timing experiences
  573. * a very minor drift.
  574. */
  575. class CDSPFracInterpolator : public CDSPProcessor
  576. {
  577. public:
  578. /**
  579. * Constructor initalizes the interpolator. It is important to call the
  580. * getMaxOutLen() function afterwards to obtain the optimal output buffer
  581. * length.
  582. *
  583. * @param aSrcSampleRate Source sample rate.
  584. * @param aDstSampleRate Destination sample rate.
  585. * @param ReqAtten Required filter attentuation.
  586. * @param IsThird "True" if one-third filter is required.
  587. * @param PrevLatency Latency, in samples (any value >=0), which was left
  588. * in the output signal by a previous process. This latency will be
  589. * consumed completely.
  590. */
  591. CDSPFracInterpolator( const double aSrcSampleRate,
  592. const double aDstSampleRate, const double ReqAtten,
  593. const bool IsThird, const double PrevLatency )
  594. : SrcSampleRate( aSrcSampleRate )
  595. , DstSampleRate( aDstSampleRate )
  596. #if R8B_FASTTIMING
  597. , FracStep( aSrcSampleRate / aDstSampleRate )
  598. #endif // R8B_FASTTIMING
  599. {
  600. R8BASSERT( SrcSampleRate > 0.0 );
  601. R8BASSERT( DstSampleRate > 0.0 );
  602. R8BASSERT( PrevLatency >= 0.0 );
  603. R8BASSERT( BufLenBits >= 5 );
  604. InitFracPos = PrevLatency;
  605. Latency = (int) InitFracPos;
  606. InitFracPos -= Latency;
  607. R8BASSERT( Latency >= 0 );
  608. #if R8B_FLTTEST
  609. IsWhole = false;
  610. LatencyFrac = 0.0;
  611. FilterBank = new CDSPFracDelayFilterBank( -1, 3, 8, ReqAtten,
  612. IsThird );
  613. #else // R8B_FLTTEST
  614. IsWhole = getWholeStepping( SrcSampleRate, DstSampleRate, InStep,
  615. OutStep );
  616. if( IsWhole )
  617. {
  618. InitFracPosW = (int) ( InitFracPos * OutStep );
  619. LatencyFrac = InitFracPos - (double) InitFracPosW / OutStep;
  620. FilterBank = &CDSPFracDelayFilterBankCache :: getFilterBank(
  621. OutStep, 1, 2, ReqAtten, IsThird, false );
  622. }
  623. else
  624. {
  625. LatencyFrac = 0.0;
  626. FilterBank = &CDSPFracDelayFilterBankCache :: getFilterBank(
  627. -1, 3, 8, ReqAtten, IsThird, true );
  628. }
  629. #endif // R8B_FLTTEST
  630. FilterLen = FilterBank -> getFilterLen();
  631. fl2 = FilterLen >> 1;
  632. fll = fl2 - 1;
  633. flo = fll + fl2;
  634. flb = BufLen - fll;
  635. R8BASSERT(( 1 << BufLenBits ) >= FilterLen * 3 );
  636. static const CConvolveFn FltConvFn0[ 13 ] = {
  637. &CDSPFracInterpolator :: convolve0< 6 >,
  638. &CDSPFracInterpolator :: convolve0< 8 >,
  639. &CDSPFracInterpolator :: convolve0< 10 >,
  640. &CDSPFracInterpolator :: convolve0< 12 >,
  641. &CDSPFracInterpolator :: convolve0< 14 >,
  642. &CDSPFracInterpolator :: convolve0< 16 >,
  643. &CDSPFracInterpolator :: convolve0< 18 >,
  644. &CDSPFracInterpolator :: convolve0< 20 >,
  645. &CDSPFracInterpolator :: convolve0< 22 >,
  646. &CDSPFracInterpolator :: convolve0< 24 >,
  647. &CDSPFracInterpolator :: convolve0< 26 >,
  648. &CDSPFracInterpolator :: convolve0< 28 >,
  649. &CDSPFracInterpolator :: convolve0< 30 >
  650. };
  651. convfn = ( IsWhole ? FltConvFn0[ fl2 - 3 ] :
  652. &CDSPFracInterpolator :: convolve2 );
  653. R8BCONSOLE( "CDSPFracInterpolator: src=%.2f dst=%.2f taps=%i "
  654. "fracs=%i whole=%i third=%i step=%.6f\n", SrcSampleRate,
  655. DstSampleRate, FilterLen, ( IsWhole ? OutStep :
  656. FilterBank -> getFilterFracs() ), (int) IsWhole, (int) IsThird,
  657. aSrcSampleRate / aDstSampleRate );
  658. clear();
  659. }
  660. virtual ~CDSPFracInterpolator()
  661. {
  662. #if R8B_FLTTEST
  663. delete FilterBank;
  664. #else // R8B_FLTTEST
  665. FilterBank -> unref();
  666. #endif // R8B_FLTTEST
  667. }
  668. virtual int getLatency() const
  669. {
  670. return( 0 );
  671. }
  672. virtual double getLatencyFrac() const
  673. {
  674. return( LatencyFrac );
  675. }
  676. virtual int getMaxOutLen( const int MaxInLen ) const
  677. {
  678. R8BASSERT( MaxInLen >= 0 );
  679. return( (int) ceil( MaxInLen * DstSampleRate / SrcSampleRate ) + 1 );
  680. }
  681. virtual void clear()
  682. {
  683. LatencyLeft = Latency;
  684. BufLeft = 0;
  685. WritePos = 0;
  686. ReadPos = flb; // Set "read" position to account for filter's
  687. // latency at zero fractional delay.
  688. memset( &Buf[ ReadPos ], 0, ( BufLen - flb ) * sizeof( Buf[ 0 ]));
  689. if( IsWhole )
  690. {
  691. InPosFracW = InitFracPosW;
  692. }
  693. else
  694. {
  695. InPosFrac = InitFracPos;
  696. #if !R8B_FASTTIMING
  697. InCounter = 0;
  698. InPosInt = 0;
  699. InPosShift = InitFracPos * DstSampleRate / SrcSampleRate;
  700. #endif // !R8B_FASTTIMING
  701. }
  702. }
  703. virtual int process( double* ip, int l, double*& op0 )
  704. {
  705. R8BASSERT( l >= 0 );
  706. R8BASSERT( ip != op0 || l == 0 || SrcSampleRate > DstSampleRate );
  707. if( LatencyLeft != 0 )
  708. {
  709. if( LatencyLeft >= l )
  710. {
  711. LatencyLeft -= l;
  712. return( 0 );
  713. }
  714. l -= LatencyLeft;
  715. ip += LatencyLeft;
  716. LatencyLeft = 0;
  717. }
  718. double* op = op0;
  719. while( l > 0 )
  720. {
  721. // Add new input samples to both halves of the ring buffer.
  722. const int b = min( min( l, BufLen - WritePos ), flb - BufLeft );
  723. double* const wp1 = Buf + WritePos;
  724. memcpy( wp1, ip, b * sizeof( wp1[ 0 ]));
  725. if( WritePos < flo )
  726. {
  727. const int c = min( b, flo - WritePos );
  728. memcpy( wp1 + BufLen, wp1, c * sizeof( wp1[ 0 ]));
  729. }
  730. ip += b;
  731. WritePos = ( WritePos + b ) & BufLenMask;
  732. l -= b;
  733. BufLeft += b;
  734. // Produce as many output samples as possible.
  735. op = ( *this.*convfn )( op );
  736. }
  737. #if !R8B_FASTTIMING
  738. if( !IsWhole && InCounter > 1000 )
  739. {
  740. // Reset the interpolation position counter to achieve a
  741. // higher sample timing precision.
  742. InCounter = 0;
  743. InPosInt = 0;
  744. InPosShift = InPosFrac * DstSampleRate / SrcSampleRate;
  745. }
  746. #endif // !R8B_FASTTIMING
  747. return( (int) ( op - op0 ));
  748. }
  749. private:
  750. static const int BufLenBits = 8; ///< The length of the ring buffer,
  751. ///< expressed as Nth power of 2. This value can be reduced if it is
  752. ///< known that only short input buffers will be passed to the
  753. ///< interpolator. The minimum value of this parameter is 5, and
  754. ///< 1 << BufLenBits should be at least 3 times larger than the
  755. ///< FilterLen. However, this condition can be easily met if the input
  756. ///< signal is suitably downsampled first before the interpolation is
  757. ///< performed.
  758. ///<
  759. static const int BufLen = 1 << BufLenBits; ///< The length of the ring
  760. ///< buffer. The actual length is twice as long to allow "beyond max
  761. ///< position" positioning.
  762. ///<
  763. static const int BufLenMask = BufLen - 1; ///< Mask used for quick buffer
  764. ///< position wrapping.
  765. ///<
  766. int FilterLen; ///< Filter length, in taps. Even value.
  767. ///<
  768. int fl2; ///< Right-side (half) filter length.
  769. ///<
  770. int fll; ///< Input latency.
  771. ///<
  772. int flo; ///< Overrun length.
  773. ///<
  774. int flb; ///< Initial read position and maximal buffer write length.
  775. ///<
  776. double Buf[ BufLen + 29 ]; ///< The ring buffer, including overrun
  777. ///< protection for maximal filter length.
  778. ///<
  779. double SrcSampleRate; ///< Source sample rate.
  780. ///<
  781. double DstSampleRate; ///< Destination sample rate.
  782. ///<
  783. bool IsWhole; ///< "True" if whole-number stepping is in use.
  784. ///<
  785. int InStep; ///< Input whole-number stepping.
  786. ///<
  787. int OutStep; ///< Output whole-number stepping (corresponds to filter bank
  788. ///< size).
  789. ///<
  790. double InitFracPos; ///< Initial fractional position, in samples, in the
  791. ///< range [0; 1).
  792. ///<
  793. int InitFracPosW; ///< Initial fractional position for whole-number
  794. ///< stepping.
  795. ///<
  796. int Latency; ///< Initial latency that should be removed from the input.
  797. ///<
  798. double LatencyFrac; ///< Left-over fractional latency.
  799. ///<
  800. int BufLeft; ///< The number of samples left in the buffer to process.
  801. ///<
  802. int WritePos; ///< The current buffer write position. Incremented together
  803. ///< with the BufLeft variable.
  804. ///<
  805. int ReadPos; ///< The current buffer read position.
  806. ///<
  807. int LatencyLeft; ///< Input latency left to remove.
  808. ///<
  809. double InPosFrac; ///< Interpolation position (fractional part).
  810. ///<
  811. int InPosFracW; ///< Interpolation position (fractional part) for
  812. ///< whole-number stepping. Corresponds to the index into the filter
  813. ///< bank.
  814. ///<
  815. CDSPFracDelayFilterBank* FilterBank; ///< Filter bank in use, may be
  816. ///< whole-number stepping filter bank or static bank.
  817. ///<
  818. #if R8B_FASTTIMING
  819. double FracStep; ///< Fractional sample timing step.
  820. #else // R8B_FASTTIMING
  821. int InCounter; ///< Interpolation step counter.
  822. ///<
  823. int InPosInt; ///< Interpolation position (integer part).
  824. ///<
  825. double InPosShift; ///< Interpolation position fractional shift.
  826. ///<
  827. #endif // R8B_FASTTIMING
  828. typedef double*( CDSPFracInterpolator :: *CConvolveFn )( double* op ); ///<
  829. ///< Convolution function type.
  830. ///<
  831. CConvolveFn convfn; ///< Convolution function in use.
  832. ///<
  833. /**
  834. * Convolution function for 0th order resampling.
  835. *
  836. * @param[out] op Output buffer.
  837. * @return Advanced "op" value.
  838. * @tparam fltlen Filter length, in taps.
  839. */
  840. template< int fltlen >
  841. double* convolve0( double* op )
  842. {
  843. while( BufLeft > fl2 )
  844. {
  845. const double* const ftp = &(*FilterBank)[ InPosFracW ];
  846. const double* const rp = Buf + ReadPos;
  847. int i;
  848. #if defined( R8B_SSE2 ) && !defined( __INTEL_COMPILER )
  849. __m128d s = _mm_setzero_pd();
  850. for( i = 0; i < fltlen; i += 2 )
  851. {
  852. const __m128d m = _mm_mul_pd( _mm_load_pd( ftp + i ),
  853. _mm_loadu_pd( rp + i ));
  854. s = _mm_add_pd( s, m );
  855. }
  856. _mm_storel_pd( op, _mm_add_pd( s, _mm_shuffle_pd( s, s, 1 )));
  857. #elif defined( R8B_NEON )
  858. float64x2_t s = vdupq_n_f64( 0.0 );
  859. for( i = 0; i < fltlen; i += 2 )
  860. {
  861. s = vmlaq_f64( s, vld1q_f64( ftp + i ), vld1q_f64( rp + i ));
  862. }
  863. *op = vaddvq_f64( s );
  864. #else // SIMD
  865. double s = 0.0;
  866. for( i = 0; i < fltlen; i++ )
  867. {
  868. s += ftp[ i ] * rp[ i ];
  869. }
  870. *op = s;
  871. #endif // SIMD
  872. op++;
  873. InPosFracW += InStep;
  874. const int PosIncr = InPosFracW / OutStep;
  875. InPosFracW -= PosIncr * OutStep;
  876. ReadPos = ( ReadPos + PosIncr ) & BufLenMask;
  877. BufLeft -= PosIncr;
  878. }
  879. return( op );
  880. }
  881. /**
  882. * Convolution function for 2nd order resampling.
  883. *
  884. * @param[out] op Output buffer.
  885. * @return Advanced "op" value.
  886. */
  887. double* convolve2( double* op )
  888. {
  889. const CDSPFracDelayFilterBank& fb = *FilterBank;
  890. const int fltlen = FilterLen;
  891. while( BufLeft > fl2 )
  892. {
  893. double x = InPosFrac * fb.getFilterFracs();
  894. const int fti = (int) x; // Function table index.
  895. x -= fti; // Coefficient for interpolation between
  896. // adjacent fractional delay filters.
  897. const double x2d = x * x;
  898. const double* ftp = &fb[ fti ];
  899. const double* const rp = Buf + ReadPos;
  900. int i;
  901. #if defined( R8B_SSE2 ) && defined( R8B_SIMD_ISH )
  902. const __m128d x1 = _mm_set1_pd( x );
  903. const __m128d x2 = _mm_set1_pd( x2d );
  904. __m128d s = _mm_setzero_pd();
  905. for( i = 0; i < fltlen; i += 2 )
  906. {
  907. const __m128d ftp2 = _mm_load_pd( ftp + 2 );
  908. const __m128d xx1 = _mm_mul_pd( ftp2, x1 );
  909. const __m128d ftp4 = _mm_load_pd( ftp + 4 );
  910. const __m128d xx2 = _mm_mul_pd( ftp4, x2 );
  911. const __m128d ftp0 = _mm_load_pd( ftp );
  912. ftp += 6;
  913. const __m128d rpi = _mm_loadu_pd( rp + i );
  914. const __m128d xxs = _mm_add_pd( ftp0, _mm_add_pd( xx1, xx2 ));
  915. s = _mm_add_pd( s, _mm_mul_pd( rpi, xxs ));
  916. }
  917. _mm_storel_pd( op, _mm_add_pd( s, _mm_shuffle_pd( s, s, 1 )));
  918. #elif defined( R8B_NEON ) && defined( R8B_SIMD_ISH )
  919. const float64x2_t x1 = vdupq_n_f64( x );
  920. const float64x2_t x2 = vdupq_n_f64( x2d );
  921. float64x2_t s = vdupq_n_f64( 0.0 );
  922. for( i = 0; i < fltlen; i += 2 )
  923. {
  924. const float64x2_t ftp2 = vld1q_f64( ftp + 2 );
  925. const float64x2_t xx1 = vmulq_f64( ftp2, x1 );
  926. const float64x2_t ftp4 = vld1q_f64( ftp + 4 );
  927. const float64x2_t xx2 = vmulq_f64( ftp4, x2 );
  928. const float64x2_t ftp0 = vld1q_f64( ftp );
  929. ftp += 6;
  930. const float64x2_t rpi = vld1q_f64( rp + i );
  931. const float64x2_t xxs = vaddq_f64( ftp0,
  932. vaddq_f64( xx1, xx2 ));
  933. s = vmlaq_f64( s, rpi, xxs );
  934. }
  935. *op = vaddvq_f64( s );
  936. #else // SIMD
  937. double s = 0.0;
  938. for( i = 0; i < fltlen; i++ )
  939. {
  940. s += ( ftp[ 0 ] + ftp[ 1 ] * x + ftp[ 2 ] * x2d ) * rp[ i ];
  941. ftp += 3;
  942. }
  943. *op = s;
  944. #endif // SIMD
  945. op++;
  946. #if R8B_FASTTIMING
  947. InPosFrac += FracStep;
  948. const int PosIncr = (int) InPosFrac;
  949. InPosFrac -= PosIncr;
  950. #else // R8B_FASTTIMING
  951. InCounter++;
  952. const double NextInPos = ( InCounter + InPosShift ) *
  953. SrcSampleRate / DstSampleRate;
  954. const int NextInPosInt = (int) NextInPos;
  955. const int PosIncr = NextInPosInt - InPosInt;
  956. InPosInt = NextInPosInt;
  957. InPosFrac = NextInPos - NextInPosInt;
  958. #endif // R8B_FASTTIMING
  959. ReadPos = ( ReadPos + PosIncr ) & BufLenMask;
  960. BufLeft -= PosIncr;
  961. }
  962. return( op );
  963. }
  964. };
  965. // ---------------------------------------------------------------------------
  966. } // namespace r8b
  967. #endif // R8B_CDSPFRACINTERPOLATOR_INCLUDED