annotate transform/FeatureExtractionModelTransformer.cpp @ 1784:4eac4bf35b45

More graceful handling of failure to construct FFT models in the case where the source model has already been deleted before this occurs
author Chris Cannam
date Tue, 17 Sep 2019 11:21:33 +0100
parents d484490cdf69
children 5f8fbbde08ff
rev   line source
Chris@320 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@320 2
Chris@320 3 /*
Chris@320 4 Sonic Visualiser
Chris@320 5 An audio file viewer and annotation editor.
Chris@320 6 Centre for Digital Music, Queen Mary, University of London.
Chris@320 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@320 8
Chris@320 9 This program is free software; you can redistribute it and/or
Chris@320 10 modify it under the terms of the GNU General Public License as
Chris@320 11 published by the Free Software Foundation; either version 2 of the
Chris@320 12 License, or (at your option) any later version. See the file
Chris@320 13 COPYING included with this distribution for more information.
Chris@320 14 */
Chris@320 15
Chris@331 16 #include "FeatureExtractionModelTransformer.h"
Chris@320 17
Chris@320 18 #include "plugin/FeatureExtractionPluginFactory.h"
Chris@1225 19
Chris@320 20 #include "plugin/PluginXml.h"
Chris@475 21 #include <vamp-hostsdk/Plugin.h>
Chris@320 22
Chris@320 23 #include "data/model/Model.h"
Chris@320 24 #include "base/Window.h"
Chris@387 25 #include "base/Exceptions.h"
Chris@320 26 #include "data/model/SparseOneDimensionalModel.h"
Chris@320 27 #include "data/model/SparseTimeValueModel.h"
Chris@1777 28 #include "data/model/BasicCompressedDenseThreeDimensionalModel.h"
Chris@320 29 #include "data/model/DenseTimeValueModel.h"
Chris@320 30 #include "data/model/NoteModel.h"
Chris@441 31 #include "data/model/RegionModel.h"
Chris@320 32 #include "data/model/FFTModel.h"
Chris@320 33 #include "data/model/WaveFileModel.h"
Chris@558 34 #include "rdf/PluginRDFDescription.h"
Chris@320 35
Chris@350 36 #include "TransformFactory.h"
Chris@350 37
Chris@320 38 #include <iostream>
Chris@320 39
Chris@859 40 #include <QSettings>
Chris@859 41
Chris@1558 42 //#define DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN 1
Chris@1558 43
Chris@350 44 FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Input in,
Chris@859 45 const Transform &transform) :
Chris@350 46 ModelTransformer(in, transform),
Chris@1582 47 m_plugin(nullptr),
Chris@1211 48 m_haveOutputs(false)
Chris@320 49 {
Chris@1080 50 SVDEBUG << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << m_transforms.begin()->getPluginIdentifier() << ", outputName " << m_transforms.begin()->getOutput() << endl;
Chris@849 51 }
Chris@849 52
Chris@849 53 FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Input in,
Chris@859 54 const Transforms &transforms) :
Chris@849 55 ModelTransformer(in, transforms),
Chris@1582 56 m_plugin(nullptr),
Chris@1211 57 m_haveOutputs(false)
Chris@849 58 {
Chris@1080 59 if (m_transforms.empty()) {
Chris@1080 60 SVDEBUG << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: " << transforms.size() << " transform(s)" << endl;
Chris@1080 61 } else {
Chris@1080 62 SVDEBUG << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: " << transforms.size() << " transform(s), first has plugin " << m_transforms.begin()->getPluginIdentifier() << ", outputName " << m_transforms.begin()->getOutput() << endl;
Chris@1080 63 }
Chris@849 64 }
Chris@849 65
Chris@849 66 static bool
Chris@849 67 areTransformsSimilar(const Transform &t1, const Transform &t2)
Chris@849 68 {
Chris@849 69 Transform t2o(t2);
Chris@849 70 t2o.setOutput(t1.getOutput());
Chris@849 71 return t1 == t2o;
Chris@849 72 }
Chris@849 73
Chris@849 74 bool
Chris@849 75 FeatureExtractionModelTransformer::initialise()
Chris@849 76 {
Chris@1237 77 // This is (now) called from the run thread. The plugin is
Chris@1237 78 // constructed, initialised, used, and destroyed all from a single
Chris@1237 79 // thread.
Chris@1237 80
Chris@849 81 // All transforms must use the same plugin, parameters, and
Chris@849 82 // inputs: they can differ only in choice of plugin output. So we
Chris@849 83 // initialise based purely on the first transform in the list (but
Chris@849 84 // first check that they are actually similar as promised)
Chris@849 85
Chris@1739 86 for (int j = 1; in_range_for(m_transforms, j); ++j) {
Chris@849 87 if (!areTransformsSimilar(m_transforms[0], m_transforms[j])) {
Chris@849 88 m_message = tr("Transforms supplied to a single FeatureExtractionModelTransformer instance must be similar in every respect except plugin output");
Chris@1368 89 SVCERR << m_message << endl;
Chris@849 90 return false;
Chris@849 91 }
Chris@849 92 }
Chris@849 93
Chris@849 94 Transform primaryTransform = m_transforms[0];
Chris@849 95
Chris@849 96 QString pluginId = primaryTransform.getPluginIdentifier();
Chris@320 97
Chris@1226 98 FeatureExtractionPluginFactory *factory =
Chris@1226 99 FeatureExtractionPluginFactory::instance();
Chris@320 100
Chris@320 101 if (!factory) {
Chris@361 102 m_message = tr("No factory available for feature extraction plugin id \"%1\" (unknown plugin type, or internal error?)").arg(pluginId);
Chris@1368 103 SVCERR << m_message << endl;
Chris@1429 104 return false;
Chris@320 105 }
Chris@320 106
Chris@1739 107 auto input = ModelById::getAs<DenseTimeValueModel>(getInputModel());
Chris@350 108 if (!input) {
Chris@361 109 m_message = tr("Input model for feature extraction plugin \"%1\" is of wrong type (internal error?)").arg(pluginId);
Chris@1368 110 SVCERR << m_message << endl;
Chris@849 111 return false;
Chris@350 112 }
Chris@320 113
Chris@1264 114 SVDEBUG << "FeatureExtractionModelTransformer: Instantiating plugin for transform in thread "
Chris@1264 115 << QThread::currentThreadId() << endl;
Chris@1211 116
Chris@1040 117 m_plugin = factory->instantiatePlugin(pluginId, input->getSampleRate());
Chris@320 118 if (!m_plugin) {
Chris@361 119 m_message = tr("Failed to instantiate plugin \"%1\"").arg(pluginId);
Chris@1368 120 SVCERR << m_message << endl;
Chris@1429 121 return false;
Chris@320 122 }
Chris@320 123
Chris@350 124 TransformFactory::getInstance()->makeContextConsistentWithPlugin
Chris@849 125 (primaryTransform, m_plugin);
Chris@1368 126
Chris@350 127 TransformFactory::getInstance()->setPluginParameters
Chris@849 128 (primaryTransform, m_plugin);
Chris@1368 129
Chris@930 130 int channelCount = input->getChannelCount();
Chris@930 131 if ((int)m_plugin->getMaxChannelCount() < channelCount) {
Chris@1429 132 channelCount = 1;
Chris@320 133 }
Chris@930 134 if ((int)m_plugin->getMinChannelCount() > channelCount) {
Chris@361 135 m_message = tr("Cannot provide enough channels to feature extraction plugin \"%1\" (plugin min is %2, max %3; input model has %4)")
Chris@361 136 .arg(pluginId)
Chris@361 137 .arg(m_plugin->getMinChannelCount())
Chris@361 138 .arg(m_plugin->getMaxChannelCount())
Chris@361 139 .arg(input->getChannelCount());
Chris@1368 140 SVCERR << m_message << endl;
Chris@1429 141 return false;
Chris@320 142 }
Chris@1374 143
Chris@1374 144 int step = primaryTransform.getStepSize();
Chris@1374 145 int block = primaryTransform.getBlockSize();
Chris@1368 146
Chris@690 147 SVDEBUG << "Initialising feature extraction plugin with channels = "
Chris@1374 148 << channelCount << ", step = " << step
Chris@1374 149 << ", block = " << block << endl;
Chris@320 150
Chris@1374 151 if (!m_plugin->initialise(channelCount, step, block)) {
Chris@1374 152
Chris@1374 153 int preferredStep = int(m_plugin->getPreferredStepSize());
Chris@1374 154 int preferredBlock = int(m_plugin->getPreferredBlockSize());
Chris@1264 155
Chris@1374 156 if (step != preferredStep || block != preferredBlock) {
Chris@361 157
Chris@1374 158 SVDEBUG << "Initialisation failed, trying again with preferred step = "
Chris@1374 159 << preferredStep << ", block = " << preferredBlock << endl;
Chris@361 160
Chris@1739 161 if (!m_plugin->initialise(channelCount,
Chris@1739 162 preferredStep,
Chris@1739 163 preferredBlock)) {
Chris@361 164
Chris@1264 165 SVDEBUG << "Initialisation failed again" << endl;
Chris@1264 166
Chris@361 167 m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId);
Chris@1368 168 SVCERR << m_message << endl;
Chris@849 169 return false;
Chris@361 170
Chris@361 171 } else {
Chris@1264 172
Chris@1264 173 SVDEBUG << "Initialisation succeeded this time" << endl;
Chris@1374 174
Chris@1374 175 // Set these values into the primary transform in the list
Chris@1374 176 m_transforms[0].setStepSize(preferredStep);
Chris@1374 177 m_transforms[0].setBlockSize(preferredBlock);
Chris@1264 178
Chris@361 179 m_message = tr("Feature extraction plugin \"%1\" rejected the given step and block sizes (%2 and %3); using plugin defaults (%4 and %5) instead")
Chris@361 180 .arg(pluginId)
Chris@1374 181 .arg(step)
Chris@1374 182 .arg(block)
Chris@1374 183 .arg(preferredStep)
Chris@1374 184 .arg(preferredBlock);
Chris@1368 185 SVCERR << m_message << endl;
Chris@361 186 }
Chris@361 187
Chris@361 188 } else {
Chris@361 189
Chris@1374 190 SVDEBUG << "Initialisation failed (with step = " << step
Chris@1374 191 << " and block = " << block
Chris@1374 192 << ", both matching the plugin's preference)" << endl;
Chris@1264 193
Chris@361 194 m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId);
Chris@1368 195 SVCERR << m_message << endl;
Chris@849 196 return false;
Chris@361 197 }
Chris@1264 198 } else {
Chris@1264 199 SVDEBUG << "Initialisation succeeded" << endl;
Chris@320 200 }
Chris@320 201
Chris@849 202 if (primaryTransform.getPluginVersion() != "") {
Chris@366 203 QString pv = QString("%1").arg(m_plugin->getPluginVersion());
Chris@849 204 if (pv != primaryTransform.getPluginVersion()) {
Chris@366 205 QString vm = tr("Transform was configured for version %1 of plugin \"%2\", but the plugin being used is version %3")
Chris@849 206 .arg(primaryTransform.getPluginVersion())
Chris@366 207 .arg(pluginId)
Chris@366 208 .arg(pv);
Chris@366 209 if (m_message != "") {
Chris@366 210 m_message = QString("%1; %2").arg(vm).arg(m_message);
Chris@366 211 } else {
Chris@366 212 m_message = vm;
Chris@366 213 }
Chris@1368 214 SVCERR << m_message << endl;
Chris@366 215 }
Chris@366 216 }
Chris@366 217
Chris@320 218 Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors();
Chris@320 219
Chris@320 220 if (outputs.empty()) {
Chris@361 221 m_message = tr("Plugin \"%1\" has no outputs").arg(pluginId);
Chris@1368 222 SVCERR << m_message << endl;
Chris@1429 223 return false;
Chris@320 224 }
Chris@320 225
Chris@1739 226 for (int j = 0; in_range_for(m_transforms, j); ++j) {
Chris@849 227
Chris@1739 228 for (int i = 0; in_range_for(outputs, i); ++i) {
Chris@1739 229
Chris@849 230 if (m_transforms[j].getOutput() == "" ||
Chris@1739 231 outputs[i].identifier ==
Chris@1739 232 m_transforms[j].getOutput().toStdString()) {
Chris@1739 233
Chris@849 234 m_outputNos.push_back(i);
Chris@1739 235 m_descriptors.push_back(outputs[i]);
Chris@849 236 m_fixedRateFeatureNos.push_back(-1); // we increment before use
Chris@849 237 break;
Chris@849 238 }
Chris@849 239 }
Chris@849 240
Chris@1739 241 if (!in_range_for(m_descriptors, j)) {
Chris@849 242 m_message = tr("Plugin \"%1\" has no output named \"%2\"")
Chris@849 243 .arg(pluginId)
Chris@849 244 .arg(m_transforms[j].getOutput());
Chris@1368 245 SVCERR << m_message << endl;
Chris@849 246 return false;
Chris@849 247 }
Chris@320 248 }
Chris@320 249
Chris@1739 250 for (int j = 0; in_range_for(m_transforms, j); ++j) {
Chris@876 251 createOutputModels(j);
Chris@849 252 }
Chris@849 253
Chris@1211 254 m_outputMutex.lock();
Chris@1211 255 m_haveOutputs = true;
Chris@1211 256 m_outputsCondition.wakeAll();
Chris@1211 257 m_outputMutex.unlock();
Chris@1211 258
Chris@849 259 return true;
Chris@558 260 }
Chris@558 261
Chris@558 262 void
Chris@1237 263 FeatureExtractionModelTransformer::deinitialise()
Chris@1237 264 {
Chris@1264 265 SVDEBUG << "FeatureExtractionModelTransformer: deleting plugin for transform in thread "
Chris@1264 266 << QThread::currentThreadId() << endl;
Chris@1372 267
Chris@1372 268 try {
Chris@1372 269 delete m_plugin;
Chris@1372 270 } catch (const std::exception &e) {
Chris@1372 271 // A destructor shouldn't throw an exception. But at one point
Chris@1372 272 // (now fixed) our plugin stub destructor could have
Chris@1372 273 // accidentally done so, so just in case:
Chris@1372 274 SVCERR << "FeatureExtractionModelTransformer: caught exception while deleting plugin: " << e.what() << endl;
Chris@1372 275 m_message = e.what();
Chris@1372 276 }
Chris@1582 277 m_plugin = nullptr;
Chris@1739 278
Chris@1739 279 m_descriptors.clear();
Chris@1237 280 }
Chris@1237 281
Chris@1237 282 void
Chris@876 283 FeatureExtractionModelTransformer::createOutputModels(int n)
Chris@558 284 {
Chris@1739 285 auto input = ModelById::getAs<DenseTimeValueModel>(getInputModel());
Chris@1739 286 if (!input) return;
Chris@712 287
Chris@849 288 PluginRDFDescription description(m_transforms[n].getPluginIdentifier());
Chris@849 289 QString outputId = m_transforms[n].getOutput();
Chris@558 290
Chris@320 291 int binCount = 1;
Chris@320 292 float minValue = 0.0, maxValue = 0.0;
Chris@320 293 bool haveExtents = false;
Chris@1739 294 bool haveBinCount = m_descriptors[n].hasFixedBinCount;
Chris@876 295
Chris@876 296 if (haveBinCount) {
Chris@1739 297 binCount = (int)m_descriptors[n].binCount;
Chris@320 298 }
Chris@320 299
Chris@876 300 m_needAdditionalModels[n] = false;
Chris@876 301
Chris@1739 302 if (binCount > 0 && m_descriptors[n].hasKnownExtents) {
Chris@1739 303 minValue = m_descriptors[n].minValue;
Chris@1739 304 maxValue = m_descriptors[n].maxValue;
Chris@320 305 haveExtents = true;
Chris@320 306 }
Chris@320 307
Chris@1040 308 sv_samplerate_t modelRate = input->getSampleRate();
Chris@1254 309 sv_samplerate_t outputRate = modelRate;
Chris@930 310 int modelResolution = 1;
Chris@712 311
Chris@1739 312 if (m_descriptors[n].sampleType !=
Chris@785 313 Vamp::Plugin::OutputDescriptor::OneSamplePerStep) {
Chris@1254 314
Chris@1739 315 outputRate = m_descriptors[n].sampleRate;
Chris@1254 316
Chris@1254 317 //!!! SV doesn't actually support display of models that have
Chris@1254 318 //!!! different underlying rates together -- so we always set
Chris@1254 319 //!!! the model rate to be the input model's rate, and adjust
Chris@1254 320 //!!! the resolution appropriately. We can't properly display
Chris@1254 321 //!!! data with a higher resolution than the base model at all
Chris@1254 322 if (outputRate > input->getSampleRate()) {
Chris@1264 323 SVDEBUG << "WARNING: plugin reports output sample rate as "
Chris@1264 324 << outputRate
Chris@1264 325 << " (can't display features with finer resolution than the input rate of "
Chris@1264 326 << modelRate << ")" << endl;
Chris@1254 327 outputRate = modelRate;
Chris@785 328 }
Chris@785 329 }
Chris@785 330
Chris@1739 331 switch (m_descriptors[n].sampleType) {
Chris@320 332
Chris@320 333 case Vamp::Plugin::OutputDescriptor::VariableSampleRate:
Chris@1429 334 if (outputRate != 0.0) {
Chris@1429 335 modelResolution = int(round(modelRate / outputRate));
Chris@1429 336 }
Chris@1429 337 break;
Chris@320 338
Chris@320 339 case Vamp::Plugin::OutputDescriptor::OneSamplePerStep:
Chris@1429 340 modelResolution = m_transforms[n].getStepSize();
Chris@1429 341 break;
Chris@320 342
Chris@320 343 case Vamp::Plugin::OutputDescriptor::FixedSampleRate:
Chris@1254 344 if (outputRate <= 0.0) {
Chris@1739 345 SVDEBUG << "WARNING: Fixed sample-rate plugin reports invalid sample rate " << m_descriptors[n].sampleRate << "; defaulting to input rate of " << input->getSampleRate() << endl;
Chris@1071 346 modelResolution = 1;
Chris@451 347 } else {
Chris@1254 348 modelResolution = int(round(modelRate / outputRate));
Chris@1254 349 // cerr << "modelRate = " << modelRate << ", descriptor rate = " << outputRate << ", modelResolution = " << modelResolution << endl;
Chris@451 350 }
Chris@1429 351 break;
Chris@320 352 }
Chris@320 353
Chris@441 354 bool preDurationPlugin = (m_plugin->getVampApiVersion() < 2);
Chris@441 355
Chris@1739 356 std::shared_ptr<Model> out;
Chris@849 357
Chris@441 358 if (binCount == 0 &&
Chris@1739 359 (preDurationPlugin || !m_descriptors[n].hasDuration)) {
Chris@320 360
Chris@445 361 // Anything with no value and no duration is an instant
Chris@445 362
Chris@1739 363 out = std::make_shared<SparseOneDimensionalModel>
Chris@1739 364 (modelRate, modelResolution, false);
Chris@1739 365
Chris@558 366 QString outputEventTypeURI = description.getOutputEventTypeURI(outputId);
Chris@849 367 out->setRDFTypeURI(outputEventTypeURI);
Chris@558 368
Chris@441 369 } else if ((preDurationPlugin && binCount > 1 &&
Chris@1739 370 (m_descriptors[n].sampleType ==
Chris@441 371 Vamp::Plugin::OutputDescriptor::VariableSampleRate)) ||
Chris@1739 372 (!preDurationPlugin && m_descriptors[n].hasDuration)) {
Chris@441 373
Chris@441 374 // For plugins using the old v1 API without explicit duration,
Chris@441 375 // we treat anything that has multiple bins (i.e. that has the
Chris@441 376 // potential to have value and duration) and a variable sample
Chris@441 377 // rate as a note model, taking its values as pitch, duration
Chris@441 378 // and velocity (if present) respectively. This is the same
Chris@441 379 // behaviour as always applied by SV to these plugins in the
Chris@441 380 // past.
Chris@441 381
Chris@441 382 // For plugins with the newer API, we treat anything with
Chris@441 383 // duration as either a note model with pitch and velocity, or
Chris@441 384 // a region model.
Chris@441 385
Chris@441 386 // How do we know whether it's an interval or note model?
Chris@441 387 // What's the essential difference? Is a note model any
Chris@441 388 // interval model using a Hz or "MIDI pitch" scale? There
Chris@441 389 // isn't really a reliable test for "MIDI pitch"... Does a
Chris@441 390 // note model always have velocity? This is a good question
Chris@441 391 // to be addressed by accompanying RDF, but for the moment we
Chris@441 392 // will do the following...
Chris@441 393
Chris@441 394 bool isNoteModel = false;
Chris@441 395
Chris@441 396 // Regions have only value (and duration -- we can't extract a
Chris@441 397 // region model from an old-style plugin that doesn't support
Chris@441 398 // duration)
Chris@441 399 if (binCount > 1) isNoteModel = true;
Chris@441 400
Chris@595 401 // Regions do not have units of Hz or MIDI things (a sweeping
Chris@595 402 // assumption!)
Chris@1739 403 if (m_descriptors[n].unit == "Hz" ||
Chris@1739 404 m_descriptors[n].unit.find("MIDI") != std::string::npos ||
Chris@1739 405 m_descriptors[n].unit.find("midi") != std::string::npos) {
Chris@595 406 isNoteModel = true;
Chris@595 407 }
Chris@441 408
Chris@441 409 // If we had a "sparse 3D model", we would have the additional
Chris@441 410 // problem of determining whether to use that here (if bin
Chris@441 411 // count > 1). But we don't.
Chris@441 412
Chris@859 413 QSettings settings;
Chris@859 414
Chris@1647 415 if (isNoteModel) {
Chris@441 416
Chris@1769 417 QSettings settings;
Chris@1769 418 settings.beginGroup("Transformer");
Chris@1769 419 bool flexi = settings.value("use-flexi-note-model", false).toBool();
Chris@1769 420 settings.endGroup();
Chris@1769 421
Chris@1769 422 SVCERR << "flexi = " << flexi << endl;
Chris@1769 423
Chris@441 424 NoteModel *model;
Chris@441 425 if (haveExtents) {
Chris@859 426 model = new NoteModel
Chris@1769 427 (modelRate, modelResolution, minValue, maxValue, false,
Chris@1769 428 flexi ? NoteModel::FLEXI_NOTE : NoteModel::NORMAL_NOTE);
Chris@441 429 } else {
Chris@859 430 model = new NoteModel
Chris@1769 431 (modelRate, modelResolution, false,
Chris@1769 432 flexi ? NoteModel::FLEXI_NOTE : NoteModel::NORMAL_NOTE);
gyorgyf@786 433 }
Chris@1739 434 model->setScaleUnits(m_descriptors[n].unit.c_str());
Chris@1739 435 out.reset(model);
gyorgyf@786 436
Chris@441 437 } else {
Chris@441 438
Chris@441 439 RegionModel *model;
Chris@441 440 if (haveExtents) {
Chris@441 441 model = new RegionModel
Chris@441 442 (modelRate, modelResolution, minValue, maxValue, false);
Chris@441 443 } else {
Chris@441 444 model = new RegionModel
Chris@441 445 (modelRate, modelResolution, false);
Chris@441 446 }
Chris@1739 447 model->setScaleUnits(m_descriptors[n].unit.c_str());
Chris@1739 448 out.reset(model);
Chris@441 449 }
Chris@441 450
Chris@558 451 QString outputEventTypeURI = description.getOutputEventTypeURI(outputId);
Chris@849 452 out->setRDFTypeURI(outputEventTypeURI);
Chris@558 453
Chris@876 454 } else if (binCount == 1 ||
Chris@1739 455 (m_descriptors[n].sampleType ==
Chris@441 456 Vamp::Plugin::OutputDescriptor::VariableSampleRate)) {
Chris@441 457
Chris@441 458 // Anything that is not a 1D, note, or interval model and that
Chris@441 459 // has only one value per result must be a sparse time value
Chris@441 460 // model.
Chris@441 461
Chris@441 462 // Anything that is not a 1D, note, or interval model and that
Chris@876 463 // has a variable sample rate is treated as a set of sparse
Chris@876 464 // time value models, one per output bin, because we lack a
Chris@441 465 // sparse 3D model.
Chris@320 466
Chris@876 467 // Anything that is not a 1D, note, or interval model and that
Chris@876 468 // has a fixed sample rate but an unknown number of values per
Chris@876 469 // result is also treated as a set of sparse time value models.
Chris@876 470
Chris@876 471 // For sets of sparse time value models, we create a single
Chris@876 472 // model first as the "standard" output and then create models
Chris@876 473 // for bins 1+ in the additional model map (mapping the output
Chris@876 474 // descriptor to a list of models indexed by bin-1). But we
Chris@876 475 // don't create the additional models yet, as this case has to
Chris@876 476 // work even if the number of bins is unknown at this point --
Chris@877 477 // we create an additional model (copying its parameters from
Chris@877 478 // the default one) each time a new bin is encountered.
Chris@876 479
Chris@876 480 if (!haveBinCount || binCount > 1) {
Chris@876 481 m_needAdditionalModels[n] = true;
Chris@876 482 }
Chris@876 483
Chris@320 484 SparseTimeValueModel *model;
Chris@320 485 if (haveExtents) {
Chris@320 486 model = new SparseTimeValueModel
Chris@320 487 (modelRate, modelResolution, minValue, maxValue, false);
Chris@320 488 } else {
Chris@320 489 model = new SparseTimeValueModel
Chris@320 490 (modelRate, modelResolution, false);
Chris@320 491 }
Chris@558 492
Chris@558 493 Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors();
Chris@849 494 model->setScaleUnits(outputs[m_outputNos[n]].unit.c_str());
Chris@320 495
Chris@1739 496 out.reset(model);
Chris@320 497
Chris@558 498 QString outputEventTypeURI = description.getOutputEventTypeURI(outputId);
Chris@849 499 out->setRDFTypeURI(outputEventTypeURI);
Chris@558 500
Chris@441 501 } else {
Chris@320 502
Chris@441 503 // Anything that is not a 1D, note, or interval model and that
Chris@441 504 // has a fixed sample rate and more than one value per result
Chris@441 505 // must be a dense 3D model.
Chris@320 506
Chris@1777 507 auto model =
Chris@1777 508 new BasicCompressedDenseThreeDimensionalModel
Chris@1777 509 (modelRate, modelResolution, binCount, false);
Chris@320 510
Chris@1739 511 if (!m_descriptors[n].binNames.empty()) {
Chris@1429 512 std::vector<QString> names;
Chris@1739 513 for (int i = 0; i < (int)m_descriptors[n].binNames.size(); ++i) {
Chris@1739 514 names.push_back(m_descriptors[n].binNames[i].c_str());
Chris@1429 515 }
Chris@1429 516 model->setBinNames(names);
Chris@1429 517 }
Chris@320 518
Chris@1739 519 out.reset(model);
Chris@558 520
Chris@558 521 QString outputSignalTypeURI = description.getOutputSignalTypeURI(outputId);
Chris@849 522 out->setRDFTypeURI(outputSignalTypeURI);
Chris@320 523 }
Chris@333 524
Chris@849 525 if (out) {
Chris@1739 526 out->setSourceModel(getInputModel());
Chris@1752 527 m_outputs.push_back(ModelById::add(out));
Chris@849 528 }
Chris@320 529 }
Chris@320 530
Chris@1211 531 void
Chris@1211 532 FeatureExtractionModelTransformer::awaitOutputModels()
Chris@1211 533 {
Chris@1211 534 m_outputMutex.lock();
Chris@1368 535 while (!m_haveOutputs && !m_abandoned) {
Chris@1368 536 m_outputsCondition.wait(&m_outputMutex, 500);
Chris@1211 537 }
Chris@1211 538 m_outputMutex.unlock();
Chris@1211 539 }
Chris@1211 540
Chris@331 541 FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()
Chris@320 542 {
Chris@1237 543 // Parent class dtor set the abandoned flag and waited for the run
Chris@1237 544 // thread to exit; the run thread owns the plugin, and should have
Chris@1237 545 // destroyed it before exiting (via a call to deinitialise)
Chris@320 546 }
Chris@320 547
Chris@876 548 FeatureExtractionModelTransformer::Models
Chris@876 549 FeatureExtractionModelTransformer::getAdditionalOutputModels()
Chris@876 550 {
Chris@876 551 Models mm;
Chris@1739 552 for (auto mp : m_additionalModels) {
Chris@1739 553 for (auto m: mp.second) {
Chris@1739 554 mm.push_back(m.second);
Chris@876 555 }
Chris@876 556 }
Chris@876 557 return mm;
Chris@876 558 }
Chris@876 559
Chris@877 560 bool
Chris@877 561 FeatureExtractionModelTransformer::willHaveAdditionalOutputModels()
Chris@877 562 {
Chris@1739 563 for (auto p : m_needAdditionalModels) {
Chris@1739 564 if (p.second) return true;
Chris@877 565 }
Chris@877 566 return false;
Chris@877 567 }
Chris@877 568
Chris@1739 569 ModelId
Chris@876 570 FeatureExtractionModelTransformer::getAdditionalModel(int n, int binNo)
Chris@876 571 {
Chris@876 572 if (binNo == 0) {
Chris@1739 573 SVCERR << "Internal error: binNo == 0 in getAdditionalModel (should be using primary model, not calling getAdditionalModel)" << endl;
Chris@1739 574 return {};
Chris@876 575 }
Chris@876 576
Chris@1739 577 if (!in_range_for(m_outputs, n)) {
Chris@1739 578 SVCERR << "getAdditionalModel: Output " << n << " out of range" << endl;
Chris@1739 579 return {};
Chris@1739 580 }
Chris@876 581
Chris@1739 582 if (!in_range_for(m_needAdditionalModels, n) ||
Chris@1739 583 !m_needAdditionalModels[n]) {
Chris@1739 584 return {};
Chris@1739 585 }
Chris@1739 586
Chris@1739 587 if (!m_additionalModels[n][binNo].isNone()) {
Chris@1739 588 return m_additionalModels[n][binNo];
Chris@1739 589 }
Chris@876 590
Chris@1739 591 SVDEBUG << "getAdditionalModel(" << n << ", " << binNo
Chris@1739 592 << "): creating" << endl;
Chris@876 593
Chris@1739 594 auto baseModel = ModelById::getAs<SparseTimeValueModel>(m_outputs[n]);
Chris@1739 595 if (!baseModel) {
Chris@1739 596 SVCERR << "getAdditionalModel: Output model not conformable, or has vanished" << endl;
Chris@1739 597 return {};
Chris@1739 598 }
Chris@1739 599
Chris@1739 600 SVDEBUG << "getAdditionalModel(" << n << ", " << binNo
Chris@1739 601 << "): (from " << baseModel << ")" << endl;
Chris@876 602
Chris@876 603 SparseTimeValueModel *additional =
Chris@876 604 new SparseTimeValueModel(baseModel->getSampleRate(),
Chris@876 605 baseModel->getResolution(),
Chris@876 606 baseModel->getValueMinimum(),
Chris@876 607 baseModel->getValueMaximum(),
Chris@876 608 false);
Chris@876 609
Chris@876 610 additional->setScaleUnits(baseModel->getScaleUnits());
Chris@876 611 additional->setRDFTypeURI(baseModel->getRDFTypeURI());
Chris@876 612
Chris@1752 613 ModelId additionalId = ModelById::add
Chris@1752 614 (std::shared_ptr<SparseTimeValueModel>(additional));
Chris@1739 615 m_additionalModels[n][binNo] = additionalId;
Chris@1739 616 return additionalId;
Chris@320 617 }
Chris@320 618
Chris@320 619 void
Chris@331 620 FeatureExtractionModelTransformer::run()
Chris@320 621 {
Chris@1373 622 try {
Chris@1373 623 if (!initialise()) {
Chris@1373 624 abandon();
Chris@1373 625 return;
Chris@1373 626 }
Chris@1373 627 } catch (const std::exception &e) {
Chris@1368 628 abandon();
Chris@1373 629 m_message = e.what();
Chris@1368 630 return;
Chris@1368 631 }
Chris@320 632
Chris@1368 633 if (m_outputs.empty()) {
Chris@1368 634 abandon();
Chris@1368 635 return;
Chris@1368 636 }
Chris@320 637
Chris@850 638 Transform primaryTransform = m_transforms[0];
Chris@850 639
Chris@1775 640 ModelId inputId = getInputModel();
Chris@1775 641
Chris@1739 642 bool ready = false;
Chris@1739 643 while (!ready && !m_abandoned) {
Chris@1739 644 { // scope so as to release input shared_ptr before sleeping
Chris@1775 645 auto input = ModelById::getAs<DenseTimeValueModel>(inputId);
Chris@1784 646 if (!input || !input->isOK()) {
Chris@1739 647 abandon();
Chris@1739 648 return;
Chris@1739 649 }
Chris@1739 650 ready = input->isReady();
Chris@1739 651 }
Chris@1739 652 if (!ready) {
Chris@1775 653 SVDEBUG << "FeatureExtractionModelTransformer::run: Waiting for input model "
Chris@1775 654 << inputId << " to be ready..." << endl;
Chris@1739 655 usleep(500000);
Chris@1739 656 }
Chris@320 657 }
Chris@497 658 if (m_abandoned) return;
Chris@320 659
Chris@1775 660 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1775 661 SVDEBUG << "FeatureExtractionModelTransformer::run: Input model "
Chris@1775 662 << inputId << " is ready, going ahead" << endl;
Chris@1775 663 #endif
Chris@1739 664
Chris@1755 665 sv_samplerate_t sampleRate;
Chris@1755 666 int channelCount;
Chris@1755 667 sv_frame_t startFrame;
Chris@1755 668 sv_frame_t endFrame;
Chris@1755 669
Chris@1755 670 { // scope so as not to have this borrowed pointer retained around
Chris@1755 671 // the edges of the process loop
Chris@1755 672 auto input = ModelById::getAs<DenseTimeValueModel>(inputId);
Chris@1755 673 if (!input) {
Chris@1755 674 abandon();
Chris@1755 675 return;
Chris@1755 676 }
Chris@320 677
Chris@1755 678 sampleRate = input->getSampleRate();
Chris@1755 679
Chris@1755 680 channelCount = input->getChannelCount();
Chris@1755 681 if ((int)m_plugin->getMaxChannelCount() < channelCount) {
Chris@1755 682 channelCount = 1;
Chris@1755 683 }
Chris@1755 684
Chris@1755 685 startFrame = input->getStartFrame();
Chris@1755 686 endFrame = input->getEndFrame();
Chris@320 687 }
Chris@320 688
Chris@320 689 float **buffers = new float*[channelCount];
Chris@930 690 for (int ch = 0; ch < channelCount; ++ch) {
Chris@1429 691 buffers[ch] = new float[primaryTransform.getBlockSize() + 2];
Chris@320 692 }
Chris@320 693
Chris@930 694 int stepSize = primaryTransform.getStepSize();
Chris@930 695 int blockSize = primaryTransform.getBlockSize();
Chris@350 696
Chris@320 697 bool frequencyDomain = (m_plugin->getInputDomain() ==
Chris@320 698 Vamp::Plugin::FrequencyDomain);
Chris@1558 699
Chris@320 700 std::vector<FFTModel *> fftModels;
Chris@320 701
Chris@320 702 if (frequencyDomain) {
Chris@1775 703 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1775 704 SVDEBUG << "FeatureExtractionModelTransformer::run: Input is frequency-domain" << endl;
Chris@1775 705 #endif
Chris@930 706 for (int ch = 0; ch < channelCount; ++ch) {
Chris@320 707 FFTModel *model = new FFTModel
Chris@1752 708 (inputId,
Chris@1739 709 channelCount == 1 ? m_input.getChannel() : ch,
Chris@1739 710 primaryTransform.getWindowType(),
Chris@1739 711 blockSize,
Chris@1739 712 stepSize,
Chris@1739 713 blockSize);
Chris@1080 714 if (!model->isOK() || model->getError() != "") {
Chris@1080 715 QString err = model->getError();
Chris@320 716 delete model;
Chris@1739 717 for (int j = 0; in_range_for(m_outputNos, j); ++j) {
Chris@850 718 setCompletion(j, 100);
Chris@850 719 }
Chris@1775 720 SVDEBUG << "FeatureExtractionModelTransformer::run: Failed to create FFT model for input model " << inputId << ": " << err << endl;
Chris@1784 721 m_message = "Failed to create the FFT model for this feature extraction model transformer: error is: " + err;
Chris@1784 722 for (int cch = 0; cch < ch; ++cch) {
Chris@1784 723 delete fftModels[cch];
Chris@1784 724 }
Chris@1784 725 abandon();
Chris@1784 726 return;
Chris@320 727 }
Chris@320 728 fftModels.push_back(model);
Chris@320 729 }
Chris@1775 730 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1775 731 SVDEBUG << "FeatureExtractionModelTransformer::run: Created FFT model(s) for frequency-domain input" << endl;
Chris@1775 732 #endif
Chris@320 733 }
Chris@320 734
Chris@850 735 RealTime contextStartRT = primaryTransform.getStartTime();
Chris@850 736 RealTime contextDurationRT = primaryTransform.getDuration();
Chris@350 737
Chris@1040 738 sv_frame_t contextStart =
Chris@350 739 RealTime::realTime2Frame(contextStartRT, sampleRate);
Chris@350 740
Chris@1040 741 sv_frame_t contextDuration =
Chris@350 742 RealTime::realTime2Frame(contextDurationRT, sampleRate);
Chris@320 743
Chris@320 744 if (contextStart == 0 || contextStart < startFrame) {
Chris@320 745 contextStart = startFrame;
Chris@320 746 }
Chris@320 747
Chris@320 748 if (contextDuration == 0) {
Chris@320 749 contextDuration = endFrame - contextStart;
Chris@320 750 }
Chris@320 751 if (contextStart + contextDuration > endFrame) {
Chris@320 752 contextDuration = endFrame - contextStart;
Chris@320 753 }
Chris@320 754
Chris@1039 755 sv_frame_t blockFrame = contextStart;
Chris@320 756
Chris@320 757 long prevCompletion = 0;
Chris@320 758
Chris@1739 759 for (int j = 0; in_range_for(m_outputNos, j); ++j) {
Chris@850 760 setCompletion(j, 0);
Chris@850 761 }
Chris@320 762
Chris@1582 763 float *reals = nullptr;
Chris@1582 764 float *imaginaries = nullptr;
Chris@556 765 if (frequencyDomain) {
Chris@556 766 reals = new float[blockSize/2 + 1];
Chris@556 767 imaginaries = new float[blockSize/2 + 1];
Chris@556 768 }
Chris@556 769
Chris@678 770 QString error = "";
Chris@678 771
Chris@1372 772 try {
Chris@1372 773 while (!m_abandoned) {
Chris@320 774
Chris@1372 775 if (frequencyDomain) {
Chris@1372 776 if (blockFrame - int(blockSize)/2 >
Chris@1739 777 contextStart + contextDuration) {
Chris@1739 778 break;
Chris@1739 779 }
Chris@1372 780 } else {
Chris@1372 781 if (blockFrame >=
Chris@1739 782 contextStart + contextDuration) {
Chris@1739 783 break;
Chris@1739 784 }
Chris@1372 785 }
Chris@320 786
Chris@1558 787 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1558 788 SVDEBUG << "FeatureExtractionModelTransformer::run: blockFrame "
Chris@1558 789 << blockFrame << ", endFrame " << endFrame << ", blockSize "
Chris@1558 790 << blockSize << endl;
Chris@1558 791 #endif
Chris@1558 792
Chris@1372 793 int completion = int
Chris@1372 794 ((((blockFrame - contextStart) / stepSize) * 99) /
Chris@1372 795 (contextDuration / stepSize + 1));
Chris@320 796
Chris@1762 797 bool haveAllModels = true;
Chris@1755 798 if (!ModelById::get(inputId)) {
Chris@1762 799 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1762 800 SVDEBUG << "FeatureExtractionModelTransformer::run: Input model " << inputId << " no longer exists" << endl;
Chris@1762 801 #endif
Chris@1762 802 haveAllModels = false;
Chris@1762 803 } else {
Chris@1763 804 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1762 805 SVDEBUG << "Input model " << inputId << " still exists" << endl;
Chris@1763 806 #endif
Chris@1762 807 }
Chris@1762 808 for (auto mid: m_outputs) {
Chris@1762 809 if (!ModelById::get(mid)) {
Chris@1762 810 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1762 811 SVDEBUG << "FeatureExtractionModelTransformer::run: Output model " << mid << " no longer exists" << endl;
Chris@1762 812 #endif
Chris@1762 813 haveAllModels = false;
Chris@1762 814 } else {
Chris@1762 815 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1762 816 SVDEBUG << "Output model " << mid << " still exists" << endl;
Chris@1762 817 #endif
Chris@1762 818 }
Chris@1762 819 }
Chris@1762 820 if (!haveAllModels) {
Chris@1755 821 abandon();
Chris@1762 822 break;
Chris@1755 823 }
Chris@1762 824
Chris@1762 825 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1762 826 SVDEBUG << "FeatureExtractionModelTransformer::run: All models still exist" << endl;
Chris@1762 827 #endif
Chris@1755 828
Chris@1739 829 // channelCount is either input->channelCount or 1
Chris@320 830
Chris@1372 831 if (frequencyDomain) {
Chris@1372 832 for (int ch = 0; ch < channelCount; ++ch) {
Chris@1372 833 int column = int((blockFrame - startFrame) / stepSize);
Chris@1372 834 if (fftModels[ch]->getValuesAt(column, reals, imaginaries)) {
Chris@1372 835 for (int i = 0; i <= blockSize/2; ++i) {
Chris@1372 836 buffers[ch][i*2] = reals[i];
Chris@1372 837 buffers[ch][i*2+1] = imaginaries[i];
Chris@1372 838 }
Chris@1372 839 } else {
Chris@1372 840 for (int i = 0; i <= blockSize/2; ++i) {
Chris@1372 841 buffers[ch][i*2] = 0.f;
Chris@1372 842 buffers[ch][i*2+1] = 0.f;
Chris@1372 843 }
Chris@1558 844 }
Chris@1558 845
Chris@1372 846 error = fftModels[ch]->getError();
Chris@1372 847 if (error != "") {
Chris@1372 848 SVCERR << "FeatureExtractionModelTransformer::run: Abandoning, error is " << error << endl;
Chris@1372 849 m_abandoned = true;
Chris@1372 850 m_message = error;
Chris@1372 851 break;
Chris@1008 852 }
Chris@1372 853 }
Chris@1372 854 } else {
Chris@1372 855 getFrames(channelCount, blockFrame, blockSize, buffers);
Chris@1372 856 }
Chris@1372 857
Chris@1372 858 if (m_abandoned) break;
Chris@1372 859
Chris@1740 860 auto features = m_plugin->process
Chris@1558 861 (buffers,
Chris@1558 862 RealTime::frame2RealTime(blockFrame, sampleRate)
Chris@1558 863 .toVampRealTime());
Chris@1558 864
Chris@1372 865 if (m_abandoned) break;
Chris@1372 866
Chris@1740 867 for (int j = 0; in_range_for(m_outputNos, j); ++j) {
Chris@1740 868 for (int fi = 0; in_range_for(features[m_outputNos[j]], fi); ++fi) {
Chris@1740 869 auto feature = features[m_outputNos[j]][fi];
Chris@1372 870 addFeature(j, blockFrame, feature);
Chris@678 871 }
Chris@363 872 }
Chris@1372 873
Chris@1372 874 if (blockFrame == contextStart || completion > prevCompletion) {
Chris@1740 875 for (int j = 0; in_range_for(m_outputNos, j); ++j) {
Chris@1372 876 setCompletion(j, completion);
Chris@1372 877 }
Chris@1372 878 prevCompletion = completion;
Chris@1372 879 }
Chris@1372 880
Chris@1372 881 blockFrame += stepSize;
Chris@1372 882
Chris@320 883 }
Chris@320 884
Chris@1372 885 if (!m_abandoned) {
Chris@1740 886 auto features = m_plugin->getRemainingFeatures();
Chris@497 887
Chris@1740 888 for (int j = 0; in_range_for(m_outputNos, j); ++j) {
Chris@1740 889 for (int fi = 0; in_range_for(features[m_outputNos[j]], fi); ++fi) {
Chris@1740 890 auto feature = features[m_outputNos[j]][fi];
Chris@1372 891 addFeature(j, blockFrame, feature);
Chris@1762 892 if (m_abandoned) {
Chris@1762 893 break;
Chris@1762 894 }
Chris@1372 895 }
Chris@850 896 }
Chris@850 897 }
Chris@1372 898 } catch (const std::exception &e) {
Chris@1372 899 SVCERR << "FeatureExtractionModelTransformer::run: Exception caught: "
Chris@1372 900 << e.what() << endl;
Chris@1372 901 m_abandoned = true;
Chris@1372 902 m_message = e.what();
Chris@497 903 }
Chris@320 904
Chris@850 905 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@850 906 setCompletion(j, 100);
Chris@850 907 }
Chris@320 908
Chris@320 909 if (frequencyDomain) {
Chris@930 910 for (int ch = 0; ch < channelCount; ++ch) {
Chris@320 911 delete fftModels[ch];
Chris@320 912 }
Chris@556 913 delete[] reals;
Chris@556 914 delete[] imaginaries;
Chris@320 915 }
Chris@974 916
Chris@974 917 for (int ch = 0; ch < channelCount; ++ch) {
Chris@974 918 delete[] buffers[ch];
Chris@974 919 }
Chris@974 920 delete[] buffers;
Chris@1237 921
Chris@1237 922 deinitialise();
Chris@320 923 }
Chris@320 924
Chris@320 925 void
Chris@363 926 FeatureExtractionModelTransformer::getFrames(int channelCount,
Chris@1039 927 sv_frame_t startFrame,
Chris@1039 928 sv_frame_t size,
Chris@363 929 float **buffers)
Chris@320 930 {
Chris@1039 931 sv_frame_t offset = 0;
Chris@320 932
Chris@320 933 if (startFrame < 0) {
Chris@363 934 for (int c = 0; c < channelCount; ++c) {
Chris@1039 935 for (sv_frame_t i = 0; i < size && startFrame + i < 0; ++i) {
Chris@363 936 buffers[c][i] = 0.0f;
Chris@363 937 }
Chris@320 938 }
Chris@320 939 offset = -startFrame;
Chris@320 940 size -= offset;
Chris@320 941 if (size <= 0) return;
Chris@320 942 startFrame = 0;
Chris@320 943 }
Chris@320 944
Chris@1739 945 auto input = ModelById::getAs<DenseTimeValueModel>(getInputModel());
Chris@1739 946 if (!input) {
Chris@1739 947 return;
Chris@1739 948 }
Chris@363 949
Chris@1039 950 sv_frame_t got = 0;
Chris@350 951
Chris@363 952 if (channelCount == 1) {
Chris@363 953
Chris@1096 954 auto data = input->getData(m_input.getChannel(), startFrame, size);
Chris@1096 955 got = data.size();
Chris@1096 956
Chris@1096 957 copy(data.begin(), data.end(), buffers[0] + offset);
Chris@363 958
Chris@363 959 if (m_input.getChannel() == -1 && input->getChannelCount() > 1) {
Chris@363 960 // use mean instead of sum, as plugin input
Chris@363 961 float cc = float(input->getChannelCount());
Chris@1096 962 for (sv_frame_t i = 0; i < got; ++i) {
Chris@363 963 buffers[0][i + offset] /= cc;
Chris@363 964 }
Chris@363 965 }
Chris@363 966
Chris@363 967 } else {
Chris@363 968
Chris@1096 969 auto data = input->getMultiChannelData(0, channelCount-1, startFrame, size);
Chris@1096 970 if (!data.empty()) {
Chris@1096 971 got = data[0].size();
Chris@1096 972 for (int c = 0; in_range_for(data, c); ++c) {
Chris@1096 973 copy(data[c].begin(), data[c].end(), buffers[c] + offset);
Chris@363 974 }
Chris@363 975 }
Chris@363 976 }
Chris@320 977
Chris@320 978 while (got < size) {
Chris@363 979 for (int c = 0; c < channelCount; ++c) {
Chris@363 980 buffers[c][got + offset] = 0.0;
Chris@363 981 }
Chris@320 982 ++got;
Chris@320 983 }
Chris@320 984 }
Chris@320 985
Chris@320 986 void
Chris@850 987 FeatureExtractionModelTransformer::addFeature(int n,
Chris@1039 988 sv_frame_t blockFrame,
Chris@850 989 const Vamp::Plugin::Feature &feature)
Chris@320 990 {
Chris@1739 991 auto input = ModelById::get(getInputModel());
Chris@1739 992 if (!input) return;
Chris@1739 993
Chris@1739 994 sv_samplerate_t inputRate = input->getSampleRate();
Chris@320 995
Chris@843 996 // cerr << "FeatureExtractionModelTransformer::addFeature: blockFrame = "
Chris@712 997 // << blockFrame << ", hasTimestamp = " << feature.hasTimestamp
Chris@712 998 // << ", timestamp = " << feature.timestamp << ", hasDuration = "
Chris@712 999 // << feature.hasDuration << ", duration = " << feature.duration
Chris@843 1000 // << endl;
Chris@320 1001
Chris@1039 1002 sv_frame_t frame = blockFrame;
Chris@320 1003
Chris@1739 1004 if (m_descriptors[n].sampleType ==
Chris@1429 1005 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
Chris@320 1006
Chris@1429 1007 if (!feature.hasTimestamp) {
Chris@1429 1008 SVDEBUG
Chris@1429 1009 << "WARNING: FeatureExtractionModelTransformer::addFeature: "
Chris@1429 1010 << "Feature has variable sample rate but no timestamp!"
Chris@1429 1011 << endl;
Chris@1429 1012 return;
Chris@1429 1013 } else {
Chris@1429 1014 frame = RealTime::realTime2Frame(feature.timestamp, inputRate);
Chris@1429 1015 }
Chris@320 1016
Chris@1071 1017 // cerr << "variable sample rate: timestamp = " << feature.timestamp
Chris@1071 1018 // << " at input rate " << inputRate << " -> " << frame << endl;
Chris@1071 1019
Chris@1739 1020 } else if (m_descriptors[n].sampleType ==
Chris@1429 1021 Vamp::Plugin::OutputDescriptor::FixedSampleRate) {
Chris@320 1022
Chris@1739 1023 sv_samplerate_t rate = m_descriptors[n].sampleRate;
Chris@1071 1024 if (rate <= 0.0) {
Chris@1071 1025 rate = inputRate;
Chris@1071 1026 }
Chris@1071 1027
Chris@779 1028 if (!feature.hasTimestamp) {
Chris@849 1029 ++m_fixedRateFeatureNos[n];
Chris@779 1030 } else {
Chris@779 1031 RealTime ts(feature.timestamp.sec, feature.timestamp.nsec);
Chris@1071 1032 m_fixedRateFeatureNos[n] = (int)lrint(ts.toDouble() * rate);
Chris@779 1033 }
Chris@862 1034
Chris@1071 1035 // cerr << "m_fixedRateFeatureNo = " << m_fixedRateFeatureNos[n]
Chris@1739 1036 // << ", m_descriptor->sampleRate = " << m_descriptors[n].sampleRate
Chris@862 1037 // << ", inputRate = " << inputRate
Chris@862 1038 // << " giving frame = ";
Chris@1071 1039 frame = lrint((double(m_fixedRateFeatureNos[n]) / rate) * inputRate);
Chris@1071 1040 // cerr << frame << endl;
Chris@320 1041 }
Chris@862 1042
Chris@862 1043 if (frame < 0) {
Chris@1264 1044 SVDEBUG
Chris@862 1045 << "WARNING: FeatureExtractionModelTransformer::addFeature: "
Chris@862 1046 << "Negative frame counts are not supported (frame = " << frame
Chris@862 1047 << " from timestamp " << feature.timestamp
Chris@862 1048 << "), dropping feature"
Chris@862 1049 << endl;
Chris@862 1050 return;
Chris@862 1051 }
Chris@862 1052
Chris@441 1053 // Rather than repeat the complicated tests from the constructor
Chris@441 1054 // to determine what sort of model we must be adding the features
Chris@441 1055 // to, we instead test what sort of model the constructor decided
Chris@441 1056 // to create.
Chris@320 1057
Chris@1739 1058 ModelId outputId = m_outputs[n];
Chris@1740 1059
Chris@1740 1060 if (isOutputType<SparseOneDimensionalModel>(n)) {
Chris@1740 1061
Chris@1740 1062 auto model = ModelById::getAs<SparseOneDimensionalModel>(outputId);
Chris@1740 1063 if (!model) return;
Chris@1740 1064 model->add(Event(frame, feature.label.c_str()));
Chris@1740 1065
Chris@1740 1066 } else if (isOutputType<SparseTimeValueModel>(n)) {
Chris@1740 1067
Chris@1740 1068 auto model = ModelById::getAs<SparseTimeValueModel>(outputId);
Chris@1740 1069 if (!model) return;
Chris@1740 1070
Chris@1740 1071 for (int i = 0; in_range_for(feature.values, i); ++i) {
Chris@1740 1072
Chris@1740 1073 float value = feature.values[i];
Chris@1740 1074
Chris@1740 1075 QString label = feature.label.c_str();
Chris@1740 1076 if (feature.values.size() > 1) {
Chris@1740 1077 label = QString("[%1] %2").arg(i+1).arg(label);
Chris@1740 1078 }
Chris@1740 1079
Chris@1740 1080 auto targetModel = model;
Chris@1740 1081
Chris@1740 1082 if (m_needAdditionalModels[n] && i > 0) {
Chris@1740 1083 targetModel = ModelById::getAs<SparseTimeValueModel>
Chris@1740 1084 (getAdditionalModel(n, i));
Chris@1740 1085 if (!targetModel) targetModel = model;
Chris@1740 1086 }
Chris@1740 1087
Chris@1740 1088 targetModel->add(Event(frame, value, label));
Chris@1740 1089 }
Chris@1740 1090
Chris@1740 1091 } else if (isOutputType<NoteModel>(n) || isOutputType<RegionModel>(n)) {
Chris@1739 1092
Chris@1740 1093 int index = 0;
Chris@1740 1094
Chris@1740 1095 float value = 0.0;
Chris@1740 1096 if ((int)feature.values.size() > index) {
Chris@1740 1097 value = feature.values[index++];
Chris@1739 1098 }
Chris@441 1099
Chris@1740 1100 sv_frame_t duration = 1;
Chris@1740 1101 if (feature.hasDuration) {
Chris@1740 1102 duration = RealTime::realTime2Frame(feature.duration, inputRate);
Chris@1740 1103 } else {
Chris@1740 1104 if (in_range_for(feature.values, index)) {
Chris@1740 1105 duration = lrintf(feature.values[index++]);
Chris@1739 1106 }
Chris@1739 1107 }
Chris@1739 1108
Chris@1740 1109 auto noteModel = ModelById::getAs<NoteModel>(outputId);
Chris@1740 1110 if (noteModel) {
Chris@1739 1111
Chris@1740 1112 float velocity = 100;
Chris@1739 1113 if ((int)feature.values.size() > index) {
Chris@1740 1114 velocity = feature.values[index++];
Chris@454 1115 }
Chris@1740 1116 if (velocity < 0) velocity = 127;
Chris@1740 1117 if (velocity > 127) velocity = 127;
Chris@1740 1118
Chris@1740 1119 noteModel->add(Event(frame, value, // value is pitch
Chris@1740 1120 duration,
Chris@1740 1121 velocity / 127.f,
Chris@1740 1122 feature.label.c_str()));
Chris@1740 1123 }
Chris@454 1124
Chris@1740 1125 auto regionModel = ModelById::getAs<RegionModel>(outputId);
Chris@1740 1126 if (regionModel) {
Chris@1740 1127
Chris@1740 1128 if (feature.hasDuration && !feature.values.empty()) {
Chris@1740 1129
Chris@1740 1130 for (int i = 0; in_range_for(feature.values, i); ++i) {
Chris@1740 1131
Chris@1740 1132 float value = feature.values[i];
Chris@1740 1133
Chris@1740 1134 QString label = feature.label.c_str();
Chris@1740 1135 if (feature.values.size() > 1) {
Chris@1740 1136 label = QString("[%1] %2").arg(i+1).arg(label);
Chris@1739 1137 }
Chris@1740 1138
Chris@1739 1139 regionModel->add(Event(frame,
Chris@1739 1140 value,
Chris@1739 1141 duration,
Chris@1740 1142 label));
Chris@1739 1143 }
Chris@1740 1144 } else {
Chris@1740 1145
Chris@1740 1146 regionModel->add(Event(frame,
Chris@1740 1147 value,
Chris@1740 1148 duration,
Chris@1740 1149 feature.label.c_str()));
Chris@441 1150 }
Chris@441 1151 }
Chris@1740 1152
Chris@1777 1153 } else if (isOutputType<BasicCompressedDenseThreeDimensionalModel>(n)) {
Chris@1740 1154
Chris@1740 1155 auto model = ModelById::getAs
Chris@1777 1156 <BasicCompressedDenseThreeDimensionalModel>(outputId);
Chris@1740 1157 if (!model) return;
Chris@1740 1158
Chris@1740 1159 DenseThreeDimensionalModel::Column values = feature.values;
Chris@1740 1160
Chris@1740 1161 if (!feature.hasTimestamp && m_fixedRateFeatureNos[n] >= 0) {
Chris@1740 1162 model->setColumn(m_fixedRateFeatureNos[n], values);
Chris@1740 1163 } else {
Chris@1740 1164 model->setColumn(int(frame / model->getResolution()), values);
Chris@1740 1165 }
Chris@1762 1166 } else {
Chris@1762 1167
Chris@1762 1168 SVDEBUG << "FeatureExtractionModelTransformer::addFeature: Unknown output model type - possibly a deleted model" << endl;
Chris@1762 1169 abandon();
Chris@1739 1170 }
Chris@320 1171 }
Chris@320 1172
Chris@320 1173 void
Chris@850 1174 FeatureExtractionModelTransformer::setCompletion(int n, int completion)
Chris@320 1175 {
Chris@1558 1176 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1558 1177 SVDEBUG << "FeatureExtractionModelTransformer::setCompletion("
Chris@1558 1178 << completion << ")" << endl;
Chris@1558 1179 #endif
Chris@320 1180
Chris@1740 1181 (void)
Chris@1740 1182 (setOutputCompletion<SparseOneDimensionalModel>(n, completion) ||
Chris@1740 1183 setOutputCompletion<SparseTimeValueModel>(n, completion) ||
Chris@1740 1184 setOutputCompletion<NoteModel>(n, completion) ||
Chris@1740 1185 setOutputCompletion<RegionModel>(n, completion) ||
Chris@1777 1186 setOutputCompletion<BasicCompressedDenseThreeDimensionalModel>(n, completion));
Chris@320 1187 }
Chris@320 1188