Revision 52:34f42a384a7f

View differences:

AgentFeeder.h
1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2
/*
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

  
25
#ifndef _AGENT_FEEDER_H_
26
#define _AGENT_FEEDER_H_
27

  
28
#include "NoteHypothesis.h"
29

  
30
#include <vector>
31

  
32
/**
33
 * Take a series of estimates (one at a time) and feed them to a set
34
 * of note hypotheses, creating a new candidate hypothesis for each
35
 * observation and also testing the observation against the existing
36
 * set of hypotheses.
37
 *
38
 * One satisfied hypothesis is considered to be "accepted" at any
39
 * moment (that is, the earliest contemporary hypothesis to have
40
 * become satisfied). The series of accepted and completed hypotheses
41
 * from construction to the present time can be queried through
42
 * getAcceptedHypotheses().
43
 *
44
 * Call feed() to provide a new observation. Call finish() when all
45
 * observations have been provided. The set of hypotheses returned by
46
 * getAcceptedHypotheses() will not be complete unless finish() has
47
 * been called.
48
 */
49
class AgentFeeder
50
{
51
public:
52
    AgentFeeder() { }
53

  
54
    void feed(NoteHypothesis::Estimate);
55
    void finish();
56

  
57
    typedef std::vector<NoteHypothesis> Hypotheses;
58

  
59
    Hypotheses getAcceptedHypotheses() const {
60
        return m_accepted;
61
    }
62

  
63
private:
64
    Hypotheses m_candidates;
65
    NoteHypothesis m_current;
66
    Hypotheses m_accepted;
67
};
68

  
69

  
70
#endif
71

  
Makefile.inc
14 14
PLUGIN := cepstral-pitchtracker$(PLUGIN_EXT)
15 15

  
16 16
HEADERS := CepstralPitchTracker.h \
17
           AgentFeeder.h \
17 18
           MeanFilter.h \
18 19
	   NoteHypothesis.h \
19 20
	   PeakInterpolator.h
20 21

  
21 22
SOURCES := CepstralPitchTracker.cpp \
23
           AgentFeeder.cpp \
22 24
	   NoteHypothesis.cpp \
23 25
	   PeakInterpolator.cpp
24 26

  
......
28 30
         test/test-fft \
29 31
	 test/test-cepstrum \
30 32
         test/test-peakinterpolator \
31
	 test/test-notehypothesis
33
	 test/test-notehypothesis \
34
	 test/test-agentfeeder
32 35
         
33 36
OBJECTS := $(SOURCES:.cpp=.o)
34 37
OBJECTS := $(OBJECTS:.c=.o)
......
44 47
test/test-notehypothesis: test/TestNoteHypothesis.o $(OBJECTS)
45 48
	$(CXX) -o $@ $^ $(TEST_LDFLAGS)
46 49

  
50
test/test-agentfeeder: test/TestAgentFeeder.o $(OBJECTS)
51
	$(CXX) -o $@ $^ $(TEST_LDFLAGS)
52

  
47 53
test/test-meanfilter: test/TestMeanFilter.o $(OBJECTS)
48 54
	$(CXX) -o $@ $^ $(TEST_LDFLAGS)
49 55

  
NoteHypothesis.cpp
149 149
    }
150 150
}
151 151

  
152
RealTime
153
NoteHypothesis::getStartTime() const
154
{
155
    if (!(m_state == Satisfied || m_state == Expired)) {
156
        return RealTime::zeroTime;
157
    } else {
158
        return m_pending.begin()->time;
159
    }
160
}
161

  
152 162
double
153 163
NoteHypothesis::getMeanFrequency() const
154 164
{
NoteHypothesis.h
113 113
    };
114 114
    
115 115
    /**
116
     * Return the time of the first accepted estimate
117
     */
118
    Vamp::RealTime getStartTime() const;
119

  
120
    /**
116 121
     * Return the mean frequency of the accepted estimates
117 122
     */
118 123
    double getMeanFrequency() const;
