annotate SimpleCepstrum.cpp @ 6:ffed34f519db

Add total and peak-proportion outputs
author Chris Cannam
date Mon, 25 Jun 2012 13:39:46 +0100
parents 05c558f1a23b
children 47355877a58d
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@4 10 #include <complex>
Chris@0 11
Chris@0 12 using std::string;
Chris@0 13
Chris@0 14 SimpleCepstrum::SimpleCepstrum(float inputSampleRate) :
Chris@0 15 Plugin(inputSampleRate),
Chris@0 16 m_channels(0),
Chris@0 17 m_stepSize(256),
Chris@0 18 m_blockSize(1024),
Chris@0 19 m_fmin(50),
Chris@0 20 m_fmax(1000),
Chris@5 21 m_histlen(3),
Chris@3 22 m_clamp(false),
Chris@5 23 m_method(InverseSymmetric),
Chris@5 24 m_binFrom(0),
Chris@5 25 m_binTo(0),
Chris@5 26 m_bins(0),
Chris@5 27 m_history(0)
Chris@0 28 {
Chris@0 29 }
Chris@0 30
Chris@0 31 SimpleCepstrum::~SimpleCepstrum()
Chris@0 32 {
Chris@5 33 if (m_history) {
Chris@5 34 for (int i = 0; i < m_histlen; ++i) {
Chris@5 35 delete[] m_history[i];
Chris@5 36 }
Chris@5 37 delete[] m_history;
Chris@5 38 }
Chris@0 39 }
Chris@0 40
Chris@0 41 string
Chris@0 42 SimpleCepstrum::getIdentifier() const
Chris@0 43 {
Chris@0 44 return "simple-cepstrum";
Chris@0 45 }
Chris@0 46
Chris@0 47 string
Chris@0 48 SimpleCepstrum::getName() const
Chris@0 49 {
Chris@0 50 return "Simple Cepstrum";
Chris@0 51 }
Chris@0 52
Chris@0 53 string
Chris@0 54 SimpleCepstrum::getDescription() const
Chris@0 55 {
Chris@2 56 return "Return simple cepstral data from DFT bins. This plugin is intended for casual inspection of cepstral data. It returns a lot of different sorts of data and is quite slow; it's not a good way to extract a single feature rapidly.";
Chris@0 57 }
Chris@0 58
Chris@0 59 string
Chris@0 60 SimpleCepstrum::getMaker() const
Chris@0 61 {
Chris@0 62 // Your name here
Chris@0 63 return "";
Chris@0 64 }
Chris@0 65
Chris@0 66 int
Chris@0 67 SimpleCepstrum::getPluginVersion() const
Chris@0 68 {
Chris@0 69 // Increment this each time you release a version that behaves
Chris@0 70 // differently from the previous one
Chris@0 71 return 1;
Chris@0 72 }
Chris@0 73
Chris@0 74 string
Chris@0 75 SimpleCepstrum::getCopyright() const
Chris@0 76 {
Chris@0 77 // This function is not ideally named. It does not necessarily
Chris@0 78 // need to say who made the plugin -- getMaker does that -- but it
Chris@0 79 // should indicate the terms under which it is distributed. For
Chris@0 80 // example, "Copyright (year). All Rights Reserved", or "GPL"
Chris@0 81 return "";
Chris@0 82 }
Chris@0 83
Chris@0 84 SimpleCepstrum::InputDomain
Chris@0 85 SimpleCepstrum::getInputDomain() const
Chris@0 86 {
Chris@0 87 return FrequencyDomain;
Chris@0 88 }
Chris@0 89
Chris@0 90 size_t
Chris@0 91 SimpleCepstrum::getPreferredBlockSize() const
Chris@0 92 {
Chris@0 93 return 1024;
Chris@0 94 }
Chris@0 95
Chris@0 96 size_t
Chris@0 97 SimpleCepstrum::getPreferredStepSize() const
Chris@0 98 {
Chris@0 99 return 256;
Chris@0 100 }
Chris@0 101
Chris@0 102 size_t
Chris@0 103 SimpleCepstrum::getMinChannelCount() const
Chris@0 104 {
Chris@0 105 return 1;
Chris@0 106 }
Chris@0 107
Chris@0 108 size_t
Chris@0 109 SimpleCepstrum::getMaxChannelCount() const
Chris@0 110 {
Chris@0 111 return 1;
Chris@0 112 }
Chris@0 113
Chris@0 114 SimpleCepstrum::ParameterList
Chris@0 115 SimpleCepstrum::getParameterDescriptors() const
Chris@0 116 {
Chris@0 117 ParameterList list;
Chris@0 118
Chris@0 119 ParameterDescriptor d;
Chris@0 120
Chris@0 121 d.identifier = "fmin";
Chris@0 122 d.name = "Minimum frequency";
Chris@0 123 d.description = "";
Chris@0 124 d.unit = "Hz";
Chris@0 125 d.minValue = m_inputSampleRate / m_blockSize;
Chris@0 126 d.maxValue = m_inputSampleRate / 2;
Chris@0 127 d.defaultValue = 50;
Chris@0 128 d.isQuantized = false;
Chris@0 129 list.push_back(d);
Chris@0 130
Chris@0 131 d.identifier = "fmax";
Chris@0 132 d.name = "Maximum frequency";
Chris@0 133 d.description = "";
Chris@0 134 d.unit = "Hz";
Chris@0 135 d.minValue = m_inputSampleRate / m_blockSize;
Chris@0 136 d.maxValue = m_inputSampleRate / 2;
Chris@0 137 d.defaultValue = 1000;
Chris@0 138 d.isQuantized = false;
Chris@0 139 list.push_back(d);
Chris@0 140
Chris@5 141 d.identifier = "histlen";
Chris@5 142 d.name = "Mean filter history length";
Chris@5 143 d.description = "";
Chris@5 144 d.unit = "";
Chris@5 145 d.minValue = 1;
Chris@5 146 d.maxValue = 10;
Chris@5 147 d.defaultValue = 3;
Chris@5 148 d.isQuantized = true;
Chris@5 149 d.quantizeStep = 1;
Chris@5 150 list.push_back(d);
Chris@5 151
Chris@3 152 d.identifier = "method";
Chris@3 153 d.name = "Cepstrum transform method";
Chris@3 154 d.unit = "";
Chris@3 155 d.minValue = 0;
Chris@5 156 d.maxValue = 4;
Chris@3 157 d.defaultValue = 0;
Chris@3 158 d.isQuantized = true;
Chris@3 159 d.quantizeStep = 1;
Chris@3 160 d.valueNames.push_back("Inverse symmetric");
Chris@3 161 d.valueNames.push_back("Inverse asymmetric");
Chris@4 162 d.valueNames.push_back("Inverse complex");
Chris@3 163 d.valueNames.push_back("Forward magnitude");
Chris@3 164 d.valueNames.push_back("Forward difference");
Chris@3 165 list.push_back(d);
Chris@3 166
Chris@0 167 d.identifier = "clamp";
Chris@0 168 d.name = "Clamp negative values in cepstrum at zero";
Chris@0 169 d.unit = "";
Chris@0 170 d.minValue = 0;
Chris@0 171 d.maxValue = 1;
Chris@0 172 d.defaultValue = 0;
Chris@0 173 d.isQuantized = true;
Chris@0 174 d.quantizeStep = 1;
Chris@3 175 d.valueNames.clear();
Chris@0 176 list.push_back(d);
Chris@0 177
Chris@0 178 return list;
Chris@0 179 }
Chris@0 180
Chris@0 181 float
Chris@0 182 SimpleCepstrum::getParameter(string identifier) const
Chris@0 183 {
Chris@0 184 if (identifier == "fmin") return m_fmin;
Chris@0 185 else if (identifier == "fmax") return m_fmax;
Chris@5 186 else if (identifier == "histlen") return m_histlen;
Chris@0 187 else if (identifier == "clamp") return (m_clamp ? 1 : 0);
Chris@3 188 else if (identifier == "method") return (int)m_method;
Chris@0 189 else return 0.f;
Chris@0 190 }
Chris@0 191
Chris@0 192 void
Chris@0 193 SimpleCepstrum::setParameter(string identifier, float value)
Chris@0 194 {
Chris@0 195 if (identifier == "fmin") m_fmin = value;
Chris@0 196 else if (identifier == "fmax") m_fmax = value;
Chris@5 197 else if (identifier == "histlen") m_histlen = value;
Chris@0 198 else if (identifier == "clamp") m_clamp = (value > 0.5);
Chris@3 199 else if (identifier == "method") m_method = Method(int(value + 0.5));
Chris@0 200 }
Chris@0 201
Chris@0 202 SimpleCepstrum::ProgramList
Chris@0 203 SimpleCepstrum::getPrograms() const
Chris@0 204 {
Chris@0 205 ProgramList list;
Chris@0 206 return list;
Chris@0 207 }
Chris@0 208
Chris@0 209 string
Chris@0 210 SimpleCepstrum::getCurrentProgram() const
Chris@0 211 {
Chris@0 212 return ""; // no programs
Chris@0 213 }
Chris@0 214
Chris@0 215 void
Chris@0 216 SimpleCepstrum::selectProgram(string name)
Chris@0 217 {
Chris@0 218 }
Chris@0 219
Chris@0 220 SimpleCepstrum::OutputList
Chris@0 221 SimpleCepstrum::getOutputDescriptors() const
Chris@0 222 {
Chris@0 223 OutputList outputs;
Chris@0 224
Chris@0 225 int n = 0;
Chris@0 226
Chris@0 227 OutputDescriptor d;
Chris@2 228
Chris@0 229 d.identifier = "f0";
Chris@0 230 d.name = "Estimated fundamental frequency";
Chris@0 231 d.description = "";
Chris@6 232 d.unit = "Hz";
Chris@0 233 d.hasFixedBinCount = true;
Chris@0 234 d.binCount = 1;
Chris@0 235 d.hasKnownExtents = true;
Chris@0 236 d.minValue = m_fmin;
Chris@0 237 d.maxValue = m_fmax;
Chris@0 238 d.isQuantized = false;
Chris@0 239 d.sampleType = OutputDescriptor::OneSamplePerStep;
Chris@0 240 d.hasDuration = false;
Chris@2 241 /*
Chris@0 242 m_f0Output = n++;
Chris@0 243 outputs.push_back(d);
Chris@2 244 */
Chris@0 245
Chris@0 246 d.identifier = "raw_cepstral_peak";
Chris@0 247 d.name = "Frequency corresponding to raw cepstral peak";
Chris@2 248 d.description = "Return the frequency whose period corresponds to the quefrency with the maximum value within the specified range of the cepstrum";
Chris@0 249 d.unit = "Hz";
Chris@5 250 m_pkOutput = n++;
Chris@0 251 outputs.push_back(d);
Chris@0 252
Chris@0 253 d.identifier = "variance";
Chris@0 254 d.name = "Variance of cepstral bins in range";
Chris@0 255 d.unit = "";
Chris@2 256 d.description = "Return the variance of bin values within the specified range of the cepstrum";
Chris@6 257 d.hasKnownExtents = false;
Chris@0 258 m_varOutput = n++;
Chris@0 259 outputs.push_back(d);
Chris@0 260
Chris@0 261 d.identifier = "peak";
Chris@0 262 d.name = "Peak value";
Chris@0 263 d.unit = "";
Chris@2 264 d.description = "Return the value found in the maximum-valued bin within the specified range of the cepstrum";
Chris@0 265 m_pvOutput = n++;
Chris@0 266 outputs.push_back(d);
Chris@0 267
Chris@0 268 d.identifier = "peak_to_mean";
Chris@0 269 d.name = "Peak-to-mean distance";
Chris@0 270 d.unit = "";
Chris@2 271 d.description = "Return the difference between maximum and mean bin values within the specified range of the cepstrum";
Chris@0 272 m_p2mOutput = n++;
Chris@0 273 outputs.push_back(d);
Chris@0 274
Chris@5 275 d.identifier = "peak_to_rms";
Chris@5 276 d.name = "Peak-to-RMS distance";
Chris@5 277 d.unit = "";
Chris@5 278 d.description = "Return the difference between maximum and root mean square bin values within the specified range of the cepstrum";
Chris@5 279 m_p2rOutput = n++;
Chris@5 280 outputs.push_back(d);
Chris@5 281
Chris@6 282 d.identifier = "peak_proportion";
Chris@6 283 d.name = "Peak proportion";
Chris@6 284 d.unit = "";
Chris@6 285 d.description = "Return the proportion of total energy found in the bins around the peak bin of the cepstrum (as far as the nearest local minima)";
Chris@6 286 m_ppOutput = n++;
Chris@6 287 outputs.push_back(d);
Chris@6 288
Chris@6 289 d.identifier = "total";
Chris@6 290 d.name = "Total energy";
Chris@6 291 d.unit = "";
Chris@6 292 d.description = "Return the total energy found in all cepstrum bins within range";
Chris@6 293 m_totOutput = n++;
Chris@6 294 outputs.push_back(d);
Chris@6 295
Chris@0 296 d.identifier = "cepstrum";
Chris@0 297 d.name = "Cepstrum";
Chris@0 298 d.unit = "";
Chris@2 299 d.description = "The unprocessed cepstrum bins within the specified range";
Chris@0 300
Chris@0 301 int from = int(m_inputSampleRate / m_fmax);
Chris@0 302 int to = int(m_inputSampleRate / m_fmin);
Chris@0 303 if (to >= (int)m_blockSize / 2) {
Chris@0 304 to = m_blockSize / 2 - 1;
Chris@0 305 }
Chris@0 306 d.binCount = to - from + 1;
Chris@0 307 for (int i = from; i <= to; ++i) {
Chris@0 308 float freq = m_inputSampleRate / i;
Chris@5 309 char buffer[20];
Chris@2 310 sprintf(buffer, "%.2f Hz", freq);
Chris@0 311 d.binNames.push_back(buffer);
Chris@0 312 }
Chris@0 313
Chris@0 314 d.hasKnownExtents = false;
Chris@0 315 m_cepOutput = n++;
Chris@0 316 outputs.push_back(d);
Chris@0 317
Chris@0 318 d.identifier = "am";
Chris@5 319 d.name = "Cepstrum bins relative to RMS";
Chris@5 320 d.description = "The cepstrum bins within the specified range, expressed as a value relative to the root mean square bin value in the range, with values below the RMS clamped to zero";
Chris@0 321 m_amOutput = n++;
Chris@0 322 outputs.push_back(d);
Chris@0 323
Chris@2 324 d.identifier = "env";
Chris@2 325 d.name = "Spectral envelope";
Chris@2 326 d.description = "Envelope calculated from the cepstral values below the specified minimum";
Chris@2 327 d.binCount = m_blockSize/2 + 1;
Chris@2 328 d.binNames.clear();
Chris@2 329 for (int i = 0; i < d.binCount; ++i) {
Chris@2 330 float freq = (m_inputSampleRate / m_blockSize) * i;
Chris@5 331 char buffer[20];
Chris@2 332 sprintf(buffer, "%.2f Hz", freq);
Chris@2 333 d.binNames.push_back(buffer);
Chris@2 334 }
Chris@2 335 m_envOutput = n++;
Chris@2 336 outputs.push_back(d);
Chris@2 337
Chris@2 338 d.identifier = "es";
Chris@2 339 d.name = "Spectrum without envelope";
Chris@2 340 d.description = "Magnitude of spectrum values divided by calculated envelope values, to deconvolve the envelope";
Chris@2 341 m_esOutput = n++;
Chris@2 342 outputs.push_back(d);
Chris@2 343
Chris@0 344 return outputs;
Chris@0 345 }
Chris@0 346
Chris@0 347 bool
Chris@0 348 SimpleCepstrum::initialise(size_t channels, size_t stepSize, size_t blockSize)
Chris@0 349 {
Chris@0 350 if (channels < getMinChannelCount() ||
Chris@0 351 channels > getMaxChannelCount()) return false;
Chris@0 352
Chris@0 353 // std::cerr << "SimpleCepstrum::initialise: channels = " << channels
Chris@0 354 // << ", stepSize = " << stepSize << ", blockSize = " << blockSize
Chris@0 355 // << std::endl;
Chris@0 356
Chris@0 357 m_channels = channels;
Chris@0 358 m_stepSize = stepSize;
Chris@0 359 m_blockSize = blockSize;
Chris@0 360
Chris@5 361 m_binFrom = int(m_inputSampleRate / m_fmax);
Chris@5 362 m_binTo = int(m_inputSampleRate / m_fmin);
Chris@5 363
Chris@5 364 if (m_binTo >= m_blockSize / 2) {
Chris@5 365 m_binTo = m_blockSize / 2 - 1;
Chris@5 366 }
Chris@5 367
Chris@5 368 m_bins = (m_binTo - m_binFrom) + 1;
Chris@5 369
Chris@5 370 m_history = new double *[m_histlen];
Chris@5 371 for (int i = 0; i < m_histlen; ++i) {
Chris@5 372 m_history[i] = new double[m_bins];
Chris@5 373 }
Chris@5 374
Chris@5 375 reset();
Chris@5 376
Chris@0 377 return true;
Chris@0 378 }
Chris@0 379
Chris@0 380 void
Chris@0 381 SimpleCepstrum::reset()
Chris@0 382 {
Chris@5 383 for (int i = 0; i < m_histlen; ++i) {
Chris@5 384 for (int j = 0; j < m_bins; ++j) {
Chris@5 385 m_history[i][j] = 0.0;
Chris@5 386 }
Chris@5 387 }
Chris@5 388 }
Chris@5 389
Chris@5 390 void
Chris@5 391 SimpleCepstrum::filter(const double *cep, double *result)
Chris@5 392 {
Chris@5 393 int hix = m_histlen - 1; // current history index
Chris@5 394
Chris@5 395 // roll back the history
Chris@5 396 if (m_histlen > 1) {
Chris@5 397 double *oldest = m_history[0];
Chris@5 398 for (int i = 1; i < m_histlen; ++i) {
Chris@5 399 m_history[i-1] = m_history[i];
Chris@5 400 }
Chris@5 401 // and stick this back in the newest spot, to recycle
Chris@5 402 m_history[hix] = oldest;
Chris@5 403 }
Chris@5 404
Chris@5 405 for (int i = 0; i < m_bins; ++i) {
Chris@5 406 m_history[hix][i] = cep[i + m_binFrom];
Chris@5 407 }
Chris@5 408
Chris@5 409 for (int i = 0; i < m_bins; ++i) {
Chris@5 410 double mean = 0.0;
Chris@5 411 for (int j = 0; j < m_histlen; ++j) {
Chris@5 412 mean += m_history[j][i];
Chris@5 413 }
Chris@5 414 mean /= m_histlen;
Chris@5 415 result[i] = mean;
Chris@5 416 }
Chris@5 417 }
Chris@5 418
Chris@5 419 void
Chris@5 420 SimpleCepstrum::addStatisticalOutputs(FeatureSet &fs, const double *data)
Chris@5 421 {
Chris@5 422 int n = m_bins;
Chris@5 423
Chris@6 424 double maxval = 0.0;
Chris@5 425 int maxbin = 0;
Chris@5 426
Chris@5 427 for (int i = 0; i < n; ++i) {
Chris@5 428 if (data[i] > maxval) {
Chris@5 429 maxval = data[i];
Chris@6 430 maxbin = i;
Chris@5 431 }
Chris@5 432 }
Chris@5 433
Chris@5 434 Feature rf;
Chris@6 435 if (maxval > 0.0) {
Chris@6 436 rf.values.push_back(m_inputSampleRate / (maxbin + m_binFrom));
Chris@5 437 } else {
Chris@5 438 rf.values.push_back(0);
Chris@5 439 }
Chris@5 440 fs[m_pkOutput].push_back(rf);
Chris@5 441
Chris@6 442 double total = 0;
Chris@5 443 for (int i = 0; i < n; ++i) {
Chris@6 444 total += data[i];
Chris@5 445 }
Chris@5 446
Chris@6 447 Feature tot;
Chris@6 448 tot.values.push_back(total);
Chris@6 449 fs[m_totOutput].push_back(tot);
Chris@6 450
Chris@6 451 double mean = total / n;
Chris@6 452
Chris@6 453 double totsqr = 0;
Chris@5 454 for (int i = 0; i < n; ++i) {
Chris@6 455 totsqr += data[i] * data[i];
Chris@5 456 }
Chris@6 457 double rms = sqrt(totsqr / n);
Chris@5 458
Chris@5 459 double variance = 0;
Chris@5 460 for (int i = 0; i < n; ++i) {
Chris@5 461 double dev = fabs(data[i] - mean);
Chris@5 462 variance += dev * dev;
Chris@5 463 }
Chris@5 464 variance /= n;
Chris@5 465
Chris@6 466 double aroundPeak = 0.0;
Chris@6 467 double peakProportion = 0.0;
Chris@6 468 if (maxval > 0.0) {
Chris@6 469 aroundPeak += maxval * maxval;
Chris@6 470 int i = maxbin - 1;
Chris@6 471 while (i > 0 && data[i] <= data[i+1]) {
Chris@6 472 aroundPeak += data[i] * data[i];
Chris@6 473 --i;
Chris@6 474 }
Chris@6 475 i = maxbin + 1;
Chris@6 476 while (i < n && data[i] <= data[i-1]) {
Chris@6 477 aroundPeak += data[i] * data[i];
Chris@6 478 ++i;
Chris@6 479 }
Chris@6 480 }
Chris@6 481 peakProportion = sqrt(aroundPeak) / sqrt(totsqr);
Chris@6 482 Feature pp;
Chris@6 483 pp.values.push_back(peakProportion);
Chris@6 484 fs[m_ppOutput].push_back(pp);
Chris@6 485
Chris@5 486 Feature vf;
Chris@5 487 vf.values.push_back(variance);
Chris@5 488 fs[m_varOutput].push_back(vf);
Chris@5 489
Chris@5 490 Feature pf;
Chris@5 491 pf.values.push_back(maxval - mean);
Chris@5 492 fs[m_p2mOutput].push_back(pf);
Chris@5 493
Chris@5 494 Feature pr;
Chris@5 495 pr.values.push_back(maxval - rms);
Chris@5 496 fs[m_p2rOutput].push_back(pr);
Chris@5 497
Chris@5 498 Feature pv;
Chris@5 499 pv.values.push_back(maxval);
Chris@5 500 fs[m_pvOutput].push_back(pv);
Chris@5 501
Chris@5 502 Feature am;
Chris@5 503 for (int i = 0; i < n; ++i) {
Chris@5 504 if (data[i] < rms) am.values.push_back(0);
Chris@5 505 else am.values.push_back(data[i] - rms);
Chris@5 506 }
Chris@5 507 fs[m_amOutput].push_back(am);
Chris@5 508 }
Chris@5 509
Chris@5 510 void
Chris@5 511 SimpleCepstrum::addEnvelopeOutputs(FeatureSet &fs, const float *const *inputBuffers, const double *cep)
Chris@5 512 {
Chris@5 513 // Wipe the higher cepstral bins in order to calculate the
Chris@5 514 // envelope. This calculation uses the raw cepstrum, not the
Chris@5 515 // filtered values (because only values "in frequency range" are
Chris@5 516 // filtered).
Chris@5 517 int bs = m_blockSize;
Chris@5 518 int hs = m_blockSize/2 + 1;
Chris@5 519
Chris@5 520 double *ecep = new double[bs];
Chris@5 521 for (int i = 0; i < m_binFrom; ++i) {
Chris@5 522 ecep[i] = cep[i] / bs;
Chris@5 523 }
Chris@5 524 for (int i = m_binFrom; i < bs; ++i) {
Chris@5 525 ecep[i] = 0;
Chris@5 526 }
Chris@5 527 ecep[0] /= 2;
Chris@5 528 ecep[m_binFrom-1] /= 2;
Chris@5 529
Chris@5 530 double *env = new double[bs];
Chris@5 531 double *io = new double[bs];
Chris@5 532 fft(bs, false, ecep, 0, env, io);
Chris@5 533
Chris@5 534 for (int i = 0; i < hs; ++i) {
Chris@5 535 env[i] = exp(env[i]);
Chris@5 536 }
Chris@5 537 Feature envf;
Chris@5 538 for (int i = 0; i < hs; ++i) {
Chris@5 539 envf.values.push_back(env[i]);
Chris@5 540 }
Chris@5 541 fs[m_envOutput].push_back(envf);
Chris@5 542
Chris@5 543 Feature es;
Chris@5 544 for (int i = 0; i < hs; ++i) {
Chris@5 545 double re = inputBuffers[0][i*2 ] / env[i];
Chris@5 546 double im = inputBuffers[0][i*2+1] / env[i];
Chris@5 547 double mag = sqrt(re*re + im*im);
Chris@5 548 es.values.push_back(mag);
Chris@5 549 }
Chris@5 550 fs[m_esOutput].push_back(es);
Chris@5 551
Chris@5 552 delete[] env;
Chris@5 553 delete[] ecep;
Chris@5 554 delete[] io;
Chris@0 555 }
Chris@0 556
Chris@0 557 SimpleCepstrum::FeatureSet
Chris@0 558 SimpleCepstrum::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
Chris@0 559 {
Chris@1 560 FeatureSet fs;
Chris@1 561
Chris@0 562 int bs = m_blockSize;
Chris@0 563 int hs = m_blockSize/2 + 1;
Chris@0 564
Chris@5 565 double *rawcep = new double[bs];
Chris@3 566 double *io = new double[bs];
Chris@3 567
Chris@4 568 if (m_method != InverseComplex) {
Chris@3 569
Chris@4 570 double *logmag = new double[bs];
Chris@4 571
Chris@4 572 for (int i = 0; i < hs; ++i) {
Chris@3 573
Chris@4 574 double power =
Chris@4 575 inputBuffers[0][i*2 ] * inputBuffers[0][i*2 ] +
Chris@4 576 inputBuffers[0][i*2+1] * inputBuffers[0][i*2+1];
Chris@5 577 double mag = sqrt(power);
Chris@3 578
Chris@5 579 double lm = log(mag + 0.00000001);
Chris@4 580
Chris@4 581 switch (m_method) {
Chris@4 582 case InverseSymmetric:
Chris@4 583 logmag[i] = lm;
Chris@4 584 if (i > 0) logmag[bs - i] = lm;
Chris@4 585 break;
Chris@4 586 case InverseAsymmetric:
Chris@4 587 logmag[i] = lm;
Chris@4 588 if (i > 0) logmag[bs - i] = 0;
Chris@4 589 break;
Chris@4 590 default:
Chris@4 591 logmag[bs/2 + i - 1] = lm;
Chris@4 592 if (i < hs-1) {
Chris@4 593 logmag[bs/2 - i - 1] = lm;
Chris@4 594 }
Chris@4 595 break;
Chris@3 596 }
Chris@3 597 }
Chris@4 598
Chris@4 599 if (m_method == InverseSymmetric ||
Chris@4 600 m_method == InverseAsymmetric) {
Chris@4 601
Chris@5 602 fft(bs, true, logmag, 0, rawcep, io);
Chris@4 603
Chris@4 604 } else {
Chris@4 605
Chris@5 606 fft(bs, false, logmag, 0, rawcep, io);
Chris@4 607
Chris@4 608 if (m_method == ForwardDifference) {
Chris@4 609 for (int i = 0; i < hs; ++i) {
Chris@5 610 rawcep[i] = fabs(io[i]) - fabs(rawcep[i]);
Chris@4 611 }
Chris@4 612 } else {
Chris@4 613 for (int i = 0; i < hs; ++i) {
Chris@5 614 rawcep[i] = sqrt(rawcep[i]*rawcep[i] + io[i]*io[i]);
Chris@4 615 }
Chris@4 616 }
Chris@4 617 }
Chris@4 618
Chris@4 619 delete[] logmag;
Chris@4 620
Chris@4 621 } else { // InverseComplex
Chris@4 622
Chris@4 623 double *ri = new double[bs];
Chris@4 624 double *ii = new double[bs];
Chris@4 625
Chris@4 626 for (int i = 0; i < hs; ++i) {
Chris@4 627 double re = inputBuffers[0][i*2];
Chris@4 628 double im = inputBuffers[0][i*2+1];
Chris@4 629 std::complex<double> c(re, im);
Chris@4 630 std::complex<double> clog = std::log(c);
Chris@4 631 ri[i] = clog.real();
Chris@4 632 ii[i] = clog.imag();
Chris@4 633 if (i > 0) {
Chris@4 634 ri[bs - i] = ri[i];
Chris@4 635 ii[bs - i] = -ii[i];
Chris@4 636 }
Chris@4 637 }
Chris@4 638
Chris@5 639 fft(bs, true, ri, ii, rawcep, io);
Chris@4 640
Chris@4 641 delete[] ri;
Chris@4 642 delete[] ii;
Chris@3 643 }
Chris@0 644
Chris@0 645 if (m_clamp) {
Chris@0 646 for (int i = 0; i < bs; ++i) {
Chris@5 647 if (rawcep[i] < 0) rawcep[i] = 0;
Chris@0 648 }
Chris@0 649 }
Chris@0 650
Chris@5 651 delete[] io;
Chris@0 652
Chris@5 653 double *latest = new double[m_bins];
Chris@5 654 filter(rawcep, latest);
Chris@5 655
Chris@5 656 int n = m_bins;
Chris@0 657
Chris@0 658 Feature cf;
Chris@5 659 for (int i = 0; i < n; ++i) {
Chris@5 660 cf.values.push_back(latest[i]);
Chris@0 661 }
Chris@0 662 fs[m_cepOutput].push_back(cf);
Chris@0 663
Chris@5 664 addStatisticalOutputs(fs, latest);
Chris@0 665
Chris@5 666 addEnvelopeOutputs(fs, inputBuffers, rawcep);
Chris@0 667
Chris@5 668 delete[] latest;
Chris@0 669
Chris@0 670 return fs;
Chris@0 671 }
Chris@0 672
Chris@0 673 SimpleCepstrum::FeatureSet
Chris@0 674 SimpleCepstrum::getRemainingFeatures()
Chris@0 675 {
Chris@0 676 FeatureSet fs;
Chris@0 677 return fs;
Chris@0 678 }
Chris@0 679
Chris@0 680 void
Chris@0 681 SimpleCepstrum::fft(unsigned int n, bool inverse,
Chris@0 682 double *ri, double *ii, double *ro, double *io)
Chris@0 683 {
Chris@0 684 if (!ri || !ro || !io) return;
Chris@0 685
Chris@0 686 unsigned int bits;
Chris@0 687 unsigned int i, j, k, m;
Chris@0 688 unsigned int blockSize, blockEnd;
Chris@0 689
Chris@0 690 double tr, ti;
Chris@0 691
Chris@0 692 if (n < 2) return;
Chris@0 693 if (n & (n-1)) return;
Chris@0 694
Chris@0 695 double angle = 2.0 * M_PI;
Chris@0 696 if (inverse) angle = -angle;
Chris@0 697
Chris@0 698 for (i = 0; ; ++i) {
Chris@0 699 if (n & (1 << i)) {
Chris@0 700 bits = i;
Chris@0 701 break;
Chris@0 702 }
Chris@0 703 }
Chris@0 704
Chris@0 705 static unsigned int tableSize = 0;
Chris@0 706 static int *table = 0;
Chris@0 707
Chris@0 708 if (tableSize != n) {
Chris@0 709
Chris@0 710 delete[] table;
Chris@0 711
Chris@0 712 table = new int[n];
Chris@0 713
Chris@0 714 for (i = 0; i < n; ++i) {
Chris@0 715
Chris@0 716 m = i;
Chris@0 717
Chris@0 718 for (j = k = 0; j < bits; ++j) {
Chris@0 719 k = (k << 1) | (m & 1);
Chris@0 720 m >>= 1;
Chris@0 721 }
Chris@0 722
Chris@0 723 table[i] = k;
Chris@0 724 }
Chris@0 725
Chris@0 726 tableSize = n;
Chris@0 727 }
Chris@0 728
Chris@0 729 if (ii) {
Chris@0 730 for (i = 0; i < n; ++i) {
Chris@0 731 ro[table[i]] = ri[i];
Chris@0 732 io[table[i]] = ii[i];
Chris@0 733 }
Chris@0 734 } else {
Chris@0 735 for (i = 0; i < n; ++i) {
Chris@0 736 ro[table[i]] = ri[i];
Chris@0 737 io[table[i]] = 0.0;
Chris@0 738 }
Chris@0 739 }
Chris@0 740
Chris@0 741 blockEnd = 1;
Chris@0 742
Chris@0 743 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
Chris@0 744
Chris@0 745 double delta = angle / (double)blockSize;
Chris@0 746 double sm2 = -sin(-2 * delta);
Chris@0 747 double sm1 = -sin(-delta);
Chris@0 748 double cm2 = cos(-2 * delta);
Chris@0 749 double cm1 = cos(-delta);
Chris@0 750 double w = 2 * cm1;
Chris@0 751 double ar[3], ai[3];
Chris@0 752
Chris@0 753 for (i = 0; i < n; i += blockSize) {
Chris@0 754
Chris@0 755 ar[2] = cm2;
Chris@0 756 ar[1] = cm1;
Chris@0 757
Chris@0 758 ai[2] = sm2;
Chris@0 759 ai[1] = sm1;
Chris@0 760
Chris@0 761 for (j = i, m = 0; m < blockEnd; j++, m++) {
Chris@0 762
Chris@0 763 ar[0] = w * ar[1] - ar[2];
Chris@0 764 ar[2] = ar[1];
Chris@0 765 ar[1] = ar[0];
Chris@0 766
Chris@0 767 ai[0] = w * ai[1] - ai[2];
Chris@0 768 ai[2] = ai[1];
Chris@0 769 ai[1] = ai[0];
Chris@0 770
Chris@0 771 k = j + blockEnd;
Chris@0 772 tr = ar[0] * ro[k] - ai[0] * io[k];
Chris@0 773 ti = ar[0] * io[k] + ai[0] * ro[k];
Chris@0 774
Chris@0 775 ro[k] = ro[j] - tr;
Chris@0 776 io[k] = io[j] - ti;
Chris@0 777
Chris@0 778 ro[j] += tr;
Chris@0 779 io[j] += ti;
Chris@0 780 }
Chris@0 781 }
Chris@0 782
Chris@0 783 blockEnd = blockSize;
Chris@0 784 }
Chris@0 785 }
Chris@0 786
Chris@0 787