r8brain-free-src
High-quality pro audio sample rate converter library
 
Loading...
Searching...
No Matches
CDSPBlockConvolver.h
Go to the documentation of this file.
1//$ nobt
2//$ nocpp
3
15
16#ifndef R8B_CDSPBLOCKCONVOLVER_INCLUDED
17#define R8B_CDSPBLOCKCONVOLVER_INCLUDED
18
19#include "CDSPFIRFilter.h"
20#include "CDSPProcessor.h"
21
22namespace r8b {
23
38
39class CDSPBlockConvolver : public CDSPProcessor
40{
41public:
61
62 CDSPBlockConvolver( CDSPFIRFilter& aFilter, const int aUpFactor,
63 const int aDownFactor, const double PrevLatency = 0.0,
64 const bool aDoConsumeLatency = true )
65 : Filter( &aFilter )
66 , UpFactor( aUpFactor )
67 , DownFactor( aDownFactor )
68 , BlockLen2( 2 << Filter -> getBlockLenBits() )
69 , DoConsumeLatency( aDoConsumeLatency )
70 {
71 R8BASSERT( UpFactor > 0 );
72 R8BASSERT( DownFactor > 0 );
73 R8BASSERT( PrevLatency >= 0.0 );
74
75 int fftinBits;
76 UpShift = getBitOccupancy( UpFactor ) - 1;
77
78 if(( 1 << UpShift ) == UpFactor )
79 {
80 fftinBits = Filter -> getBlockLenBits() + 1 - UpShift;
81 PrevInputLen = ( Filter -> getKernelLen() - 1 + UpFactor - 1 ) /
82 UpFactor;
83
84 InputLen = BlockLen2 - PrevInputLen * UpFactor;
85 }
86 else
87 {
88 UpShift = -1;
89 fftinBits = Filter -> getBlockLenBits() + 1;
90 PrevInputLen = Filter -> getKernelLen() - 1;
91 InputLen = BlockLen2 - PrevInputLen;
92 }
93
94 OutOffset = ( Filter -> isZeroPhase() ? Filter -> getLatency() : 0 );
95 LatencyFrac = Filter -> getLatencyFrac() + PrevLatency * UpFactor;
96 Latency = (int) LatencyFrac;
97 const int InLatency = Latency + Filter -> getLatency() - OutOffset;
98 LatencyFrac -= Latency;
99 LatencyFrac /= DownFactor;
100
101 Latency += InputLen + Filter -> getLatency();
102
103 int fftoutBits;
104 InputDelay = 0;
105 DownSkipInit = 0;
106 DownShift = getBitOccupancy( DownFactor ) - 1;
107
108 if(( 1 << DownShift ) == DownFactor )
109 {
110 fftoutBits = Filter -> getBlockLenBits() + 1 - DownShift;
111
112 if( DownFactor > 1 )
113 {
114 if( UpShift > 0 )
115 {
116 // This case never happens in practice due to mutual
117 // exclusion of "power of 2" DownFactor and UpFactor
118 // values.
119
120 R8BASSERT( UpShift == 0 );
121 }
122 else
123 {
124 // Make sure InputLen is divisible by DownFactor.
125
126 const int ilc = InputLen & ( DownFactor - 1 );
127 PrevInputLen += ilc;
128 InputLen -= ilc;
129 Latency -= ilc;
130
131 // Correct InputDelay for input and filter's latency.
132
133 const int lc = InLatency & ( DownFactor - 1 );
134
135 if( lc > 0 )
136 {
137 InputDelay = DownFactor - lc;
138 }
139
140 if( !DoConsumeLatency )
141 {
142 Latency /= DownFactor;
143 }
144 }
145 }
146 }
147 else
148 {
149 fftoutBits = Filter -> getBlockLenBits() + 1;
150 DownShift = -1;
151
152 if( !DoConsumeLatency && DownFactor > 1 )
153 {
154 DownSkipInit = Latency % DownFactor;
155 Latency /= DownFactor;
156 }
157 }
158
159 R8BASSERT( Latency >= 0 );
160
161 fftin = new CDSPRealFFTKeeper( fftinBits );
162
163 if( fftoutBits == fftinBits )
164 {
165 fftout = fftin;
166 }
167 else
168 {
169 ffto2 = new CDSPRealFFTKeeper( fftoutBits );
170 fftout = ffto2;
171 }
172
173 WorkBlocks.alloc( BlockLen2 * 2 + PrevInputLen );
174 CurInput = &WorkBlocks[ 0 ];
175 CurOutput = &WorkBlocks[ BlockLen2 ]; // CurInput and
176 // CurOutput are address-aligned.
177 PrevInput = &WorkBlocks[ BlockLen2 * 2 ];
178
179 clear();
180
181 R8BCONSOLE( "CDSPBlockConvolver: flt_len=%i in_len=%i io=%i/%i "
182 "fft=%i/%i latency=%i\n", Filter -> getKernelLen(), InputLen,
183 UpFactor, DownFactor, (*fftin) -> getLen(), (*fftout) -> getLen(),
184 getLatency() );
185 }
186
187 virtual ~CDSPBlockConvolver()
188 {
189 Filter -> unref();
190 }
191
192 virtual int getInLenBeforeOutPos( const int ReqOutPos ) const
193 {
194 return( (int) (( Latency + (double) ReqOutPos * DownFactor ) /
195 UpFactor + LatencyFrac * DownFactor / UpFactor ));
196 }
197
198 virtual int getLatency() const
199 {
200 return( DoConsumeLatency ? 0 : Latency );
201 }
202
203 virtual double getLatencyFrac() const
204 {
205 return( LatencyFrac );
206 }
207
208 virtual int getMaxOutLen( const int MaxInLen ) const
209 {
210 R8BASSERT( MaxInLen >= 0 );
211
212 return(( MaxInLen * UpFactor + DownFactor - 1 ) / DownFactor );
213 }
214
215 virtual void clear()
216 {
217 memset( &PrevInput[ 0 ], 0,
218 (size_t) PrevInputLen * sizeof( PrevInput[ 0 ]));
219
220 if( DoConsumeLatency )
221 {
222 LatencyLeft = Latency;
223 }
224 else
225 {
226 LatencyLeft = 0;
227
228 if( DownShift > 0 )
229 {
230 memset( &CurOutput[ 0 ], 0,
231 (size_t) ( BlockLen2 >> DownShift ) *
232 sizeof( CurOutput[ 0 ]));
233 }
234 else
235 {
236 memset( &CurOutput[ BlockLen2 - OutOffset ], 0,
237 (size_t) OutOffset * sizeof( CurOutput[ 0 ]));
238
239 memset( &CurOutput[ 0 ], 0,
240 (size_t) ( InputLen - OutOffset ) *
241 sizeof( CurOutput[ 0 ]));
242 }
243 }
244
245 memset( CurInput, 0, (size_t) InputDelay * sizeof( CurInput[ 0 ]));
246
247 InDataLeft = InputLen - InputDelay;
248 UpSkip = 0;
249 DownSkip = DownSkipInit;
250 }
251
252 virtual int process( double* ip, int l0, double*& op0 )
253 {
254 R8BASSERT( l0 >= 0 );
255 R8BASSERT( UpFactor / DownFactor <= 1 || ip != op0 || l0 == 0 );
256
257 double* op = op0;
258 int l = l0 * UpFactor;
259 l0 = 0;
260
261 while( l > 0 )
262 {
263 const int Offs = InputLen - InDataLeft;
264
265 if( l < InDataLeft )
266 {
267 InDataLeft -= l;
268
269 if( UpShift >= 0 )
270 {
271 memcpy( &CurInput[ Offs >> UpShift ], ip,
272 (size_t) ( l >> UpShift ) * sizeof( CurInput[ 0 ]));
273 }
274 else
275 {
276 copyUpsample( ip, &CurInput[ Offs ], l );
277 }
278
279 copyToOutput( Offs - OutOffset, op, l, l0 );
280 break;
281 }
282
283 const int b = InDataLeft;
284 l -= b;
285 InDataLeft = InputLen;
286 int ilu;
287
288 if( UpShift >= 0 )
289 {
290 const int bu = b >> UpShift;
291 memcpy( &CurInput[ Offs >> UpShift ], ip,
292 (size_t) bu * sizeof( CurInput[ 0 ]));
293
294 ip += bu;
295 ilu = InputLen >> UpShift;
296 }
297 else
298 {
299 copyUpsample( ip, &CurInput[ Offs ], b );
300 ilu = InputLen;
301 }
302
303 const size_t pil = (size_t) PrevInputLen * sizeof( CurInput[ 0 ]);
304 memcpy( &CurInput[ ilu ], PrevInput, pil );
305 memcpy( PrevInput, &CurInput[ ilu - PrevInputLen ], pil );
306
307 (*fftin) -> forward( CurInput );
308
309 if( UpShift > 0 )
310 {
311 #if R8B_FLOATFFT
312 mirrorInputSpectrum( (float*) CurInput );
313 #else // R8B_FLOATFFT
314 mirrorInputSpectrum( CurInput );
315 #endif // R8B_FLOATFFT
316 }
317
318 if( Filter -> isZeroPhase() )
319 {
320 (*fftout) -> multiplyBlocksZP( Filter -> getKernelBlock(),
321 CurInput );
322 }
323 else
324 {
325 (*fftout) -> multiplyBlocks( Filter -> getKernelBlock(),
326 CurInput );
327 }
328
329 if( DownShift > 0 )
330 {
331 const int z = BlockLen2 >> DownShift;
332
333 #if R8B_FLOATFFT
334 float* const kb = (float*) Filter -> getKernelBlock();
335 float* const p = (float*) CurInput;
336 #else // R8B_FLOATFFT
337 const double* const kb = Filter -> getKernelBlock();
338 double* const p = CurInput;
339 #endif // R8B_FLOATFFT
340
341 p[ 1 ] = kb[ z ] * p[ z ] - kb[ z + 1 ] * p[ z + 1 ];
342 }
343
344 (*fftout) -> inverse( CurInput );
345
346 copyToOutput( Offs - OutOffset, op, b, l0 );
347
348 double* const tmp = CurInput;
349 CurInput = CurOutput;
350 CurOutput = tmp;
351 }
352
353 return( l0 );
354 }
355
356private:
357 CDSPFIRFilter* Filter;
362 CDSPRealFFTKeeper* fftout;
365 int UpFactor;
366 int DownFactor;
367 int BlockLen2;
368 int OutOffset;
369 int PrevInputLen;
371 int InputLen;
373 double LatencyFrac;
375 int Latency;
376 int UpShift;
378 int DownShift;
381 int InputDelay;
384 double* PrevInput;
385 double* CurInput;
386 double* CurOutput;
387 int InDataLeft;
389 int LatencyLeft;
390 int UpSkip;
392 int DownSkip;
395 int DownSkipInit;
396 CFixedBuffer< double > WorkBlocks;
399 bool DoConsumeLatency;
402
413
414 void copyUpsample( double*& ip0, double* op, int l0 )
415 {
416 int b = min( UpSkip, l0 );
417
418 if( b != 0 )
419 {
420 UpSkip -= b;
421 l0 -= b;
422
423 *op = 0.0;
424 op++;
425
426 while( --b != 0 )
427 {
428 *op = 0.0;
429 op++;
430 }
431 }
432
433 double* ip = ip0;
434 const int upf = UpFactor;
435 int l = l0 / upf;
436 int lz = l0 - l * upf;
437
438 if( upf == 3 )
439 {
440 while( l != 0 )
441 {
442 op[ 0 ] = *ip;
443 op[ 1 ] = 0.0;
444 op[ 2 ] = 0.0;
445 ip++;
446 op += upf;
447 l--;
448 }
449 }
450 else
451 if( upf == 5 )
452 {
453 while( l != 0 )
454 {
455 op[ 0 ] = *ip;
456 op[ 1 ] = 0.0;
457 op[ 2 ] = 0.0;
458 op[ 3 ] = 0.0;
459 op[ 4 ] = 0.0;
460 ip++;
461 op += upf;
462 l--;
463 }
464 }
465 else
466 {
467 const size_t zc = (size_t) ( upf - 1 ) * sizeof( op[ 0 ]);
468
469 while( l != 0 )
470 {
471 *op = *ip;
472 ip++;
473
474 memset( op + 1, 0, zc );
475 op += upf;
476 l--;
477 }
478 }
479
480 if( lz != 0 )
481 {
482 *op = *ip;
483 ip++;
484 op++;
485
486 UpSkip = upf - lz;
487
488 while( --lz != 0 )
489 {
490 *op = 0.0;
491 op++;
492 }
493 }
494
495 ip0 = ip;
496 }
497
511
512 void copyToOutput( int Offs, double*& op0, int b, int& l0 )
513 {
514 if( Offs < 0 )
515 {
516 if( Offs + b <= 0 )
517 {
518 Offs += BlockLen2;
519 }
520 else
521 {
522 copyToOutput( Offs + BlockLen2, op0, -Offs, l0 );
523 b += Offs;
524 Offs = 0;
525 }
526 }
527
528 if( LatencyLeft != 0 )
529 {
530 if( LatencyLeft >= b )
531 {
532 LatencyLeft -= b;
533 return;
534 }
535
536 Offs += LatencyLeft;
537 b -= LatencyLeft;
538 LatencyLeft = 0;
539 }
540
541 const int df = DownFactor;
542
543 if( DownShift > 0 )
544 {
545 int Skip = Offs & ( df - 1 );
546
547 if( Skip > 0 )
548 {
549 Skip = df - Skip;
550 b -= Skip;
551 Offs += Skip;
552 }
553
554 if( b > 0 )
555 {
556 b = ( b + df - 1 ) >> DownShift;
557 memcpy( op0, &CurOutput[ Offs >> DownShift ],
558 (size_t) b * sizeof( op0[ 0 ]));
559
560 op0 += b;
561 l0 += b;
562 }
563 }
564 else
565 {
566 if( df > 1 )
567 {
568 const double* ip = &CurOutput[ Offs + DownSkip ];
569 int l = ( b + df - 1 - DownSkip ) / df;
570 DownSkip += l * df - b;
571
572 double* op = op0;
573 l0 += l;
574 op0 += l;
575
576 while( l > 0 )
577 {
578 *op = *ip;
579 ip += df;
580 op++;
581 l--;
582 }
583 }
584 else
585 {
586 memcpy( op0, &CurOutput[ Offs ],
587 (size_t) b * sizeof( op0[ 0 ]));
588
589 op0 += b;
590 l0 += b;
591 }
592 }
593 }
594
605
606 template< typename T >
607 void mirrorInputSpectrum( T* const p )
608 {
609 const int bl1 = BlockLen2 >> UpShift;
610 const int bl2 = bl1 + bl1;
611 int i;
612
613 for( i = bl1 + 2; i < bl2; i += 2 )
614 {
615 p[ i ] = p[ bl2 - i ];
616 p[ i + 1 ] = -p[ bl2 - i + 1 ];
617 }
618
619 p[ bl1 ] = p[ 1 ];
620 p[ bl1 + 1 ] = (T) 0;
621 p[ 1 ] = p[ 0 ];
622
623 for( i = 1; i < UpShift; i++ )
624 {
625 const int z = bl1 << i;
626 memcpy( &p[ z ], p, (size_t) z * sizeof( p[ 0 ]));
627 p[ z + 1 ] = (T) 0;
628 }
629 }
630};
631
632} // namespace r8b
633
634#endif // R8B_CDSPBLOCKCONVOLVER_INCLUDED
FIR filter generator and filter cache classes.
The base virtual class for DSP processing algorithms.
#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
T min(const T &v1, const T &v2)
Returns minimum of two values.
Definition r8bbase.h:1079
int getBitOccupancy(const int v)
Calculate the exact number of bits a value needs for representation.
Definition r8bbase.h:766
Single-block overlap-save convolution processing class.
Definition CDSPBlockConvolver.h:40
virtual int getMaxOutLen(const int MaxInLen) const
Returns the maximal length of the output buffer required when processing the MaxInLen number of input...
Definition CDSPBlockConvolver.h:208
virtual double getLatencyFrac() const
Returns fractional latency, in samples, which is present in the output signal.
Definition CDSPBlockConvolver.h:203
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 CDSPBlockConvolver.h:192
virtual int getLatency() const
Return the latency, in samples, which is present in the output signal.
Definition CDSPBlockConvolver.h:198
virtual void clear()
Clears (resets) the state of this object and returns it to the state after construction.
Definition CDSPBlockConvolver.h:215
CDSPBlockConvolver(CDSPFIRFilter &aFilter, const int aUpFactor, const int aDownFactor, const double PrevLatency=0.0, const bool aDoConsumeLatency=true)
Initializes internal variables and constants of this object.
Definition CDSPBlockConvolver.h:62
virtual int process(double *ip, int l0, double *&op0)
Performs DSP processing.
Definition CDSPBlockConvolver.h:252
Calculation and storage class for FIR filters.
Definition CDSPFIRFilter.h:59
A "keeper" class for real-valued FFT transform objects.
Definition CDSPRealFFT.h:519
Pointer-to-object "keeper" class with automatic deletion.
Definition r8bbase.h:405