Chris@32: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@32: /* Copyright Chris Cannam - All Rights Reserved */ Chris@32: Chris@32: #include "NoteHypothesis.h" Chris@32: Chris@32: #include Chris@32: Chris@35: using Vamp::RealTime; Chris@32: Chris@32: NoteHypothesis::NoteHypothesis() Chris@32: { Chris@32: m_state = New; Chris@32: } Chris@32: Chris@32: NoteHypothesis::~NoteHypothesis() Chris@32: { Chris@32: } Chris@32: Chris@32: bool Chris@32: NoteHypothesis::isWithinTolerance(Estimate s) const Chris@32: { Chris@32: if (m_pending.empty()) { Chris@32: return true; Chris@32: } Chris@32: Chris@32: // check we are within a relatively close tolerance of the last Chris@32: // candidate Chris@32: Estimate last = m_pending[m_pending.size()-1]; Chris@32: double r = s.freq / last.freq; Chris@32: int cents = lrint(1200.0 * (log(r) / log(2.0))); Chris@32: if (cents < -60 || cents > 60) return false; Chris@32: Chris@32: // and within a slightly bigger tolerance of the current mean Chris@32: double meanFreq = getMeanFrequency(); Chris@32: r = s.freq / meanFreq; Chris@32: cents = lrint(1200.0 * (log(r) / log(2.0))); Chris@32: if (cents < -80 || cents > 80) return false; Chris@32: Chris@32: return true; Chris@32: } Chris@32: Chris@32: bool Chris@32: NoteHypothesis::isOutOfDateFor(Estimate s) const Chris@32: { Chris@32: if (m_pending.empty()) return false; Chris@32: Chris@32: return ((s.time - m_pending[m_pending.size()-1].time) > Chris@32: RealTime::fromMilliseconds(40)); Chris@32: } Chris@32: Chris@32: bool Chris@32: NoteHypothesis::isSatisfied() const Chris@32: { Chris@32: if (m_pending.empty()) return false; Chris@32: Chris@32: double meanConfidence = 0.0; Chris@32: for (int i = 0; i < (int)m_pending.size(); ++i) { Chris@32: meanConfidence += m_pending[i].confidence; Chris@32: } Chris@32: meanConfidence /= m_pending.size(); Chris@32: Chris@32: int lengthRequired = 10000; Chris@32: if (meanConfidence > 0.0) { Chris@32: lengthRequired = int(2.0 / meanConfidence + 0.5); Chris@32: } Chris@32: Chris@32: return ((int)m_pending.size() > lengthRequired); Chris@32: } Chris@32: Chris@32: bool Chris@32: NoteHypothesis::accept(Estimate s) Chris@32: { Chris@32: bool accept = false; Chris@32: Chris@32: switch (m_state) { Chris@32: Chris@32: case New: Chris@32: m_state = Provisional; Chris@32: accept = true; Chris@32: break; Chris@32: Chris@32: case Provisional: Chris@32: if (isOutOfDateFor(s)) { Chris@32: m_state = Rejected; Chris@32: } else if (isWithinTolerance(s)) { Chris@32: accept = true; Chris@32: } Chris@32: break; Chris@32: Chris@32: case Satisfied: Chris@32: if (isOutOfDateFor(s)) { Chris@32: m_state = Expired; Chris@32: } else if (isWithinTolerance(s)) { Chris@32: accept = true; Chris@32: } Chris@32: break; Chris@32: Chris@32: case Rejected: Chris@32: break; Chris@32: Chris@32: case Expired: Chris@32: break; Chris@32: } Chris@32: Chris@32: if (accept) { Chris@32: m_pending.push_back(s); Chris@32: if (m_state == Provisional && isSatisfied()) { Chris@32: m_state = Satisfied; Chris@32: } Chris@32: } Chris@32: Chris@32: return accept; Chris@32: } Chris@32: Chris@32: NoteHypothesis::State Chris@32: NoteHypothesis::getState() const Chris@32: { Chris@32: return m_state; Chris@32: } Chris@32: Chris@32: NoteHypothesis::Estimates Chris@32: NoteHypothesis::getAcceptedEstimates() const Chris@32: { Chris@32: if (m_state == Satisfied || m_state == Expired) { Chris@32: return m_pending; Chris@32: } else { Chris@32: return Estimates(); Chris@32: } Chris@32: } Chris@32: Chris@32: double Chris@32: NoteHypothesis::getMeanFrequency() const Chris@32: { Chris@32: double acc = 0.0; Chris@34: if (m_pending.empty()) return acc; Chris@32: for (int i = 0; i < (int)m_pending.size(); ++i) { Chris@32: acc += m_pending[i].freq; Chris@32: } Chris@32: acc /= m_pending.size(); Chris@32: return acc; Chris@32: } Chris@32: Chris@32: NoteHypothesis::Note Chris@32: NoteHypothesis::getAveragedNote() const Chris@32: { Chris@32: Note n; Chris@32: Chris@32: if (!(m_state == Satisfied || m_state == Expired)) { Chris@32: n.freq = 0.0; Chris@32: n.time = RealTime::zeroTime; Chris@32: n.duration = RealTime::zeroTime; Chris@32: return n; Chris@32: } Chris@32: Chris@32: n.time = m_pending.begin()->time; Chris@32: Chris@32: Estimates::const_iterator i = m_pending.end(); Chris@32: --i; Chris@32: n.duration = i->time - n.time; Chris@32: Chris@32: // just mean frequency for now, but this isn't at all right perceptually Chris@32: n.freq = getMeanFrequency(); Chris@32: Chris@32: return n; Chris@32: } Chris@32: