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