test/TestAgentFeeder.cpp
1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2
/*
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

  
25
#include "AgentFeeder.h"
26

  
27
#define BOOST_TEST_DYN_LINK
28
#define BOOST_TEST_MAIN
29

  
30
#include <boost/test/unit_test.hpp>
31

  
32
static Vamp::RealTime ms(int n) { return Vamp::RealTime::fromMilliseconds(n); }
33

  
34
typedef NoteHypothesis::Estimate Est;
35

  
36
BOOST_AUTO_TEST_SUITE(TestAgentFeeder)
37

  
38
BOOST_AUTO_TEST_CASE(feederEmpty)
39
{
40
    AgentFeeder f;
41
    f.finish();
42
    AgentFeeder::Hypotheses accepted = f.getAcceptedHypotheses();
43
    BOOST_CHECK(accepted.empty());
44
}
45

  
46
BOOST_AUTO_TEST_CASE(feederSingle)
47
{
48
    Est e0(1, ms(0), 1);
49
    Est e10(1, ms(10), 1);
50
    Est e20(1, ms(20), 1);
51
    Est e30(1, ms(30), 1);
52

  
53
    AgentFeeder f;
54
    f.feed(e0);
55
    f.feed(e10);
56
    f.feed(e20);
57
    f.feed(e30);
58
    f.finish();
59

  
60
    AgentFeeder::Hypotheses accepted = f.getAcceptedHypotheses();
61
    
62
    BOOST_CHECK_EQUAL(accepted.size(), size_t(1));
63
}
64

  
65
BOOST_AUTO_TEST_CASE(feederPairSeparate)
66
{
67
    Est e0(1, ms(0), 1);
68
    Est e10(1, ms(10), 1);
69
    Est e20(1, ms(20), 1);
70
    Est e30(1, ms(30), 1);
71

  
72
    Est f0(3, ms(2000), 1);
73
    Est f10(3, ms(2010), 1);
74
    Est f20(3, ms(2020), 1);
75
    Est f30(3, ms(2030), 1);
76

  
77
    AgentFeeder f;
78
    f.feed(e0);
79
    f.feed(e10);
80
    f.feed(e20);
81
    f.feed(e30);
82
    f.feed(f0);
83
    f.feed(f10);
84
    f.feed(f20);
85
    f.feed(f30);
86
    f.finish();
87

  
88
    AgentFeeder::Hypotheses accepted =
89
	f.getAcceptedHypotheses();
90
    
91
    BOOST_CHECK_EQUAL(accepted.size(), size_t(2));
92
}
93

  
94
BOOST_AUTO_TEST_CASE(feederPairOverlapping)
95
{
96
    // eeee
97
    //   fffffff
98

  
99
    // (With fffffff stopping before eeee has expired.)
100

  
101
    // This should give us one hypothesis, eeee.
102

  
103
    Est e0(1, ms(0), 1);
104
    Est e10(1, ms(10), 1);
105

  
106
    Est e20(1, ms(20), 1);
107
    Est f20(3, ms(20), 1);
108

  
109
    Est e30(1, ms(30), 1);
110
    Est f30(3, ms(30), 1);
111

  
112
    Est f40(3, ms(40), 1);
113
    Est f50(3, ms(50), 1);
114
    Est f60(3, ms(60), 1);
115
    Est f70(3, ms(70), 1);
116
    Est f80(3, ms(80), 1);
117

  
118
    AgentFeeder f;
119
    f.feed(e0);
120
    f.feed(e10);
121
    f.feed(e20);
122
    f.feed(f20);
123
    f.feed(e30);
124
    f.feed(f30);
125
    f.feed(f40);
126
    f.feed(f50);
127
    f.feed(f60);
128
    f.feed(f70);
129
    f.feed(f80);
130
    f.finish();
131

  
132
    AgentFeeder::Hypotheses accepted = f.getAcceptedHypotheses();
133
    
134
    BOOST_CHECK_EQUAL(accepted.size(), size_t(1));
135

  
136
    AgentFeeder::Hypotheses::const_iterator i = accepted.begin();
137

  
138
    BOOST_CHECK_EQUAL(i->getStartTime(), ms(0)); 
139
    BOOST_CHECK_EQUAL(i->getAcceptedEstimates().size(), size_t(4));
140
}
141
        
142
BOOST_AUTO_TEST_CASE(feederPairOverlappingLong)
143
{
144
    // eeee
145
    //   fffffff
146

  
147
    // (With fffffff continuing until after eeee has expired.)
148

  
149
    // This should give us two overlapping hypotheses. Even though
150
    // the mono feeder only has one satisfied hypothesis at a
151
    // time, the eeee hypothesis should become satisfied before
152
    // the fffffff hypothesis has been, but when the eeee
153
    // hypothesis ends, the fffffff one should replace it. So,
154
    // both should be recognised.
155

  
156
    Est e0(1, ms(0), 1);
157
    Est e10(1, ms(500), 1);
158

  
159
    Est e20(1, ms(1000), 1);
160
    Est f20(3, ms(1000), 1);
161

  
162
    Est e30(1, ms(1500), 1);
163
    Est f30(3, ms(1500), 1);
164

  
165
    Est f40(3, ms(2000), 1);
166
    Est f50(3, ms(2500), 1);
167
    Est f60(3, ms(3000), 1);
168
    Est f70(3, ms(3500), 1);
169
    Est f80(3, ms(4000), 1);
170

  
171
    AgentFeeder f;
172
    f.feed(e0);
173
    f.feed(e10);
174
    f.feed(e20);
175
    f.feed(f20);
176
    f.feed(e30);
177
    f.feed(f30);
178
    f.feed(f40);
179
    f.feed(f50);
180
    f.feed(f60);
181
    f.feed(f70);
182
    f.feed(f80);
183
    f.finish();
184

  
185
    AgentFeeder::Hypotheses accepted = f.getAcceptedHypotheses();
186
    
187
    BOOST_CHECK_EQUAL(accepted.size(), size_t(2));
188

  
189
    AgentFeeder::Hypotheses::const_iterator i = accepted.begin();
190

  
191
    BOOST_CHECK_EQUAL(i->getStartTime(), ms(0)); 
192
    BOOST_CHECK_EQUAL(i->getAcceptedEstimates().size(), size_t(4));
193
    ++i;
194

  
195
    BOOST_CHECK_EQUAL(i->getStartTime(), ms(1000)); 
196
    BOOST_CHECK_EQUAL(i->getAcceptedEstimates().size(), size_t(7));
197
    ++i;
198
}
199
        
200

  
201
BOOST_AUTO_TEST_CASE(feederPairContaining)
202
{
203
    // eeeeeeee
204
    //   ffff
205

  
206
    // This should give us eeeeeeee only. The ffff hypothesis
207
    // (even when satisfied itself) cannot replace the single
208
    // satisfied hypothesis eeeeeeee while it is still in
209
    // progress.
210

  
211
    Est e0(1, ms(0), 1);
212
    Est e10(1, ms(10), 1);
213
    Est e20(1, ms(20), 1);
214
    Est e30(1, ms(30), 1);
215
    Est e40(1, ms(40), 1);
216
    Est e50(1, ms(50), 1);
217
    Est e60(1, ms(60), 1);
218
    Est e70(1, ms(70), 1);
219

  
220
    Est f20(3, ms(20), 1);
221
    Est f30(3, ms(30), 1);
222
    Est f40(3, ms(40), 1);
223
    Est f50(3, ms(50), 1);
224

  
225
    AgentFeeder f;
226

  
227
    f.feed(e0);
228
    f.feed(e10);
229

  
230
    f.feed(e20);
231
    f.feed(f20);
232

  
233
    f.feed(e30);
234
    f.feed(f30);
235

  
236
    f.feed(e40);
237
    f.feed(f40);
238

  
239
    f.feed(e50);
240
    f.feed(f50);
241

  
242
    f.feed(e60);
243
    f.feed(e70);
244

  
245
    f.finish();
246

  
247
    AgentFeeder::Hypotheses accepted = f.getAcceptedHypotheses();
248
    
249
    BOOST_CHECK_EQUAL(accepted.size(), size_t(1));
250
   
251
    AgentFeeder::Hypotheses::const_iterator i = accepted.begin();
252

  
253
    BOOST_CHECK_EQUAL(i->getStartTime(), ms(0));
254
    BOOST_CHECK_EQUAL(i->getAcceptedEstimates().size(), size_t(8));
255
    ++i;
256
}
257
        
258
BOOST_AUTO_TEST_SUITE_END()
259

  

Also available in: Unified diff