comparison NoteHypothesis.cpp @ 32:c88a9972975b

Add pitch note-hypothesis class
author Chris Cannam
date Fri, 13 Jul 2012 21:40:50 +0100
parents
children 3fb9c657d86b
comparison
equal deleted inserted replaced
31:2c175adf8736 32:c88a9972975b
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 /* Copyright Chris Cannam - All Rights Reserved */
3
4 #include "NoteHypothesis.h"
5
6 #include <cmath>
7
8 #include "system/sysutils.h"
9
10 namespace Turbot {
11
12 NoteHypothesis::NoteHypothesis()
13 {
14 m_state = New;
15 }
16
17 NoteHypothesis::~NoteHypothesis()
18 {
19 }
20
21 bool
22 NoteHypothesis::isWithinTolerance(Estimate s) const
23 {
24 if (m_pending.empty()) {
25 return true;
26 }
27
28 // check we are within a relatively close tolerance of the last
29 // candidate
30 Estimate last = m_pending[m_pending.size()-1];
31 double r = s.freq / last.freq;
32 int cents = lrint(1200.0 * (log(r) / log(2.0)));
33 if (cents < -60 || cents > 60) return false;
34
35 // and within a slightly bigger tolerance of the current mean
36 double meanFreq = getMeanFrequency();
37 r = s.freq / meanFreq;
38 cents = lrint(1200.0 * (log(r) / log(2.0)));
39 if (cents < -80 || cents > 80) return false;
40
41 return true;
42 }
43
44 bool
45 NoteHypothesis::isOutOfDateFor(Estimate s) const
46 {
47 if (m_pending.empty()) return false;
48
49 return ((s.time - m_pending[m_pending.size()-1].time) >
50 RealTime::fromMilliseconds(40));
51 }
52
53 bool
54 NoteHypothesis::isSatisfied() const
55 {
56 if (m_pending.empty()) return false;
57
58 double meanConfidence = 0.0;
59 for (int i = 0; i < (int)m_pending.size(); ++i) {
60 meanConfidence += m_pending[i].confidence;
61 }
62 meanConfidence /= m_pending.size();
63
64 int lengthRequired = 10000;
65 if (meanConfidence > 0.0) {
66 lengthRequired = int(2.0 / meanConfidence + 0.5);
67 }
68
69 return ((int)m_pending.size() > lengthRequired);
70 }
71
72 bool
73 NoteHypothesis::accept(Estimate s)
74 {
75 bool accept = false;
76
77 switch (m_state) {
78
79 case New:
80 m_state = Provisional;
81 accept = true;
82 break;
83
84 case Provisional:
85 if (isOutOfDateFor(s)) {
86 m_state = Rejected;
87 } else if (isWithinTolerance(s)) {
88 accept = true;
89 }
90 break;
91
92 case Satisfied:
93 if (isOutOfDateFor(s)) {
94 m_state = Expired;
95 } else if (isWithinTolerance(s)) {
96 accept = true;
97 }
98 break;
99
100 case Rejected:
101 break;
102
103 case Expired:
104 break;
105 }
106
107 if (accept) {
108 m_pending.push_back(s);
109 if (m_state == Provisional && isSatisfied()) {
110 m_state = Satisfied;
111 }
112 }
113
114 return accept;
115 }
116
117 NoteHypothesis::State
118 NoteHypothesis::getState() const
119 {
120 return m_state;
121 }
122
123 NoteHypothesis::Estimates
124 NoteHypothesis::getAcceptedEstimates() const
125 {
126 if (m_state == Satisfied || m_state == Expired) {
127 return m_pending;
128 } else {
129 return Estimates();
130 }
131 }
132
133 double
134 NoteHypothesis::getMeanFrequency() const
135 {
136 double acc = 0.0;
137 for (int i = 0; i < (int)m_pending.size(); ++i) {
138 acc += m_pending[i].freq;
139 }
140 acc /= m_pending.size();
141 return acc;
142 }
143
144 NoteHypothesis::Note
145 NoteHypothesis::getAveragedNote() const
146 {
147 Note n;
148
149 if (!(m_state == Satisfied || m_state == Expired)) {
150 n.freq = 0.0;
151 n.time = RealTime::zeroTime;
152 n.duration = RealTime::zeroTime;
153 return n;
154 }
155
156 n.time = m_pending.begin()->time;
157
158 Estimates::const_iterator i = m_pending.end();
159 --i;
160 n.duration = i->time - n.time;
161
162 // just mean frequency for now, but this isn't at all right perceptually
163 n.freq = getMeanFrequency();
164
165 return n;
166 }
167
168 }
169