To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

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