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