Mercurial > hg > cepstral-pitchtracker
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 } |