Revision 23:633ec097fa56

View differences:

Agent.cpp
16 16
#include "Agent.h"
17 17
#include "BeatTracker.h"
18 18

  
19
double Agent::POST_MARGIN_FACTOR = 0.3;
20
double Agent::PRE_MARGIN_FACTOR = 0.15;
19
const double AgentParameters::DEFAULT_POST_MARGIN_FACTOR = 0.3;
20
const double AgentParameters::DEFAULT_PRE_MARGIN_FACTOR = 0.15;
21
const double AgentParameters::DEFAULT_MAX_CHANGE = 0.2;
22
const double AgentParameters::DEFAULT_EXPIRY_TIME = 10.0;
23

  
21 24
const double Agent::INNER_MARGIN = 0.040;
22
double Agent::MAX_CHANGE = 0.2;
23
double Agent::CONF_FACTOR = 0.5;
25
const double Agent::CONF_FACTOR = 0.5;
24 26
const double Agent::DEFAULT_CORRECTION_FACTOR = 50.0;
25
const double Agent::DEFAULT_EXPIRY_TIME = 10.0;
26 27

  
27 28
int Agent::idCounter = 0;
28 29

  
29
double Agent::innerMargin = 0.0;
30
double Agent::correctionFactor = 0.0;
31
double Agent::expiryTime = 0.0;
32
double Agent::decayFactor = 0.0;
33

  
34 30
void Agent::accept(Event e, double err, int beats) {
35 31
    beatTime = e.time;
36 32
    events.push_back(e);
37 33
    if (fabs(initialBeatInterval - beatInterval -
38
             err / correctionFactor) < MAX_CHANGE * initialBeatInterval)
34
             err / correctionFactor) < maxChange * initialBeatInterval)
39 35
        beatInterval += err / correctionFactor;// Adjust tempo
40 36
    beatCount += beats;
41 37
    double conFactor = 1.0 - CONF_FACTOR * err /
Agent.h
26 26

  
27 27
class AgentList;
28 28

  
29
class AgentParameters
30
{
31
public:
32
    static const double DEFAULT_POST_MARGIN_FACTOR;
33
    static const double DEFAULT_PRE_MARGIN_FACTOR;
34
    static const double DEFAULT_MAX_CHANGE;
35
    static const double DEFAULT_EXPIRY_TIME;
36

  
37
    AgentParameters() :
38
        postMarginFactor(DEFAULT_POST_MARGIN_FACTOR),
39
        preMarginFactor(DEFAULT_PRE_MARGIN_FACTOR),
40
        maxChange(DEFAULT_MAX_CHANGE),
41
        expiryTime(DEFAULT_EXPIRY_TIME) { }
42

  
43
    /** The maximum amount by which a beat can be later than the
44
     *  predicted beat time, expressed as a fraction of the beat
45
     *  period. */
46
    double postMarginFactor;
47

  
48
    /** The maximum amount by which a beat can be earlier than the
49
     *  predicted beat time, expressed as a fraction of the beat
50
     *  period. */
51
    double preMarginFactor;
52

  
53
    /** The maximum allowed deviation from the initial tempo,
54
     * expressed as a fraction of the initial beat period. */
55
    double maxChange;
56

  
57
    /** The default value of expiryTime, which is the time (in
58
     *  seconds) after which an Agent that has no Event matching its
59
     *  beat predictions will be destroyed. */
60
    double expiryTime;
61
};
62

  
29 63
/** Agent is the central class for beat tracking.
30 64
 *  Each Agent object has a tempo hypothesis, a history of tracked beats, and
31 65
 *  a score evaluating the continuity, regularity and salience of its beat track.
......
33 67
class Agent
34 68
{
35 69
public:
36
    /** The maximum amount by which a beat can be later than the predicted beat time,
37
     *  expressed as a fraction of the beat period. */
38
    static double POST_MARGIN_FACTOR;
39

  
40
    /** The maximum amount by which a beat can be earlier than the predicted beat time,
41
     *  expressed as a fraction of the beat period. */
