annotate SimpleCepstrum.cpp @ 1:ce41d2d066a1

Minor simplification
author Chris Cannam
date Fri, 22 Jun 2012 16:58:55 +0100
parents 02587f02ef41
children e6faf01e25d8
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 #include "SimpleCepstrum.h"
Chris@0 4
Chris@0 5 #include <vector>
Chris@0 6 #include <algorithm>
Chris@0 7
Chris@0 8 #include <cstdio>
Chris@0 9 #include <cmath>
Chris@0 10
Chris@0 11 using std::string;
Chris@0 12
Chris@0 13 SimpleCepstrum::SimpleCepstrum(float inputSampleRate) :
Chris@0 14 Plugin(inputSampleRate),
Chris@0 15 m_channels(0),
Chris@0 16 m_stepSize(256),
Chris@0 17 m_blockSize(1024),
Chris@0 18 m_fmin(50),
Chris@0 19 m_fmax(1000),
Chris@0 20 m_clamp(false)
Chris@0 21 {
Chris@0 22 }
Chris@0 23
Chris@0 24 SimpleCepstrum::~SimpleCepstrum()
Chris@0 25 {
Chris@0 26 }
Chris@0 27
Chris@0 28 string
Chris@0 29 SimpleCepstrum::getIdentifier() const
Chris@0 30 {
Chris@0 31 return "simple-cepstrum";
Chris@0 32 }
Chris@0 33
Chris@0 34 string
Chris@0 35 SimpleCepstrum::getName() const
Chris@0 36 {
Chris@0 37 return "Simple Cepstrum";
Chris@0 38 }
Chris@0 39
Chris@0 40 string
Chris@0 41 SimpleCepstrum::getDescription() const
Chris@0 42 {
Chris@0 43 return "Return simple cepstral data from DFT bins";
Chris@0 44 }
Chris@0 45
Chris@0 46 string
Chris@0 47 SimpleCepstrum::getMaker() const
Chris@0 48 {
Chris@0 49 // Your name here
Chris@0 50 return "";
Chris@0 51 }
Chris@0 52
Chris@0 53 int
Chris@0 54 SimpleCepstrum::getPluginVersion() const
Chris@0 55 {
Chris@0 56 // Increment this each time you release a version that behaves
Chris@0 57 // differently from the previous one
Chris@0 58 return 1;
Chris@0 59 }
Chris@0 60
Chris@0 61 string
Chris@0 62 SimpleCepstrum::getCopyright() const
Chris@0 63 {
Chris@0 64 // This function is not ideally named. It does not necessarily
Chris@0 65 // need to say who made the plugin -- getMaker does that -- but it
Chris@0 66 // should indicate the terms under which it is distributed. For
Chris@0 67 // example, "Copyright (year). All Rights Reserved", or "GPL"
Chris@0 68 return "";
Chris@0 69 }
Chris@0 70
Chris@0 71 SimpleCepstrum::InputDomain
Chris@0 72 SimpleCepstrum::getInputDomain() const
Chris@0 73 {
Chris@0 74 return FrequencyDomain;
Chris@0 75 }
Chris@0 76
Chris@0 77 size_t
Chris@0 78 SimpleCepstrum::getPreferredBlockSize() const
Chris@0 79 {
Chris@0 80 return 1024;
Chris@0 81 }
Chris@0 82
Chris@0 83 size_t
Chris@0 84 SimpleCepstrum::getPreferredStepSize() const
Chris@0 85 {
Chris@0 86 return 256;
Chris@0 87 }
Chris@0 88
Chris@0 89 size_t
Chris@0 90 SimpleCepstrum::getMinChannelCount() const
Chris@0 91 {
Chris@0 92 return 1;
Chris@0 93 }
Chris@0 94
Chris@0 95 size_t
Chris@0 96 SimpleCepstrum::getMaxChannelCount() const
Chris@0 97 {
Chris@0 98 return 1;
Chris@0 99 }
Chris@0 100
Chris@0 101 SimpleCepstrum::ParameterList
Chris@0 102 SimpleCepstrum::getParameterDescriptors() const
Chris@0 103 {
Chris@0 104 ParameterList list;
Chris@0 105
Chris@0 106 ParameterDescriptor d;
Chris@0 107
Chris@0 108 d.identifier = "fmin";
Chris@0 109 d.name = "Minimum frequency";
Chris@0 110 d.description = "";
Chris@0 111 d.unit = "Hz";
Chris@0 112 d.minValue = m_inputSampleRate / m_blockSize;
Chris@0 113 d.maxValue = m_inputSampleRate / 2;
Chris@0 114 d.defaultValue = 50;
Chris@0 115 d.isQuantized = false;
Chris@0 116 list.push_back(d);
Chris@0 117
Chris@0 118 d.identifier = "fmax";
Chris@0 119 d.name = "Maximum frequency";
Chris@0 120 d.description = "";
Chris@0 121 d.unit = "Hz";
Chris@0 122 d.minValue = m_inputSampleRate / m_blockSize;
Chris@0 123 d.maxValue = m_inputSampleRate / 2;
Chris@0 124 d.defaultValue = 1000;
Chris@0 125 d.isQuantized = false;
Chris@0 126 list.push_back(d);
Chris@0 127
Chris@0 128 d.identifier = "clamp";
Chris@0 129 d.name = "Clamp negative values in cepstrum at zero";
Chris@0 130 d.unit = "";
Chris@0 131 d.minValue = 0;
Chris@0 132 d.maxValue = 1;
Chris@0 133 d.defaultValue = 0;
Chris@0 134 d.isQuantized = true;
Chris@0 135 d.quantizeStep = 1;
Chris@0 136 list.push_back(d);
Chris@0 137
Chris@0 138 return list;
Chris@0 139 }
Chris@0 140
Chris@0 141 float
Chris@0 142 SimpleCepstrum::getParameter(string identifier) const
Chris@0 143 {
Chris@0 144 if (identifier == "fmin") return m_fmin;
Chris@0 145 else if (identifier == "fmax") return m_fmax;
Chris@0 146 else if (identifier == "clamp") return (m_clamp ? 1 : 0);
Chris@0 147 else return 0.f;
Chris@0 148 }
Chris@0 149
Chris@0 150 void
Chris@0 151 SimpleCepstrum::setParameter(string identifier, float value)
Chris@0 152 {
Chris@0 153 if (identifier == "fmin") m_fmin = value;
Chris@0 154 else if (identifier == "fmax") m_fmax = value;
Chris@0 155 else if (identifier == "clamp") m_clamp = (value > 0.5);
Chris@0 156 }
Chris@0 157
Chris@0 158 SimpleCepstrum::ProgramList
Chris@0 159 SimpleCepstrum::getPrograms() const
Chris@0 160 {
Chris@0 161 ProgramList list;
Chris@0 162 return list;
Chris@0 163 }
Chris@0 164
Chris@0 165 string
Chris@0 166 SimpleCepstrum::getCurrentProgram() const
Chris@0 167 {
Chris@0 168 return ""; // no programs
Chris@0 169 }
Chris@0 170
Chris@0 171 void
Chris@0 172 SimpleCepstrum::selectProgram(string name)
Chris@0 173 {
Chris@0 174 }
Chris@0 175
Chris@0 176 SimpleCepstrum::OutputList
Chris@0 177 SimpleCepstrum::getOutputDescriptors() const
Chris@0 178 {
Chris@0 179 OutputList outputs;
Chris@0 180
Chris@0 181 int n = 0;
Chris@0 182
Chris@0 183 OutputDescriptor d;
Chris@0 184 d.identifier = "f0";
Chris@0 185 d.name = "Estimated fundamental frequency";
Chris@0 186 d.description = "";
Chris@0 187 d.unit = "";
Chris@0 188 d.hasFixedBinCount = true;
Chris@0 189 d.binCount = 1;
Chris@0 190 d.hasKnownExtents = true;
Chris@0 191 d.minValue = m_fmin;
Chris@0 192 d.maxValue = m_fmax;
Chris@0 193 d.isQuantized = false;
Chris@0 194 d.sampleType = OutputDescriptor::OneSamplePerStep;
Chris@0 195 d.hasDuration = false;
Chris@0 196 m_f0Output = n++;
Chris@0 197 outputs.push_back(d);
Chris@0 198
Chris@0 199 d.identifier = "raw_cepstral_peak";
Chris@0 200 d.name = "Frequency corresponding to raw cepstral peak";
Chris@0 201 d.unit = "Hz";
Chris@0 202 m_rawOutput = n++;
Chris@0 203 outputs.push_back(d);
Chris@0 204
Chris@0 205 d.identifier = "variance";
Chris@0 206 d.name = "Variance of cepstral bins in range";
Chris@0 207 d.unit = "";
Chris@0 208 m_varOutput = n++;
Chris@0 209 outputs.push_back(d);
Chris@0 210
Chris@0 211 d.identifier = "peak";
Chris@0 212 d.name = "Peak value";
Chris@0 213 d.unit = "";
Chris@0 214 m_pvOutput = n++;
Chris@0 215 outputs.push_back(d);
Chris@0 216
Chris@0 217 d.identifier = "peak_to_mean";
Chris@0 218 d.name = "Peak-to-mean distance";
Chris@0 219 d.unit = "";
Chris@0 220 m_p2mOutput = n++;
Chris@0 221 outputs.push_back(d);
Chris@0 222
Chris@0 223 d.identifier = "cepstrum";
Chris@0 224 d.name = "Cepstrum";
Chris@0 225 d.unit = "";
Chris@0 226
Chris@0 227 int from = int(m_inputSampleRate / m_fmax);
Chris@0 228 int to = int(m_inputSampleRate / m_fmin);
Chris@0 229 if (to >= (int)m_blockSize / 2) {
Chris@0 230 to = m_blockSize / 2 - 1;
Chris@0 231 }
Chris@0 232 d.binCount = to - from + 1;
Chris@0 233 for (int i = from; i <= to; ++i) {
Chris@0 234 float freq = m_inputSampleRate / i;
Chris@0 235 char buffer[10];
Chris@0 236 sprintf(buffer, "%.2f", freq);
Chris@0 237 d.binNames.push_back(buffer);
Chris@0 238 }
Chris@0 239
Chris@0 240 d.hasKnownExtents = false;
Chris@0 241 m_cepOutput = n++;
Chris@0 242 outputs.push_back(d);
Chris@0 243
Chris@0 244 d.identifier = "am";
Chris@0 245 d.name = "Cepstrum bins relative to mean";
Chris@0 246 m_amOutput = n++;
Chris@0 247 outputs.push_back(d);
Chris@0 248
Chris@0 249 return outputs;
Chris@0 250 }
Chris@0 251
Chris@0 252 bool
Chris@0 253 SimpleCepstrum::initialise(size_t channels, size_t stepSize, size_t blockSize)
Chris@0 254 {
Chris@0 255 if (channels < getMinChannelCount() ||
Chris@0 256 channels > getMaxChannelCount()) return false;
Chris@0 257
Chris@0 258 // std::cerr << "SimpleCepstrum::initialise: channels = " << channels
Chris@0 259 // << ", stepSize = " << stepSize << ", blockSize = " << blockSize
Chris@0 260 // << std::endl;
Chris@0 261
Chris@0 262 m_channels = channels;
Chris@0 263 m_stepSize = stepSize;
Chris@0 264 m_blockSize = blockSize;
Chris@0 265
Chris@0 266 return true;
Chris@0 267 }
Chris@0 268
Chris@0 269 void
Chris@0 270 SimpleCepstrum::reset()
Chris@0 271 {
Chris@0 272 }
Chris@0 273
Chris@0 274 SimpleCepstrum::FeatureSet
Chris@0 275 SimpleCepstrum::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
Chris@0 276 {
Chris@1 277 FeatureSet fs;
Chris@1 278
Chris@0 279 int bs = m_blockSize;
Chris@0 280 int hs = m_blockSize/2 + 1;
Chris@0 281
Chris@0 282 double *logmag = new double[bs];
Chris@0 283 for (int i = 0; i < hs; ++i) {
Chris@0 284 double mag = sqrt(inputBuffers[0][i*2 ] * inputBuffers[0][i*2 ] +
Chris@0 285 inputBuffers[0][i*2+1] * inputBuffers[0][i*2+1]);
Chris@0 286 logmag[i] = log(mag + 0.000001);
Chris@0 287 if (i > 0) {
Chris@0 288 logmag[bs - i] = logmag[i];
Chris@0 289 }
Chris@0 290 }
Chris@0 291
Chris@0 292 double *cep = new double[bs];
Chris@1 293 double *discard = new double[bs];
Chris@1 294 fft(bs, true, logmag, 0, cep, discard);
Chris@1 295 delete[] discard;
Chris@0 296
Chris@0 297 if (m_clamp) {
Chris@0 298 for (int i = 0; i < bs; ++i) {
Chris@0 299 if (cep[i] < 0) cep[i] = 0;
Chris@0 300 }
Chris@0 301 }
Chris@0 302
Chris@0 303 int from = int(m_inputSampleRate / m_fmax);
Chris@0 304 int to = int(m_inputSampleRate / m_fmin);
Chris@0 305
Chris@0 306 if (to >= bs / 2) {
Chris@0 307 to = bs / 2 - 1;
Chris@0 308 }
Chris@0 309
Chris@0 310 Feature cf;
Chris@0 311 for (int i = from; i <= to; ++i) {
Chris@0 312 cf.values.push_back(cep[i]);
Chris@0 313 }
Chris@0 314 fs[m_cepOutput].push_back(cf);
Chris@0 315
Chris@0 316 float maxval = 0.f;
Chris@0 317 int maxbin = 0;
Chris@0 318
Chris@0 319 for (int i = from; i <= to; ++i) {
Chris@0 320 if (cep[i] > maxval) {
Chris@0 321 maxval = cep[i];
Chris@0 322 maxbin = i;
Chris@0 323 }
Chris@0 324 }
Chris@0 325
Chris@0 326 Feature rf;
Chris@0 327 if (maxbin > 0) {
Chris@0 328 rf.values.push_back(m_inputSampleRate / maxbin);
Chris@0 329 } else {
Chris@0 330 rf.values.push_back(0);
Chris@0 331 }
Chris@0 332 fs[m_rawOutput].push_back(rf);
Chris@0 333
Chris@0 334 float mean = 0;
Chris@0 335 for (int i = from; i <= to; ++i) {
Chris@0 336 mean += cep[i];
Chris@0 337 }
Chris@0 338 mean /= (to - from) + 1;
Chris@0 339
Chris@0 340 float variance = 0;
Chris@0 341 for (int i = from; i <= to; ++i) {
Chris@0 342 float dev = fabsf(cep[i] - mean);
Chris@0 343 variance += dev * dev;
Chris@0 344 }
Chris@0 345 variance /= (to - from) + 1;
Chris@0 346
Chris@0 347 Feature vf;
Chris@0 348 vf.values.push_back(variance);
Chris@0 349 fs[m_varOutput].push_back(vf);
Chris@0 350
Chris@0 351 Feature pf;
Chris@0 352 pf.values.push_back(maxval - mean);
Chris@0 353 fs[m_p2mOutput].push_back(pf);
Chris@0 354
Chris@0 355 Feature pv;
Chris@0 356 pv.values.push_back(maxval);
Chris@0 357 fs[m_pvOutput].push_back(pv);
Chris@0 358
Chris@0 359 Feature am;
Chris@0 360 for (int i = from; i <= to; ++i) {
Chris@0 361 if (cep[i] < mean) am.values.push_back(0);
Chris@0 362 else am.values.push_back(cep[i] - mean);
Chris@0 363 }
Chris@0 364 fs[m_amOutput].push_back(am);
Chris@0 365
Chris@0 366 delete[] logmag;
Chris@0 367 delete[] cep;
Chris@0 368
Chris@0 369 return fs;
Chris@0 370 }
Chris@0 371
Chris@0 372 SimpleCepstrum::FeatureSet
Chris@0 373 SimpleCepstrum::getRemainingFeatures()
Chris@0 374 {
Chris@0 375 FeatureSet fs;
Chris@0 376 return fs;
Chris@0 377 }
Chris@0 378
Chris@0 379 void
Chris@0 380 SimpleCepstrum::fft(unsigned int n, bool inverse,
Chris@0 381 double *ri, double *ii, double *ro, double *io)
Chris@0 382 {
Chris@0 383 if (!ri || !ro || !io) return;
Chris@0 384
Chris@0 385 unsigned int bits;
Chris@0 386 unsigned int i, j, k, m;
Chris@0 387 unsigned int blockSize, blockEnd;
Chris@0 388
Chris@0 389 double tr, ti;
Chris@0 390
Chris@0 391 if (n < 2) return;
Chris@0 392 if (n & (n-1)) return;
Chris@0 393
Chris@0 394 double angle = 2.0 * M_PI;
Chris@0 395 if (inverse) angle = -angle;
Chris@0 396
Chris@0 397 for (i = 0; ; ++i) {
Chris@0 398 if (n & (1 << i)) {
Chris@0 399 bits = i;
Chris@0 400 break;
Chris@0 401 }
Chris@0 402 }
Chris@0 403
Chris@0 404 static unsigned int tableSize = 0;
Chris@0 405 static int *table = 0;
Chris@0 406
Chris@0 407 if (tableSize != n) {
Chris@0 408
Chris@0 409 delete[] table;
Chris@0 410
Chris@0 411 table = new int[n];
Chris@0 412
Chris@0 413 for (i = 0; i < n; ++i) {
Chris@0 414
Chris@0 415 m = i;
Chris@0 416
Chris@0 417 for (j = k = 0; j < bits; ++j) {
Chris@0 418 k = (k << 1) | (m & 1);
Chris@0 419 m >>= 1;
Chris@0 420 }
Chris@0 421
Chris@0 422 table[i] = k;
Chris@0 423 }
Chris@0 424
Chris@0 425 tableSize = n;
Chris@0 426 }
Chris@0 427
Chris@0 428 if (ii) {
Chris@0 429 for (i = 0; i < n; ++i) {
Chris@0 430 ro[table[i]] = ri[i];
Chris@0 431 io[table[i]] = ii[i];
Chris@0 432 }
Chris@0 433 } else {
Chris@0 434 for (i = 0; i < n; ++i) {
Chris@0 435 ro[table[i]] = ri[i];
Chris@0 436 io[table[i]] = 0.0;
Chris@0 437 }
Chris@0 438 }
Chris@0 439
Chris@0 440 blockEnd = 1;
Chris@0 441
Chris@0 442 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
Chris@0 443
Chris@0 444 double delta = angle / (double)blockSize;
Chris@0 445 double sm2 = -sin(-2 * delta);
Chris@0 446 double sm1 = -sin(-delta);
Chris@0 447 double cm2 = cos(-2 * delta);
Chris@0 448 double cm1 = cos(-delta);
Chris@0 449 double w = 2 * cm1;
Chris@0 450 double ar[3], ai[3];
Chris@0 451
Chris@0 452 for (i = 0; i < n; i += blockSize) {
Chris@0 453
Chris@0 454 ar[2] = cm2;
Chris@0 455 ar[1] = cm1;
Chris@0 456
Chris@0 457 ai[2] = sm2;
Chris@0 458 ai[1] = sm1;
Chris@0 459
Chris@0 460 for (j = i, m = 0; m < blockEnd; j++, m++) {
Chris@0 461
Chris@0 462 ar[0] = w * ar[1] - ar[2];
Chris@0 463 ar[2] = ar[1];
Chris@0 464 ar[1] = ar[0];
Chris@0 465
Chris@0 466 ai[0] = w * ai[1] - ai[2];
Chris@0 467 ai[2] = ai[1];
Chris@0 468 ai[1] = ai[0];
Chris@0 469
Chris@0 470 k = j + blockEnd;
Chris@0 471 tr = ar[0] * ro[k] - ai[0] * io[k];
Chris@0 472 ti = ar[0] * io[k] + ai[0] * ro[k];
Chris@0 473
Chris@0 474 ro[k] = ro[j] - tr;
Chris@0 475 io[k] = io[j] - ti;
Chris@0 476
Chris@0 477 ro[j] += tr;
Chris@0 478 io[j] += ti;
Chris@0 479 }
Chris@0 480 }
Chris@0 481
Chris@0 482 blockEnd = blockSize;
Chris@0 483 }
Chris@0 484 }
Chris@0 485
Chris@0 486