annotate dsp/rateconversion/Resampler.cpp @ 209:ccd2019190bf msvc

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