42
    static double PRE_MARGIN_FACTOR;
43
	
44
    /** The default value of innerMargin, which is the maximum time (in seconds) that a
45
     * 	beat can deviate from the predicted beat time without a fork occurring. */
70
    /** The default value of innerMargin, which is the maximum time
71
     * 	(in seconds) that a beat can deviate from the predicted beat
72
     * 	time without a fork occurring. */
46 73
    static const double INNER_MARGIN;
47 74
	
48
    /** The maximum allowed deviation from the initial tempo, expressed as a fraction of the initial beat period. */
49
    static double MAX_CHANGE;
50
		
51
    /** The slope of the penalty function for onsets which do not coincide precisely with predicted beat times. */
52
    static double CONF_FACTOR;
75
    /** The slope of the penalty function for onsets which do not
76
     * coincide precisely with predicted beat times. */
77
    static const double CONF_FACTOR;
53 78
	
54
    /** The reactiveness/inertia balance, i.e. degree of change in the tempo, is controlled by the correctionFactor
55
     *  variable.  This constant defines its default value, which currently is not subsequently changed. The
56
     *  beat period is updated by the reciprocal of the correctionFactor multiplied by the difference between the
57
     *  predicted beat time and matching onset. */
79
    /** The reactiveness/inertia balance, i.e. degree of change in the
80
     *  tempo, is controlled by the correctionFactor variable.  This
81
     *  constant defines its default value, which currently is not
82
     *  subsequently changed. The beat period is updated by the
83
     *  reciprocal of the correctionFactor multiplied by the
84
     *  difference between the predicted beat time and matching
85
     *  onset. */
58 86
    static const double DEFAULT_CORRECTION_FACTOR;
59 87
	
60
    /** The default value of expiryTime, which is the time (in seconds) after which an Agent that
61
     *  has no Event matching its beat predictions will be destroyed. */
62
    static const double DEFAULT_EXPIRY_TIME;
63

  
64 88
protected:
65 89
    /** The identity number of the next created Agent */
66 90
    static int idCounter;
67 91
	
68
    /** The maximum time (in seconds) that a beat can deviate from the predicted beat time
69
     *  without a fork occurring (i.e. a 2nd Agent being created). */
70
    static double innerMargin;
92
    /** The maximum time (in seconds) that a beat can deviate from the
93
     *  predicted beat time without a fork occurring (i.e. a 2nd Agent
94
     *  being created). */
95
    double innerMargin;
71 96

  
72
    /** Controls the reactiveness/inertia balance, i.e. degree of change in the tempo.  The
73
     *  beat period is updated by the reciprocal of the correctionFactor multiplied by the difference between the
74
     *  predicted beat time and matching onset. */
75
    static double correctionFactor;
97
    /** Controls the reactiveness/inertia balance, i.e. degree of
98
     *  change in the tempo.  The beat period is updated by the
99
     *  reciprocal of the correctionFactor multiplied by the
100
     *  difference between the predicted beat time and matching
101
     *  onset. */
102
    double correctionFactor;
76 103

  
77
    /** The time (in seconds) after which an Agent that
78
     *  has no Event matching its beat predictions will be destroyed. */
79
    static double expiryTime;
104
    /** The time (in seconds) after which an Agent that has no Event
105
     *  matching its beat predictions will be destroyed. */
106
    double expiryTime;
80 107
	
81
    /** For scoring Agents in a (non-existent) real-time version (otherwise not used). */
82
    static double decayFactor;
108
    /** For scoring Agents in a (non-existent) real-time version
109
     * (otherwise not used). */
110
    double decayFactor;
83 111

  
84 112
public:
85 113
    /** The size of the outer half-window before the predicted beat time. */
......
94 122
    /** To be used in real-time version?? */
95 123
    double tempoScore;
96 124
	
97
    /** Sum of salience values of the Events which have been interpreted
98
     *  as beats by this Agent, weighted by their nearness to the predicted beat times. */
