# HG changeset patch
# User Chris Cannam
# Date 1378225929 -3600
# Node ID 633ec097fa5697b5b4595910274e70299d386798
# Parent 6afcb5edd7ab7ef005a49fdd810597f9f91f5ff6
Expose the processing parameters Simon suggests
diff -r 6afcb5edd7ab -r 633ec097fa56 Agent.cpp
--- a/Agent.cpp Wed Aug 28 16:50:40 2013 +0100
+++ b/Agent.cpp Tue Sep 03 17:32:09 2013 +0100
@@ -16,26 +16,22 @@
#include "Agent.h"
#include "BeatTracker.h"
-double Agent::POST_MARGIN_FACTOR = 0.3;
-double Agent::PRE_MARGIN_FACTOR = 0.15;
+const double AgentParameters::DEFAULT_POST_MARGIN_FACTOR = 0.3;
+const double AgentParameters::DEFAULT_PRE_MARGIN_FACTOR = 0.15;
+const double AgentParameters::DEFAULT_MAX_CHANGE = 0.2;
+const double AgentParameters::DEFAULT_EXPIRY_TIME = 10.0;
+
const double Agent::INNER_MARGIN = 0.040;
-double Agent::MAX_CHANGE = 0.2;
-double Agent::CONF_FACTOR = 0.5;
+const double Agent::CONF_FACTOR = 0.5;
const double Agent::DEFAULT_CORRECTION_FACTOR = 50.0;
-const double Agent::DEFAULT_EXPIRY_TIME = 10.0;
int Agent::idCounter = 0;
-double Agent::innerMargin = 0.0;
-double Agent::correctionFactor = 0.0;
-double Agent::expiryTime = 0.0;
-double Agent::decayFactor = 0.0;
-
void Agent::accept(Event e, double err, int beats) {
beatTime = e.time;
events.push_back(e);
if (fabs(initialBeatInterval - beatInterval -
- err / correctionFactor) < MAX_CHANGE * initialBeatInterval)
+ err / correctionFactor) < maxChange * initialBeatInterval)
beatInterval += err / correctionFactor;// Adjust tempo
beatCount += beats;
double conFactor = 1.0 - CONF_FACTOR * err /
diff -r 6afcb5edd7ab -r 633ec097fa56 Agent.h
--- a/Agent.h Wed Aug 28 16:50:40 2013 +0100
+++ b/Agent.h Tue Sep 03 17:32:09 2013 +0100
@@ -26,6 +26,40 @@
class AgentList;
+class AgentParameters
+{
+public:
+ static const double DEFAULT_POST_MARGIN_FACTOR;
+ static const double DEFAULT_PRE_MARGIN_FACTOR;
+ static const double DEFAULT_MAX_CHANGE;
+ static const double DEFAULT_EXPIRY_TIME;
+
+ AgentParameters() :
+ postMarginFactor(DEFAULT_POST_MARGIN_FACTOR),
+ preMarginFactor(DEFAULT_PRE_MARGIN_FACTOR),
+ maxChange(DEFAULT_MAX_CHANGE),
+ expiryTime(DEFAULT_EXPIRY_TIME) { }
+
+ /** The maximum amount by which a beat can be later than the
+ * predicted beat time, expressed as a fraction of the beat
+ * period. */
+ double postMarginFactor;
+
+ /** The maximum amount by which a beat can be earlier than the
+ * predicted beat time, expressed as a fraction of the beat
+ * period. */
+ double preMarginFactor;
+
+ /** The maximum allowed deviation from the initial tempo,
+ * expressed as a fraction of the initial beat period. */
+ double maxChange;
+
+ /** 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. */
+ double expiryTime;
+};
+
/** Agent is the central class for beat tracking.
* Each Agent object has a tempo hypothesis, a history of tracked beats, and
* a score evaluating the continuity, regularity and salience of its beat track.
@@ -33,53 +67,47 @@
class Agent
{
public:
- /** The maximum amount by which a beat can be later than the predicted beat time,
- * expressed as a fraction of the beat period. */
- static double POST_MARGIN_FACTOR;
-
- /** The maximum amount by which a beat can be earlier than the predicted beat time,
- * expressed as a fraction of the beat period. */
- static double PRE_MARGIN_FACTOR;
-
- /** The default value of innerMargin, which is the maximum time (in seconds) that a
- * beat can deviate from the predicted beat time without a fork occurring. */
+ /** The default value of innerMargin, which is the maximum time
+ * (in seconds) that a beat can deviate from the predicted beat
+ * time without a fork occurring. */
static const double INNER_MARGIN;
- /** The maximum allowed deviation from the initial tempo, expressed as a fraction of the initial beat period. */
- static double MAX_CHANGE;
-
- /** The slope of the penalty function for onsets which do not coincide precisely with predicted beat times. */
- static double CONF_FACTOR;
+ /** The slope of the penalty function for onsets which do not
+ * coincide precisely with predicted beat times. */
+ static const double CONF_FACTOR;
- /** The reactiveness/inertia balance, i.e. degree of change in the tempo, is controlled by the correctionFactor
- * variable. This constant defines its default value, which currently is not subsequently changed. The
- * beat period is updated by the reciprocal of the correctionFactor multiplied by the difference between the
- * predicted beat time and matching onset. */
+ /** The reactiveness/inertia balance, i.e. degree of change in the
+ * tempo, is controlled by the correctionFactor variable. This
+ * constant defines its default value, which currently is not
+ * subsequently changed. The beat period is updated by the
+ * reciprocal of the correctionFactor multiplied by the
+ * difference between the predicted beat time and matching
+ * onset. */
static const double DEFAULT_CORRECTION_FACTOR;
- /** 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. */
- static const double DEFAULT_EXPIRY_TIME;
-
protected:
/** The identity number of the next created Agent */
static int idCounter;
- /** The maximum time (in seconds) that a beat can deviate from the predicted beat time
- * without a fork occurring (i.e. a 2nd Agent being created). */
- static double innerMargin;
+ /** The maximum time (in seconds) that a beat can deviate from the
+ * predicted beat time without a fork occurring (i.e. a 2nd Agent
+ * being created). */
+ double innerMargin;
- /** Controls the reactiveness/inertia balance, i.e. degree of change in the tempo. The
- * beat period is updated by the reciprocal of the correctionFactor multiplied by the difference between the
- * predicted beat time and matching onset. */
- static double correctionFactor;
+ /** Controls the reactiveness/inertia balance, i.e. degree of
+ * change in the tempo. The beat period is updated by the
+ * reciprocal of the correctionFactor multiplied by the
+ * difference between the predicted beat time and matching
+ * onset. */
+ double correctionFactor;
- /** The time (in seconds) after which an Agent that
- * has no Event matching its beat predictions will be destroyed. */
- static double expiryTime;
+ /** The time (in seconds) after which an Agent that has no Event
+ * matching its beat predictions will be destroyed. */
+ double expiryTime;
- /** For scoring Agents in a (non-existent) real-time version (otherwise not used). */
- static double decayFactor;
+ /** For scoring Agents in a (non-existent) real-time version
+ * (otherwise not used). */
+ double decayFactor;
public:
/** The size of the outer half-window before the predicted beat time. */
@@ -94,46 +122,57 @@
/** To be used in real-time version?? */
double tempoScore;
- /** Sum of salience values of the Events which have been interpreted
- * as beats by this Agent, weighted by their nearness to the predicted beat times. */
+ /** Sum of salience values of the Events which have been
+ * interpreted as beats by this Agent, weighted by their nearness
+ * to the predicted beat times. */
double phaseScore;
- /** How long has this agent been the best? For real-time version; otherwise not used. */
+ /** How long has this agent been the best? For real-time version;
+ * otherwise not used. */
double topScoreTime;
- /** The number of beats found by this Agent, including interpolated beats. */
+ /** The number of beats found by this Agent, including
+ * interpolated beats. */
int beatCount;
- /** The current tempo hypothesis of the Agent, expressed as the beat period in seconds. */
+ /** The current tempo hypothesis of the Agent, expressed as the
+ * beat period in seconds. */
double beatInterval;
- /** The initial tempo hypothesis of the Agent, expressed as the beat period in seconds. */
+ /** The initial tempo hypothesis of the Agent, expressed as the
+ * beat period in seconds. */
double initialBeatInterval;
/** The time of the most recent beat accepted by this Agent. */
double beatTime;
+
+ /** The maximum allowed deviation from the initial tempo,
+ * expressed as a fraction of the initial beat period. */
+ double maxChange;
- /** The list of Events (onsets) accepted by this Agent as beats, plus interpolated beats. */
+ /** The list of Events (onsets) accepted by this Agent as beats,
+ * plus interpolated beats. */
EventList events;
/** Constructor: the work is performed by init()
* @param ibi The beat period (inter-beat interval) of the Agent's tempo hypothesis.
*/
- Agent(double ibi) {
- innerMargin = INNER_MARGIN;
- correctionFactor = DEFAULT_CORRECTION_FACTOR;
- expiryTime = DEFAULT_EXPIRY_TIME;
- decayFactor = 0;
- beatInterval = ibi;
- initialBeatInterval = ibi;
- postMargin = ibi * POST_MARGIN_FACTOR;
- preMargin = ibi * PRE_MARGIN_FACTOR;
- idNumber = idCounter++;
- phaseScore = 0.0;
- tempoScore = 0.0;
- topScoreTime = 0.0;
- beatCount = 0;
- beatTime = -1.0;
+ Agent(AgentParameters params, double ibi) :
+ innerMargin(INNER_MARGIN),
+ correctionFactor(DEFAULT_CORRECTION_FACTOR),
+ expiryTime(params.expiryTime),
+ decayFactor(0),
+ preMargin(ibi * params.preMarginFactor),
+ postMargin(ibi * params.postMarginFactor),
+ idNumber(idCounter++),
+ tempoScore(0.0),
+ phaseScore(0.0),
+ topScoreTime(0.0),
+ beatCount(0),
+ beatInterval(ibi),
+ initialBeatInterval(ibi),
+ beatTime(-1.0),
+ maxChange(params.maxChange) {
} // constructor
Agent *clone() const {
diff -r 6afcb5edd7ab -r 633ec097fa56 AgentList.cpp
--- a/AgentList.cpp Wed Aug 28 16:50:40 2013 +0100
+++ b/AgentList.cpp Tue Sep 03 17:32:09 2013 +0100
@@ -75,7 +75,7 @@
} // removeDuplicates()
-void AgentList::beatTrack(EventList el, double stop)
+void AgentList::beatTrack(EventList el, AgentParameters params, double stop)
{
EventList::iterator ei = el.begin();
bool phaseGiven = !empty() && ((*begin())->beatTime >= 0); // if given for one, assume given for others
@@ -102,7 +102,7 @@
std::cerr << "Creating a new agent" << std::endl;
#endif
// Create new agent with different phase
- Agent *newAgent = new Agent(prevBeatInterval);
+ Agent *newAgent = new Agent(params, prevBeatInterval);
// This may add another agent to our list as well
newAgent->considerAsBeat(ev, *this);
add(newAgent);
diff -r 6afcb5edd7ab -r 633ec097fa56 AgentList.h
--- a/AgentList.h Wed Aug 28 16:50:40 2013 +0100
+++ b/AgentList.h Tue Sep 03 17:32:09 2013 +0100
@@ -121,15 +121,15 @@
/** Perform beat tracking on a list of events (onsets).
* @param el The list of onsets (or events or peaks) to beat track
*/
- void beatTrack(EventList el) {
- beatTrack(el, -1.0);
+ void beatTrack(EventList el, AgentParameters params) {
+ beatTrack(el, params, -1.0);
} // beatTrack()/1
/** Perform beat tracking on a list of events (onsets).
* @param el The list of onsets (or events or peaks) to beat track.
* @param stop Do not find beats after stop
seconds.
*/
- void beatTrack(EventList el, double stop);
+ void beatTrack(EventList el, AgentParameters params, double stop);
/** Finds the Agent with the highest score in the list, or NULL if beat tracking has failed.
* @return The Agent with the highest score
diff -r 6afcb5edd7ab -r 633ec097fa56 BeatRootProcessor.cpp
--- a/BeatRootProcessor.cpp Wed Aug 28 16:50:40 2013 +0100
+++ b/BeatRootProcessor.cpp Tue Sep 03 17:32:09 2013 +0100
@@ -67,7 +67,7 @@
std::cerr << "Onsets: " << onsetList.size() << std::endl;
#endif
- return BeatTracker::beatTrack(onsetList);
+ return BeatTracker::beatTrack(agentParameters, onsetList);
} // processFile()
diff -r 6afcb5edd7ab -r 633ec097fa56 BeatRootProcessor.h
--- a/BeatRootProcessor.h Wed Aug 28 16:50:40 2013 +0100
+++ b/BeatRootProcessor.h Tue Sep 03 17:32:09 2013 +0100
@@ -81,6 +81,9 @@
/** The estimated onset times and their saliences. */
EventList onsetList;
+
+ /** User-specifiable processing parameters. */
+ AgentParameters agentParameters;
/** Flag for suppressing all standard output messages except results. */
static bool silent;
@@ -89,12 +92,14 @@
/** Constructor: note that streams are not opened until the input
* file is set (see setInputFile()
). */
- BeatRootProcessor(float sr) :
- sampleRate(sr) {
- hopSize = 0;
- fftSize = 0;
- hopTime = 0.010;
- fftTime = 0.04644;
+ BeatRootProcessor(float sr, AgentParameters parameters) :
+ sampleRate(sr),
+ hopSize(0),
+ fftSize(0),
+ hopTime(0.010),
+ fftTime(0.04644),
+ agentParameters(parameters)
+ {
hopSize = lrint(sampleRate * hopTime);
fftSize = lrint(pow(2, lrint( log(fftTime * sampleRate) / log(2))));
} // constructor
diff -r 6afcb5edd7ab -r 633ec097fa56 BeatRootVampPlugin.cpp
--- a/BeatRootVampPlugin.cpp Wed Aug 28 16:50:40 2013 +0100
+++ b/BeatRootVampPlugin.cpp Tue Sep 03 17:32:09 2013 +0100
@@ -24,7 +24,7 @@
BeatRootVampPlugin::BeatRootVampPlugin(float inputSampleRate) :
Plugin(inputSampleRate)
{
- m_processor = new BeatRootProcessor(inputSampleRate);
+ m_processor = new BeatRootProcessor(inputSampleRate, AgentParameters());
}
BeatRootVampPlugin::~BeatRootVampPlugin()
@@ -104,18 +104,113 @@
BeatRootVampPlugin::getParameterDescriptors() const
{
ParameterList list;
+
+ ParameterDescriptor desc;
+
+ double postMarginFactor;
+
+ /** The maximum amount by which a beat can be earlier than the
+ * predicted beat time, expressed as a fraction of the beat
+ * period. */
+ double preMarginFactor;
+
+ /** The maximum allowed deviation from the initial tempo,
+ * expressed as a fraction of the initial beat period. */
+ double maxChange;
+
+ /** 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. */
+
+ desc.identifier = "preMarginFactor";
+ desc.name = "Pre-Margin Factor";
+ 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.";
+ desc.minValue = 0;
+ desc.maxValue = 1;
+ desc.defaultValue = AgentParameters::DEFAULT_PRE_MARGIN_FACTOR;
+ desc.isQuantized = false;
+ list.push_back(desc);
+
+ desc.identifier = "postMarginFactor";
+ desc.name = "Post-Margin Factor";
+ 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.";
+ desc.minValue = 0;
+ desc.maxValue = 1;
+ desc.defaultValue = AgentParameters::DEFAULT_POST_MARGIN_FACTOR;
+ desc.isQuantized = false;
+ list.push_back(desc);
+
+ desc.identifier = "maxChange";
+ desc.name = "Maximum Change";
+ desc.description = "The maximum allowed deviation from the initial tempo, expressed as a fraction of the initial beat period.";
+ desc.minValue = 0;
+ desc.maxValue = 1;
+ desc.defaultValue = AgentParameters::DEFAULT_MAX_CHANGE;
+ desc.isQuantized = false;
+ list.push_back(desc);
+
+ desc.identifier = "expiryTime";
+ desc.name = "Expiry Time";
+ 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.";
+ desc.minValue = 2;
+ desc.maxValue = 120;
+ desc.defaultValue = AgentParameters::DEFAULT_EXPIRY_TIME;
+ desc.isQuantized = false;
+ list.push_back(desc);
+
+ // Simon says...
+
+ // These are the parameters that should be exposed (Agent.cpp):
+
+ // If Pop, both margins should be lower (0.1). If classical
+ // music, post margin can be increased
+ //
+ // double Agent::POST_MARGIN_FACTOR = 0.3;
+ // double Agent::PRE_MARGIN_FACTOR = 0.15;
+ //
+ // Max Change tells us how much tempo can change - so for
+ // classical we should make it higher
+ //
+ // double Agent::MAX_CHANGE = 0.2;
+ //
+ // The EXPIRY TIME default should be defaulted to 100 (usual cause
+ // of agents dying....) it should also be exposed in order to
+ // troubleshoot eventual problems in songs with big silences in
+ // the beggining/end.
+ //
+ // const double Agent::DEFAULT_EXPIRY_TIME = 10.0;
+
return list;
}
float
BeatRootVampPlugin::getParameter(string identifier) const
{
+ if (identifier == "preMarginFactor") {
+ return m_parameters.preMarginFactor;
+ } else if (identifier == "postMarginFactor") {
+ return m_parameters.postMarginFactor;
+ } else if (identifier == "maxChange") {
+ return m_parameters.maxChange;
+ } else if (identifier == "expiryTime") {
+ return m_parameters.expiryTime;
+ }
+
return 0;
}
void
BeatRootVampPlugin::setParameter(string identifier, float value)
{
+ if (identifier == "preMarginFactor") {
+ m_parameters.preMarginFactor = value;
+ } else if (identifier == "postMarginFactor") {
+ m_parameters.postMarginFactor = value;
+ } else if (identifier == "maxChange") {
+ m_parameters.maxChange = value;
+ } else if (identifier == "expiryTime") {
+ m_parameters.expiryTime = value;
+ }
}
BeatRootVampPlugin::ProgramList
@@ -187,7 +282,11 @@
return false;
}
- m_processor->reset();
+ // Delete the processor that was created with default parameters
+ // and used to determine the expected step and block size; replace
+ // with one using the actual parameters we have
+ delete m_processor;
+ m_processor = new BeatRootProcessor(m_inputSampleRate, m_parameters);
return true;
}
diff -r 6afcb5edd7ab -r 633ec097fa56 BeatRootVampPlugin.h
--- a/BeatRootVampPlugin.h Wed Aug 28 16:50:40 2013 +0100
+++ b/BeatRootVampPlugin.h Tue Sep 03 17:32:09 2013 +0100
@@ -16,6 +16,8 @@
#ifndef _BEATROOT_VAMP_PLUGIN_H_
#define _BEATROOT_VAMP_PLUGIN_H_
+#include "Agent.h"
+
#include
using std::string;
@@ -61,6 +63,7 @@
protected:
BeatRootProcessor *m_processor;
+ AgentParameters m_parameters;
};
diff -r 6afcb5edd7ab -r 633ec097fa56 BeatTracker.cpp
--- a/BeatTracker.cpp Wed Aug 28 16:50:40 2013 +0100
+++ b/BeatTracker.cpp Tue Sep 03 17:32:09 2013 +0100
@@ -15,7 +15,8 @@
#include "BeatTracker.h"
-EventList BeatTracker::beatTrack(EventList events, EventList beats)
+EventList BeatTracker::beatTrack(AgentParameters params,
+ EventList events, EventList beats)
{
AgentList agents;
int count = 0;
@@ -28,9 +29,9 @@
}
if (count > 0) { // tempo given by mean of initial beats
double ioi = (beatTime - beats.begin()->time) / count;
- agents.push_back(new Agent(ioi));
+ agents.push_back(new Agent(params, ioi));
} else // tempo not given; use tempo induction
- agents = Induction::beatInduction(events);
+ agents = Induction::beatInduction(params, events);
if (!beats.empty())
for (AgentList::iterator itr = agents.begin(); itr != agents.end();
++itr) {
@@ -38,7 +39,7 @@
(*itr)->beatCount = count;
(*itr)->events = beats;
}
- agents.beatTrack(events, -1);
+ agents.beatTrack(events, params, -1);
Agent *best = agents.bestAgent();
EventList results;
if (best) {
diff -r 6afcb5edd7ab -r 633ec097fa56 BeatTracker.h
--- a/BeatTracker.h Wed Aug 28 16:50:40 2013 +0100
+++ b/BeatTracker.h Tue Sep 03 17:32:09 2013 +0100
@@ -56,8 +56,8 @@
* @param events The onsets or peaks in a feature list
* @return The list of beats, or an empty list if beat tracking fails
*/
- static EventList beatTrack(EventList events) {
- return beatTrack(events, EventList());
+ static EventList beatTrack(AgentParameters params, EventList events) {
+ return beatTrack(params, events, EventList());
}
/** Perform beat tracking.
@@ -65,7 +65,8 @@
* @param beats The initial beats which are given, if any
* @return The list of beats, or an empty list if beat tracking fails
*/
- static EventList beatTrack(EventList events, EventList beats);
+ static EventList beatTrack(AgentParameters params,
+ EventList events, EventList beats);
// Various get and set methods
diff -r 6afcb5edd7ab -r 633ec097fa56 Induction.cpp
--- a/Induction.cpp Wed Aug 28 16:50:40 2013 +0100
+++ b/Induction.cpp Tue Sep 03 17:32:09 2013 +0100
@@ -23,7 +23,7 @@
int Induction::topN = 10;
-AgentList Induction::beatInduction(EventList events) {
+AgentList Induction::beatInduction(AgentParameters params, EventList events) {
int i, j, b, bestCount;
bool submult;
int intervals = 0; // number of interval clusters
@@ -183,7 +183,7 @@
while (beat > maxIBI) // Minimum speed
beat /= 2.0;
if (beat >= minIBI) {
- a.push_back(new Agent(beat));
+ a.push_back(new Agent(params, beat));
}
}
#ifdef DEBUG_BEATROOT
diff -r 6afcb5edd7ab -r 633ec097fa56 Induction.h
--- a/Induction.h Wed Aug 28 16:50:40 2013 +0100
+++ b/Induction.h Tue Sep 03 17:32:09 2013 +0100
@@ -68,7 +68,7 @@
* @return A list of beat tracking agents, where each is initialised with one
* of the top tempo hypotheses but no beats
*/
- static AgentList beatInduction(EventList events);
+ static AgentList beatInduction(AgentParameters params, EventList events);
protected:
/** For variable cluster widths in newInduction().