SpectMorph
smpandaresampler.hh
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2 #ifndef __PANDA_RESAMPLER_HH__
3 #define __PANDA_RESAMPLER_HH__
4 
5 #include <vector>
6 #include <memory>
7 #include <cstdio>
8 #include <cstdlib>
9 
10 /* ------------------------ configuration ---------------------------- */
11 
12 // uncomment this to use header only mode
13 // #define PANDA_RESAMPLER_HEADER_ONLY
14 
15 /* ------------------------------------------------------------------- */
16 
17 namespace PandaResampler {
18 
19 typedef unsigned int uint;
20 
21 static inline bool
22 check (bool value, const char *file, int line, const char *func, const char *what)
23 {
24  if (!value)
25  fprintf (stderr, "%s:%d:%s: PANDA_RESAMPLER_CHECK FAILED: %s\n", file, line, func, what);
26  return value;
27 }
28 
29 #define PANDA_RESAMPLER_CHECK(expr) (PandaResampler::check (expr, __FILE__, __LINE__, __func__, #expr))
30 
31 template<class T>
32 class AlignedArray {
33  unsigned char *unaligned_mem;
34  T *data;
35  size_t n_elements;
36 
37  void
38  allocate_aligned_data()
39  {
40  /* for SSE we need 16-byte alignment, but we also ensure that no false
41  * sharing will occur (at begin and end of data)
42  */
43  const size_t cache_line_size = 64;
44 
45  unaligned_mem = (unsigned char *) malloc (n_elements * sizeof (T) + 2 * (cache_line_size - 1));
46  unsigned char *aligned_mem = unaligned_mem;
47  if ((ptrdiff_t) aligned_mem % cache_line_size)
48  aligned_mem += cache_line_size - (ptrdiff_t) aligned_mem % cache_line_size;
49 
50  data = reinterpret_cast<T *> (aligned_mem);
51  }
52 public:
53  AlignedArray (size_t n_elements) :
54  n_elements (n_elements)
55  {
56  allocate_aligned_data();
57  for (size_t i = 0; i < n_elements; i++)
58  new (data + i) T();
59  }
60  AlignedArray (const std::vector<T>& elements) :
61  AlignedArray (elements.size())
62  {
63  std::copy (elements.begin(), elements.end(), data);
64  }
65  ~AlignedArray()
66  {
67  /* C++ destruction order: last allocated element is deleted first */
68  while (n_elements)
69  data[--n_elements].~T();
70  free (unaligned_mem);
71  }
72  T&
73  operator[] (size_t pos)
74  {
75  return data[pos];
76  }
77  const T&
78  operator[] (size_t pos) const
79  {
80  return data[pos];
81  }
82  size_t size () const
83  {
84  return n_elements;
85  }
86  T* begin()
87  {
88  return &data[0];
89  }
90  T* end()
91  {
92  return &data[n_elements];
93  }
94 };
95 
99 class Resampler2 {
100  class Impl
101  {
102  public:
103  virtual void process_block (const float *input, uint n_input_samples, float *output) = 0;
104  virtual uint order() const = 0;
105  virtual double delay() const = 0;
106  virtual void reset() = 0;
107  virtual bool sse_enabled() const = 0;
108  virtual
109  ~Impl()
110  {
111  }
112  };
113  std::unique_ptr<Impl> impl_x2;
114  std::unique_ptr<Impl> impl_x4;
115  std::unique_ptr<Impl> impl_x8;
116  uint ratio_;
117 
118  template<uint ORDER, bool USE_SSE>
119  class Upsampler2;
120  template<uint ORDER, bool USE_SSE>
121  class Downsampler2;
122  template<uint ORDER>
123  class IIRUpsampler2;
124  template<uint ORDER>
125  class IIRDownsampler2;
126  template<uint ORDER>
127  class IIRUpsampler2SSE;
128  template<uint ORDER>
129  class IIRDownsampler2SSE;
130 public:
131  enum Mode {
132  UP,
133  DOWN
134  };
135  enum Precision {
136  PREC_LINEAR = 1, /* linear interpolation */
137  PREC_48DB = 8,
138  PREC_72DB = 12,
139  PREC_96DB = 16,
140  PREC_120DB = 20,
141  PREC_144DB = 24
142  };
143  enum Filter {
144  FILTER_IIR,
145  FILTER_FIR,
146  };
147 protected:
148  Mode mode_;
149  Precision precision_;
150  bool use_sse_if_available_;
151  Filter filter_;
152 public:
156  Resampler2 (Mode mode,
157  uint ratio,
158  Precision precision,
159  bool use_sse_if_available = true,
160  Filter filter = FILTER_FIR);
164  static bool sse_available();
168  static bool test_filter_impl (bool verbose);
172  static Precision find_precision_for_bits (uint bits);
176  static const char *precision_name (Precision precision);
180  void
181  process_block (const float *input, uint n_input_samples, float *output)
182  {
183  if (ratio_ == 2)
184  {
185  impl_x2->process_block (input, n_input_samples, output);
186  }
187  else if (ratio_ == 1)
188  {
189  std::copy (input, input + n_input_samples, output);
190  }
191  else
192  {
193  while (n_input_samples)
194  {
195  const uint block_size = 1024;
196  const uint n_todo_samples = std::min (block_size, n_input_samples);
197 
198  float tmp[block_size * 4];
199  float tmp2[block_size * 4];
200 
201  if (mode_ == UP)
202  {
203  if (ratio_ == 4)
204  {
205  impl_x2->process_block (input, n_todo_samples, tmp);
206  impl_x4->process_block (tmp, n_todo_samples * 2, output);
207  }
208  else /* ratio_ == 8 */
209  {
210  impl_x2->process_block (input, n_todo_samples, tmp);
211  impl_x4->process_block (tmp, n_todo_samples * 2, tmp2);
212  impl_x8->process_block (tmp2, n_todo_samples * 4, output);
213  }
214  output += n_todo_samples * ratio_;
215  }
216  else /* (mode_ == DOWN) */
217  {
218  if (ratio_ == 4)
219  {
220  impl_x4->process_block (input, n_todo_samples, tmp);
221  impl_x2->process_block (tmp, n_todo_samples / 2, output);
222  }
223  else /* ratio_ == 8 */
224  {
225  impl_x8->process_block (input, n_todo_samples, tmp);
226  impl_x4->process_block (tmp, n_todo_samples / 2, tmp2);
227  impl_x2->process_block (tmp2, n_todo_samples / 4, output);
228  }
229  output += n_todo_samples / ratio_;
230  }
231  input += n_todo_samples;
232  n_input_samples -= n_todo_samples;
233  }
234  }
235  }
239  uint
240  order() const
241  {
242  return impl_x2->order(); // FIXME
243  }
256  double
257  delay() const
258  {
259  double d = 0;
260  if (mode_ == UP)
261  {
262  if (ratio_ >= 2)
263  d += impl_x2->delay();
264  if (ratio_ >= 4)
265  d += d + impl_x4->delay();
266  if (ratio_ >= 8)
267  d += d + impl_x8->delay();
268  }
269  else /* mode_ == DOWN */
270  {
271  if (ratio_ >= 2)
272  d += impl_x2->delay();
273  if (ratio_ >= 4)
274  d += impl_x4->delay() / 2;
275  if (ratio_ >= 8)
276  d += impl_x8->delay() / 4;
277  }
278  return d;
279  }
283  void
285  {
286  if (ratio_ >= 2)
287  impl_x2->reset();
288  if (ratio_ >= 4)
289  impl_x4->reset();
290  if (ratio_ >= 8)
291  impl_x8->reset();
292  }
296  bool
297  sse_enabled() const
298  {
299  return impl_x2->sse_enabled();
300  }
301 protected:
302  /* Creates implementation from filter coefficients and Filter implementation class
303  *
304  * Since up- and downsamplers use different (scaled) coefficients, its possible
305  * to specify a scaling factor. Usually 2 for upsampling and 1 for downsampling.
306  */
307  template<class Filter> static inline Impl*
308  create_impl_with_coeffs (const double *d,
309  uint order,
310  double scaling)
311  {
312  float taps[order];
313  for (uint i = 0; i < order; i++)
314  taps[i] = d[i] * scaling;
315 
316  Resampler2::Impl *filter = new Filter (taps);
317  if (!PANDA_RESAMPLER_CHECK (order == filter->order()))
318  return nullptr;
319 
320  return filter;
321  }
322  /* creates the actual implementation; specifying USE_SSE=true will use
323  * SSE instructions, USE_SSE=false will use FPU instructions
324  *
325  * Don't use this directly - it's only to be used by
326  * bseblockutils.cc's anonymous Impl classes.
327  */
328  template<bool USE_SSE> inline Impl*
329  create_impl (uint stage_ratio);
330 
331  template<bool USE_SSE> inline Impl*
332  create_impl_iir (uint stage_ratio);
333 
334  template<class CArray>
335  inline Impl*
336  create_impl_iir_with_coeffs (const CArray& carray, double group_delay);
337 
338  void
339  init_stage (std::unique_ptr<Impl>& impl,
340  uint stage_ratio);
341 };
342 
343 } /* namespace PandaResampler */
344 
345 // Make sure implementation is included in header-only mode
346 #if defined(PANDA_RESAMPLER_HEADER_ONLY) && !defined(PANDA_RESAMPLER_SOURCE)
347 # define PANDA_RESAMPLER_SOURCE "pandaresampler.cc"
348 # include PANDA_RESAMPLER_SOURCE
349 #endif
350 
351 #endif /* __PANDA_RESAMPLER_HH__ */
Definition: smpandaresampler.hh:32
Definition: smpandaresampler.cc:494
Definition: smpandaresampler.cc:1067
Definition: smpandaresampler.cc:1106
Definition: smpandaresampler.cc:353
Definition: smpandaresampler.hh:99
static const char * precision_name(Precision precision)
Definition: smpandaresampler.cc:148
Resampler2(Mode mode, uint ratio, Precision precision, bool use_sse_if_available=true, Filter filter=FILTER_FIR)
Definition: smpandaresampler.cc:64
void process_block(const float *input, uint n_input_samples, float *output)
Definition: smpandaresampler.hh:181
void reset()
Definition: smpandaresampler.hh:284
static bool test_filter_impl(bool verbose)
Definition: smpandaresampler.cc:1387
static bool sse_available()
Definition: smpandaresampler.cc:116
bool sse_enabled() const
Definition: smpandaresampler.hh:297
double delay() const
Definition: smpandaresampler.hh:257
static Precision find_precision_for_bits(uint bits)
Definition: smpandaresampler.cc:127
uint order() const
Definition: smpandaresampler.hh:240