annotate transform/FeatureExtractionModelTransformer.cpp @ 985:f073d924a7c3

Fix #1058 clicking row in Layer Edit dialog when colour 3d plot layer active jumps to wrong frame (was using sample rate where resolution intended)
author Chris Cannam
date Tue, 16 Sep 2014 10:29:19 +0100
parents 65494d0d9ded
children d9e0e59a1581
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@320 19 #include "plugin/PluginXml.h"
Chris@475 20 #include <vamp-hostsdk/Plugin.h>
Chris@320 21
Chris@320 22 #include "data/model/Model.h"
Chris@320 23 #include "base/Window.h"
Chris@387 24 #include "base/Exceptions.h"
Chris@320 25 #include "data/model/SparseOneDimensionalModel.h"
Chris@320 26 #include "data/model/SparseTimeValueModel.h"
Chris@320 27 #include "data/model/EditableDenseThreeDimensionalModel.h"
Chris@320 28 #include "data/model/DenseTimeValueModel.h"
Chris@320 29 #include "data/model/NoteModel.h"
gyorgyf@786 30 #include "data/model/FlexiNoteModel.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@350 42 FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Input in,
Chris@859 43 const Transform &transform) :
Chris@350 44 ModelTransformer(in, transform),
Chris@859 45 m_plugin(0)
Chris@320 46 {
Chris@690 47 // SVDEBUG << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << pluginId << ", outputName " << m_transform.getOutput() << endl;
Chris@350 48
Chris@849 49 initialise();
Chris@849 50 }
Chris@849 51
Chris@849 52 FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Input in,
Chris@859 53 const Transforms &transforms) :
Chris@849 54 ModelTransformer(in, transforms),
Chris@859 55 m_plugin(0)
Chris@849 56 {
Chris@849 57 // SVDEBUG << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << pluginId << ", outputName " << m_transform.getOutput() << endl;
Chris@849 58
Chris@849 59 initialise();
Chris@849 60 }
Chris@849 61
Chris@849 62 static bool
Chris@849 63 areTransformsSimilar(const Transform &t1, const Transform &t2)
Chris@849 64 {
Chris@849 65 Transform t2o(t2);
Chris@849 66 t2o.setOutput(t1.getOutput());
Chris@849 67 return t1 == t2o;
Chris@849 68 }
Chris@849 69
Chris@849 70 bool
Chris@849 71 FeatureExtractionModelTransformer::initialise()
Chris@849 72 {
Chris@849 73 // All transforms must use the same plugin, parameters, and
Chris@849 74 // inputs: they can differ only in choice of plugin output. So we
Chris@849 75 // initialise based purely on the first transform in the list (but
Chris@849 76 // first check that they are actually similar as promised)
Chris@849 77
Chris@849 78 for (int j = 1; j < (int)m_transforms.size(); ++j) {
Chris@849 79 if (!areTransformsSimilar(m_transforms[0], m_transforms[j])) {
Chris@849 80 m_message = tr("Transforms supplied to a single FeatureExtractionModelTransformer instance must be similar in every respect except plugin output");
Chris@849 81 return false;
Chris@849 82 }
Chris@849 83 }
Chris@849 84
Chris@849 85 Transform primaryTransform = m_transforms[0];
Chris@849 86
Chris@849 87 QString pluginId = primaryTransform.getPluginIdentifier();
Chris@320 88
Chris@320 89 FeatureExtractionPluginFactory *factory =
Chris@320 90 FeatureExtractionPluginFactory::instanceFor(pluginId);
Chris@320 91
Chris@320 92 if (!factory) {
Chris@361 93 m_message = tr("No factory available for feature extraction plugin id \"%1\" (unknown plugin type, or internal error?)").arg(pluginId);
Chris@849 94 return false;
Chris@320 95 }
Chris@320 96
Chris@350 97 DenseTimeValueModel *input = getConformingInput();
Chris@350 98 if (!input) {
Chris@361 99 m_message = tr("Input model for feature extraction plugin \"%1\" is of wrong type (internal error?)").arg(pluginId);
Chris@849 100 return false;
Chris@350 101 }
Chris@320 102
Chris@350 103 m_plugin = factory->instantiatePlugin(pluginId, input->getSampleRate());
Chris@320 104 if (!m_plugin) {
Chris@361 105 m_message = tr("Failed to instantiate plugin \"%1\"").arg(pluginId);
Chris@849 106 return false;
Chris@320 107 }
Chris@320 108
Chris@350 109 TransformFactory::getInstance()->makeContextConsistentWithPlugin
Chris@849 110 (primaryTransform, m_plugin);
Chris@343 111
Chris@350 112 TransformFactory::getInstance()->setPluginParameters
Chris@849 113 (primaryTransform, m_plugin);
Chris@320 114
Chris@930 115 int channelCount = input->getChannelCount();
Chris@930 116 if ((int)m_plugin->getMaxChannelCount() < channelCount) {
Chris@320 117 channelCount = 1;
Chris@320 118 }
Chris@930 119 if ((int)m_plugin->getMinChannelCount() > channelCount) {
Chris@361 120 m_message = tr("Cannot provide enough channels to feature extraction plugin \"%1\" (plugin min is %2, max %3; input model has %4)")
Chris@361 121 .arg(pluginId)
Chris@361 122 .arg(m_plugin->getMinChannelCount())
Chris@361 123 .arg(m_plugin->getMaxChannelCount())
Chris@361 124 .arg(input->getChannelCount());
Chris@849 125 return false;
Chris@320 126 }
Chris@320 127
Chris@690 128 SVDEBUG << "Initialising feature extraction plugin with channels = "
Chris@849 129 << channelCount << ", step = " << primaryTransform.getStepSize()
Chris@849 130 << ", block = " << primaryTransform.getBlockSize() << endl;
Chris@320 131
Chris@320 132 if (!m_plugin->initialise(channelCount,
Chris@849 133 primaryTransform.getStepSize(),
Chris@849 134 primaryTransform.getBlockSize())) {
Chris@361 135
Chris@930 136 int pstep = primaryTransform.getStepSize();
Chris@930 137 int pblock = primaryTransform.getBlockSize();
Chris@361 138
Chris@850 139 ///!!! hang on, this isn't right -- we're modifying a copy
Chris@849 140 primaryTransform.setStepSize(0);
Chris@849 141 primaryTransform.setBlockSize(0);
Chris@361 142 TransformFactory::getInstance()->makeContextConsistentWithPlugin
Chris@849 143 (primaryTransform, m_plugin);
Chris@361 144
Chris@849 145 if (primaryTransform.getStepSize() != pstep ||
Chris@849 146 primaryTransform.getBlockSize() != pblock) {
Chris@361 147
Chris@361 148 if (!m_plugin->initialise(channelCount,
Chris@849 149 primaryTransform.getStepSize(),
Chris@849 150 primaryTransform.getBlockSize())) {
Chris@361 151
Chris@361 152 m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId);
Chris@849 153 return false;
Chris@361 154
Chris@361 155 } else {
Chris@361 156
Chris@361 157 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 158 .arg(pluginId)
Chris@361 159 .arg(pstep)
Chris@361 160 .arg(pblock)
Chris@849 161 .arg(primaryTransform.getStepSize())
Chris@849 162 .arg(primaryTransform.getBlockSize());
Chris@361 163 }
Chris@361 164
Chris@361 165 } else {
Chris@361 166
Chris@361 167 m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId);
Chris@849 168 return false;
Chris@361 169 }
Chris@320 170 }
Chris@320 171
Chris@849 172 if (primaryTransform.getPluginVersion() != "") {
Chris@366 173 QString pv = QString("%1").arg(m_plugin->getPluginVersion());
Chris@849 174 if (pv != primaryTransform.getPluginVersion()) {
Chris@366 175 QString vm = tr("Transform was configured for version %1 of plugin \"%2\", but the plugin being used is version %3")
Chris@849 176 .arg(primaryTransform.getPluginVersion())
Chris@366 177 .arg(pluginId)
Chris@366 178 .arg(pv);
Chris@366 179 if (m_message != "") {
Chris@366 180 m_message = QString("%1; %2").arg(vm).arg(m_message);
Chris@366 181 } else {
Chris@366 182 m_message = vm;
Chris@366 183 }
Chris@366 184 }
Chris@366 185 }
Chris@366 186
Chris@320 187 Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors();
Chris@320 188
Chris@320 189 if (outputs.empty()) {
Chris@361 190 m_message = tr("Plugin \"%1\" has no outputs").arg(pluginId);
Chris@849 191 return false;
Chris@320 192 }
Chris@320 193
Chris@849 194 for (int j = 0; j < (int)m_transforms.size(); ++j) {
Chris@849 195
Chris@849 196 for (int i = 0; i < (int)outputs.size(); ++i) {
Chris@849 197 // SVDEBUG << "comparing output " << i << " name \"" << outputs[i].identifier << "\" with expected \"" << m_transform.getOutput() << "\"" << endl;
Chris@849 198 if (m_transforms[j].getOutput() == "" ||
Chris@849 199 outputs[i].identifier == m_transforms[j].getOutput().toStdString()) {
Chris@849 200 m_outputNos.push_back(i);
Chris@849 201 m_descriptors.push_back(new Vamp::Plugin::OutputDescriptor(outputs[i]));
Chris@849 202 m_fixedRateFeatureNos.push_back(-1); // we increment before use
Chris@849 203 break;
Chris@849 204 }
Chris@849 205 }
Chris@849 206
Chris@930 207 if ((int)m_descriptors.size() <= j) {
Chris@849 208 m_message = tr("Plugin \"%1\" has no output named \"%2\"")
Chris@849 209 .arg(pluginId)
Chris@849 210 .arg(m_transforms[j].getOutput());
Chris@849 211 return false;
Chris@849 212 }
Chris@320 213 }
Chris@320 214
Chris@849 215 for (int j = 0; j < (int)m_transforms.size(); ++j) {
Chris@876 216 createOutputModels(j);
Chris@849 217 }
Chris@849 218
Chris@849 219 return true;
Chris@558 220 }
Chris@558 221
Chris@558 222 void
Chris@876 223 FeatureExtractionModelTransformer::createOutputModels(int n)
Chris@558 224 {
Chris@558 225 DenseTimeValueModel *input = getConformingInput();
Chris@558 226
Chris@843 227 // cerr << "FeatureExtractionModelTransformer::createOutputModel: sample type " << m_descriptor->sampleType << ", rate " << m_descriptor->sampleRate << endl;
Chris@712 228
Chris@849 229 PluginRDFDescription description(m_transforms[n].getPluginIdentifier());
Chris@849 230 QString outputId = m_transforms[n].getOutput();
Chris@558 231
Chris@320 232 int binCount = 1;
Chris@320 233 float minValue = 0.0, maxValue = 0.0;
Chris@320 234 bool haveExtents = false;
Chris@876 235 bool haveBinCount = m_descriptors[n]->hasFixedBinCount;
Chris@876 236
Chris@876 237 if (haveBinCount) {
Chris@849 238 binCount = m_descriptors[n]->binCount;
Chris@320 239 }
Chris@320 240
Chris@876 241 m_needAdditionalModels[n] = false;
Chris@876 242
Chris@843 243 // cerr << "FeatureExtractionModelTransformer: output bin count "
Chris@843 244 // << binCount << endl;
Chris@320 245
Chris@849 246 if (binCount > 0 && m_descriptors[n]->hasKnownExtents) {
Chris@849 247 minValue = m_descriptors[n]->minValue;
Chris@849 248 maxValue = m_descriptors[n]->maxValue;
Chris@320 249 haveExtents = true;
Chris@320 250 }
Chris@320 251
Chris@930 252 int modelRate = input->getSampleRate();
Chris@930 253 int modelResolution = 1;
Chris@712 254
Chris@849 255 if (m_descriptors[n]->sampleType !=
Chris@785 256 Vamp::Plugin::OutputDescriptor::OneSamplePerStep) {
Chris@849 257 if (m_descriptors[n]->sampleRate > input->getSampleRate()) {
Chris@843 258 cerr << "WARNING: plugin reports output sample rate as "
Chris@849 259 << m_descriptors[n]->sampleRate << " (can't display features with finer resolution than the input rate of " << input->getSampleRate() << ")" << endl;
Chris@785 260 }
Chris@785 261 }
Chris@785 262
Chris@849 263 switch (m_descriptors[n]->sampleType) {
Chris@320 264
Chris@320 265 case Vamp::Plugin::OutputDescriptor::VariableSampleRate:
Chris@849 266 if (m_descriptors[n]->sampleRate != 0.0) {
Chris@930 267 modelResolution = int(modelRate / m_descriptors[n]->sampleRate + 0.001);
Chris@320 268 }
Chris@320 269 break;
Chris@320 270
Chris@320 271 case Vamp::Plugin::OutputDescriptor::OneSamplePerStep:
Chris@849 272 modelResolution = m_transforms[n].getStepSize();
Chris@320 273 break;
Chris@320 274
Chris@320 275 case Vamp::Plugin::OutputDescriptor::FixedSampleRate:
Chris@451 276 //!!! SV doesn't actually support display of models that have
Chris@451 277 //!!! different underlying rates together -- so we always set
Chris@451 278 //!!! the model rate to be the input model's rate, and adjust
Chris@451 279 //!!! the resolution appropriately. We can't properly display
Chris@451 280 //!!! data with a higher resolution than the base model at all
Chris@849 281 if (m_descriptors[n]->sampleRate > input->getSampleRate()) {
Chris@451 282 modelResolution = 1;
Chris@451 283 } else {
Chris@930 284 modelResolution = int(round(input->getSampleRate() /
Chris@891 285 m_descriptors[n]->sampleRate));
Chris@451 286 }
Chris@320 287 break;
Chris@320 288 }
Chris@320 289
Chris@441 290 bool preDurationPlugin = (m_plugin->getVampApiVersion() < 2);
Chris@441 291
Chris@849 292 Model *out = 0;
Chris@849 293
Chris@441 294 if (binCount == 0 &&
Chris@849 295 (preDurationPlugin || !m_descriptors[n]->hasDuration)) {
Chris@320 296
Chris@445 297 // Anything with no value and no duration is an instant
Chris@445 298
Chris@849 299 out = new SparseOneDimensionalModel(modelRate, modelResolution, false);
Chris@558 300 QString outputEventTypeURI = description.getOutputEventTypeURI(outputId);
Chris@849 301 out->setRDFTypeURI(outputEventTypeURI);
Chris@558 302
Chris@441 303 } else if ((preDurationPlugin && binCount > 1 &&
Chris@849 304 (m_descriptors[n]->sampleType ==
Chris@441 305 Vamp::Plugin::OutputDescriptor::VariableSampleRate)) ||
Chris@849 306 (!preDurationPlugin && m_descriptors[n]->hasDuration)) {
Chris@441 307
Chris@441 308 // For plugins using the old v1 API without explicit duration,
Chris@441 309 // we treat anything that has multiple bins (i.e. that has the
Chris@441 310 // potential to have value and duration) and a variable sample
Chris@441 311 // rate as a note model, taking its values as pitch, duration
Chris@441 312 // and velocity (if present) respectively. This is the same
Chris@441 313 // behaviour as always applied by SV to these plugins in the
Chris@441 314 // past.
Chris@441 315
Chris@441 316 // For plugins with the newer API, we treat anything with
Chris@441 317 // duration as either a note model with pitch and velocity, or
Chris@441 318 // a region model.
Chris@441 319
Chris@441 320 // How do we know whether it's an interval or note model?
Chris@441 321 // What's the essential difference? Is a note model any
Chris@441 322 // interval model using a Hz or "MIDI pitch" scale? There
Chris@441 323 // isn't really a reliable test for "MIDI pitch"... Does a
Chris@441 324 // note model always have velocity? This is a good question
Chris@441 325 // to be addressed by accompanying RDF, but for the moment we
Chris@441 326 // will do the following...
Chris@441 327
Chris@441 328 bool isNoteModel = false;
Chris@441 329
Chris@441 330 // Regions have only value (and duration -- we can't extract a
Chris@441 331 // region model from an old-style plugin that doesn't support
Chris@441 332 // duration)
Chris@441 333 if (binCount > 1) isNoteModel = true;
Chris@441 334
Chris@595 335 // Regions do not have units of Hz or MIDI things (a sweeping
Chris@595 336 // assumption!)
Chris@849 337 if (m_descriptors[n]->unit == "Hz" ||
Chris@849 338 m_descriptors[n]->unit.find("MIDI") != std::string::npos ||
Chris@849 339 m_descriptors[n]->unit.find("midi") != std::string::npos) {
Chris@595 340 isNoteModel = true;
Chris@595 341 }
Chris@441 342
Chris@441 343 // If we had a "sparse 3D model", we would have the additional
Chris@441 344 // problem of determining whether to use that here (if bin
Chris@441 345 // count > 1). But we don't.
Chris@441 346
Chris@859 347 QSettings settings;
Chris@859 348 settings.beginGroup("Transformer");
Chris@859 349 bool flexi = settings.value("use-flexi-note-model", false).toBool();
Chris@859 350 settings.endGroup();
Chris@859 351
Chris@859 352 cerr << "flexi = " << flexi << endl;
Chris@859 353
Chris@859 354 if (isNoteModel && !flexi) {
Chris@441 355
Chris@441 356 NoteModel *model;
Chris@441 357 if (haveExtents) {
Chris@859 358 model = new NoteModel
Chris@859 359 (modelRate, modelResolution, minValue, maxValue, false);
Chris@441 360 } else {
Chris@859 361 model = new NoteModel
Chris@859 362 (modelRate, modelResolution, false);
gyorgyf@786 363 }
Chris@849 364 model->setScaleUnits(m_descriptors[n]->unit.c_str());
Chris@849 365 out = model;
gyorgyf@786 366
Chris@859 367 } else if (isNoteModel && flexi) {
gyorgyf@786 368
gyorgyf@786 369 FlexiNoteModel *model;
gyorgyf@786 370 if (haveExtents) {
Chris@859 371 model = new FlexiNoteModel
Chris@859 372 (modelRate, modelResolution, minValue, maxValue, false);
gyorgyf@786 373 } else {
Chris@859 374 model = new FlexiNoteModel
Chris@859 375 (modelRate, modelResolution, false);
Chris@441 376 }
Chris@849 377 model->setScaleUnits(m_descriptors[n]->unit.c_str());
Chris@849 378 out = model;
Chris@441 379
Chris@441 380 } else {
Chris@441 381
Chris@441 382 RegionModel *model;
Chris@441 383 if (haveExtents) {
Chris@441 384 model = new RegionModel
Chris@441 385 (modelRate, modelResolution, minValue, maxValue, false);
Chris@441 386 } else {
Chris@441 387 model = new RegionModel
Chris@441 388 (modelRate, modelResolution, false);
Chris@441 389 }
Chris@849 390 model->setScaleUnits(m_descriptors[n]->unit.c_str());
Chris@849 391 out = model;
Chris@441 392 }
Chris@441 393
Chris@558 394 QString outputEventTypeURI = description.getOutputEventTypeURI(outputId);
Chris@849 395 out->setRDFTypeURI(outputEventTypeURI);
Chris@558 396
Chris@876 397 } else if (binCount == 1 ||
Chris@849 398 (m_descriptors[n]->sampleType ==
Chris@441 399 Vamp::Plugin::OutputDescriptor::VariableSampleRate)) {
Chris@441 400
Chris@441 401 // Anything that is not a 1D, note, or interval model and that
Chris@441 402 // has only one value per result must be a sparse time value
Chris@441 403 // model.
Chris@441 404
Chris@441 405 // Anything that is not a 1D, note, or interval model and that
Chris@876 406 // has a variable sample rate is treated as a set of sparse
Chris@876 407 // time value models, one per output bin, because we lack a
Chris@441 408 // sparse 3D model.
Chris@320 409
Chris@876 410 // Anything that is not a 1D, note, or interval model and that
Chris@876 411 // has a fixed sample rate but an unknown number of values per
Chris@876 412 // result is also treated as a set of sparse time value models.
Chris@876 413
Chris@876 414 // For sets of sparse time value models, we create a single
Chris@876 415 // model first as the "standard" output and then create models
Chris@876 416 // for bins 1+ in the additional model map (mapping the output
Chris@876 417 // descriptor to a list of models indexed by bin-1). But we
Chris@876 418 // don't create the additional models yet, as this case has to
Chris@876 419 // work even if the number of bins is unknown at this point --
Chris@877 420 // we create an additional model (copying its parameters from
Chris@877 421 // the default one) each time a new bin is encountered.
Chris@876 422
Chris@876 423 if (!haveBinCount || binCount > 1) {
Chris@876 424 m_needAdditionalModels[n] = true;
Chris@876 425 }
Chris@876 426
Chris@320 427 SparseTimeValueModel *model;
Chris@320 428 if (haveExtents) {
Chris@320 429 model = new SparseTimeValueModel
Chris@320 430 (modelRate, modelResolution, minValue, maxValue, false);
Chris@320 431 } else {
Chris@320 432 model = new SparseTimeValueModel
Chris@320 433 (modelRate, modelResolution, false);
Chris@320 434 }
Chris@558 435
Chris@558 436 Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors();
Chris@849 437 model->setScaleUnits(outputs[m_outputNos[n]].unit.c_str());
Chris@320 438
Chris@849 439 out = model;
Chris@320 440
Chris@558 441 QString outputEventTypeURI = description.getOutputEventTypeURI(outputId);
Chris@849 442 out->setRDFTypeURI(outputEventTypeURI);
Chris@558 443
Chris@441 444 } else {
Chris@320 445
Chris@441 446 // Anything that is not a 1D, note, or interval model and that
Chris@441 447 // has a fixed sample rate and more than one value per result
Chris@441 448 // must be a dense 3D model.
Chris@320 449
Chris@320 450 EditableDenseThreeDimensionalModel *model =
Chris@320 451 new EditableDenseThreeDimensionalModel
Chris@535 452 (modelRate, modelResolution, binCount,
Chris@535 453 EditableDenseThreeDimensionalModel::BasicMultirateCompression,
Chris@535 454 false);
Chris@320 455
Chris@849 456 if (!m_descriptors[n]->binNames.empty()) {
Chris@320 457 std::vector<QString> names;
Chris@930 458 for (int i = 0; i < (int)m_descriptors[n]->binNames.size(); ++i) {
Chris@849 459 names.push_back(m_descriptors[n]->binNames[i].c_str());
Chris@320 460 }
Chris@320 461 model->setBinNames(names);
Chris@320 462 }
Chris@320 463
Chris@849 464 out = model;
Chris@558 465
Chris@558 466 QString outputSignalTypeURI = description.getOutputSignalTypeURI(outputId);
Chris@849 467 out->setRDFTypeURI(outputSignalTypeURI);
Chris@320 468 }
Chris@333 469
Chris@849 470 if (out) {
Chris@849 471 out->setSourceModel(input);
Chris@849 472 m_outputs.push_back(out);
Chris@849 473 }
Chris@320 474 }
Chris@320 475
Chris@331 476 FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()
Chris@320 477 {
Chris@690 478 // SVDEBUG << "FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()" << endl;
Chris@320 479 delete m_plugin;
Chris@930 480 for (int j = 0; j < (int)m_descriptors.size(); ++j) {
Chris@850 481 delete m_descriptors[j];
Chris@850 482 }
Chris@320 483 }
Chris@320 484
Chris@876 485 FeatureExtractionModelTransformer::Models
Chris@876 486 FeatureExtractionModelTransformer::getAdditionalOutputModels()
Chris@876 487 {
Chris@876 488 Models mm;
Chris@876 489 for (AdditionalModelMap::iterator i = m_additionalModels.begin();
Chris@876 490 i != m_additionalModels.end(); ++i) {
Chris@876 491 for (std::map<int, SparseTimeValueModel *>::iterator j =
Chris@876 492 i->second.begin();
Chris@876 493 j != i->second.end(); ++j) {
Chris@876 494 SparseTimeValueModel *m = j->second;
Chris@876 495 if (m) mm.push_back(m);
Chris@876 496 }
Chris@876 497 }
Chris@876 498 return mm;
Chris@876 499 }
Chris@876 500
Chris@877 501 bool
Chris@877 502 FeatureExtractionModelTransformer::willHaveAdditionalOutputModels()
Chris@877 503 {
Chris@877 504 for (std::map<int, bool>::const_iterator i =
Chris@877 505 m_needAdditionalModels.begin();
Chris@877 506 i != m_needAdditionalModels.end(); ++i) {
Chris@877 507 if (i->second) return true;
Chris@877 508 }
Chris@877 509 return false;
Chris@877 510 }
Chris@877 511
Chris@876 512 SparseTimeValueModel *
Chris@876 513 FeatureExtractionModelTransformer::getAdditionalModel(int n, int binNo)
Chris@876 514 {
Chris@893 515 // std::cerr << "getAdditionalModel(" << n << ", " << binNo << ")" << std::endl;
Chris@876 516
Chris@876 517 if (binNo == 0) {
Chris@876 518 std::cerr << "Internal error: binNo == 0 in getAdditionalModel (should be using primary model)" << std::endl;
Chris@876 519 return 0;
Chris@876 520 }
Chris@876 521
Chris@876 522 if (!m_needAdditionalModels[n]) return 0;
Chris@876 523 if (!isOutput<SparseTimeValueModel>(n)) return 0;
Chris@876 524 if (m_additionalModels[n][binNo]) return m_additionalModels[n][binNo];
Chris@876 525
Chris@876 526 std::cerr << "getAdditionalModel(" << n << ", " << binNo << "): creating" << std::endl;
Chris@876 527
Chris@876 528 SparseTimeValueModel *baseModel = getConformingOutput<SparseTimeValueModel>(n);
Chris@876 529 if (!baseModel) return 0;
Chris@876 530
Chris@876 531 std::cerr << "getAdditionalModel(" << n << ", " << binNo << "): (from " << baseModel << ")" << std::endl;
Chris@876 532
Chris@876 533 SparseTimeValueModel *additional =
Chris@876 534 new SparseTimeValueModel(baseModel->getSampleRate(),
Chris@876 535 baseModel->getResolution(),
Chris@876 536 baseModel->getValueMinimum(),
Chris@876 537 baseModel->getValueMaximum(),
Chris@876 538 false);
Chris@876 539
Chris@876 540 additional->setScaleUnits(baseModel->getScaleUnits());
Chris@876 541 additional->setRDFTypeURI(baseModel->getRDFTypeURI());
Chris@876 542
Chris@876 543 m_additionalModels[n][binNo] = additional;
Chris@876 544 return additional;
Chris@876 545 }
Chris@876 546
Chris@320 547 DenseTimeValueModel *
Chris@350 548 FeatureExtractionModelTransformer::getConformingInput()
Chris@320 549 {
Chris@690 550 // SVDEBUG << "FeatureExtractionModelTransformer::getConformingInput: input model is " << getInputModel() << endl;
Chris@408 551
Chris@320 552 DenseTimeValueModel *dtvm =
Chris@320 553 dynamic_cast<DenseTimeValueModel *>(getInputModel());
Chris@320 554 if (!dtvm) {
Chris@690 555 SVDEBUG << "FeatureExtractionModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << endl;
Chris@320 556 }
Chris@320 557 return dtvm;
Chris@320 558 }
Chris@320 559
Chris@320 560 void
Chris@331 561 FeatureExtractionModelTransformer::run()
Chris@320 562 {
Chris@350 563 DenseTimeValueModel *input = getConformingInput();
Chris@320 564 if (!input) return;
Chris@320 565
Chris@849 566 if (m_outputs.empty()) return;
Chris@320 567
Chris@850 568 Transform primaryTransform = m_transforms[0];
Chris@850 569
Chris@497 570 while (!input->isReady() && !m_abandoned) {
Chris@877 571 cerr << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << endl;
Chris@497 572 usleep(500000);
Chris@320 573 }
Chris@497 574 if (m_abandoned) return;
Chris@320 575
Chris@930 576 int sampleRate = input->getSampleRate();
Chris@320 577
Chris@930 578 int channelCount = input->getChannelCount();
Chris@930 579 if ((int)m_plugin->getMaxChannelCount() < channelCount) {
Chris@320 580 channelCount = 1;
Chris@320 581 }
Chris@320 582
Chris@320 583 float **buffers = new float*[channelCount];
Chris@930 584 for (int ch = 0; ch < channelCount; ++ch) {
Chris@850 585 buffers[ch] = new float[primaryTransform.getBlockSize() + 2];
Chris@320 586 }
Chris@320 587
Chris@930 588 int stepSize = primaryTransform.getStepSize();
Chris@930 589 int blockSize = primaryTransform.getBlockSize();
Chris@350 590
Chris@320 591 bool frequencyDomain = (m_plugin->getInputDomain() ==
Chris@320 592 Vamp::Plugin::FrequencyDomain);
Chris@320 593 std::vector<FFTModel *> fftModels;
Chris@320 594
Chris@320 595 if (frequencyDomain) {
Chris@930 596 for (int ch = 0; ch < channelCount; ++ch) {
Chris@320 597 FFTModel *model = new FFTModel
Chris@350 598 (getConformingInput(),
Chris@350 599 channelCount == 1 ? m_input.getChannel() : ch,
Chris@850 600 primaryTransform.getWindowType(),
Chris@350 601 blockSize,
Chris@350 602 stepSize,
Chris@350 603 blockSize,
Chris@334 604 false,
Chris@334 605 StorageAdviser::PrecisionCritical);
Chris@320 606 if (!model->isOK()) {
Chris@320 607 delete model;
Chris@850 608 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@850 609 setCompletion(j, 100);
Chris@850 610 }
Chris@387 611 //!!! 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@387 612 throw AllocationFailed("Failed to create the FFT model for this feature extraction model transformer");
Chris@320 613 }
Chris@320 614 model->resume();
Chris@320 615 fftModels.push_back(model);
Chris@320 616 }
Chris@320 617 }
Chris@320 618
Chris@350 619 long startFrame = m_input.getModel()->getStartFrame();
Chris@350 620 long endFrame = m_input.getModel()->getEndFrame();
Chris@320 621
Chris@850 622 RealTime contextStartRT = primaryTransform.getStartTime();
Chris@850 623 RealTime contextDurationRT = primaryTransform.getDuration();
Chris@350 624
Chris@350 625 long contextStart =
Chris@350 626 RealTime::realTime2Frame(contextStartRT, sampleRate);
Chris@350 627
Chris@350 628 long contextDuration =
Chris@350 629 RealTime::realTime2Frame(contextDurationRT, sampleRate);
Chris@320 630
Chris@320 631 if (contextStart == 0 || contextStart < startFrame) {
Chris@320 632 contextStart = startFrame;
Chris@320 633 }
Chris@320 634
Chris@320 635 if (contextDuration == 0) {
Chris@320 636 contextDuration = endFrame - contextStart;
Chris@320 637 }
Chris@320 638 if (contextStart + contextDuration > endFrame) {
Chris@320 639 contextDuration = endFrame - contextStart;
Chris@320 640 }
Chris@320 641
Chris@320 642 long blockFrame = contextStart;
Chris@320 643
Chris@320 644 long prevCompletion = 0;
Chris@320 645
Chris@850 646 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@850 647 setCompletion(j, 0);
Chris@850 648 }
Chris@320 649
Chris@556 650 float *reals = 0;
Chris@556 651 float *imaginaries = 0;
Chris@556 652 if (frequencyDomain) {
Chris@556 653 reals = new float[blockSize/2 + 1];
Chris@556 654 imaginaries = new float[blockSize/2 + 1];
Chris@556 655 }
Chris@556 656
Chris@678 657 QString error = "";
Chris@678 658
Chris@320 659 while (!m_abandoned) {
Chris@320 660
Chris@320 661 if (frequencyDomain) {
Chris@350 662 if (blockFrame - int(blockSize)/2 >
Chris@320 663 contextStart + contextDuration) break;
Chris@320 664 } else {
Chris@320 665 if (blockFrame >=
Chris@320 666 contextStart + contextDuration) break;
Chris@320 667 }
Chris@320 668
Chris@690 669 // SVDEBUG << "FeatureExtractionModelTransformer::run: blockFrame "
Chris@320 670 // << blockFrame << ", endFrame " << endFrame << ", blockSize "
Chris@687 671 // << blockSize << endl;
Chris@320 672
Chris@320 673 long completion =
Chris@350 674 (((blockFrame - contextStart) / stepSize) * 99) /
Chris@557 675 (contextDuration / stepSize + 1);
Chris@320 676
Chris@350 677 // channelCount is either m_input.getModel()->channelCount or 1
Chris@320 678
Chris@363 679 if (frequencyDomain) {
Chris@930 680 for (int ch = 0; ch < channelCount; ++ch) {
Chris@350 681 int column = (blockFrame - startFrame) / stepSize;
Chris@556 682 fftModels[ch]->getValuesAt(column, reals, imaginaries);
Chris@930 683 for (int i = 0; i <= blockSize/2; ++i) {
Chris@556 684 buffers[ch][i*2] = reals[i];
Chris@556 685 buffers[ch][i*2+1] = imaginaries[i];
Chris@320 686 }
Chris@678 687 error = fftModels[ch]->getError();
Chris@678 688 if (error != "") {
Chris@843 689 cerr << "FeatureExtractionModelTransformer::run: Abandoning, error is " << error << endl;
Chris@678 690 m_abandoned = true;
Chris@678 691 m_message = error;
Chris@678 692 }
Chris@363 693 }
Chris@363 694 } else {
Chris@363 695 getFrames(channelCount, blockFrame, blockSize, buffers);
Chris@320 696 }
Chris@320 697
Chris@497 698 if (m_abandoned) break;
Chris@497 699
Chris@320 700 Vamp::Plugin::FeatureSet features = m_plugin->process
Chris@320 701 (buffers, Vamp::RealTime::frame2RealTime(blockFrame, sampleRate));
Chris@320 702
Chris@497 703 if (m_abandoned) break;
Chris@497 704
Chris@850 705 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@930 706 for (int fi = 0; fi < (int)features[m_outputNos[j]].size(); ++fi) {
Chris@850 707 Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi];
Chris@850 708 addFeature(j, blockFrame, feature);
Chris@850 709 }
Chris@850 710 }
Chris@320 711
Chris@320 712 if (blockFrame == contextStart || completion > prevCompletion) {
Chris@850 713 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@850 714 setCompletion(j, completion);
Chris@850 715 }
Chris@320 716 prevCompletion = completion;
Chris@320 717 }
Chris@320 718
Chris@350 719 blockFrame += stepSize;
Chris@320 720 }
Chris@320 721
Chris@497 722 if (!m_abandoned) {
Chris@497 723 Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures();
Chris@320 724
Chris@850 725 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@930 726 for (int fi = 0; fi < (int)features[m_outputNos[j]].size(); ++fi) {
Chris@850 727 Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi];
Chris@850 728 addFeature(j, blockFrame, feature);
Chris@850 729 }
Chris@497 730 }
Chris@497 731 }
Chris@320 732
Chris@850 733 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@850 734 setCompletion(j, 100);
Chris@850 735 }
Chris@320 736
Chris@320 737 if (frequencyDomain) {
Chris@930 738 for (int ch = 0; ch < channelCount; ++ch) {
Chris@320 739 delete fftModels[ch];
Chris@320 740 }
Chris@556 741 delete[] reals;
Chris@556 742 delete[] imaginaries;
Chris@320 743 }
Chris@974 744
Chris@974 745 for (int ch = 0; ch < channelCount; ++ch) {
Chris@974 746 delete[] buffers[ch];
Chris@974 747 }
Chris@974 748 delete[] buffers;
Chris@320 749 }
Chris@320 750
Chris@320 751 void
Chris@363 752 FeatureExtractionModelTransformer::getFrames(int channelCount,
Chris@363 753 long startFrame, long size,
Chris@363 754 float **buffers)
Chris@320 755 {
Chris@320 756 long offset = 0;
Chris@320 757
Chris@320 758 if (startFrame < 0) {
Chris@363 759 for (int c = 0; c < channelCount; ++c) {
Chris@363 760 for (int i = 0; i < size && startFrame + i < 0; ++i) {
Chris@363 761 buffers[c][i] = 0.0f;
Chris@363 762 }
Chris@320 763 }
Chris@320 764 offset = -startFrame;
Chris@320 765 size -= offset;
Chris@320 766 if (size <= 0) return;
Chris@320 767 startFrame = 0;
Chris@320 768 }
Chris@320 769
Chris@350 770 DenseTimeValueModel *input = getConformingInput();
Chris@350 771 if (!input) return;
Chris@363 772
Chris@363 773 long got = 0;
Chris@350 774
Chris@363 775 if (channelCount == 1) {
Chris@363 776
Chris@363 777 got = input->getData(m_input.getChannel(), startFrame, size,
Chris@363 778 buffers[0] + offset);
Chris@363 779
Chris@363 780 if (m_input.getChannel() == -1 && input->getChannelCount() > 1) {
Chris@363 781 // use mean instead of sum, as plugin input
Chris@363 782 float cc = float(input->getChannelCount());
Chris@363 783 for (long i = 0; i < size; ++i) {
Chris@363 784 buffers[0][i + offset] /= cc;
Chris@363 785 }
Chris@363 786 }
Chris@363 787
Chris@363 788 } else {
Chris@363 789
Chris@363 790 float **writebuf = buffers;
Chris@363 791 if (offset > 0) {
Chris@363 792 writebuf = new float *[channelCount];
Chris@363 793 for (int i = 0; i < channelCount; ++i) {
Chris@363 794 writebuf[i] = buffers[i] + offset;
Chris@363 795 }
Chris@363 796 }
Chris@363 797
Chris@363 798 got = input->getData(0, channelCount-1, startFrame, size, writebuf);
Chris@363 799
Chris@363 800 if (writebuf != buffers) delete[] writebuf;
Chris@363 801 }
Chris@320 802
Chris@320 803 while (got < size) {
Chris@363 804 for (int c = 0; c < channelCount; ++c) {
Chris@363 805 buffers[c][got + offset] = 0.0;
Chris@363 806 }
Chris@320 807 ++got;
Chris@320 808 }
Chris@320 809 }
Chris@320 810
Chris@320 811 void
Chris@850 812 FeatureExtractionModelTransformer::addFeature(int n,
Chris@930 813 int blockFrame,
Chris@850 814 const Vamp::Plugin::Feature &feature)
Chris@320 815 {
Chris@930 816 int inputRate = m_input.getModel()->getSampleRate();
Chris@320 817
Chris@843 818 // cerr << "FeatureExtractionModelTransformer::addFeature: blockFrame = "
Chris@712 819 // << blockFrame << ", hasTimestamp = " << feature.hasTimestamp
Chris@712 820 // << ", timestamp = " << feature.timestamp << ", hasDuration = "
Chris@712 821 // << feature.hasDuration << ", duration = " << feature.duration
Chris@843 822 // << endl;
Chris@320 823
Chris@862 824 int frame = blockFrame;
Chris@320 825
Chris@849 826 if (m_descriptors[n]->sampleType ==
Chris@320 827 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
Chris@320 828
Chris@320 829 if (!feature.hasTimestamp) {
Chris@843 830 cerr
Chris@331 831 << "WARNING: FeatureExtractionModelTransformer::addFeature: "
Chris@320 832 << "Feature has variable sample rate but no timestamp!"
Chris@843 833 << endl;
Chris@320 834 return;
Chris@320 835 } else {
Chris@320 836 frame = Vamp::RealTime::realTime2Frame(feature.timestamp, inputRate);
Chris@320 837 }
Chris@320 838
Chris@849 839 } else if (m_descriptors[n]->sampleType ==
Chris@320 840 Vamp::Plugin::OutputDescriptor::FixedSampleRate) {
Chris@320 841
Chris@779 842 if (!feature.hasTimestamp) {
Chris@849 843 ++m_fixedRateFeatureNos[n];
Chris@779 844 } else {
Chris@779 845 RealTime ts(feature.timestamp.sec, feature.timestamp.nsec);
Chris@849 846 m_fixedRateFeatureNos[n] =
Chris@849 847 lrint(ts.toDouble() * m_descriptors[n]->sampleRate);
Chris@779 848 }
Chris@862 849
Chris@862 850 // cerr << "m_fixedRateFeatureNo = " << m_fixedRateFeatureNo
Chris@862 851 // << ", m_descriptor->sampleRate = " << m_descriptor->sampleRate
Chris@862 852 // << ", inputRate = " << inputRate
Chris@862 853 // << " giving frame = ";
Chris@849 854 frame = lrintf((m_fixedRateFeatureNos[n] / m_descriptors[n]->sampleRate)
Chris@862 855 * int(inputRate));
Chris@320 856 }
Chris@862 857
Chris@862 858 if (frame < 0) {
Chris@862 859 cerr
Chris@862 860 << "WARNING: FeatureExtractionModelTransformer::addFeature: "
Chris@862 861 << "Negative frame counts are not supported (frame = " << frame
Chris@862 862 << " from timestamp " << feature.timestamp
Chris@862 863 << "), dropping feature"
Chris@862 864 << endl;
Chris@862 865 return;
Chris@862 866 }
Chris@862 867
Chris@441 868 // Rather than repeat the complicated tests from the constructor
Chris@441 869 // to determine what sort of model we must be adding the features
Chris@441 870 // to, we instead test what sort of model the constructor decided
Chris@441 871 // to create.
Chris@320 872
Chris@849 873 if (isOutput<SparseOneDimensionalModel>(n)) {
Chris@441 874
Chris@441 875 SparseOneDimensionalModel *model =
Chris@849 876 getConformingOutput<SparseOneDimensionalModel>(n);
Chris@320 877 if (!model) return;
Chris@350 878
Chris@441 879 model->addPoint(SparseOneDimensionalModel::Point
Chris@441 880 (frame, feature.label.c_str()));
Chris@320 881
Chris@849 882 } else if (isOutput<SparseTimeValueModel>(n)) {
Chris@320 883
Chris@350 884 SparseTimeValueModel *model =
Chris@849 885 getConformingOutput<SparseTimeValueModel>(n);
Chris@320 886 if (!model) return;
Chris@350 887
Chris@930 888 for (int i = 0; i < (int)feature.values.size(); ++i) {
Chris@454 889
Chris@454 890 float value = feature.values[i];
Chris@454 891
Chris@454 892 QString label = feature.label.c_str();
Chris@454 893 if (feature.values.size() > 1) {
Chris@454 894 label = QString("[%1] %2").arg(i+1).arg(label);
Chris@454 895 }
Chris@454 896
Chris@876 897 SparseTimeValueModel *targetModel = model;
Chris@876 898
Chris@876 899 if (m_needAdditionalModels[n] && i > 0) {
Chris@876 900 targetModel = getAdditionalModel(n, i);
Chris@876 901 if (!targetModel) targetModel = model;
Chris@893 902 // std::cerr << "adding point to model " << targetModel
Chris@893 903 // << " for output " << n << " bin " << i << std::endl;
Chris@876 904 }
Chris@876 905
Chris@876 906 targetModel->addPoint
Chris@876 907 (SparseTimeValueModel::Point(frame, value, label));
Chris@454 908 }
Chris@320 909
Chris@849 910 } else if (isOutput<FlexiNoteModel>(n) || isOutput<NoteModel>(n) || isOutput<RegionModel>(n)) { //GF: Added Note Model
Chris@320 911
Chris@441 912 int index = 0;
Chris@441 913
Chris@441 914 float value = 0.0;
Chris@930 915 if ((int)feature.values.size() > index) {
Chris@441 916 value = feature.values[index++];
Chris@441 917 }
Chris@320 918
Chris@320 919 float duration = 1;
Chris@441 920 if (feature.hasDuration) {
Chris@441 921 duration = Vamp::RealTime::realTime2Frame(feature.duration, inputRate);
Chris@441 922 } else {
Chris@930 923 if ((int)feature.values.size() > index) {
Chris@441 924 duration = feature.values[index++];
Chris@441 925 }
Chris@441 926 }
gyorgyf@786 927
Chris@891 928 if (isOutput<FlexiNoteModel>(n)) { // GF: added for flexi note model
gyorgyf@786 929
gyorgyf@786 930 float velocity = 100;
Chris@930 931 if ((int)feature.values.size() > index) {
gyorgyf@786 932 velocity = feature.values[index++];
gyorgyf@786 933 }
gyorgyf@786 934 if (velocity < 0) velocity = 127;
gyorgyf@786 935 if (velocity > 127) velocity = 127;
gyorgyf@786 936
Chris@849 937 FlexiNoteModel *model = getConformingOutput<FlexiNoteModel>(n);
gyorgyf@786 938 if (!model) return;
gyorgyf@786 939 model->addPoint(FlexiNoteModel::Point(frame, value, // value is pitch
gyorgyf@786 940 lrintf(duration),
gyorgyf@786 941 velocity / 127.f,
gyorgyf@786 942 feature.label.c_str()));
gyorgyf@786 943 // GF: end -- added for flexi note model
Chris@849 944 } else if (isOutput<NoteModel>(n)) {
Chris@320 945
Chris@441 946 float velocity = 100;
Chris@930 947 if ((int)feature.values.size() > index) {
Chris@441 948 velocity = feature.values[index++];
Chris@441 949 }
Chris@441 950 if (velocity < 0) velocity = 127;
Chris@441 951 if (velocity > 127) velocity = 127;
Chris@320 952
Chris@849 953 NoteModel *model = getConformingOutput<NoteModel>(n);
Chris@441 954 if (!model) return;
Chris@441 955 model->addPoint(NoteModel::Point(frame, value, // value is pitch
Chris@441 956 lrintf(duration),
Chris@441 957 velocity / 127.f,
Chris@441 958 feature.label.c_str()));
Chris@441 959 } else {
gyorgyf@786 960
Chris@849 961 RegionModel *model = getConformingOutput<RegionModel>(n);
Chris@454 962 if (!model) return;
Chris@454 963
Chris@474 964 if (feature.hasDuration && !feature.values.empty()) {
Chris@454 965
Chris@930 966 for (int i = 0; i < (int)feature.values.size(); ++i) {
Chris@454 967
Chris@454 968 float value = feature.values[i];
Chris@454 969
Chris@454 970 QString label = feature.label.c_str();
Chris@454 971 if (feature.values.size() > 1) {
Chris@454 972 label = QString("[%1] %2").arg(i+1).arg(label);
Chris@454 973 }
Chris@454 974
Chris@454 975 model->addPoint(RegionModel::Point(frame, value,
Chris@454 976 lrintf(duration),
Chris@454 977 label));
Chris@454 978 }
Chris@454 979 } else {
Chris@454 980
Chris@441 981 model->addPoint(RegionModel::Point(frame, value,
Chris@441 982 lrintf(duration),
Chris@441 983 feature.label.c_str()));
Chris@454 984 }
Chris@441 985 }
Chris@320 986
Chris@849 987 } else if (isOutput<EditableDenseThreeDimensionalModel>(n)) {
Chris@320 988
Chris@533 989 DenseThreeDimensionalModel::Column values =
Chris@533 990 DenseThreeDimensionalModel::Column::fromStdVector(feature.values);
Chris@320 991
Chris@320 992 EditableDenseThreeDimensionalModel *model =
Chris@849 993 getConformingOutput<EditableDenseThreeDimensionalModel>(n);
Chris@320 994 if (!model) return;
Chris@320 995
Chris@889 996 // cerr << "(note: model resolution = " << model->getResolution() << ")"
Chris@889 997 // << endl;
Chris@889 998
Chris@891 999 if (!feature.hasTimestamp && m_fixedRateFeatureNos[n] >= 0) {
Chris@891 1000 model->setColumn(m_fixedRateFeatureNos[n], values);
Chris@889 1001 } else {
Chris@889 1002 model->setColumn(frame / model->getResolution(), values);
Chris@889 1003 }
Chris@441 1004
Chris@441 1005 } else {
Chris@690 1006 SVDEBUG << "FeatureExtractionModelTransformer::addFeature: Unknown output model type!" << endl;
Chris@320 1007 }
Chris@320 1008 }
Chris@320 1009
Chris@320 1010 void
Chris@850 1011 FeatureExtractionModelTransformer::setCompletion(int n, int completion)
Chris@320 1012 {
Chris@690 1013 // SVDEBUG << "FeatureExtractionModelTransformer::setCompletion("
Chris@687 1014 // << completion << ")" << endl;
Chris@320 1015
Chris@849 1016 if (isOutput<SparseOneDimensionalModel>(n)) {
Chris@320 1017
Chris@350 1018 SparseOneDimensionalModel *model =
Chris@849 1019 getConformingOutput<SparseOneDimensionalModel>(n);
Chris@320 1020 if (!model) return;
Chris@923 1021 if (model->isAbandoning()) abandon();
Chris@441 1022 model->setCompletion(completion, true);
Chris@320 1023
Chris@849 1024 } else if (isOutput<SparseTimeValueModel>(n)) {
Chris@320 1025
Chris@350 1026 SparseTimeValueModel *model =
Chris@849 1027 getConformingOutput<SparseTimeValueModel>(n);
Chris@320 1028 if (!model) return;
Chris@923 1029 if (model->isAbandoning()) abandon();
Chris@441 1030 model->setCompletion(completion, true);
Chris@320 1031
Chris@849 1032 } else if (isOutput<NoteModel>(n)) {
Chris@320 1033
Chris@849 1034 NoteModel *model = getConformingOutput<NoteModel>(n);
Chris@320 1035 if (!model) return;
Chris@923 1036 if (model->isAbandoning()) abandon();
Chris@441 1037 model->setCompletion(completion, true);
gyorgyf@786 1038
Chris@923 1039 } else if (isOutput<FlexiNoteModel>(n)) {
gyorgyf@786 1040
Chris@849 1041 FlexiNoteModel *model = getConformingOutput<FlexiNoteModel>(n);
gyorgyf@786 1042 if (!model) return;
Chris@923 1043 if (model->isAbandoning()) abandon();
gyorgyf@786 1044 model->setCompletion(completion, true);
Chris@320 1045
Chris@849 1046 } else if (isOutput<RegionModel>(n)) {
Chris@441 1047
Chris@849 1048 RegionModel *model = getConformingOutput<RegionModel>(n);
Chris@441 1049 if (!model) return;
Chris@923 1050 if (model->isAbandoning()) abandon();
Chris@441 1051 model->setCompletion(completion, true);
Chris@441 1052
Chris@849 1053 } else if (isOutput<EditableDenseThreeDimensionalModel>(n)) {
Chris@320 1054
Chris@320 1055 EditableDenseThreeDimensionalModel *model =
Chris@849 1056 getConformingOutput<EditableDenseThreeDimensionalModel>(n);
Chris@320 1057 if (!model) return;
Chris@923 1058 if (model->isAbandoning()) abandon();
Chris@350 1059 model->setCompletion(completion, true); //!!!m_context.updates);
Chris@320 1060 }
Chris@320 1061 }
Chris@320 1062