annotate src/MatchVampPlugin.cpp @ 86:f07b9b7f1ab6 refactors

Previous commit was a mistake: the ahead-of-time business is in Finder::getExpandDirection. In fact we were failing to swap advance directions in forward path when writing to the "other" finder. This does not actually affect the backward path calculation, but it does mean we can restore the sanity check.
author Chris Cannam
date Thu, 27 Nov 2014 12:08:16 +0000
parents 10e76188c846
children bdd8158d401f 593054bf6476
rev   line source
cannam@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
cannam@0 2
cannam@0 3 /*
cannam@0 4 Vamp feature extraction plugin using the MATCH audio alignment
cannam@0 5 algorithm.
cannam@0 6
cannam@0 7 Centre for Digital Music, Queen Mary, University of London.
cannam@0 8 This file copyright 2007 Simon Dixon, Chris Cannam and QMUL.
cannam@0 9
cannam@0 10 This program is free software; you can redistribute it and/or
cannam@0 11 modify it under the terms of the GNU General Public License as
cannam@0 12 published by the Free Software Foundation; either version 2 of the
cannam@0 13 License, or (at your option) any later version. See the file
cannam@0 14 COPYING included with this distribution for more information.
cannam@0 15 */
cannam@0 16
cannam@0 17 #include "MatchVampPlugin.h"
cannam@0 18
cannam@0 19 #include "Matcher.h"
Chris@74 20 #include "MatchFeatureFeeder.h"
Chris@74 21 #include "FeatureExtractor.h"
cannam@0 22 #include "Path.h"
cannam@0 23
cannam@0 24 #include <vamp/vamp.h>
cannam@0 25 #include <vamp-sdk/PluginAdapter.h>
cannam@0 26 #include <vamp-sdk/RealTime.h>
cannam@0 27
cannam@0 28 #include <vector>
cannam@0 29 #include <algorithm>
cannam@0 30
cannam@0 31 //static int extant = 0;
cannam@0 32
cannam@0 33 #ifdef _WIN32
cannam@0 34 HANDLE
cannam@0 35 MatchVampPlugin::m_serialisingMutex;
cannam@0 36 #else
cannam@0 37 pthread_mutex_t
cannam@0 38 MatchVampPlugin::m_serialisingMutex;
cannam@0 39 #endif
cannam@0 40
cannam@0 41 bool
cannam@0 42 MatchVampPlugin::m_serialisingMutexInitialised = false;
cannam@0 43
Chris@10 44 // We want to ensure our freq map / crossover bin in Matcher.cpp are
Chris@10 45 // always valid with a fixed FFT length in seconds, so must reject low
Chris@10 46 // sample rates
Chris@10 47 static float sampleRateMin = 5000.f;
Chris@10 48
Chris@52 49 static float defaultStepTime = 0.020f;
Chris@15 50
cannam@0 51 MatchVampPlugin::MatchVampPlugin(float inputSampleRate) :
cannam@0 52 Plugin(inputSampleRate),
Chris@52 53 m_stepSize(int(inputSampleRate * defaultStepTime + 0.001)),
Chris@15 54 m_stepTime(defaultStepTime),
Chris@16 55 m_blockSize(2048),
cannam@0 56 m_serialise(false),
cannam@0 57 m_begin(true),
Chris@17 58 m_locked(false),
Chris@32 59 m_smooth(true),
Chris@74 60 m_frameNo(0),
Chris@74 61 m_lastFrameIn1(0),
Chris@74 62 m_lastFrameIn2(0),
Chris@17 63 m_params(inputSampleRate, defaultStepTime, m_blockSize),
Chris@38 64 m_defaultParams(inputSampleRate, defaultStepTime, m_blockSize),
Chris@38 65 m_feParams(inputSampleRate, m_blockSize),
Chris@38 66 m_defaultFeParams(inputSampleRate, m_blockSize)
cannam@0 67 {
Chris@10 68 if (inputSampleRate < sampleRateMin) {
Chris@10 69 std::cerr << "MatchVampPlugin::MatchVampPlugin: input sample rate "
Chris@10 70 << inputSampleRate << " < min supported rate "
Chris@10 71 << sampleRateMin << ", plugin will refuse to initialise"
Chris@10 72 << std::endl;
Chris@10 73 }
Chris@10 74
cannam@0 75 if (!m_serialisingMutexInitialised) {
cannam@0 76 m_serialisingMutexInitialised = true;
cannam@0 77 #ifdef _WIN32
cannam@0 78 m_serialisingMutex = CreateMutex(NULL, FALSE, NULL);
cannam@0 79 #else
cannam@0 80 pthread_mutex_init(&m_serialisingMutex, 0);
cannam@0 81 #endif
cannam@0 82 }
cannam@0 83
Chris@74 84 m_pm1 = 0;
Chris@74 85 m_pm2 = 0;
Chris@74 86 m_fe1 = 0;
Chris@74 87 m_fe2 = 0;
Chris@74 88 m_feeder = 0;
cannam@0 89 // std::cerr << "MatchVampPlugin::MatchVampPlugin(" << this << "): extant = " << ++extant << std::endl;
cannam@0 90 }
cannam@0 91
cannam@0 92 MatchVampPlugin::~MatchVampPlugin()
cannam@0 93 {
cannam@0 94 // std::cerr << "MatchVampPlugin::~MatchVampPlugin(" << this << "): extant = " << --extant << std::endl;
cannam@0 95
Chris@74 96 delete m_feeder;
Chris@74 97 delete m_fe1;
Chris@74 98 delete m_fe2;
Chris@74 99 delete m_pm1;
Chris@74 100 delete m_pm2;
cannam@0 101
cannam@0 102 if (m_locked) {
cannam@0 103 #ifdef _WIN32
cannam@0 104 ReleaseMutex(m_serialisingMutex);
cannam@0 105 #else
cannam@0 106 pthread_mutex_unlock(&m_serialisingMutex);
cannam@0 107 #endif
cannam@0 108 m_locked = false;
cannam@0 109 }
cannam@0 110 }
cannam@0 111
cannam@0 112 string
cannam@0 113 MatchVampPlugin::getIdentifier() const
cannam@0 114 {
cannam@0 115 return "match";
cannam@0 116 }
cannam@0 117
cannam@0 118 string
cannam@0 119 MatchVampPlugin::getName() const
cannam@0 120 {
cannam@0 121 return "Match Performance Aligner";
cannam@0 122 }
cannam@0 123
cannam@0 124 string
cannam@0 125 MatchVampPlugin::getDescription() const
cannam@0 126 {
cannam@0 127 return "Calculate alignment between two performances in separate channel inputs";
cannam@0 128 }
cannam@0 129
cannam@0 130 string
cannam@0 131 MatchVampPlugin::getMaker() const
cannam@0 132 {
cannam@0 133 return "Simon Dixon (plugin by Chris Cannam)";
cannam@0 134 }
cannam@0 135
cannam@0 136 int
cannam@0 137 MatchVampPlugin::getPluginVersion() const
cannam@0 138 {
Chris@19 139 return 2;
cannam@0 140 }
cannam@0 141
cannam@0 142 string
cannam@0 143 MatchVampPlugin::getCopyright() const
cannam@0 144 {
cannam@0 145 return "GPL";
cannam@0 146 }
cannam@0 147
cannam@0 148 MatchVampPlugin::ParameterList
cannam@0 149 MatchVampPlugin::getParameterDescriptors() const
cannam@0 150 {
cannam@0 151 ParameterList list;
cannam@0 152
cannam@0 153 ParameterDescriptor desc;
Chris@18 154
cannam@0 155 desc.identifier = "serialise";
cannam@0 156 desc.name = "Serialise Plugin Invocations";
cannam@0 157 desc.description = "Reduce potential memory load at the expense of multiprocessor performance by serialising multi-threaded plugin runs";
cannam@0 158 desc.minValue = 0;
cannam@0 159 desc.maxValue = 1;
cannam@0 160 desc.defaultValue = 0;
cannam@0 161 desc.isQuantized = true;
cannam@0 162 desc.quantizeStep = 1;
cannam@0 163 list.push_back(desc);
cannam@0 164
Chris@18 165 desc.identifier = "framenorm";
Chris@18 166 desc.name = "Frame Normalisation";
Chris@18 167 desc.description = "Type of normalisation to use for frequency-domain audio features";
Chris@18 168 desc.minValue = 0;
Chris@18 169 desc.maxValue = 2;
Chris@38 170 desc.defaultValue = (int)m_defaultFeParams.frameNorm;
Chris@18 171 desc.isQuantized = true;
Chris@18 172 desc.quantizeStep = 1;
Chris@18 173 desc.valueNames.clear();
Chris@18 174 desc.valueNames.push_back("None");
Chris@18 175 desc.valueNames.push_back("Sum To 1");
Chris@18 176 desc.valueNames.push_back("Long-Term Average");
Chris@18 177 list.push_back(desc);
Chris@18 178 desc.valueNames.clear();
Chris@18 179
Chris@18 180 desc.identifier = "distnorm";
Chris@18 181 desc.name = "Distance Normalisation";
Chris@18 182 desc.description = "Type of normalisation to use for distance metric";
Chris@18 183 desc.minValue = 0;
Chris@18 184 desc.maxValue = 2;
Chris@18 185 desc.defaultValue = (int)m_defaultParams.distanceNorm;
Chris@18 186 desc.isQuantized = true;
Chris@18 187 desc.quantizeStep = 1;
Chris@18 188 desc.valueNames.clear();
Chris@18 189 desc.valueNames.push_back("None");
Chris@18 190 desc.valueNames.push_back("Sum of Frames");
Chris@18 191 desc.valueNames.push_back("Log Sum of Frames");
Chris@18 192 list.push_back(desc);
Chris@18 193 desc.valueNames.clear();
Chris@18 194
Chris@18 195 desc.identifier = "usespecdiff";
Chris@18 196 desc.name = "Use Spectral Difference";
Chris@18 197 desc.description = "Whether to use half-wave rectified spectral difference instead of straight spectrum";
Chris@18 198 desc.minValue = 0;
Chris@18 199 desc.maxValue = 1;
Chris@38 200 desc.defaultValue = m_defaultFeParams.useSpectralDifference ? 1 : 0;
Chris@18 201 desc.isQuantized = true;
Chris@18 202 desc.quantizeStep = 1;
Chris@18 203 list.push_back(desc);
Chris@18 204
Chris@18 205 desc.identifier = "usechroma";
Chris@18 206 desc.name = "Use Chroma Frequency Map";
Chris@18 207 desc.description = "Whether to use a chroma frequency map instead of the default warped spectrogram";
Chris@18 208 desc.minValue = 0;
Chris@18 209 desc.maxValue = 1;
Chris@38 210 desc.defaultValue = m_defaultFeParams.useChromaFrequencyMap ? 1 : 0;
Chris@18 211 desc.isQuantized = true;
Chris@18 212 desc.quantizeStep = 1;
Chris@18 213 list.push_back(desc);
Chris@18 214
Chris@25 215 desc.identifier = "gradientlimit";
Chris@25 216 desc.name = "Gradient Limit";
Chris@18 217 desc.description = "Limit of number of frames that will be accepted from one source without a frame from the other source being accepted";
Chris@18 218 desc.minValue = 1;
Chris@18 219 desc.maxValue = 10;
Chris@18 220 desc.defaultValue = m_defaultParams.maxRunCount;
Chris@18 221 desc.isQuantized = true;
Chris@18 222 desc.quantizeStep = 1;
Chris@18 223 list.push_back(desc);
Chris@18 224
Chris@25 225 desc.identifier = "zonewidth";
Chris@25 226 desc.name = "Search Zone Width";
Chris@25 227 desc.description = "Width of the search zone (error margin) either side of the ongoing match position, in seconds";
Chris@25 228 desc.minValue = 1;
Chris@25 229 desc.maxValue = 60;
Chris@52 230 desc.defaultValue = (float)m_defaultParams.blockTime;
Chris@25 231 desc.isQuantized = true;
Chris@25 232 desc.quantizeStep = 1;
Chris@25 233 desc.unit = "s";
Chris@25 234 list.push_back(desc);
Chris@25 235
Chris@83 236 desc.identifier = "diagonalweight";
Chris@83 237 desc.name = "Diagonal Weight";
Chris@83 238 desc.description = "Weight applied to cost of diagonal step relative to horizontal or vertical step. The default of 2.0 is good for gross tracking of quite different performances; closer to 1.0 produces a smoother path for performances more similar in tempo";
Chris@83 239 desc.minValue = 1.0;
Chris@86 240 desc.maxValue = 2.0;
Chris@83 241 desc.defaultValue = 2.0;
Chris@83 242 desc.isQuantized = false;
Chris@83 243 desc.unit = "";
Chris@83 244 list.push_back(desc);
Chris@83 245
Chris@32 246 desc.identifier = "smooth";
Chris@32 247 desc.name = "Smooth Path";
Chris@32 248 desc.description = "Smooth the path by replacing steps with diagonals";
Chris@32 249 desc.minValue = 0;
Chris@32 250 desc.maxValue = 1;
Chris@32 251 desc.defaultValue = 1;
Chris@32 252 desc.isQuantized = true;
Chris@32 253 desc.quantizeStep = 1;
Chris@32 254 desc.unit = "";
Chris@32 255 list.push_back(desc);
Chris@32 256
cannam@0 257 return list;
cannam@0 258 }
cannam@0 259
cannam@0 260 float
cannam@0 261 MatchVampPlugin::getParameter(std::string name) const
cannam@0 262 {
cannam@0 263 if (name == "serialise") {
cannam@0 264 return m_serialise ? 1.0 : 0.0;
Chris@18 265 } else if (name == "framenorm") {
Chris@38 266 return (int)m_feParams.frameNorm;
Chris@18 267 } else if (name == "distnorm") {
Chris@18 268 return (int)m_params.distanceNorm;
Chris@18 269 } else if (name == "usespecdiff") {
Chris@38 270 return m_feParams.useSpectralDifference ? 1.0 : 0.0;
Chris@18 271 } else if (name == "usechroma") {
Chris@38 272 return m_feParams.useChromaFrequencyMap ? 1.0 : 0.0;
Chris@25 273 } else if (name == "gradientlimit") {
Chris@18 274 return m_params.maxRunCount;
Chris@83 275 } else if (name == "diagonalweight") {
Chris@83 276 return m_params.diagonalWeight;
Chris@25 277 } else if (name == "zonewidth") {
Chris@52 278 return (float)m_params.blockTime;
Chris@32 279 } else if (name == "smooth") {
Chris@32 280 return m_smooth ? 1.0 : 0.0;
cannam@0 281 }
Chris@18 282
cannam@0 283 return 0.0;
cannam@0 284 }
cannam@0 285
cannam@0 286 void
cannam@0 287 MatchVampPlugin::setParameter(std::string name, float value)
cannam@0 288 {
cannam@0 289 if (name == "serialise") {
cannam@0 290 m_serialise = (value > 0.5);
Chris@18 291 } else if (name == "framenorm") {
Chris@38 292 m_feParams.frameNorm = (FeatureExtractor::FrameNormalisation)(int(value + 0.1));
Chris@18 293 } else if (name == "distnorm") {
Chris@26 294 m_params.distanceNorm = (DistanceMetric::DistanceNormalisation)(int(value + 0.1));
Chris@18 295 } else if (name == "usespecdiff") {
Chris@38 296 m_feParams.useSpectralDifference = (value > 0.5);
Chris@18 297 } else if (name == "usechroma") {
Chris@38 298 m_feParams.useChromaFrequencyMap = (value > 0.5);
Chris@25 299 } else if (name == "gradientlimit") {
Chris@18 300 m_params.maxRunCount = int(value + 0.1);
Chris@83 301 } else if (name == "diagonalweight") {
Chris@83 302 m_params.diagonalWeight = value;
Chris@25 303 } else if (name == "zonewidth") {
Chris@25 304 m_params.blockTime = value;
Chris@32 305 } else if (name == "smooth") {
Chris@32 306 m_smooth = (value > 0.5);
cannam@0 307 }
cannam@0 308 }
cannam@0 309
cannam@0 310 size_t
cannam@0 311 MatchVampPlugin::getPreferredStepSize() const
cannam@0 312 {
Chris@52 313 return int(m_inputSampleRate * defaultStepTime + 0.001);
cannam@0 314 }
cannam@0 315
cannam@0 316 size_t
cannam@0 317 MatchVampPlugin::getPreferredBlockSize() const
cannam@0 318 {
Chris@15 319 return 2048;
cannam@0 320 }
cannam@0 321
cannam@0 322 void
Chris@17 323 MatchVampPlugin::createMatchers()
cannam@0 324 {
Chris@17 325 m_params.hopTime = m_stepTime;
Chris@17 326 m_params.fftSize = m_blockSize;
Chris@38 327 m_feParams.fftSize = m_blockSize;
Chris@74 328 m_fe1 = new FeatureExtractor(m_feParams);
Chris@74 329 m_fe2 = new FeatureExtractor(m_feParams);
Chris@74 330 m_pm1 = new Matcher(m_params, 0, m_fe1->getFeatureSize());
Chris@74 331 m_pm2 = new Matcher(m_params, m_pm1, m_fe2->getFeatureSize());
Chris@74 332 m_pm1->setOtherMatcher(m_pm2);
Chris@74 333 m_feeder = new MatchFeatureFeeder(m_pm1, m_pm2);
cannam@0 334 }
cannam@0 335
cannam@0 336 bool
cannam@0 337 MatchVampPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
cannam@0 338 {
Chris@10 339 if (m_inputSampleRate < sampleRateMin) {
Chris@10 340 std::cerr << "MatchVampPlugin::MatchVampPlugin: input sample rate "
Chris@10 341 << m_inputSampleRate << " < min supported rate "
Chris@10 342 << sampleRateMin << std::endl;
Chris@10 343 return false;
Chris@10 344 }
cannam@0 345 if (channels < getMinChannelCount() ||
cannam@0 346 channels > getMaxChannelCount()) return false;
cannam@1 347 if (stepSize > blockSize/2 ||
cannam@0 348 blockSize != getPreferredBlockSize()) return false;
Chris@15 349
cannam@6 350 m_stepSize = stepSize;
Chris@15 351 m_stepTime = float(stepSize) / m_inputSampleRate;
Chris@15 352 m_blockSize = blockSize;
Chris@15 353
Chris@15 354 createMatchers();
cannam@0 355 m_begin = true;
cannam@0 356 m_locked = false;
Chris@15 357
cannam@0 358 return true;
cannam@0 359 }
cannam@0 360
cannam@0 361 void
cannam@0 362 MatchVampPlugin::reset()
cannam@0 363 {
Chris@74 364 delete m_feeder;
Chris@74 365 delete m_fe1;
Chris@74 366 delete m_fe2;
Chris@74 367 delete m_pm1;
Chris@74 368 delete m_pm2;
Chris@74 369
Chris@74 370 m_feeder = 0;
Chris@74 371 m_fe1 = 0;
Chris@74 372 m_fe2 = 0;
Chris@74 373 m_pm1 = 0;
Chris@74 374 m_pm2 = 0;
Chris@74 375
Chris@74 376 m_frameNo = 0;
Chris@74 377 m_lastFrameIn1 = 0;
Chris@74 378 m_lastFrameIn2 = 0;
cannam@6 379
cannam@6 380 createMatchers();
cannam@6 381 m_begin = true;
cannam@6 382 m_locked = false;
cannam@0 383 }
cannam@0 384
cannam@0 385 MatchVampPlugin::OutputList
cannam@0 386 MatchVampPlugin::getOutputDescriptors() const
cannam@0 387 {
cannam@0 388 OutputList list;
cannam@0 389
Chris@52 390 float outRate = 1.0f / m_stepTime;
cannam@0 391
cannam@0 392 OutputDescriptor desc;
cannam@0 393 desc.identifier = "path";
cannam@0 394 desc.name = "Path";
cannam@0 395 desc.description = "Alignment path";
cannam@0 396 desc.unit = "";
cannam@0 397 desc.hasFixedBinCount = true;
cannam@0 398 desc.binCount = 1;
cannam@0 399 desc.hasKnownExtents = false;
cannam@0 400 desc.isQuantized = true;
cannam@0 401 desc.quantizeStep = 1;
cannam@0 402 desc.sampleType = OutputDescriptor::VariableSampleRate;
cannam@0 403 desc.sampleRate = outRate;
Chris@16 404 m_pathOutNo = list.size();
cannam@0 405 list.push_back(desc);
cannam@0 406
cannam@0 407 desc.identifier = "a_b";
cannam@0 408 desc.name = "A-B Timeline";
cannam@0 409 desc.description = "Timing in performance B corresponding to moments in performance A";
cannam@0 410 desc.unit = "sec";
cannam@0 411 desc.hasFixedBinCount = true;
cannam@0 412 desc.binCount = 1;
cannam@0 413 desc.hasKnownExtents = false;
cannam@0 414 desc.isQuantized = false;
cannam@0 415 desc.sampleType = OutputDescriptor::VariableSampleRate;
cannam@0 416 desc.sampleRate = outRate;
Chris@16 417 m_abOutNo = list.size();
cannam@0 418 list.push_back(desc);
cannam@0 419
cannam@0 420 desc.identifier = "b_a";
cannam@0 421 desc.name = "B-A Timeline";
cannam@0 422 desc.description = "Timing in performance A corresponding to moments in performance B";
cannam@0 423 desc.unit = "sec";
cannam@0 424 desc.hasFixedBinCount = true;
cannam@0 425 desc.binCount = 1;
cannam@0 426 desc.hasKnownExtents = false;
cannam@0 427 desc.isQuantized = false;
cannam@0 428 desc.sampleType = OutputDescriptor::VariableSampleRate;
cannam@0 429 desc.sampleRate = outRate;
Chris@16 430 m_baOutNo = list.size();
cannam@0 431 list.push_back(desc);
cannam@0 432
cannam@0 433 desc.identifier = "a_b_divergence";
cannam@0 434 desc.name = "A-B Divergence";
cannam@0 435 desc.description = "Difference between timings in performances A and B";
cannam@0 436 desc.unit = "sec";
cannam@0 437 desc.hasFixedBinCount = true;
cannam@0 438 desc.binCount = 1;
cannam@0 439 desc.hasKnownExtents = false;
cannam@0 440 desc.isQuantized = false;
cannam@0 441 desc.sampleType = OutputDescriptor::VariableSampleRate;
cannam@0 442 desc.sampleRate = outRate;
Chris@16 443 m_abDivOutNo = list.size();
cannam@0 444 list.push_back(desc);
cannam@0 445
cannam@0 446 desc.identifier = "a_b_temporatio";
cannam@0 447 desc.name = "A-B Tempo Ratio";
cannam@0 448 desc.description = "Ratio of tempi between performances A and B";
cannam@0 449 desc.unit = "";
cannam@0 450 desc.hasFixedBinCount = true;
cannam@0 451 desc.binCount = 1;
cannam@0 452 desc.hasKnownExtents = false;
cannam@0 453 desc.isQuantized = false;
cannam@0 454 desc.sampleType = OutputDescriptor::VariableSampleRate;
cannam@0 455 desc.sampleRate = outRate;
Chris@16 456 m_abRatioOutNo = list.size();
cannam@0 457 list.push_back(desc);
cannam@0 458
Chris@38 459 int featureSize = FeatureExtractor(m_feParams).getFeatureSize();
Chris@38 460
Chris@15 461 desc.identifier = "a_features";
Chris@15 462 desc.name = "A Features";
Chris@15 463 desc.description = "Spectral features extracted from performance A";
Chris@15 464 desc.unit = "";
Chris@15 465 desc.hasFixedBinCount = true;
Chris@38 466 desc.binCount = featureSize;
Chris@15 467 desc.hasKnownExtents = false;
Chris@15 468 desc.isQuantized = false;
Chris@16 469 desc.sampleType = OutputDescriptor::FixedSampleRate;
Chris@15 470 desc.sampleRate = outRate;
Chris@16 471 m_aFeaturesOutNo = list.size();
Chris@16 472 list.push_back(desc);
Chris@16 473
Chris@16 474 desc.identifier = "b_features";
Chris@16 475 desc.name = "B Features";
Chris@16 476 desc.description = "Spectral features extracted from performance B";
Chris@16 477 desc.unit = "";
Chris@16 478 desc.hasFixedBinCount = true;
Chris@38 479 desc.binCount = featureSize;
Chris@16 480 desc.hasKnownExtents = false;
Chris@16 481 desc.isQuantized = false;
Chris@16 482 desc.sampleType = OutputDescriptor::FixedSampleRate;
Chris@16 483 desc.sampleRate = outRate;
Chris@16 484 m_bFeaturesOutNo = list.size();
Chris@15 485 list.push_back(desc);
Chris@15 486
cannam@0 487 return list;
cannam@0 488 }
cannam@0 489
Chris@74 490 bool
Chris@74 491 MatchVampPlugin::aboveThreshold(const float *frame)
Chris@74 492 {
Chris@74 493 float threshold = 1e-5f;
Chris@74 494 float rms = 0.f;
Chris@74 495 for (int i = 0; i < m_blockSize/2 + 2; ++i) {
Chris@74 496 rms += frame[i] * frame[i];
Chris@74 497 }
Chris@74 498 rms = sqrtf(rms / (m_blockSize/2 + 2));
Chris@74 499 return (rms > threshold);
Chris@74 500 }
Chris@74 501
cannam@0 502 MatchVampPlugin::FeatureSet
cannam@0 503 MatchVampPlugin::process(const float *const *inputBuffers,
cannam@0 504 Vamp::RealTime timestamp)
cannam@0 505 {
cannam@0 506 if (m_begin) {
cannam@0 507 if (!m_locked && m_serialise) {
cannam@0 508 m_locked = true;
cannam@0 509 #ifdef _WIN32
cannam@0 510 WaitForSingleObject(m_serialisingMutex, INFINITE);
cannam@0 511 #else
cannam@0 512 pthread_mutex_lock(&m_serialisingMutex);
cannam@0 513 #endif
cannam@0 514 }
Chris@10 515 m_startTime = timestamp;
cannam@0 516 m_begin = false;
cannam@0 517 }
cannam@0 518
cannam@0 519 // std::cerr << timestamp.toString();
cannam@0 520
Chris@74 521 if (aboveThreshold(inputBuffers[0])) m_lastFrameIn1 = m_frameNo;
Chris@74 522 if (aboveThreshold(inputBuffers[1])) m_lastFrameIn2 = m_frameNo;
Chris@74 523
Chris@74 524 vector<double> f1 = m_fe1->process(inputBuffers[0]);
Chris@74 525 vector<double> f2 = m_fe2->process(inputBuffers[1]);
Chris@74 526
Chris@74 527 m_feeder->feed(f1, f2);
Chris@16 528
Chris@16 529 FeatureSet returnFeatures;
Chris@16 530
Chris@16 531 Feature f;
Chris@16 532 f.hasTimestamp = false;
Chris@16 533
Chris@74 534 f.values.clear();
Chris@74 535 for (int j = 0; j < (int)f1.size(); ++j) {
Chris@74 536 f.values.push_back(float(f1[j]));
Chris@16 537 }
Chris@74 538 returnFeatures[m_aFeaturesOutNo].push_back(f);
Chris@16 539
Chris@74 540 f.values.clear();
Chris@74 541 for (int j = 0; j < (int)f2.size(); ++j) {
Chris@74 542 f.values.push_back(float(f2[j]));
Chris@16 543 }
Chris@74 544 returnFeatures[m_bFeaturesOutNo].push_back(f);
cannam@0 545
cannam@0 546 // std::cerr << ".";
cannam@0 547 // std::cerr << std::endl;
cannam@0 548
Chris@74 549 ++m_frameNo;
Chris@74 550
Chris@16 551 return returnFeatures;
cannam@0 552 }
cannam@0 553
cannam@0 554 MatchVampPlugin::FeatureSet
cannam@0 555 MatchVampPlugin::getRemainingFeatures()
cannam@0 556 {
Chris@74 557 m_feeder->finish();
Chris@74 558
Chris@63 559 FeatureSet returnFeatures;
Chris@63 560
Chris@74 561 Finder *finder = m_feeder->getFinder();
Chris@79 562 finder->setDurations(m_lastFrameIn1, m_lastFrameIn2);
cannam@0 563 std::vector<int> pathx;
cannam@0 564 std::vector<int> pathy;
Chris@32 565 int len = finder->retrievePath(m_smooth, pathx, pathy);
cannam@0 566
cannam@0 567 int prevx = 0;
cannam@0 568 int prevy = 0;
cannam@0 569
Chris@30 570 for (int i = 0; i < len; ++i) {
cannam@0 571
cannam@0 572 int x = pathx[i];
cannam@0 573 int y = pathy[i];
cannam@0 574
cannam@0 575 Vamp::RealTime xt = Vamp::RealTime::frame2RealTime
Chris@15 576 (x * m_stepSize, lrintf(m_inputSampleRate));
cannam@0 577 Vamp::RealTime yt = Vamp::RealTime::frame2RealTime
Chris@15 578 (y * m_stepSize, lrintf(m_inputSampleRate));
cannam@0 579
cannam@0 580 Feature feature;
cannam@0 581 feature.hasTimestamp = true;
Chris@10 582 feature.timestamp = m_startTime + xt;
cannam@0 583 feature.values.clear();
Chris@52 584 feature.values.push_back(float(yt.sec + double(yt.nsec)/1.0e9));
Chris@16 585 returnFeatures[m_pathOutNo].push_back(feature);
cannam@0 586
cannam@0 587 if (x != prevx) {
cannam@0 588
cannam@0 589 feature.hasTimestamp = true;
Chris@10 590 feature.timestamp = m_startTime + xt;
cannam@0 591 feature.values.clear();
Chris@52 592 feature.values.push_back(float(yt.sec + yt.msec()/1000.0));
Chris@16 593 returnFeatures[m_abOutNo].push_back(feature);
cannam@0 594
cannam@0 595 Vamp::RealTime diff = yt - xt;
cannam@0 596 feature.values.clear();
Chris@52 597 feature.values.push_back(float(diff.sec + diff.msec()/1000.0));
Chris@16 598 returnFeatures[m_abDivOutNo].push_back(feature);
cannam@0 599
cannam@0 600 if (i > 0) {
cannam@0 601 int lookback = 100; //!!! arbitrary
cannam@0 602 if (lookback > i) lookback = i;
cannam@0 603 int xdiff = x - pathx[i-lookback];
cannam@0 604 int ydiff = y - pathy[i-lookback];
cannam@0 605 if (xdiff != 0 && ydiff != 0) {
cannam@0 606 float ratio = float(ydiff)/float(xdiff);
cannam@0 607 if (ratio < 8 && ratio > (1.0/8)) { //!!! just for now, since we aren't dealing properly with silence yet
cannam@0 608 feature.values.clear();
cannam@0 609 feature.values.push_back(ratio);
Chris@16 610 returnFeatures[m_abRatioOutNo].push_back(feature);
cannam@0 611 }
cannam@0 612 }
cannam@0 613 }
cannam@0 614 }
cannam@0 615
cannam@0 616 if (y != prevy) {
cannam@0 617 feature.hasTimestamp = true;
Chris@10 618 feature.timestamp = m_startTime + yt;
cannam@0 619 feature.values.clear();
Chris@52 620 feature.values.push_back(float(xt.sec + xt.msec()/1000.0));
Chris@16 621 returnFeatures[m_baOutNo].push_back(feature);
cannam@0 622 }
cannam@0 623
cannam@0 624 prevx = x;
cannam@0 625 prevy = y;
cannam@0 626 }
cannam@0 627
Chris@74 628 delete m_feeder;
Chris@74 629 delete m_pm1;
Chris@74 630 delete m_pm2;
Chris@74 631 m_feeder = 0;
Chris@74 632 m_pm1 = 0;
Chris@74 633 m_pm2 = 0;
cannam@0 634
cannam@0 635 if (m_locked) {
cannam@0 636 #ifdef _WIN32
cannam@0 637 ReleaseMutex(m_serialisingMutex);
cannam@0 638 #else
cannam@0 639 pthread_mutex_unlock(&m_serialisingMutex);
cannam@0 640 #endif
cannam@0 641 m_locked = false;
cannam@0 642 }
cannam@0 643
cannam@0 644 return returnFeatures;
cannam@0 645
cannam@0 646
cannam@0 647 /*
Chris@30 648 for (int i = 0; i < len; ++i) {
cannam@0 649 std::cerr << i << ": [" << pathx[i] << "," << pathy[i] << "]" << std::endl;
cannam@0 650 }
cannam@0 651
cannam@0 652 std::cerr << std::endl;
cannam@0 653 std::cerr << "File: A" << std::endl;
cannam@0 654 std::cerr << "Marks: -1" << std::endl;
cannam@0 655 std::cerr << "FixedPoints: true 0" << std::endl;
cannam@0 656 std::cerr << "0" << std::endl;
cannam@0 657 std::cerr << "0" << std::endl;
cannam@0 658 std::cerr << "0" << std::endl;
cannam@0 659 std::cerr << "0" << std::endl;
cannam@0 660 std::cerr << "File: B" << std::endl;
cannam@0 661 std::cerr << "Marks: 0" << std::endl;
cannam@0 662 std::cerr << "FixedPoints: true 0" << std::endl;
cannam@0 663 std::cerr << "0.02" << std::endl;
cannam@0 664 std::cerr << "0.02" << std::endl;
cannam@0 665
Chris@30 666 std::cerr << len << std::endl;
Chris@30 667 for (int i = 0; i < len; ++i) {
cannam@0 668 std::cerr << pathx[i] << std::endl;
cannam@0 669 }
cannam@0 670
Chris@30 671 std::cerr << len << std::endl;
Chris@30 672 for (int i = 0; i < len; ++i) {
cannam@0 673 std::cerr << pathy[i] << std::endl;
cannam@0 674 }
cannam@0 675 */
cannam@0 676 }
cannam@0 677
cannam@0 678 static Vamp::PluginAdapter<MatchVampPlugin> mvpAdapter;
cannam@0 679
cannam@0 680 const VampPluginDescriptor *vampGetPluginDescriptor(unsigned int version,
cannam@0 681 unsigned int index)
cannam@0 682 {
cannam@0 683 if (version < 1) return 0;
cannam@0 684
cannam@0 685 switch (index) {
cannam@0 686 case 0: return mvpAdapter.getDescriptor();
cannam@0 687 default: return 0;
cannam@0 688 }
cannam@0 689 }