annotate Agent.h @ 12:59520cd6abac

Debug output, and some actual debugging... getting there.
author Chris Cannam
date Thu, 06 Oct 2011 15:21:03 +0100
parents 4f6626f9ffac
children f1252b6a7cf5
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@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@12 139 Agent clone() const {
Chris@12 140 Agent a(*this);
Chris@12 141 a.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@6 160 void accept(Event e, double err, int beats) {
Chris@6 161 beatTime = e.time;
Chris@6 162 events.push_back(e);
Chris@6 163 if (fabs(initialBeatInterval - beatInterval -
Chris@6 164 err / correctionFactor) < MAX_CHANGE * initialBeatInterval)
Chris@6 165 beatInterval += err / correctionFactor;// Adjust tempo
Chris@6 166 beatCount += beats;
Chris@6 167 double conFactor = 1.0 - CONF_FACTOR * err /
Chris@6 168 (err>0? postMargin: -preMargin);
Chris@6 169 if (decayFactor > 0) {
Chris@6 170 double memFactor = 1. - 1. / threshold((double)beatCount,1,decayFactor);
Chris@6 171 phaseScore = memFactor * phaseScore +
Chris@6 172 (1.0 - memFactor) * conFactor * e.salience;
Chris@6 173 } else
Chris@6 174 phaseScore += conFactor * e.salience;
Chris@12 175 #ifdef DEBUG_BEATROOT
Chris@12 176 std::cerr << "Ag#" << idNumber << ": " << beatInterval << std::endl;
Chris@12 177 std::cerr << " Beat" << beatCount << " Time=" << beatTime
Chris@12 178 << " Score=" << tempoScore << ":P" << phaseScore << ":"
Chris@12 179 << topScoreTime << std::endl;
Chris@12 180 #endif
Chris@6 181 } // accept()
Chris@6 182
Chris@6 183 /** The given Event is tested for a possible beat time. The following situations can occur:
Chris@6 184 * 1) The Agent has no beats yet; the Event is accepted as the first beat.
Chris@6 185 * 2) The Event is beyond expiryTime seconds after the Agent's last 'confirming' beat; the Agent is terminated.
Chris@6 186 * 3) The Event is within the innerMargin of the beat prediction; it is accepted as a beat.
Chris@9 187 * 4) The Event is within the postMargin's of the beat prediction; it is accepted as a beat by this Agent,
Chris@6 188 * and a new Agent is created which doesn't accept it as a beat.
Chris@6 189 * 5) The Event is ignored because it is outside the windows around the Agent's predicted beat time.
Chris@6 190 * @param e The Event to be tested
Chris@6 191 * @param a The list of all agents, which is updated if a new agent is created.
Chris@6 192 * @return Indicate whether the given Event was accepted as a beat by this Agent.
Chris@6 193 */
Chris@8 194 bool considerAsBeat(Event e, AgentList &a);
Chris@6 195
Chris@6 196 /** Interpolates missing beats in the Agent's beat track, starting from the beginning of the piece. */
Chris@6 197 void fillBeats() {
Chris@6 198 fillBeats(-1.0);
Chris@6 199 } // fillBeats()/0
Chris@6 200
Chris@6 201 /** Interpolates missing beats in the Agent's beat track.
Chris@6 202 * @param start Ignore beats earlier than this start time
Chris@6 203 */
Chris@6 204 void fillBeats(double start);
Chris@6 205
Chris@8 206 // for sorting AgentList
Chris@8 207 bool operator<(const Agent &a) const {
Chris@8 208 return beatInterval < a.beatInterval;
Chris@8 209 }
Chris@8 210
Chris@6 211 }; // class Agent
Chris@6 212
Chris@6 213 #endif