125
    /** Sum of salience values of the Events which have been
126
     *  interpreted as beats by this Agent, weighted by their nearness
127
     *  to the predicted beat times. */
99 128
    double phaseScore;
100 129
	
101
    /** How long has this agent been the best?  For real-time version; otherwise not used. */
130
    /** How long has this agent been the best?  For real-time version;
131
     * otherwise not used. */
102 132
    double topScoreTime;
103 133
	
104
    /** The number of beats found by this Agent, including interpolated beats. */
134
    /** The number of beats found by this Agent, including
135
     * interpolated beats. */
105 136
    int beatCount;
106 137
	
107
    /** The current tempo hypothesis of the Agent, expressed as the beat period in seconds. */
138
    /** The current tempo hypothesis of the Agent, expressed as the
139
     * beat period in seconds. */
108 140
    double beatInterval;
109 141

  
110
    /** The initial tempo hypothesis of the Agent, expressed as the beat period in seconds. */
142
    /** The initial tempo hypothesis of the Agent, expressed as the
143
     * beat period in seconds. */
111 144
    double initialBeatInterval;
112 145
	
113 146
    /** The time of the most recent beat accepted by this Agent. */
114 147
    double beatTime;
148

  
149
    /** The maximum allowed deviation from the initial tempo,
150
     * expressed as a fraction of the initial beat period. */
151
    double maxChange;
115 152
	
116
    /** The list of Events (onsets) accepted by this Agent as beats, plus interpolated beats. */
153
    /** The list of Events (onsets) accepted by this Agent as beats,
154
     * plus interpolated beats. */
117 155
    EventList events;
118 156

  
119 157
    /** Constructor: the work is performed by init()
120 158
     *  @param ibi The beat period (inter-beat interval) of the Agent's tempo hypothesis.
121 159
     */
