comparison Agent.h @ 6:02d388f98c23

Introduce a number of new classes derived from the Java
author Chris Cannam
date Tue, 27 Sep 2011 18:37:01 +0100
parents
children f04f87b5e643
comparison
equal deleted inserted replaced
5:2150607d4726 6:02d388f98c23
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Vamp feature extraction plugin for the BeatRoot beat tracker.
5
6 Centre for Digital Music, Queen Mary, University of London.
7 This file copyright 2011 Simon Dixon, Chris Cannam and QMUL.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file
13 COPYING included with this distribution for more information.
14 */
15
16 #ifndef _AGENT_H_
17 #define _AGENT_H_
18
19 #include "Event.h"
20
21 /** Agent is the central class for beat tracking.
22 * Each Agent object has a tempo hypothesis, a history of tracked beats, and
23 * a score evaluating the continuity, regularity and salience of its beat track.
24 */
25 class Agent
26 {
27 public:
28
29 typedef std::vector<Agent> AgentList;
30
31 /** The maximum amount by which a beat can be later than the predicted beat time,
32 * expressed as a fraction of the beat period. */
33 static double POST_MARGIN_FACTOR;
34
35 /** The maximum amount by which a beat can be earlier than the predicted beat time,
36 * expressed as a fraction of the beat period. */
37 static double PRE_MARGIN_FACTOR;
38
39 /** The default value of innerMargin, which is the maximum time (in seconds) that a
40 * beat can deviate from the predicted beat time without a fork occurring. */
41 static const double INNER_MARGIN;
42
43 /** The maximum allowed deviation from the initial tempo, expressed as a fraction of the initial beat period. */
44 static double MAX_CHANGE;
45
46 /** The slope of the penalty function for onsets which do not coincide precisely with predicted beat times. */
47 static double CONF_FACTOR;
48
49 /** The reactiveness/inertia balance, i.e. degree of change in the tempo, is controlled by the correctionFactor
50 * variable. This constant defines its default value, which currently is not subsequently changed. The
51 * beat period is updated by the reciprocal of the correctionFactor multiplied by the difference between the
52 * predicted beat time and matching onset. */
53 static const double DEFAULT_CORRECTION_FACTOR;
54
55 /** The default value of expiryTime, which is the time (in seconds) after which an Agent that
56 * has no Event matching its beat predictions will be destroyed. */
57 static const double DEFAULT_EXPIRY_TIME;
58
59 protected:
60 /** The identity number of the next created Agent */
61 static int idCounter;
62
63 /** The maximum time (in seconds) that a beat can deviate from the predicted beat time
64 * without a fork occurring (i.e. a 2nd Agent being created). */
65 static double innerMargin;
66
67 /** Controls the reactiveness/inertia balance, i.e. degree of change in the tempo. The
68 * beat period is updated by the reciprocal of the correctionFactor multiplied by the difference between the
69 * predicted beat time and matching onset. */
70 static double correctionFactor;
71
72 /** The time (in seconds) after which an Agent that
73 * has no Event matching its beat predictions will be destroyed. */
74 static double expiryTime;
75
76 /** For scoring Agents in a (non-existent) real-time version (otherwise not used). */
77 static double decayFactor;
78
79 public:
80 /** The size of the outer half-window before the predicted beat time. */
81 double preMargin;
82
83 /** The size of the outer half-window after the predicted beat time. */
84 double postMargin;
85
86 /** The Agent's unique identity number. */
87 int idNumber;
88
89 /** To be used in real-time version?? */
90 double tempoScore;
91
92 /** Sum of salience values of the Events which have been interpreted
93 * as beats by this Agent, weighted by their nearness to the predicted beat times. */
94 double phaseScore;
95
96 /** How long has this agent been the best? For real-time version; otherwise not used. */
97 double topScoreTime;
98
99 /** The number of beats found by this Agent, including interpolated beats. */
100 int beatCount;
101
102 /** The current tempo hypothesis of the Agent, expressed as the beat period in seconds. */
103 double beatInterval;
104
105 /** The initial tempo hypothesis of the Agent, expressed as the beat period in seconds. */
106 double initialBeatInterval;
107
108 /** The time of the most recent beat accepted by this Agent. */
109 double beatTime;
110
111 /** The list of Events (onsets) accepted by this Agent as beats, plus interpolated beats. */
112 EventList events;
113
114 /** Constructor: the work is performed by init()
115 * @param ibi The beat period (inter-beat interval) of the Agent's tempo hypothesis.
116 */
117 Agent(double ibi) {
118 init(ibi);
119 } // constructor
120
121 /** Copy constructor.
122 * @param clone The Agent to duplicate. */
123 Agent(const Agent &clone) {
124 idNumber = idCounter++;
125 phaseScore = clone.phaseScore;
126 tempoScore = clone.tempoScore;
127 topScoreTime = clone.topScoreTime;
128 beatCount = clone.beatCount;
129 beatInterval = clone.beatInterval;
130 initialBeatInterval = clone.initialBeatInterval;
131 beatTime = clone.beatTime;
132 events = EventList(clone.events);
133 postMargin = clone.postMargin;
134 preMargin = clone.preMargin;
135 } // copy constructor
136
137 protected:
138 /** Initialise all the fields of this Agent.
139 * @param ibi The initial tempo hypothesis of the Agent.
140 */
141 void init(double ibi) {
142 innerMargin = INNER_MARGIN;
143 correctionFactor = DEFAULT_CORRECTION_FACTOR;
144 expiryTime = DEFAULT_EXPIRY_TIME;
145 decayFactor = 0;
146 beatInterval = ibi;
147 initialBeatInterval = ibi;
148 postMargin = ibi * POST_MARGIN_FACTOR;
149 preMargin = ibi * PRE_MARGIN_FACTOR;
150 idNumber = idCounter++;
151 phaseScore = 0.0;
152 tempoScore = 0.0;
153 topScoreTime = 0.0;
154 beatCount = 0;
155 beatTime = -1.0;
156 events.clear();
157 } // init()
158
159
160 double threshold(double value, double min, double max) {
161 if (value < min)
162 return min;
163 if (value > max)
164 return max;
165 return value;
166 }
167
168 /** Accept a new Event as a beat time, and update the state of the Agent accordingly.
169 * @param e The Event which is accepted as being on the beat.
170 * @param err The difference between the predicted and actual beat times.
171 * @param beats The number of beats since the last beat that matched an Event.
172 */
173 void accept(Event e, double err, int beats) {
174 beatTime = e.time;
175 events.push_back(e);
176 if (fabs(initialBeatInterval - beatInterval -
177 err / correctionFactor) < MAX_CHANGE * initialBeatInterval)
178 beatInterval += err / correctionFactor;// Adjust tempo
179 beatCount += beats;
180 double conFactor = 1.0 - CONF_FACTOR * err /
181 (err>0? postMargin: -preMargin);
182 if (decayFactor > 0) {
183 double memFactor = 1. - 1. / threshold((double)beatCount,1,decayFactor);
184 phaseScore = memFactor * phaseScore +
185 (1.0 - memFactor) * conFactor * e.salience;
186 } else
187 phaseScore += conFactor * e.salience;
188 } // accept()
189
190 /** The given Event is tested for a possible beat time. The following situations can occur:
191 * 1) The Agent has no beats yet; the Event is accepted as the first beat.
192 * 2) The Event is beyond expiryTime seconds after the Agent's last 'confirming' beat; the Agent is terminated.
193 * 3) The Event is within the innerMargin of the beat prediction; it is accepted as a beat.
194 * 4) The Event is within the outerMargin's of the beat prediction; it is accepted as a beat by this Agent,
195 * and a new Agent is created which doesn't accept it as a beat.
196 * 5) The Event is ignored because it is outside the windows around the Agent's predicted beat time.
197 * @param e The Event to be tested
198 * @param a The list of all agents, which is updated if a new agent is created.
199 * @return Indicate whether the given Event was accepted as a beat by this Agent.
200 */
201 bool considerAsBeat(Event e, const AgentList &a);
202
203 /** Interpolates missing beats in the Agent's beat track, starting from the beginning of the piece. */
204 void fillBeats() {
205 fillBeats(-1.0);
206 } // fillBeats()/0
207
208 /** Interpolates missing beats in the Agent's beat track.
209 * @param start Ignore beats earlier than this start time
210 */
211 void fillBeats(double start);
212
213 }; // class Agent
214
215 typedef Agent::AgentList AgentList;
216
217 #endif