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