You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CDSPFracInterpolator.h 16KB

3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. //$ nocpp
  2. /**
  3. * @file CDSPFracInterpolator.h
  4. *
  5. * @brief Fractional delay interpolator and filter bank classes.
  6. *
  7. * This file includes fractional delay interpolator class.
  8. *
  9. * r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
  10. * See the "License.txt" file for license.
  11. */
  12. #ifndef R8B_CDSPFRACINTERPOLATOR_INCLUDED
  13. #define R8B_CDSPFRACINTERPOLATOR_INCLUDED
  14. #include "CDSPSincFilterGen.h"
  15. #include "CDSPProcessor.h"
  16. namespace r8b
  17. {
  18. /**
  19. * @brief Sinc function-based fractional delay filter bank class.
  20. *
  21. * Class implements storage and initialization of a bank of sinc-based
  22. * fractional delay filters, expressed as 1st, 2nd or 3rd order polynomial
  23. * interpolation coefficients. The filters are windowed by either the "Vaneev"
  24. * or "Kaiser" power-raised window function. The FilterLen and FilterFracs
  25. * parameters can be varied freely without breaking the resampler.
  26. *
  27. * @param FilterLen Specifies the number of samples (taps) each fractional
  28. * delay filter should have. This must be an even value, the minimal value for
  29. * FilterLen is 6, the maximal value is 30. To achieve a higher resampling
  30. * precision, the oversampling should be used in the first place instead of
  31. * using a higher FilterLen value. The lower this value is the lower the
  32. * signal-to-noise performance of the interpolator will be. Each FilterLen
  33. * decrease by 2 decreases SNR by approximately 12 to 14 decibel.
  34. * @param FilterFracs The number of fractional delay positions to sample. For
  35. * a high signal-to-noise ratio this has to be a larger value. The larger the
  36. * FilterLen is the larger the FilterFracs should be. Approximate FilterLen to
  37. * FilterFracs correspondence (for 2nd order interpolation only): 6:11, 8:17,
  38. * 10:23, 12:41, 14:67, 16:97, 18:137, 20:211, 22:353, 24:673, 26:1051,
  39. * 28:1733, 30:2833. The FilterFracs can be considerably reduced with 3rd
  40. * order interpolation in use. In order to get consistent results when
  41. * resampling to/from different sample rates, it is suggested to set this
  42. * parameter to a suitable prime number.
  43. * @param ElementSize The size of each filter's tap, in "double" values. This
  44. * parameter corresponds to the complexity of interpolation. 4 should be set
  45. * for 3rd order, 3 for 2nd order, 2 for linear interpolation.
  46. * @param InterpPoints The number of points the interpolation is based on.
  47. * This value should not be confused with the ElementSize. Set to 2 for linear
  48. * interpolation.
  49. */
  50. template <int FilterLen, int FilterFracs, int ElementSize, int InterpPoints>
  51. class CDSPFracDelayFilterBank : public R8B_BASECLASS
  52. {
  53. public:
  54. CDSPFracDelayFilterBank()
  55. {
  56. R8BASSERT(FilterLen >= 6);
  57. R8BASSERT(FilterLen <= 30);
  58. R8BASSERT(( FilterLen & 1 ) == 0);
  59. R8BASSERT(FilterFracs > 0);
  60. R8BASSERT(ElementSize >= 2 && ElementSize <= 4);
  61. R8BASSERT(InterpPoints == 2 || InterpPoints == 8);
  62. calculate();
  63. }
  64. /**
  65. * Function calculates the filter bank.
  66. *
  67. * @param Params Window function's parameters. If NULL then the built-in
  68. * table values for the current FilterLen will be used.
  69. */
  70. void calculate(const double* const Params = nullptr)
  71. {
  72. CDSPSincFilterGen sinc;
  73. sinc.Len2 = FilterLen / 2;
  74. double* p = Table;
  75. const int pc2 = InterpPoints / 2;
  76. int i;
  77. if (FilterLen <= 20)
  78. {
  79. for (i = -pc2 + 1; i <= FilterFracs + pc2; ++i)
  80. {
  81. sinc.FracDelay = double(FilterFracs - i) / FilterFracs;
  82. sinc.initFrac(CDSPSincFilterGen::wftVaneev, Params);
  83. sinc.generateFrac(p, &CDSPSincFilterGen::calcWindowVaneev,
  84. ElementSize);
  85. normalizeFIRFilter(p, FilterLen, 1.0, ElementSize);
  86. p += FilterSize;
  87. }
  88. }
  89. else
  90. {
  91. for (i = -pc2 + 1; i <= FilterFracs + pc2; ++i)
  92. {
  93. sinc.FracDelay = double(FilterFracs - i) / FilterFracs;
  94. sinc.initFrac(CDSPSincFilterGen::wftKaiser, Params, true);
  95. sinc.generateFrac(p, &CDSPSincFilterGen::calcWindowKaiser,
  96. ElementSize);
  97. normalizeFIRFilter(p, FilterLen, 1.0, ElementSize);
  98. p += FilterSize;
  99. }
  100. }
  101. const int TablePos2 = FilterSize;
  102. const int TablePos3 = FilterSize * 2;
  103. const int TablePos4 = FilterSize * 3;
  104. const int TablePos5 = FilterSize * 4;
  105. const int TablePos6 = FilterSize * 5;
  106. const int TablePos7 = FilterSize * 6;
  107. const int TablePos8 = FilterSize * 7;
  108. double* const TableEnd = Table + (FilterFracs + 1) * FilterSize;
  109. p = Table;
  110. if (InterpPoints == 8)
  111. {
  112. if (ElementSize == 3)
  113. {
  114. // Calculate 2nd order spline (polynomial) interpolation
  115. // coefficients using 8 points.
  116. while (p < TableEnd)
  117. {
  118. calcSpline2p8Coeffs(p, p[0], p[TablePos2],
  119. p[TablePos3], p[TablePos4], p[TablePos5],
  120. p[TablePos6], p[TablePos7], p[TablePos8]);
  121. p += ElementSize;
  122. }
  123. }
  124. else if (ElementSize == 4)
  125. {
  126. // Calculate 3rd order spline (polynomial) interpolation
  127. // coefficients using 8 points.
  128. while (p < TableEnd)
  129. {
  130. calcSpline3p8Coeffs(p, p[0], p[TablePos2],
  131. p[TablePos3], p[TablePos4], p[TablePos5],
  132. p[TablePos6], p[TablePos7], p[TablePos8]);
  133. p += ElementSize;
  134. }
  135. }
  136. }
  137. else
  138. {
  139. // Calculate linear interpolation coefficients.
  140. while (p < TableEnd)
  141. {
  142. p[1] = p[TablePos2] - p[0];
  143. p += ElementSize;
  144. }
  145. }
  146. }
  147. /**
  148. * @param i Filter index, in the range 0 to FilterFracs, inclusive.
  149. * @return Reference to the filter.
  150. */
  151. const double& operator [](const int i) const
  152. {
  153. R8BASSERT(i >= 0 && i <= FilterFracs);
  154. return (Table[i * FilterSize]);
  155. }
  156. private:
  157. static const int FilterSize = FilterLen * ElementSize; ///< This constant
  158. ///< specifies the "size" of a single filter in "double" elements.
  159. ///<
  160. double Table[ FilterSize * (FilterFracs + InterpPoints)]; ///< The
  161. ///< table of fractional delay filters for all discrete fractional
  162. ///< x = 0..1 sample positions, and interpolation coefficients.
  163. ///<
  164. };
  165. /**
  166. * @brief Fractional delay filter bank-based interpolator class.
  167. *
  168. * Class implements the fractional delay interpolator. This implementation at
  169. * first puts the input signal into a ring buffer and then performs
  170. * interpolation. The interpolation is performed using sinc-based fractional
  171. * delay filters. These filters are contained in a bank, and for higher
  172. * precision they are interpolated between adjacent filters.
  173. *
  174. * To increase sample timing precision, this class uses "resettable counter"
  175. * approach. This gives less than "1 per 100 billion" sample timing error when
  176. * converting 44100 to 48000 sample rate.
  177. *
  178. * VERY IMPORTANT: the interpolation step should not exceed FilterLen / 2 + 1
  179. * samples or the algorithm in its current form will fail. However, this
  180. * condition can be easily met if the input signal is suitably downsampled
  181. * first before the interpolation is performed.
  182. *
  183. * @param FilterLen Specifies the number of samples (taps) each fractional
  184. * delay filter should have. See the r8b::CDSPFracDelayFilterBank class for
  185. * more details.
  186. * @param FilterFracs The number of fractional delay positions to sample. See
  187. * the r8b::CDSPFracDelayFilterBank class for more details.
  188. */
  189. template <int FilterLen, int FilterFracs>
  190. class CDSPFracInterpolator final : public CDSPProcessor
  191. {
  192. public:
  193. /**
  194. * Constructor initalizes the interpolator. It is important to call the
  195. * getMaxOutLen() function afterwards to obtain the optimal output buffer
  196. * length.
  197. *
  198. * @param aSrcSampleRate Source sample rate.
  199. * @param aDstSampleRate Destination sample rate.
  200. * @param aInitFracPos Initial fractional position, in samples, in the
  201. * range [0; 1). A non-zero value can be specified to remove the
  202. * fractional delay introduced by a minimum-phase filter. This value is
  203. * usually equal to the CDSPBlockConvolver.getLatencyFrac() value.
  204. */
  205. CDSPFracInterpolator(const double aSrcSampleRate,
  206. const double aDstSampleRate, const double aInitFracPos)
  207. : SrcSampleRate(aSrcSampleRate)
  208. , DstSampleRate(aDstSampleRate)
  209. , InitFracPos(aInitFracPos)
  210. {
  211. R8BASSERT(SrcSampleRate > 0.0);
  212. R8BASSERT(DstSampleRate > 0.0);
  213. R8BASSERT(InitFracPos >= 0.0 && InitFracPos < 1.0);
  214. R8BASSERT(BufLenBits >= 5);
  215. R8BASSERT(( 1 << BufLenBits ) >= FilterLen * 3);
  216. clear();
  217. }
  218. int getLatency() const override { return (0); }
  219. double getLatencyFrac() const override { return (0.0); }
  220. int getInLenBeforeOutStart(const int NextInLen) const override { return (FilterLenD2 + NextInLen); }
  221. int getMaxOutLen(const int MaxInLen) const override
  222. {
  223. R8BASSERT(MaxInLen >= 0);
  224. return ((int)ceil(MaxInLen * DstSampleRate / SrcSampleRate) + 1);
  225. }
  226. /**
  227. * Function changes the destination sample rate "on the fly". Note that
  228. * the getMaxOutLen() function may needed to be called after calling this
  229. * function as the maximal number of output samples produced by the
  230. * interpolator depends on the destination sample rate.
  231. *
  232. * It can be a useful approach to construct *this object passing the
  233. * maximal possible destination sample rate to the constructor, obtaining
  234. * the getMaxOutLen() value and then setting the destination sample rate
  235. * to whatever lower value is needed.
  236. *
  237. * It is advisable to change the sample rate in small increments, and as
  238. * rarely as possible: e.g. every several samples.
  239. *
  240. * @param NewDstSampleRate New destination sample rate.
  241. */
  242. void setDstSampleRate(const double NewDstSampleRate)
  243. {
  244. R8BASSERT(DstSampleRate > 0.0);
  245. DstSampleRate = NewDstSampleRate;
  246. InCounter = 0;
  247. InPosInt = 0;
  248. InPosShift = InPosFrac;
  249. }
  250. /**
  251. * Function clears (resets) the state of *this object and returns it to
  252. * the state after construction. All input data accumulated in the
  253. * internal buffer so far will be discarded.
  254. *
  255. * Note that the destination sample rate will remain unchanged, even if it
  256. * was changed since the time of *this object's construction.
  257. */
  258. void clear() override
  259. {
  260. BufLeft = 0;
  261. WritePos = 0;
  262. ReadPos = BufLen - FilterLenD2Minus1; // Set "read" position to
  263. // account for filter's latency at zero fractional delay which
  264. // equals to FilterLenD2Minus1.
  265. memset(&Buf[ReadPos], 0, FilterLenD2Minus1 * sizeof(double));
  266. InCounter = 0;
  267. InPosInt = 0;
  268. InPosFrac = InitFracPos;
  269. InPosShift = InitFracPos;
  270. }
  271. int process(double* ip, int l, double*& op0) override
  272. {
  273. R8BASSERT(l >= 0);
  274. R8BASSERT(ip != op0 || l == 0 || SrcSampleRate > DstSampleRate);
  275. double* op = op0;
  276. while (l > 0)
  277. {
  278. // Add new input samples to both halves of the ring buffer.
  279. const int b = min(min(l, BufLen - WritePos),
  280. BufLeftMax - BufLeft);
  281. double* const wp1 = Buf + WritePos;
  282. double* const wp2 = wp1 + BufLen;
  283. int i;
  284. for (i = 0; i < b; ++i)
  285. {
  286. wp1[i] = ip[i];
  287. wp2[i] = ip[i];
  288. }
  289. ip += b;
  290. WritePos = (WritePos + b) & BufLenMask;
  291. l -= b;
  292. BufLeft += b;
  293. // Produce as many output samples as possible.
  294. while (BufLeft >= FilterLenD2Plus1)
  295. {
  296. double x = InPosFrac * FilterFracs;
  297. const int fti = (int)x; // Function table index.
  298. x -= fti; // Coefficient for interpolation between adjacent
  299. // fractional delay filters.
  300. const double x2 = x * x;
  301. const double* const ftp = &FilterBank[fti];
  302. const double* const rp = Buf + ReadPos;
  303. double s = 0.0;
  304. int ii = 0;
  305. #if R8B_FLTTEST
  306. const double x3 = x2 * x;
  307. #endif // R8B_FLTTEST
  308. for (i = 0; i < FilterLen; ++i)
  309. {
  310. #if !R8B_FLTTEST
  311. s += (ftp[ii] + ftp[ii + 1] * x +
  312. ftp[ii + 2] * x2) * rp[i];
  313. #else // !R8B_FLTTEST
  314. s += ( ftp[ ii ] + ftp[ ii + 1 ] * x +
  315. ftp[ ii + 2 ] * x2 + ftp[ ii + 3 ] * x3 ) * rp[ i ];
  316. #endif // !R8B_FLTTEST
  317. ii += FilterElementSize;
  318. }
  319. *op = s;
  320. op++;
  321. InCounter++;
  322. const double NextInPos =
  323. InCounter * SrcSampleRate / DstSampleRate + InPosShift;
  324. const int NextInPosInt = (int)NextInPos;
  325. const int PosIncr = NextInPosInt - InPosInt;
  326. InPosInt = NextInPosInt;
  327. InPosFrac = NextInPos - NextInPosInt;
  328. ReadPos = (ReadPos + PosIncr) & BufLenMask;
  329. BufLeft -= PosIncr;
  330. }
  331. }
  332. if (InCounter > 1000)
  333. {
  334. // Reset the interpolation position counter to achieve a higher
  335. // sample timing precision.
  336. InCounter = 0;
  337. InPosInt = 0;
  338. InPosShift = InPosFrac;
  339. }
  340. return (int(op - op0));
  341. }
  342. private:
  343. #if !R8B_FLTTEST
  344. static const int FilterElementSize = 3; ///< The number of "doubles" a
  345. ///< single filter tap consists of (includes interpolation
  346. ///< coefficients).
  347. ///<
  348. #else // !R8B_FLTTEST
  349. static const int FilterElementSize = 4; ///< The number of "doubles" a
  350. ///< single filter tap consists of (includes interpolation
  351. ///< coefficients). During filter testing a higher precision
  352. ///< interpolation is used.
  353. ///<
  354. #endif // !R8B_FLTTEST
  355. static const int FilterLenD2 = FilterLen >> 1; ///< = FilterLen / 2.
  356. ///<
  357. static const int FilterLenD2Minus1 = FilterLenD2 - 1; ///< =
  358. ///< FilterLen / 2 - 1. This value also equals to filter's latency in
  359. ///< samples (taps).
  360. ///<
  361. static const int FilterLenD2Plus1 = FilterLenD2 + 1; ///< =
  362. ///< FilterLen / 2 + 1.
  363. ///<
  364. static const int BufLenBits = 8; ///< The length of the ring buffer,
  365. ///< expressed as Nth power of 2. This value can be reduced if it is
  366. ///< known that only short input buffers will be passed to the
  367. ///< interpolator. The minimum value of this parameter is 5, and
  368. ///< 1 << BufLenBits should be at least 3 times larger than the
  369. ///< FilterLen.
  370. ///<
  371. static const int BufLen = 1 << BufLenBits; ///< The length of the ring
  372. ///< buffer. The actual length is twice as long to allow "beyond max
  373. ///< position" positioning.
  374. ///<
  375. static const int BufLenMask = BufLen - 1; ///< Mask used for quick buffer
  376. ///< position wrapping.
  377. ///<
  378. static const int BufLeftMax = BufLen - FilterLenD2Minus1; ///< The number
  379. ///< of new samples that the ring buffer can hold at most. The
  380. ///< remaining FilterLenD2Minus1 samples hold "previous" input samples
  381. ///< for the filter.
  382. ///<
  383. double Buf[ BufLen * 2 ]; ///< The ring buffer.
  384. ///<
  385. double SrcSampleRate = 0; ///< Source sample rate.
  386. ///<
  387. double DstSampleRate = 0; ///< Destination sample rate.
  388. ///<
  389. double InitFracPos = 0; ///< Initial fractional position, in samples, in the
  390. ///< range [0; 1).
  391. ///<
  392. int BufLeft = 0; ///< The number of samples left in the buffer to process.
  393. ///< When this value is below FilterLenD2Plus1, the interpolation
  394. ///< cycle ends.
  395. ///<
  396. int WritePos = 0; ///< The current buffer write position. Incremented together
  397. ///< with the BufLeft variable.
  398. ///<
  399. int ReadPos = 0; ///< The current buffer read position.
  400. ///<
  401. int InCounter = 0; ///< Interpolation step counter.
  402. ///<
  403. int InPosInt = 0; ///< Interpolation position (integer part).
  404. ///<
  405. double InPosFrac = 0; ///< Interpolation position (fractional part).
  406. ///<
  407. double InPosShift = 0; ///< Interpolation position fractional shift.
  408. ///<
  409. #if !R8B_FLTTEST
  410. static const CDSPFracDelayFilterBank<FilterLen, FilterFracs, 3,
  411. 8> FilterBank; ///< Filter bank object, defined statically if no
  412. ///< filter test takes place.
  413. ///<
  414. #else // !R8B_FLTTEST
  415. public:
  416. CDSPFracDelayFilterBank< FilterLen, FilterFracs, 4, 8 > FilterBank; ///<
  417. ///< Filter bank object, defined as a member variable to allow for
  418. ///< recalculation.
  419. ///<
  420. #endif // !R8B_FLTTEST
  421. };
  422. // ---------------------------------------------------------------------------
  423. #if !R8B_FLTTEST
  424. template <int FilterLen, int FilterFracs>
  425. const CDSPFracDelayFilterBank<FilterLen, FilterFracs, 3, 8>
  426. CDSPFracInterpolator<FilterLen, FilterFracs>::FilterBank;
  427. #endif // !R8B_FLTTEST
  428. // ---------------------------------------------------------------------------
  429. } // namespace r8b
  430. #endif // R8B_CDSPFRACINTERPOLATOR_INCLUDED