r8brain-free-src
High-quality pro audio sample rate converter library
Loading...
Searching...
No Matches
CDSPFIRFilter.h
Go to the documentation of this file.
1//$ nobt
2//$ nocpp
3
15#ifndef R8B_CDSPFIRFILTER_INCLUDED
16#define R8B_CDSPFIRFILTER_INCLUDED
17
18#include "CDSPSincFilterGen.h"
19#include "CDSPRealFFT.h"
20
21namespace r8b {
22
28{
29 fprLinearPhase = 0,
46};
47
58{
60
61 friend class CDSPFIRFilterCache;
62
63public:
65 {
66 R8BASSERT( RefCount == 0 );
67
68 delete Next;
69 }
70
76 static double getLPMinTransBand()
77 {
78 return( 0.5 );
79 }
80
86 static double getLPMaxTransBand()
87 {
88 return( 45.0 );
89 }
90
96 static double getLPMinAtten()
97 {
98 return( 49.0 );
99 }
100
106 static double getLPMaxAtten()
107 {
108 return( 218.0 );
109 }
110
115 bool isZeroPhase() const
116 {
117 return( IsZeroPhase );
118 }
119
124 int getLatency() const
125 {
126 return( Latency );
127 }
128
134 double getLatencyFrac() const
135 {
136 return( LatencyFrac );
137 }
138
144 int getKernelLen() const
145 {
146 return( KernelLen );
147 }
148
154 int getBlockLenBits() const
155 {
156 return( BlockLenBits );
157 }
158
167 const double* getKernelBlock() const
168 {
169 return( KernelBlock );
170 }
171
177 void unref();
178
179private:
180 double ReqNormFreq;
181 double ReqTransBand;
183 double ReqAtten;
185 EDSPFilterPhaseResponse ReqPhase;
186 double ReqGain;
187 CDSPFIRFilter* Next;
188 int RefCount;
189 bool IsZeroPhase;
191 int Latency;
192 double LatencyFrac;
193 int KernelLen;
194 int BlockLenBits;
197 CFixedBuffer< double > KernelBlock;
201
203 : RefCount( 1 )
204 {
205 }
206
214 void buildLPFilter( const double* const ExtAttenCorrs )
215 {
216 const double tb = ReqTransBand * 0.01;
217 double pwr;
218 double fo1;
219 double hl;
220 double atten = -ReqAtten;
221
222 if( tb >= 0.25 )
223 {
224 if( ReqAtten >= 117.0 )
225 {
226 atten -= 1.60;
227 }
228 else
229 if( ReqAtten >= 60.0 )
230 {
231 atten -= 1.91;
232 }
233 else
234 {
235 atten -= 2.25;
236 }
237 }
238 else
239 if( tb >= 0.10 )
240 {
241 if( ReqAtten >= 117.0 )
242 {
243 atten -= 0.69;
244 }
245 else
246 if( ReqAtten >= 60.0 )
247 {
248 atten -= 0.73;
249 }
250 else
251 {
252 atten -= 1.13;
253 }
254 }
255 else
256 {
257 if( ReqAtten >= 117.0 )
258 {
259 atten -= 0.21;
260 }
261 else
262 if( ReqAtten >= 60.0 )
263 {
264 atten -= 0.25;
265 }
266 else
267 {
268 atten -= 0.36;
269 }
270 }
271
272 static const int AttenCorrCount = 264;
273 static const double AttenCorrMin = 49.0;
274 static const double AttenCorrDiff = 176.25;
275 int AttenCorr = (int) floor(( -atten - AttenCorrMin ) *
276 AttenCorrCount / AttenCorrDiff + 0.5 );
277
278 AttenCorr = min( AttenCorrCount, max( 0, AttenCorr ));
279
280 if( ExtAttenCorrs != NULL )
281 {
282 atten -= ExtAttenCorrs[ AttenCorr ];
283 }
284 else
285 if( tb >= 0.25 )
286 {
287 static const double AttenCorrScale = 101.0;
288 static const signed char AttenCorrs[] = {
289 -127, -127, -125, -125, -122, -119, -115, -110, -104, -97,
290 -91, -82, -75, -24, -16, -6, 4, 14, 24, 29, 30, 32, 37, 44,
291 51, 57, 63, 67, 65, 50, 53, 56, 58, 60, 63, 64, 66, 68, 74,
292 77, 78, 78, 78, 79, 79, 60, 60, 60, 61, 59, 52, 47, 41, 36,
293 30, 24, 17, 9, 0, -8, -10, -11, -14, -13, -18, -25, -31, -38,
294 -44, -50, -57, -63, -68, -74, -81, -89, -96, -101, -104, -107,
295 -109, -110, -86, -84, -85, -82, -80, -77, -73, -67, -62, -55,
296 -48, -42, -35, -30, -20, -11, -2, 5, 6, 6, 7, 11, 16, 21, 26,
297 34, 41, 46, 49, 52, 55, 56, 48, 49, 51, 51, 52, 52, 52, 52,
298 52, 51, 51, 50, 47, 47, 50, 48, 46, 42, 38, 35, 31, 27, 24,
299 20, 16, 12, 11, 12, 10, 8, 4, -1, -6, -11, -16, -19, -17, -21,
300 -24, -27, -32, -34, -37, -38, -40, -41, -40, -40, -42, -41,
301 -44, -45, -43, -41, -34, -31, -28, -24, -21, -18, -14, -10,
302 -5, -1, 2, 5, 8, 7, 4, 3, 2, 2, 4, 6, 8, 9, 9, 10, 10, 10, 10,
303 9, 8, 9, 11, 14, 13, 12, 11, 10, 8, 7, 6, 5, 3, 2, 2, -1, -1,
304 -3, -3, -4, -4, -5, -4, -6, -7, -9, -5, -1, -1, 0, 1, 0, -2,
305 -3, -4, -5, -5, -8, -13, -13, -13, -12, -13, -12, -11, -11,
306 -9, -8, -7, -5, -3, -1, 2, 4, 6, 9, 10, 11, 14, 18, 21, 24,
307 27, 30, 34, 37, 37, 39, 40 };
308
309 atten -= AttenCorrs[ AttenCorr ] / AttenCorrScale;
310 }
311 else
312 if( tb >= 0.10 )
313 {
314 static const double AttenCorrScale = 210.0;
315 static const signed char AttenCorrs[] = {
316 -113, -118, -122, -125, -126, -97, -95, -92, -92, -89, -82,
317 -75, -69, -48, -42, -36, -30, -22, -14, -5, -2, 1, 6, 13, 22,
318 28, 35, 41, 48, 55, 56, 56, 61, 65, 71, 77, 81, 83, 85, 85,
319 74, 74, 73, 72, 71, 70, 68, 64, 59, 56, 49, 52, 46, 42, 36,
320 32, 26, 20, 13, 7, -2, -6, -10, -15, -20, -27, -33, -38, -44,
321 -43, -48, -53, -57, -63, -69, -73, -75, -79, -81, -74, -76,
322 -77, -77, -78, -81, -80, -80, -78, -76, -65, -62, -59, -56,
323 -51, -48, -44, -38, -33, -25, -19, -13, -5, -1, 2, 7, 13, 17,
324 21, 25, 30, 35, 40, 45, 50, 53, 56, 57, 55, 58, 59, 62, 64,
325 67, 67, 68, 68, 62, 61, 61, 59, 59, 57, 57, 55, 52, 48, 42,
326 38, 35, 31, 26, 20, 15, 13, 10, 7, 3, -2, -8, -13, -17, -23,
327 -28, -34, -37, -40, -41, -45, -48, -50, -53, -57, -59, -62,
328 -63, -63, -57, -57, -56, -56, -54, -54, -53, -49, -48, -41,
329 -38, -33, -31, -26, -23, -18, -12, -9, -7, -7, -3, 0, 5, 9,
330 14, 16, 20, 22, 21, 23, 25, 27, 28, 29, 34, 33, 35, 33, 31,
331 30, 29, 29, 26, 26, 25, 24, 20, 19, 15, 10, 8, 4, 1, -2, -6,
332 -10, -16, -19, -23, -26, -27, -30, -34, -39, -43, -47, -51,
333 -52, -54, -56, -58, -59, -62, -63, -66, -65, -65, -64, -59,
334 -57, -54, -52, -48, -44, -42, -37, -32, -22, -17, -10, -3, 5,
335 13, 22, 30, 40, 50, 60, 72 };
336
337 atten -= AttenCorrs[ AttenCorr ] / AttenCorrScale;
338 }
339 else
340 {
341 static const double AttenCorrScale = 196.0;
342 static const signed char AttenCorrs[] = {
343 -15, -17, -20, -20, -20, -21, -20, -16, -17, -18, -17, -13,
344 -12, -11, -9, -7, -5, -4, -1, 1, 3, 4, 5, 6, 7, 9, 9, 10, 10,
345 10, 11, 11, 11, 12, 12, 12, 10, 11, 10, 10, 8, 10, 11, 10, 11,
346 11, 13, 14, 15, 19, 27, 26, 23, 18, 14, 8, 4, -2, -6, -12,
347 -17, -23, -28, -33, -37, -42, -46, -49, -53, -57, -60, -61,
348 -64, -65, -67, -66, -66, -66, -65, -64, -61, -59, -56, -52,
349 -48, -42, -38, -31, -27, -19, -13, -7, -1, 8, 14, 22, 29, 37,
350 45, 52, 59, 66, 73, 80, 86, 91, 96, 100, 104, 108, 111, 114,
351 115, 117, 118, 120, 120, 118, 117, 114, 113, 111, 107, 103,
352 99, 95, 89, 84, 78, 72, 66, 60, 52, 44, 37, 30, 21, 14, 6, -3,
353 -11, -18, -26, -34, -43, -51, -58, -65, -73, -78, -85, -90,
354 -97, -102, -107, -113, -115, -118, -121, -125, -125, -126,
355 -126, -126, -125, -124, -121, -119, -115, -111, -109, -101,
356 -102, -95, -88, -81, -73, -67, -63, -54, -47, -40, -33, -26,
357 -18, -11, -5, 2, 8, 14, 19, 25, 31, 36, 37, 43, 47, 49, 51,
358 52, 57, 57, 56, 57, 58, 58, 58, 57, 56, 52, 52, 50, 48, 44,
359 41, 39, 37, 33, 31, 26, 24, 21, 18, 14, 11, 8, 4, 2, -2, -5,
360 -7, -9, -11, -13, -15, -16, -18, -19, -20, -23, -24, -24, -25,
361 -27, -26, -27, -29, -30, -31, -32, -35, -36, -39, -40, -44,
362 -46, -51, -54, -59, -63, -69, -76, -83, -91, -98 };
363
364 atten -= AttenCorrs[ AttenCorr ] / AttenCorrScale;
365 }
366
367 pwr = 7.43932822146293e-8 * sqr( atten ) + 0.000102747434588003 *
368 cos( 0.00785021930010397 * atten ) * cos( 0.633854318781239 +
369 0.103208573657699 * atten ) - 0.00798132247867036 -
370 0.000903555213543865 * atten - 0.0969365532127236 * exp(
371 0.0779275237937911 * atten ) - 1.37304948662012e-5 * atten * cos(
372 0.00785021930010397 * atten );
373
374 if( pwr <= 0.067665322581 )
375 {
376 if( tb >= 0.25 )
377 {
378 hl = 2.6778150875894 / tb + 300.547590563091 * atan( atan(
379 2.68959772209918 * pwr )) / ( 5.5099277187035 * tb - tb *
380 tanh( cos( asinh( atten ))));
381
382 fo1 = 0.987205355829873 * tb + 1.00011788929851 * atan2(
383 -0.321432067051302 - 6.19131357321578 * sqrt( pwr ),
384 hl + -1.14861472207245 / ( hl - 14.1821147585957 ) + pow(
385 0.9521145021664, pow( atan2( 1.12018764830637, tb ),
386 2.10988901686912 * hl - 20.9691278378345 )));
387 }
388 else
389 if( tb >= 0.10 )
390 {
391 hl = ( 1.56688617018066 + 142.064321294568 * pwr +
392 0.00419441117131136 * cos( 243.633511747297 * pwr ) -
393 0.022953443903576 * atten - 0.026629568860284 * cos(
394 127.715550622571 * pwr )) / tb;
395
396 fo1 = 0.982299356642411 * tb + 0.999441744774215 * asinh((
397 -0.361783054039583 - 5.80540593623676 * sqrt( pwr )) /
398 hl );
399 }
400 else
401 {
402 hl = ( 2.45739657014937 + 269.183679500541 * pwr * cos(
403 5.73225668178813 + atan2( cosh( 0.988861169868941 -
404 17.2201556280744 * pwr ), 1.08340138240431 * pwr ))) / tb;
405
406 fo1 = 2.291956939 * tb + 0.01942450693 * sqr( tb ) * hl -
407 4.67538973161837 * pwr * tb - 1.668433124 * tb *
408 pow( pwr, pwr );
409 }
410 }
411 else
412 {
413 if( tb >= 0.25 )
414 {
415 hl = ( 1.50258368698213 + 158.556968859477 * asinh( pwr ) *
416 tanh( 57.9466246871383 * tanh( pwr )) -
417 0.0105440479814834 * atten ) / tb;
418
419 fo1 = 0.994024401639321 * tb + ( -0.236282717577215 -
420 6.8724924545387 * sqrt( sin( pwr ))) / hl;
421 }
422 else
423 if( tb >= 0.10 )
424 {
425 hl = ( 1.50277377248945 + 158.222625721046 * asinh( pwr ) *
426 tanh( 1.02875299001715 + 42.072277322604 * pwr ) -
427 0.0108380943845632 * atten ) / tb;
428
429 fo1 = 0.992539376734551 * tb + ( -0.251747813037178 -
430 6.74159892452584 * sqrt( tanh( tanh( tan( pwr ))))) / hl;
431 }
432 else
433 {
434 hl = ( 1.15990238966306 * pwr - 5.02124037125213 * sqr(
435 pwr ) - 0.158676856669827 * atten * cos( 1.1609073390614 *
436 pwr - 6.33932586197475 * pwr * sqr( pwr ))) / tb;
437
438 fo1 = 0.867344453126885 * tb + 0.052693817907757 * tb * log(
439 pwr ) + 0.0895511178735932 * tb * atan( 59.7538527741309 *
440 pwr ) - 0.0745653568081453 * pwr * tb;
441 }
442 }
443
444 double WinParams[ 2 ];
445 WinParams[ 0 ] = 125.0;
446 WinParams[ 1 ] = pwr;
447
448 CDSPSincFilterGen sinc;
449 sinc.Len2 = 0.25 * hl / ReqNormFreq;
450 sinc.Freq1 = 0.0;
451 sinc.Freq2 = R8B_PI * ( 1.0 - fo1 ) * ReqNormFreq;
452 sinc.initBand( CDSPSincFilterGen :: wftKaiser, WinParams, true );
453
454 KernelLen = sinc.KernelLen;
455 BlockLenBits = getBitOccupancy( KernelLen - 1 ) + R8B_EXTFFT;
456 const int BlockLen = 1 << BlockLenBits;
457
458 KernelBlock.alloc( BlockLen * 2 );
459 sinc.generateBand( &KernelBlock[ 0 ],
460 &CDSPSincFilterGen :: calcWindowKaiser );
461
462 if( ReqPhase == fprLinearPhase )
463 {
464 IsZeroPhase = true;
465 Latency = sinc.fl2;
466 LatencyFrac = 0.0;
467 }
468 else
469 {
470 IsZeroPhase = false;
471 double DCGroupDelay;
472
473 calcMinPhaseTransform( &KernelBlock[ 0 ], KernelLen, 16, false,
474 &DCGroupDelay );
475
476 Latency = (int) DCGroupDelay;
477 LatencyFrac = DCGroupDelay - Latency;
478 }
479
480 CDSPRealFFTKeeper ffto( BlockLenBits + 1 );
481
482 if( IsZeroPhase )
483 {
484 // Calculate DC gain.
485
486 double s = 0.0;
487 int i;
488
489 for( i = 0; i < KernelLen; i++ )
490 {
491 s += KernelBlock[ i ];
492 }
493
494 s = ffto -> getInvMulConst() * ReqGain / s;
495
496 // Time-shift the filter so that zero-phase response is produced.
497 // Simultaneously multiply by "s".
498
499 for( i = 0; i <= sinc.fl2; i++ )
500 {
501 KernelBlock[ i ] = KernelBlock[ sinc.fl2 + i ] * s;
502 }
503
504 for( i = 1; i <= sinc.fl2; i++ )
505 {
506 KernelBlock[ BlockLen * 2 - i ] = KernelBlock[ i ];
507 }
508
509 memset( &KernelBlock[ sinc.fl2 + 1 ], 0,
510 ( BlockLen * 2 - KernelLen ) * sizeof( KernelBlock[ 0 ]));
511
512 ffto -> forward( KernelBlock );
513 ffto -> convertToZP( KernelBlock );
514 }
515 else
516 {
517 normalizeFIRFilter( &KernelBlock[ 0 ], KernelLen,
518 ffto -> getInvMulConst() * ReqGain );
519
520 memset( &KernelBlock[ KernelLen ], 0,
521 ( BlockLen * 2 - KernelLen ) * sizeof( KernelBlock[ 0 ]));
522
523 ffto -> forward( KernelBlock );
524 }
525
526 R8BCONSOLE( "CDSPFIRFilter: flt_len=%i latency=%i nfreq=%.4f "
527 "tb=%.1f att=%.1f gain=%.3f\n", KernelLen, Latency,
528 ReqNormFreq, ReqTransBand, ReqAtten, ReqGain );
529 }
530};
531
540{
542
543 friend class CDSPFIRFilter;
544
545public:
551 static int getObjCount()
552 {
553 R8BSYNC( StateSync );
554
555 return( ObjCount );
556 }
557
588 static CDSPFIRFilter& getLPFilter( const double ReqNormFreq,
589 const double ReqTransBand, const double ReqAtten,
590 const EDSPFilterPhaseResponse ReqPhase, const double ReqGain,
591 const double* const AttenCorrs = NULL )
592 {
593 R8BASSERT( ReqNormFreq > 0.0 && ReqNormFreq <= 1.0 );
594 R8BASSERT( ReqTransBand >= CDSPFIRFilter :: getLPMinTransBand() );
595 R8BASSERT( ReqTransBand <= CDSPFIRFilter :: getLPMaxTransBand() );
596 R8BASSERT( ReqAtten >= CDSPFIRFilter :: getLPMinAtten() );
597 R8BASSERT( ReqAtten <= CDSPFIRFilter :: getLPMaxAtten() );
598 R8BASSERT( ReqGain > 0.0 );
599
600 R8BSYNC( StateSync );
601
602 CDSPFIRFilter* PrevObj = NULL;
603 CDSPFIRFilter* CurObj = Objects;
604
605 while( CurObj != NULL )
606 {
607 if( CurObj -> ReqNormFreq == ReqNormFreq &&
608 CurObj -> ReqTransBand == ReqTransBand &&
609 CurObj -> ReqGain == ReqGain &&
610 CurObj -> ReqAtten == ReqAtten &&
611 CurObj -> ReqPhase == ReqPhase )
612 {
613 break;
614 }
615
616 if( CurObj -> Next == NULL && ObjCount >= R8B_FILTER_CACHE_MAX )
617 {
618 if( CurObj -> RefCount == 0 )
619 {
620 // Delete the last filter which is not used.
621
622 PrevObj -> Next = NULL;
623 delete CurObj;
624 ObjCount--;
625 }
626 else
627 {
628 // Move the last filter to the top of the list since it
629 // seems to be in use for a long time.
630
631 PrevObj -> Next = NULL;
632 CurObj -> Next = Objects.unkeep();
633 Objects = CurObj;
634 }
635
636 CurObj = NULL;
637 break;
638 }
639
640 PrevObj = CurObj;
641 CurObj = CurObj -> Next;
642 }
643
644 if( CurObj != NULL )
645 {
646 CurObj -> RefCount++;
647
648 if( PrevObj == NULL )
649 {
650 return( *CurObj );
651 }
652
653 // Remove the filter from the list temporarily.
654
655 PrevObj -> Next = CurObj -> Next;
656 }
657 else
658 {
659 // Create a new filter object (with RefCount == 1) and build the
660 // filter kernel.
661
662 CurObj = new CDSPFIRFilter();
663 CurObj -> ReqNormFreq = ReqNormFreq;
664 CurObj -> ReqTransBand = ReqTransBand;
665 CurObj -> ReqAtten = ReqAtten;
666 CurObj -> ReqPhase = ReqPhase;
667 CurObj -> ReqGain = ReqGain;
668 ObjCount++;
669
670 CurObj -> buildLPFilter( AttenCorrs );
671 }
672
673 // Insert the filter at the start of the list.
674
675 CurObj -> Next = Objects.unkeep();
676 Objects = CurObj;
677
678 return( *CurObj );
679 }
680
681private:
682 static CSyncObject StateSync;
683 static CPtrKeeper< CDSPFIRFilter* > Objects;
685 static int ObjCount;
687};
688
689// ---------------------------------------------------------------------------
690// CDSPFIRFilter PUBLIC
691// ---------------------------------------------------------------------------
692
693inline void CDSPFIRFilter :: unref()
694{
695 R8BSYNC( CDSPFIRFilterCache :: StateSync );
696
697 RefCount--;
698}
699
700// ---------------------------------------------------------------------------
701
702} // namespace r8b
703
704#endif // R8B_CDSPFIRFILTER_INCLUDED
Real-valued FFT transform class.
Sinc function-based FIR filter generator class.
#define R8BSYNC(SyncObject)
Definition: r8bbase.h:660
#define R8B_PI
Definition: r8bbase.h:119
#define R8BNOCTOR(ClassName)
Definition: r8bbase.h:154
#define R8B_EXTFFT
Definition: r8bconf.h:134
#define R8BASSERT(e)
Definition: r8bconf.h:27
#define R8B_BASECLASS
Definition: r8bconf.h:54
#define R8BCONSOLE(...)
Definition: r8bconf.h:40
#define R8B_FILTER_CACHE_MAX
Definition: r8bconf.h:84
The "r8brain-free-src" library namespace.
Definition: CDSPBlockConvolver.h:21
EDSPFilterPhaseResponse
Definition: CDSPFIRFilter.h:28
@ fprMinPhase
Minimum-phase response. Features a minimal-latency response, but the response's phase is non-linear....
Definition: CDSPFIRFilter.h:32
@ fprLinearPhase
Linear-phase response. Features a linear-phase, high-latency response, with the latency expressed as ...
Definition: CDSPFIRFilter.h:29
T min(const T &v1, const T &v2)
Definition: r8bbase.h:1063
int getBitOccupancy(const int v)
Definition: r8bbase.h:766
double asinh(const double v)
Definition: r8bbase.h:1152
T max(const T &v1, const T &v2)
Definition: r8bbase.h:1080
void calcMinPhaseTransform(double *const Kernel, const int KernelLen, const int LenMult=2, const bool DoFinalMul=true, double *const DCGroupDelay=NULL)
Definition: CDSPRealFFT.h:691
void normalizeFIRFilter(double *const p, const int l, const double DCGain, const int pstep=1)
Definition: r8bbase.h:928
double sqr(const double x)
Definition: r8bbase.h:1120
Calculation and storage class for FIR filters.
Definition: CDSPFIRFilter.h:58
int getBlockLenBits() const
Definition: CDSPFIRFilter.h:154
int getLatency() const
Definition: CDSPFIRFilter.h:124
static double getLPMaxTransBand()
Definition: CDSPFIRFilter.h:86
void unref()
Definition: CDSPFIRFilter.h:693
double getLatencyFrac() const
Definition: CDSPFIRFilter.h:134
static double getLPMinAtten()
Definition: CDSPFIRFilter.h:96
int getKernelLen() const
Definition: CDSPFIRFilter.h:144
const double * getKernelBlock() const
Definition: CDSPFIRFilter.h:167
static double getLPMaxAtten()
Definition: CDSPFIRFilter.h:106
static double getLPMinTransBand()
Definition: CDSPFIRFilter.h:76
bool isZeroPhase() const
Definition: CDSPFIRFilter.h:115
FIR filter cache class.
Definition: CDSPFIRFilter.h:540
static int getObjCount()
Definition: CDSPFIRFilter.h:551
static CDSPFIRFilter & getLPFilter(const double ReqNormFreq, const double ReqTransBand, const double ReqAtten, const EDSPFilterPhaseResponse ReqPhase, const double ReqGain, const double *const AttenCorrs=NULL)
Definition: CDSPFIRFilter.h:588
void alloc(const int Capacity)
Definition: r8bbase.h:343
Pointer-to-object "keeper" class with automatic deletion.
Definition: r8bbase.h:428
Multi-threaded synchronization object class.
Definition: r8bbase.h:526