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