To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / test / TestNoteHypothesis.cpp @ 58:9f50a5876dd3
History | View | Annotate | Download (12.3 KB)
| 1 | 33:5f2a57b1a75a | Chris | /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
|---|---|---|---|
| 2 | 37:7bf67d2dfc30 | Chris | /*
|
| 3 | This file is Copyright (c) 2012 Chris Cannam
|
||
| 4 | |||
| 5 | Permission is hereby granted, free of charge, to any person
|
||
| 6 | obtaining a copy of this software and associated documentation
|
||
| 7 | files (the "Software"), to deal in the Software without
|
||
| 8 | restriction, including without limitation the rights to use, copy,
|
||
| 9 | modify, merge, publish, distribute, sublicense, and/or sell copies
|
||
| 10 | of the Software, and to permit persons to whom the Software is
|
||
| 11 | furnished to do so, subject to the following conditions:
|
||
| 12 | |||
| 13 | The above copyright notice and this permission notice shall be
|
||
| 14 | included in all copies or substantial portions of the Software.
|
||
| 15 | |||
| 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
| 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
| 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
| 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
||
| 20 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||
| 21 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||
| 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
| 23 | */
|
||
| 24 | 33:5f2a57b1a75a | Chris | |
| 25 | 38:944898c2e14e | Chris | #include "NoteHypothesis.h" |
| 26 | 33:5f2a57b1a75a | Chris | |
| 27 | 38:944898c2e14e | Chris | std::ostream &operator<<(std::ostream &out, const NoteHypothesis::Estimate &n) |
| 28 | 33:5f2a57b1a75a | Chris | {
|
| 29 | 38:944898c2e14e | Chris | return out << "[" << n.freq << "@" << n.time << ":" << n.confidence << "]" << std::endl; |
| 30 | 33:5f2a57b1a75a | Chris | } |
| 31 | |||
| 32 | 38:944898c2e14e | Chris | std::ostream &operator<<(std::ostream &out, const NoteHypothesis::Estimates &e) |
| 33 | {
|
||
| 34 | out << "( ";
|
||
| 35 | for (int i = 0; i < (int)e.size(); ++i) out << e[i] << "; "; |
||
| 36 | out << " )";
|
||
| 37 | return out;
|
||
| 38 | } |
||
| 39 | |||
| 40 | std::ostream &operator<<(std::ostream &out, const NoteHypothesis::Note &n) |
||
| 41 | {
|
||
| 42 | return out << "[" << n.freq << "@" << n.time << ":" << n.duration << "]" << std::endl; |
||
| 43 | } |
||
| 44 | |||
| 45 | #define BOOST_TEST_DYN_LINK
|
||
| 46 | #define BOOST_TEST_MAIN
|
||
| 47 | |||
| 48 | #include <boost/test/unit_test.hpp> |
||
| 49 | |||
| 50 | using Vamp::RealTime;
|
||
| 51 | |||
| 52 | BOOST_AUTO_TEST_SUITE(TestNoteHypothesis) |
||
| 53 | |||
| 54 | BOOST_AUTO_TEST_CASE(emptyAccept) |
||
| 55 | {
|
||
| 56 | 58:9f50a5876dd3 | Chris | // An empty hypothesis should accept any estimate with a
|
| 57 | // non-negligible confidence, and enter provisional state
|
||
| 58 | 38:944898c2e14e | Chris | NoteHypothesis h; |
| 59 | 58:9f50a5876dd3 | Chris | NoteHypothesis::Estimate e; // default estimate has confidence 1
|
| 60 | 38:944898c2e14e | Chris | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New); |
| 61 | BOOST_CHECK(h.accept(e)); |
||
| 62 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 63 | } |
||
| 64 | 58:9f50a5876dd3 | Chris | |
| 65 | BOOST_AUTO_TEST_CASE(noConfidence) |
||
| 66 | {
|
||
| 67 | // A hypothesis should reject any estimate that has a negligible
|
||
| 68 | // confidence
|
||
| 69 | NoteHypothesis h; |
||
| 70 | NoteHypothesis::Estimate e; |
||
| 71 | e.confidence = 0;
|
||
| 72 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New); |
||
| 73 | BOOST_CHECK(!h.accept(e)); |
||
| 74 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Rejected); |
||
| 75 | } |
||
| 76 | 45:8db4a1f096f0 | Chris | |
| 77 | BOOST_AUTO_TEST_CASE(tooSlow) |
||
| 78 | {
|
||
| 79 | // Having accepted a first estimate, a hypothesis should reject a
|
||
| 80 | // second (and enter rejected state) if there is too long a gap
|
||
| 81 | // between them for them to belong to a single note
|
||
| 82 | NoteHypothesis h; |
||
| 83 | NoteHypothesis::Estimate e1(500, RealTime::fromMilliseconds(0), 1); |
||
| 84 | NoteHypothesis::Estimate e2(500, RealTime::fromMilliseconds(50), 1); |
||
| 85 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New); |
||
| 86 | BOOST_CHECK(h.accept(e1)); |
||
| 87 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 88 | BOOST_CHECK(!h.accept(e2)); |
||
| 89 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Rejected); |
||
| 90 | } |
||
| 91 | 38:944898c2e14e | Chris | |
| 92 | BOOST_AUTO_TEST_CASE(simpleSatisfy) |
||
| 93 | {
|
||
| 94 | 45:8db4a1f096f0 | Chris | // A hypothesis should enter satisfied state after accepting three
|
| 95 | // consistent estimates, and then remain satisfied while accepting
|
||
| 96 | // further consistent estimates
|
||
| 97 | NoteHypothesis h; |
||
| 98 | NoteHypothesis::Estimate e1(500, RealTime::fromMilliseconds(0), 1); |
||
| 99 | NoteHypothesis::Estimate e2(500, RealTime::fromMilliseconds(10), 1); |
||
| 100 | NoteHypothesis::Estimate e3(500, RealTime::fromMilliseconds(20), 1); |
||
| 101 | NoteHypothesis::Estimate e4(500, RealTime::fromMilliseconds(30), 1); |
||
| 102 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New); |
||
| 103 | BOOST_CHECK(h.accept(e1)); |
||
| 104 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 105 | BOOST_CHECK(h.accept(e2)); |
||
| 106 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 107 | BOOST_CHECK(h.accept(e3)); |
||
| 108 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Satisfied); |
||
| 109 | BOOST_CHECK(h.accept(e4)); |
||
| 110 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Satisfied); |
||
| 111 | } |
||
| 112 | |||
| 113 | BOOST_AUTO_TEST_CASE(expiry) |
||
| 114 | {
|
||
| 115 | // A hypothesis that has been satisfied, but that is subsequently
|
||
| 116 | // offered an estimate that follows too long a gap, should enter
|
||
| 117 | // expired state rather than rejected state (showing that it has a
|
||
| 118 | // valid note but that the note has apparently finished)
|
||
| 119 | 38:944898c2e14e | Chris | NoteHypothesis h; |
| 120 | NoteHypothesis::Estimate e1(500, RealTime::fromMilliseconds(0), 1); |
||
| 121 | NoteHypothesis::Estimate e2(500, RealTime::fromMilliseconds(10), 1); |
||
| 122 | NoteHypothesis::Estimate e3(500, RealTime::fromMilliseconds(20), 1); |
||
| 123 | NoteHypothesis::Estimate e4(500, RealTime::fromMilliseconds(30), 1); |
||
| 124 | NoteHypothesis::Estimate e5(500, RealTime::fromMilliseconds(80), 1); |
||
| 125 | NoteHypothesis::Estimate e6(500, RealTime::fromMilliseconds(90), 1); |
||
| 126 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New); |
||
| 127 | BOOST_CHECK(h.accept(e1)); |
||
| 128 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 129 | BOOST_CHECK(h.accept(e2)); |
||
| 130 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 131 | BOOST_CHECK(h.accept(e3)); |
||
| 132 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Satisfied); |
||
| 133 | BOOST_CHECK(h.accept(e4)); |
||
| 134 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Satisfied); |
||
| 135 | BOOST_CHECK(!h.accept(e5)); |
||
| 136 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Expired); |
||
| 137 | BOOST_CHECK(!h.accept(e6)); |
||
| 138 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Expired); |
||
| 139 | } |
||
| 140 | |||
| 141 | 45:8db4a1f096f0 | Chris | BOOST_AUTO_TEST_CASE(strayReject1) |
| 142 | 38:944898c2e14e | Chris | {
|
| 143 | 45:8db4a1f096f0 | Chris | // A wildly different frequency occurring in the middle of a
|
| 144 | // provisionally accepted note should be ignored
|
||
| 145 | 38:944898c2e14e | Chris | NoteHypothesis h; |
| 146 | NoteHypothesis::Estimate e1(500, RealTime::fromMilliseconds(0), 1); |
||
| 147 | NoteHypothesis::Estimate e2(1000, RealTime::fromMilliseconds(10), 1); |
||
| 148 | NoteHypothesis::Estimate e3(500, RealTime::fromMilliseconds(20), 1); |
||
| 149 | NoteHypothesis::Estimate e4(500, RealTime::fromMilliseconds(30), 1); |
||
| 150 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New); |
||
| 151 | BOOST_CHECK(h.accept(e1)); |
||
| 152 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 153 | BOOST_CHECK(!h.accept(e2)); |
||
| 154 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 155 | BOOST_CHECK(h.accept(e3)); |
||
| 156 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 157 | BOOST_CHECK(h.accept(e4)); |
||
| 158 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Satisfied); |
||
| 159 | } |
||
| 160 | 45:8db4a1f096f0 | Chris | |
| 161 | BOOST_AUTO_TEST_CASE(strayReject2) |
||
| 162 | 38:944898c2e14e | Chris | {
|
| 163 | 45:8db4a1f096f0 | Chris | // A wildly different frequency occurring in the middle of a
|
| 164 | // satisfied note should be ignored
|
||
| 165 | 38:944898c2e14e | Chris | NoteHypothesis h; |
| 166 | NoteHypothesis::Estimate e1(500, RealTime::fromMilliseconds(0), 1); |
||
| 167 | 45:8db4a1f096f0 | Chris | NoteHypothesis::Estimate e2(500, RealTime::fromMilliseconds(10), 1); |
| 168 | NoteHypothesis::Estimate e3(500, RealTime::fromMilliseconds(20), 1); |
||
| 169 | NoteHypothesis::Estimate e4(1000, RealTime::fromMilliseconds(30), 1); |
||
| 170 | NoteHypothesis::Estimate e5(500, RealTime::fromMilliseconds(40), 1); |
||
| 171 | 38:944898c2e14e | Chris | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New); |
| 172 | BOOST_CHECK(h.accept(e1)); |
||
| 173 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 174 | 45:8db4a1f096f0 | Chris | BOOST_CHECK(h.accept(e2)); |
| 175 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 176 | BOOST_CHECK(h.accept(e3)); |
||
| 177 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Satisfied); |
||
| 178 | BOOST_CHECK(!h.accept(e4)); |
||
| 179 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Satisfied); |
||
| 180 | BOOST_CHECK(h.accept(e5)); |
||
| 181 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Satisfied); |
||
| 182 | 38:944898c2e14e | Chris | } |
| 183 | |||
| 184 | BOOST_AUTO_TEST_CASE(weakSatisfy) |
||
| 185 | {
|
||
| 186 | 45:8db4a1f096f0 | Chris | // Behaviour with slightly varying frequencies should be as for
|
| 187 | // that with fixed frequency
|
||
| 188 | 38:944898c2e14e | Chris | NoteHypothesis h; |
| 189 | NoteHypothesis::Estimate e1(500, RealTime::fromMilliseconds(0), 0.5); |
||
| 190 | NoteHypothesis::Estimate e2(502, RealTime::fromMilliseconds(10), 0.5); |
||
| 191 | NoteHypothesis::Estimate e3(504, RealTime::fromMilliseconds(20), 0.5); |
||
| 192 | NoteHypothesis::Estimate e4(506, RealTime::fromMilliseconds(30), 0.5); |
||
| 193 | NoteHypothesis::Estimate e5(508, RealTime::fromMilliseconds(40), 0.5); |
||
| 194 | NoteHypothesis::Estimate e6(510, RealTime::fromMilliseconds(90), 0.5); |
||
| 195 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New); |
||
| 196 | BOOST_CHECK(h.accept(e1)); |
||
| 197 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 198 | BOOST_CHECK(h.accept(e2)); |
||
| 199 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 200 | BOOST_CHECK(h.accept(e3)); |
||
| 201 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 202 | BOOST_CHECK(h.accept(e4)); |
||
| 203 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 204 | BOOST_CHECK(h.accept(e5)); |
||
| 205 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Satisfied); |
||
| 206 | BOOST_CHECK(!h.accept(e6)); |
||
| 207 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Expired); |
||
| 208 | } |
||
| 209 | |||
| 210 | BOOST_AUTO_TEST_CASE(frequencyRange) |
||
| 211 | {
|
||
| 212 | 45:8db4a1f096f0 | Chris | // But there's a limit: outside a certain range we should reject
|
| 213 | //!!! (but what is this range? is it part of the spec?)
|
||
| 214 | 38:944898c2e14e | Chris | NoteHypothesis h; |
| 215 | NoteHypothesis::Estimate e1(440, RealTime::fromMilliseconds(0), 1); |
||
| 216 | NoteHypothesis::Estimate e2(448, RealTime::fromMilliseconds(10), 1); |
||
| 217 | NoteHypothesis::Estimate e3(444, RealTime::fromMilliseconds(20), 1); |
||
| 218 | NoteHypothesis::Estimate e4(470, RealTime::fromMilliseconds(30), 1); |
||
| 219 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New); |
||
| 220 | BOOST_CHECK(h.accept(e1)); |
||
| 221 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 222 | BOOST_CHECK(h.accept(e2)); |
||
| 223 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 224 | BOOST_CHECK(h.accept(e3)); |
||
| 225 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Satisfied); |
||
| 226 | BOOST_CHECK(!h.accept(e4)); |
||
| 227 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Satisfied); |
||
| 228 | } |
||
| 229 | |||
| 230 | BOOST_AUTO_TEST_CASE(acceptedEstimates) |
||
| 231 | {
|
||
| 232 | 45:8db4a1f096f0 | Chris | // Check that getAcceptedEstimates() returns the right result
|
| 233 | 38:944898c2e14e | Chris | NoteHypothesis h; |
| 234 | NoteHypothesis::Estimate e1(440, RealTime::fromMilliseconds(0), 1); |
||
| 235 | NoteHypothesis::Estimate e2(448, RealTime::fromMilliseconds(10), 1); |
||
| 236 | NoteHypothesis::Estimate e3(444, RealTime::fromMilliseconds(20), 1); |
||
| 237 | NoteHypothesis::Estimate e4(470, RealTime::fromMilliseconds(30), 1); |
||
| 238 | NoteHypothesis::Estimate e5(444, RealTime::fromMilliseconds(90), 1); |
||
| 239 | NoteHypothesis::Estimates es; |
||
| 240 | es.push_back(e1); |
||
| 241 | es.push_back(e2); |
||
| 242 | es.push_back(e3); |
||
| 243 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::New); |
||
| 244 | BOOST_CHECK_EQUAL(h.getAcceptedEstimates(), NoteHypothesis::Estimates()); |
||
| 245 | BOOST_CHECK(h.accept(e1)); |
||
| 246 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 247 | BOOST_CHECK_EQUAL(h.getAcceptedEstimates(), NoteHypothesis::Estimates()); |
||
| 248 | BOOST_CHECK(h.accept(e2)); |
||
| 249 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Provisional); |
||
| 250 | BOOST_CHECK_EQUAL(h.getAcceptedEstimates(), NoteHypothesis::Estimates()); |
||
| 251 | BOOST_CHECK(h.accept(e3)); |
||
| 252 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Satisfied); |
||
| 253 | BOOST_CHECK_EQUAL(h.getAcceptedEstimates(), es); |
||
| 254 | BOOST_CHECK(!h.accept(e4)); |
||
| 255 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Satisfied); |
||
| 256 | BOOST_CHECK_EQUAL(h.getAcceptedEstimates(), es); |
||
| 257 | BOOST_CHECK(!h.accept(e5)); |
||
| 258 | BOOST_CHECK_EQUAL(h.getState(), NoteHypothesis::Expired); |
||
| 259 | BOOST_CHECK_EQUAL(h.getAcceptedEstimates(), es); |
||
| 260 | } |
||
| 261 | |||
| 262 | BOOST_AUTO_TEST_CASE(meanFrequency) |
||
| 263 | {
|
||
| 264 | 45:8db4a1f096f0 | Chris | // Check that the mean frequency is the mean of the frequencies
|
| 265 | 38:944898c2e14e | Chris | NoteHypothesis h; |
| 266 | NoteHypothesis::Estimate e1(440, RealTime::fromMilliseconds(0), 1); |
||
| 267 | NoteHypothesis::Estimate e2(448, RealTime::fromMilliseconds(10), 1); |
||
| 268 | NoteHypothesis::Estimate e3(444, RealTime::fromMilliseconds(20), 1); |
||
| 269 | BOOST_CHECK(h.accept(e1)); |
||
| 270 | BOOST_CHECK(h.accept(e2)); |
||
| 271 | BOOST_CHECK(h.accept(e3)); |
||
| 272 | BOOST_CHECK_EQUAL(h.getMeanFrequency(), 444.0); |
||
| 273 | } |
||
| 274 | |||
| 275 | BOOST_AUTO_TEST_CASE(averagedNote) |
||
| 276 | {
|
||
| 277 | 45:8db4a1f096f0 | Chris | // Check that getAveragedNote returns something sane
|
| 278 | 38:944898c2e14e | Chris | NoteHypothesis h; |
| 279 | NoteHypothesis::Estimate e1(440, RealTime::fromMilliseconds(10), 1); |
||
| 280 | NoteHypothesis::Estimate e2(448, RealTime::fromMilliseconds(20), 1); |
||
| 281 | NoteHypothesis::Estimate e3(444, RealTime::fromMilliseconds(30), 1); |
||
| 282 | BOOST_CHECK(h.accept(e1)); |
||
| 283 | BOOST_CHECK(h.accept(e2)); |
||
| 284 | BOOST_CHECK(h.accept(e3)); |
||
| 285 | BOOST_CHECK_EQUAL(h.getAveragedNote(), NoteHypothesis::Note |
||
| 286 | (444,
|
||
| 287 | RealTime::fromMilliseconds(10),
|
||
| 288 | RealTime::fromMilliseconds(20)));
|
||
| 289 | } |
||
| 290 | |||
| 291 | 45:8db4a1f096f0 | Chris | //!!! Not yet tested: Confidence scores
|
| 292 | |||
| 293 | 38:944898c2e14e | Chris | BOOST_AUTO_TEST_SUITE_END() |