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