122
    Agent(double ibi) {
123
	innerMargin = INNER_MARGIN;
124
	correctionFactor = DEFAULT_CORRECTION_FACTOR;
125
	expiryTime = DEFAULT_EXPIRY_TIME;
126
	decayFactor = 0;
127
	beatInterval = ibi;
128
	initialBeatInterval = ibi;
129
	postMargin = ibi * POST_MARGIN_FACTOR;
130
	preMargin = ibi * PRE_MARGIN_FACTOR;
131
	idNumber = idCounter++;
132
	phaseScore = 0.0;
133
	tempoScore = 0.0;
134
	topScoreTime = 0.0;
135
	beatCount = 0;
136
	beatTime = -1.0;
160
    Agent(AgentParameters params, double ibi) :
161
	innerMargin(INNER_MARGIN),
162
	correctionFactor(DEFAULT_CORRECTION_FACTOR),
163
	expiryTime(params.expiryTime),
164
	decayFactor(0),
165
	preMargin(ibi * params.preMarginFactor),
166
	postMargin(ibi * params.postMarginFactor),
167
	idNumber(idCounter++),
168
	tempoScore(0.0),
169
	phaseScore(0.0),
170
	topScoreTime(0.0),
171
	beatCount(0),
172
	beatInterval(ibi),
173
	initialBeatInterval(ibi),
174
	beatTime(-1.0),
175
        maxChange(params.maxChange) {
137 176
    } // constructor
138 177

  
139 178
    Agent *clone() const {
AgentList.cpp
75 75
} // removeDuplicates()
76 76

  
77 77

  
78
void AgentList::beatTrack(EventList el, double stop)
78
void AgentList::beatTrack(EventList el, AgentParameters params, double stop)
79 79
{
80 80
    EventList::iterator ei = el.begin();
81 81
    bool phaseGiven = !empty() && ((*begin())->beatTime >= 0); // if given for one, assume given for others
......
102 102
                    std::cerr << "Creating a new agent" << std::endl;
103 103
#endif
104 104
                    // Create new agent with different phase
105
                    Agent *newAgent = new Agent(prevBeatInterval);
105
                    Agent *newAgent = new Agent(params, prevBeatInterval);
106 106
                    // This may add another agent to our list as well
107 107
                    newAgent->considerAsBeat(ev, *this);
108 108
                    add(newAgent);
AgentList.h
121 121
    /** Perform beat tracking on a list of events (onsets).
122 122
     *  @param el The list of onsets (or events or peaks) to beat track
123 123
     */
124
    void beatTrack(EventList el) {
125
	beatTrack(el, -1.0);
124
    void beatTrack(EventList el, AgentParameters params) {
125
	beatTrack(el, params, -1.0);
126 126
    } // beatTrack()/1
127 127
	
128 128
    /** Perform beat tracking on a list of events (onsets).
129 129
     *  @param el The list of onsets (or events or peaks) to beat track.
130 130
     *  @param stop Do not find beats after <code>stop</code> seconds.
131 131
     */
132
    void beatTrack(EventList el, double stop);
132
    void beatTrack(EventList el, AgentParameters params, double stop);
133 133

  
134 134
    /** Finds the Agent with the highest score in the list, or NULL if beat tracking has failed.
135 135
     *  @return The Agent with the highest score
BeatRootProcessor.cpp
67 67
    std::cerr << "Onsets: " << onsetList.size() << std::endl;
68 68
#endif
69 69

  
70
    return BeatTracker::beatTrack(onsetList);
70
    return BeatTracker::beatTrack(agentParameters, onsetList);
71 71

  
72 72
} // processFile()
73 73

  
BeatRootProcessor.h
81 81
	
82 82
    /** The estimated onset times and their saliences. */	
83 83
    EventList onsetList;
84
    
85
    /** User-specifiable processing parameters. */
86
    AgentParameters agentParameters;
84 87
	
85 88
    /** Flag for suppressing all standard output messages except results. */
86 89
    static bool silent;
......
89 92

  
90 93
    /** Constructor: note that streams are not opened until the input
91 94
     *  file is set (see <code>setInputFile()</code>). */
92
    BeatRootProcessor(float sr) :
93
        sampleRate(sr) {
94
        hopSize = 0;
95
        fftSize = 0;
96
        hopTime = 0.010;
97
        fftTime = 0.04644;
95
    BeatRootProcessor(float sr, AgentParameters parameters) :
96
        sampleRate(sr),
97
        hopSize(0),
98
        fftSize(0),
99
        hopTime(0.010),
100
        fftTime(0.04644),
101
        agentParameters(parameters)
102
    {
98 103
        hopSize = lrint(sampleRate * hopTime);
99 104
        fftSize = lrint(pow(2, lrint( log(fftTime * sampleRate) / log(2))));
100 105
    } // constructor
BeatRootVampPlugin.cpp
24 24
BeatRootVampPlugin::BeatRootVampPlugin(float inputSampleRate) :
25 25
    Plugin(inputSampleRate)
26 26
{
27
    m_processor = new BeatRootProcessor(inputSampleRate);
27
    m_processor = new BeatRootProcessor(inputSampleRate, AgentParameters());
28 28
}
29 29

  
30 30
BeatRootVampPlugin::~BeatRootVampPlugin()
......
104 104
BeatRootVampPlugin::getParameterDescriptors() const
105 105
{
106 106
    ParameterList list;
107

  
108
    ParameterDescriptor desc;
109

  
110
    double postMarginFactor;
111

  
112
    /** The maximum amount by which a beat can be earlier than the
113
     *  predicted beat time, expressed as a fraction of the beat
114
     *  period. */
115
    double preMarginFactor;
116

  
117
    /** The maximum allowed deviation from the initial tempo,
118
     * expressed as a fraction of the initial beat period. */
119
    double maxChange;
120

  
121
    /** The default value of expiryTime, which is the time (in
122
     *  seconds) after which an Agent that has no Event matching its
123
     *  beat predictions will be destroyed. */
124
    
125
    desc.identifier = "preMarginFactor";
126
    desc.name = "Pre-Margin Factor";
127
    desc.description = "The maximum amount by which a beat can be earlier than the predicted beat time, expressed as a fraction of the beat period.";
128
    desc.minValue = 0;
129
    desc.maxValue = 1;
130
    desc.defaultValue = AgentParameters::DEFAULT_PRE_MARGIN_FACTOR;
131
    desc.isQuantized = false;
132
    list.push_back(desc);
133
    
134
    desc.identifier = "postMarginFactor";
135
    desc.name = "Post-Margin Factor";
136
    desc.description = "The maximum amount by which a beat can be later than the predicted beat time, expressed as a fraction of the beat period.";
137
    desc.minValue = 0;
138
    desc.maxValue = 1;
139
    desc.defaultValue = AgentParameters::DEFAULT_POST_MARGIN_FACTOR;
140
    desc.isQuantized = false;
141
    list.push_back(desc);
142
    
143
    desc.identifier = "maxChange";
144
    desc.name = "Maximum Change";
145
    desc.description = "The maximum allowed deviation from the initial tempo, expressed as a fraction of the initial beat period.";
146
    desc.minValue = 0;
147
    desc.maxValue = 1;
148
    desc.defaultValue = AgentParameters::DEFAULT_MAX_CHANGE;
149
    desc.isQuantized = false;
150
    list.push_back(desc);
151
    
152
    desc.identifier = "expiryTime";
153
    desc.name = "Expiry Time";
154
    desc.description = "The default value of expiryTime, which is the time (in seconds) after which an Agent that has no Event matching its beat predictions will be destroyed.";
155
    desc.minValue = 2;
156
    desc.maxValue = 120;
157
    desc.defaultValue = AgentParameters::DEFAULT_EXPIRY_TIME;
158
    desc.isQuantized = false;
159
    list.push_back(desc);
160

  
161
    // Simon says...
162

  
163
    // These are the parameters that should be exposed (Agent.cpp):
164

  
165
    // If Pop, both margins should be lower (0.1).  If classical
166
    // music, post margin can be increased
167
    //
168
    // double Agent::POST_MARGIN_FACTOR = 0.3;
169
    // double Agent::PRE_MARGIN_FACTOR = 0.15;
170
    //
171
    // Max Change tells us how much tempo can change - so for
172
    // classical we should make it higher
173
    // 
174
    // double Agent::MAX_CHANGE = 0.2;
175
    // 
176
    // The EXPIRY TIME default should be defaulted to 100 (usual cause
177
    // of agents dying....)  it should also be exposed in order to
178
    // troubleshoot eventual problems in songs with big silences in
179
    // the beggining/end.
180
    // 
181
    // const double Agent::DEFAULT_EXPIRY_TIME = 10.0;
182

  
107 183
    return list;
108 184
}
109 185

  
110 186
float
111 187
BeatRootVampPlugin::getParameter(string identifier) const
112 188
{
189
    if (identifier == "preMarginFactor") {
190
        return m_parameters.preMarginFactor;
191
    } else if (identifier == "postMarginFactor") {
192
        return m_parameters.postMarginFactor;
193
    } else if (identifier == "maxChange") {
194
        return m_parameters.maxChange;
195
    } else if (identifier == "expiryTime") {
196
        return m_parameters.expiryTime;
197
    }
198
    
113 199
    return 0;
114 200
}
115 201

  
116 202
void
117 203
BeatRootVampPlugin::setParameter(string identifier, float value) 
118 204
{
205
    if (identifier == "preMarginFactor") {
206
        m_parameters.preMarginFactor = value;
207
    } else if (identifier == "postMarginFactor") {
208
        m_parameters.postMarginFactor = value;
209
    } else if (identifier == "maxChange") {
210
        m_parameters.maxChange = value;
211
    } else if (identifier == "expiryTime") {
212
        m_parameters.expiryTime = value;
213
    }
119 214
}
120 215

  
121 216
BeatRootVampPlugin::ProgramList
......
187 282
	return false;
188 283
    }
189 284

  
190
    m_processor->reset();
285
    // Delete the processor that was created with default parameters
286
    // and used to determine the expected step and block size; replace
287
    // with one using the actual parameters we have
288
    delete m_processor;
289
    m_processor = new BeatRootProcessor(m_inputSampleRate, m_parameters);
191 290

  
192 291
    return true;
193 292
}
BeatRootVampPlugin.h
16 16
#ifndef _BEATROOT_VAMP_PLUGIN_H_
17 17
#define _BEATROOT_VAMP_PLUGIN_H_
18 18

  
19
#include "Agent.h"
20

  
19 21
#include <vamp-sdk/Plugin.h>
20 22

  
21 23
using std::string;
......
61 63

  
62 64
protected:
63 65
    BeatRootProcessor *m_processor;
66
    AgentParameters m_parameters;
64 67
};
65 68

  
66 69

  
BeatTracker.cpp
15 15

  
16 16
#include "BeatTracker.h"
17 17

  
18
EventList BeatTracker::beatTrack(EventList events, EventList beats)
18
EventList BeatTracker::beatTrack(AgentParameters params,
19
                                 EventList events, EventList beats)
19 20
{
20 21
    AgentList agents;
21 22
    int count = 0;
......
28 29
    }
29 30
    if (count > 0) { // tempo given by mean of initial beats
30 31
	double ioi = (beatTime - beats.begin()->time) / count;
31
	agents.push_back(new Agent(ioi));
32
	agents.push_back(new Agent(params, ioi));
32 33
    } else // tempo not given; use tempo induction
33
	agents = Induction::beatInduction(events);
34
	agents = Induction::beatInduction(params, events);
34 35
    if (!beats.empty())
35 36
	for (AgentList::iterator itr = agents.begin(); itr != agents.end();
36 37
	     ++itr) {
......
38 39
	    (*itr)->beatCount = count;
39 40
	    (*itr)->events = beats;
40 41
	}
41
    agents.beatTrack(events, -1);
42
    agents.beatTrack(events, params, -1);
42 43
    Agent *best = agents.bestAgent();
43 44
    EventList results;
44 45
    if (best) {
BeatTracker.h
56 56
     *  @param events The onsets or peaks in a feature list
57 57
     *  @return The list of beats, or an empty list if beat tracking fails
58 58
     */
59
    static EventList beatTrack(EventList events) {
60
	return beatTrack(events, EventList());
59
    static EventList beatTrack(AgentParameters params, EventList events) {
60
	return beatTrack(params, events, EventList());
61 61
    }
62 62
	
63 63
    /** Perform beat tracking.
......
65 65
     *  @param beats The initial beats which are given, if any
66 66
     *  @return The list of beats, or an empty list if beat tracking fails
67 67
     */
68
    static EventList beatTrack(EventList events, EventList beats);
68
    static EventList beatTrack(AgentParameters params,
69
                               EventList events, EventList beats);
69 70
	
70 71
	
71 72
    // Various get and set methods
Induction.cpp
23 23
int Induction::topN = 10;
24 24

  
25 25

  
26
AgentList Induction::beatInduction(EventList events) {
26
AgentList Induction::beatInduction(AgentParameters params, EventList events) {
27 27
    int i, j, b, bestCount;
28 28
    bool submult;
29 29
    int intervals = 0;			// number of interval clusters
......
183 183
        while (beat > maxIBI)		// Minimum speed
184 184
            beat /= 2.0;
185 185
        if (beat >= minIBI) {
186
            a.push_back(new Agent(beat));
186
            a.push_back(new Agent(params, beat));
187 187
        }
188 188
    }
189 189
#ifdef DEBUG_BEATROOT
Induction.h
68 68
     *  @return A list of beat tracking agents, where each is initialised with one
69 69
     *          of the top tempo hypotheses but no beats
70 70
     */
71
    static AgentList beatInduction(EventList events);
71
    static AgentList beatInduction(AgentParameters params, EventList events);
72 72

  
73 73
protected:
74 74
    /** For variable cluster widths in newInduction().

Also available in: Unified diff