Mercurial > hg > beatroot-vamp
changeset 23:633ec097fa56
Expose the processing parameters Simon suggests
author | Chris Cannam |
---|---|
date | Tue, 03 Sep 2013 17:32:09 +0100 |
parents | 6afcb5edd7ab |
children | 3a5840de4d5f |
files | Agent.cpp Agent.h AgentList.cpp AgentList.h BeatRootProcessor.cpp BeatRootProcessor.h BeatRootVampPlugin.cpp BeatRootVampPlugin.h BeatTracker.cpp BeatTracker.h Induction.cpp Induction.h |
diffstat | 12 files changed, 236 insertions(+), 92 deletions(-) [+] |
line wrap: on
line diff
--- 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 /
--- 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 {
--- 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);
--- 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 <code>stop</code> 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
--- 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()
--- 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 <code>setInputFile()</code>). */ - 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
--- 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; }
--- 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 <vamp-sdk/Plugin.h> using std::string; @@ -61,6 +63,7 @@ protected: BeatRootProcessor *m_processor; + AgentParameters m_parameters; };
--- 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) {
--- 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
--- 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
--- 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().