Mercurial > hg > cepstral-pitchtracker
diff NoteHypothesis.cpp @ 32:c88a9972975b
Add pitch note-hypothesis class
author | Chris Cannam |
---|---|
date | Fri, 13 Jul 2012 21:40:50 +0100 |
parents | |
children | 3fb9c657d86b |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NoteHypothesis.cpp Fri Jul 13 21:40:50 2012 +0100 @@ -0,0 +1,169 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ +/* Copyright Chris Cannam - All Rights Reserved */ + +#include "NoteHypothesis.h" + +#include <cmath> + +#include "system/sysutils.h" + +namespace Turbot { + +NoteHypothesis::NoteHypothesis() +{ + m_state = New; +} + +NoteHypothesis::~NoteHypothesis() +{ +} + +bool +NoteHypothesis::isWithinTolerance(Estimate s) const +{ + if (m_pending.empty()) { + return true; + } + + // check we are within a relatively close tolerance of the last + // candidate + Estimate last = m_pending[m_pending.size()-1]; + double r = s.freq / last.freq; + int cents = lrint(1200.0 * (log(r) / log(2.0))); + if (cents < -60 || cents > 60) return false; + + // and within a slightly bigger tolerance of the current mean + double meanFreq = getMeanFrequency(); + r = s.freq / meanFreq; + cents = lrint(1200.0 * (log(r) / log(2.0))); + if (cents < -80 || cents > 80) return false; + + return true; +} + +bool +NoteHypothesis::isOutOfDateFor(Estimate s) const +{ + if (m_pending.empty()) return false; + + return ((s.time - m_pending[m_pending.size()-1].time) > + RealTime::fromMilliseconds(40)); +} + +bool +NoteHypothesis::isSatisfied() const +{ + if (m_pending.empty()) return false; + + double meanConfidence = 0.0; + for (int i = 0; i < (int)m_pending.size(); ++i) { + meanConfidence += m_pending[i].confidence; + } + meanConfidence /= m_pending.size(); + + int lengthRequired = 10000; + if (meanConfidence > 0.0) { + lengthRequired = int(2.0 / meanConfidence + 0.5); + } + + return ((int)m_pending.size() > lengthRequired); +} + +bool +NoteHypothesis::accept(Estimate s) +{ + bool accept = false; + + switch (m_state) { + + case New: + m_state = Provisional; + accept = true; + break; + + case Provisional: + if (isOutOfDateFor(s)) { + m_state = Rejected; + } else if (isWithinTolerance(s)) { + accept = true; + } + break; + + case Satisfied: + if (isOutOfDateFor(s)) { + m_state = Expired; + } else if (isWithinTolerance(s)) { + accept = true; + } + break; + + case Rejected: + break; + + case Expired: + break; + } + + if (accept) { + m_pending.push_back(s); + if (m_state == Provisional && isSatisfied()) { + m_state = Satisfied; + } + } + + return accept; +} + +NoteHypothesis::State +NoteHypothesis::getState() const +{ + return m_state; +} + +NoteHypothesis::Estimates +NoteHypothesis::getAcceptedEstimates() const +{ + if (m_state == Satisfied || m_state == Expired) { + return m_pending; + } else { + return Estimates(); + } +} + +double +NoteHypothesis::getMeanFrequency() const +{ + double acc = 0.0; + for (int i = 0; i < (int)m_pending.size(); ++i) { + acc += m_pending[i].freq; + } + acc /= m_pending.size(); + return acc; +} + +NoteHypothesis::Note +NoteHypothesis::getAveragedNote() const +{ + Note n; + + if (!(m_state == Satisfied || m_state == Expired)) { + n.freq = 0.0; + n.time = RealTime::zeroTime; + n.duration = RealTime::zeroTime; + return n; + } + + n.time = m_pending.begin()->time; + + Estimates::const_iterator i = m_pending.end(); + --i; + n.duration = i->time - n.time; + + // just mean frequency for now, but this isn't at all right perceptually + n.freq = getMeanFrequency(); + + return n; +} + +} +