Mercurial > hg > silvet
comparison src/NoteHypothesis.cpp @ 181:10e7c3ff575e noteagent
Experimental branch toward note-agent stuff (not actually plumbed in yet)
author | Chris Cannam |
---|---|
date | Fri, 23 May 2014 12:40:18 +0100 |
parents | |
children | e1718e64a921 |
comparison
equal
deleted
inserted
replaced
179:825193ef09d2 | 181:10e7c3ff575e |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 | |
3 /* | |
4 Silvet | |
5 | |
6 A Vamp plugin for note transcription. | |
7 Centre for Digital Music, Queen Mary University of London. | |
8 This file Copyright 2012 Chris Cannam. | |
9 | |
10 This program is free software; you can redistribute it and/or | |
11 modify it under the terms of the GNU General Public License as | |
12 published by the Free Software Foundation; either version 2 of the | |
13 License, or (at your option) any later version. See the file | |
14 COPYING included with this distribution for more information. | |
15 */ | |
16 | |
17 #include "NoteHypothesis.h" | |
18 #include "AgentFeederPoly.h" | |
19 | |
20 #include <cmath> | |
21 #include <cassert> | |
22 | |
23 #include <map> | |
24 | |
25 using Vamp::RealTime; | |
26 | |
27 //#define DEBUG_NOTE_HYPOTHESIS 1 | |
28 | |
29 NoteHypothesis::NoteHypothesis() | |
30 { | |
31 m_state = New; | |
32 } | |
33 | |
34 NoteHypothesis::~NoteHypothesis() | |
35 { | |
36 } | |
37 | |
38 bool | |
39 NoteHypothesis::isWithinTolerance(Observation s) const | |
40 { | |
41 if (m_pending.empty()) { | |
42 return true; | |
43 } | |
44 | |
45 // check we are within a relatively close tolerance of the last | |
46 // candidate | |
47 Observations::const_iterator i = m_pending.end(); | |
48 --i; | |
49 Observation last = *i; | |
50 double r = s.value / last.value; | |
51 int cents = lrint(1200.0 * (log(r) / log(2.0))); | |
52 #ifdef DEBUG_NOTE_HYPOTHESIS | |
53 std::cerr << "isWithinTolerance: this " << s.value << " is " << cents | |
54 << " cents from prior " << last.value << std::endl; | |
55 #endif | |
56 if (cents < -60 || cents > 60) return false; | |
57 | |
58 // and within a slightly bigger tolerance of the current mean | |
59 double meanFreq = getMeanFrequency(); | |
60 r = s.value / meanFreq; | |
61 cents = lrint(1200.0 * (log(r) / log(2.0))); | |
62 #ifdef DEBUG_NOTE_HYPOTHESIS | |
63 std::cerr << "isWithinTolerance: this " << s.value << " is " << cents | |
64 << " cents from mean " << meanFreq << std::endl; | |
65 #endif | |
66 if (cents < -80 || cents > 80) return false; | |
67 | |
68 return true; | |
69 } | |
70 | |
71 bool | |
72 NoteHypothesis::isOutOfDateFor(Observation s) const | |
73 { | |
74 if (m_pending.empty()) return false; | |
75 | |
76 Observations::const_iterator i = m_pending.end(); | |
77 --i; | |
78 Observation last = *i; | |
79 | |
80 #ifdef DEBUG_NOTE_HYPOTHESIS | |
81 std::cerr << "isOutOfDateFor: this " << s.time << " is " | |
82 << (s.time - last.time) << " from last " << last.time | |
83 << " (threshold " << RealTime::fromMilliseconds(40) << ")" | |
84 << std::endl; | |
85 #endif | |
86 | |
87 return ((s.time - last.time) > RealTime::fromMilliseconds(40)); | |
88 } | |
89 | |
90 bool | |
91 NoteHypothesis::isSatisfied() const | |
92 { | |
93 if (m_pending.empty()) return false; | |
94 | |
95 double meanConfidence = 0.0; | |
96 for (Observations::const_iterator i = m_pending.begin(); | |
97 i != m_pending.end(); ++i) { | |
98 meanConfidence += i->confidence; | |
99 } | |
100 meanConfidence /= m_pending.size(); | |
101 | |
102 int lengthRequired = 100; | |
103 if (meanConfidence > 0.0) { | |
104 lengthRequired = int(2.0 / meanConfidence + 0.5); | |
105 } | |
106 // if (lengthRequired < 1) lengthRequired = 1; | |
107 | |
108 #ifdef DEBUG_NOTE_HYPOTHESIS | |
109 std::cerr << "meanConfidence " << meanConfidence << ", lengthRequired " << lengthRequired << std::endl; | |
110 #endif | |
111 | |
112 return ((int)m_pending.size() > lengthRequired); | |
113 } | |
114 | |
115 bool | |
116 NoteHypothesis::accept(Observation s) | |
117 { | |
118 bool accept = false; | |
119 | |
120 #ifdef DEBUG_NOTE_HYPOTHESIS | |
121 std::cerr << "NoteHypothesis[" << this << "]::accept: state " << m_state << "..." << std::endl; | |
122 #endif | |
123 | |
124 static double negligibleConfidence = 0.0001; | |
125 | |
126 if (s.confidence < negligibleConfidence) { | |
127 // avoid piling up a lengthy sequence of estimates that are | |
128 // all acceptable but are in total not enough to cause us to | |
129 // be satisfied | |
130 if (m_state == New) { | |
131 m_state = Rejected; | |
132 } | |
133 return false; | |
134 } | |
135 | |
136 switch (m_state) { | |
137 | |
138 case New: | |
139 m_state = Provisional; | |
140 accept = true; | |
141 break; | |
142 | |
143 case Provisional: | |
144 if (isOutOfDateFor(s)) { | |
145 m_state = Rejected; | |
146 } else if (isWithinTolerance(s)) { | |
147 accept = true; | |
148 } | |
149 break; | |
150 | |
151 case Satisfied: | |
152 if (isOutOfDateFor(s)) { | |
153 m_state = Expired; | |
154 } else if (isWithinTolerance(s)) { | |
155 accept = true; | |
156 } | |
157 break; | |
158 | |
159 case Rejected: | |
160 break; | |
161 | |
162 case Expired: | |
163 break; | |
164 } | |
165 | |
166 if (accept) { | |
167 m_pending.insert(s); | |
168 if (m_state == Provisional && isSatisfied()) { | |
169 m_state = Satisfied; | |
170 } | |
171 } | |
172 | |
173 #ifdef DEBUG_NOTE_HYPOTHESIS | |
174 std::cerr << "... -> " << m_state << " (pending: " << m_pending.size() << ")" << std::endl; | |
175 #endif | |
176 | |
177 return accept; | |
178 } | |
179 | |
180 NoteHypothesis::State | |
181 NoteHypothesis::getState() const | |
182 { | |
183 return m_state; | |
184 } | |
185 | |
186 NoteHypothesis::Observations | |
187 NoteHypothesis::getAcceptedObservations() const | |
188 { | |
189 if (m_state == Satisfied || m_state == Expired) { | |
190 return m_pending; | |
191 } else { | |
192 return Observations(); | |
193 } | |
194 } | |
195 | |
196 double | |
197 NoteHypothesis::getMeanFrequency() const | |
198 { | |
199 double acc = 0.0; | |
200 if (m_pending.empty()) return acc; | |
201 for (Observations::const_iterator i = m_pending.begin(); | |
202 i != m_pending.end(); ++i) { | |
203 acc += i->value; | |
204 } | |
205 acc /= m_pending.size(); | |
206 return acc; | |
207 } | |
208 | |
209 NoteHypothesis::Note | |
210 NoteHypothesis::getAveragedNote() const | |
211 { | |
212 Note n; | |
213 | |
214 n.time = getStartTime(); | |
215 n.duration = getDuration(); | |
216 | |
217 // just mean frequency for now, but this isn't at all right perceptually | |
218 n.freq = getMeanFrequency(); | |
219 | |
220 return n; | |
221 } | |
222 | |
223 RealTime | |
224 NoteHypothesis::getStartTime() const | |
225 { | |
226 if (!(m_state == Satisfied || m_state == Expired)) { | |
227 return RealTime::zeroTime; | |
228 } else { | |
229 return m_pending.begin()->time; | |
230 } | |
231 } | |
232 | |
233 RealTime | |
234 NoteHypothesis::getDuration() const | |
235 { | |
236 //!!! test this! it is wrong | |
237 if (!(m_state == Satisfied || m_state == Expired)) { | |
238 return RealTime::zeroTime; | |
239 } else { | |
240 RealTime start = m_pending.begin()->time; | |
241 Observations::const_iterator i = m_pending.end(); | |
242 --i; | |
243 return i->time - start; | |
244 } | |
245 } | |
246 | |
247 std::vector<double> | |
248 NoteHypothesis::sample(const std::set<NoteHypothesis> ¬es, | |
249 RealTime startTime, | |
250 RealTime endTime, | |
251 RealTime interval) | |
252 { | |
253 //!!! where should this live? in AgentHypothesis? a Feeder class? | |
254 | |
255 assert(interval > RealTime::zeroTime); | |
256 | |
257 Observations obs = flatten(notes); | |
258 Observations::const_iterator oi = obs.begin(); | |
259 | |
260 // std::cerr << "sample: start " << startTime << " end " << endTime << " interval " << interval << std::endl; | |
261 | |
262 // std::cerr << "sample: flatten gives " << obs.size() << " observations" << std::endl; | |
263 | |
264 std::vector<double> samples; | |
265 | |
266 RealTime obsInterval; | |
267 | |
268 RealTime t = startTime; | |
269 | |
270 while (oi != obs.end()) { | |
271 | |
272 Observation o = *oi; | |
273 | |
274 if (obsInterval == RealTime()) { | |
275 //!!! should pull out a function to establish this from the list | |
276 Observations::const_iterator oj = oi; | |
277 ++oj; | |
278 if (oj != obs.end()) { | |
279 obsInterval = oj->time - o.time; | |
280 } | |
281 } | |
282 | |
283 // std::cerr << "t = " << t << ", o.time = " << o.time << ", interval = " << interval << ", obsInterval = " << obsInterval << std::endl; | |
284 | |
285 if (t > endTime) { | |
286 break; | |
287 } else if (o.time > t) { | |
288 samples.push_back(0.0); | |
289 t = t + interval; | |
290 } else if (o.time + obsInterval <= t) { | |
291 ++oi; | |
292 } else { | |
293 samples.push_back(o.value); | |
294 t = t + interval; | |
295 } | |
296 } | |
297 | |
298 while (1) { | |
299 // std::cerr << "t = " << t << std::endl; | |
300 if (t > endTime) { | |
301 break; | |
302 } else { | |
303 samples.push_back(0.0); | |
304 t = t + interval; | |
305 } | |
306 } | |
307 | |
308 return samples; | |
309 } | |
310 | |
311 std::vector<double> | |
312 NoteHypothesis::winnow(const Observations &obs, | |
313 RealTime startTime, | |
314 RealTime endTime, | |
315 RealTime interval) | |
316 { | |
317 AgentFeederPoly<NoteHypothesis> feeder; | |
318 | |
319 #ifdef DEBUG_NOTE_HYPOTHESIS | |
320 std::cerr << "winnow: " << obs.size() << " input observations" | |
321 << std::endl; | |
322 int nonzero = 0; | |
323 #endif | |
324 | |
325 for (Observations::const_iterator i = obs.begin(); | |
326 i != obs.end(); ++i) { | |
327 if (i->value != 0.0) { // 0.0 is a special unvoiced value | |
328 feeder.feed(*i); | |
329 #ifdef DEBUG_NOTE_HYPOTHESIS | |
330 std::cerr << i->value << " "; | |
331 ++nonzero; | |
332 if (nonzero % 6 == 0) { | |
333 std::cerr << std::endl; | |
334 } | |
335 #endif | |
336 } | |
337 } | |
338 | |
339 #ifdef DEBUG_NOTE_HYPOTHESIS | |
340 std::cerr << "winnow: " << nonzero << " non-zero" | |
341 << std::endl; | |
342 #endif | |
343 | |
344 feeder.finish(); | |
345 | |
346 AgentFeederPoly<NoteHypothesis>::Hypotheses accepted = | |
347 feeder.getAcceptedHypotheses(); | |
348 | |
349 #ifdef DEBUG_NOTE_HYPOTHESIS | |
350 std::cerr << "winnow: " << accepted.size() << " accepted hypotheses" | |
351 << std::endl; | |
352 #endif | |
353 | |
354 return NoteHypothesis::sample(accepted, startTime, endTime, interval); | |
355 } | |
356 |