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#ifndef R8B_CDSPBLOCKCONVOLVER_INCLUDED
16#define R8B_CDSPBLOCKCONVOLVER_INCLUDED
17
18#include "CDSPFIRFilter.h"
19#include "CDSPProcessor.h"
20
21namespace r8b {
22
39{
40public:
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, PrevInputLen * sizeof( PrevInput[ 0 ]));
218
219 if( DoConsumeLatency )
220 {
221 LatencyLeft = Latency;
222 }
223 else
224 {
225 LatencyLeft = 0;
226
227 if( DownShift > 0 )
228 {
229 memset( &CurOutput[ 0 ], 0, ( BlockLen2 >> DownShift ) *
230 sizeof( CurOutput[ 0 ]));
231 }
232 else
233 {
234 memset( &CurOutput[ BlockLen2 - OutOffset ], 0, OutOffset *
235 sizeof( CurOutput[ 0 ]));
236
237 memset( &CurOutput[ 0 ], 0, ( InputLen - OutOffset ) *
238 sizeof( CurOutput[ 0 ]));
239 }
240 }
241
242 memset( CurInput, 0, InputDelay * sizeof( CurInput[ 0 ]));
243
244 InDataLeft = InputLen - InputDelay;
245 UpSkip = 0;
246 DownSkip = DownSkipInit;
247 }
248
249 virtual int process( double* ip, int l0, double*& op0 )
250 {
251 R8BASSERT( l0 >= 0 );
252 R8BASSERT( UpFactor / DownFactor <= 1 || ip != op0 || l0 == 0 );
253
254 double* op = op0;
255 int l = l0 * UpFactor;
256 l0 = 0;
257
258 while( l > 0 )
259 {
260 const int Offs = InputLen - InDataLeft;
261
262 if( l < InDataLeft )
263 {
264 InDataLeft -= l;
265
266 if( UpShift >= 0 )
267 {
268 memcpy( &CurInput[ Offs >> UpShift ], ip,
269 ( l >> UpShift ) * sizeof( CurInput[ 0 ]));
270 }
271 else
272 {
273 copyUpsample( ip, &CurInput[ Offs ], l );
274 }
275
276 copyToOutput( Offs - OutOffset, op, l, l0 );
277 break;
278 }
279
280 const int b = InDataLeft;
281 l -= b;
282 InDataLeft = InputLen;
283 int ilu;
284
285 if( UpShift >= 0 )
286 {
287 const int bu = b >> UpShift;
288 memcpy( &CurInput[ Offs >> UpShift ], ip,
289 bu * sizeof( CurInput[ 0 ]));
290
291 ip += bu;
292 ilu = InputLen >> UpShift;
293 }
294 else
295 {
296 copyUpsample( ip, &CurInput[ Offs ], b );
297 ilu = InputLen;
298 }
299
300 const size_t pil = PrevInputLen * sizeof( CurInput[ 0 ]);
301 memcpy( &CurInput[ ilu ], PrevInput, pil );
302 memcpy( PrevInput, &CurInput[ ilu - PrevInputLen ], pil );
303
304 (*fftin) -> forward( CurInput );
305
306 if( UpShift > 0 )
307 {
308 #if R8B_FLOATFFT
309 mirrorInputSpectrum( (float*) CurInput );
310 #else // R8B_FLOATFFT
311 mirrorInputSpectrum( CurInput );
312 #endif // R8B_FLOATFFT
313 }
314
315 if( Filter -> isZeroPhase() )
316 {
317 (*fftout) -> multiplyBlocksZP( Filter -> getKernelBlock(),
318 CurInput );
319 }
320 else
321 {
322 (*fftout) -> multiplyBlocks( Filter -> getKernelBlock(),
323 CurInput );
324 }
325
326 if( DownShift > 0 )
327 {
328 const int z = BlockLen2 >> DownShift;
329
330 #if R8B_FLOATFFT
331 float* const kb = (float*) Filter -> getKernelBlock();
332 float* const p = (float*) CurInput;
333 #else // R8B_FLOATFFT
334 const double* const kb = Filter -> getKernelBlock();
335 double* const p = CurInput;
336 #endif // R8B_FLOATFFT
337
338 p[ 1 ] = kb[ z ] * p[ z ] - kb[ z + 1 ] * p[ z + 1 ];
339 }
340
341 (*fftout) -> inverse( CurInput );
342
343 copyToOutput( Offs - OutOffset, op, b, l0 );
344
345 double* const tmp = CurInput;
346 CurInput = CurOutput;
347 CurOutput = tmp;
348 }
349
350 return( l0 );
351 }
352
353private:
354 CDSPFIRFilter* Filter;
358 CDSPRealFFTKeeper* fftout;
361 int UpFactor;
362 int DownFactor;
363 int BlockLen2;
364 int OutOffset;
365 int PrevInputLen;
367 int InputLen;
369 double LatencyFrac;
371 int Latency;
372 int UpShift;
374 int DownShift;
377 int InputDelay;
380 double* PrevInput;
381 double* CurInput;
382 double* CurOutput;
383 int InDataLeft;
385 int LatencyLeft;
386 int UpSkip;
388 int DownSkip;
390 int DownSkipInit;
391 CFixedBuffer< double > WorkBlocks;
394 bool DoConsumeLatency;
397
409 void copyUpsample( double*& ip0, double* op, int l0 )
410 {
411 int b = min( UpSkip, l0 );
412
413 if( b != 0 )
414 {
415 UpSkip -= b;
416 l0 -= b;
417
418 *op = 0.0;
419 op++;
420
421 while( --b != 0 )
422 {
423 *op = 0.0;
424 op++;
425 }
426 }
427
428 double* ip = ip0;
429 const int upf = UpFactor;
430 int l = l0 / upf;
431 int lz = l0 - l * upf;
432
433 if( upf == 3 )
434 {
435 while( l != 0 )
436 {
437 op[ 0 ] = *ip;
438 op[ 1 ] = 0.0;
439 op[ 2 ] = 0.0;
440 ip++;
441 op += upf;
442 l--;
443 }
444 }
445 else
446 if( upf == 5 )
447 {
448 while( l != 0 )
449 {
450 op[ 0 ] = *ip;
451 op[ 1 ] = 0.0;
452 op[ 2 ] = 0.0;
453 op[ 3 ] = 0.0;
454 op[ 4 ] = 0.0;
455 ip++;
456 op += upf;
457 l--;
458 }
459 }
460 else
461 {
462 const size_t zc = ( upf - 1 ) * sizeof( op[ 0 ]);
463
464 while( l != 0 )
465 {
466 *op = *ip;
467 ip++;
468
469 memset( op + 1, 0, zc );
470 op += upf;
471 l--;
472 }
473 }
474
475 if( lz != 0 )
476 {
477 *op = *ip;
478 ip++;
479 op++;
480
481 UpSkip = upf - lz;
482
483 while( --lz != 0 )
484 {
485 *op = 0.0;
486 op++;
487 }
488 }
489
490 ip0 = ip;
491 }
492
505 void copyToOutput( int Offs, double*& op0, int b, int& l0 )
506 {
507 if( Offs < 0 )
508 {
509 if( Offs + b <= 0 )
510 {
511 Offs += BlockLen2;
512 }
513 else
514 {
515 copyToOutput( Offs + BlockLen2, op0, -Offs, l0 );
516 b += Offs;
517 Offs = 0;
518 }
519 }
520
521 if( LatencyLeft != 0 )
522 {
523 if( LatencyLeft >= b )
524 {
525 LatencyLeft -= b;
526 return;
527 }
528
529 Offs += LatencyLeft;
530 b -= LatencyLeft;
531 LatencyLeft = 0;
532 }
533
534 const int df = DownFactor;
535
536 if( DownShift > 0 )
537 {
538 int Skip = Offs & ( df - 1 );
539
540 if( Skip > 0 )
541 {
542 Skip = df - Skip;
543 b -= Skip;
544 Offs += Skip;
545 }
546
547 if( b > 0 )
548 {
549 b = ( b + df - 1 ) >> DownShift;
550 memcpy( op0, &CurOutput[ Offs >> DownShift ],
551 b * sizeof( op0[ 0 ]));
552
553 op0 += b;
554 l0 += b;
555 }
556 }
557 else
558 {
559 if( df > 1 )
560 {
561 const double* ip = &CurOutput[ Offs + DownSkip ];
562 int l = ( b + df - 1 - DownSkip ) / df;
563 DownSkip += l * df - b;
564
565 double* op = op0;
566 l0 += l;
567 op0 += l;
568
569 while( l > 0 )
570 {
571 *op = *ip;
572 ip += df;
573 op++;
574 l--;
575 }
576 }
577 else
578 {
579 memcpy( op0, &CurOutput[ Offs ], b * sizeof( op0[ 0 ]));
580 op0 += b;
581 l0 += b;
582 }
583 }
584 }
585
595 template< typename T >
596 void mirrorInputSpectrum( T* const p )
597 {
598 const int bl1 = BlockLen2 >> UpShift;
599 const int bl2 = bl1 + bl1;
600 int i;
601
602 for( i = bl1 + 2; i < bl2; i += 2 )
603 {
604 p[ i ] = p[ bl2 - i ];
605 p[ i + 1 ] = -p[ bl2 - i + 1 ];
606 }
607
608 p[ bl1 ] = p[ 1 ];
609 p[ bl1 + 1 ] = (T) 0;
610 p[ 1 ] = p[ 0 ];
611
612 for( i = 1; i < UpShift; i++ )
613 {
614 const int z = bl1 << i;
615 memcpy( &p[ z ], p, z * sizeof( p[ 0 ]));
616 p[ z + 1 ] = (T) 0;
617 }
618 }
619};
620
621} // namespace r8b
622
623#endif // R8B_CDSPBLOCKCONVOLVER_INCLUDED
FIR filter generator and filter cache classes.
The base virtual class for DSP processing algorithms.
#define R8BASSERT(e)
Definition: r8bconf.h:27
#define R8BCONSOLE(...)
Definition: r8bconf.h:40
The "r8brain-free-src" library namespace.
Definition: CDSPBlockConvolver.h:21
T min(const T &v1, const T &v2)
Definition: r8bbase.h:1063
int getBitOccupancy(const int v)
Definition: r8bbase.h:766
Single-block overlap-save convolution processing class.
Definition: CDSPBlockConvolver.h:39
virtual int getMaxOutLen(const int MaxInLen) const
Definition: CDSPBlockConvolver.h:208
virtual double getLatencyFrac() const
Definition: CDSPBlockConvolver.h:203
virtual int getInLenBeforeOutPos(const int ReqOutPos) const
Definition: CDSPBlockConvolver.h:192
virtual int getLatency() const
Definition: CDSPBlockConvolver.h:198
virtual void clear()
Definition: CDSPBlockConvolver.h:215
CDSPBlockConvolver(CDSPFIRFilter &aFilter, const int aUpFactor, const int aDownFactor, const double PrevLatency=0.0, const bool aDoConsumeLatency=true)
Definition: CDSPBlockConvolver.h:62
virtual int process(double *ip, int l0, double *&op0)
Definition: CDSPBlockConvolver.h:249
Calculation and storage class for FIR filters.
Definition: CDSPFIRFilter.h:58
The base virtual class for DSP processing algorithms.
Definition: CDSPProcessor.h:32
A "keeper" class for real-valued FFT transform objects.
Definition: CDSPRealFFT.h:547
void alloc(const int Capacity)
Definition: r8bbase.h:343
Pointer-to-object "keeper" class with automatic deletion.
Definition: r8bbase.h:428