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 #include "AgentList.h"
|
Chris@8
|
17
|
Chris@8
|
18 bool AgentList::useAverageSalience = false;
|
Chris@8
|
19 const double AgentList::DEFAULT_BI = 0.02;
|
Chris@8
|
20 const double AgentList::DEFAULT_BT = 0.04;
|
Chris@8
|
21
|
Chris@15
|
22 void AgentList::removeDuplicates()
|
Chris@15
|
23 {
|
Chris@15
|
24 sort();
|
Chris@15
|
25 for (iterator itr = begin(); itr != end(); ++itr) {
|
Chris@22
|
26 #ifdef DEBUG_BEATROOT
|
Chris@22
|
27 std::cerr << "removeDuplicates: considering agent " << (*itr)->idNumber << std::endl;
|
Chris@22
|
28 #endif
|
Chris@16
|
29 if ((*itr)->phaseScore < 0.0) // already flagged for deletion
|
Chris@15
|
30 continue;
|
Chris@15
|
31 iterator itr2 = itr;
|
Chris@15
|
32 for (++itr2; itr2 != end(); ++itr2) {
|
Chris@16
|
33 if ((*itr2)->beatInterval - (*itr)->beatInterval > DEFAULT_BI)
|
Chris@15
|
34 break;
|
Chris@16
|
35 if (fabs((*itr)->beatTime - (*itr2)->beatTime) > DEFAULT_BT)
|
Chris@15
|
36 continue;
|
Chris@16
|
37 if ((*itr)->phaseScore < (*itr2)->phaseScore) {
|
Chris@22
|
38 #ifdef DEBUG_BEATROOT
|
Chris@22
|
39 std::cerr << "agent " << (*itr)->idNumber << " is similar to but lower-scoring than agent " << (*itr2)->idNumber << ", marking for deletion" << std::endl;
|
Chris@22
|
40 #endif
|
Chris@16
|
41 (*itr)->phaseScore = -1.0; // flag for deletion
|
Chris@16
|
42 if ((*itr2)->topScoreTime < (*itr)->topScoreTime)
|
Chris@16
|
43 (*itr2)->topScoreTime = (*itr)->topScoreTime;
|
Chris@15
|
44 break;
|
Chris@15
|
45 } else {
|
Chris@22
|
46 #ifdef DEBUG_BEATROOT
|
Chris@22
|
47 std::cerr << "agent " << (*itr2)->idNumber << " is similar to but lower-scoring than agent " << (*itr)->idNumber << ", marking for deletion" << std::endl;
|
Chris@22
|
48 #endif
|
Chris@16
|
49 (*itr2)->phaseScore = -1.0; // flag for deletion
|
Chris@16
|
50 if ((*itr)->topScoreTime < (*itr2)->topScoreTime)
|
Chris@16
|
51 (*itr)->topScoreTime = (*itr2)->topScoreTime;
|
Chris@15
|
52 }
|
Chris@15
|
53 }
|
Chris@15
|
54 }
|
Chris@15
|
55 int removed = 0;
|
Chris@15
|
56 for (iterator itr = begin(); itr != end(); ) {
|
Chris@16
|
57 if ((*itr)->phaseScore < 0.0) {
|
Chris@15
|
58 ++removed;
|
Chris@16
|
59 delete *itr;
|
Chris@15
|
60 list.erase(itr);
|
Chris@15
|
61 } else {
|
Chris@15
|
62 ++itr;
|
Chris@15
|
63 }
|
Chris@15
|
64 }
|
Chris@15
|
65 #ifdef DEBUG_BEATROOT
|
Chris@15
|
66 if (removed > 0) {
|
Chris@15
|
67 std::cerr << "removeDuplicates: removed " << removed << ", have "
|
Chris@15
|
68 << list.size() << " agent(s) remaining" << std::endl;
|
Chris@15
|
69 }
|
Chris@15
|
70 int n = 0;
|
Chris@15
|
71 for (Container::iterator i = list.begin(); i != list.end(); ++i) {
|
Chris@16
|
72 std::cerr << "agent " << n++ << ": time " << (*i)->beatTime << std::endl;
|
Chris@15
|
73 }
|
Chris@15
|
74 #endif
|
Chris@15
|
75 } // removeDuplicates()
|
Chris@15
|
76
|
Chris@15
|
77
|
Chris@23
|
78 void AgentList::beatTrack(EventList el, AgentParameters params, double stop)
|
Chris@15
|
79 {
|
Chris@15
|
80 EventList::iterator ei = el.begin();
|
Chris@16
|
81 bool phaseGiven = !empty() && ((*begin())->beatTime >= 0); // if given for one, assume given for others
|
Chris@15
|
82 while (ei != el.end()) {
|
Chris@15
|
83 Event ev = *ei;
|
Chris@15
|
84 ++ei;
|
Chris@15
|
85 if ((stop > 0) && (ev.time > stop))
|
Chris@15
|
86 break;
|
Chris@15
|
87 bool created = phaseGiven;
|
Chris@15
|
88 double prevBeatInterval = -1.0;
|
Chris@15
|
89 // cc: Duplicate our list of agents, and scan through the
|
Chris@15
|
90 // copy. This means we can safely add agents to our own
|
Chris@15
|
91 // list while scanning without disrupting our scan. Each
|
Chris@15
|
92 // agent needs to be re-added to our own list explicitly
|
Chris@15
|
93 // (since it is modified by e.g. considerAsBeat)
|
Chris@15
|
94 Container currentAgents = list;
|
Chris@15
|
95 list.clear();
|
Chris@15
|
96 for (Container::iterator ai = currentAgents.begin();
|
Chris@15
|
97 ai != currentAgents.end(); ++ai) {
|
Chris@16
|
98 Agent *currentAgent = *ai;
|
Chris@16
|
99 if (currentAgent->beatInterval != prevBeatInterval) {
|
Chris@15
|
100 if ((prevBeatInterval>=0) && !created && (ev.time<5.0)) {
|
Chris@15
|
101 #ifdef DEBUG_BEATROOT
|
Chris@15
|
102 std::cerr << "Creating a new agent" << std::endl;
|
Chris@15
|
103 #endif
|
Chris@15
|
104 // Create new agent with different phase
|
Chris@23
|
105 Agent *newAgent = new Agent(params, prevBeatInterval);
|
Chris@15
|
106 // This may add another agent to our list as well
|
Chris@16
|
107 newAgent->considerAsBeat(ev, *this);
|
Chris@15
|
108 add(newAgent);
|
Chris@15
|
109 }
|
Chris@16
|
110 prevBeatInterval = currentAgent->beatInterval;
|
Chris@15
|
111 created = phaseGiven;
|
Chris@15
|
112 }
|
Chris@16
|
113 if (currentAgent->considerAsBeat(ev, *this))
|
Chris@15
|
114 created = true;
|
Chris@15
|
115 add(currentAgent);
|
Chris@15
|
116 } // loop for each agent
|
Chris@15
|
117 removeDuplicates();
|
Chris@15
|
118 } // loop for each event
|
Chris@15
|
119 } // beatTrack()
|
Chris@15
|
120
|
Chris@15
|
121 Agent *AgentList::bestAgent()
|
Chris@15
|
122 {
|
Chris@15
|
123 double best = -1.0;
|
Chris@15
|
124 Agent *bestAg = 0;
|
Chris@15
|
125 for (iterator itr = begin(); itr != end(); ++itr) {
|
Chris@16
|
126 if ((*itr)->events.empty()) continue;
|
Chris@16
|
127 double conf = ((*itr)->phaseScore + (*itr)->tempoScore) /
|
Chris@16
|
128 (useAverageSalience? (double)(*itr)->beatCount: 1.0);
|
Chris@15
|
129 if (conf > best) {
|
Chris@16
|
130 bestAg = *itr;
|
Chris@15
|
131 best = conf;
|
Chris@15
|
132 }
|
Chris@15
|
133 }
|
Chris@15
|
134 #ifdef DEBUG_BEATROOT
|
Chris@15
|
135 if (bestAg) {
|
Chris@15
|
136 std::cerr << "Best agent: Ag#" << bestAg->idNumber << std::endl;
|
Chris@15
|
137 std::cerr << " Av-salience = " << best << std::endl;
|
Chris@15
|
138 } else {
|
Chris@15
|
139 std::cerr << "No surviving agent - beat tracking failed" << std::endl;
|
Chris@15
|
140 }
|
Chris@15
|
141 #endif
|
Chris@15
|
142 return bestAg;
|
Chris@15
|
143 } // bestAgent()
|
Chris@15
|
144
|