annotate BeatRootVampPlugin.cpp @ 37:1f175ae200a6 tip

Update RDF
author Chris Cannam
date Wed, 25 Jun 2014 13:48:49 +0100
parents 937432fc2898
children
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@0 4 Vamp feature extraction plugin for the BeatRoot beat tracker.
Chris@0 5
Chris@0 6 Centre for Digital Music, Queen Mary, University of London.
Chris@0 7 This file copyright 2011 Simon Dixon, Chris Cannam and QMUL.
Chris@0 8
Chris@0 9 This program is free software; you can redistribute it and/or
Chris@0 10 modify it under the terms of the GNU General Public License as
Chris@0 11 published by the Free Software Foundation; either version 2 of the
Chris@0 12 License, or (at your option) any later version. See the file
Chris@0 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "BeatRootVampPlugin.h"
Chris@2 17 #include "BeatRootProcessor.h"
Chris@0 18
Chris@10 19 #include "Event.h"
Chris@10 20
Chris@10 21 #include <vamp-sdk/RealTime.h>
Chris@9 22 #include <vamp-sdk/PluginAdapter.h>
Chris@0 23
Chris@0 24 BeatRootVampPlugin::BeatRootVampPlugin(float inputSampleRate) :
Chris@31 25 Plugin(inputSampleRate),
Chris@31 26 m_firstFrame(true)
Chris@0 27 {
Chris@23 28 m_processor = new BeatRootProcessor(inputSampleRate, AgentParameters());
Chris@0 29 }
Chris@0 30
Chris@0 31 BeatRootVampPlugin::~BeatRootVampPlugin()
Chris@0 32 {
Chris@0 33 delete m_processor;
Chris@0 34 }
Chris@0 35
Chris@0 36 string
Chris@0 37 BeatRootVampPlugin::getIdentifier() const
Chris@0 38 {
Chris@0 39 return "beatroot";
Chris@0 40 }
Chris@0 41
Chris@0 42 string
Chris@0 43 BeatRootVampPlugin::getName() const
Chris@0 44 {
Chris@0 45 return "BeatRoot Beat Tracker";
Chris@0 46 }
Chris@0 47
Chris@0 48 string
Chris@0 49 BeatRootVampPlugin::getDescription() const
Chris@0 50 {
Chris@0 51 return "Identify beat locations in music";
Chris@0 52 }
Chris@0 53
Chris@0 54 string
Chris@0 55 BeatRootVampPlugin::getMaker() const
Chris@0 56 {
Chris@0 57 return "Simon Dixon (plugin by Chris Cannam)";
Chris@0 58 }
Chris@0 59
Chris@0 60 int
Chris@0 61 BeatRootVampPlugin::getPluginVersion() const
Chris@0 62 {
Chris@0 63 // Increment this each time you release a version that behaves
Chris@0 64 // differently from the previous one
Chris@0 65 return 1;
Chris@0 66 }
Chris@0 67
Chris@0 68 string
Chris@0 69 BeatRootVampPlugin::getCopyright() const
Chris@0 70 {
Chris@0 71 return "GPL";
Chris@0 72 }
Chris@0 73
Chris@0 74 BeatRootVampPlugin::InputDomain
Chris@0 75 BeatRootVampPlugin::getInputDomain() const
Chris@0 76 {
Chris@0 77 return FrequencyDomain;
Chris@0 78 }
Chris@0 79
Chris@0 80 size_t
Chris@0 81 BeatRootVampPlugin::getPreferredBlockSize() const
Chris@0 82 {
Chris@0 83 return m_processor->getFFTSize();
Chris@0 84 }
Chris@0 85
Chris@0 86 size_t
Chris@0 87 BeatRootVampPlugin::getPreferredStepSize() const
Chris@0 88 {
Chris@0 89 return m_processor->getHopSize();
Chris@0 90 }
Chris@0 91
Chris@0 92 size_t
Chris@0 93 BeatRootVampPlugin::getMinChannelCount() const
Chris@0 94 {
Chris@0 95 return 1;
Chris@0 96 }
Chris@0 97
Chris@0 98 size_t
Chris@0 99 BeatRootVampPlugin::getMaxChannelCount() const
Chris@0 100 {
Chris@0 101 return 1;
Chris@0 102 }
Chris@0 103
Chris@0 104 BeatRootVampPlugin::ParameterList
Chris@0 105 BeatRootVampPlugin::getParameterDescriptors() const
Chris@0 106 {
Chris@0 107 ParameterList list;
Chris@23 108
Chris@23 109 ParameterDescriptor desc;
Chris@23 110
Chris@23 111 desc.identifier = "preMarginFactor";
Chris@23 112 desc.name = "Pre-Margin Factor";
Chris@23 113 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.";
Chris@23 114 desc.minValue = 0;
Chris@23 115 desc.maxValue = 1;
Chris@23 116 desc.defaultValue = AgentParameters::DEFAULT_PRE_MARGIN_FACTOR;
Chris@23 117 desc.isQuantized = false;
Chris@23 118 list.push_back(desc);
Chris@23 119
Chris@23 120 desc.identifier = "postMarginFactor";
Chris@23 121 desc.name = "Post-Margin Factor";
Chris@23 122 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.";
Chris@23 123 desc.minValue = 0;
Chris@23 124 desc.maxValue = 1;
Chris@23 125 desc.defaultValue = AgentParameters::DEFAULT_POST_MARGIN_FACTOR;
Chris@23 126 desc.isQuantized = false;
Chris@23 127 list.push_back(desc);
Chris@23 128
Chris@23 129 desc.identifier = "maxChange";
Chris@23 130 desc.name = "Maximum Change";
Chris@23 131 desc.description = "The maximum allowed deviation from the initial tempo, expressed as a fraction of the initial beat period.";
Chris@23 132 desc.minValue = 0;
Chris@23 133 desc.maxValue = 1;
Chris@23 134 desc.defaultValue = AgentParameters::DEFAULT_MAX_CHANGE;
Chris@23 135 desc.isQuantized = false;
Chris@23 136 list.push_back(desc);
Chris@23 137
Chris@23 138 desc.identifier = "expiryTime";
Chris@23 139 desc.name = "Expiry Time";
Chris@23 140 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.";
Chris@23 141 desc.minValue = 2;
Chris@23 142 desc.maxValue = 120;
Chris@23 143 desc.defaultValue = AgentParameters::DEFAULT_EXPIRY_TIME;
Chris@23 144 desc.isQuantized = false;
Chris@23 145 list.push_back(desc);
Chris@23 146
Chris@23 147 // Simon says...
Chris@23 148
Chris@23 149 // These are the parameters that should be exposed (Agent.cpp):
Chris@23 150
Chris@23 151 // If Pop, both margins should be lower (0.1). If classical
Chris@23 152 // music, post margin can be increased
Chris@23 153 //
Chris@23 154 // double Agent::POST_MARGIN_FACTOR = 0.3;
Chris@23 155 // double Agent::PRE_MARGIN_FACTOR = 0.15;
Chris@23 156 //
Chris@23 157 // Max Change tells us how much tempo can change - so for
Chris@23 158 // classical we should make it higher
Chris@23 159 //
Chris@23 160 // double Agent::MAX_CHANGE = 0.2;
Chris@23 161 //
Chris@23 162 // The EXPIRY TIME default should be defaulted to 100 (usual cause
Chris@23 163 // of agents dying....) it should also be exposed in order to
Chris@23 164 // troubleshoot eventual problems in songs with big silences in
Chris@23 165 // the beggining/end.
Chris@23 166 //
Chris@23 167 // const double Agent::DEFAULT_EXPIRY_TIME = 10.0;
Chris@23 168
Chris@0 169 return list;
Chris@0 170 }
Chris@0 171
Chris@0 172 float
Chris@0 173 BeatRootVampPlugin::getParameter(string identifier) const
Chris@0 174 {
Chris@23 175 if (identifier == "preMarginFactor") {
Chris@23 176 return m_parameters.preMarginFactor;
Chris@23 177 } else if (identifier == "postMarginFactor") {
Chris@23 178 return m_parameters.postMarginFactor;
Chris@23 179 } else if (identifier == "maxChange") {
Chris@23 180 return m_parameters.maxChange;
Chris@23 181 } else if (identifier == "expiryTime") {
Chris@23 182 return m_parameters.expiryTime;
Chris@23 183 }
Chris@23 184
Chris@0 185 return 0;
Chris@0 186 }
Chris@0 187
Chris@0 188 void
Chris@0 189 BeatRootVampPlugin::setParameter(string identifier, float value)
Chris@0 190 {
Chris@23 191 if (identifier == "preMarginFactor") {
Chris@23 192 m_parameters.preMarginFactor = value;
Chris@23 193 } else if (identifier == "postMarginFactor") {
Chris@23 194 m_parameters.postMarginFactor = value;
Chris@23 195 } else if (identifier == "maxChange") {
Chris@23 196 m_parameters.maxChange = value;
Chris@23 197 } else if (identifier == "expiryTime") {
Chris@23 198 m_parameters.expiryTime = value;
Chris@23 199 }
Chris@0 200 }
Chris@0 201
Chris@0 202 BeatRootVampPlugin::ProgramList
Chris@0 203 BeatRootVampPlugin::getPrograms() const
Chris@0 204 {
Chris@0 205 ProgramList list;
Chris@0 206 return list;
Chris@0 207 }
Chris@0 208
Chris@0 209 string
Chris@0 210 BeatRootVampPlugin::getCurrentProgram() const
Chris@0 211 {
Chris@0 212 return ""; // no programs
Chris@0 213 }
Chris@0 214
Chris@0 215 void
Chris@0 216 BeatRootVampPlugin::selectProgram(string name)
Chris@0 217 {
Chris@0 218 }
Chris@0 219
Chris@0 220 BeatRootVampPlugin::OutputList
Chris@0 221 BeatRootVampPlugin::getOutputDescriptors() const
Chris@0 222 {
Chris@0 223 OutputList list;
Chris@0 224
Chris@0 225 // See OutputDescriptor documentation for the possibilities here.
Chris@0 226 // Every plugin must have at least one output.
Chris@0 227
Chris@0 228 OutputDescriptor d;
Chris@0 229 d.identifier = "beats";
Chris@0 230 d.name = "Beats";
Chris@0 231 d.description = "Estimated beat locations";
Chris@0 232 d.unit = "";
Chris@0 233 d.hasFixedBinCount = true;
Chris@0 234 d.binCount = 0;
Chris@0 235 d.hasKnownExtents = false;
Chris@0 236 d.isQuantized = false;
Chris@0 237 d.sampleType = OutputDescriptor::VariableSampleRate;
Chris@19 238 d.sampleRate = m_inputSampleRate;
Chris@0 239 d.hasDuration = false;
Chris@0 240 list.push_back(d);
Chris@0 241
Chris@36 242 d.identifier = "unfilled";
Chris@36 243 d.name = "Un-interpolated beats";
Chris@36 244 d.description = "Locations of detected beats, before agent interpolation occurs";
Chris@36 245 list.push_back(d);
Chris@36 246
Chris@0 247 return list;
Chris@0 248 }
Chris@0 249
Chris@0 250 bool
Chris@0 251 BeatRootVampPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
Chris@0 252 {
Chris@0 253 if (channels < getMinChannelCount() ||
Chris@0 254 channels > getMaxChannelCount()) {
Chris@0 255 std::cerr << "BeatRootVampPlugin::initialise: Unsupported number ("
Chris@0 256 << channels << ") of channels" << std::endl;
Chris@0 257 return false;
Chris@0 258 }
Chris@0 259
Chris@0 260 if (stepSize != getPreferredStepSize()) {
Chris@0 261 std::cerr << "BeatRootVampPlugin::initialise: Unsupported step size "
Chris@0 262 << "for sample rate (" << stepSize << ", required step is "
Chris@0 263 << getPreferredStepSize() << " for rate " << m_inputSampleRate
Chris@0 264 << ")" << std::endl;
Chris@0 265 return false;
Chris@0 266 }
Chris@0 267
Chris@0 268 if (blockSize != getPreferredBlockSize()) {
Chris@0 269 std::cerr << "BeatRootVampPlugin::initialise: Unsupported block size "
Chris@0 270 << "for sample rate (" << blockSize << ", required size is "
Chris@0 271 << getPreferredBlockSize() << " for rate " << m_inputSampleRate
Chris@0 272 << ")" << std::endl;
Chris@0 273 return false;
Chris@0 274 }
Chris@0 275
Chris@23 276 // Delete the processor that was created with default parameters
Chris@23 277 // and used to determine the expected step and block size; replace
Chris@23 278 // with one using the actual parameters we have
Chris@23 279 delete m_processor;
Chris@23 280 m_processor = new BeatRootProcessor(m_inputSampleRate, m_parameters);
Chris@0 281
Chris@0 282 return true;
Chris@0 283 }
Chris@0 284
Chris@0 285 void
Chris@0 286 BeatRootVampPlugin::reset()
Chris@0 287 {
Chris@0 288 m_processor->reset();
Chris@31 289 m_firstFrame = true;
Chris@31 290 m_origin = Vamp::RealTime::zeroTime;
Chris@0 291 }
Chris@0 292
Chris@0 293 BeatRootVampPlugin::FeatureSet
Chris@0 294 BeatRootVampPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
Chris@0 295 {
Chris@31 296 if (m_firstFrame) {
Chris@31 297 m_origin = timestamp;
Chris@31 298 m_firstFrame = false;
Chris@31 299 }
Chris@31 300
Chris@10 301 m_processor->processFrame(inputBuffers);
Chris@0 302 return FeatureSet();
Chris@0 303 }
Chris@0 304
Chris@0 305 BeatRootVampPlugin::FeatureSet
Chris@0 306 BeatRootVampPlugin::getRemainingFeatures()
Chris@0 307 {
Chris@36 308 EventList unfilled;
Chris@36 309 EventList el = m_processor->beatTrack(&unfilled);
Chris@22 310
Chris@10 311 Feature f;
Chris@10 312 f.hasTimestamp = true;
Chris@10 313 f.hasDuration = false;
Chris@10 314 f.label = "";
Chris@10 315 f.values.clear();
Chris@10 316
Chris@10 317 FeatureSet fs;
Chris@10 318
Chris@13 319 for (EventList::const_iterator i = el.begin(); i != el.end(); ++i) {
Chris@31 320 f.timestamp = m_origin + Vamp::RealTime::fromSeconds(i->time);
Chris@10 321 fs[0].push_back(f);
Chris@10 322 }
Chris@10 323
Chris@36 324 for (EventList::const_iterator i = unfilled.begin();
Chris@36 325 i != unfilled.end(); ++i) {
Chris@36 326 f.timestamp = m_origin + Vamp::RealTime::fromSeconds(i->time);
Chris@36 327 fs[1].push_back(f);
Chris@36 328 }
Chris@36 329
Chris@10 330 return fs;
Chris@0 331 }
Chris@0 332
Chris@0 333
Chris@0 334 static Vamp::PluginAdapter<BeatRootVampPlugin> brAdapter;
Chris@0 335
Chris@0 336 const VampPluginDescriptor *vampGetPluginDescriptor(unsigned int version,
Chris@0 337 unsigned int index)
Chris@0 338 {
Chris@0 339 if (version < 1) return 0;
Chris@0 340
Chris@0 341 switch (index) {
Chris@0 342 case 0: return brAdapter.getDescriptor();
Chris@0 343 default: return 0;
Chris@0 344 }
Chris@0 345 }
Chris@0 346