annotate src/NoteHypothesis.cpp @ 202:f6cdb050614b noteagent

Merge from default branch
author Chris Cannam
date Wed, 04 Jun 2014 12:11:25 +0100
parents 462b165c8c0f
children
rev   line source
Chris@181 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@181 2
Chris@181 3 /*
Chris@181 4 Silvet
Chris@181 5
Chris@181 6 A Vamp plugin for note transcription.
Chris@181 7 Centre for Digital Music, Queen Mary University of London.
Chris@181 8 This file Copyright 2012 Chris Cannam.
Chris@181 9
Chris@181 10 This program is free software; you can redistribute it and/or
Chris@181 11 modify it under the terms of the GNU General Public License as
Chris@181 12 published by the Free Software Foundation; either version 2 of the
Chris@181 13 License, or (at your option) any later version. See the file
Chris@181 14 COPYING included with this distribution for more information.
Chris@181 15 */
Chris@181 16
Chris@181 17 #include "NoteHypothesis.h"
Chris@181 18 #include "AgentFeederPoly.h"
Chris@181 19
Chris@181 20 #include <cmath>
Chris@181 21 #include <cassert>
Chris@181 22
Chris@181 23 #include <map>
Chris@184 24 #include <algorithm>
Chris@181 25
Chris@181 26 using Vamp::RealTime;
Chris@181 27
Chris@184 28 using std::cerr;
Chris@184 29 using std::endl;
Chris@184 30
Chris@188 31 //#define DEBUG_NOTE_HYPOTHESIS 1
Chris@181 32
Chris@181 33 NoteHypothesis::NoteHypothesis()
Chris@181 34 {
Chris@181 35 m_state = New;
Chris@181 36 }
Chris@181 37
Chris@181 38 NoteHypothesis::~NoteHypothesis()
Chris@181 39 {
Chris@181 40 }
Chris@181 41
Chris@181 42 bool
Chris@181 43 NoteHypothesis::isWithinTolerance(Observation s) const
Chris@181 44 {
Chris@181 45 if (m_pending.empty()) {
Chris@181 46 return true;
Chris@181 47 }
Chris@181 48
Chris@181 49 // check we are within a relatively close tolerance of the last
Chris@181 50 // candidate
Chris@181 51 Observations::const_iterator i = m_pending.end();
Chris@181 52 --i;
Chris@181 53 Observation last = *i;
Chris@181 54 double r = s.value / last.value;
Chris@181 55 int cents = lrint(1200.0 * (log(r) / log(2.0)));
Chris@181 56 #ifdef DEBUG_NOTE_HYPOTHESIS
Chris@184 57 cerr << "isWithinTolerance: this " << s.value << " is " << cents
Chris@184 58 << " cents from prior " << last.value << endl;
Chris@181 59 #endif
Chris@181 60 if (cents < -60 || cents > 60) return false;
Chris@181 61
Chris@181 62 // and within a slightly bigger tolerance of the current mean
Chris@181 63 double meanFreq = getMeanFrequency();
Chris@181 64 r = s.value / meanFreq;
Chris@181 65 cents = lrint(1200.0 * (log(r) / log(2.0)));
Chris@181 66 #ifdef DEBUG_NOTE_HYPOTHESIS
Chris@184 67 cerr << "isWithinTolerance: this " << s.value << " is " << cents
Chris@184 68 << " cents from mean " << meanFreq << endl;
Chris@181 69 #endif
Chris@181 70 if (cents < -80 || cents > 80) return false;
Chris@181 71
Chris@181 72 return true;
Chris@181 73 }
Chris@181 74
Chris@181 75 bool
Chris@181 76 NoteHypothesis::isOutOfDateFor(Observation s) const
Chris@181 77 {
Chris@181 78 if (m_pending.empty()) return false;
Chris@181 79
Chris@181 80 Observations::const_iterator i = m_pending.end();
Chris@181 81 --i;
Chris@181 82 Observation last = *i;
Chris@181 83
Chris@181 84 #ifdef DEBUG_NOTE_HYPOTHESIS
Chris@184 85 cerr << "isOutOfDateFor: this " << s.time << " is "
Chris@181 86 << (s.time - last.time) << " from last " << last.time
Chris@181 87 << " (threshold " << RealTime::fromMilliseconds(40) << ")"
Chris@184 88 << endl;
Chris@181 89 #endif
Chris@181 90
Chris@181 91 return ((s.time - last.time) > RealTime::fromMilliseconds(40));
Chris@181 92 }
Chris@181 93
Chris@181 94 bool
Chris@181 95 NoteHypothesis::isSatisfied() const
Chris@181 96 {
Chris@181 97 if (m_pending.empty()) return false;
Chris@181 98
Chris@181 99 double meanConfidence = 0.0;
Chris@181 100 for (Observations::const_iterator i = m_pending.begin();
Chris@181 101 i != m_pending.end(); ++i) {
Chris@181 102 meanConfidence += i->confidence;
Chris@181 103 }
Chris@181 104 meanConfidence /= m_pending.size();
Chris@181 105
Chris@184 106 //!!! surely this depends on the hop size?
Chris@181 107 int lengthRequired = 100;
Chris@181 108 if (meanConfidence > 0.0) {
Chris@181 109 lengthRequired = int(2.0 / meanConfidence + 0.5);
Chris@181 110 }
Chris@184 111 //!!!
Chris@184 112 lengthRequired = lengthRequired / 2;
Chris@184 113 if (lengthRequired < 1) lengthRequired = 1;
Chris@181 114
Chris@181 115 #ifdef DEBUG_NOTE_HYPOTHESIS
Chris@184 116 cerr << "meanConfidence " << meanConfidence << ", lengthRequired " << lengthRequired << endl;
Chris@181 117 #endif
Chris@181 118
Chris@181 119 return ((int)m_pending.size() > lengthRequired);
Chris@181 120 }
Chris@181 121
Chris@188 122 #ifdef DEBUG_NOTE_HYPOTHESIS
Chris@184 123 static void printState(NoteHypothesis::State s)
Chris@184 124 {
Chris@184 125 switch (s) {
Chris@184 126 case NoteHypothesis::New: cerr << "New"; break;
Chris@184 127 case NoteHypothesis::Provisional: cerr << "Provisional"; break;
Chris@184 128 case NoteHypothesis::Rejected: cerr << "Rejected"; break;
Chris@184 129 case NoteHypothesis::Satisfied: cerr << "Satisfied"; break;
Chris@184 130 case NoteHypothesis::Expired: cerr << "Expired"; break;
Chris@184 131 }
Chris@184 132 }
Chris@188 133 #endif
Chris@184 134
Chris@181 135 bool
Chris@181 136 NoteHypothesis::accept(Observation s)
Chris@181 137 {
Chris@181 138 bool accept = false;
Chris@181 139
Chris@181 140 #ifdef DEBUG_NOTE_HYPOTHESIS
Chris@184 141 cerr << "NoteHypothesis[" << this << "]::accept (value " << s.value << ", time " << s.time << ", confidence " << s.confidence << "): state ";
Chris@184 142 printState(m_state);
Chris@184 143 cerr << "..." << endl;
Chris@181 144 #endif
Chris@181 145
Chris@181 146 static double negligibleConfidence = 0.0001;
Chris@181 147
Chris@181 148 if (s.confidence < negligibleConfidence) {
Chris@181 149 // avoid piling up a lengthy sequence of estimates that are
Chris@181 150 // all acceptable but are in total not enough to cause us to
Chris@181 151 // be satisfied
Chris@181 152 if (m_state == New) {
Chris@181 153 m_state = Rejected;
Chris@181 154 }
Chris@181 155 return false;
Chris@181 156 }
Chris@181 157
Chris@181 158 switch (m_state) {
Chris@181 159
Chris@181 160 case New:
Chris@181 161 m_state = Provisional;
Chris@181 162 accept = true;
Chris@181 163 break;
Chris@181 164
Chris@181 165 case Provisional:
Chris@181 166 if (isOutOfDateFor(s)) {
Chris@181 167 m_state = Rejected;
Chris@181 168 } else if (isWithinTolerance(s)) {
Chris@181 169 accept = true;
Chris@181 170 }
Chris@181 171 break;
Chris@181 172
Chris@181 173 case Satisfied:
Chris@181 174 if (isOutOfDateFor(s)) {
Chris@181 175 m_state = Expired;
Chris@181 176 } else if (isWithinTolerance(s)) {
Chris@181 177 accept = true;
Chris@181 178 }
Chris@181 179 break;
Chris@181 180
Chris@181 181 case Rejected:
Chris@181 182 break;
Chris@181 183
Chris@181 184 case Expired:
Chris@181 185 break;
Chris@181 186 }
Chris@181 187
Chris@181 188 if (accept) {
Chris@184 189 #ifdef DEBUG_NOTE_HYPOTHESIS
Chris@184 190 cerr << "... accepting" << endl;
Chris@184 191 #endif
Chris@181 192 m_pending.insert(s);
Chris@181 193 if (m_state == Provisional && isSatisfied()) {
Chris@181 194 m_state = Satisfied;
Chris@181 195 }
Chris@184 196 } else {
Chris@184 197 #ifdef DEBUG_NOTE_HYPOTHESIS
Chris@184 198 cerr << "... not accepting" << endl;
Chris@184 199 #endif
Chris@181 200 }
Chris@181 201
Chris@181 202 #ifdef DEBUG_NOTE_HYPOTHESIS
Chris@184 203 cerr << "... -> ";
Chris@184 204 printState(m_state);
Chris@184 205 cerr << " (pending: " << m_pending.size() << ")" << endl;
Chris@181 206 #endif
Chris@181 207
Chris@181 208 return accept;
Chris@181 209 }
Chris@181 210
Chris@181 211 NoteHypothesis::State
Chris@181 212 NoteHypothesis::getState() const
Chris@181 213 {
Chris@181 214 return m_state;
Chris@181 215 }
Chris@181 216
Chris@181 217 NoteHypothesis::Observations
Chris@181 218 NoteHypothesis::getAcceptedObservations() const
Chris@181 219 {
Chris@181 220 if (m_state == Satisfied || m_state == Expired) {
Chris@181 221 return m_pending;
Chris@181 222 } else {
Chris@181 223 return Observations();
Chris@181 224 }
Chris@181 225 }
Chris@181 226
Chris@181 227 double
Chris@184 228 NoteHypothesis::getMedianFrequency() const
Chris@184 229 {
Chris@184 230 if (m_pending.empty()) return 0.0;
Chris@184 231 std::vector<double> freqs;
Chris@184 232 for (Observations::const_iterator i = m_pending.begin();
Chris@184 233 i != m_pending.end(); ++i) {
Chris@184 234 freqs.push_back(i->value);
Chris@184 235 }
Chris@184 236 std::sort(freqs.begin(), freqs.end());
Chris@184 237 return freqs[freqs.size()/2];
Chris@184 238 }
Chris@184 239
Chris@184 240 double
Chris@181 241 NoteHypothesis::getMeanFrequency() const
Chris@181 242 {
Chris@181 243 double acc = 0.0;
Chris@181 244 if (m_pending.empty()) return acc;
Chris@181 245 for (Observations::const_iterator i = m_pending.begin();
Chris@181 246 i != m_pending.end(); ++i) {
Chris@181 247 acc += i->value;
Chris@181 248 }
Chris@181 249 acc /= m_pending.size();
Chris@181 250 return acc;
Chris@181 251 }
Chris@181 252
Chris@184 253 double
Chris@184 254 NoteHypothesis::getMedianConfidence() const
Chris@184 255 {
Chris@184 256 if (m_pending.empty()) return 0.0;
Chris@184 257 std::vector<double> confs;
Chris@184 258 for (Observations::const_iterator i = m_pending.begin();
Chris@184 259 i != m_pending.end(); ++i) {
Chris@184 260 confs.push_back(i->confidence);
Chris@184 261 }
Chris@184 262 std::sort(confs.begin(), confs.end());
Chris@184 263 return confs[confs.size()/2];
Chris@184 264 }
Chris@184 265
Chris@181 266 NoteHypothesis::Note
Chris@181 267 NoteHypothesis::getAveragedNote() const
Chris@181 268 {
Chris@181 269 Note n;
Chris@181 270
Chris@181 271 n.time = getStartTime();
Chris@181 272 n.duration = getDuration();
Chris@184 273 n.freq = getMedianFrequency();
Chris@184 274 n.confidence = getMedianConfidence();
Chris@181 275
Chris@181 276 return n;
Chris@181 277 }
Chris@181 278
Chris@181 279 RealTime
Chris@181 280 NoteHypothesis::getStartTime() const
Chris@181 281 {
Chris@181 282 if (!(m_state == Satisfied || m_state == Expired)) {
Chris@181 283 return RealTime::zeroTime;
Chris@181 284 } else {
Chris@181 285 return m_pending.begin()->time;
Chris@181 286 }
Chris@181 287 }
Chris@181 288
Chris@181 289 RealTime
Chris@181 290 NoteHypothesis::getDuration() const
Chris@181 291 {
Chris@181 292 //!!! test this! it is wrong
Chris@181 293 if (!(m_state == Satisfied || m_state == Expired)) {
Chris@181 294 return RealTime::zeroTime;
Chris@181 295 } else {
Chris@181 296 RealTime start = m_pending.begin()->time;
Chris@181 297 Observations::const_iterator i = m_pending.end();
Chris@181 298 --i;
Chris@181 299 return i->time - start;
Chris@181 300 }
Chris@181 301 }