comparison AgentList.h @ 8:f04f87b5e643

Add agent list class, and continue plodding through
author Chris Cannam
date Fri, 30 Sep 2011 11:37:25 +0100
parents
children 1c1e98cd1b2e
comparison
equal deleted inserted replaced
7:3c11becfc81a 8:f04f87b5e643
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 <algorithm>
23
24 /** Class for maintaining the set of all Agents involved in beat tracking a piece of music.
25 */
26 class AgentList
27 {
28 public:
29 typedef std::vector<Agent> Container;
30 typedef Container::iterator iterator;
31
32 protected:
33 Container list;
34
35 public:
36 // expose some vector methods
37 //!!! can we remove these again once the rest of AgentList is implemented?
38 bool empty() const { return list.empty(); }
39 Container::iterator begin() { return list.begin(); }
40 Container::iterator end() { return list.end(); }
41 void push_back(const Agent &a) { list.push_back(a); }
42
43 /** Flag for choice between sum and average beat salience values for Agent scores.
44 * The use of summed saliences favours faster tempi or lower metrical levels. */
45 static bool useAverageSalience;
46
47 /** For the purpose of removing duplicate agents, the default JND of IBI */
48 static const double DEFAULT_BI;
49
50 /** For the purpose of removing duplicate agents, the default JND of phase */
51 static const double DEFAULT_BT;
52
53 /** Inserts newAgent into the list in ascending order of beatInterval */
54 void add(Agent a) {
55 add(a, true);
56 } // add()/1
57
58 /** Appends newAgent to list (sort==false), or inserts newAgent into the list
59 * in ascending order of beatInterval
60 * @param newAgent The agent to be added to the list
61 * @param sort Flag indicating whether the list is sorted or not
62 */
63 void add(Agent newAgent, bool sort){
64 list.push_back(newAgent);
65 if (sort) this->sort();
66 } // add()/2
67
68 /** Sorts the AgentList by increasing beatInterval. */
69 void sort() {
70 std::sort(list.begin(), list.end());
71 } // sort()
72
73 /** Removes the given item from the list.
74 * @param ptr Points to the Agent which is removed from the list
75 */
76 void remove(iterator itr) {
77 list.erase(itr);
78 } // remove()
79
80 protected:
81 /** Removes Agents from the list which are duplicates of other Agents.
82 * A duplicate is defined by the tempo and phase thresholds
83 * thresholdBI and thresholdBT respectively.
84 */
85 void removeDuplicates() {
86 sort();
87 for (iterator itr = begin(); itr != end(); ++itr) {
88 if (itr->phaseScore < 0.0) // already flagged for deletion
89 continue;
90 iterator itr2 = itr;
91 for (++itr2; itr != end(); ++itr) {
92 if (itr2->beatInterval - itr->beatInterval > DEFAULT_BI)
93 break;
94 if (fabs(itr->beatTime - itr2->beatTime) > DEFAULT_BT)
95 continue;
96 if (itr->phaseScore < itr2->phaseScore) {
97 itr->phaseScore = -1.0; // flag for deletion
98 if (itr2->topScoreTime < itr->topScoreTime)
99 itr2->topScoreTime = itr->topScoreTime;
100 break;
101 } else {
102 itr2->phaseScore = -1.0; // flag for deletion
103 if (itr->topScoreTime < itr2->topScoreTime)
104 itr->topScoreTime = itr2->topScoreTime;
105 }
106 }
107 }
108 for (iterator itr = begin(); itr != end(); ) {
109 if (itr->phaseScore < 0.0) {
110 list.erase(itr);
111 } else {
112 ++itr;
113 }
114 }
115 } // removeDuplicates()
116
117 public:
118 /** Perform beat tracking on a list of events (onsets).
119 * @param el The list of onsets (or events or peaks) to beat track
120 */
121 void beatTrack(EventList el) {
122 beatTrack(el, -1.0);
123 } // beatTrack()/1
124
125 /** Perform beat tracking on a list of events (onsets).
126 * @param el The list of onsets (or events or peaks) to beat track.
127 * @param stop Do not find beats after <code>stop</code> seconds.
128 */
129 void beatTrack(EventList el, double stop) {
130 EventList::iterator ei = el.begin();
131 bool phaseGiven = !empty() && (begin()->beatTime >= 0); // if given for one, assume given for others
132 while (ei != el.end()) {
133 Event ev = *ei;
134 ++ei;
135 if ((stop > 0) && (ev.time > stop))
136 break;
137 bool created = phaseGiven;
138 double prevBeatInterval = -1.0;
139 // cc: Duplicate our list of agents, and scan through
140 // the (now immutable) copy. This means we can safely
141 // add agents to our own list while scanning without
142 // disrupting our scan.
143 Container currentAgents = list;
144 for (Container::iterator ai = currentAgents.begin();
145 ai != currentAgents.end(); ++ai) {
146 Agent currentAgent = *ai;
147 if (currentAgent.beatInterval != prevBeatInterval) {
148 if ((prevBeatInterval>=0) && !created && (ev.time<5.0)) {
149 // Create new agent with different phase
150 Agent newAgent(prevBeatInterval);
151 // This may add an agent to our list
152 newAgent.considerAsBeat(ev, *this);
153 }
154 prevBeatInterval = currentAgent.beatInterval;
155 created = phaseGiven;
156 }
157 if (currentAgent.considerAsBeat(ev, *this))
158 created = true;
159 } // loop for each agent
160 removeDuplicates();
161 } // loop for each event
162 } // beatTrack()
163
164 /** Finds the Agent with the highest score in the list, or NULL if beat tracking has failed.
165 * @return The Agent with the highest score
166 */
167 Agent *bestAgent() {
168 double best = -1.0;
169 Agent *bestAg = 0;
170 for (iterator itr = begin(); itr != end(); ++itr) {
171 double startTime = itr->events.begin()->time;
172 double conf = (itr->phaseScore + itr->tempoScore) /
173 (useAverageSalience? (double)itr->beatCount: 1.0);
174 if (conf > best) {
175 bestAg = &(*itr);
176 best = conf;
177 }
178 }
179 return bestAg;
180 } // bestAgent()
181
182
183 }; // class AgentList
184
185 #endif
186