r8brain-free-src
High-quality pro audio sample rate converter library
 
Loading...
Searching...
No Matches
CDSPResampler.h
Go to the documentation of this file.
1//$ nobt
2//$ nocpp
3
16
17#ifndef R8B_CDSPRESAMPLER_INCLUDED
18#define R8B_CDSPRESAMPLER_INCLUDED
19
20#include "CDSPHBDownsampler.h"
21#include "CDSPHBUpsampler.h"
22#include "CDSPBlockConvolver.h"
24
25namespace r8b {
26
51
52class CDSPResampler : public CDSPProcessor
53{
54public:
121
122 CDSPResampler( const double SrcSampleRate, const double DstSampleRate,
123 const int aMaxInLen, const double ReqTransBand = 2.0,
124 const double ReqAtten = 206.91,
125 const EDSPFilterPhaseResponse ReqPhase = fprLinearPhase )
126 : StepCapacity( 0 )
127 , StepCount( 0 )
128 , MaxInLen( aMaxInLen )
129 , CurMaxOutLen( aMaxInLen )
130 , LatencyFrac( 0.0 )
131 {
132 R8BASSERT( SrcSampleRate > 0.0 );
133 R8BASSERT( DstSampleRate > 0.0 );
134 R8BASSERT( MaxInLen > 0 );
135
136 R8BCONSOLE( "* CDSPResampler: src=%.1f dst=%.1f len=%i tb=%.1f "
137 "att=%.2f ph=%i\n", SrcSampleRate, DstSampleRate, aMaxInLen,
138 ReqTransBand, ReqAtten, (int) ReqPhase );
139
140 if( SrcSampleRate == DstSampleRate )
141 {
142 return;
143 }
144
145 TmpBufCapacities[ 0 ] = 0;
146 TmpBufCapacities[ 1 ] = 0;
147 CurTmpBuf = 0;
148
149 // Try some common efficient ratios requiring only a single step.
150
151 const int CommonRatioCount = 5;
152 const int CommonRatios[ CommonRatioCount ][ 2 ] = {
153 { 1, 2 },
154 { 1, 3 },
155 { 2, 3 },
156 { 3, 2 },
157 { 3, 4 }
158 };
159
160 int i;
161
162 for( i = 0; i < CommonRatioCount; i++ )
163 {
164 const int num = CommonRatios[ i ][ 0 ];
165 const int den = CommonRatios[ i ][ 1 ];
166
167 if( SrcSampleRate * num == DstSampleRate * den )
168 {
169 addProcessor( new CDSPBlockConvolver(
170 CDSPFIRFilterCache :: getLPFilter(
171 1.0 / ( num > den ? num : den ), ReqTransBand,
172 ReqAtten, ReqPhase, num ), num, den, LatencyFrac ));
173
174 createTmpBuffers();
175 return;
176 }
177 }
178
179 // Try whole-number power-of-2 or 3*power-of-2 upsampling.
180
181 for( i = 2; i <= 3; i++ )
182 {
183 bool WasFound = false;
184 double sm = i;
185 int c = 0;
186
187 while( true )
188 {
189 const double NewSR = SrcSampleRate * sm;
190
191 if( NewSR == DstSampleRate )
192 {
193 WasFound = true;
194 break;
195 }
196
197 if( NewSR > DstSampleRate )
198 {
199 break;
200 }
201
202 sm *= 2.0;
203 c++;
204 }
205
206 if( WasFound )
207 {
208 addProcessor( new CDSPBlockConvolver(
209 CDSPFIRFilterCache :: getLPFilter( 1.0 / i, ReqTransBand,
210 ReqAtten, ReqPhase, i ), i, 1, LatencyFrac ));
211
212 const bool IsThird = ( i == 3 );
213
214 for( i = 0; i < c; i++ )
215 {
216 addProcessor( new CDSPHBUpsampler( ReqAtten, i, IsThird,
217 LatencyFrac ));
218 }
219
220 createTmpBuffers();
221 return;
222 }
223 }
224
225 if( DstSampleRate * 2.0 > SrcSampleRate )
226 {
227 // Upsampling or fractional downsampling down to 2X.
228
229 const double NormFreq = ( DstSampleRate > SrcSampleRate ? 0.5 :
230 0.5 * DstSampleRate / SrcSampleRate );
231
232 addProcessor( new CDSPBlockConvolver(
233 CDSPFIRFilterCache :: getLPFilter( NormFreq, ReqTransBand,
234 ReqAtten, ReqPhase, 2.0 ), 2, 1, LatencyFrac ));
235
236 // Try intermediate interpolated resampling with subsequent 2X
237 // or 3X upsampling.
238
239 const double tbw = 0.0175; // Intermediate filter's transition
240 // band extension coefficient.
241 const double ThreshSampleRate = SrcSampleRate /
242 ( 1.0 - tbw * ReqTransBand ); // Make sure intermediate
243 // filter's transition band is not steeper than ReqTransBand
244 // (this keeps the latency under control).
245
246 int c = 0;
247 double div = 1.0;
248
249 while( true )
250 {
251 const double ndiv = div * 2.0;
252
253 if( DstSampleRate < ThreshSampleRate * ndiv )
254 {
255 break;
256 }
257
258 div = ndiv;
259 c++;
260 }
261
262 int c2 = 0;
263 double div2 = 1.0;
264
265 while( true )
266 {
267 const double ndiv = div * ( c2 == 0 ? 3.0 : 2.0 );
268
269 if( DstSampleRate < ThreshSampleRate * ndiv )
270 {
271 break;
272 }
273
274 div2 = ndiv;
275 c2++;
276 }
277
278 const double SrcSampleRate2 = SrcSampleRate * 2.0;
279 int tmp1;
280 int tmp2;
281
282 if( c == 1 && getWholeStepping( SrcSampleRate2, DstSampleRate,
283 tmp1, tmp2 ))
284 {
285 // Do not use intermediate interpolation if whole stepping is
286 // available as it performs very fast.
287
288 c = 0;
289 }
290
291 if( c > 0 )
292 {
293 // Add steps using intermediate interpolation.
294
295 int num;
296
297 if( c2 > 0 && div2 > div )
298 {
299 div = div2;
300 c = c2;
301 num = 3;
302 }
303 else
304 {
305 num = 2;
306 }
307
308 addProcessor( new CDSPFracInterpolator( SrcSampleRate2 * div,
309 DstSampleRate, ReqAtten, false, LatencyFrac ));
310
311 double tb = ( 1.0 - SrcSampleRate * div / DstSampleRate ) /
312 tbw; // Divide TransBand by a constant that assures a
313 // linear response in the pass-band.
314
315 if( tb > CDSPFIRFilter :: getLPMaxTransBand() )
316 {
317 tb = CDSPFIRFilter :: getLPMaxTransBand();
318 }
319
320 addProcessor( new CDSPBlockConvolver(
321 CDSPFIRFilterCache :: getLPFilter( 1.0 / num, tb,
322 ReqAtten, ReqPhase, num ), num, 1, LatencyFrac ));
323
324 const bool IsThird = ( num == 3 );
325
326 for( i = 1; i < c; i++ )
327 {
328 addProcessor( new CDSPHBUpsampler( ReqAtten, i - 1,
329 IsThird, LatencyFrac ));
330 }
331 }
332 else
333 {
334 addProcessor( new CDSPFracInterpolator( SrcSampleRate2,
335 DstSampleRate, ReqAtten, false, LatencyFrac ));
336 }
337
338 createTmpBuffers();
339 return;
340 }
341
342 // Use downsampling steps, including power-of-2 downsampling.
343
344 double CheckSR = DstSampleRate * 4.0;
345 double SrcSRDiv = 1.0;
346 int c = 0;
347
348 while( CheckSR <= SrcSampleRate )
349 {
350 c++;
351 CheckSR *= 2.0;
352 SrcSRDiv *= 2.0;
353 }
354
355 const double FinGain = 1.0 / SrcSRDiv;
356
357 double NormFreq = 0.5;
358 bool UseInterp = true;
359 bool IsThird = false;
360 int downf;
361
362 for( downf = 2; downf <= 3; downf++ )
363 {
364 if( DstSampleRate * SrcSRDiv * downf == SrcSampleRate )
365 {
366 NormFreq = 1.0 / downf;
367 UseInterp = false;
368 IsThird = ( downf == 3 );
369 break;
370 }
371 }
372
373 if( UseInterp )
374 {
375 downf = 1;
376 NormFreq = DstSampleRate * SrcSRDiv / SrcSampleRate;
377 IsThird = ( NormFreq * 3.0 <= 1.0 );
378 }
379
380 for( i = 0; i < c; i++ )
381 {
382 // Use fixed, very relaxed 2X downsampling filters, that at the
383 // final stage only guarantee stop-band between 0.75 and 1.0 of
384 // Nyquist. 0.5-0.75 range will be aliased to 0.25-0.5 range which
385 // will then be filtered out by the final filter.
386
387 addProcessor( new CDSPHBDownsampler( ReqAtten, c - 1 - i, IsThird,
388 LatencyFrac ));
389 }
390
391 addProcessor( new CDSPBlockConvolver(
392 CDSPFIRFilterCache :: getLPFilter( NormFreq, ReqTransBand,
393 ReqAtten, ReqPhase, FinGain ), 1, downf, LatencyFrac ));
394
395 if( UseInterp )
396 {
397 addProcessor( new CDSPFracInterpolator( SrcSampleRate,
398 DstSampleRate * SrcSRDiv, ReqAtten, IsThird, LatencyFrac ));
399 }
400
401 createTmpBuffers();
402 }
403
404 virtual ~CDSPResampler()
405 {
406 int i;
407
408 for( i = 0; i < StepCount; i++ )
409 {
410 delete Steps[ i ];
411 }
412 }
413
414 virtual int getInLenBeforeOutPos( const int ReqOutPos ) const
415 {
416 R8BASSERT( ReqOutPos >= 0 );
417
418 int ReqInSamples = ReqOutPos;
419 int c = StepCount;
420
421 while( --c >= 0 )
422 {
423 ReqInSamples = Steps[ c ] -> getInLenBeforeOutPos( ReqInSamples );
424 }
425
426 return( ReqInSamples );
427 }
428
450
451 int getInLenBeforeOutStart( const int ReqOutPos = 0 )
452 {
453 R8BASSERT( ReqOutPos >= 0 );
454
455 int inc = 0;
456 int outc = 0;
457
458 while( true )
459 {
460 double ins = 0.0;
461 double* op;
462 outc += process( &ins, 1, op );
463
464 if( outc > ReqOutPos )
465 {
466 clear();
467 return( inc );
468 }
469
470 inc++;
471 }
472 }
473
483
484 int getInputRequiredForOutput( const int ReqOutSamples ) const
485 {
486 if( ReqOutSamples < 1 )
487 {
488 return( 0 );
489 }
490
491 return( getInLenBeforeOutPos( ReqOutSamples - 1 ) + 1 );
492 }
493
494 virtual int getLatency() const
495 {
496 return( 0 );
497 }
498
499 virtual double getLatencyFrac() const
500 {
501 return( LatencyFrac );
502 }
503
509
510 virtual int getMaxOutLen( const int/* MaxInLen */) const
511 {
512 return( CurMaxOutLen );
513 }
514
528
529 virtual void clear()
530 {
531 int i;
532
533 for( i = 0; i < StepCount; i++ )
534 {
535 Steps[ i ] -> clear();
536 }
537 }
538
566
567 virtual int process( double* ip0, int l, double*& op0 )
568 {
569 R8BASSERT( l >= 0 );
570
571 double* ip = ip0;
572 int i;
573
574 for( i = 0; i < StepCount; i++ )
575 {
576 double* op = TmpBufs[ i & 1 ];
577 l = Steps[ i ] -> process( ip, l, op );
578 ip = op;
579 }
580
581 op0 = ip;
582 return( l );
583 }
584
599
600 template< typename Tin, typename Tout >
601 void oneshot( Tin* ip, int iplen, Tout* op, int oplen )
602 {
603 CFixedBuffer< double > Buf( MaxInLen );
604 bool IsZero = false;
605
606 while( oplen > 0 )
607 {
608 int rc;
609 double* p;
610 int i;
611
612 if( iplen == 0 )
613 {
614 rc = MaxInLen;
615 p = &Buf[ 0 ];
616
617 if( !IsZero )
618 {
619 IsZero = true;
620 memset( p, 0, MaxInLen * sizeof( p[ 0 ]));
621 }
622 }
623 else
624 {
625 rc = min( iplen, MaxInLen );
626 p = getOneshotBuf( ip, Buf, rc );
627 ip += rc;
628 iplen -= rc;
629 }
630
631 double* op0;
632 int wc = process( p, rc, op0 );
633 wc = min( oplen, wc );
634
635 for( i = 0; i < wc; i++ )
636 {
637 op[ i ] = (Tout) op0[ i ];
638 }
639
640 op += wc;
641 oplen -= wc;
642 }
643
644 clear();
645 }
646
647private:
649 int StepCapacity;
650 int StepCount;
651 int MaxInLen;
652 CFixedBuffer< double > TmpBufAll;
654 double* TmpBufs[ 2 ];
655 int TmpBufCapacities[ 2 ];
657 int CurTmpBuf;
658 int CurMaxOutLen;
659 double LatencyFrac;
662
670
671 void addProcessor( CDSPProcessor* const Proc )
672 {
673 if( StepCount == StepCapacity )
674 {
675 // Reallocate and increase Steps array's capacity.
676
677 const int NewCapacity = StepCapacity + 8;
678 Steps.realloc( StepCapacity, NewCapacity );
679 StepCapacity = NewCapacity;
680 }
681
682 LatencyFrac = Proc -> getLatencyFrac();
683 CurMaxOutLen = Proc -> getMaxOutLen( CurMaxOutLen );
684
685 if( CurMaxOutLen > TmpBufCapacities[ CurTmpBuf ])
686 {
687 TmpBufCapacities[ CurTmpBuf ] = CurMaxOutLen;
688 }
689
690 CurTmpBuf ^= 1;
691
692 Steps[ StepCount ] = Proc;
693 StepCount++;
694 }
695
699
700 void createTmpBuffers()
701 {
702 const int ol = TmpBufCapacities[ 0 ] + TmpBufCapacities[ 1 ];
703
704 if( ol > 0 )
705 {
706 TmpBufAll.alloc( ol );
707 TmpBufs[ 0 ] = &TmpBufAll[ 0 ];
708 TmpBufs[ 1 ] = &TmpBufAll[ TmpBufCapacities[ 0 ]];
709 }
710
711 R8BCONSOLE( "* CDSPResampler: init done\n" );
712 }
713
721
722 static double* getOneshotBuf( double* const ip, double* const, const int )
723 {
724 return( ip );
725 }
726
736
737 static double* getOneshotBuf( float* const ip, double* const Buf,
738 const int rc )
739 {
740 int i;
741
742 for( i = 0; i < rc; i++ )
743 {
744 Buf[ i ] = ip[ i ];
745 }
746
747 return( Buf );
748 }
749};
750
758
760{
761public:
772
773 CDSPResampler16( const double SrcSampleRate, const double DstSampleRate,
774 const int aMaxInLen, const double ReqTransBand = 2.0 )
775 : CDSPResampler( SrcSampleRate, DstSampleRate, aMaxInLen, ReqTransBand,
776 136.45, fprLinearPhase )
777 {
778 }
779};
780
789
791{
792public:
803
804 CDSPResampler16IR( const double SrcSampleRate, const double DstSampleRate,
805 const int aMaxInLen, const double ReqTransBand = 2.0 )
806 : CDSPResampler( SrcSampleRate, DstSampleRate, aMaxInLen, ReqTransBand,
807 109.56, fprLinearPhase )
808 {
809 }
810};
811
819
821{
822public:
833
834 CDSPResampler24( const double SrcSampleRate, const double DstSampleRate,
835 const int aMaxInLen, const double ReqTransBand = 2.0 )
836 : CDSPResampler( SrcSampleRate, DstSampleRate, aMaxInLen, ReqTransBand,
837 180.15, fprLinearPhase )
838 {
839 }
840};
841
842} // namespace r8b
843
844#endif // R8B_CDSPRESAMPLER_INCLUDED
Single-block overlap-save convolution processor class.
Fractional delay interpolator and filter bank classes.
Half-band downsampling convolver class.
Half-band upsampling class.
#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 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:644
EDSPFilterPhaseResponse
Enumeration of filter's phase responses.
Definition CDSPFIRFilter.h:29
@ fprLinearPhase
Linear-phase response. Features a linear-phase, high-latency response, with the latency expressed as ...
Definition CDSPFIRFilter.h:30
T min(const T &v1, const T &v2)
Returns minimum of two values.
Definition r8bbase.h:1092
Single-block overlap-save convolution processing class.
Definition CDSPBlockConvolver.h:40
Fractional delay filter bank-based interpolator class.
Definition CDSPFracInterpolator.h:694
Half-band downsampler class.
Definition CDSPHBDownsampler.h:31
Half-band upsampling class.
Definition CDSPHBUpsampler.h:32
The base virtual class for DSP processing algorithms.
Definition CDSPProcessor.h:33
The master sample rate converter (resampler) class.
Definition CDSPResampler.h:53
virtual int getMaxOutLen(const int) const
This implementation ignores the supplied parameter and returns the maximal output buffer length that ...
Definition CDSPResampler.h:510
virtual double getLatencyFrac() const
Returns fractional latency, in samples, which is present in the output signal.
Definition CDSPResampler.h:499
virtual int getInLenBeforeOutPos(const int ReqOutPos) const
Returns the number of input samples required to advance to the specified output sample position (so t...
Definition CDSPResampler.h:414
int getInLenBeforeOutStart(const int ReqOutPos=0)
Returns the number of input samples required to advance to the specified output sample position (so t...
Definition CDSPResampler.h:451
void oneshot(Tin *ip, int iplen, Tout *op, int oplen)
Performs resampling of an input sample buffer of the specified length in the "one-shot" mode.
Definition CDSPResampler.h:601
virtual void clear()
Clears (resets) the state of this object and returns it to the state after construction.
Definition CDSPResampler.h:529
virtual int getLatency() const
Return the latency, in samples, which is present in the output signal.
Definition CDSPResampler.h:494
CDSPResampler(const double SrcSampleRate, const double DstSampleRate, const int aMaxInLen, const double ReqTransBand=2.0, const double ReqAtten=206.91, const EDSPFilterPhaseResponse ReqPhase=fprLinearPhase)
Initalizes the resampler object.
Definition CDSPResampler.h:122
virtual int process(double *ip0, int l, double *&op0)
Performs sample rate conversion.
Definition CDSPResampler.h:567
int getInputRequiredForOutput(const int ReqOutSamples) const
Returns the number of input samples required to produce at least the specified number of output sampl...
Definition CDSPResampler.h:484
CDSPResampler16(const double SrcSampleRate, const double DstSampleRate, const int aMaxInLen, const double ReqTransBand=2.0)
Initializes the 16-bit resampler. See the r8b::CDSPResampler class for details.
Definition CDSPResampler.h:773
CDSPResampler16IR(const double SrcSampleRate, const double DstSampleRate, const int aMaxInLen, const double ReqTransBand=2.0)
Initializes the 16-bit impulse response resampler. See the r8b::CDSPResampler class for details.
Definition CDSPResampler.h:804
CDSPResampler24(const double SrcSampleRate, const double DstSampleRate, const int aMaxInLen, const double ReqTransBand=2.0)
Initializes the 24-bit resampler (including 32-bit floating point). See the r8b::CDSPResampler class ...
Definition CDSPResampler.h:834
void realloc(const int PrevCapacity, const int NewCapacity)
Reallocates memory so that the specified number of elements of type T can be stored in this buffer ob...
Definition r8bbase.h:353