r8brain-free-src
High-quality pro audio sample rate converter library
 
Loading...
Searching...
No Matches
CDSPFracInterpolator.h
Go to the documentation of this file.
1//$ nobt
2//$ nocpp
3
15
16#ifndef R8B_CDSPFRACINTERPOLATOR_INCLUDED
17#define R8B_CDSPFRACINTERPOLATOR_INCLUDED
18
19#include "CDSPSincFilterGen.h"
20#include "CDSPProcessor.h"
21
22namespace r8b {
23
24#if R8B_FLTTEST
25 extern int InterpFilterFracs;
27#endif // R8B_FLTTEST
28
37
39 private CSinglyLinkedListItem< CDSPFracDelayFilterBank >
40{
42
43 friend class CSinglyLinkedListItem< CDSPFracDelayFilterBank >;
44 friend class CDSPFracDelayFilterBankCache;
45
46public:
62
63 CDSPFracDelayFilterBank( const int aFilterFracs, const int aElementSize,
64 const int aInterpPoints, const double aReqAtten, const bool aIsThird )
65 : InitFilterFracs( aFilterFracs )
66 , ElementSize( aElementSize )
67 , InterpPoints( aInterpPoints )
68 , ReqAtten( aReqAtten )
69 , IsThird( aIsThird )
70 , RefCount( 1 )
71 {
72 R8BASSERT( ElementSize >= 1 && ElementSize <= 4 );
73
74 // Kaiser window function Params, for half and third-band.
75
76 const double* const Params = getWinParams( ReqAtten, IsThird,
77 FilterLen );
78
79 FilterSize = FilterLen * ElementSize;
80
81 if( InitFilterFracs == -1 )
82 {
83 FilterFracs = (int) ceil( pow( 6.4, ReqAtten / 50.0 ));
84
85 #if R8B_FLTTEST
86
87 if( InterpFilterFracs != -1 )
88 {
89 FilterFracs = InterpFilterFracs;
90 }
91
92 #endif // R8B_FLTTEST
93 }
94 else
95 {
96 FilterFracs = InitFilterFracs;
97 }
98
99 Table.alloc( FilterSize * ( FilterFracs + InterpPoints ));
100
102 sinc.Len2 = FilterLen / 2;
103
104 double* p = Table;
105 const int pc2 = InterpPoints / 2;
106 int i;
107
108 for( i = -pc2 + 1; i <= FilterFracs + pc2; i++ )
109 {
110 sinc.FracDelay = (double) ( FilterFracs - i ) / FilterFracs;
111 sinc.initFrac( CDSPSincFilterGen :: wftKaiser, Params, true );
112 sinc.generateFrac( p, &CDSPSincFilterGen :: calcWindowKaiser,
113 ElementSize );
114
115 normalizeFIRFilter( p, FilterLen, 1.0, ElementSize );
116 p += FilterSize;
117 }
118
119 const int TablePos2 = FilterSize;
120 const int TablePos3 = FilterSize * 2;
121 const int TablePos4 = FilterSize * 3;
122 const int TablePos5 = FilterSize * 4;
123 const int TablePos6 = FilterSize * 5;
124 const int TablePos7 = FilterSize * 6;
125 const int TablePos8 = FilterSize * 7;
126 double* const TableEnd = Table + ( FilterFracs + 1 ) * FilterSize;
127 p = Table;
128
129 if( InterpPoints == 8 )
130 {
131 if( ElementSize == 3 )
132 {
133 // Calculate 2nd order spline (polynomial) interpolation
134 // coefficients using 8 points.
135
136 while( p < TableEnd )
137 {
138 calcSpline2p8Coeffs( p, p[ 0 ], p[ TablePos2 ],
139 p[ TablePos3 ], p[ TablePos4 ], p[ TablePos5 ],
140 p[ TablePos6 ], p[ TablePos7 ], p[ TablePos8 ]);
141
142 p += ElementSize;
143 }
144
145 #if defined( R8B_SIMD_ISH )
146 shuffle2_3( Table, TableEnd );
147 #endif // SIMD
148 }
149 else
150 if( ElementSize == 4 )
151 {
152 // Calculate 3rd order spline (polynomial) interpolation
153 // coefficients using 8 points.
154
155 while( p < TableEnd )
156 {
157 calcSpline3p8Coeffs( p, p[ 0 ], p[ TablePos2 ],
158 p[ TablePos3 ], p[ TablePos4 ], p[ TablePos5 ],
159 p[ TablePos6 ], p[ TablePos7 ], p[ TablePos8 ]);
160
161 p += ElementSize;
162 }
163
164 #if defined( R8B_SIMD_ISH )
165 shuffle2_4( Table, TableEnd );
166 #endif // SIMD
167 }
168 }
169 else
170 {
171 if( ElementSize == 2 )
172 {
173 // Calculate linear interpolation coefficients.
174
175 while( p < TableEnd )
176 {
177 p[ 1 ] = p[ TablePos2 ] - p[ 0 ];
178 p += ElementSize;
179 }
180
181 #if defined( R8B_SIMD_ISH )
182 shuffle2_2( Table, TableEnd );
183 #endif // SIMD
184 }
185 }
186
187 R8BCONSOLE( "CDSPFracDelayFilterBank: fracs=%i order=%i taps=%i "
188 "att=%.1f third=%i\n", FilterFracs, ElementSize - 1, FilterLen,
189 ReqAtten, (int) IsThird );
190 }
191
199
200 static void roundReqAtten( double& att, const bool aIsThird )
201 {
202 int tmp;
203 getWinParams( att, aIsThird, tmp );
204 }
205
210
211 int getFilterLen() const
212 {
213 return( FilterLen );
214 }
215
219
220 int getFilterFracs() const
221 {
222 return( FilterFracs );
223 }
224
230
231 const double& operator []( const int i ) const
232 {
233 R8BASSERT( i >= 0 && i <= FilterFracs );
234
235 return( Table[ i * FilterSize ]);
236 }
237
244
245 void unref();
246
247private:
248 int FilterLen;
249 int FilterFracs;
250 int InitFilterFracs;
252 int ElementSize;
253 int InterpPoints;
254 double ReqAtten;
255 bool IsThird;
256 int FilterSize;
261 int RefCount;
263
273
274 static const double* getWinParams( double& att, const bool aIsThird,
275 int& fltlen )
276 {
277 static const int Coeffs2Base = 8;
278 static const int Coeffs2Count = 12;
279 static const double Coeffs2[ Coeffs2Count ][ 3 ] = {
280 { 4.1308468534586913, 1.1752580009977263, 55.5446 }, // 0.0256
281 { 4.4241520324148826, 1.8004881791443044, 81.4191 }, // 0.0886
282 { 5.2615232289173663, 1.8133318236025469, 96.3392 }, // 0.0481
283 { 5.9433751227216174, 1.8730186391986436, 111.1315 }, // 0.0264
284 { 6.8308658290513815, 1.8549555110340281, 125.4653 }, // 0.0146
285 { 7.6648458290312904, 1.8565766090828464, 139.7379 }, // 0.0081
286 { 8.2038728664307605, 1.9269521820570166, 154.0532 }, // 0.0045
287 { 8.7865150946655142, 1.9775307667441668, 168.2101 }, // 0.0025
288 { 9.5945017884101773, 1.9718456992078597, 182.1076 }, // 0.0014
289 { 10.5163141145985240, 1.9504067820201083, 195.5668 }, // 0.0008
290 { 10.2382465206362470, 2.1608923446870087, 209.0610 }, // 0.0004
291 { 10.9976060250714000, 2.1536533525688935, 222.5010 }, // 0.0003
292 };
293
294 static const int Coeffs3Base = 6;
295 static const int Coeffs3Count = 10;
296 static const double Coeffs3[ Coeffs3Count ][ 3 ] = {
297 { 3.9888564562781847, 1.5869927184268915, 66.5701 }, // 0.0467
298 { 4.6986694038145007, 1.8086068597928262, 86.4715 }, // 0.0136
299 { 5.5995071329337822, 1.8930163360942349, 106.1195 }, // 0.0040
300 { 6.3627287800257228, 1.9945748322093975, 125.2307 }, // 0.0012
301 { 7.4299550711428308, 1.9893400572347544, 144.3469 }, // 0.0004
302 { 8.0667715944075642, 2.0928201458699909, 163.4099 }, // 0.0001
303 { 8.7469970226288822, 2.1640279784268355, 181.0694 }, // 0.0000
304 { 10.0823430069835230, 2.0896678025321922, 199.2880 }, // 0.0000
305 { 10.9222206090489510, 2.1221681162186004, 216.6865 }, // 0.0000
306 { 21.2017743894772010, 1.1856768080118900, 233.9188 }, // 0.0000
307 };
308
309 const double* Params;
310 int i = 0;
311
312 if( aIsThird )
313 {
314 while( i != Coeffs3Count - 1 && Coeffs3[ i ][ 2 ] < att )
315 {
316 i++;
317 }
318
319 Params = &Coeffs3[ i ][ 0 ];
320 att = Coeffs3[ i ][ 2 ];
321 fltlen = Coeffs3Base + i * 2;
322 }
323 else
324 {
325 while( i != Coeffs2Count - 1 && Coeffs2[ i ][ 2 ] < att )
326 {
327 i++;
328 }
329
330 Params = &Coeffs2[ i ][ 0 ];
331 att = Coeffs2[ i ][ 2 ];
332 fltlen = Coeffs2Base + i * 2;
333 }
334
335 return( Params );
336 }
337
344
345 static void shuffle2_2( double* p, double* const pe )
346 {
347 while( p != pe )
348 {
349 const double t = p[ 2 ];
350 p[ 2 ] = p[ 1 ];
351 p[ 1 ] = t;
352
353 p += 4;
354 }
355 }
356
363
364 static void shuffle2_3( double* p, double* const pe )
365 {
366 while( p != pe )
367 {
368 const double t1 = p[ 1 ];
369 const double t2 = p[ 2 ];
370 const double t3 = p[ 3 ];
371 const double t4 = p[ 4 ];
372 p[ 1 ] = t3;
373 p[ 2 ] = t1;
374 p[ 3 ] = t4;
375 p[ 4 ] = t2;
376
377 p += 6;
378 }
379 }
380
387
388 static void shuffle2_4( double* p, double* const pe )
389 {
390 while( p != pe )
391 {
392 const double t1 = p[ 1 ];
393 const double t2 = p[ 2 ];
394 const double t3 = p[ 3 ];
395 const double t4 = p[ 4 ];
396 const double t5 = p[ 5 ];
397 const double t6 = p[ 6 ];
398 p[ 1 ] = t4;
399 p[ 2 ] = t1;
400 p[ 3 ] = t5;
401 p[ 4 ] = t2;
402 p[ 5 ] = t6;
403 p[ 6 ] = t3;
404
405 p += 8;
406 }
407 }
408};
409
415
417{
419
420 friend class CDSPFracDelayFilterBank;
421
422public:
438
439 static CDSPFracDelayFilterBank& getFilterBank( const int aFilterFracs,
440 const int aElementSize, const int aInterpPoints,
441 double ReqAtten, const bool IsThird, const bool IsStatic )
442 {
444 // The chain of cached objects.
446 // The chain of static objects.
447 R8B_EXITDTOR static int ObjCount = 0; // The number of objects
448 // currently present in the Objects cache.
449
450 CDSPFracDelayFilterBank :: roundReqAtten( ReqAtten, IsThird );
451
453
454 if( IsStatic )
455 {
456 CDSPFracDelayFilterBank* PrevObj = R8B_NULL;
457 CDSPFracDelayFilterBank* CurObj = StaticObjects;
458
459 while( CurObj != R8B_NULL )
460 {
461 if( CurObj -> InitFilterFracs == aFilterFracs &&
462 CurObj -> IsThird == IsThird &&
463 CurObj -> ElementSize == aElementSize &&
464 CurObj -> InterpPoints == aInterpPoints &&
465 CurObj -> ReqAtten == ReqAtten )
466 {
467 if( PrevObj != R8B_NULL )
468 {
469 // Move the object to the top of the list.
470
471 PrevObj -> Next = CurObj -> Next;
472 CurObj -> Next = StaticObjects.unkeep();
473 StaticObjects = CurObj;
474 }
475
476 return( *CurObj );
477 }
478
479 PrevObj = CurObj;
480 CurObj = CurObj -> Next;
481 }
482
483 // Create a new filter bank and build it.
484
485 CurObj = new CDSPFracDelayFilterBank( aFilterFracs, aElementSize,
486 aInterpPoints, ReqAtten, IsThird );
487
488 // Insert the bank at the start of the list.
489
490 CurObj -> Next = StaticObjects.unkeep();
491 StaticObjects = CurObj;
492
493 return( *CurObj );
494 }
495
496 CDSPFracDelayFilterBank* PrevObj = R8B_NULL;
497 CDSPFracDelayFilterBank* CurObj = Objects;
498
499 while( CurObj != R8B_NULL )
500 {
501 if( CurObj -> InitFilterFracs == aFilterFracs &&
502 CurObj -> IsThird == IsThird &&
503 CurObj -> ElementSize == aElementSize &&
504 CurObj -> InterpPoints == aInterpPoints &&
505 CurObj -> ReqAtten == ReqAtten )
506 {
507 break;
508 }
509
510 if( CurObj -> Next == R8B_NULL &&
511 ObjCount >= R8B_FRACBANK_CACHE_MAX )
512 {
513 if( CurObj -> RefCount == 0 )
514 {
515 // Delete the last bank which is not used.
516
517 PrevObj -> Next = R8B_NULL;
518 delete CurObj;
519 ObjCount--;
520 }
521 else
522 {
523 // Move the last bank to the top of the list since it
524 // seems to be in use for a long time.
525
526 PrevObj -> Next = R8B_NULL;
527 CurObj -> Next = Objects.unkeep();
528 Objects = CurObj;
529 }
530
531 CurObj = R8B_NULL;
532 break;
533 }
534
535 PrevObj = CurObj;
536 CurObj = CurObj -> Next;
537 }
538
539 if( CurObj != R8B_NULL )
540 {
541 CurObj -> RefCount++;
542
543 if( PrevObj == R8B_NULL )
544 {
545 return( *CurObj );
546 }
547
548 // Remove the bank from the list temporarily.
549
550 PrevObj -> Next = CurObj -> Next;
551 }
552 else
553 {
554 // Create a new filter bank (with RefCount == 1) and build it.
555
556 CurObj = new CDSPFracDelayFilterBank( aFilterFracs, aElementSize,
557 aInterpPoints, ReqAtten, IsThird );
558
559 ObjCount++;
560 }
561
562 // Insert the bank at the start of the list.
563
564 CurObj -> Next = Objects.unkeep();
565 Objects = CurObj;
566
567 return( *CurObj );
568 }
569
570protected:
574
576 {
577 R8B_EXITDTOR static CSyncObject StateSync;
578
579 return( StateSync );
580 }
581};
582
583// ---------------------------------------------------------------------------
584// CDSPFracDelayFilterBank PUBLIC
585// ---------------------------------------------------------------------------
586
587inline void CDSPFracDelayFilterBank :: unref()
588{
589 R8BSYNC( CDSPFracDelayFilterBankCache :: getStateSync() );
590
591 RefCount--;
592}
593
603
604inline bool findGCD( double l, double s, double& GCD )
605{
606 int it = 0;
607
608 while( ++it < 150 )
609 {
610 const double r = l - s;
611
612 if( r == 0.0 )
613 {
614 GCD = s;
615 return( s > 0.0 );
616 }
617
618 l = s;
619 s = fabs( r );
620 }
621
622 return( false );
623}
624
638
639inline bool getWholeStepping( const double SSampleRate,
640 const double DSampleRate, int& ResInStep, int& ResOutStep )
641{
642 double GCD;
643
644 if( !findGCD( SSampleRate, DSampleRate, GCD ))
645 {
646 return( false );
647 }
648
649 const double InStep0 = SSampleRate / GCD;
650 const double OutStep0 = DSampleRate / GCD;
651
652 if( OutStep0 > 1500.0 )
653 {
654 // Do not allow large output stepping due to low cache
655 // performance of large filter banks.
656
657 return( false );
658 }
659
660 ResInStep = (int) InStep0;
661 ResOutStep = (int) OutStep0;
662
663 // Check for double-to-int precision and overflows.
664
665 if( InStep0 != ResInStep || OutStep0 != ResOutStep )
666 {
667 return( false );
668 }
669
670 return( true );
671}
672
685
686class CDSPFracInterpolator : public CDSPProcessor
687{
688public:
702
703 CDSPFracInterpolator( const double aSrcSampleRate,
704 const double aDstSampleRate, const double ReqAtten,
705 const bool IsThird, const double PrevLatency )
706 : SrcSampleRate( aSrcSampleRate )
707 , DstSampleRate( aDstSampleRate )
708 , FracStep( aSrcSampleRate / aDstSampleRate )
709 {
710 R8BASSERT( SrcSampleRate > 0.0 );
711 R8BASSERT( DstSampleRate > 0.0 );
712 R8BASSERT( PrevLatency >= 0.0 );
713 R8BASSERT( BufLenBits >= 5 );
714
715 InitFracPos = PrevLatency;
716 Latency = (int) InitFracPos;
717 InitFracPos -= Latency;
718
719 R8BASSERT( Latency >= 0 );
720
721 #if R8B_FLTTEST
722
723 IsWhole = false;
724 LatencyFrac = 0.0;
725 FilterBank = new CDSPFracDelayFilterBank( -1, 3, 8, ReqAtten,
726 IsThird );
727
728 #else // R8B_FLTTEST
729
730 IsWhole = getWholeStepping( SrcSampleRate, DstSampleRate, InStep,
731 OutStep );
732
733 if( IsWhole )
734 {
735 const double spos = InitFracPos * OutStep;
736 InitFracPosW = (int) spos;
737 LatencyFrac = ( spos - InitFracPosW ) / InStep;
738
739 FilterBank = &CDSPFracDelayFilterBankCache :: getFilterBank(
740 OutStep, 1, 2, ReqAtten, IsThird, false );
741 }
742 else
743 {
744 LatencyFrac = 0.0;
745 FilterBank = &CDSPFracDelayFilterBankCache :: getFilterBank(
746 -1, 3, 8, ReqAtten, IsThird, true );
747 }
748
749 #endif // R8B_FLTTEST
750
751 FilterLen = FilterBank -> getFilterLen();
752 fl2 = FilterLen >> 1;
753 fll = fl2 - 1;
754 flo = fll + fl2;
755 flb = BufLen - fll;
756
757 R8BASSERT(( 1 << BufLenBits ) >= FilterLen * 3 );
758
759 static const CConvolveFn FltConvFn0[ 13 ] = {
760 &CDSPFracInterpolator :: convolve0< 6 >,
761 &CDSPFracInterpolator :: convolve0< 8 >,
762 &CDSPFracInterpolator :: convolve0< 10 >,
763 &CDSPFracInterpolator :: convolve0< 12 >,
764 &CDSPFracInterpolator :: convolve0< 14 >,
765 &CDSPFracInterpolator :: convolve0< 16 >,
766 &CDSPFracInterpolator :: convolve0< 18 >,
767 &CDSPFracInterpolator :: convolve0< 20 >,
768 &CDSPFracInterpolator :: convolve0< 22 >,
769 &CDSPFracInterpolator :: convolve0< 24 >,
770 &CDSPFracInterpolator :: convolve0< 26 >,
771 &CDSPFracInterpolator :: convolve0< 28 >,
772 &CDSPFracInterpolator :: convolve0< 30 >
773 };
774
775 convfn = ( IsWhole ? FltConvFn0[ fl2 - 3 ] :
776 &CDSPFracInterpolator :: convolve2 );
777
778 R8BCONSOLE( "CDSPFracInterpolator: src=%.2f dst=%.2f taps=%i "
779 "fracs=%i whole=%i third=%i step=%.6f\n", SrcSampleRate,
780 DstSampleRate, FilterLen, ( IsWhole ? OutStep :
781 FilterBank -> getFilterFracs() ), (int) IsWhole, (int) IsThird,
782 aSrcSampleRate / aDstSampleRate );
783
784 clear();
785 }
786
787 virtual int getInLenBeforeOutPos( int ReqOutPos ) const
788 {
789 if( Next != R8B_NULL )
790 {
791 ReqOutPos = Next -> getInLenBeforeOutPos( ReqOutPos );
792 }
793
794 const int ilat = fl2 + Latency;
795
796 if( IsWhole )
797 {
798 return( ilat + (int) (( InitFracPosW +
799 (double) ReqOutPos * InStep ) / OutStep +
800 LatencyFrac * InStep / OutStep ));
801 }
802
803 return( ilat + (int) ( InitFracPos + ReqOutPos * SrcSampleRate /
804 DstSampleRate ));
805 }
806
807 virtual int getLatency() const
808 {
809 return( 0 );
810 }
811
812 virtual double getLatencyFrac() const
813 {
814 return( LatencyFrac );
815 }
816
817 virtual int getMaxOutLen( const int MaxInLen ) const
818 {
819 R8BASSERT( MaxInLen >= 0 );
820
821 return( (int) ceil( MaxInLen * DstSampleRate / SrcSampleRate ) + 1 );
822 }
823
824 virtual void clear()
825 {
826 LatencyLeft = Latency;
827 BufLeft = 0;
828 WritePos = 0;
829 ReadPos = flb; // Set "read" position to account for filter's
830 // latency at zero fractional delay.
831
832 memset( &Buf[ ReadPos ], 0,
833 (size_t) ( BufLen - flb ) * sizeof( Buf[ 0 ]));
834
835 if( IsWhole )
836 {
837 InPosFracW = InitFracPosW;
838 }
839 else
840 {
841 InPosFrac = InitFracPos;
842 InPosInt = 0;
843 InPosShift = InitFracPos / SrcSampleRate * DstSampleRate;
844 }
845 }
846
847 virtual int process( double* ip, int l, double*& op0 )
848 {
849 R8BASSERT( l >= 0 );
850 R8BASSERT( ip != op0 || l == 0 || SrcSampleRate > DstSampleRate );
851
852 if( LatencyLeft != 0 )
853 {
854 if( LatencyLeft >= l )
855 {
856 LatencyLeft -= l;
857 return( 0 );
858 }
859
860 l -= LatencyLeft;
861 ip += LatencyLeft;
862 LatencyLeft = 0;
863 }
864
865 double* op = op0;
866
867 while( l > 0 )
868 {
869 // Copy new input samples to the ring buffer.
870
871 const int b = min( l, min( BufLen - WritePos, flb - BufLeft ));
872
873 double* const wp1 = Buf + WritePos;
874 memcpy( wp1, ip, (size_t) b * sizeof( wp1[ 0 ]));
875 const int ec = flo - WritePos;
876
877 if( ec > 0 )
878 {
879 memcpy( wp1 + BufLen, ip,
880 (size_t) min( b, ec ) * sizeof( wp1[ 0 ]));
881 }
882
883 ip += b;
884 WritePos = ( WritePos + b ) & BufLenMask;
885 l -= b;
886 BufLeft += b;
887
888 // Produce as many output samples as possible.
889
890 op = ( *this.*convfn )( op );
891 }
892
893 if( !IsWhole && (int) InPosShift > 1000 )
894 {
895 // Reset the interpolation position counter to achieve a higher
896 // sample-timing precision.
897
898 InPosInt = 0;
899 InPosShift = InPosFrac / SrcSampleRate * DstSampleRate;
900 }
901
902 return( (int) ( op - op0 ));
903 }
904
905private:
906 static const int BufLenBits = 8;
914 static const int BufLen = 1 << BufLenBits;
917 static const int BufLenMask = BufLen - 1;
919 double Buf[ BufLen + 29 ];
921 double SrcSampleRate;
922 double DstSampleRate;
923 double InitFracPos;
925 int InitFracPosW;
927 int Latency;
928 double LatencyFrac;
930 int FilterLen;
931 int fll;
932 int fl2;
933 int flo;
934 int flb;
935 int InStep;
936 int OutStep;
938 int LatencyLeft;
939 int BufLeft;
940 int WritePos;
942 int ReadPos;
943 int InPosFracW;
946 int InPosInt;
947 double InPosFrac;
948 double FracStep;
949 double InPosShift;
950
951 #if !R8B_FLTTEST
954 #else // !R8B_FLTTEST
956 #endif // !R8B_FLTTEST
957
958 bool IsWhole;
959
960 typedef double*( CDSPFracInterpolator ::* CConvolveFn )( double* op );
962 CConvolveFn convfn;
963
971
972 template< int fltlen >
973 double* convolve0( double* op )
974 {
975 const CDSPFracDelayFilterBank& fb = *FilterBank;
976 const int istep = InStep;
977 const int ostep = OutStep;
978 int fpos = InPosFracW;
979 int rpos = ReadPos;
980 int bl = BufLeft - fl2;
981
982 while( bl > 0 )
983 {
984 const double* const ftp = &fb[ fpos ];
985 const double* const rp = Buf + rpos;
986 int i;
987
988 #if defined( R8B_SSE2 ) && !defined( __INTEL_COMPILER )
989
990 __m128d s = _mm_setzero_pd();
991
992 for( i = 0; i < fltlen; i += 2 )
993 {
994 const __m128d m = _mm_mul_pd( _mm_load_pd( ftp + i ),
995 _mm_loadu_pd( rp + i ));
996
997 s = _mm_add_pd( s, m );
998 }
999
1000 _mm_storel_pd( op, _mm_add_pd( s, _mm_shuffle_pd( s, s, 1 )));
1001
1002 #elif defined( R8B_NEON )
1003
1004 float64x2_t s = vdupq_n_f64( 0.0 );
1005
1006 for( i = 0; i < fltlen; i += 2 )
1007 {
1008 s = vmlaq_f64( s, vld1q_f64( ftp + i ), vld1q_f64( rp + i ));
1009 }
1010
1011 *op = vaddvq_f64( s );
1012
1013 #else // SIMD
1014
1015 double s = 0.0;
1016
1017 for( i = 0; i < fltlen; i++ )
1018 {
1019 s += ftp[ i ] * rp[ i ];
1020 }
1021
1022 *op = s;
1023
1024 #endif // SIMD
1025
1026 op++;
1027
1028 fpos += istep;
1029 const int PosIncr = fpos / ostep;
1030 fpos -= PosIncr * ostep;
1031
1032 rpos = ( rpos + PosIncr ) & BufLenMask;
1033 bl -= PosIncr;
1034 }
1035
1036 BufLeft = bl + fl2;
1037 ReadPos = rpos;
1038 InPosFracW = fpos;
1039
1040 return( op );
1041 }
1042
1049
1050 double* convolve2( double* op )
1051 {
1052 const CDSPFracDelayFilterBank& fb = *FilterBank;
1053 const int fltlen = FilterLen;
1054 const double ffracs = fb.getFilterFracs();
1055 const double fs = FracStep;
1056 int ipos = InPosInt;
1057 double fpos = InPosFrac;
1058 double psh = InPosShift;
1059 int rpos = ReadPos;
1060 int bl = BufLeft - fl2;
1061
1062 while( bl > 0 )
1063 {
1064 double x = fpos * ffracs;
1065 const int fti = (int) x; // Function table index.
1066 const double* ftp = &fb[ fti ];
1067 x -= fti; // Coefficient for interpolation between adjacent
1068 // fractional delay filters.
1069 const double* const rp = Buf + rpos;
1070 const double x2d = x * x;
1071 int i;
1072
1073 #if defined( R8B_SSE2 ) && defined( R8B_SIMD_ISH )
1074
1075 const __m128d x1 = _mm_set1_pd( x );
1076 const __m128d x2 = _mm_set1_pd( x2d );
1077 __m128d s = _mm_setzero_pd();
1078
1079 for( i = 0; i < fltlen; i += 2 )
1080 {
1081 const __m128d ftp2 = _mm_load_pd( ftp + 2 );
1082 const __m128d xx1 = _mm_mul_pd( ftp2, x1 );
1083 const __m128d ftp4 = _mm_load_pd( ftp + 4 );
1084 const __m128d xx2 = _mm_mul_pd( ftp4, x2 );
1085 const __m128d ftp0 = _mm_load_pd( ftp );
1086 ftp += 6;
1087
1088 const __m128d rpi = _mm_loadu_pd( rp + i );
1089 const __m128d xxs = _mm_add_pd( ftp0, _mm_add_pd( xx1, xx2 ));
1090
1091 s = _mm_add_pd( s, _mm_mul_pd( rpi, xxs ));
1092 }
1093
1094 _mm_storel_pd( op, _mm_add_pd( s, _mm_shuffle_pd( s, s, 1 )));
1095
1096 #elif defined( R8B_NEON ) && defined( R8B_SIMD_ISH )
1097
1098 const float64x2_t x1 = vdupq_n_f64( x );
1099 const float64x2_t x2 = vdupq_n_f64( x2d );
1100 float64x2_t s = vdupq_n_f64( 0.0 );
1101
1102 for( i = 0; i < fltlen; i += 2 )
1103 {
1104 const float64x2_t ftp2 = vld1q_f64( ftp + 2 );
1105 const float64x2_t xx1 = vmulq_f64( ftp2, x1 );
1106 const float64x2_t ftp4 = vld1q_f64( ftp + 4 );
1107 const float64x2_t xx2 = vmulq_f64( ftp4, x2 );
1108 const float64x2_t ftp0 = vld1q_f64( ftp );
1109 ftp += 6;
1110
1111 const float64x2_t rpi = vld1q_f64( rp + i );
1112 const float64x2_t xxs = vaddq_f64( ftp0,
1113 vaddq_f64( xx1, xx2 ));
1114
1115 s = vmlaq_f64( s, rpi, xxs );
1116 }
1117
1118 *op = vaddvq_f64( s );
1119
1120 #else // SIMD
1121
1122 double s = 0.0;
1123
1124 for( i = 0; i < fltlen; i++ )
1125 {
1126 s += ( ftp[ 0 ] + ftp[ 1 ] * x + ftp[ 2 ] * x2d ) * rp[ i ];
1127 ftp += 3;
1128 }
1129
1130 *op = s;
1131
1132 #endif // SIMD
1133
1134 psh += 1.0;
1135 op++;
1136
1137 const double NextInPos = psh * fs;
1138 const int NextInPosInt = (int) NextInPos;
1139 const int PosIncr = NextInPosInt - ipos;
1140
1141 fpos = NextInPos - NextInPosInt;
1142 ipos = NextInPosInt;
1143
1144 bl -= PosIncr;
1145 rpos = ( rpos + PosIncr ) & BufLenMask;
1146 }
1147
1148 BufLeft = bl + fl2;
1149 ReadPos = rpos;
1150 InPosInt = ipos;
1151 InPosFrac = fpos;
1152 InPosShift = psh;
1153
1154 return( op );
1155 }
1156};
1157
1158// ---------------------------------------------------------------------------
1159
1160} // namespace r8b
1161
1162#endif // R8B_CDSPFRACINTERPOLATOR_INCLUDED
The base virtual class for DSP processing algorithms.
Sinc function-based FIR filter generator class.
#define R8BSYNC(SyncObject)
Thread synchronization macro.
Definition r8bbase.h:833
#define R8B_EXITDTOR
Macro that defines the attribute specifying that the exit-time destructor should be called for a stat...
Definition r8bbase.h:149
#define R8B_NULL
The "null pointer" value, portable between C++11 and earlier C++ versions.
Definition r8bbase.h:101
#define R8BNOCTOR(ClassName)
Macro that defines empty copy-constructor and copy operator.
Definition r8bbase.h:213
#define R8BASSERT(e)
Assertion macro used to check for certain run-time conditions. By default, no action is taken if asse...
Definition r8bconf.h:28
#define R8B_BASECLASS
Macro defines the name of the class from which all classes that are designed to be created on heap ar...
Definition r8bconf.h:56
#define R8B_FRACBANK_CACHE_MAX
Macro specifies the number of whole-number stepping fractional delay filter banks kept in the cache a...
Definition r8bconf.h:103
#define R8BCONSOLE(...)
Console output macro, used to output various resampler status strings, including filter design parame...
Definition r8bconf.h:41
The "r8brain-free-src" library namespace.
Definition CDSPBlockConvolver.h:22
bool getWholeStepping(const double SSampleRate, const double DSampleRate, int &ResInStep, int &ResOutStep)
Evaluates source and destination sample rate ratio and returns the required input and output stepping...
Definition CDSPFracInterpolator.h:639
T min(const T &v1, const T &v2)
Returns minimum of two values.
Definition r8bbase.h:1257
void calcSpline3p8Coeffs(double *const c, const double xm3, const double xm2, const double xm1, const double x0, const double x1, const double x2, const double x3, const double x4)
Calculates 3rd order spline coefficients, using 8 points.
Definition r8bbase.h:1158
void calcSpline2p8Coeffs(double *const c, const double xm3, const double xm2, const double xm1, const double x0, const double x1, const double x2, const double x3, const double x4)
Calculates 2nd order spline coefficients, using 8 points.
Definition r8bbase.h:1192
bool findGCD(double l, double s, double &GCD)
Interatively searches for a greatest common denominator (GCD) of 2 numbers.
Definition CDSPFracInterpolator.h:604
void normalizeFIRFilter(double *const p, const int l, const double DCGain, const int pstep=1)
FIR filter's gain normalization.
Definition r8bbase.h:1112
Sinc function-based fractional delay filter bank class.
Definition CDSPFracInterpolator.h:40
void unref()
Reduces reference count to this object.
Definition CDSPFracInterpolator.h:587
int getFilterFracs() const
Returns the number of fractional positions sampled by the bank.
Definition CDSPFracInterpolator.h:220
const double & operator[](const int i) const
Returns reference to the filter.
Definition CDSPFracInterpolator.h:231
static void roundReqAtten(double &att, const bool aIsThird)
Rounds the specified attenuation to the nearest effective value.
Definition CDSPFracInterpolator.h:200
int getFilterLen() const
Returns the length of the filter, in samples (taps). Always an even number, not less than 6.
Definition CDSPFracInterpolator.h:211
CDSPFracDelayFilterBank(const int aFilterFracs, const int aElementSize, const int aInterpPoints, const double aReqAtten, const bool aIsThird)
Initializes the filter bank object.
Definition CDSPFracInterpolator.h:63
Fractional delay filter cache class.
Definition CDSPFracInterpolator.h:417
static CDSPFracDelayFilterBank & getFilterBank(const int aFilterFracs, const int aElementSize, const int aInterpPoints, double ReqAtten, const bool IsThird, const bool IsStatic)
Calculates or returns reference to a previously calculated (cached) fractional delay filter bank.
Definition CDSPFracInterpolator.h:439
static CSyncObject & getStateSync()
Returns reference to global filter bank cache sync object.
Definition CDSPFracInterpolator.h:575
Fractional delay filter bank-based interpolator class.
Definition CDSPFracInterpolator.h:687
CDSPFracInterpolator(const double aSrcSampleRate, const double aDstSampleRate, const double ReqAtten, const bool IsThird, const double PrevLatency)
Initalizes the interpolator. It is important to call the getMaxOutLen() function afterwards to obtain...
Definition CDSPFracInterpolator.h:703
virtual int process(double *ip, int l, double *&op0)
Performs DSP processing.
Definition CDSPFracInterpolator.h:847
virtual int getInLenBeforeOutPos(int ReqOutPos) const
Returns the number of input samples required to advance to the specified output sample position (so t...
Definition CDSPFracInterpolator.h:787
virtual int getLatency() const
Return the latency, in samples, which is present in the output signal.
Definition CDSPFracInterpolator.h:807
virtual int getMaxOutLen(const int MaxInLen) const
Returns the maximal length of the output buffer required when processing the MaxInLen number of input...
Definition CDSPFracInterpolator.h:817
virtual double getLatencyFrac() const
Returns fractional latency, in samples, which is present in the output signal.
Definition CDSPFracInterpolator.h:812
virtual void clear()
Clears (resets) the state of this object and returns it to the state after construction.
Definition CDSPFracInterpolator.h:824
Sinc function-based FIR filter generator class.
Definition CDSPSincFilterGen.h:33
double FracDelay
Fractional delay in the range [0; 1], used only in the generateFrac() function. Note that the FracDel...
Definition CDSPSincFilterGen.h:52
void generateFrac(double *op, CWindowFunc wfunc=&CDSPSincFilterGen ::calcWindowBlackman, const int opinc=1)
Calculates windowed fractional delay filter kernel.
Definition CDSPSincFilterGen.h:452
void initFrac(const EWindowFunctionType WinType=wftCosine, const double *const Params=R8B_NULL, const bool UsePower=false)
Initializes this structure for generation of full-bandwidth fractional delay sinc filter kernel.
Definition CDSPSincFilterGen.h:168
double Len2
Required half filter kernel's length in samples (can be a fractional value). Final physical kernel le...
Definition CDSPSincFilterGen.h:35
Pointer-to-object "keeper" class with automatic deletion.
Definition r8bbase.h:457
Reference "keeper" class with automatic unref() call.
Definition r8bbase.h:557
CDSPProcessor * Next
Definition r8bbase.h:652
Multi-threaded synchronization object class.
Definition r8bbase.h:691