annotate SimpleCepstrum.cpp @ 8:10dfd77951bf track

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