annotate PYIN.cpp @ 94:2687ba2cafae

some cleaning up
author matthiasm
date Wed, 07 Jan 2015 17:05:07 +0000
parents 854d9403c5be
children ca0ea8c9c712
rev   line source
matthiasm@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@9 2
matthiasm@0 3 /*
Chris@9 4 pYIN - A fundamental frequency estimator for monophonic audio
Chris@9 5 Centre for Digital Music, Queen Mary, University of London.
Chris@9 6
Chris@9 7 This program is free software; you can redistribute it and/or
Chris@9 8 modify it under the terms of the GNU General Public License as
Chris@9 9 published by the Free Software Foundation; either version 2 of the
Chris@9 10 License, or (at your option) any later version. See the file
Chris@9 11 COPYING included with this distribution for more information.
matthiasm@0 12 */
matthiasm@0 13
matthiasm@0 14 #include "PYIN.h"
matthiasm@0 15 #include "MonoNote.h"
matthiasm@0 16 #include "MonoPitch.h"
matthiasm@0 17
matthiasm@0 18 #include "vamp-sdk/FFT.h"
matthiasm@0 19
matthiasm@0 20 #include <vector>
matthiasm@0 21 #include <algorithm>
matthiasm@0 22
matthiasm@0 23 #include <cstdio>
matthiasm@0 24 #include <cmath>
matthiasm@0 25 #include <complex>
matthiasm@0 26
matthiasm@0 27 using std::string;
matthiasm@0 28 using std::vector;
matthiasm@0 29 using Vamp::RealTime;
matthiasm@0 30
matthiasm@0 31
matthiasm@0 32 PYIN::PYIN(float inputSampleRate) :
matthiasm@0 33 Plugin(inputSampleRate),
matthiasm@0 34 m_channels(0),
matthiasm@0 35 m_stepSize(256),
matthiasm@0 36 m_blockSize(2048),
matthiasm@0 37 m_fmin(40),
matthiasm@0 38 m_fmax(700),
matthiasm@0 39 m_yin(2048, inputSampleRate, 0.0),
matthiasm@0 40 m_oF0Candidates(0),
matthiasm@0 41 m_oF0Probs(0),
matthiasm@0 42 m_oVoicedProb(0),
matthiasm@0 43 m_oCandidateSalience(0),
matthiasm@0 44 m_oSmoothedPitchTrack(0),
matthiasm@0 45 m_oNotes(0),
matthiasm@0 46 m_threshDistr(2.0f),
matthiasm@6 47 m_outputUnvoiced(0.0f),
matthiasm@0 48 m_pitchProb(0),
matthiasm@91 49 m_timestamp(0),
matthiasm@91 50 m_level(0)
matthiasm@0 51 {
matthiasm@0 52 }
matthiasm@0 53
matthiasm@0 54 PYIN::~PYIN()
matthiasm@0 55 {
matthiasm@0 56 }
matthiasm@0 57
matthiasm@0 58 string
matthiasm@0 59 PYIN::getIdentifier() const
matthiasm@0 60 {
matthiasm@1 61 return "pyin";
matthiasm@0 62 }
matthiasm@0 63
matthiasm@0 64 string
matthiasm@0 65 PYIN::getName() const
matthiasm@0 66 {
matthiasm@1 67 return "pYin";
matthiasm@0 68 }
matthiasm@0 69
matthiasm@0 70 string
matthiasm@0 71 PYIN::getDescription() const
matthiasm@0 72 {
matthiasm@0 73 return "Monophonic pitch and note tracking based on a probabilistic Yin extension.";
matthiasm@0 74 }
matthiasm@0 75
matthiasm@0 76 string
matthiasm@0 77 PYIN::getMaker() const
matthiasm@0 78 {
matthiasm@0 79 return "Matthias Mauch";
matthiasm@0 80 }
matthiasm@0 81
matthiasm@0 82 int
matthiasm@0 83 PYIN::getPluginVersion() const
matthiasm@0 84 {
matthiasm@0 85 // Increment this each time you release a version that behaves
matthiasm@0 86 // differently from the previous one
matthiasm@0 87 return 1;
matthiasm@0 88 }
matthiasm@0 89
matthiasm@0 90 string
matthiasm@0 91 PYIN::getCopyright() const
matthiasm@0 92 {
matthiasm@0 93 return "GPL";
matthiasm@0 94 }
matthiasm@0 95
matthiasm@0 96 PYIN::InputDomain
matthiasm@0 97 PYIN::getInputDomain() const
matthiasm@0 98 {
matthiasm@0 99 return TimeDomain;
matthiasm@0 100 }
matthiasm@0 101
matthiasm@0 102 size_t
matthiasm@0 103 PYIN::getPreferredBlockSize() const
matthiasm@0 104 {
matthiasm@0 105 return 2048;
matthiasm@0 106 }
matthiasm@0 107
matthiasm@0 108 size_t
matthiasm@0 109 PYIN::getPreferredStepSize() const
matthiasm@0 110 {
matthiasm@0 111 return 256;
matthiasm@0 112 }
matthiasm@0 113
matthiasm@0 114 size_t
matthiasm@0 115 PYIN::getMinChannelCount() const
matthiasm@0 116 {
matthiasm@0 117 return 1;
matthiasm@0 118 }
matthiasm@0 119
matthiasm@0 120 size_t
matthiasm@0 121 PYIN::getMaxChannelCount() const
matthiasm@0 122 {
matthiasm@0 123 return 1;
matthiasm@0 124 }
matthiasm@0 125
matthiasm@0 126 PYIN::ParameterList
matthiasm@0 127 PYIN::getParameterDescriptors() const
matthiasm@0 128 {
matthiasm@0 129 ParameterList list;
matthiasm@0 130
matthiasm@0 131 ParameterDescriptor d;
matthiasm@0 132
matthiasm@0 133 d.identifier = "threshdistr";
matthiasm@0 134 d.name = "Yin threshold distribution";
matthiasm@0 135 d.description = ".";
matthiasm@0 136 d.unit = "";
matthiasm@0 137 d.minValue = 0.0f;
matthiasm@0 138 d.maxValue = 7.0f;
matthiasm@0 139 d.defaultValue = 2.0f;
matthiasm@0 140 d.isQuantized = true;
matthiasm@0 141 d.quantizeStep = 1.0f;
matthiasm@0 142 d.valueNames.push_back("Uniform");
matthiasm@0 143 d.valueNames.push_back("Beta (mean 0.10)");
matthiasm@0 144 d.valueNames.push_back("Beta (mean 0.15)");
matthiasm@0 145 d.valueNames.push_back("Beta (mean 0.20)");
matthiasm@0 146 d.valueNames.push_back("Beta (mean 0.30)");
matthiasm@0 147 d.valueNames.push_back("Single Value 0.10");
matthiasm@0 148 d.valueNames.push_back("Single Value 0.15");
matthiasm@0 149 d.valueNames.push_back("Single Value 0.20");
matthiasm@0 150 list.push_back(d);
matthiasm@0 151
matthiasm@0 152 d.identifier = "outputunvoiced";
matthiasm@0 153 d.valueNames.clear();
matthiasm@0 154 d.name = "Output estimates classified as unvoiced?";
matthiasm@0 155 d.description = ".";
matthiasm@0 156 d.unit = "";
matthiasm@0 157 d.minValue = 0.0f;
matthiasm@0 158 d.maxValue = 2.0f;
matthiasm@6 159 d.defaultValue = 0.0f;
matthiasm@0 160 d.isQuantized = true;
matthiasm@0 161 d.quantizeStep = 1.0f;
matthiasm@0 162 d.valueNames.push_back("No");
matthiasm@0 163 d.valueNames.push_back("Yes");
matthiasm@0 164 d.valueNames.push_back("Yes, as negative frequencies");
matthiasm@0 165 list.push_back(d);
matthiasm@0 166
matthiasm@0 167 return list;
matthiasm@0 168 }
matthiasm@0 169
matthiasm@0 170 float
matthiasm@0 171 PYIN::getParameter(string identifier) const
matthiasm@0 172 {
matthiasm@0 173 if (identifier == "threshdistr") {
matthiasm@0 174 return m_threshDistr;
matthiasm@0 175 }
matthiasm@0 176 if (identifier == "outputunvoiced") {
matthiasm@0 177 return m_outputUnvoiced;
matthiasm@0 178 }
matthiasm@0 179 return 0.f;
matthiasm@0 180 }
matthiasm@0 181
matthiasm@0 182 void
matthiasm@0 183 PYIN::setParameter(string identifier, float value)
matthiasm@0 184 {
matthiasm@0 185 if (identifier == "threshdistr")
matthiasm@0 186 {
matthiasm@0 187 m_threshDistr = value;
matthiasm@0 188 }
matthiasm@0 189 if (identifier == "outputunvoiced")
matthiasm@0 190 {
matthiasm@0 191 m_outputUnvoiced = value;
matthiasm@0 192 }
matthiasm@0 193
matthiasm@0 194 }
matthiasm@0 195
matthiasm@0 196 PYIN::ProgramList
matthiasm@0 197 PYIN::getPrograms() const
matthiasm@0 198 {
matthiasm@0 199 ProgramList list;
matthiasm@0 200 return list;
matthiasm@0 201 }
matthiasm@0 202
matthiasm@0 203 string
matthiasm@0 204 PYIN::getCurrentProgram() const
matthiasm@0 205 {
matthiasm@0 206 return ""; // no programs
matthiasm@0 207 }
matthiasm@0 208
matthiasm@0 209 void
matthiasm@0 210 PYIN::selectProgram(string name)
matthiasm@0 211 {
matthiasm@0 212 }
matthiasm@0 213
matthiasm@0 214 PYIN::OutputList
matthiasm@0 215 PYIN::getOutputDescriptors() const
matthiasm@0 216 {
matthiasm@0 217 OutputList outputs;
matthiasm@0 218
matthiasm@0 219 OutputDescriptor d;
matthiasm@0 220
matthiasm@0 221 int outputNumber = 0;
matthiasm@0 222
matthiasm@0 223 d.identifier = "f0candidates";
matthiasm@0 224 d.name = "F0 Candidates";
matthiasm@0 225 d.description = "Estimated fundamental frequency candidates.";
matthiasm@0 226 d.unit = "Hz";
matthiasm@0 227 d.hasFixedBinCount = false;
matthiasm@0 228 // d.binCount = 1;
matthiasm@0 229 d.hasKnownExtents = true;
matthiasm@0 230 d.minValue = m_fmin;
matthiasm@0 231 d.maxValue = 500;
matthiasm@0 232 d.isQuantized = false;
matthiasm@0 233 d.sampleType = OutputDescriptor::FixedSampleRate;
matthiasm@0 234 d.sampleRate = (m_inputSampleRate / m_stepSize);
matthiasm@0 235 d.hasDuration = false;
matthiasm@0 236 outputs.push_back(d);
matthiasm@0 237 m_oF0Candidates = outputNumber++;
matthiasm@0 238
matthiasm@0 239 d.identifier = "f0probs";
matthiasm@0 240 d.name = "Candidate Probabilities";
matthiasm@0 241 d.description = "Probabilities of estimated fundamental frequency candidates.";
matthiasm@0 242 d.unit = "";
matthiasm@0 243 d.hasFixedBinCount = false;
matthiasm@0 244 // d.binCount = 1;
matthiasm@0 245 d.hasKnownExtents = true;
matthiasm@0 246 d.minValue = 0;
matthiasm@0 247 d.maxValue = 1;
matthiasm@0 248 d.isQuantized = false;
matthiasm@0 249 d.sampleType = OutputDescriptor::FixedSampleRate;
matthiasm@0 250 d.sampleRate = (m_inputSampleRate / m_stepSize);
matthiasm@0 251 d.hasDuration = false;
matthiasm@0 252 outputs.push_back(d);
matthiasm@0 253 m_oF0Probs = outputNumber++;
matthiasm@0 254
matthiasm@0 255 d.identifier = "voicedprob";
matthiasm@0 256 d.name = "Voiced Probability";
matthiasm@0 257 d.description = "Probability that the signal is voiced according to Probabilistic Yin.";
matthiasm@0 258 d.unit = "";
matthiasm@0 259 d.hasFixedBinCount = true;
matthiasm@0 260 d.binCount = 1;
matthiasm@0 261 d.hasKnownExtents = true;
matthiasm@0 262 d.minValue = 0;
matthiasm@0 263 d.maxValue = 1;
matthiasm@0 264 d.isQuantized = false;
matthiasm@0 265 d.sampleType = OutputDescriptor::FixedSampleRate;
matthiasm@0 266 d.sampleRate = (m_inputSampleRate / m_stepSize);
matthiasm@0 267 d.hasDuration = false;
matthiasm@0 268 outputs.push_back(d);
matthiasm@0 269 m_oVoicedProb = outputNumber++;
matthiasm@0 270
matthiasm@0 271 d.identifier = "candidatesalience";
matthiasm@0 272 d.name = "Candidate Salience";
matthiasm@0 273 d.description = "Candidate Salience";
matthiasm@0 274 d.hasFixedBinCount = true;
matthiasm@0 275 d.binCount = m_blockSize / 2;
matthiasm@0 276 d.hasKnownExtents = true;
matthiasm@0 277 d.minValue = 0;
matthiasm@0 278 d.maxValue = 1;
matthiasm@0 279 d.isQuantized = false;
matthiasm@0 280 d.sampleType = OutputDescriptor::FixedSampleRate;
matthiasm@0 281 d.sampleRate = (m_inputSampleRate / m_stepSize);
matthiasm@0 282 d.hasDuration = false;
matthiasm@0 283 outputs.push_back(d);
matthiasm@0 284 m_oCandidateSalience = outputNumber++;
matthiasm@0 285
matthiasm@0 286 d.identifier = "smoothedpitchtrack";
matthiasm@0 287 d.name = "Smoothed Pitch Track";
matthiasm@0 288 d.description = ".";
matthiasm@0 289 d.unit = "Hz";
matthiasm@0 290 d.hasFixedBinCount = true;
matthiasm@0 291 d.binCount = 1;
matthiasm@0 292 d.hasKnownExtents = false;
matthiasm@0 293 // d.minValue = 0;
matthiasm@0 294 // d.maxValue = 1;
matthiasm@0 295 d.isQuantized = false;
matthiasm@0 296 d.sampleType = OutputDescriptor::FixedSampleRate;
matthiasm@0 297 d.sampleRate = (m_inputSampleRate / m_stepSize);
matthiasm@0 298 d.hasDuration = false;
matthiasm@0 299 outputs.push_back(d);
matthiasm@0 300 m_oSmoothedPitchTrack = outputNumber++;
matthiasm@0 301
matthiasm@0 302 d.identifier = "notes";
matthiasm@0 303 d.name = "Notes";
matthiasm@0 304 d.description = "Derived fixed-pitch note frequencies";
matthiasm@0 305 // d.unit = "MIDI unit";
matthiasm@0 306 d.unit = "Hz";
matthiasm@0 307 d.hasFixedBinCount = true;
matthiasm@0 308 d.binCount = 1;
matthiasm@0 309 d.hasKnownExtents = false;
matthiasm@0 310 d.isQuantized = false;
matthiasm@0 311 d.sampleType = OutputDescriptor::VariableSampleRate;
matthiasm@0 312 d.sampleRate = (m_inputSampleRate / m_stepSize);
matthiasm@0 313 d.hasDuration = true;
matthiasm@0 314 outputs.push_back(d);
matthiasm@0 315 m_oNotes = outputNumber++;
matthiasm@0 316
matthiasm@0 317 return outputs;
matthiasm@0 318 }
matthiasm@0 319
matthiasm@0 320 bool
matthiasm@0 321 PYIN::initialise(size_t channels, size_t stepSize, size_t blockSize)
matthiasm@0 322 {
matthiasm@0 323 if (channels < getMinChannelCount() ||
matthiasm@0 324 channels > getMaxChannelCount()) return false;
matthiasm@0 325
Chris@9 326 /*
matthiasm@0 327 std::cerr << "PYIN::initialise: channels = " << channels
matthiasm@0 328 << ", stepSize = " << stepSize << ", blockSize = " << blockSize
matthiasm@0 329 << std::endl;
Chris@9 330 */
matthiasm@0 331 m_channels = channels;
matthiasm@0 332 m_stepSize = stepSize;
matthiasm@0 333 m_blockSize = blockSize;
matthiasm@0 334
matthiasm@0 335 reset();
matthiasm@0 336
matthiasm@0 337 return true;
matthiasm@0 338 }
matthiasm@0 339
matthiasm@0 340 void
matthiasm@0 341 PYIN::reset()
matthiasm@0 342 {
matthiasm@0 343 m_yin.setThresholdDistr(m_threshDistr);
matthiasm@0 344 m_yin.setFrameSize(m_blockSize);
matthiasm@0 345
matthiasm@0 346 m_pitchProb.clear();
matthiasm@0 347 m_timestamp.clear();
matthiasm@91 348 m_level.clear();
Chris@9 349 /*
matthiasm@0 350 std::cerr << "PYIN::reset"
matthiasm@0 351 << ", blockSize = " << m_blockSize
matthiasm@0 352 << std::endl;
Chris@9 353 */
matthiasm@0 354 }
matthiasm@0 355
matthiasm@0 356 PYIN::FeatureSet
matthiasm@0 357 PYIN::process(const float *const *inputBuffers, RealTime timestamp)
matthiasm@0 358 {
matthiasm@0 359 timestamp = timestamp + Vamp::RealTime::frame2RealTime(m_blockSize/4, lrintf(m_inputSampleRate));
matthiasm@0 360 FeatureSet fs;
matthiasm@0 361
matthiasm@0 362 double *dInputBuffers = new double[m_blockSize];
matthiasm@0 363 for (size_t i = 0; i < m_blockSize; ++i) dInputBuffers[i] = inputBuffers[0][i];
matthiasm@0 364
matthiasm@0 365 Yin::YinOutput yo = m_yin.processProbabilisticYin(dInputBuffers);
matthiasm@27 366 delete [] dInputBuffers;
matthiasm@27 367
matthiasm@91 368 m_level.push_back(yo.rms);
matthiasm@91 369
matthiasm@27 370 // First, get the things out of the way that we don't want to output
matthiasm@27 371 // immediately, but instead save for later.
matthiasm@27 372 vector<pair<double, double> > tempPitchProb;
matthiasm@27 373 for (size_t iCandidate = 0; iCandidate < yo.freqProb.size(); ++iCandidate)
matthiasm@27 374 {
matthiasm@27 375 double tempPitch = 12 * std::log(yo.freqProb[iCandidate].first/440)/std::log(2.) + 69;
matthiasm@27 376 tempPitchProb.push_back(pair<double, double>
matthiasm@27 377 (tempPitch, yo.freqProb[iCandidate].second));
matthiasm@27 378 }
matthiasm@27 379 m_pitchProb.push_back(tempPitchProb);
matthiasm@27 380 m_timestamp.push_back(timestamp);
matthiasm@27 381
matthiasm@27 382 // F0 CANDIDATES
matthiasm@0 383 Feature f;
matthiasm@0 384 f.hasTimestamp = true;
matthiasm@0 385 f.timestamp = timestamp;
matthiasm@0 386 for (size_t i = 0; i < yo.freqProb.size(); ++i)
matthiasm@0 387 {
matthiasm@0 388 f.values.push_back(yo.freqProb[i].first);
matthiasm@0 389 }
matthiasm@0 390 fs[m_oF0Candidates].push_back(f);
matthiasm@0 391
matthiasm@27 392 // VOICEDPROB
matthiasm@0 393 f.values.clear();
matthiasm@0 394 float voicedProb = 0;
matthiasm@0 395 for (size_t i = 0; i < yo.freqProb.size(); ++i)
matthiasm@0 396 {
matthiasm@0 397 f.values.push_back(yo.freqProb[i].second);
matthiasm@0 398 voicedProb += yo.freqProb[i].second;
matthiasm@0 399 }
matthiasm@0 400 fs[m_oF0Probs].push_back(f);
matthiasm@0 401
matthiasm@0 402 f.values.clear();
matthiasm@0 403 f.values.push_back(voicedProb);
matthiasm@0 404 fs[m_oVoicedProb].push_back(f);
matthiasm@0 405
matthiasm@27 406 // SALIENCE -- maybe this should eventually disappear
matthiasm@0 407 f.values.clear();
matthiasm@0 408 float salienceSum = 0;
matthiasm@0 409 for (size_t iBin = 0; iBin < yo.salience.size(); ++iBin)
matthiasm@0 410 {
matthiasm@0 411 f.values.push_back(yo.salience[iBin]);
matthiasm@0 412 salienceSum += yo.salience[iBin];
matthiasm@0 413 }
matthiasm@0 414 fs[m_oCandidateSalience].push_back(f);
matthiasm@0 415
matthiasm@0 416 return fs;
matthiasm@0 417 }
matthiasm@0 418
matthiasm@0 419 PYIN::FeatureSet
matthiasm@0 420 PYIN::getRemainingFeatures()
matthiasm@0 421 {
matthiasm@0 422 FeatureSet fs;
matthiasm@0 423 Feature f;
matthiasm@0 424 f.hasTimestamp = true;
matthiasm@0 425 f.hasDuration = false;
matthiasm@0 426
Chris@4 427 if (m_pitchProb.empty()) {
Chris@4 428 return fs;
Chris@4 429 }
Chris@4 430
matthiasm@0 431 // MONO-PITCH STUFF
matthiasm@0 432 MonoPitch mp;
matthiasm@0 433 vector<float> mpOut = mp.process(m_pitchProb);
matthiasm@0 434 for (size_t iFrame = 0; iFrame < mpOut.size(); ++iFrame)
matthiasm@0 435 {
matthiasm@0 436 if (mpOut[iFrame] < 0 && (m_outputUnvoiced==0)) continue;
matthiasm@0 437 f.timestamp = m_timestamp[iFrame];
matthiasm@0 438 f.values.clear();
matthiasm@0 439 if (m_outputUnvoiced == 1)
matthiasm@0 440 {
matthiasm@26 441 f.values.push_back(fabs(mpOut[iFrame]));
matthiasm@0 442 } else {
matthiasm@0 443 f.values.push_back(mpOut[iFrame]);
matthiasm@0 444 }
matthiasm@0 445
matthiasm@0 446 fs[m_oSmoothedPitchTrack].push_back(f);
matthiasm@0 447 }
matthiasm@0 448
matthiasm@1 449 // MONO-NOTE STUFF
matthiasm@1 450 MonoNote mn;
matthiasm@1 451 std::vector<std::vector<std::pair<double, double> > > smoothedPitch;
matthiasm@1 452 for (size_t iFrame = 0; iFrame < mpOut.size(); ++iFrame) {
matthiasm@1 453 std::vector<std::pair<double, double> > temp;
matthiasm@1 454 if (mpOut[iFrame] > 0)
matthiasm@1 455 {
matthiasm@1 456 double tempPitch = 12 * std::log(mpOut[iFrame]/440)/std::log(2.) + 69;
matthiasm@1 457 temp.push_back(std::pair<double,double>(tempPitch, .9));
matthiasm@1 458 }
matthiasm@1 459 smoothedPitch.push_back(temp);
matthiasm@1 460 }
matthiasm@0 461 // vector<MonoNote::FrameOutput> mnOut = mn.process(m_pitchProb);
matthiasm@1 462 vector<MonoNote::FrameOutput> mnOut = mn.process(smoothedPitch);
matthiasm@1 463
matthiasm@6 464 // turning feature into a note feature
matthiasm@1 465 f.hasTimestamp = true;
matthiasm@1 466 f.hasDuration = true;
matthiasm@1 467 f.values.clear();
matthiasm@6 468
matthiasm@6 469 int onsetFrame = 0;
matthiasm@6 470 bool isVoiced = 0;
matthiasm@6 471 bool oldIsVoiced = 0;
matthiasm@6 472 size_t nFrame = m_pitchProb.size();
matthiasm@1 473
matthiasm@6 474 std::vector<float> notePitchTrack; // collects pitches for one note at a time
matthiasm@6 475 for (size_t iFrame = 0; iFrame < nFrame; ++iFrame)
matthiasm@1 476 {
matthiasm@91 477 isVoiced = mnOut[iFrame].noteState < 3
matthiasm@91 478 && smoothedPitch[iFrame].size() > 0
matthiasm@94 479 && (iFrame >= nFrame-2
matthiasm@94 480 || ((m_level[iFrame]/m_level[iFrame+2]) > 0.8));
matthiasm@91 481 // std::cerr << m_level[iFrame]/m_level[iFrame-1] << std::endl;
matthiasm@6 482 if (isVoiced && iFrame != nFrame-1)
matthiasm@1 483 {
matthiasm@6 484 if (oldIsVoiced == 0) // beginning of a note
matthiasm@1 485 {
matthiasm@6 486 onsetFrame = iFrame;
matthiasm@6 487 notePitchTrack.clear();
matthiasm@1 488 }
matthiasm@6 489 float pitch = smoothedPitch[iFrame][0].first;
matthiasm@6 490 notePitchTrack.push_back(pitch); // add to the note's pitch track
matthiasm@6 491 } else { // not currently voiced
matthiasm@91 492 if (oldIsVoiced == 1 && notePitchTrack.size() > 14) // end of note
matthiasm@6 493 {
matthiasm@1 494 std::sort(notePitchTrack.begin(), notePitchTrack.end());
matthiasm@6 495 float medianPitch = notePitchTrack[notePitchTrack.size()/2];
matthiasm@6 496 float medianFreq = std::pow(2,(medianPitch - 69) / 12) * 440;
matthiasm@6 497 f.values.clear();
matthiasm@6 498 f.values.push_back(medianFreq);
matthiasm@6 499 f.timestamp = m_timestamp[onsetFrame];
matthiasm@6 500 f.duration = m_timestamp[iFrame] - m_timestamp[onsetFrame];
matthiasm@5 501 fs[m_oNotes].push_back(f);
matthiasm@1 502 }
matthiasm@1 503 }
matthiasm@6 504 oldIsVoiced = isVoiced;
matthiasm@1 505 }
matthiasm@0 506 return fs;
matthiasm@0 507 }