annotate CepstralPitchTracker.cpp @ 75:84d1a0647ce5 tip

Split out COPYING from README
author Chris Cannam
date Fri, 06 Mar 2020 11:01:53 +0000
parents 9f50a5876dd3
children 7ad142c710c6
rev   line source
Chris@3 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@3 2 /*
Chris@31 3 This file is Copyright (c) 2012 Chris Cannam
Chris@31 4
Chris@3 5 Permission is hereby granted, free of charge, to any person
Chris@3 6 obtaining a copy of this software and associated documentation
Chris@3 7 files (the "Software"), to deal in the Software without
Chris@3 8 restriction, including without limitation the rights to use, copy,
Chris@3 9 modify, merge, publish, distribute, sublicense, and/or sell copies
Chris@3 10 of the Software, and to permit persons to whom the Software is
Chris@3 11 furnished to do so, subject to the following conditions:
Chris@3 12
Chris@3 13 The above copyright notice and this permission notice shall be
Chris@3 14 included in all copies or substantial portions of the Software.
Chris@3 15
Chris@3 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Chris@3 17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Chris@3 18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Chris@3 19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
Chris@3 20 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
Chris@3 21 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
Chris@3 22 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Chris@3 23 */
Chris@3 24
Chris@31 25 #include "CepstralPitchTracker.h"
Chris@51 26 #include "Cepstrum.h"
Chris@47 27 #include "MeanFilter.h"
Chris@50 28 #include "PeakInterpolator.h"
Chris@56 29 #include "AgentFeeder.h"
Chris@3 30
Chris@26 31 #include "vamp-sdk/FFT.h"
Chris@26 32
Chris@3 33 #include <vector>
Chris@3 34 #include <algorithm>
Chris@3 35
Chris@3 36 #include <cstdio>
Chris@3 37 #include <cmath>
Chris@3 38 #include <complex>
Chris@3 39
Chris@3 40 using std::string;
Chris@7 41 using std::vector;
Chris@16 42 using Vamp::RealTime;
Chris@7 43
Chris@16 44
Chris@31 45 CepstralPitchTracker::CepstralPitchTracker(float inputSampleRate) :
Chris@3 46 Plugin(inputSampleRate),
Chris@3 47 m_channels(0),
Chris@3 48 m_stepSize(256),
Chris@3 49 m_blockSize(1024),
Chris@3 50 m_fmin(50),
Chris@25 51 m_fmax(900),
Chris@18 52 m_vflen(1),
Chris@3 53 m_binFrom(0),
Chris@3 54 m_binTo(0),
Chris@56 55 m_bins(0),
Chris@57 56 m_nAccepted(0),
Chris@56 57 m_feeder(0)
Chris@3 58 {
Chris@3 59 }
Chris@3 60
Chris@31 61 CepstralPitchTracker::~CepstralPitchTracker()
Chris@3 62 {
Chris@56 63 delete m_feeder;
Chris@3 64 }
Chris@3 65
Chris@3 66 string
Chris@31 67 CepstralPitchTracker::getIdentifier() const
Chris@3 68 {
Chris@39 69 return "cepstral-pitchtracker";
Chris@3 70 }
Chris@3 71
Chris@3 72 string
Chris@31 73 CepstralPitchTracker::getName() const
Chris@3 74 {
Chris@39 75 return "Cepstral Pitch Tracker";
Chris@3 76 }
Chris@3 77
Chris@3 78 string
Chris@31 79 CepstralPitchTracker::getDescription() const
Chris@3 80 {
Chris@3 81 return "Estimate f0 of monophonic material using a cepstrum method.";
Chris@3 82 }
Chris@3 83
Chris@3 84 string
Chris@31 85 CepstralPitchTracker::getMaker() const
Chris@3 86 {
Chris@3 87 return "Chris Cannam";
Chris@3 88 }
Chris@3 89
Chris@3 90 int
Chris@31 91 CepstralPitchTracker::getPluginVersion() const
Chris@3 92 {
Chris@3 93 // Increment this each time you release a version that behaves
Chris@3 94 // differently from the previous one
Chris@3 95 return 1;
Chris@3 96 }
Chris@3 97
Chris@3 98 string
Chris@31 99 CepstralPitchTracker::getCopyright() const
Chris@3 100 {
Chris@3 101 return "Freely redistributable (BSD license)";
Chris@3 102 }
Chris@3 103
Chris@31 104 CepstralPitchTracker::InputDomain
Chris@31 105 CepstralPitchTracker::getInputDomain() const
Chris@3 106 {
Chris@3 107 return FrequencyDomain;
Chris@3 108 }
Chris@3 109
Chris@3 110 size_t
Chris@31 111 CepstralPitchTracker::getPreferredBlockSize() const
Chris@3 112 {
Chris@3 113 return 1024;
Chris@3 114 }
Chris@3 115
Chris@3 116 size_t
Chris@31 117 CepstralPitchTracker::getPreferredStepSize() const
Chris@3 118 {
Chris@3 119 return 256;
Chris@3 120 }
Chris@3 121
Chris@3 122 size_t
Chris@31 123 CepstralPitchTracker::getMinChannelCount() const
Chris@3 124 {
Chris@3 125 return 1;
Chris@3 126 }
Chris@3 127
Chris@3 128 size_t
Chris@31 129 CepstralPitchTracker::getMaxChannelCount() const
Chris@3 130 {
Chris@3 131 return 1;
Chris@3 132 }
Chris@3 133
Chris@31 134 CepstralPitchTracker::ParameterList
Chris@31 135 CepstralPitchTracker::getParameterDescriptors() const
Chris@3 136 {
Chris@3 137 ParameterList list;
Chris@3 138 return list;
Chris@3 139 }
Chris@3 140
Chris@3 141 float
Chris@31 142 CepstralPitchTracker::getParameter(string identifier) const
Chris@3 143 {
Chris@3 144 return 0.f;
Chris@3 145 }
Chris@3 146
Chris@3 147 void
Chris@31 148 CepstralPitchTracker::setParameter(string identifier, float value)
Chris@3 149 {
Chris@3 150 }
Chris@3 151
Chris@31 152 CepstralPitchTracker::ProgramList
Chris@31 153 CepstralPitchTracker::getPrograms() const
Chris@3 154 {
Chris@3 155 ProgramList list;
Chris@3 156 return list;
Chris@3 157 }
Chris@3 158
Chris@3 159 string
Chris@31 160 CepstralPitchTracker::getCurrentProgram() const
Chris@3 161 {
Chris@3 162 return ""; // no programs
Chris@3 163 }
Chris@3 164
Chris@3 165 void
Chris@31 166 CepstralPitchTracker::selectProgram(string name)
Chris@3 167 {
Chris@3 168 }
Chris@3 169
Chris@31 170 CepstralPitchTracker::OutputList
Chris@31 171 CepstralPitchTracker::getOutputDescriptors() const
Chris@3 172 {
Chris@3 173 OutputList outputs;
Chris@3 174
Chris@3 175 OutputDescriptor d;
Chris@3 176
Chris@3 177 d.identifier = "f0";
Chris@3 178 d.name = "Estimated f0";
Chris@3 179 d.description = "Estimated fundamental frequency";
Chris@3 180 d.unit = "Hz";
Chris@3 181 d.hasFixedBinCount = true;
Chris@3 182 d.binCount = 1;
Chris@3 183 d.hasKnownExtents = true;
Chris@3 184 d.minValue = m_fmin;
Chris@3 185 d.maxValue = m_fmax;
Chris@3 186 d.isQuantized = false;
Chris@3 187 d.sampleType = OutputDescriptor::FixedSampleRate;
Chris@3 188 d.sampleRate = (m_inputSampleRate / m_stepSize);
Chris@3 189 d.hasDuration = false;
Chris@3 190 outputs.push_back(d);
Chris@3 191
Chris@16 192 d.identifier = "notes";
Chris@16 193 d.name = "Notes";
Chris@16 194 d.description = "Derived fixed-pitch note frequencies";
Chris@16 195 d.unit = "Hz";
Chris@16 196 d.hasFixedBinCount = true;
Chris@16 197 d.binCount = 1;
Chris@16 198 d.hasKnownExtents = true;
Chris@16 199 d.minValue = m_fmin;
Chris@16 200 d.maxValue = m_fmax;
Chris@16 201 d.isQuantized = false;
Chris@16 202 d.sampleType = OutputDescriptor::FixedSampleRate;
Chris@16 203 d.sampleRate = (m_inputSampleRate / m_stepSize);
Chris@16 204 d.hasDuration = true;
Chris@16 205 outputs.push_back(d);
Chris@16 206
Chris@3 207 return outputs;
Chris@3 208 }
Chris@3 209
Chris@3 210 bool
Chris@31 211 CepstralPitchTracker::initialise(size_t channels, size_t stepSize, size_t blockSize)
Chris@3 212 {
Chris@3 213 if (channels < getMinChannelCount() ||
Chris@3 214 channels > getMaxChannelCount()) return false;
Chris@3 215
Chris@31 216 // std::cerr << "CepstralPitchTracker::initialise: channels = " << channels
Chris@3 217 // << ", stepSize = " << stepSize << ", blockSize = " << blockSize
Chris@3 218 // << std::endl;
Chris@3 219
Chris@3 220 m_channels = channels;
Chris@3 221 m_stepSize = stepSize;
Chris@3 222 m_blockSize = blockSize;
Chris@3 223
Chris@3 224 m_binFrom = int(m_inputSampleRate / m_fmax);
Chris@3 225 m_binTo = int(m_inputSampleRate / m_fmin);
Chris@3 226
Chris@3 227 if (m_binTo >= (int)m_blockSize / 2) {
Chris@3 228 m_binTo = m_blockSize / 2 - 1;
Chris@3 229 }
Chris@56 230 if (m_binFrom >= m_binTo) {
Chris@56 231 // shouldn't happen except for degenerate samplerate / blocksize combos
Chris@56 232 m_binFrom = m_binTo - 1;
Chris@56 233 }
Chris@3 234
Chris@3 235 m_bins = (m_binTo - m_binFrom) + 1;
Chris@3 236
Chris@3 237 reset();
Chris@3 238
Chris@3 239 return true;
Chris@3 240 }
Chris@3 241
Chris@3 242 void
Chris@31 243 CepstralPitchTracker::reset()
Chris@3 244 {
Chris@56 245 delete m_feeder;
Chris@56 246 m_feeder = new AgentFeeder();
Chris@57 247 m_nAccepted = 0;
Chris@3 248 }
Chris@3 249
Chris@3 250 void
Chris@35 251 CepstralPitchTracker::addFeaturesFrom(NoteHypothesis h, FeatureSet &fs)
Chris@30 252 {
Chris@35 253 NoteHypothesis::Estimates es = h.getAcceptedEstimates();
Chris@30 254
Chris@35 255 for (int i = 0; i < (int)es.size(); ++i) {
Chris@30 256 Feature f;
Chris@30 257 f.hasTimestamp = true;
Chris@30 258 f.timestamp = es[i].time;
Chris@30 259 f.values.push_back(es[i].freq);
Chris@30 260 fs[0].push_back(f);
Chris@30 261 }
Chris@30 262
Chris@30 263 Feature nf;
Chris@30 264 nf.hasTimestamp = true;
Chris@30 265 nf.hasDuration = true;
Chris@35 266 NoteHypothesis::Note n = h.getAveragedNote();
Chris@30 267 nf.timestamp = n.time;
Chris@30 268 nf.duration = n.duration;
Chris@30 269 nf.values.push_back(n.freq);
Chris@30 270 fs[1].push_back(nf);
Chris@30 271 }
Chris@30 272
Chris@57 273 void
Chris@57 274 CepstralPitchTracker::addNewFeatures(FeatureSet &fs)
Chris@57 275 {
Chris@57 276 int n = m_feeder->getAcceptedHypotheses().size();
Chris@57 277 if (n == m_nAccepted) return;
Chris@57 278
Chris@57 279 AgentFeeder::Hypotheses accepted = m_feeder->getAcceptedHypotheses();
Chris@57 280
Chris@57 281 for (int i = m_nAccepted; i < n; ++i) {
Chris@57 282 addFeaturesFrom(accepted[i], fs);
Chris@57 283 }
Chris@57 284
Chris@57 285 m_nAccepted = n;
Chris@57 286 }
Chris@57 287
Chris@31 288 CepstralPitchTracker::FeatureSet
Chris@31 289 CepstralPitchTracker::process(const float *const *inputBuffers, RealTime timestamp)
Chris@3 290 {
Chris@51 291 double *rawcep = new double[m_blockSize];
Chris@51 292 double magmean = Cepstrum(m_blockSize).process(inputBuffers[0], rawcep);
Chris@3 293
Chris@3 294 int n = m_bins;
Chris@3 295 double *data = new double[n];
Chris@51 296 MeanFilter(m_vflen).filterSubsequence
Chris@51 297 (rawcep, data, m_blockSize, n, m_binFrom);
Chris@51 298
Chris@3 299 delete[] rawcep;
Chris@3 300
Chris@3 301 double maxval = 0.0;
Chris@6 302 int maxbin = -1;
Chris@3 303
Chris@3 304 for (int i = 0; i < n; ++i) {
Chris@3 305 if (data[i] > maxval) {
Chris@3 306 maxval = data[i];
Chris@3 307 maxbin = i;
Chris@3 308 }
Chris@3 309 }
Chris@3 310
Chris@15 311 if (maxbin < 0) {
Chris@15 312 delete[] data;
Chris@57 313 return FeatureSet();
Chris@15 314 }
Chris@15 315
Chris@15 316 double nextPeakVal = 0.0;
Chris@15 317 for (int i = 1; i+1 < n; ++i) {
Chris@15 318 if (data[i] > data[i-1] &&
Chris@15 319 data[i] > data[i+1] &&
Chris@15 320 i != maxbin &&
Chris@15 321 data[i] > nextPeakVal) {
Chris@15 322 nextPeakVal = data[i];
Chris@15 323 }
Chris@15 324 }
Chris@8 325
Chris@50 326 PeakInterpolator pi;
Chris@50 327 double cimax = pi.findPeakLocation(data, m_bins, maxbin);
Chris@18 328 double peakfreq = m_inputSampleRate / (cimax + m_binFrom);
Chris@15 329
Chris@15 330 double confidence = 0.0;
Chris@51 331 double threshold = 0.1; // for magmean
Chris@51 332
Chris@15 333 if (nextPeakVal != 0.0) {
Chris@27 334 confidence = (maxval - nextPeakVal) * 10.0;
Chris@25 335 if (magmean < threshold) confidence = 0.0;
Chris@15 336 }
Chris@15 337
Chris@57 338 delete[] data;
Chris@57 339
Chris@35 340 NoteHypothesis::Estimate e;
Chris@8 341 e.freq = peakfreq;
Chris@8 342 e.time = timestamp;
Chris@15 343 e.confidence = confidence;
Chris@8 344
Chris@56 345 m_feeder->feed(e);
Chris@14 346
Chris@57 347 FeatureSet fs;
Chris@57 348 addNewFeatures(fs);
Chris@3 349 return fs;
Chris@3 350 }
Chris@3 351
Chris@31 352 CepstralPitchTracker::FeatureSet
Chris@31 353 CepstralPitchTracker::getRemainingFeatures()
Chris@3 354 {
Chris@56 355 m_feeder->finish();
Chris@56 356
Chris@3 357 FeatureSet fs;
Chris@57 358 addNewFeatures(fs);
Chris@3 359 return fs;
Chris@3 360 }