Flan
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 
21 namespace r8b {
22 
39 {
40 public:
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  , DoConsumeLatency( aDoConsumeLatency )
69  , BlockLen2( 2 << Filter -> getBlockLenBits() )
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  UpSkipInit = 0;
106  DownSkipInit = 0;
107  DownShift = getBitOccupancy( DownFactor ) - 1;
108 
109  if(( 1 << DownShift ) == DownFactor )
110  {
111  fftoutBits = Filter -> getBlockLenBits() + 1 - DownShift;
112 
113  if( DownFactor > 1 )
114  {
115  if( UpShift > 0 )
116  {
117  // This case never happens in practice due to mutual
118  // exclusion of "power of 2" DownFactor and UpFactor
119  // values.
120 
121  R8BASSERT( UpShift == 0 );
122  }
123  else
124  {
125  // Make sure InputLen is divisible by DownFactor.
126 
127  const int ilc = InputLen & ( DownFactor - 1 );
128  PrevInputLen += ilc;
129  InputLen -= ilc;
130  Latency -= ilc;
131 
132  // Correct InputDelay for input and filter's latency.
133 
134  const int lc = InLatency & ( DownFactor - 1 );
135 
136  if( lc > 0 )
137  {
138  InputDelay = DownFactor - lc;
139  }
140 
141  if( !DoConsumeLatency )
142  {
143  Latency /= DownFactor;
144  }
145  }
146  }
147  }
148  else
149  {
150  fftoutBits = Filter -> getBlockLenBits() + 1;
151  DownShift = -1;
152 
153  if( !DoConsumeLatency && DownFactor > 1 )
154  {
155  DownSkipInit = Latency % DownFactor;
156  Latency /= DownFactor;
157  }
158  }
159 
160  fftin = new CDSPRealFFTKeeper( fftinBits );
161 
162  if( fftoutBits == fftinBits )
163  {
164  fftout = fftin;
165  }
166  else
167  {
168  ffto2 = new CDSPRealFFTKeeper( fftoutBits );
169  fftout = ffto2;
170  }
171 
172  WorkBlocks.alloc( BlockLen2 * 2 + PrevInputLen );
173  CurInput = &WorkBlocks[ 0 ];
174  CurOutput = &WorkBlocks[ BlockLen2 ]; // CurInput and
175  // CurOutput are memory-aligned.
176  PrevInput = &WorkBlocks[ BlockLen2 * 2 ];
177 
178  clear();
179 
180  R8BCONSOLE( "CDSPBlockConvolver: flt_len=%i in_len=%i io=%i/%i "
181  "fft=%i/%i latency=%i\n", Filter -> getKernelLen(), InputLen,
182  UpFactor, DownFactor, (*fftin) -> getLen(), (*fftout) -> getLen(),
183  getLatency() );
184  }
185 
186  virtual ~CDSPBlockConvolver()
187  {
188  Filter -> unref();
189  }
190 
191  virtual int getLatency() const
192  {
193  return( DoConsumeLatency ? 0 : Latency );
194  }
195 
196  virtual double getLatencyFrac() const
197  {
198  return( LatencyFrac );
199  }
200 
201  virtual int getMaxOutLen( const int MaxInLen ) const
202  {
203  R8BASSERT( MaxInLen >= 0 );
204 
205  return(( MaxInLen * UpFactor + DownFactor - 1 ) / DownFactor );
206  }
207 
208  virtual void clear()
209  {
210  memset( &PrevInput[ 0 ], 0, PrevInputLen * sizeof( double ));
211 
212  if( DoConsumeLatency )
213  {
214  LatencyLeft = Latency;
215  }
216  else
217  {
218  LatencyLeft = 0;
219 
220  if( DownShift > 0 )
221  {
222  memset( &CurOutput[ 0 ], 0, ( BlockLen2 >> DownShift ) *
223  sizeof( double ));
224  }
225  else
226  {
227  memset( &CurOutput[ BlockLen2 - OutOffset ], 0, OutOffset *
228  sizeof( double ));
229 
230  memset( &CurOutput[ 0 ], 0, ( InputLen - OutOffset ) *
231  sizeof( double ));
232  }
233  }
234 
235  memset( CurInput, 0, InputDelay * sizeof( double ));
236 
237  InDataLeft = InputLen - InputDelay;
238  UpSkip = UpSkipInit;
239  DownSkip = DownSkipInit;
240  }
241 
242  virtual int process( double* ip, int l0, double*& op0 )
243  {
244  R8BASSERT( l0 >= 0 );
245  R8BASSERT( UpFactor / DownFactor <= 1 || ip != op0 || l0 == 0 );
246 
247  double* op = op0;
248  int l = l0 * UpFactor;
249  l0 = 0;
250 
251  while( l > 0 )
252  {
253  const int Offs = InputLen - InDataLeft;
254 
255  if( l < InDataLeft )
256  {
257  InDataLeft -= l;
258 
259  if( UpShift >= 0 )
260  {
261  memcpy( &CurInput[ Offs >> UpShift ], ip,
262  ( l >> UpShift ) * sizeof( double ));
263  }
264  else
265  {
266  copyUpsample( ip, &CurInput[ Offs ], l );
267  }
268 
269  copyToOutput( Offs - OutOffset, op, l, l0 );
270  break;
271  }
272 
273  const int b = InDataLeft;
274  l -= b;
275  InDataLeft = InputLen;
276  int ilu;
277 
278  if( UpShift >= 0 )
279  {
280  const int bu = b >> UpShift;
281  memcpy( &CurInput[ Offs >> UpShift ], ip,
282  bu * sizeof( double ));
283 
284  ip += bu;
285  ilu = InputLen >> UpShift;
286  }
287  else
288  {
289  copyUpsample( ip, &CurInput[ Offs ], b );
290  ilu = InputLen;
291  }
292 
293  const int pil = (int) ( PrevInputLen * sizeof( double ));
294  memcpy( &CurInput[ ilu ], PrevInput, pil );
295  memcpy( PrevInput, &CurInput[ ilu - PrevInputLen ], pil );
296 
297  (*fftin) -> forward( CurInput );
298 
299  if( UpShift > 0 )
300  {
301  #if R8B_FLOATFFT
302  mirrorInputSpectrum( (float*) CurInput );
303  #else // R8B_FLOATFFT
304  mirrorInputSpectrum( CurInput );
305  #endif // R8B_FLOATFFT
306  }
307 
308  if( Filter -> isZeroPhase() )
309  {
310  (*fftout) -> multiplyBlocksZ( Filter -> getKernelBlock(),
311  CurInput );
312  }
313  else
314  {
315  (*fftout) -> multiplyBlocks( Filter -> getKernelBlock(),
316  CurInput );
317  }
318 
319  if( DownShift > 0 )
320  {
321  const int z = BlockLen2 >> DownShift;
322 
323  #if R8B_FLOATFFT
324  float* const kb = (float*) Filter -> getKernelBlock();
325  float* const p = (float*) CurInput;
326  #else // R8B_FLOATFFT
327  const double* const kb = Filter -> getKernelBlock();
328  double* const p = CurInput;
329  #endif // R8B_FLOATFFT
330 
331  p[ 1 ] = kb[ z ] * p[ z ] - kb[ z + 1 ] * p[ z + 1 ];
332  }
333 
334  (*fftout) -> inverse( CurInput );
335 
336  copyToOutput( Offs - OutOffset, op, b, l0 );
337 
338  double* const tmp = CurInput;
339  CurInput = CurOutput;
340  CurOutput = tmp;
341  }
342 
343  return( l0 );
344  }
345 
346 private:
347  CDSPFIRFilter* Filter;
353  CDSPRealFFTKeeper* fftout;
355  int UpFactor;
359  int DownFactor;
361  bool DoConsumeLatency;
363  int BlockLen2;
367  int OutOffset;
369  int PrevInputLen;
371  int InputLen;
374  int Latency;
377  double LatencyFrac;
379  int UpShift;
382  int DownShift;
385  int InputDelay;
389  CFixedBuffer< double > WorkBlocks;
393  double* PrevInput;
397  double* CurInput;
399  double* CurOutput;
401  int InDataLeft;
403  int LatencyLeft;
406  int UpSkip;
408  int UpSkipInit;
411  int DownSkip;
413  int DownSkipInit;
416 
429  void copyUpsample( double*& ip0, double* op, int l0 )
430  {
431  int b = min( UpSkip, l0 );
432 
433  if( b > 0 )
434  {
435  l0 -= b;
436  UpSkip -= b;
437  *op = 0.0;
438  op++;
439  b--;
440 
441  while( b > 0 )
442  {
443  *op = 0.0;
444  op++;
445  b--;
446  }
447  }
448 
449  double* ip = ip0;
450  int l = l0 / UpFactor;
451  int lz = l0 - l * UpFactor;
452  const int upf = UpFactor;
453 
454  if( upf == 3 )
455  {
456  while( l > 0 )
457  {
458  op[ 0 ] = *ip;
459  op[ 1 ] = 0.0;
460  op[ 2 ] = 0.0;
461  ip++;
462  op += upf;
463  l--;
464  }
465  }
466  else
467  if( upf == 5 )
468  {
469  while( l > 0 )
470  {
471  op[ 0 ] = *ip;
472  op[ 1 ] = 0.0;
473  op[ 2 ] = 0.0;
474  op[ 3 ] = 0.0;
475  op[ 4 ] = 0.0;
476  ip++;
477  op += upf;
478  l--;
479  }
480  }
481  else
482  {
483  while( l > 0 )
484  {
485  op[ 0 ] = *ip;
486  int j;
487 
488  for( j = 1; j < upf; j++ )
489  {
490  op[ j ] = 0.0;
491  }
492 
493  ip++;
494  op += upf;
495  l--;
496  }
497  }
498 
499  if( lz > 0 )
500  {
501  *op = *ip;
502  op++;
503  ip++;
504  UpSkip = UpFactor - lz;
505 
506  while( lz > 1 )
507  {
508  *op = 0.0;
509  op++;
510  lz--;
511  }
512  }
513 
514  ip0 = ip;
515  }
516 
529  void copyToOutput( int Offs, double*& op0, int b, int& l0 )
530  {
531  if( Offs < 0 )
532  {
533  if( Offs + b <= 0 )
534  {
535  Offs += BlockLen2;
536  }
537  else
538  {
539  copyToOutput( Offs + BlockLen2, op0, -Offs, l0 );
540  b += Offs;
541  Offs = 0;
542  }
543  }
544 
545  if( LatencyLeft > 0 )
546  {
547  if( LatencyLeft >= b )
548  {
549  LatencyLeft -= b;
550  return;
551  }
552 
553  Offs += LatencyLeft;
554  b -= LatencyLeft;
555  LatencyLeft = 0;
556  }
557 
558  const int df = DownFactor;
559 
560  if( DownShift > 0 )
561  {
562  int Skip = Offs & ( df - 1 );
563 
564  if( Skip > 0 )
565  {
566  Skip = df - Skip;
567  b -= Skip;
568  Offs += Skip;
569  }
570 
571  if( b > 0 )
572  {
573  b = ( b + df - 1 ) >> DownShift;
574  memcpy( op0, &CurOutput[ Offs >> DownShift ],
575  b * sizeof( double ));
576 
577  op0 += b;
578  l0 += b;
579  }
580  }
581  else
582  {
583  if( df > 1 )
584  {
585  const double* ip = &CurOutput[ Offs + DownSkip ];
586  int l = ( b + df - 1 - DownSkip ) / df;
587  DownSkip += l * df - b;
588 
589  double* op = op0;
590  l0 += l;
591  op0 += l;
592 
593  while( l > 0 )
594  {
595  *op = *ip;
596  op++;
597  ip += df;
598  l--;
599  }
600  }
601  else
602  {
603  memcpy( op0, &CurOutput[ Offs ], b * sizeof( double ));
604  op0 += b;
605  l0 += b;
606  }
607  }
608  }
609 
618  template< class T >
619  void mirrorInputSpectrum( T* const p )
620  {
621  const int bl1 = BlockLen2 >> UpShift;
622  const int bl2 = bl1 + bl1;
623  int i;
624 
625  for( i = bl1 + 2; i < bl2; i += 2 )
626  {
627  p[ i ] = p[ bl2 - i ];
628  p[ i + 1 ] = -p[ bl2 - i + 1 ];
629  }
630 
631  p[ bl1 ] = p[ 1 ];
632  p[ bl1 + 1 ] = 0.0;
633  p[ 1 ] = p[ 0 ];
634 
635  for( i = 1; i < UpShift; i++ )
636  {
637  const int z = bl1 << i;
638  memcpy( &p[ z ], p, z * sizeof( T ));
639  p[ z + 1 ] = 0.0;
640  }
641  }
642 };
643 
644 } // namespace r8b
645 
646 #endif // R8B_CDSPBLOCKCONVOLVER_INCLUDED
int getBitOccupancy(const int v)
Definition: r8bbase.h:775
#define R8BCONSOLE(...)
Console output macro, used to output various resampler status strings, including filter design parame...
Definition: r8bconf.h:85
virtual int getLatency() const
Definition: CDSPBlockConvolver.h:191
#define R8BASSERT(e)
Assertion macro used to check for certain run-time conditions.
Definition: r8bconf.h:72
Pointer-to-object "keeper" class with automatic deletion.
Definition: r8bbase.h:429
Calculation and storage class for FIR filters.
Definition: CDSPFIRFilter.h:57
The base virtual class for DSP processing algorithms.
virtual void clear()
Function clears (resets) the state of *this object and returns it to the state after construction...
Definition: CDSPBlockConvolver.h:208
CDSPBlockConvolver(CDSPFIRFilter &aFilter, const int aUpFactor, const int aDownFactor, const double PrevLatency=0.0, const bool aDoConsumeLatency=true)
Constructor initializes internal variables and constants of *this object.
Definition: CDSPBlockConvolver.h:62
T min(const T &v1, const T &v2)
Definition: r8bbase.h:1118
Single-block overlap-save convolution processing class.
Definition: CDSPBlockConvolver.h:38
virtual double getLatencyFrac() const
Definition: CDSPBlockConvolver.h:196
A "keeper" class for real-valued FFT transform objects.
Definition: CDSPRealFFT.h:461
virtual int process(double *ip, int l0, double *&op0)
Function performs DSP processing.
Definition: CDSPBlockConvolver.h:242
FIR filter generator and filter cache classes.
virtual int getMaxOutLen(const int MaxInLen) const
Definition: CDSPBlockConvolver.h:201
The base virtual class for DSP processing algorithms.
Definition: CDSPProcessor.h:31
The "r8brain-free-src" library namespace.
Definition: CDSPBlockConvolver.h:21
void alloc(const int Capacity)
Function allocates memory so that the specified number of elements of type T can be stored in *this b...
Definition: r8bbase.h:318