Resampler.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 /*
3  QM DSP Library
4 
5  Centre for Digital Music, Queen Mary, University of London.
6  This file by Chris Cannam.
7 
8  This program is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License as
10  published by the Free Software Foundation; either version 2 of the
11  License, or (at your option) any later version. See the file
12  COPYING included with this distribution for more information.
13 */
14 
15 #include "Resampler.h"
16 
17 #include "maths/MathUtilities.h"
18 #include "base/KaiserWindow.h"
19 #include "base/SincWindow.h"
20 #include "base/Restrict.h"
21 
22 #include <iostream>
23 #include <vector>
24 #include <map>
25 #include <cassert>
26 #include <algorithm>
27 
28 using std::vector;
29 using std::map;
30 using std::cerr;
31 using std::endl;
32 
33 //#define DEBUG_RESAMPLER 1
34 //#define DEBUG_RESAMPLER_VERBOSE 1
35 
36 Resampler::Resampler(int sourceRate, int targetRate) :
37  m_sourceRate(sourceRate),
38  m_targetRate(targetRate)
39 {
40 #ifdef DEBUG_RESAMPLER
41  cerr << "Resampler::Resampler(" << sourceRate << "," << targetRate << ")" << endl;
42 #endif
43  initialise(100, 0.02);
44 }
45 
46 Resampler::Resampler(int sourceRate, int targetRate,
47  double snr, double bandwidth) :
48  m_sourceRate(sourceRate),
49  m_targetRate(targetRate)
50 {
51  initialise(snr, bandwidth);
52 }
53 
55 {
56  delete[] m_phaseData;
57 }
58 
59 void
60 Resampler::initialise(double snr, double bandwidth)
61 {
62  int higher = std::max(m_sourceRate, m_targetRate);
63  int lower = std::min(m_sourceRate, m_targetRate);
64 
65  m_gcd = MathUtilities::gcd(lower, higher);
66  m_peakToPole = higher / m_gcd;
67 
68  if (m_targetRate < m_sourceRate) {
69  // antialiasing filter, should be slightly below nyquist
70  m_peakToPole = m_peakToPole / (1.0 - bandwidth/2.0);
71  }
72 
74  KaiserWindow::parametersForBandwidth(snr, bandwidth, higher / m_gcd);
75 
76  params.length =
77  (params.length % 2 == 0 ? params.length + 1 : params.length);
78 
79  params.length =
80  (params.length > 200001 ? 200001 : params.length);
81 
82  m_filterLength = params.length;
83 
84  vector<double> filter;
85 
86  KaiserWindow kw(params);
88 
89  filter = vector<double>(m_filterLength, 0.0);
90  for (int i = 0; i < m_filterLength; ++i) filter[i] = 1.0;
91  sw.cut(filter.data());
92  kw.cut(filter.data());
93 
94  int inputSpacing = m_targetRate / m_gcd;
95  int outputSpacing = m_sourceRate / m_gcd;
96 
97 #ifdef DEBUG_RESAMPLER
98  cerr << "resample " << m_sourceRate << " -> " << m_targetRate
99  << ": inputSpacing " << inputSpacing << ", outputSpacing "
100  << outputSpacing << ": filter length " << m_filterLength
101  << endl;
102 #endif
103 
104  // Now we have a filter of (odd) length flen in which the lower
105  // sample rate corresponds to every n'th point and the higher rate
106  // to every m'th where n and m are higher and lower rates divided
107  // by their gcd respectively. So if x coordinates are on the same
108  // scale as our filter resolution, then source sample i is at i *
109  // (targetRate / gcd) and target sample j is at j * (sourceRate /
110  // gcd).
111 
112  // To reconstruct a single target sample, we want a buffer (real
113  // or virtual) of flen values formed of source samples spaced at
114  // intervals of (targetRate / gcd), in our example case 3. This
115  // is initially formed with the first sample at the filter peak.
116  //
117  // 0 0 0 0 a 0 0 b 0
118  //
119  // and of course we have our filter
120  //
121  // f1 f2 f3 f4 f5 f6 f7 f8 f9
122  //
123  // We take the sum of products of non-zero values from this buffer
124  // with corresponding values in the filter
125  //
126  // a * f5 + b * f8
127  //
128  // Then we drop (sourceRate / gcd) values, in our example case 4,
129  // from the start of the buffer and fill until it has flen values
130  // again
131  //
132  // a 0 0 b 0 0 c 0 0
133  //
134  // repeat to reconstruct the next target sample
135  //
136  // a * f1 + b * f4 + c * f7
137  //
138  // and so on.
139  //
140  // Above I said the buffer could be "real or virtual" -- ours is
141  // virtual. We don't actually store all the zero spacing values,
142  // except for padding at the start; normally we store only the
143  // values that actually came from the source stream, along with a
144  // phase value that tells us how many virtual zeroes there are at
145  // the start of the virtual buffer. So the two examples above are
146  //
147  // 0 a b [ with phase 1 ]
148  // a b c [ with phase 0 ]
149  //
150  // Having thus broken down the buffer so that only the elements we
151  // need to multiply are present, we can also unzip the filter into
152  // every-nth-element subsets at each phase, allowing us to do the
153  // filter multiplication as a simply vector multiply. That is, rather
154  // than store
155  //
156  // f1 f2 f3 f4 f5 f6 f7 f8 f9
157  //
158  // we store separately
159  //
160  // f1 f4 f7
161  // f2 f5 f8
162  // f3 f6 f9
163  //
164  // Each time we complete a multiply-and-sum, we need to work out
165  // how many (real) samples to drop from the start of our buffer,
166  // and how many to add at the end of it for the next multiply. We
167  // know we want to drop enough real samples to move along by one
168  // computed output sample, which is our outputSpacing number of
169  // virtual buffer samples. Depending on the relationship between
170  // input and output spacings, this may mean dropping several real
171  // samples, one real sample, or none at all (and simply moving to
172  // a different "phase").
173 
174  m_phaseData = new Phase[inputSpacing];
175 
176  for (int phase = 0; phase < inputSpacing; ++phase) {
177 
178  Phase p;
179 
180  p.nextPhase = phase - outputSpacing;
181  while (p.nextPhase < 0) p.nextPhase += inputSpacing;
182  p.nextPhase %= inputSpacing;
183 
184  p.drop = int(ceil(std::max(0.0, double(outputSpacing - phase))
185  / inputSpacing));
186 
187  int filtZipLength = int(ceil(double(m_filterLength - phase)
188  / inputSpacing));
189 
190  for (int i = 0; i < filtZipLength; ++i) {
191  p.filter.push_back(filter[i * inputSpacing + phase]);
192  }
193 
194  m_phaseData[phase] = p;
195  }
196 
197 #ifdef DEBUG_RESAMPLER
198  int cp = 0;
199  int totDrop = 0;
200  for (int i = 0; i < inputSpacing; ++i) {
201  cerr << "phase = " << cp << ", drop = " << m_phaseData[cp].drop
202  << ", filter length = " << m_phaseData[cp].filter.size()
203  << ", next phase = " << m_phaseData[cp].nextPhase << endl;
204  totDrop += m_phaseData[cp].drop;
205  cp = m_phaseData[cp].nextPhase;
206  }
207  cerr << "total drop = " << totDrop << endl;
208 #endif
209 
210  // The May implementation of this uses a pull model -- we ask the
211  // resampler for a certain number of output samples, and it asks
212  // its source stream for as many as it needs to calculate
213  // those. This means (among other things) that the source stream
214  // can be asked for enough samples up-front to fill the buffer
215  // before the first output sample is generated.
216  //
217  // In this implementation we're using a push model in which a
218  // certain number of source samples is provided and we're asked
219  // for as many output samples as that makes available. But we
220  // can't return any samples from the beginning until half the
221  // filter length has been provided as input. This means we must
222  // either return a very variable number of samples (none at all
223  // until the filter fills, then half the filter length at once) or
224  // else have a lengthy declared latency on the output. We do the
225  // latter. (What do other implementations do?)
226  //
227  // We want to make sure the first "real" sample will eventually be
228  // aligned with the centre sample in the filter (it's tidier, and
229  // easier to do diagnostic calculations that way). So we need to
230  // pick the initial phase and buffer fill accordingly.
231  //
232  // Example: if the inputSpacing is 2, outputSpacing is 3, and
233  // filter length is 7,
234  //
235  // x x x x a b c ... input samples
236  // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 ...
237  // i j k l ... output samples
238  // [--------|--------] <- filter with centre mark
239  //
240  // Let h be the index of the centre mark, here 3 (generally
241  // int(filterLength/2) for odd-length filters).
242  //
243  // The smallest n such that h + n * outputSpacing > filterLength
244  // is 2 (that is, ceil((filterLength - h) / outputSpacing)), and
245  // (h + 2 * outputSpacing) % inputSpacing == 1, so the initial
246  // phase is 1.
247  //
248  // To achieve our n, we need to pre-fill the "virtual" buffer with
249  // 4 zero samples: the x's above. This is int((h + n *
250  // outputSpacing) / inputSpacing). It's the phase that makes this
251  // buffer get dealt with in such a way as to give us an effective
252  // index for sample a of 9 rather than 8 or 10 or whatever.
253  //
254  // This gives us output latency of 2 (== n), i.e. output samples i
255  // and j will appear before the one in which input sample a is at
256  // the centre of the filter.
257 
258  int h = int(m_filterLength / 2);
259  int n = ceil(double(m_filterLength - h) / outputSpacing);
260 
261  m_phase = (h + n * outputSpacing) % inputSpacing;
262 
263  int fill = (h + n * outputSpacing) / inputSpacing;
264 
265  m_latency = n;
266 
267  m_buffer = vector<double>(fill, 0);
268  m_bufferOrigin = 0;
269 
270 #ifdef DEBUG_RESAMPLER
271  cerr << "initial phase " << m_phase << " (as " << (m_filterLength/2) << " % " << inputSpacing << ")"
272  << ", latency " << m_latency << endl;
273 #endif
274 }
275 
276 double
278 {
279  Phase &pd = m_phaseData[m_phase];
280  double v = 0.0;
281  int n = pd.filter.size();
282 
283  if (n + m_bufferOrigin > (int)m_buffer.size()) {
284  cerr << "ERROR: n + m_bufferOrigin > m_buffer.size() [" << n << " + "
285  << m_bufferOrigin << " > " << m_buffer.size() << "]" << endl;
286  throw std::logic_error("n + m_bufferOrigin > m_buffer.size()");
287  }
288 
289  const double *const QM_R__ buf(m_buffer.data() + m_bufferOrigin);
290  const double *const QM_R__ filt(pd.filter.data());
291 
292  for (int i = 0; i < n; ++i) {
293  // NB gcc can only vectorize this with -ffast-math
294  v += buf[i] * filt[i];
295  }
296 
297  m_bufferOrigin += pd.drop;
298  m_phase = pd.nextPhase;
299  return v;
300 }
301 
302 int
303 Resampler::process(const double *src, double *dst, int n)
304 {
305  m_buffer.insert(m_buffer.end(), src, src + n);
306 
307  int maxout = int(ceil(double(n) * m_targetRate / m_sourceRate));
308  int outidx = 0;
309 
310 #ifdef DEBUG_RESAMPLER
311  cerr << "process: buf siz " << m_buffer.size() << " filt siz for phase " << m_phase << " " << m_phaseData[m_phase].filter.size() << endl;
312 #endif
313 
314  double scaleFactor = (double(m_targetRate) / m_gcd) / m_peakToPole;
315 
316  while (outidx < maxout &&
317  m_buffer.size() >= m_phaseData[m_phase].filter.size() + m_bufferOrigin) {
318  dst[outidx] = scaleFactor * reconstructOne();
319  outidx++;
320  }
321 
322  if (m_bufferOrigin > (int)m_buffer.size()) {
323  cerr << "ERROR: m_bufferOrigin > m_buffer.size() ["
324  << m_bufferOrigin << " > " << m_buffer.size() << "]" << endl;
325  throw std::logic_error("m_bufferOrigin > m_buffer.size()");
326  }
327 
328  m_buffer = vector<double>(m_buffer.begin() + m_bufferOrigin, m_buffer.end());
329  m_bufferOrigin = 0;
330 
331  return outidx;
332 }
333 
334 vector<double>
335 Resampler::process(const double *src, int n)
336 {
337  int maxout = int(ceil(double(n) * m_targetRate / m_sourceRate));
338  vector<double> out(maxout, 0.0);
339  int got = process(src, out.data(), n);
340  assert(got <= maxout);
341  if (got < maxout) out.resize(got);
342  return out;
343 }
344 
345 vector<double>
346 Resampler::resample(int sourceRate, int targetRate, const double *data, int n)
347 {
348  Resampler r(sourceRate, targetRate);
349 
350  int latency = r.getLatency();
351 
352  // latency is the output latency. We need to provide enough
353  // padding input samples at the end of input to guarantee at
354  // *least* the latency's worth of output samples. that is,
355 
356  int inputPad = int(ceil((double(latency) * sourceRate) / targetRate));
357 
358  // that means we are providing this much input in total:
359 
360  int n1 = n + inputPad;
361 
362  // and obtaining this much output in total:
363 
364  int m1 = int(ceil((double(n1) * targetRate) / sourceRate));
365 
366  // in order to return this much output to the user:
367 
368  int m = int(ceil((double(n) * targetRate) / sourceRate));
369 
370 #ifdef DEBUG_RESAMPLER
371  cerr << "n = " << n << ", sourceRate = " << sourceRate << ", targetRate = " << targetRate << ", m = " << m << ", latency = " << latency << ", inputPad = " << inputPad << ", m1 = " << m1 << ", n1 = " << n1 << ", n1 - n = " << n1 - n << endl;
372 #endif
373 
374  vector<double> pad(n1 - n, 0.0);
375  vector<double> out(m1 + 1, 0.0);
376 
377  int gotData = r.process(data, out.data(), n);
378  int gotPad = r.process(pad.data(), out.data() + gotData, pad.size());
379  int got = gotData + gotPad;
380 
381 #ifdef DEBUG_RESAMPLER
382  cerr << "resample: " << n << " in, " << pad.size() << " padding, " << got << " out (" << gotData << " data, " << gotPad << " padding, latency = " << latency << ")" << endl;
383 #endif
384 #ifdef DEBUG_RESAMPLER_VERBOSE
385  int printN = 50;
386  cerr << "first " << printN << " in:" << endl;
387  for (int i = 0; i < printN && i < n; ++i) {
388  if (i % 5 == 0) cerr << endl << i << "... ";
389  cerr << data[i] << " ";
390  }
391  cerr << endl;
392 #endif
393 
394  int toReturn = got - latency;
395  if (toReturn > m) toReturn = m;
396 
397  vector<double> sliced(out.begin() + latency,
398  out.begin() + latency + toReturn);
399 
400 #ifdef DEBUG_RESAMPLER_VERBOSE
401  cerr << "first " << printN << " out (after latency compensation), length " << sliced.size() << ":";
402  for (int i = 0; i < printN && i < sliced.size(); ++i) {
403  if (i % 5 == 0) cerr << endl << i << "... ";
404  cerr << sliced[i] << " ";
405  }
406  cerr << endl;
407 #endif
408 
409  return sliced;
410 }
411 
Kaiser window: A windower whose bandwidth and sidelobe height (signal-noise ratio) can be specified...
Definition: KaiserWindow.h:25
std::vector< double > filter
Definition: Resampler.h:87
void initialise(double, double)
Definition: Resampler.cpp:60
int m_latency
Definition: Resampler.h:82
double reconstructOne()
Definition: Resampler.cpp:277
#define QM_R__
Definition: Restrict.h:15
int m_phase
Definition: Resampler.h:92
Resampler resamples a stream from one integer sample rate to another (arbitrary) rate, using a kaiser-windowed sinc filter.
Definition: Resampler.h:30
int getLatency() const
Return the number of samples of latency at the output due by the filter.
Definition: Resampler.h:68
A window containing values of the sinc function, i.e.
Definition: SincWindow.h:23
void cut(double *src) const
Definition: SincWindow.h:43
virtual ~Resampler()
Definition: Resampler.cpp:54
int m_sourceRate
Definition: Resampler.h:78
static Parameters parametersForBandwidth(double attenuation, double bandwidth, double samplerate)
Obtain the parameters necessary for a Kaiser window of the given attenuation in dB and transition ban...
Definition: KaiserWindow.h:72
int m_filterLength
Definition: Resampler.h:81
int m_gcd
Definition: Resampler.h:80
int process(const double *src, double *dst, int n)
Read n input samples from src and write resampled data to dst.
Definition: Resampler.cpp:303
Phase * m_phaseData
Definition: Resampler.h:91
int m_bufferOrigin
Definition: Resampler.h:94
int m_targetRate
Definition: Resampler.h:79
std::vector< double > m_buffer
Definition: Resampler.h:93
Resampler(int sourceRate, int targetRate)
Construct a Resampler to resample from sourceRate to targetRate.
Definition: Resampler.cpp:36
static int gcd(int a, int b)
Return the greatest common divisor of natural numbers a and b.
static std::vector< double > resample(int sourceRate, int targetRate, const double *data, int n)
Carry out a one-off resample of a single block of n samples.
Definition: Resampler.cpp:346
void cut(double *src) const
Definition: KaiserWindow.h:87
double m_peakToPole
Definition: Resampler.h:83