annotate LocalCandidatePYIN.cpp @ 37:34820224da74 tony

changed Vamp output format for candidates
author matthiasm
date Tue, 28 Jan 2014 10:21:34 +0000
parents 5d43ce0eeeb8
children ae21806fe84b
rev   line source
matthiasm@32 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
matthiasm@32 2
matthiasm@32 3 /*
matthiasm@32 4 pYIN - A fundamental frequency estimator for monophonic audio
matthiasm@32 5 Centre for Digital Music, Queen Mary, University of London.
matthiasm@32 6
matthiasm@32 7 This program is free software; you can redistribute it and/or
matthiasm@32 8 modify it under the terms of the GNU General Public License as
matthiasm@32 9 published by the Free Software Foundation; either version 2 of the
matthiasm@32 10 License, or (at your option) any later version. See the file
matthiasm@32 11 COLocalCandidatePYING included with this distribution for more information.
matthiasm@32 12 */
matthiasm@32 13
matthiasm@32 14 #include "LocalCandidatePYIN.h"
matthiasm@32 15 #include "MonoPitch.h"
matthiasm@32 16 #include "YinUtil.h"
matthiasm@32 17
matthiasm@32 18 #include "vamp-sdk/FFT.h"
matthiasm@32 19
matthiasm@32 20 #include <vector>
matthiasm@32 21 #include <algorithm>
matthiasm@32 22
matthiasm@32 23 #include <cstdio>
matthiasm@32 24 #include <sstream>
matthiasm@32 25 // #include <iostream>
matthiasm@32 26 #include <cmath>
matthiasm@32 27 #include <complex>
matthiasm@32 28
matthiasm@32 29 using std::string;
matthiasm@32 30 using std::vector;
matthiasm@32 31 using Vamp::RealTime;
matthiasm@32 32
matthiasm@32 33
matthiasm@32 34 LocalCandidatePYIN::LocalCandidatePYIN(float inputSampleRate) :
matthiasm@32 35 Plugin(inputSampleRate),
matthiasm@32 36 m_channels(0),
matthiasm@32 37 m_stepSize(256),
matthiasm@32 38 m_blockSize(2048),
matthiasm@32 39 m_fmin(40),
matthiasm@32 40 m_fmax(700),
matthiasm@32 41 m_yin(2048, inputSampleRate, 0.0),
matthiasm@32 42 m_oPitchTrackCandidates(0),
matthiasm@32 43 m_threshDistr(2.0f),
matthiasm@32 44 m_outputUnvoiced(0.0f),
matthiasm@32 45 m_pitchProb(0),
matthiasm@32 46 m_timestamp(0),
matthiasm@32 47 m_nCandidate(20)
matthiasm@32 48 {
matthiasm@32 49 }
matthiasm@32 50
matthiasm@32 51 LocalCandidatePYIN::~LocalCandidatePYIN()
matthiasm@32 52 {
matthiasm@32 53 }
matthiasm@32 54
matthiasm@32 55 string
matthiasm@32 56 LocalCandidatePYIN::getIdentifier() const
matthiasm@32 57 {
matthiasm@32 58 return "localcandidatepyin";
matthiasm@32 59 }
matthiasm@32 60
matthiasm@32 61 string
matthiasm@32 62 LocalCandidatePYIN::getName() const
matthiasm@32 63 {
matthiasm@32 64 return "Local Candidate PYIN";
matthiasm@32 65 }
matthiasm@32 66
matthiasm@32 67 string
matthiasm@32 68 LocalCandidatePYIN::getDescription() const
matthiasm@32 69 {
matthiasm@32 70 return "Monophonic pitch and note tracking based on a probabilistic Yin extension.";
matthiasm@32 71 }
matthiasm@32 72
matthiasm@32 73 string
matthiasm@32 74 LocalCandidatePYIN::getMaker() const
matthiasm@32 75 {
matthiasm@32 76 return "Matthias Mauch";
matthiasm@32 77 }
matthiasm@32 78
matthiasm@32 79 int
matthiasm@32 80 LocalCandidatePYIN::getPluginVersion() const
matthiasm@32 81 {
matthiasm@32 82 // Increment this each time you release a version that behaves
matthiasm@32 83 // differently from the previous one
matthiasm@32 84 return 1;
matthiasm@32 85 }
matthiasm@32 86
matthiasm@32 87 string
matthiasm@32 88 LocalCandidatePYIN::getCopyright() const
matthiasm@32 89 {
matthiasm@32 90 return "GPL";
matthiasm@32 91 }
matthiasm@32 92
matthiasm@32 93 LocalCandidatePYIN::InputDomain
matthiasm@32 94 LocalCandidatePYIN::getInputDomain() const
matthiasm@32 95 {
matthiasm@32 96 return TimeDomain;
matthiasm@32 97 }
matthiasm@32 98
matthiasm@32 99 size_t
matthiasm@32 100 LocalCandidatePYIN::getPreferredBlockSize() const
matthiasm@32 101 {
matthiasm@32 102 return 2048;
matthiasm@32 103 }
matthiasm@32 104
matthiasm@32 105 size_t
matthiasm@32 106 LocalCandidatePYIN::getPreferredStepSize() const
matthiasm@32 107 {
matthiasm@32 108 return 256;
matthiasm@32 109 }
matthiasm@32 110
matthiasm@32 111 size_t
matthiasm@32 112 LocalCandidatePYIN::getMinChannelCount() const
matthiasm@32 113 {
matthiasm@32 114 return 1;
matthiasm@32 115 }
matthiasm@32 116
matthiasm@32 117 size_t
matthiasm@32 118 LocalCandidatePYIN::getMaxChannelCount() const
matthiasm@32 119 {
matthiasm@32 120 return 1;
matthiasm@32 121 }
matthiasm@32 122
matthiasm@32 123 LocalCandidatePYIN::ParameterList
matthiasm@32 124 LocalCandidatePYIN::getParameterDescriptors() const
matthiasm@32 125 {
matthiasm@32 126 ParameterList list;
matthiasm@32 127
matthiasm@32 128 ParameterDescriptor d;
matthiasm@32 129
matthiasm@32 130 d.identifier = "threshdistr";
matthiasm@32 131 d.name = "Yin threshold distribution";
matthiasm@32 132 d.description = ".";
matthiasm@32 133 d.unit = "";
matthiasm@32 134 d.minValue = 0.0f;
matthiasm@32 135 d.maxValue = 7.0f;
matthiasm@32 136 d.defaultValue = 2.0f;
matthiasm@32 137 d.isQuantized = true;
matthiasm@32 138 d.quantizeStep = 1.0f;
matthiasm@32 139 d.valueNames.push_back("Uniform");
matthiasm@32 140 d.valueNames.push_back("Beta (mean 0.10)");
matthiasm@32 141 d.valueNames.push_back("Beta (mean 0.15)");
matthiasm@32 142 d.valueNames.push_back("Beta (mean 0.20)");
matthiasm@32 143 d.valueNames.push_back("Beta (mean 0.30)");
matthiasm@32 144 d.valueNames.push_back("Single Value 0.10");
matthiasm@32 145 d.valueNames.push_back("Single Value 0.15");
matthiasm@32 146 d.valueNames.push_back("Single Value 0.20");
matthiasm@32 147 list.push_back(d);
matthiasm@32 148
matthiasm@32 149 d.identifier = "outputunvoiced";
matthiasm@32 150 d.valueNames.clear();
matthiasm@32 151 d.name = "Output estimates classified as unvoiced?";
matthiasm@32 152 d.description = ".";
matthiasm@32 153 d.unit = "";
matthiasm@32 154 d.minValue = 0.0f;
matthiasm@32 155 d.maxValue = 2.0f;
matthiasm@32 156 d.defaultValue = 0.0f;
matthiasm@32 157 d.isQuantized = true;
matthiasm@32 158 d.quantizeStep = 1.0f;
matthiasm@32 159 d.valueNames.push_back("No");
matthiasm@32 160 d.valueNames.push_back("Yes");
matthiasm@32 161 d.valueNames.push_back("Yes, as negative frequencies");
matthiasm@32 162 list.push_back(d);
matthiasm@32 163
matthiasm@32 164 return list;
matthiasm@32 165 }
matthiasm@32 166
matthiasm@32 167 float
matthiasm@32 168 LocalCandidatePYIN::getParameter(string identifier) const
matthiasm@32 169 {
matthiasm@32 170 if (identifier == "threshdistr") {
matthiasm@32 171 return m_threshDistr;
matthiasm@32 172 }
matthiasm@32 173 if (identifier == "outputunvoiced") {
matthiasm@32 174 return m_outputUnvoiced;
matthiasm@32 175 }
matthiasm@32 176 return 0.f;
matthiasm@32 177 }
matthiasm@32 178
matthiasm@32 179 void
matthiasm@32 180 LocalCandidatePYIN::setParameter(string identifier, float value)
matthiasm@32 181 {
matthiasm@32 182 if (identifier == "threshdistr")
matthiasm@32 183 {
matthiasm@32 184 m_threshDistr = value;
matthiasm@32 185 }
matthiasm@32 186 if (identifier == "outputunvoiced")
matthiasm@32 187 {
matthiasm@32 188 m_outputUnvoiced = value;
matthiasm@32 189 }
matthiasm@32 190
matthiasm@32 191 }
matthiasm@32 192
matthiasm@32 193 LocalCandidatePYIN::ProgramList
matthiasm@32 194 LocalCandidatePYIN::getPrograms() const
matthiasm@32 195 {
matthiasm@32 196 ProgramList list;
matthiasm@32 197 return list;
matthiasm@32 198 }
matthiasm@32 199
matthiasm@32 200 string
matthiasm@32 201 LocalCandidatePYIN::getCurrentProgram() const
matthiasm@32 202 {
matthiasm@32 203 return ""; // no programs
matthiasm@32 204 }
matthiasm@32 205
matthiasm@32 206 void
matthiasm@32 207 LocalCandidatePYIN::selectProgram(string name)
matthiasm@32 208 {
matthiasm@32 209 }
matthiasm@32 210
matthiasm@32 211 LocalCandidatePYIN::OutputList
matthiasm@32 212 LocalCandidatePYIN::getOutputDescriptors() const
matthiasm@32 213 {
matthiasm@32 214 OutputList outputs;
matthiasm@32 215
matthiasm@32 216 OutputDescriptor d;
matthiasm@32 217
matthiasm@32 218 int outputNumber = 0;
matthiasm@32 219
matthiasm@32 220 d.identifier = "pitchtrackcandidates";
matthiasm@32 221 d.name = "Pitch track candidates";
matthiasm@32 222 d.description = "Multiple candidate pitch tracks.";
matthiasm@32 223 d.unit = "Hz";
matthiasm@37 224 d.hasFixedBinCount = true;
matthiasm@37 225 d.binCount = m_nCandidate;
matthiasm@32 226 d.hasKnownExtents = true;
matthiasm@32 227 d.minValue = m_fmin;
matthiasm@32 228 d.maxValue = 500;
matthiasm@32 229 d.isQuantized = false;
matthiasm@32 230 d.sampleType = OutputDescriptor::FixedSampleRate;
matthiasm@32 231 d.sampleRate = (m_inputSampleRate / m_stepSize);
matthiasm@32 232 d.hasDuration = false;
matthiasm@32 233 outputs.push_back(d);
matthiasm@32 234 // m_oPitchTrackCandidates = outputNumber++;
matthiasm@32 235
matthiasm@32 236 return outputs;
matthiasm@32 237 }
matthiasm@32 238
matthiasm@32 239 bool
matthiasm@32 240 LocalCandidatePYIN::initialise(size_t channels, size_t stepSize, size_t blockSize)
matthiasm@32 241 {
matthiasm@32 242 if (channels < getMinChannelCount() ||
matthiasm@32 243 channels > getMaxChannelCount()) return false;
matthiasm@32 244
matthiasm@32 245 /*
matthiasm@32 246 std::cerr << "LocalCandidatePYIN::initialise: channels = " << channels
matthiasm@32 247 << ", stepSize = " << stepSize << ", blockSize = " << blockSize
matthiasm@32 248 << std::endl;
matthiasm@32 249 */
matthiasm@32 250 m_channels = channels;
matthiasm@32 251 m_stepSize = stepSize;
matthiasm@32 252 m_blockSize = blockSize;
matthiasm@32 253
matthiasm@32 254 reset();
matthiasm@32 255
matthiasm@32 256 return true;
matthiasm@32 257 }
matthiasm@32 258
matthiasm@32 259 void
matthiasm@32 260 LocalCandidatePYIN::reset()
matthiasm@32 261 {
matthiasm@32 262 m_yin.setThresholdDistr(m_threshDistr);
matthiasm@32 263 m_yin.setFrameSize(m_blockSize);
matthiasm@32 264
matthiasm@32 265 m_pitchProb.clear();
matthiasm@32 266 for (size_t iCandidate = 0; iCandidate < m_nCandidate; ++iCandidate)
matthiasm@32 267 {
matthiasm@32 268 m_pitchProb.push_back(vector<vector<pair<double, double> > >());
matthiasm@32 269 }
matthiasm@32 270 m_timestamp.clear();
matthiasm@32 271 /*
matthiasm@32 272 std::cerr << "LocalCandidatePYIN::reset"
matthiasm@32 273 << ", blockSize = " << m_blockSize
matthiasm@32 274 << std::endl;
matthiasm@32 275 */
matthiasm@32 276 }
matthiasm@32 277
matthiasm@32 278 LocalCandidatePYIN::FeatureSet
matthiasm@32 279 LocalCandidatePYIN::process(const float *const *inputBuffers, RealTime timestamp)
matthiasm@32 280 {
matthiasm@32 281 timestamp = timestamp + Vamp::RealTime::frame2RealTime(m_blockSize/4, lrintf(m_inputSampleRate));
matthiasm@32 282 FeatureSet fs;
matthiasm@32 283
matthiasm@32 284 double *dInputBuffers = new double[m_blockSize];
matthiasm@32 285 for (size_t i = 0; i < m_blockSize; ++i) dInputBuffers[i] = inputBuffers[0][i];
matthiasm@32 286
matthiasm@32 287 size_t yinBufferSize = m_blockSize/2;
matthiasm@32 288 double* yinBuffer = new double[yinBufferSize];
matthiasm@32 289 YinUtil::fastDifference(dInputBuffers, yinBuffer, yinBufferSize);
matthiasm@32 290
matthiasm@32 291 delete [] dInputBuffers;
matthiasm@32 292
matthiasm@32 293 YinUtil::cumulativeDifference(yinBuffer, yinBufferSize);
matthiasm@32 294
matthiasm@32 295 for (size_t iCandidate = 0; iCandidate < m_nCandidate; ++iCandidate)
matthiasm@32 296 {
matthiasm@32 297 float minFrequency = m_fmin * std::pow(2,(3.0*iCandidate)/12);
matthiasm@32 298 float maxFrequency = m_fmin * std::pow(2,(3.0*iCandidate+9)/12);
matthiasm@34 299 vector<double> peakProbability = YinUtil::yinProb(yinBuffer,
matthiasm@34 300 m_threshDistr,
matthiasm@34 301 yinBufferSize,
matthiasm@34 302 m_inputSampleRate/maxFrequency,
matthiasm@34 303 m_inputSampleRate/minFrequency);
matthiasm@32 304
matthiasm@32 305 vector<pair<double, double> > tempPitchProb;
matthiasm@32 306 for (size_t iBuf = 0; iBuf < yinBufferSize; ++iBuf)
matthiasm@32 307 {
matthiasm@32 308 if (peakProbability[iBuf] > 0)
matthiasm@32 309 {
matthiasm@32 310 double currentF0 =
matthiasm@32 311 m_inputSampleRate * (1.0 /
matthiasm@32 312 YinUtil::parabolicInterpolation(yinBuffer, iBuf, yinBufferSize));
matthiasm@32 313 double tempPitch = 12 * std::log(currentF0/440)/std::log(2.) + 69;
matthiasm@32 314 tempPitchProb.push_back(pair<double, double>(tempPitch, peakProbability[iBuf]));
matthiasm@32 315 }
matthiasm@32 316 }
matthiasm@32 317 m_pitchProb[iCandidate].push_back(tempPitchProb);
matthiasm@32 318 }
matthiasm@32 319 m_timestamp.push_back(timestamp);
matthiasm@32 320
matthiasm@32 321 return fs;
matthiasm@32 322 }
matthiasm@32 323
matthiasm@32 324 LocalCandidatePYIN::FeatureSet
matthiasm@32 325 LocalCandidatePYIN::getRemainingFeatures()
matthiasm@32 326 {
matthiasm@32 327 FeatureSet fs;
matthiasm@32 328 Feature f;
matthiasm@32 329 f.hasTimestamp = true;
matthiasm@32 330 f.hasDuration = false;
matthiasm@32 331 f.values.push_back(0);
matthiasm@32 332
matthiasm@37 333 // std::cerr << "in remaining features" << std::endl;
matthiasm@32 334
matthiasm@32 335 if (m_pitchProb.empty()) {
matthiasm@32 336 return fs;
matthiasm@32 337 }
matthiasm@32 338
matthiasm@32 339 // MONO-PITCH STUFF
matthiasm@32 340 MonoPitch mp;
matthiasm@32 341 size_t nFrame = m_timestamp.size();
matthiasm@32 342 vector<vector<float> > pitchTracks;
matthiasm@32 343 vector<float> freqSum = vector<float>(m_nCandidate);
matthiasm@32 344 vector<float> freqNumber = vector<float>(m_nCandidate);
matthiasm@32 345 vector<float> freqMean = vector<float>(m_nCandidate);
matthiasm@32 346
matthiasm@32 347 for (size_t iCandidate = 0; iCandidate < m_nCandidate; ++iCandidate)
matthiasm@32 348 {
matthiasm@32 349 pitchTracks.push_back(vector<float>(nFrame));
matthiasm@32 350 vector<float> mpOut = mp.process(m_pitchProb[iCandidate]);
matthiasm@32 351 for (size_t iFrame = 0; iFrame < nFrame; ++iFrame)
matthiasm@32 352 {
matthiasm@32 353 if (mpOut[iFrame] > 0) {
matthiasm@32 354 pitchTracks[iCandidate][iFrame] = mpOut[iFrame];
matthiasm@32 355 freqSum[iCandidate] += mpOut[iFrame];
matthiasm@32 356 freqNumber[iCandidate]++;
matthiasm@32 357 }
matthiasm@32 358 }
matthiasm@32 359 freqMean[iCandidate] = freqSum[iCandidate]*1.0/freqNumber[iCandidate];
matthiasm@32 360 }
matthiasm@32 361
matthiasm@37 362 // find near duplicate pitch tracks
matthiasm@34 363 vector<size_t> duplicates;
matthiasm@34 364 for (size_t iCandidate = 0; iCandidate < m_nCandidate; ++iCandidate) {
matthiasm@34 365 for (size_t jCandidate = iCandidate+1; jCandidate < m_nCandidate; ++jCandidate) {
matthiasm@34 366 size_t countEqual = 0;
matthiasm@34 367 for (size_t iFrame = 0; iFrame < nFrame; ++iFrame)
matthiasm@34 368 {
matthiasm@34 369 if (fabs(pitchTracks[iCandidate][iFrame]/pitchTracks[jCandidate][iFrame]-1)<0.01)
matthiasm@34 370 countEqual++;
matthiasm@34 371 }
matthiasm@34 372 if (countEqual * 1.0 / nFrame > 0.8) {
matthiasm@34 373 if (freqNumber[iCandidate] > freqNumber[jCandidate]) {
matthiasm@34 374 duplicates.push_back(jCandidate);
matthiasm@34 375 } else {
matthiasm@34 376 duplicates.push_back(iCandidate);
matthiasm@34 377 }
matthiasm@34 378 }
matthiasm@34 379 }
matthiasm@34 380 }
matthiasm@34 381
matthiasm@37 382 // now find non-duplicate pitch tracks
matthiasm@37 383 vector<size_t> actualCandidates;
matthiasm@32 384 for (size_t iCandidate = 0; iCandidate < m_nCandidate; ++iCandidate) {
matthiasm@34 385 bool isDuplicate = false;
matthiasm@34 386 for (size_t i = 0; i < duplicates.size(); ++i) {
matthiasm@37 387 // std::cerr << duplicates[i] << std::endl;
matthiasm@34 388 if (duplicates[i] == iCandidate) {
matthiasm@34 389 isDuplicate = true;
matthiasm@34 390 break;
matthiasm@34 391 }
matthiasm@34 392 }
matthiasm@37 393 if (!isDuplicate && (freqNumber[iCandidate] > 0.5*nFrame)) {
matthiasm@37 394 actualCandidates.push_back(iCandidate);
matthiasm@37 395 // std::cerr << iCandidate << std::endl;
matthiasm@37 396 }
matthiasm@37 397 }
matthiasm@37 398
matthiasm@37 399 // finally write them out
matthiasm@37 400 for (size_t iFrame = 0; iFrame < nFrame; ++iFrame)
matthiasm@37 401 {
matthiasm@37 402 f.timestamp = m_timestamp[iFrame];
matthiasm@37 403 f.values.clear();
matthiasm@37 404 for (size_t iCandidate = 0; iCandidate < actualCandidates.size(); ++iCandidate) {
matthiasm@37 405 // std::ostringstream convert;
matthiasm@37 406 // convert << actualCandidateNumber++;
matthiasm@37 407 // f.label = convert.str();
matthiasm@37 408 // std::cerr << freqNumber[iCandidate] << " " << freqMean[iCandidate] << std::endl;
matthiasm@37 409 if (pitchTracks[actualCandidates[iCandidate]][iFrame] > 0)
matthiasm@32 410 {
matthiasm@37 411 f.values.push_back(pitchTracks[actualCandidates[iCandidate]][iFrame]);
matthiasm@37 412 } else {
matthiasm@37 413 f.values.push_back(0);
matthiasm@32 414 }
matthiasm@32 415 }
matthiasm@37 416 fs[m_oPitchTrackCandidates].push_back(f);
matthiasm@32 417 // std::cerr << freqNumber[iCandidate] << " " << (freqSum[iCandidate]*1.0/freqNumber[iCandidate]) << std::endl;
matthiasm@32 418 }
matthiasm@32 419
matthiasm@32 420 // only retain those that are close to their means
matthiasm@32 421
matthiasm@32 422 return fs;
matthiasm@32 423 }