annotate NoteHypothesis.cpp @ 75:84d1a0647ce5 tip

Split out COPYING from README
author Chris Cannam
date Fri, 06 Mar 2020 11:01:53 +0000
parents c06fe5350b34
children 7ad142c710c6
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@32 31 NoteHypothesis::NoteHypothesis()
Chris@32 32 {
Chris@32 33 m_state = New;
Chris@32 34 }
Chris@32 35
Chris@32 36 NoteHypothesis::~NoteHypothesis()
Chris@32 37 {
Chris@32 38 }
Chris@32 39
Chris@32 40 bool
Chris@32 41 NoteHypothesis::isWithinTolerance(Estimate s) const
Chris@32 42 {
Chris@32 43 if (m_pending.empty()) {
Chris@32 44 return true;
Chris@32 45 }
Chris@32 46
Chris@32 47 // check we are within a relatively close tolerance of the last
Chris@32 48 // candidate
Chris@32 49 Estimate last = m_pending[m_pending.size()-1];
Chris@32 50 double r = s.freq / last.freq;
Chris@32 51 int cents = lrint(1200.0 * (log(r) / log(2.0)));
Chris@32 52 if (cents < -60 || cents > 60) return false;
Chris@32 53
Chris@32 54 // and within a slightly bigger tolerance of the current mean
Chris@32 55 double meanFreq = getMeanFrequency();
Chris@32 56 r = s.freq / meanFreq;
Chris@32 57 cents = lrint(1200.0 * (log(r) / log(2.0)));
Chris@32 58 if (cents < -80 || cents > 80) return false;
Chris@32 59
Chris@32 60 return true;
Chris@32 61 }
Chris@32 62
Chris@32 63 bool
Chris@32 64 NoteHypothesis::isOutOfDateFor(Estimate s) const
Chris@32 65 {
Chris@32 66 if (m_pending.empty()) return false;
Chris@32 67 return ((s.time - m_pending[m_pending.size()-1].time) >
Chris@32 68 RealTime::fromMilliseconds(40));
Chris@32 69 }
Chris@32 70
Chris@32 71 bool
Chris@32 72 NoteHypothesis::isSatisfied() const
Chris@32 73 {
Chris@32 74 if (m_pending.empty()) return false;
Chris@32 75
Chris@32 76 double meanConfidence = 0.0;
Chris@32 77 for (int i = 0; i < (int)m_pending.size(); ++i) {
Chris@32 78 meanConfidence += m_pending[i].confidence;
Chris@32 79 }
Chris@32 80 meanConfidence /= m_pending.size();
Chris@32 81
Chris@59 82 int lengthRequired = 100;
Chris@32 83 if (meanConfidence > 0.0) {
Chris@32 84 lengthRequired = int(2.0 / meanConfidence + 0.5);
Chris@32 85 }
Chris@32 86
Chris@32 87 return ((int)m_pending.size() > lengthRequired);
Chris@32 88 }
Chris@32 89
Chris@32 90 bool
Chris@32 91 NoteHypothesis::accept(Estimate s)
Chris@32 92 {
Chris@32 93 bool accept = false;
Chris@32 94
Chris@58 95 static double negligibleConfidence = 0.0001;
Chris@58 96
Chris@58 97 if (s.confidence < negligibleConfidence) {
Chris@58 98 // avoid piling up a lengthy sequence of estimates that are
Chris@58 99 // all acceptable but are in total not enough to cause us to
Chris@58 100 // be satisfied
Chris@60 101 if (m_pending.empty()) {
Chris@60 102 m_state = Rejected;
Chris@60 103 }
Chris@58 104 return false;
Chris@58 105 }
Chris@58 106
Chris@32 107 switch (m_state) {
Chris@32 108
Chris@32 109 case New:
Chris@32 110 m_state = Provisional;
Chris@32 111 accept = true;
Chris@32 112 break;
Chris@32 113
Chris@32 114 case Provisional:
Chris@32 115 if (isOutOfDateFor(s)) {
Chris@32 116 m_state = Rejected;
Chris@32 117 } else if (isWithinTolerance(s)) {
Chris@32 118 accept = true;
Chris@32 119 }
Chris@32 120 break;
Chris@32 121
Chris@32 122 case Satisfied:
Chris@32 123 if (isOutOfDateFor(s)) {
Chris@32 124 m_state = Expired;
Chris@32 125 } else if (isWithinTolerance(s)) {
Chris@32 126 accept = true;
Chris@32 127 }
Chris@32 128 break;
Chris@32 129
Chris@32 130 case Rejected:
Chris@32 131 break;
Chris@32 132
Chris@32 133 case Expired:
Chris@32 134 break;
Chris@32 135 }
Chris@32 136
Chris@32 137 if (accept) {
Chris@32 138 m_pending.push_back(s);
Chris@32 139 if (m_state == Provisional && isSatisfied()) {
Chris@32 140 m_state = Satisfied;
Chris@32 141 }
Chris@32 142 }
Chris@32 143
Chris@32 144 return accept;
Chris@32 145 }
Chris@32 146
Chris@32 147 NoteHypothesis::State
Chris@32 148 NoteHypothesis::getState() const
Chris@32 149 {
Chris@32 150 return m_state;
Chris@32 151 }
Chris@32 152
Chris@32 153 NoteHypothesis::Estimates
Chris@32 154 NoteHypothesis::getAcceptedEstimates() const
Chris@32 155 {
Chris@32 156 if (m_state == Satisfied || m_state == Expired) {
Chris@32 157 return m_pending;
Chris@32 158 } else {
Chris@32 159 return Estimates();
Chris@32 160 }
Chris@32 161 }
Chris@32 162
Chris@52 163 RealTime
Chris@52 164 NoteHypothesis::getStartTime() const
Chris@52 165 {
Chris@52 166 if (!(m_state == Satisfied || m_state == Expired)) {
Chris@52 167 return RealTime::zeroTime;
Chris@52 168 } else {
Chris@52 169 return m_pending.begin()->time;
Chris@52 170 }
Chris@52 171 }
Chris@52 172
Chris@32 173 double
Chris@32 174 NoteHypothesis::getMeanFrequency() const
Chris@32 175 {
Chris@32 176 double acc = 0.0;
Chris@34 177 if (m_pending.empty()) return acc;
Chris@32 178 for (int i = 0; i < (int)m_pending.size(); ++i) {
Chris@32 179 acc += m_pending[i].freq;
Chris@32 180 }
Chris@32 181 acc /= m_pending.size();
Chris@32 182 return acc;
Chris@32 183 }
Chris@32 184
Chris@32 185 NoteHypothesis::Note
Chris@32 186 NoteHypothesis::getAveragedNote() const
Chris@32 187 {
Chris@32 188 Note n;
Chris@32 189
Chris@32 190 if (!(m_state == Satisfied || m_state == Expired)) {
Chris@32 191 n.freq = 0.0;
Chris@32 192 n.time = RealTime::zeroTime;
Chris@32 193 n.duration = RealTime::zeroTime;
Chris@32 194 return n;
Chris@32 195 }
Chris@32 196
Chris@32 197 n.time = m_pending.begin()->time;
Chris@32 198
Chris@32 199 Estimates::const_iterator i = m_pending.end();
Chris@32 200 --i;
Chris@32 201 n.duration = i->time - n.time;
Chris@32 202
Chris@32 203 // just mean frequency for now, but this isn't at all right perceptually
Chris@32 204 n.freq = getMeanFrequency();
Chris@32 205
Chris@32 206 return n;
Chris@32 207 }
Chris@32 208