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 int getInLenBeforeOutPos( int ReqOutPos ) const
188 {
189 if( Next != R8B_NULL )
190 {
191 ReqOutPos = Next -> getInLenBeforeOutPos( ReqOutPos );
192 }
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 realfft_t* CurInputFFT = (*fftin) -> forward( CurInput );
308 realfft_t dsn = 0;
309
310 if( UpShift > 0 || DownShift > 0 )
311 {
312 CurInputFFT = (*fftin) -> reorderForward( CurInputFFT,
313 ( (*fftin) -> getLen() > (*fftout) -> getLen() ?
314 (*fftin) -> getWorkBuf() : (*fftout) -> getWorkBuf() ));
315
316 if( UpShift > 0 )
317 {
318 mirrorInputSpectrum( CurInputFFT );
319 }
320
321 if( DownShift > 0 )
322 {
323 const int z = BlockLen2 >> DownShift;
324 dsn = Filter -> calcDownShiftNyquist( CurInputFFT[ z ],
325 CurInputFFT[ z + 1 ]);
326 }
327
328 CurInputFFT = (*fftout) -> reorderInverse( CurInputFFT,
329 CurInput );
330 }
331
332 if( Filter -> isZeroPhase() )
333 {
334 (*fftout) -> multiplyBlocksZP( Filter -> getKernelBlock(),
335 CurInputFFT );
336 }
337 else
338 {
339 (*fftout) -> multiplyBlocks( Filter -> getKernelBlock(),
340 CurInputFFT );
341 }
342
343 if( DownShift > 0 )
344 {
345 (*fftout) -> setBinNyquist( CurInputFFT, dsn );
346 }
347
348 (*fftout) -> inverse( CurInputFFT );
349
350 copyToOutput( Offs - OutOffset, op, b, l0 );
351
352 double* const tmp = CurInput;
353 CurInput = CurOutput;
354 CurOutput = tmp;
355 }
356
357 return( l0 );
358 }
359
360private:
366 CDSPRealFFTKeeper* fftout;
369 int UpFactor;
370 int DownFactor;
371 int BlockLen2;
372 int OutOffset;
373 int PrevInputLen;
375 int InputLen;
377 double LatencyFrac;
379 int Latency;
380 int UpShift;
382 int DownShift;
385 int InputDelay;
388 double* PrevInput;
389 double* CurInput;
390 double* CurOutput;
391 int InDataLeft;
393 int LatencyLeft;
394 int UpSkip;
396 int DownSkip;
399 int DownSkipInit;
400 CFixedBuffer< double > WorkBlocks;
403 bool DoConsumeLatency;
406
417
418 void copyUpsample( double*& ip0, double* op, int l0 )
419 {
420 int b = min( UpSkip, l0 );
421
422 if( b != 0 )
423 {
424 UpSkip -= b;
425 l0 -= b;
426
427 *op = 0.0;
428 op++;
429
430 while( --b != 0 )
431 {
432 *op = 0.0;
433 op++;
434 }
435 }
436
437 double* ip = ip0;
438 const int upf = UpFactor;
439 int l = l0 / upf;
440 int lz = l0 - l * upf;
441
442 if( upf == 3 )
443 {
444 while( l != 0 )
445 {
446 op[ 0 ] = *ip;
447 op[ 1 ] = 0.0;
448 op[ 2 ] = 0.0;
449 ip++;
450 op += upf;
451 l--;
452 }
453 }
454 else
455 if( upf == 5 )
456 {
457 while( l != 0 )
458 {
459 op[ 0 ] = *ip;
460 op[ 1 ] = 0.0;
461 op[ 2 ] = 0.0;
462 op[ 3 ] = 0.0;
463 op[ 4 ] = 0.0;
464 ip++;
465 op += upf;
466 l--;
467 }
468 }
469 else
470 {
471 const size_t zc = (size_t) ( upf - 1 ) * sizeof( op[ 0 ]);
472
473 while( l != 0 )
474 {
475 *op = *ip;
476 ip++;
477
478 memset( op + 1, 0, zc );
479 op += upf;
480 l--;
481 }
482 }
483
484 if( lz != 0 )
485 {
486 *op = *ip;
487 ip++;
488 op++;
489
490 UpSkip = upf - lz;
491
492 while( --lz != 0 )
493 {
494 *op = 0.0;
495 op++;
496 }
497 }
498
499 ip0 = ip;
500 }
501
515
516 void copyToOutput( int Offs, double*& op0, int b, int& l0 )
517 {
518 if( Offs < 0 )
519 {
520 if( Offs + b <= 0 )
521 {
522 Offs += BlockLen2;
523 }
524 else
525 {
526 copyToOutput( Offs + BlockLen2, op0, -Offs, l0 );
527 b += Offs;
528 Offs = 0;
529 }
530 }
531
532 if( LatencyLeft != 0 )
533 {
534 if( LatencyLeft >= b )
535 {
536 LatencyLeft -= b;
537 return;
538 }
539
540 Offs += LatencyLeft;
541 b -= LatencyLeft;
542 LatencyLeft = 0;
543 }
544
545 const int df = DownFactor;
546
547 if( DownShift > 0 )
548 {
549 int Skip = Offs & ( df - 1 );
550
551 if( Skip > 0 )
552 {
553 Skip = df - Skip;
554 b -= Skip;
555 Offs += Skip;
556 }
557
558 if( b > 0 )
559 {
560 b = ( b + df - 1 ) >> DownShift;
561 memcpy( op0, &CurOutput[ Offs >> DownShift ],
562 (size_t) b * sizeof( op0[ 0 ]));
563
564 op0 += b;
565 l0 += b;
566 }
567 }
568 else
569 {
570 if( df > 1 )
571 {
572 const double* ip = &CurOutput[ Offs + DownSkip ];
573 int l = ( b + df - 1 - DownSkip ) / df;
574 DownSkip += l * df - b;
575
576 double* op = op0;
577 l0 += l;
578 op0 += l;
579
580 while( l > 0 )
581 {
582 *op = *ip;
583 ip += df;
584 op++;
585 l--;
586 }
587 }
588 else
589 {
590 memcpy( op0, &CurOutput[ Offs ],
591 (size_t) b * sizeof( op0[ 0 ]));
592
593 op0 += b;
594 l0 += b;
595 }
596 }
597 }
598
609
610 template< typename T >
611 void mirrorInputSpectrum( T* const p )
612 {
613 const int bl1 = BlockLen2 >> UpShift;
614 const int bl2 = bl1 * 2;
615 int i;
616
617 for( i = bl1 + 2; i < bl2; i += 2 )
618 {
619 p[ i ] = p[ bl2 - i ];
620 p[ i + 1 ] = -p[ bl2 - i + 1 ];
621 }
622
623 p[ bl1 ] = p[ 1 ];
624 p[ bl1 + 1 ] = (T) 0;
625 p[ 1 ] = p[ 0 ];
626
627 for( i = 1; i < UpShift; i++ )
628 {
629 const int z = bl1 << i;
630 memcpy( &p[ z ], p, (size_t) z * sizeof( p[ 0 ]));
631 p[ z + 1 ] = (T) 0;
632 }
633 }
634};
635
636} // namespace r8b
637
638#endif // R8B_CDSPBLOCKCONVOLVER_INCLUDED
FIR filter generator and filter cache classes.
The base virtual class for DSP processing algorithms.
#define R8B_NULL
The "null pointer" value, portable between C++11 and earlier C++ versions.
Definition r8bbase.h:101
#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
double realfft_t
Forward transform's native type.
Definition CDSPRealFFT.h:37
T min(const T &v1, const T &v2)
Returns minimum of two values.
Definition r8bbase.h:1257
int getBitOccupancy(const int v)
Calculate the exact number of bits a value needs for representation.
Definition r8bbase.h:944
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(int ReqOutPos) const
Returns the number of input samples required to advance to the specified output sample position (so t...
Definition CDSPBlockConvolver.h:187
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:60
A "keeper" class for real-valued FFT transform objects.
Definition CDSPRealFFT.h:790
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