Mercurial > hg > beatroot-vamp
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 |