comparison CepstralPitchTracker.cpp @ 35:2f5b169e4a3b

Integrate NoteHypothesis class, build fixes
author Chris Cannam
date Thu, 19 Jul 2012 13:46:45 +0100
parents 2c175adf8736
children 822cf7b8e070
comparison
equal deleted inserted replaced
34:3fb9c657d86b 35:2f5b169e4a3b
35 35
36 using std::string; 36 using std::string;
37 using std::vector; 37 using std::vector;
38 using Vamp::RealTime; 38 using Vamp::RealTime;
39 39
40 CepstralPitchTracker::Hypothesis::Hypothesis()
41 {
42 m_state = New;
43 }
44
45 CepstralPitchTracker::Hypothesis::~Hypothesis()
46 {
47 }
48
49 bool
50 CepstralPitchTracker::Hypothesis::isWithinTolerance(Estimate s) const
51 {
52 if (m_pending.empty()) {
53 return true;
54 }
55
56 // check we are within a relatively close tolerance of the last
57 // candidate
58 Estimate last = m_pending[m_pending.size()-1];
59 double r = s.freq / last.freq;
60 int cents = lrint(1200.0 * (log(r) / log(2.0)));
61 if (cents < -60 || cents > 60) return false;
62
63 // and within a slightly bigger tolerance of the current mean
64 double meanFreq = getMeanFrequency();
65 r = s.freq / meanFreq;
66 cents = lrint(1200.0 * (log(r) / log(2.0)));
67 if (cents < -80 || cents > 80) return false;
68
69 return true;
70 }
71
72 bool
73 CepstralPitchTracker::Hypothesis::isOutOfDateFor(Estimate s) const
74 {
75 if (m_pending.empty()) return false;
76
77 return ((s.time - m_pending[m_pending.size()-1].time) >
78 RealTime::fromMilliseconds(40));
79 }
80
81 bool
82 CepstralPitchTracker::Hypothesis::isSatisfied() const
83 {
84 if (m_pending.empty()) return false;
85
86 double meanConfidence = 0.0;
87 for (int i = 0; i < m_pending.size(); ++i) {
88 meanConfidence += m_pending[i].confidence;
89 }
90 meanConfidence /= m_pending.size();
91
92 int lengthRequired = 10000;
93 if (meanConfidence > 0.0) {
94 lengthRequired = int(2.0 / meanConfidence + 0.5);
95 }
96
97 return (m_pending.size() > lengthRequired);
98 }
99
100 bool
101 CepstralPitchTracker::Hypothesis::accept(Estimate s)
102 {
103 bool accept = false;
104
105 switch (m_state) {
106
107 case New:
108 m_state = Provisional;
109 accept = true;
110 break;
111
112 case Provisional:
113 if (isOutOfDateFor(s)) {
114 m_state = Rejected;
115 } else if (isWithinTolerance(s)) {
116 accept = true;
117 }
118 break;
119
120 case Satisfied:
121 if (isOutOfDateFor(s)) {
122 m_state = Expired;
123 } else if (isWithinTolerance(s)) {
124 accept = true;
125 }
126 break;
127
128 case Rejected:
129 break;
130
131 case Expired:
132 break;
133 }
134
135 if (accept) {
136 m_pending.push_back(s);
137 if (m_state == Provisional && isSatisfied()) {
138 m_state = Satisfied;
139 }
140 }
141
142 return accept;
143 }
144
145 CepstralPitchTracker::Hypothesis::State
146 CepstralPitchTracker::Hypothesis::getState() const
147 {
148 return m_state;
149 }
150
151 CepstralPitchTracker::Hypothesis::Estimates
152 CepstralPitchTracker::Hypothesis::getAcceptedEstimates() const
153 {
154 if (m_state == Satisfied || m_state == Expired) {
155 return m_pending;
156 } else {
157 return Estimates();
158 }
159 }
160
161 double
162 CepstralPitchTracker::Hypothesis::getMeanFrequency() const
163 {
164 double acc = 0.0;
165 for (int i = 0; i < m_pending.size(); ++i) {
166 acc += m_pending[i].freq;
167 }
168 acc /= m_pending.size();
169 return acc;
170 }
171
172 CepstralPitchTracker::Hypothesis::Note
173 CepstralPitchTracker::Hypothesis::getAveragedNote() const
174 {
175 Note n;
176
177 if (!(m_state == Satisfied || m_state == Expired)) {
178 n.freq = 0.0;
179 n.time = RealTime::zeroTime;
180 n.duration = RealTime::zeroTime;
181 return n;
182 }
183
184 n.time = m_pending.begin()->time;
185
186 Estimates::const_iterator i = m_pending.end();
187 --i;
188 n.duration = i->time - n.time;
189
190 // just mean frequency for now, but this isn't at all right perceptually
191 n.freq = getMeanFrequency();
192
193 return n;
194 }
195 40
196 CepstralPitchTracker::CepstralPitchTracker(float inputSampleRate) : 41 CepstralPitchTracker::CepstralPitchTracker(float inputSampleRate) :
197 Plugin(inputSampleRate), 42 Plugin(inputSampleRate),
198 m_channels(0), 43 m_channels(0),
199 m_stepSize(256), 44 m_stepSize(256),
317 162
318 CepstralPitchTracker::OutputList 163 CepstralPitchTracker::OutputList
319 CepstralPitchTracker::getOutputDescriptors() const 164 CepstralPitchTracker::getOutputDescriptors() const
320 { 165 {
321 OutputList outputs; 166 OutputList outputs;
322
323 int n = 0;
324 167
325 OutputDescriptor d; 168 OutputDescriptor d;
326 169
327 d.identifier = "f0"; 170 d.identifier = "f0";
328 d.name = "Estimated f0"; 171 d.name = "Estimated f0";
389 CepstralPitchTracker::reset() 232 CepstralPitchTracker::reset()
390 { 233 {
391 } 234 }
392 235
393 void 236 void
394 CepstralPitchTracker::addFeaturesFrom(Hypothesis h, FeatureSet &fs) 237 CepstralPitchTracker::addFeaturesFrom(NoteHypothesis h, FeatureSet &fs)
395 { 238 {
396 Hypothesis::Estimates es = h.getAcceptedEstimates(); 239 NoteHypothesis::Estimates es = h.getAcceptedEstimates();
397 240
398 for (int i = 0; i < es.size(); ++i) { 241 for (int i = 0; i < (int)es.size(); ++i) {
399 Feature f; 242 Feature f;
400 f.hasTimestamp = true; 243 f.hasTimestamp = true;
401 f.timestamp = es[i].time; 244 f.timestamp = es[i].time;
402 f.values.push_back(es[i].freq); 245 f.values.push_back(es[i].freq);
403 fs[0].push_back(f); 246 fs[0].push_back(f);
404 } 247 }
405 248
406 Feature nf; 249 Feature nf;
407 nf.hasTimestamp = true; 250 nf.hasTimestamp = true;
408 nf.hasDuration = true; 251 nf.hasDuration = true;
409 Hypothesis::Note n = h.getAveragedNote(); 252 NoteHypothesis::Note n = h.getAveragedNote();
410 nf.timestamp = n.time; 253 nf.timestamp = n.time;
411 nf.duration = n.duration; 254 nf.duration = n.duration;
412 nf.values.push_back(n.freq); 255 nf.values.push_back(n.freq);
413 fs[1].push_back(nf); 256 fs[1].push_back(nf);
414 } 257 }
420 double v = 0; 263 double v = 0;
421 int n = 0; 264 int n = 0;
422 // average according to the vertical filter length 265 // average according to the vertical filter length
423 for (int j = -m_vflen/2; j <= m_vflen/2; ++j) { 266 for (int j = -m_vflen/2; j <= m_vflen/2; ++j) {
424 int ix = i + m_binFrom + j; 267 int ix = i + m_binFrom + j;
425 if (ix >= 0 && ix < m_blockSize) { 268 if (ix >= 0 && ix < (int)m_blockSize) {
426 v += cep[ix]; 269 v += cep[ix];
427 ++n; 270 ++n;
428 } 271 }
429 } 272 }
430 data[i] = v / n; 273 data[i] = v / n;
573 confidence = (maxval - nextPeakVal) * 10.0; 416 confidence = (maxval - nextPeakVal) * 10.0;
574 if (magmean < threshold) confidence = 0.0; 417 if (magmean < threshold) confidence = 0.0;
575 std::cerr << "magmean = " << magmean << ", confidence = " << confidence << std::endl; 418 std::cerr << "magmean = " << magmean << ", confidence = " << confidence << std::endl;
576 } 419 }
577 420
578 Hypothesis::Estimate e; 421 NoteHypothesis::Estimate e;
579 e.freq = peakfreq; 422 e.freq = peakfreq;
580 e.time = timestamp; 423 e.time = timestamp;
581 e.confidence = confidence; 424 e.confidence = confidence;
582 425
583 // m_good.advanceTime();
584 for (int i = 0; i < m_possible.size(); ++i) {
585 // m_possible[i].advanceTime();
586 }
587
588 if (!m_good.accept(e)) { 426 if (!m_good.accept(e)) {
589 427
590 int candidate = -1; 428 int candidate = -1;
591 bool accepted = false; 429 bool accepted = false;
592 430
593 for (int i = 0; i < m_possible.size(); ++i) { 431 for (int i = 0; i < (int)m_possible.size(); ++i) {
594 if (m_possible[i].accept(e)) { 432 if (m_possible[i].accept(e)) {
595 if (m_possible[i].getState() == Hypothesis::Satisfied) { 433 if (m_possible[i].getState() == NoteHypothesis::Satisfied) {
596 accepted = true; 434 accepted = true;
597 candidate = i; 435 candidate = i;
598 } 436 }
599 break; 437 break;
600 } 438 }
601 } 439 }
602 440
603 if (!accepted) { 441 if (!accepted) {
604 Hypothesis h; 442 NoteHypothesis h;
605 h.accept(e); //!!! must succeed as h is new, so perhaps there should be a ctor for this 443 h.accept(e); //!!! must succeed as h is new, so perhaps there should be a ctor for this
606 m_possible.push_back(h); 444 m_possible.push_back(h);
607 } 445 }
608 446
609 if (m_good.getState() == Hypothesis::Expired) { 447 if (m_good.getState() == NoteHypothesis::Expired) {
610 addFeaturesFrom(m_good, fs); 448 addFeaturesFrom(m_good, fs);
611 } 449 }
612 450
613 if (m_good.getState() == Hypothesis::Expired || 451 if (m_good.getState() == NoteHypothesis::Expired ||
614 m_good.getState() == Hypothesis::Rejected) { 452 m_good.getState() == NoteHypothesis::Rejected) {
615 if (candidate >= 0) { 453 if (candidate >= 0) {
616 m_good = m_possible[candidate]; 454 m_good = m_possible[candidate];
617 } else { 455 } else {
618 m_good = Hypothesis(); 456 m_good = NoteHypothesis();
619 } 457 }
620 } 458 }
621 459
622 // reap rejected/expired hypotheses from possible list 460 // reap rejected/expired hypotheses from possible list
623 Hypotheses toReap = m_possible; 461 Hypotheses toReap = m_possible;
624 m_possible.clear(); 462 m_possible.clear();
625 for (int i = 0; i < toReap.size(); ++i) { 463 for (int i = 0; i < (int)toReap.size(); ++i) {
626 Hypothesis h = toReap[i]; 464 NoteHypothesis h = toReap[i];
627 if (h.getState() != Hypothesis::Rejected && 465 if (h.getState() != NoteHypothesis::Rejected &&
628 h.getState() != Hypothesis::Expired) { 466 h.getState() != NoteHypothesis::Expired) {
629 m_possible.push_back(h); 467 m_possible.push_back(h);
630 } 468 }
631 } 469 }
632 } 470 }
633 471
637 475
638 CepstralPitchTracker::FeatureSet 476 CepstralPitchTracker::FeatureSet
639 CepstralPitchTracker::getRemainingFeatures() 477 CepstralPitchTracker::getRemainingFeatures()
640 { 478 {
641 FeatureSet fs; 479 FeatureSet fs;
642 if (m_good.getState() == Hypothesis::Satisfied) { 480 if (m_good.getState() == NoteHypothesis::Satisfied) {
643 addFeaturesFrom(m_good, fs); 481 addFeaturesFrom(m_good, fs);
644 } 482 }
645 return fs; 483 return fs;
646 } 484 }