annotate Agent.h @ 9:4f6626f9ffac

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