annotate NoteHypothesis.cpp @ 66:7ad142c710c6 parameters

Add some parameters
author Chris Cannam
date Fri, 30 Aug 2013 15:35:49 +0100
parents c06fe5350b34
children
rev   line source
Chris@32 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@37 2 /*
Chris@37 3 This file is Copyright (c) 2012 Chris Cannam
Chris@37 4
Chris@37 5 Permission is hereby granted, free of charge, to any person
Chris@37 6 obtaining a copy of this software and associated documentation
Chris@37 7 files (the "Software"), to deal in the Software without
Chris@37 8 restriction, including without limitation the rights to use, copy,
Chris@37 9 modify, merge, publish, distribute, sublicense, and/or sell copies
Chris@37 10 of the Software, and to permit persons to whom the Software is
Chris@37 11 furnished to do so, subject to the following conditions:
Chris@37 12
Chris@37 13 The above copyright notice and this permission notice shall be
Chris@37 14 included in all copies or substantial portions of the Software.
Chris@37 15
Chris@37 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Chris@37 17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Chris@37 18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Chris@37 19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
Chris@37 20 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
Chris@37 21 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
Chris@37 22 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Chris@37 23 */
Chris@32 24
Chris@32 25 #include "NoteHypothesis.h"
Chris@32 26
Chris@32 27 #include <cmath>
Chris@32 28
Chris@35 29 using Vamp::RealTime;
Chris@32 30
Chris@66 31 NoteHypothesis::NoteHypothesis(float slack) :
Chris@66 32 m_state(New),
Chris@66 33 m_slack(slack)
Chris@32 34 {
Chris@32 35 }
Chris@32 36
Chris@32 37 NoteHypothesis::~NoteHypothesis()
Chris@32 38 {
Chris@32 39 }
Chris@32 40
Chris@32 41 bool
Chris@32 42 NoteHypothesis::isWithinTolerance(Estimate s) const
Chris@32 43 {
Chris@32 44 if (m_pending.empty()) {
Chris@32 45 return true;
Chris@32 46 }
Chris@32 47
Chris@32 48 // check we are within a relatively close tolerance of the last
Chris@32 49 // candidate
Chris@32 50 Estimate last = m_pending[m_pending.size()-1];
Chris@32 51 double r = s.freq / last.freq;
Chris@32 52 int cents = lrint(1200.0 * (log(r) / log(2.0)));
Chris@32 53 if (cents < -60 || cents > 60) return false;
Chris@32 54
Chris@32 55 // and within a slightly bigger tolerance of the current mean
Chris@32 56 double meanFreq = getMeanFrequency();
Chris@32 57 r = s.freq / meanFreq;
Chris@32 58 cents = lrint(1200.0 * (log(r) / log(2.0)));
Chris@32 59 if (cents < -80 || cents > 80) return false;
Chris@32 60
Chris@32 61 return true;
Chris@32 62 }
Chris@32 63
Chris@32 64 bool
Chris@32 65 NoteHypothesis::isOutOfDateFor(Estimate s) const
Chris@32 66 {
Chris@32 67 if (m_pending.empty()) return false;
Chris@32 68 return ((s.time - m_pending[m_pending.size()-1].time) >
Chris@66 69 RealTime::fromMilliseconds(m_slack));
Chris@32 70 }
Chris@32 71
Chris@32 72 bool
Chris@32 73 NoteHypothesis::isSatisfied() const
Chris@32 74 {
Chris@32 75 if (m_pending.empty()) return false;
Chris@32 76
Chris@32 77 double meanConfidence = 0.0;
Chris@32 78 for (int i = 0; i < (int)m_pending.size(); ++i) {
Chris@32 79 meanConfidence += m_pending[i].confidence;
Chris@32 80 }
Chris@32 81 meanConfidence /= m_pending.size();
Chris@32 82
Chris@59 83 int lengthRequired = 100;
Chris@32 84 if (meanConfidence > 0.0) {
Chris@32 85 lengthRequired = int(2.0 / meanConfidence + 0.5);
Chris@32 86 }
Chris@32 87
Chris@32 88 return ((int)m_pending.size() > lengthRequired);
Chris@32 89 }
Chris@32 90
Chris@32 91 bool
Chris@32 92 NoteHypothesis::accept(Estimate s)
Chris@32 93 {
Chris@32 94 bool accept = false;
Chris@32 95
Chris@58 96 static double negligibleConfidence = 0.0001;
Chris@58 97
Chris@58 98 if (s.confidence < negligibleConfidence) {
Chris@58 99 // avoid piling up a lengthy sequence of estimates that are
Chris@58 100 // all acceptable but are in total not enough to cause us to
Chris@58 101 // be satisfied
Chris@60 102 if (m_pending.empty()) {
Chris@60 103 m_state = Rejected;
Chris@60 104 }
Chris@58 105 return false;
Chris@58 106 }
Chris@58 107
Chris@32 108 switch (m_state) {
Chris@32 109
Chris@32 110 case New:
Chris@32 111 m_state = Provisional;
Chris@32 112 accept = true;
Chris@32 113 break;
Chris@32 114
Chris@32 115 case Provisional:
Chris@32 116 if (isOutOfDateFor(s)) {
Chris@32 117 m_state = Rejected;
Chris@32 118 } else if (isWithinTolerance(s)) {
Chris@32 119 accept = true;
Chris@32 120 }
Chris@32 121 break;
Chris@32 122
Chris@32 123 case Satisfied:
Chris@32 124 if (isOutOfDateFor(s)) {
Chris@32 125 m_state = Expired;
Chris@32 126 } else if (isWithinTolerance(s)) {
Chris@32 127 accept = true;
Chris@32 128 }
Chris@32 129 break;
Chris@32 130
Chris@32 131 case Rejected:
Chris@32 132 break;
Chris@32 133
Chris@32 134 case Expired:
Chris@32 135 break;
Chris@32 136 }
Chris@32 137
Chris@32 138 if (accept) {
Chris@32 139 m_pending.push_back(s);
Chris@32 140 if (m_state == Provisional && isSatisfied()) {
Chris@32 141 m_state = Satisfied;
Chris@32 142 }
Chris@32 143 }
Chris@32 144
Chris@32 145 return accept;
Chris@32 146 }
Chris@32 147
Chris@32 148 NoteHypothesis::State
Chris@32 149 NoteHypothesis::getState() const
Chris@32 150 {
Chris@32 151 return m_state;
Chris@32 152 }
Chris@32 153
Chris@32 154 NoteHypothesis::Estimates
Chris@32 155 NoteHypothesis::getAcceptedEstimates() const
Chris@32 156 {
Chris@32 157 if (m_state == Satisfied || m_state == Expired) {
Chris@32 158 return m_pending;
Chris@32 159 } else {
Chris@32 160 return Estimates();
Chris@32 161 }
Chris@32 162 }
Chris@32 163
Chris@52 164 RealTime
Chris@52 165 NoteHypothesis::getStartTime() const
Chris@52 166 {
Chris@52 167 if (!(m_state == Satisfied || m_state == Expired)) {
Chris@52 168 return RealTime::zeroTime;
Chris@52 169 } else {
Chris@52 170 return m_pending.begin()->time;
Chris@52 171 }
Chris@52 172 }
Chris@52 173
Chris@32 174 double
Chris@32 175 NoteHypothesis::getMeanFrequency() const
Chris@32 176 {
Chris@32 177 double acc = 0.0;
Chris@34 178 if (m_pending.empty()) return acc;
Chris@32 179 for (int i = 0; i < (int)m_pending.size(); ++i) {
Chris@32 180 acc += m_pending[i].freq;
Chris@32 181 }
Chris@32 182 acc /= m_pending.size();
Chris@32 183 return acc;
Chris@32 184 }
Chris@32 185
Chris@32 186 NoteHypothesis::Note
Chris@32 187 NoteHypothesis::getAveragedNote() const
Chris@32 188 {
Chris@32 189 Note n;
Chris@32 190
Chris@32 191 if (!(m_state == Satisfied || m_state == Expired)) {
Chris@32 192 n.freq = 0.0;
Chris@32 193 n.time = RealTime::zeroTime;
Chris@32 194 n.duration = RealTime::zeroTime;
Chris@32 195 return n;
Chris@32 196 }
Chris@32 197
Chris@32 198 n.time = m_pending.begin()->time;
Chris@32 199
Chris@32 200 Estimates::const_iterator i = m_pending.end();
Chris@32 201 --i;
Chris@32 202 n.duration = i->time - n.time;
Chris@32 203
Chris@32 204 // just mean frequency for now, but this isn't at all right perceptually
Chris@32 205 n.freq = getMeanFrequency();
Chris@32 206
Chris@32 207 return n;
Chris@32 208 }
Chris@32 209