annotate SimpleCepstrum.cpp @ 23:1ae8041ae31b

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