annotate transform/FeatureExtractionModelTransformer.cpp @ 1777:d484490cdf69

Split EditableDenseThreeDimensionalModel into explicitly compressed and uncompressed variants. Simplifies the uncompressed version, and we may want to consider whether we need the compressed one at all.
author Chris Cannam
date Tue, 10 Sep 2019 16:34:47 +0100
parents e3db163e3f50
children 4eac4bf35b45
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@1739 646 if (!input) {
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@387 720 //!!! need a better way to handle this -- previously we were using a QMessageBox but that isn't an appropriate thing to do here either
Chris@1775 721 SVDEBUG << "FeatureExtractionModelTransformer::run: Failed to create FFT model for input model " << inputId << ": " << err << endl;
Chris@1080 722 throw AllocationFailed("Failed to create the FFT model for this feature extraction model transformer: error is: " + err);
Chris@320 723 }
Chris@320 724 fftModels.push_back(model);
Chris@320 725 }
Chris@1775 726 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1775 727 SVDEBUG << "FeatureExtractionModelTransformer::run: Created FFT model(s) for frequency-domain input" << endl;
Chris@1775 728 #endif
Chris@320 729 }
Chris@320 730
Chris@850 731 RealTime contextStartRT = primaryTransform.getStartTime();
Chris@850 732 RealTime contextDurationRT = primaryTransform.getDuration();
Chris@350 733
Chris@1040 734 sv_frame_t contextStart =
Chris@350 735 RealTime::realTime2Frame(contextStartRT, sampleRate);
Chris@350 736
Chris@1040 737 sv_frame_t contextDuration =
Chris@350 738 RealTime::realTime2Frame(contextDurationRT, sampleRate);
Chris@320 739
Chris@320 740 if (contextStart == 0 || contextStart < startFrame) {
Chris@320 741 contextStart = startFrame;
Chris@320 742 }
Chris@320 743
Chris@320 744 if (contextDuration == 0) {
Chris@320 745 contextDuration = endFrame - contextStart;
Chris@320 746 }
Chris@320 747 if (contextStart + contextDuration > endFrame) {
Chris@320 748 contextDuration = endFrame - contextStart;
Chris@320 749 }
Chris@320 750
Chris@1039 751 sv_frame_t blockFrame = contextStart;
Chris@320 752
Chris@320 753 long prevCompletion = 0;
Chris@320 754
Chris@1739 755 for (int j = 0; in_range_for(m_outputNos, j); ++j) {
Chris@850 756 setCompletion(j, 0);
Chris@850 757 }
Chris@320 758
Chris@1582 759 float *reals = nullptr;
Chris@1582 760 float *imaginaries = nullptr;
Chris@556 761 if (frequencyDomain) {
Chris@556 762 reals = new float[blockSize/2 + 1];
Chris@556 763 imaginaries = new float[blockSize/2 + 1];
Chris@556 764 }
Chris@556 765
Chris@678 766 QString error = "";
Chris@678 767
Chris@1372 768 try {
Chris@1372 769 while (!m_abandoned) {
Chris@320 770
Chris@1372 771 if (frequencyDomain) {
Chris@1372 772 if (blockFrame - int(blockSize)/2 >
Chris@1739 773 contextStart + contextDuration) {
Chris@1739 774 break;
Chris@1739 775 }
Chris@1372 776 } else {
Chris@1372 777 if (blockFrame >=
Chris@1739 778 contextStart + contextDuration) {
Chris@1739 779 break;
Chris@1739 780 }
Chris@1372 781 }
Chris@320 782
Chris@1558 783 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1558 784 SVDEBUG << "FeatureExtractionModelTransformer::run: blockFrame "
Chris@1558 785 << blockFrame << ", endFrame " << endFrame << ", blockSize "
Chris@1558 786 << blockSize << endl;
Chris@1558 787 #endif
Chris@1558 788
Chris@1372 789 int completion = int
Chris@1372 790 ((((blockFrame - contextStart) / stepSize) * 99) /
Chris@1372 791 (contextDuration / stepSize + 1));
Chris@320 792
Chris@1762 793 bool haveAllModels = true;
Chris@1755 794 if (!ModelById::get(inputId)) {
Chris@1762 795 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1762 796 SVDEBUG << "FeatureExtractionModelTransformer::run: Input model " << inputId << " no longer exists" << endl;
Chris@1762 797 #endif
Chris@1762 798 haveAllModels = false;
Chris@1762 799 } else {
Chris@1763 800 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1762 801 SVDEBUG << "Input model " << inputId << " still exists" << endl;
Chris@1763 802 #endif
Chris@1762 803 }
Chris@1762 804 for (auto mid: m_outputs) {
Chris@1762 805 if (!ModelById::get(mid)) {
Chris@1762 806 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1762 807 SVDEBUG << "FeatureExtractionModelTransformer::run: Output model " << mid << " no longer exists" << endl;
Chris@1762 808 #endif
Chris@1762 809 haveAllModels = false;
Chris@1762 810 } else {
Chris@1762 811 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1762 812 SVDEBUG << "Output model " << mid << " still exists" << endl;
Chris@1762 813 #endif
Chris@1762 814 }
Chris@1762 815 }
Chris@1762 816 if (!haveAllModels) {
Chris@1755 817 abandon();
Chris@1762 818 break;
Chris@1755 819 }
Chris@1762 820
Chris@1762 821 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1762 822 SVDEBUG << "FeatureExtractionModelTransformer::run: All models still exist" << endl;
Chris@1762 823 #endif
Chris@1755 824
Chris@1739 825 // channelCount is either input->channelCount or 1
Chris@320 826
Chris@1372 827 if (frequencyDomain) {
Chris@1372 828 for (int ch = 0; ch < channelCount; ++ch) {
Chris@1372 829 int column = int((blockFrame - startFrame) / stepSize);
Chris@1372 830 if (fftModels[ch]->getValuesAt(column, reals, imaginaries)) {
Chris@1372 831 for (int i = 0; i <= blockSize/2; ++i) {
Chris@1372 832 buffers[ch][i*2] = reals[i];
Chris@1372 833 buffers[ch][i*2+1] = imaginaries[i];
Chris@1372 834 }
Chris@1372 835 } else {
Chris@1372 836 for (int i = 0; i <= blockSize/2; ++i) {
Chris@1372 837 buffers[ch][i*2] = 0.f;
Chris@1372 838 buffers[ch][i*2+1] = 0.f;
Chris@1372 839 }
Chris@1558 840 }
Chris@1558 841
Chris@1372 842 error = fftModels[ch]->getError();
Chris@1372 843 if (error != "") {
Chris@1372 844 SVCERR << "FeatureExtractionModelTransformer::run: Abandoning, error is " << error << endl;
Chris@1372 845 m_abandoned = true;
Chris@1372 846 m_message = error;
Chris@1372 847 break;
Chris@1008 848 }
Chris@1372 849 }
Chris@1372 850 } else {
Chris@1372 851 getFrames(channelCount, blockFrame, blockSize, buffers);
Chris@1372 852 }
Chris@1372 853
Chris@1372 854 if (m_abandoned) break;
Chris@1372 855
Chris@1740 856 auto features = m_plugin->process
Chris@1558 857 (buffers,
Chris@1558 858 RealTime::frame2RealTime(blockFrame, sampleRate)
Chris@1558 859 .toVampRealTime());
Chris@1558 860
Chris@1372 861 if (m_abandoned) break;
Chris@1372 862
Chris@1740 863 for (int j = 0; in_range_for(m_outputNos, j); ++j) {
Chris@1740 864 for (int fi = 0; in_range_for(features[m_outputNos[j]], fi); ++fi) {
Chris@1740 865 auto feature = features[m_outputNos[j]][fi];
Chris@1372 866 addFeature(j, blockFrame, feature);
Chris@678 867 }
Chris@363 868 }
Chris@1372 869
Chris@1372 870 if (blockFrame == contextStart || completion > prevCompletion) {
Chris@1740 871 for (int j = 0; in_range_for(m_outputNos, j); ++j) {
Chris@1372 872 setCompletion(j, completion);
Chris@1372 873 }
Chris@1372 874 prevCompletion = completion;
Chris@1372 875 }
Chris@1372 876
Chris@1372 877 blockFrame += stepSize;
Chris@1372 878
Chris@320 879 }
Chris@320 880
Chris@1372 881 if (!m_abandoned) {
Chris@1740 882 auto features = m_plugin->getRemainingFeatures();
Chris@497 883
Chris@1740 884 for (int j = 0; in_range_for(m_outputNos, j); ++j) {
Chris@1740 885 for (int fi = 0; in_range_for(features[m_outputNos[j]], fi); ++fi) {
Chris@1740 886 auto feature = features[m_outputNos[j]][fi];
Chris@1372 887 addFeature(j, blockFrame, feature);
Chris@1762 888 if (m_abandoned) {
Chris@1762 889 break;
Chris@1762 890 }
Chris@1372 891 }
Chris@850 892 }
Chris@850 893 }
Chris@1372 894 } catch (const std::exception &e) {
Chris@1372 895 SVCERR << "FeatureExtractionModelTransformer::run: Exception caught: "
Chris@1372 896 << e.what() << endl;
Chris@1372 897 m_abandoned = true;
Chris@1372 898 m_message = e.what();
Chris@497 899 }
Chris@320 900
Chris@850 901 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@850 902 setCompletion(j, 100);
Chris@850 903 }
Chris@320 904
Chris@320 905 if (frequencyDomain) {
Chris@930 906 for (int ch = 0; ch < channelCount; ++ch) {
Chris@320 907 delete fftModels[ch];
Chris@320 908 }
Chris@556 909 delete[] reals;
Chris@556 910 delete[] imaginaries;
Chris@320 911 }
Chris@974 912
Chris@974 913 for (int ch = 0; ch < channelCount; ++ch) {
Chris@974 914 delete[] buffers[ch];
Chris@974 915 }
Chris@974 916 delete[] buffers;
Chris@1237 917
Chris@1237 918 deinitialise();
Chris@320 919 }
Chris@320 920
Chris@320 921 void
Chris@363 922 FeatureExtractionModelTransformer::getFrames(int channelCount,
Chris@1039 923 sv_frame_t startFrame,
Chris@1039 924 sv_frame_t size,
Chris@363 925 float **buffers)
Chris@320 926 {
Chris@1039 927 sv_frame_t offset = 0;
Chris@320 928
Chris@320 929 if (startFrame < 0) {
Chris@363 930 for (int c = 0; c < channelCount; ++c) {
Chris@1039 931 for (sv_frame_t i = 0; i < size && startFrame + i < 0; ++i) {
Chris@363 932 buffers[c][i] = 0.0f;
Chris@363 933 }
Chris@320 934 }
Chris@320 935 offset = -startFrame;
Chris@320 936 size -= offset;
Chris@320 937 if (size <= 0) return;
Chris@320 938 startFrame = 0;
Chris@320 939 }
Chris@320 940
Chris@1739 941 auto input = ModelById::getAs<DenseTimeValueModel>(getInputModel());
Chris@1739 942 if (!input) {
Chris@1739 943 return;
Chris@1739 944 }
Chris@363 945
Chris@1039 946 sv_frame_t got = 0;
Chris@350 947
Chris@363 948 if (channelCount == 1) {
Chris@363 949
Chris@1096 950 auto data = input->getData(m_input.getChannel(), startFrame, size);
Chris@1096 951 got = data.size();
Chris@1096 952
Chris@1096 953 copy(data.begin(), data.end(), buffers[0] + offset);
Chris@363 954
Chris@363 955 if (m_input.getChannel() == -1 && input->getChannelCount() > 1) {
Chris@363 956 // use mean instead of sum, as plugin input
Chris@363 957 float cc = float(input->getChannelCount());
Chris@1096 958 for (sv_frame_t i = 0; i < got; ++i) {
Chris@363 959 buffers[0][i + offset] /= cc;
Chris@363 960 }
Chris@363 961 }
Chris@363 962
Chris@363 963 } else {
Chris@363 964
Chris@1096 965 auto data = input->getMultiChannelData(0, channelCount-1, startFrame, size);
Chris@1096 966 if (!data.empty()) {
Chris@1096 967 got = data[0].size();
Chris@1096 968 for (int c = 0; in_range_for(data, c); ++c) {
Chris@1096 969 copy(data[c].begin(), data[c].end(), buffers[c] + offset);
Chris@363 970 }
Chris@363 971 }
Chris@363 972 }
Chris@320 973
Chris@320 974 while (got < size) {
Chris@363 975 for (int c = 0; c < channelCount; ++c) {
Chris@363 976 buffers[c][got + offset] = 0.0;
Chris@363 977 }
Chris@320 978 ++got;
Chris@320 979 }
Chris@320 980 }
Chris@320 981
Chris@320 982 void
Chris@850 983 FeatureExtractionModelTransformer::addFeature(int n,
Chris@1039 984 sv_frame_t blockFrame,
Chris@850 985 const Vamp::Plugin::Feature &feature)
Chris@320 986 {
Chris@1739 987 auto input = ModelById::get(getInputModel());
Chris@1739 988 if (!input) return;
Chris@1739 989
Chris@1739 990 sv_samplerate_t inputRate = input->getSampleRate();
Chris@320 991
Chris@843 992 // cerr << "FeatureExtractionModelTransformer::addFeature: blockFrame = "
Chris@712 993 // << blockFrame << ", hasTimestamp = " << feature.hasTimestamp
Chris@712 994 // << ", timestamp = " << feature.timestamp << ", hasDuration = "
Chris@712 995 // << feature.hasDuration << ", duration = " << feature.duration
Chris@843 996 // << endl;
Chris@320 997
Chris@1039 998 sv_frame_t frame = blockFrame;
Chris@320 999
Chris@1739 1000 if (m_descriptors[n].sampleType ==
Chris@1429 1001 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
Chris@320 1002
Chris@1429 1003 if (!feature.hasTimestamp) {
Chris@1429 1004 SVDEBUG
Chris@1429 1005 << "WARNING: FeatureExtractionModelTransformer::addFeature: "
Chris@1429 1006 << "Feature has variable sample rate but no timestamp!"
Chris@1429 1007 << endl;
Chris@1429 1008 return;
Chris@1429 1009 } else {
Chris@1429 1010 frame = RealTime::realTime2Frame(feature.timestamp, inputRate);
Chris@1429 1011 }
Chris@320 1012
Chris@1071 1013 // cerr << "variable sample rate: timestamp = " << feature.timestamp
Chris@1071 1014 // << " at input rate " << inputRate << " -> " << frame << endl;
Chris@1071 1015
Chris@1739 1016 } else if (m_descriptors[n].sampleType ==
Chris@1429 1017 Vamp::Plugin::OutputDescriptor::FixedSampleRate) {
Chris@320 1018
Chris@1739 1019 sv_samplerate_t rate = m_descriptors[n].sampleRate;
Chris@1071 1020 if (rate <= 0.0) {
Chris@1071 1021 rate = inputRate;
Chris@1071 1022 }
Chris@1071 1023
Chris@779 1024 if (!feature.hasTimestamp) {
Chris@849 1025 ++m_fixedRateFeatureNos[n];
Chris@779 1026 } else {
Chris@779 1027 RealTime ts(feature.timestamp.sec, feature.timestamp.nsec);
Chris@1071 1028 m_fixedRateFeatureNos[n] = (int)lrint(ts.toDouble() * rate);
Chris@779 1029 }
Chris@862 1030
Chris@1071 1031 // cerr << "m_fixedRateFeatureNo = " << m_fixedRateFeatureNos[n]
Chris@1739 1032 // << ", m_descriptor->sampleRate = " << m_descriptors[n].sampleRate
Chris@862 1033 // << ", inputRate = " << inputRate
Chris@862 1034 // << " giving frame = ";
Chris@1071 1035 frame = lrint((double(m_fixedRateFeatureNos[n]) / rate) * inputRate);
Chris@1071 1036 // cerr << frame << endl;
Chris@320 1037 }
Chris@862 1038
Chris@862 1039 if (frame < 0) {
Chris@1264 1040 SVDEBUG
Chris@862 1041 << "WARNING: FeatureExtractionModelTransformer::addFeature: "
Chris@862 1042 << "Negative frame counts are not supported (frame = " << frame
Chris@862 1043 << " from timestamp " << feature.timestamp
Chris@862 1044 << "), dropping feature"
Chris@862 1045 << endl;
Chris@862 1046 return;
Chris@862 1047 }
Chris@862 1048
Chris@441 1049 // Rather than repeat the complicated tests from the constructor
Chris@441 1050 // to determine what sort of model we must be adding the features
Chris@441 1051 // to, we instead test what sort of model the constructor decided
Chris@441 1052 // to create.
Chris@320 1053
Chris@1739 1054 ModelId outputId = m_outputs[n];
Chris@1740 1055
Chris@1740 1056 if (isOutputType<SparseOneDimensionalModel>(n)) {
Chris@1740 1057
Chris@1740 1058 auto model = ModelById::getAs<SparseOneDimensionalModel>(outputId);
Chris@1740 1059 if (!model) return;
Chris@1740 1060 model->add(Event(frame, feature.label.c_str()));
Chris@1740 1061
Chris@1740 1062 } else if (isOutputType<SparseTimeValueModel>(n)) {
Chris@1740 1063
Chris@1740 1064 auto model = ModelById::getAs<SparseTimeValueModel>(outputId);
Chris@1740 1065 if (!model) return;
Chris@1740 1066
Chris@1740 1067 for (int i = 0; in_range_for(feature.values, i); ++i) {
Chris@1740 1068
Chris@1740 1069 float value = feature.values[i];
Chris@1740 1070
Chris@1740 1071 QString label = feature.label.c_str();
Chris@1740 1072 if (feature.values.size() > 1) {
Chris@1740 1073 label = QString("[%1] %2").arg(i+1).arg(label);
Chris@1740 1074 }
Chris@1740 1075
Chris@1740 1076 auto targetModel = model;
Chris@1740 1077
Chris@1740 1078 if (m_needAdditionalModels[n] && i > 0) {
Chris@1740 1079 targetModel = ModelById::getAs<SparseTimeValueModel>
Chris@1740 1080 (getAdditionalModel(n, i));
Chris@1740 1081 if (!targetModel) targetModel = model;
Chris@1740 1082 }
Chris@1740 1083
Chris@1740 1084 targetModel->add(Event(frame, value, label));
Chris@1740 1085 }
Chris@1740 1086
Chris@1740 1087 } else if (isOutputType<NoteModel>(n) || isOutputType<RegionModel>(n)) {
Chris@1739 1088
Chris@1740 1089 int index = 0;
Chris@1740 1090
Chris@1740 1091 float value = 0.0;
Chris@1740 1092 if ((int)feature.values.size() > index) {
Chris@1740 1093 value = feature.values[index++];
Chris@1739 1094 }
Chris@441 1095
Chris@1740 1096 sv_frame_t duration = 1;
Chris@1740 1097 if (feature.hasDuration) {
Chris@1740 1098 duration = RealTime::realTime2Frame(feature.duration, inputRate);
Chris@1740 1099 } else {
Chris@1740 1100 if (in_range_for(feature.values, index)) {
Chris@1740 1101 duration = lrintf(feature.values[index++]);
Chris@1739 1102 }
Chris@1739 1103 }
Chris@1739 1104
Chris@1740 1105 auto noteModel = ModelById::getAs<NoteModel>(outputId);
Chris@1740 1106 if (noteModel) {
Chris@1739 1107
Chris@1740 1108 float velocity = 100;
Chris@1739 1109 if ((int)feature.values.size() > index) {
Chris@1740 1110 velocity = feature.values[index++];
Chris@454 1111 }
Chris@1740 1112 if (velocity < 0) velocity = 127;
Chris@1740 1113 if (velocity > 127) velocity = 127;
Chris@1740 1114
Chris@1740 1115 noteModel->add(Event(frame, value, // value is pitch
Chris@1740 1116 duration,
Chris@1740 1117 velocity / 127.f,
Chris@1740 1118 feature.label.c_str()));
Chris@1740 1119 }
Chris@454 1120
Chris@1740 1121 auto regionModel = ModelById::getAs<RegionModel>(outputId);
Chris@1740 1122 if (regionModel) {
Chris@1740 1123
Chris@1740 1124 if (feature.hasDuration && !feature.values.empty()) {
Chris@1740 1125
Chris@1740 1126 for (int i = 0; in_range_for(feature.values, i); ++i) {
Chris@1740 1127
Chris@1740 1128 float value = feature.values[i];
Chris@1740 1129
Chris@1740 1130 QString label = feature.label.c_str();
Chris@1740 1131 if (feature.values.size() > 1) {
Chris@1740 1132 label = QString("[%1] %2").arg(i+1).arg(label);
Chris@1739 1133 }
Chris@1740 1134
Chris@1739 1135 regionModel->add(Event(frame,
Chris@1739 1136 value,
Chris@1739 1137 duration,
Chris@1740 1138 label));
Chris@1739 1139 }
Chris@1740 1140 } else {
Chris@1740 1141
Chris@1740 1142 regionModel->add(Event(frame,
Chris@1740 1143 value,
Chris@1740 1144 duration,
Chris@1740 1145 feature.label.c_str()));
Chris@441 1146 }
Chris@441 1147 }
Chris@1740 1148
Chris@1777 1149 } else if (isOutputType<BasicCompressedDenseThreeDimensionalModel>(n)) {
Chris@1740 1150
Chris@1740 1151 auto model = ModelById::getAs
Chris@1777 1152 <BasicCompressedDenseThreeDimensionalModel>(outputId);
Chris@1740 1153 if (!model) return;
Chris@1740 1154
Chris@1740 1155 DenseThreeDimensionalModel::Column values = feature.values;
Chris@1740 1156
Chris@1740 1157 if (!feature.hasTimestamp && m_fixedRateFeatureNos[n] >= 0) {
Chris@1740 1158 model->setColumn(m_fixedRateFeatureNos[n], values);
Chris@1740 1159 } else {
Chris@1740 1160 model->setColumn(int(frame / model->getResolution()), values);
Chris@1740 1161 }
Chris@1762 1162 } else {
Chris@1762 1163
Chris@1762 1164 SVDEBUG << "FeatureExtractionModelTransformer::addFeature: Unknown output model type - possibly a deleted model" << endl;
Chris@1762 1165 abandon();
Chris@1739 1166 }
Chris@320 1167 }
Chris@320 1168
Chris@320 1169 void
Chris@850 1170 FeatureExtractionModelTransformer::setCompletion(int n, int completion)
Chris@320 1171 {
Chris@1558 1172 #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN
Chris@1558 1173 SVDEBUG << "FeatureExtractionModelTransformer::setCompletion("
Chris@1558 1174 << completion << ")" << endl;
Chris@1558 1175 #endif
Chris@320 1176
Chris@1740 1177 (void)
Chris@1740 1178 (setOutputCompletion<SparseOneDimensionalModel>(n, completion) ||
Chris@1740 1179 setOutputCompletion<SparseTimeValueModel>(n, completion) ||
Chris@1740 1180 setOutputCompletion<NoteModel>(n, completion) ||
Chris@1740 1181 setOutputCompletion<RegionModel>(n, completion) ||
Chris@1777 1182 setOutputCompletion<BasicCompressedDenseThreeDimensionalModel>(n, completion));
Chris@320 1183 }
Chris@320 1184