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.

CDSPFIRFilter.h 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. //$ nocpp
  2. /**
  3. * @file CDSPFIRFilter.h
  4. *
  5. * @brief FIR filter generator and filter cache classes.
  6. *
  7. * This file includes low-pass FIR filter generator and filter cache.
  8. *
  9. * r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
  10. * See the "License.txt" file for license.
  11. */
  12. #ifndef R8B_CDSPFIRFILTER_INCLUDED
  13. #define R8B_CDSPFIRFILTER_INCLUDED
  14. #include "CDSPSincFilterGen.h"
  15. #include "CDSPRealFFT.h"
  16. namespace r8b
  17. {
  18. /**
  19. * Enumeration of filter's phase responses.
  20. */
  21. enum EDSPFilterPhaseResponse
  22. {
  23. fprLinearPhase = 0 ///< Linear-phase response. Features a linear-phase
  24. ///< high-latency response, with the latency expressed as integer
  25. ///< value.
  26. // fprMinPhase ///< Minimum-phase response. Features a minimal latency
  27. ///< response, but the response's phase is non-linear. The latency is
  28. ///< usually expressed as non-integer value, and usually is small, but
  29. ///< is never equal to zero. The minimum-phase filter is transformed
  30. ///< from the linear-phase filter. The transformation has precision
  31. ///< limits which may skew both the -3 dB point and attenuation of the
  32. ///< filter being transformed: as it was measured, the skew happens
  33. ///< purely at random, and in most cases it is within tolerable range.
  34. ///< In a small (1%) random subset of cases the skew is bigger and
  35. ///< cannot be predicted.
  36. };
  37. /**
  38. * @brief Calculation and storage class for FIR filters.
  39. *
  40. * Class that implements calculation and storing of a FIR filter (currently
  41. * contains low-pass filter calculation routine designed for sample rate
  42. * conversion). Objects of this class cannot be created directly, but can be
  43. * obtained via the CDSPFilterCache::getLPFilter() static function.
  44. */
  45. class CDSPFIRFilter : public R8B_BASECLASS
  46. {
  47. R8BNOCTOR(CDSPFIRFilter)
  48. friend class CDSPFIRFilterCache;
  49. public:
  50. ~CDSPFIRFilter()
  51. {
  52. R8BASSERT(RefCount == 0);
  53. delete Next;
  54. }
  55. /**
  56. * @return The minimal allowed low-pass filter's transition band, in percent.
  57. */
  58. static double getLPMinTransBand() { return (0.5); }
  59. /**
  60. * @return The maximal allowed low-pass filter's transition band, in percent.
  61. */
  62. static double getLPMaxTransBand() { return (45.0); }
  63. /**
  64. * @return The minimal allowed low-pass filter's stop-band attenuation, in decibel.
  65. */
  66. static double getLPMinAtten() { return (49.0); }
  67. /**
  68. * @return The maximal allowed low-pass filter's stop-band attenuation, in decibel.
  69. */
  70. static double getLPMaxAtten() { return (218.0); }
  71. /**
  72. * @return "True" if kernel block of *this filter has zero-phase response.
  73. */
  74. bool isZeroPhase() const { return (IsZeroPhase); }
  75. /**
  76. * @return Filter's latency, in samples (integer part).
  77. */
  78. int getLatency() const { return (Latency); }
  79. /**
  80. * @return Filter's latency, in samples (fractional part). Always zero for linear-phase filters.
  81. */
  82. double getLatencyFrac() const { return (LatencyFrac); }
  83. /**
  84. * @return Filter kernel length, in samples. Not to be confused with the block length.
  85. */
  86. int getKernelLen() const { return (KernelLen); }
  87. /**
  88. * @return Filter's block length, espressed as Nth power of 2. The actual length is twice as large due to zero-padding.
  89. */
  90. int getBlockLenBits() const { return (BlockLenBits); }
  91. /**
  92. * @return Filter's kernel block, in complex-numbered form obtained via
  93. * the CDSPRealFFT::forward() function call, zero-padded, gain-adjusted
  94. * with the CDSPRealFFT::getInvMulConst() * ReqGain constant, immediately
  95. * suitable for convolution. Kernel block may have "zero-phase" response,
  96. * depending on the isZeroPhase() function's result.
  97. */
  98. const double* getKernelBlock() const { return (KernelBlock); }
  99. /**
  100. * This function should be called when the filter obtained via the
  101. * filter cache is no longer needed.
  102. */
  103. void unref();
  104. private:
  105. double ReqNormFreq = 0; ///< Required normalized frequency, 0 to 1 inclusive.
  106. double ReqTransBand = 0; ///< Required transition band in percent, as passed by the user.
  107. double ReqAtten = 0; ///< Required stop-band attenuation in decibel, as passed by the user (positive value).
  108. EDSPFilterPhaseResponse ReqPhase = fprLinearPhase; ///< Required filter's phase response.
  109. double ReqGain = 0; ///< Required overall filter's gain.
  110. CDSPFIRFilter* Next = nullptr; ///< Next FIR filter in cache's list.
  111. int RefCount = 1; ///< The number of references made to *this FIR filter.
  112. bool IsZeroPhase = false; ///< "True" if kernel block of *this filter has zero-phase response.
  113. int Latency = 0; ///< Filter's latency in samples (integer part).
  114. double LatencyFrac = 0; ///< Filter's latency in samples (fractional part).
  115. int KernelLen = 0; ///< Filter kernel length, in samples.
  116. int BlockLenBits =
  117. 0; ///< Block length used to store *this FIR filter, expressed as Nth power of 2. This value is used directly by the convolver.
  118. CFixedBuffer<double>
  119. KernelBlock; ///< FIR filter buffer, capacity equals to 1 << ( BlockLenBits + 1 ). Second part of the buffer contains zero-padding to allow alias-free convolution.
  120. CDSPFIRFilter() { }
  121. /**
  122. * Function builds filter kernel based on the "Req" parameters.
  123. *
  124. * @param ExtAttenCorrs External attentuation correction table, for
  125. * internal use.
  126. */
  127. void buildLPFilter(const double* const ExtAttenCorrs)
  128. {
  129. const double tb = ReqTransBand * 0.01;
  130. double fo1;
  131. double hl;
  132. double atten = -ReqAtten;
  133. if (tb >= 0.25)
  134. {
  135. if (ReqAtten >= 117.0) { atten -= 1.60; }
  136. else if (ReqAtten >= 60.0) { atten -= 1.91; }
  137. else { atten -= 2.25; }
  138. }
  139. else if (tb >= 0.10)
  140. {
  141. if (ReqAtten >= 117.0) { atten -= 0.69; }
  142. else if (ReqAtten >= 60.0) { atten -= 0.73; }
  143. else { atten -= 1.13; }
  144. }
  145. else
  146. {
  147. if (ReqAtten >= 117.0) { atten -= 0.21; }
  148. else if (ReqAtten >= 60.0) { atten -= 0.25; }
  149. else { atten -= 0.36; }
  150. }
  151. static const int AttenCorrCount = 264;
  152. static const double AttenCorrMin = 49.0;
  153. static const double AttenCorrDiff = 176.25;
  154. int AttenCorr = (int)floor((-atten - AttenCorrMin) * AttenCorrCount / AttenCorrDiff + 0.5);
  155. AttenCorr = min(AttenCorrCount, max(0, AttenCorr));
  156. if (ExtAttenCorrs != nullptr) { atten -= ExtAttenCorrs[AttenCorr]; }
  157. else if (tb >= 0.25)
  158. {
  159. static const double AttenCorrScale = 101.0;
  160. static const signed char AttenCorrs[] = {
  161. -127, -127, -125, -125, -122, -119, -115, -110, -104, -97,
  162. -91, -82, -75, -24, -16, -6, 4, 14, 24, 29, 30, 32, 37, 44,
  163. 51, 57, 63, 67, 65, 50, 53, 56, 58, 60, 63, 64, 66, 68, 74,
  164. 77, 78, 78, 78, 79, 79, 60, 60, 60, 61, 59, 52, 47, 41, 36,
  165. 30, 24, 17, 9, 0, -8, -10, -11, -14, -13, -18, -25, -31, -38,
  166. -44, -50, -57, -63, -68, -74, -81, -89, -96, -101, -104, -107,
  167. -109, -110, -86, -84, -85, -82, -80, -77, -73, -67, -62, -55,
  168. -48, -42, -35, -30, -20, -11, -2, 5, 6, 6, 7, 11, 16, 21, 26,
  169. 34, 41, 46, 49, 52, 55, 56, 48, 49, 51, 51, 52, 52, 52, 52,
  170. 52, 51, 51, 50, 47, 47, 50, 48, 46, 42, 38, 35, 31, 27, 24,
  171. 20, 16, 12, 11, 12, 10, 8, 4, -1, -6, -11, -16, -19, -17, -21,
  172. -24, -27, -32, -34, -37, -38, -40, -41, -40, -40, -42, -41,
  173. -44, -45, -43, -41, -34, -31, -28, -24, -21, -18, -14, -10,
  174. -5, -1, 2, 5, 8, 7, 4, 3, 2, 2, 4, 6, 8, 9, 9, 10, 10, 10, 10,
  175. 9, 8, 9, 11, 14, 13, 12, 11, 10, 8, 7, 6, 5, 3, 2, 2, -1, -1,
  176. -3, -3, -4, -4, -5, -4, -6, -7, -9, -5, -1, -1, 0, 1, 0, -2,
  177. -3, -4, -5, -5, -8, -13, -13, -13, -12, -13, -12, -11, -11,
  178. -9, -8, -7, -5, -3, -1, 2, 4, 6, 9, 10, 11, 14, 18, 21, 24,
  179. 27, 30, 34, 37, 37, 39, 40
  180. };
  181. atten -= AttenCorrs[AttenCorr] / AttenCorrScale;
  182. }
  183. else if (tb >= 0.10)
  184. {
  185. static const double AttenCorrScale = 210.0;
  186. static const signed char AttenCorrs[] = {
  187. -113, -118, -122, -125, -126, -97, -95, -92, -92, -89, -82,
  188. -75, -69, -48, -42, -36, -30, -22, -14, -5, -2, 1, 6, 13, 22,
  189. 28, 35, 41, 48, 55, 56, 56, 61, 65, 71, 77, 81, 83, 85, 85,
  190. 74, 74, 73, 72, 71, 70, 68, 64, 59, 56, 49, 52, 46, 42, 36,
  191. 32, 26, 20, 13, 7, -2, -6, -10, -15, -20, -27, -33, -38, -44,
  192. -43, -48, -53, -57, -63, -69, -73, -75, -79, -81, -74, -76,
  193. -77, -77, -78, -81, -80, -80, -78, -76, -65, -62, -59, -56,
  194. -51, -48, -44, -38, -33, -25, -19, -13, -5, -1, 2, 7, 13, 17,
  195. 21, 25, 30, 35, 40, 45, 50, 53, 56, 57, 55, 58, 59, 62, 64,
  196. 67, 67, 68, 68, 62, 61, 61, 59, 59, 57, 57, 55, 52, 48, 42,
  197. 38, 35, 31, 26, 20, 15, 13, 10, 7, 3, -2, -8, -13, -17, -23,
  198. -28, -34, -37, -40, -41, -45, -48, -50, -53, -57, -59, -62,
  199. -63, -63, -57, -57, -56, -56, -54, -54, -53, -49, -48, -41,
  200. -38, -33, -31, -26, -23, -18, -12, -9, -7, -7, -3, 0, 5, 9,
  201. 14, 16, 20, 22, 21, 23, 25, 27, 28, 29, 34, 33, 35, 33, 31,
  202. 30, 29, 29, 26, 26, 25, 24, 20, 19, 15, 10, 8, 4, 1, -2, -6,
  203. -10, -16, -19, -23, -26, -27, -30, -34, -39, -43, -47, -51,
  204. -52, -54, -56, -58, -59, -62, -63, -66, -65, -65, -64, -59,
  205. -57, -54, -52, -48, -44, -42, -37, -32, -22, -17, -10, -3, 5,
  206. 13, 22, 30, 40, 50, 60, 72
  207. };
  208. atten -= AttenCorrs[AttenCorr] / AttenCorrScale;
  209. }
  210. else
  211. {
  212. static const double AttenCorrScale = 196.0;
  213. static const signed char AttenCorrs[] = {
  214. -15, -17, -20, -20, -20, -21, -20, -16, -17, -18, -17, -13,
  215. -12, -11, -9, -7, -5, -4, -1, 1, 3, 4, 5, 6, 7, 9, 9, 10, 10,
  216. 10, 11, 11, 11, 12, 12, 12, 10, 11, 10, 10, 8, 10, 11, 10, 11,
  217. 11, 13, 14, 15, 19, 27, 26, 23, 18, 14, 8, 4, -2, -6, -12,
  218. -17, -23, -28, -33, -37, -42, -46, -49, -53, -57, -60, -61,
  219. -64, -65, -67, -66, -66, -66, -65, -64, -61, -59, -56, -52,
  220. -48, -42, -38, -31, -27, -19, -13, -7, -1, 8, 14, 22, 29, 37,
  221. 45, 52, 59, 66, 73, 80, 86, 91, 96, 100, 104, 108, 111, 114,
  222. 115, 117, 118, 120, 120, 118, 117, 114, 113, 111, 107, 103,
  223. 99, 95, 89, 84, 78, 72, 66, 60, 52, 44, 37, 30, 21, 14, 6, -3,
  224. -11, -18, -26, -34, -43, -51, -58, -65, -73, -78, -85, -90,
  225. -97, -102, -107, -113, -115, -118, -121, -125, -125, -126,
  226. -126, -126, -125, -124, -121, -119, -115, -111, -109, -101,
  227. -102, -95, -88, -81, -73, -67, -63, -54, -47, -40, -33, -26,
  228. -18, -11, -5, 2, 8, 14, 19, 25, 31, 36, 37, 43, 47, 49, 51,
  229. 52, 57, 57, 56, 57, 58, 58, 58, 57, 56, 52, 52, 50, 48, 44,
  230. 41, 39, 37, 33, 31, 26, 24, 21, 18, 14, 11, 8, 4, 2, -2, -5,
  231. -7, -9, -11, -13, -15, -16, -18, -19, -20, -23, -24, -24, -25,
  232. -27, -26, -27, -29, -30, -31, -32, -35, -36, -39, -40, -44,
  233. -46, -51, -54, -59, -63, -69, -76, -83, -91, -98
  234. };
  235. atten -= AttenCorrs[AttenCorr] / AttenCorrScale;
  236. }
  237. double pwr = 7.43932822146293e-8 * sqr(atten) + 0.000102747434588003 * cos(0.00785021930010397 * atten) * cos(
  238. 0.633854318781239 + 0.103208573657699 * atten)
  239. - 0.00798132247867036 - 0.000903555213543865 * atten - 0.0969365532127236 * exp(0.0779275237937911 * atten) - 1.37304948662012e-5 *
  240. atten * cos(0.00785021930010397 * atten);
  241. if (pwr <= 0.067665322581)
  242. {
  243. if (tb >= 0.25)
  244. {
  245. hl = 2.6778150875894 / tb + 300.547590563091 * atan(atan(2.68959772209918 * pwr)) / (5.5099277187035 * tb - tb * tanh(cos(asinh(atten))));
  246. fo1 = 0.987205355829873 * tb + 1.00011788929851 * atan2(-0.321432067051302 - 6.19131357321578 * sqrt(pwr),
  247. hl + -1.14861472207245 / (hl - 14.1821147585957) + pow(0.9521145021664,
  248. pow(
  249. atan2(
  250. 1.12018764830637,
  251. tb),
  252. 2.10988901686912 * hl -
  253. 20.9691278378345)));
  254. }
  255. else if (tb >= 0.10)
  256. {
  257. hl = (1.56688617018066 + 142.064321294568 * pwr + 0.00419441117131136 * cos(243.633511747297 * pwr) - 0.022953443903576 * atten -
  258. 0.026629568860284 * cos(127.715550622571 * pwr)) / tb;
  259. fo1 = 0.982299356642411 * tb + 0.999441744774215 * asinh((-0.361783054039583 - 5.80540593623676 * sqrt(pwr)) / hl);
  260. }
  261. else
  262. {
  263. hl = (2.45739657014937 + 269.183679500541 * pwr * cos(
  264. 5.73225668178813 + atan2(cosh(0.988861169868941 - 17.2201556280744 * pwr), 1.08340138240431 * pwr))) / tb;
  265. fo1 = 2.291956939 * tb + 0.01942450693 * sqr(tb) * hl - 4.67538973161837 * pwr * tb - 1.668433124 * tb * pow(pwr, pwr);
  266. }
  267. }
  268. else
  269. {
  270. if (tb >= 0.25)
  271. {
  272. hl = (1.50258368698213 + 158.556968859477 * asinh(pwr) * tanh(57.9466246871383 * tanh(pwr)) - 0.0105440479814834 * atten) / tb;
  273. fo1 = 0.994024401639321 * tb + (-0.236282717577215 - 6.8724924545387 * sqrt(sin(pwr))) / hl;
  274. }
  275. else if (tb >= 0.10)
  276. {
  277. hl = (1.50277377248945 + 158.222625721046 * asinh(pwr) * tanh(1.02875299001715 + 42.072277322604 * pwr) - 0.0108380943845632 * atten) / tb;
  278. fo1 = 0.992539376734551 * tb + (-0.251747813037178 - 6.74159892452584 * sqrt(tanh(tanh(tan(pwr))))) / hl;
  279. }
  280. else
  281. {
  282. hl = (1.15990238966306 * pwr - 5.02124037125213 * sqr(pwr) - 0.158676856669827 * atten * cos(
  283. 1.1609073390614 * pwr - 6.33932586197475 * pwr * sqr(pwr))) / tb;
  284. fo1 = 0.867344453126885 * tb + 0.052693817907757 * tb * log(pwr) + 0.0895511178735932 * tb * atan(59.7538527741309 * pwr) -
  285. 0.0745653568081453 * pwr * tb;
  286. }
  287. }
  288. double WinParams[ 2 ];
  289. WinParams[0] = 125.0;
  290. WinParams[1] = pwr;
  291. CDSPSincFilterGen sinc;
  292. sinc.Len2 = 0.25 * hl / ReqNormFreq;
  293. sinc.Freq1 = 0.0;
  294. sinc.Freq2 = M_PI * (1.0 - fo1) * ReqNormFreq;
  295. sinc.initBand(CDSPSincFilterGen::wftKaiser, WinParams, true);
  296. KernelLen = sinc.KernelLen;
  297. BlockLenBits = getBitOccupancy(KernelLen - 1);
  298. const int BlockLen = 1 << BlockLenBits;
  299. KernelBlock.alloc(BlockLen * 2);
  300. sinc.generateBand(&KernelBlock[0],
  301. &CDSPSincFilterGen::calcWindowKaiser);
  302. /* if( ReqPhase == fprLinearPhase )
  303. {*/
  304. IsZeroPhase = true;
  305. Latency = sinc.fl2;
  306. LatencyFrac = 0.0;
  307. /* }
  308. else
  309. {
  310. IsZeroPhase = false;
  311. double DCGroupDelay;
  312. calcMinPhaseTransform( &KernelBlock[ 0 ], KernelLen, 3, false, &DCGroupDelay );
  313. Latency = (int) DCGroupDelay;
  314. LatencyFrac = DCGroupDelay - Latency;
  315. }*/
  316. CDSPRealFFTKeeper ffto(BlockLenBits + 1);
  317. if (IsZeroPhase)
  318. {
  319. // Calculate DC gain.
  320. double s = 0.0;
  321. int i;
  322. for (i = 0; i < KernelLen; ++i) { s += KernelBlock[i]; }
  323. s = ffto->getInvMulConst() * ReqGain / s;
  324. // Time-shift the filter so that zero-phase response is produced.
  325. // Simultaneously multiply by "s".
  326. for (i = 0; i <= sinc.fl2; ++i) { KernelBlock[i] = KernelBlock[sinc.fl2 + i] * s; }
  327. for (i = 1; i <= sinc.fl2; ++i) { KernelBlock[BlockLen * 2 - i] = KernelBlock[i]; }
  328. memset(&KernelBlock[sinc.fl2 + 1], 0, (BlockLen * 2 - KernelLen) * sizeof(double));
  329. }
  330. else
  331. {
  332. normalizeFIRFilter(&KernelBlock[0], KernelLen, ffto->getInvMulConst() * ReqGain);
  333. memset(&KernelBlock[KernelLen], 0, (BlockLen * 2 - KernelLen) * sizeof(double));
  334. }
  335. ffto->forward(KernelBlock);
  336. R8BCONSOLE("CDSPFIRFilter: flt_len=%i latency=%i nfreq=%.4f tb=%.1f att=%.1f gain=%.3f\n", KernelLen, Latency, ReqNormFreq, ReqTransBand, ReqAtten,
  337. ReqGain);
  338. }
  339. };
  340. /**
  341. * @brief FIR filter cache class.
  342. *
  343. * Class that implements cache for calculated FIR filters. The required FIR
  344. * filter should be obtained via the getLPFilter() static function.
  345. */
  346. class CDSPFIRFilterCache : public R8B_BASECLASS
  347. {
  348. friend class CDSPFIRFilter;
  349. public:
  350. /**
  351. * @return The number of filters present in the cache now. This value can
  352. * be monitored for debugging "forgotten" filters.
  353. */
  354. static int getObjCount()
  355. {
  356. R8BSYNC(StateSync);
  357. return (ObjCount);
  358. }
  359. /**
  360. * Function calculates or returns reference to a previously calculated
  361. * (cached) low-pass FIR filter. Note that the real transition band and
  362. * attenuation achieved by the filter varies with the magnitude of the
  363. * required attenuation, and are never 100% exact.
  364. *
  365. * @param ReqNormFreq Required normalized frequency, in the range 0 to 1,
  366. * inclusive. This is the point after which the stop-band spans.
  367. * @param ReqTransBand Required transition band, in percent of the
  368. * 0 to ReqNormFreq spectral bandwidth, in the range
  369. * CDSPFIRFilter::getLPMinTransBand() to
  370. * CDSPFIRFilter::getLPMaxTransBand(), inclusive. The transition band
  371. * specifies the part of the spectrum between the -3 dB and ReqNormFreq
  372. * points. The real resulting -3 dB point varies in the range from -3.00
  373. * to -3.05 dB, but is generally very close to -3 dB.
  374. * @param ReqAtten Required stop-band attenuation in decibel, in the range
  375. * CDSPFIRFilter::getLPMinAtten() to CDSPFIRFilter::getLPMaxAtten(),
  376. * inclusive. Note that the actual stop-band attenuation of the resulting
  377. * filter may be 0.40-4.46 dB higher.
  378. * @param ReqPhase Required filter's phase response.
  379. * @param ReqGain Required overall filter's gain (1.0 for unity gain).
  380. * @param AttenCorrs Attentuation correction table, to pass to the filter
  381. * generation function. For internal use.
  382. * @return A reference to a new or a previously calculated low-pass FIR
  383. * filter object with the required characteristics. A reference count is
  384. * incremented in the returned filter object which should be released
  385. * after use via the CDSPFIRFilter::unref() function.
  386. */
  387. static CDSPFIRFilter& getLPFilter(const double ReqNormFreq, const double ReqTransBand, const double ReqAtten,
  388. const EDSPFilterPhaseResponse ReqPhase, const double ReqGain, const double* const AttenCorrs = nullptr)
  389. {
  390. R8BASSERT(ReqNormFreq > 0.0 && ReqNormFreq <= 1.0);
  391. R8BASSERT(ReqTransBand >= CDSPFIRFilter :: getLPMinTransBand());
  392. R8BASSERT(ReqTransBand <= CDSPFIRFilter :: getLPMaxTransBand());
  393. R8BASSERT(ReqAtten >= CDSPFIRFilter :: getLPMinAtten());
  394. R8BASSERT(ReqAtten <= CDSPFIRFilter :: getLPMaxAtten());
  395. R8BASSERT(ReqGain > 0.0);
  396. R8BSYNC(StateSync);
  397. CDSPFIRFilter* PrevObj = nullptr;
  398. CDSPFIRFilter* CurObj = Objects;
  399. while (CurObj != nullptr)
  400. {
  401. if (CurObj->ReqNormFreq == ReqNormFreq &&
  402. CurObj->ReqTransBand == ReqTransBand &&
  403. CurObj->ReqAtten == ReqAtten &&
  404. CurObj->ReqPhase == ReqPhase &&
  405. CurObj->ReqGain == ReqGain) { break; }
  406. if (CurObj->Next == nullptr && ObjCount >= R8B_FILTER_CACHE_MAX)
  407. {
  408. if (CurObj->RefCount == 0)
  409. {
  410. // Delete the last filter which is not used.
  411. PrevObj->Next = nullptr;
  412. delete CurObj;
  413. ObjCount--;
  414. }
  415. else
  416. {
  417. // Move the last filter to the top of the list since it
  418. // seems to be in use for a long time.
  419. PrevObj->Next = nullptr;
  420. CurObj->Next = Objects.unkeep();
  421. Objects = CurObj;
  422. }
  423. CurObj = nullptr;
  424. break;
  425. }
  426. PrevObj = CurObj;
  427. CurObj = CurObj->Next;
  428. }
  429. if (CurObj != nullptr)
  430. {
  431. CurObj->RefCount++;
  432. if (PrevObj == nullptr) { return (*CurObj); }
  433. // Remove the filter from the list temporarily.
  434. PrevObj->Next = CurObj->Next;
  435. }
  436. else
  437. {
  438. // Create a new filter object (with RefCount == 1) and build the
  439. // filter kernel.
  440. CurObj = new CDSPFIRFilter();
  441. CurObj->ReqNormFreq = ReqNormFreq;
  442. CurObj->ReqTransBand = ReqTransBand;
  443. CurObj->ReqAtten = ReqAtten;
  444. CurObj->ReqPhase = ReqPhase;
  445. CurObj->ReqGain = ReqGain;
  446. ObjCount++;
  447. CurObj->buildLPFilter(AttenCorrs);
  448. }
  449. // Insert the filter at the start of the list.
  450. CurObj->Next = Objects.unkeep();
  451. Objects = CurObj;
  452. return (*CurObj);
  453. }
  454. private:
  455. static CSyncObject StateSync; ///< Cache state synchronizer.
  456. ///<
  457. static CPtrKeeper<CDSPFIRFilter*> Objects; ///< The chain of cached
  458. ///< objects.
  459. ///<
  460. static int ObjCount; ///< The number of objects currently preset in the
  461. ///< cache.
  462. ///<
  463. };
  464. // ---------------------------------------------------------------------------
  465. // CDSPFIRFilter PUBLIC
  466. // ---------------------------------------------------------------------------
  467. inline void CDSPFIRFilter::unref()
  468. {
  469. R8BSYNC(CDSPFIRFilterCache :: StateSync);
  470. RefCount--;
  471. }
  472. // ---------------------------------------------------------------------------
  473. } // namespace r8b
  474. #endif // R8B_CDSPFIRFILTER_INCLUDED