annotate AgentList.h @ 12:59520cd6abac

Debug output, and some actual debugging... getting there.
author Chris Cannam
date Thu, 06 Oct 2011 15:21:03 +0100
parents 7169eb47b1bc
children 0d4048bfadbb
rev   line source
Chris@8 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@8 2
Chris@8 3 /*
Chris@8 4 Vamp feature extraction plugin for the BeatRoot beat tracker.
Chris@8 5
Chris@8 6 Centre for Digital Music, Queen Mary, University of London.
Chris@8 7 This file copyright 2011 Simon Dixon, Chris Cannam and QMUL.
Chris@8 8
Chris@8 9 This program is free software; you can redistribute it and/or
Chris@8 10 modify it under the terms of the GNU General Public License as
Chris@8 11 published by the Free Software Foundation; either version 2 of the
Chris@8 12 License, or (at your option) any later version. See the file
Chris@8 13 COPYING included with this distribution for more information.
Chris@8 14 */
Chris@8 15
Chris@8 16 #ifndef _AGENT_LIST_H_
Chris@8 17 #define _AGENT_LIST_H_
Chris@8 18
Chris@8 19 #include "Agent.h"
Chris@8 20 #include "Event.h"
Chris@8 21
Chris@8 22 #include <algorithm>
Chris@8 23
Chris@12 24 #ifdef DEBUG_BEATROOT
Chris@12 25 #include <iostream>
Chris@12 26 #endif
Chris@12 27
Chris@8 28 /** Class for maintaining the set of all Agents involved in beat tracking a piece of music.
Chris@8 29 */
Chris@8 30 class AgentList
Chris@8 31 {
Chris@8 32 public:
Chris@8 33 typedef std::vector<Agent> Container;
Chris@8 34 typedef Container::iterator iterator;
Chris@8 35
Chris@8 36 protected:
Chris@8 37 Container list;
Chris@8 38
Chris@8 39 public:
Chris@8 40 // expose some vector methods
Chris@8 41 //!!! can we remove these again once the rest of AgentList is implemented?
Chris@8 42 bool empty() const { return list.empty(); }
Chris@8 43 Container::iterator begin() { return list.begin(); }
Chris@8 44 Container::iterator end() { return list.end(); }
Chris@12 45 size_t size() { return list.size(); }
Chris@12 46 void push_back(const Agent &a) {
Chris@12 47 list.push_back(a);
Chris@12 48 #ifdef DEBUG_BEATROOT
Chris@12 49 std::cerr << " Added Ag#" << a.idNumber << ", have " << list.size() << " agent(s)" << std::endl;
Chris@12 50 #endif
Chris@12 51 }
Chris@8 52
Chris@11 53 /** Flag for choice between sum and average beat salience values for Agent scores.
Chris@11 54 * The use of summed saliences favours faster tempi or lower metrical levels. */
Chris@11 55 static bool useAverageSalience;
Chris@8 56
Chris@11 57 /** For the purpose of removing duplicate agents, the default JND of IBI */
Chris@11 58 static const double DEFAULT_BI;
Chris@8 59
Chris@11 60 /** For the purpose of removing duplicate agents, the default JND of phase */
Chris@11 61 static const double DEFAULT_BT;
Chris@8 62
Chris@11 63 /** Inserts newAgent into the list in ascending order of beatInterval */
Chris@11 64 void add(Agent a) {
Chris@11 65 add(a, true);
Chris@11 66 } // add()/1
Chris@8 67
Chris@11 68 /** Appends newAgent to list (sort==false), or inserts newAgent into the list
Chris@11 69 * in ascending order of beatInterval
Chris@11 70 * @param newAgent The agent to be added to the list
Chris@11 71 * @param sort Flag indicating whether the list is sorted or not
Chris@11 72 */
Chris@11 73 void add(Agent newAgent, bool sort){
Chris@12 74 push_back(newAgent);
Chris@11 75 if (sort) this->sort();
Chris@11 76 } // add()/2
Chris@8 77
Chris@11 78 /** Sorts the AgentList by increasing beatInterval. */
Chris@11 79 void sort() {
Chris@11 80 std::sort(list.begin(), list.end());
Chris@11 81 } // sort()
Chris@8 82
Chris@11 83 /** Removes the given item from the list.
Chris@11 84 * @param ptr Points to the Agent which is removed from the list
Chris@11 85 */
Chris@11 86 void remove(iterator itr) {
Chris@11 87 list.erase(itr);
Chris@11 88 } // remove()
Chris@8 89
Chris@8 90 protected:
Chris@11 91 /** Removes Agents from the list which are duplicates of other Agents.
Chris@11 92 * A duplicate is defined by the tempo and phase thresholds
Chris@11 93 * thresholdBI and thresholdBT respectively.
Chris@11 94 */
Chris@11 95 void removeDuplicates() {
Chris@11 96 sort();
Chris@11 97 for (iterator itr = begin(); itr != end(); ++itr) {
Chris@11 98 if (itr->phaseScore < 0.0) // already flagged for deletion
Chris@11 99 continue;
Chris@11 100 iterator itr2 = itr;
Chris@11 101 for (++itr2; itr2 != end(); ++itr2) {
Chris@11 102 if (itr2->beatInterval - itr->beatInterval > DEFAULT_BI)
Chris@11 103 break;
Chris@11 104 if (fabs(itr->beatTime - itr2->beatTime) > DEFAULT_BT)
Chris@11 105 continue;
Chris@11 106 if (itr->phaseScore < itr2->phaseScore) {
Chris@11 107 itr->phaseScore = -1.0; // flag for deletion
Chris@11 108 if (itr2->topScoreTime < itr->topScoreTime)
Chris@11 109 itr2->topScoreTime = itr->topScoreTime;
Chris@11 110 break;
Chris@11 111 } else {
Chris@11 112 itr2->phaseScore = -1.0; // flag for deletion
Chris@11 113 if (itr->topScoreTime < itr2->topScoreTime)
Chris@11 114 itr->topScoreTime = itr2->topScoreTime;
Chris@11 115 }
Chris@11 116 }
Chris@11 117 }
Chris@12 118 int removed = 0;
Chris@11 119 for (iterator itr = begin(); itr != end(); ) {
Chris@11 120 if (itr->phaseScore < 0.0) {
Chris@12 121 ++removed;
Chris@11 122 list.erase(itr);
Chris@11 123 } else {
Chris@11 124 ++itr;
Chris@11 125 }
Chris@11 126 }
Chris@12 127 #ifdef DEBUG_BEATROOT
Chris@12 128 if (removed > 0) {
Chris@12 129 std::cerr << "removeDuplicates: removed " << removed << ", have "
Chris@12 130 << list.size() << " agent(s) remaining" << std::endl;
Chris@12 131 }
Chris@12 132 for (int i = 0; i <list.size(); ++i) {
Chris@12 133 std::cerr << "agent " << i << ": time " << list[i].beatTime << std::endl;
Chris@12 134 }
Chris@12 135 #endif
Chris@11 136 } // removeDuplicates()
Chris@8 137
Chris@8 138 public:
Chris@11 139 /** Perform beat tracking on a list of events (onsets).
Chris@11 140 * @param el The list of onsets (or events or peaks) to beat track
Chris@11 141 */
Chris@11 142 void beatTrack(EventList el) {
Chris@11 143 beatTrack(el, -1.0);
Chris@11 144 } // beatTrack()/1
Chris@8 145
Chris@11 146 /** Perform beat tracking on a list of events (onsets).
Chris@11 147 * @param el The list of onsets (or events or peaks) to beat track.
Chris@11 148 * @param stop Do not find beats after <code>stop</code> seconds.
Chris@11 149 */
Chris@11 150 void beatTrack(EventList el, double stop) {
Chris@11 151 EventList::iterator ei = el.begin();
Chris@11 152 bool phaseGiven = !empty() && (begin()->beatTime >= 0); // if given for one, assume given for others
Chris@11 153 while (ei != el.end()) {
Chris@11 154 Event ev = *ei;
Chris@11 155 ++ei;
Chris@11 156 if ((stop > 0) && (ev.time > stop))
Chris@11 157 break;
Chris@11 158 bool created = phaseGiven;
Chris@11 159 double prevBeatInterval = -1.0;
Chris@12 160 // cc: Duplicate our list of agents, and scan through the
Chris@12 161 // copy. This means we can safely add agents to our own
Chris@12 162 // list while scanning without disrupting our scan. Each
Chris@12 163 // agent needs to be re-added to our own list explicitly
Chris@12 164 // (since it is modified by e.g. considerAsBeat)
Chris@11 165 Container currentAgents = list;
Chris@12 166 list.clear();
Chris@11 167 for (Container::iterator ai = currentAgents.begin();
Chris@11 168 ai != currentAgents.end(); ++ai) {
Chris@11 169 Agent currentAgent = *ai;
Chris@11 170 if (currentAgent.beatInterval != prevBeatInterval) {
Chris@11 171 if ((prevBeatInterval>=0) && !created && (ev.time<5.0)) {
Chris@12 172 #ifdef DEBUG_BEATROOT
Chris@12 173 std::cerr << "Creating a new agent" << std::endl;
Chris@12 174 #endif
Chris@11 175 // Create new agent with different phase
Chris@11 176 Agent newAgent(prevBeatInterval);
Chris@12 177 // This may add another agent to our list as well
Chris@11 178 newAgent.considerAsBeat(ev, *this);
Chris@12 179 add(newAgent);
Chris@8 180 }
Chris@11 181 prevBeatInterval = currentAgent.beatInterval;
Chris@11 182 created = phaseGiven;
Chris@11 183 }
Chris@11 184 if (currentAgent.considerAsBeat(ev, *this))
Chris@11 185 created = true;
Chris@12 186 add(currentAgent);
Chris@11 187 } // loop for each agent
Chris@11 188 removeDuplicates();
Chris@11 189 } // loop for each event
Chris@11 190 } // beatTrack()
Chris@8 191
Chris@11 192 /** Finds the Agent with the highest score in the list, or NULL if beat tracking has failed.
Chris@11 193 * @return The Agent with the highest score
Chris@11 194 */
Chris@11 195 Agent *bestAgent() {
Chris@11 196 double best = -1.0;
Chris@11 197 Agent *bestAg = 0;
Chris@11 198 for (iterator itr = begin(); itr != end(); ++itr) {
Chris@11 199 if (itr->events.empty()) continue;
Chris@11 200 double startTime = itr->events.begin()->time;
Chris@11 201 double conf = (itr->phaseScore + itr->tempoScore) /
Chris@11 202 (useAverageSalience? (double)itr->beatCount: 1.0);
Chris@11 203 if (conf > best) {
Chris@11 204 bestAg = &(*itr);
Chris@11 205 best = conf;
Chris@8 206 }
Chris@11 207 }
Chris@12 208 #ifdef DEBUG_BEATROOT
Chris@12 209 if (bestAg) {
Chris@12 210 std::cerr << "Best agent: Ag#" << bestAg->idNumber << std::endl;
Chris@12 211 std::cerr << " Av-salience = " << best << std::endl;
Chris@12 212 } else {
Chris@12 213 std::cerr << "No surviving agent - beat tracking failed" << std::endl;
Chris@12 214 }
Chris@12 215 #endif
Chris@11 216 return bestAg;
Chris@11 217 } // bestAgent()
Chris@8 218
Chris@8 219
Chris@8 220 }; // class AgentList
Chris@8 221
Chris@8 222 #endif
Chris@8 223