annotate transform/FeatureExtractionModelTransformer.cpp @ 876:47aa3aeb687b tonioni

For outputs with unknown bin count or multiple bins with variable sample rate, create additional output models for bins beyond the first
author Chris Cannam
date Wed, 29 Jan 2014 09:31:22 +0000
parents 3e6ed8a8577b
children b109b88bfa85
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@320 115 size_t channelCount = input->getChannelCount();
Chris@320 116 if (m_plugin->getMaxChannelCount() < channelCount) {
Chris@320 117 channelCount = 1;
Chris@320 118 }
Chris@320 119 if (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@849 136 size_t pstep = primaryTransform.getStepSize();
Chris@849 137 size_t 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@849 207 if (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@350 252 size_t modelRate = input->getSampleRate();
Chris@320 253 size_t 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@849 267 modelResolution = size_t(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 // modelRate = size_t(m_descriptors[n]->sampleRate + 0.001);
Chris@849 282 if (m_descriptors[n]->sampleRate > input->getSampleRate()) {
Chris@451 283 modelResolution = 1;
Chris@451 284 } else {
Chris@451 285 modelResolution = size_t(input->getSampleRate() /
Chris@849 286 m_descriptors[n]->sampleRate);
Chris@451 287 }
Chris@320 288 break;
Chris@320 289 }
Chris@320 290
Chris@441 291 bool preDurationPlugin = (m_plugin->getVampApiVersion() < 2);
Chris@441 292
Chris@849 293 Model *out = 0;
Chris@849 294
Chris@441 295 if (binCount == 0 &&
Chris@849 296 (preDurationPlugin || !m_descriptors[n]->hasDuration)) {
Chris@320 297
Chris@445 298 // Anything with no value and no duration is an instant
Chris@445 299
Chris@849 300 out = new SparseOneDimensionalModel(modelRate, modelResolution, false);
Chris@558 301 QString outputEventTypeURI = description.getOutputEventTypeURI(outputId);
Chris@849 302 out->setRDFTypeURI(outputEventTypeURI);
Chris@558 303
Chris@441 304 } else if ((preDurationPlugin && binCount > 1 &&
Chris@849 305 (m_descriptors[n]->sampleType ==
Chris@441 306 Vamp::Plugin::OutputDescriptor::VariableSampleRate)) ||
Chris@849 307 (!preDurationPlugin && m_descriptors[n]->hasDuration)) {
Chris@441 308
Chris@441 309 // For plugins using the old v1 API without explicit duration,
Chris@441 310 // we treat anything that has multiple bins (i.e. that has the
Chris@441 311 // potential to have value and duration) and a variable sample
Chris@441 312 // rate as a note model, taking its values as pitch, duration
Chris@441 313 // and velocity (if present) respectively. This is the same
Chris@441 314 // behaviour as always applied by SV to these plugins in the
Chris@441 315 // past.
Chris@441 316
Chris@441 317 // For plugins with the newer API, we treat anything with
Chris@441 318 // duration as either a note model with pitch and velocity, or
Chris@441 319 // a region model.
Chris@441 320
Chris@441 321 // How do we know whether it's an interval or note model?
Chris@441 322 // What's the essential difference? Is a note model any
Chris@441 323 // interval model using a Hz or "MIDI pitch" scale? There
Chris@441 324 // isn't really a reliable test for "MIDI pitch"... Does a
Chris@441 325 // note model always have velocity? This is a good question
Chris@441 326 // to be addressed by accompanying RDF, but for the moment we
Chris@441 327 // will do the following...
Chris@441 328
Chris@441 329 bool isNoteModel = false;
Chris@441 330
Chris@441 331 // Regions have only value (and duration -- we can't extract a
Chris@441 332 // region model from an old-style plugin that doesn't support
Chris@441 333 // duration)
Chris@441 334 if (binCount > 1) isNoteModel = true;
Chris@441 335
Chris@595 336 // Regions do not have units of Hz or MIDI things (a sweeping
Chris@595 337 // assumption!)
Chris@849 338 if (m_descriptors[n]->unit == "Hz" ||
Chris@849 339 m_descriptors[n]->unit.find("MIDI") != std::string::npos ||
Chris@849 340 m_descriptors[n]->unit.find("midi") != std::string::npos) {
Chris@595 341 isNoteModel = true;
Chris@595 342 }
Chris@441 343
Chris@441 344 // If we had a "sparse 3D model", we would have the additional
Chris@441 345 // problem of determining whether to use that here (if bin
Chris@441 346 // count > 1). But we don't.
Chris@441 347
Chris@859 348 QSettings settings;
Chris@859 349 settings.beginGroup("Transformer");
Chris@859 350 bool flexi = settings.value("use-flexi-note-model", false).toBool();
Chris@859 351 settings.endGroup();
Chris@859 352
Chris@859 353 cerr << "flexi = " << flexi << endl;
Chris@859 354
Chris@859 355 if (isNoteModel && !flexi) {
Chris@441 356
Chris@441 357 NoteModel *model;
Chris@441 358 if (haveExtents) {
Chris@859 359 model = new NoteModel
Chris@859 360 (modelRate, modelResolution, minValue, maxValue, false);
Chris@441 361 } else {
Chris@859 362 model = new NoteModel
Chris@859 363 (modelRate, modelResolution, false);
gyorgyf@786 364 }
Chris@849 365 model->setScaleUnits(m_descriptors[n]->unit.c_str());
Chris@849 366 out = model;
gyorgyf@786 367
Chris@859 368 } else if (isNoteModel && flexi) {
gyorgyf@786 369
gyorgyf@786 370 FlexiNoteModel *model;
gyorgyf@786 371 if (haveExtents) {
Chris@859 372 model = new FlexiNoteModel
Chris@859 373 (modelRate, modelResolution, minValue, maxValue, false);
gyorgyf@786 374 } else {
Chris@859 375 model = new FlexiNoteModel
Chris@859 376 (modelRate, modelResolution, false);
Chris@441 377 }
Chris@849 378 model->setScaleUnits(m_descriptors[n]->unit.c_str());
Chris@849 379 out = model;
Chris@441 380
Chris@441 381 } else {
Chris@441 382
Chris@441 383 RegionModel *model;
Chris@441 384 if (haveExtents) {
Chris@441 385 model = new RegionModel
Chris@441 386 (modelRate, modelResolution, minValue, maxValue, false);
Chris@441 387 } else {
Chris@441 388 model = new RegionModel
Chris@441 389 (modelRate, modelResolution, false);
Chris@441 390 }
Chris@849 391 model->setScaleUnits(m_descriptors[n]->unit.c_str());
Chris@849 392 out = model;
Chris@441 393 }
Chris@441 394
Chris@558 395 QString outputEventTypeURI = description.getOutputEventTypeURI(outputId);
Chris@849 396 out->setRDFTypeURI(outputEventTypeURI);
Chris@558 397
Chris@876 398 } else if (binCount == 1 ||
Chris@849 399 (m_descriptors[n]->sampleType ==
Chris@441 400 Vamp::Plugin::OutputDescriptor::VariableSampleRate)) {
Chris@441 401
Chris@441 402 // Anything that is not a 1D, note, or interval model and that
Chris@441 403 // has only one value per result must be a sparse time value
Chris@441 404 // model.
Chris@441 405
Chris@441 406 // Anything that is not a 1D, note, or interval model and that
Chris@876 407 // has a variable sample rate is treated as a set of sparse
Chris@876 408 // time value models, one per output bin, because we lack a
Chris@441 409 // sparse 3D model.
Chris@320 410
Chris@876 411 // Anything that is not a 1D, note, or interval model and that
Chris@876 412 // has a fixed sample rate but an unknown number of values per
Chris@876 413 // result is also treated as a set of sparse time value models.
Chris@876 414
Chris@876 415 // For sets of sparse time value models, we create a single
Chris@876 416 // model first as the "standard" output and then create models
Chris@876 417 // for bins 1+ in the additional model map (mapping the output
Chris@876 418 // descriptor to a list of models indexed by bin-1). But we
Chris@876 419 // don't create the additional models yet, as this case has to
Chris@876 420 // work even if the number of bins is unknown at this point --
Chris@876 421 // we just create an additional model (copying its parameters
Chris@876 422 // from the default one) each time a new bin is encountered.
Chris@876 423
Chris@876 424 if (!haveBinCount || binCount > 1) {
Chris@876 425 m_needAdditionalModels[n] = true;
Chris@876 426 }
Chris@876 427
Chris@320 428 SparseTimeValueModel *model;
Chris@320 429 if (haveExtents) {
Chris@320 430 model = new SparseTimeValueModel
Chris@320 431 (modelRate, modelResolution, minValue, maxValue, false);
Chris@320 432 } else {
Chris@320 433 model = new SparseTimeValueModel
Chris@320 434 (modelRate, modelResolution, false);
Chris@320 435 }
Chris@558 436
Chris@558 437 Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors();
Chris@849 438 model->setScaleUnits(outputs[m_outputNos[n]].unit.c_str());
Chris@320 439
Chris@849 440 out = model;
Chris@320 441
Chris@558 442 QString outputEventTypeURI = description.getOutputEventTypeURI(outputId);
Chris@849 443 out->setRDFTypeURI(outputEventTypeURI);
Chris@558 444
Chris@441 445 } else {
Chris@320 446
Chris@441 447 // Anything that is not a 1D, note, or interval model and that
Chris@441 448 // has a fixed sample rate and more than one value per result
Chris@441 449 // must be a dense 3D model.
Chris@320 450
Chris@320 451 EditableDenseThreeDimensionalModel *model =
Chris@320 452 new EditableDenseThreeDimensionalModel
Chris@535 453 (modelRate, modelResolution, binCount,
Chris@535 454 EditableDenseThreeDimensionalModel::BasicMultirateCompression,
Chris@535 455 false);
Chris@320 456
Chris@849 457 if (!m_descriptors[n]->binNames.empty()) {
Chris@320 458 std::vector<QString> names;
Chris@849 459 for (size_t i = 0; i < m_descriptors[n]->binNames.size(); ++i) {
Chris@849 460 names.push_back(m_descriptors[n]->binNames[i].c_str());
Chris@320 461 }
Chris@320 462 model->setBinNames(names);
Chris@320 463 }
Chris@320 464
Chris@849 465 out = model;
Chris@558 466
Chris@558 467 QString outputSignalTypeURI = description.getOutputSignalTypeURI(outputId);
Chris@849 468 out->setRDFTypeURI(outputSignalTypeURI);
Chris@320 469 }
Chris@333 470
Chris@849 471 if (out) {
Chris@849 472 out->setSourceModel(input);
Chris@849 473 m_outputs.push_back(out);
Chris@849 474 }
Chris@320 475 }
Chris@320 476
Chris@331 477 FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()
Chris@320 478 {
Chris@690 479 // SVDEBUG << "FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()" << endl;
Chris@320 480 delete m_plugin;
Chris@850 481 for (int j = 0; j < m_descriptors.size(); ++j) {
Chris@850 482 delete m_descriptors[j];
Chris@850 483 }
Chris@320 484 }
Chris@320 485
Chris@876 486 FeatureExtractionModelTransformer::Models
Chris@876 487 FeatureExtractionModelTransformer::getAdditionalOutputModels()
Chris@876 488 {
Chris@876 489 Models mm;
Chris@876 490 for (AdditionalModelMap::iterator i = m_additionalModels.begin();
Chris@876 491 i != m_additionalModels.end(); ++i) {
Chris@876 492 for (std::map<int, SparseTimeValueModel *>::iterator j =
Chris@876 493 i->second.begin();
Chris@876 494 j != i->second.end(); ++j) {
Chris@876 495 SparseTimeValueModel *m = j->second;
Chris@876 496 if (m) mm.push_back(m);
Chris@876 497 }
Chris@876 498 }
Chris@876 499 return mm;
Chris@876 500 }
Chris@876 501
Chris@876 502 SparseTimeValueModel *
Chris@876 503 FeatureExtractionModelTransformer::getAdditionalModel(int n, int binNo)
Chris@876 504 {
Chris@876 505 std::cerr << "getAdditionalModel(" << n << ", " << binNo << ")" << std::endl;
Chris@876 506
Chris@876 507 if (binNo == 0) {
Chris@876 508 std::cerr << "Internal error: binNo == 0 in getAdditionalModel (should be using primary model)" << std::endl;
Chris@876 509 return 0;
Chris@876 510 }
Chris@876 511
Chris@876 512 if (!m_needAdditionalModels[n]) return 0;
Chris@876 513 if (!isOutput<SparseTimeValueModel>(n)) return 0;
Chris@876 514 if (m_additionalModels[n][binNo]) return m_additionalModels[n][binNo];
Chris@876 515
Chris@876 516 std::cerr << "getAdditionalModel(" << n << ", " << binNo << "): creating" << std::endl;
Chris@876 517
Chris@876 518 SparseTimeValueModel *baseModel = getConformingOutput<SparseTimeValueModel>(n);
Chris@876 519 if (!baseModel) return 0;
Chris@876 520
Chris@876 521 std::cerr << "getAdditionalModel(" << n << ", " << binNo << "): (from " << baseModel << ")" << std::endl;
Chris@876 522
Chris@876 523 SparseTimeValueModel *additional =
Chris@876 524 new SparseTimeValueModel(baseModel->getSampleRate(),
Chris@876 525 baseModel->getResolution(),
Chris@876 526 baseModel->getValueMinimum(),
Chris@876 527 baseModel->getValueMaximum(),
Chris@876 528 false);
Chris@876 529
Chris@876 530 additional->setScaleUnits(baseModel->getScaleUnits());
Chris@876 531 additional->setRDFTypeURI(baseModel->getRDFTypeURI());
Chris@876 532
Chris@876 533 m_additionalModels[n][binNo] = additional;
Chris@876 534 return additional;
Chris@876 535 }
Chris@876 536
Chris@320 537 DenseTimeValueModel *
Chris@350 538 FeatureExtractionModelTransformer::getConformingInput()
Chris@320 539 {
Chris@690 540 // SVDEBUG << "FeatureExtractionModelTransformer::getConformingInput: input model is " << getInputModel() << endl;
Chris@408 541
Chris@320 542 DenseTimeValueModel *dtvm =
Chris@320 543 dynamic_cast<DenseTimeValueModel *>(getInputModel());
Chris@320 544 if (!dtvm) {
Chris@690 545 SVDEBUG << "FeatureExtractionModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << endl;
Chris@320 546 }
Chris@320 547 return dtvm;
Chris@320 548 }
Chris@320 549
Chris@320 550 void
Chris@331 551 FeatureExtractionModelTransformer::run()
Chris@320 552 {
Chris@350 553 DenseTimeValueModel *input = getConformingInput();
Chris@320 554 if (!input) return;
Chris@320 555
Chris@849 556 if (m_outputs.empty()) return;
Chris@320 557
Chris@850 558 Transform primaryTransform = m_transforms[0];
Chris@850 559
Chris@497 560 while (!input->isReady() && !m_abandoned) {
Chris@690 561 SVDEBUG << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << endl;
Chris@497 562 usleep(500000);
Chris@320 563 }
Chris@497 564 if (m_abandoned) return;
Chris@320 565
Chris@350 566 size_t sampleRate = input->getSampleRate();
Chris@320 567
Chris@320 568 size_t channelCount = input->getChannelCount();
Chris@320 569 if (m_plugin->getMaxChannelCount() < channelCount) {
Chris@320 570 channelCount = 1;
Chris@320 571 }
Chris@320 572
Chris@320 573 float **buffers = new float*[channelCount];
Chris@320 574 for (size_t ch = 0; ch < channelCount; ++ch) {
Chris@850 575 buffers[ch] = new float[primaryTransform.getBlockSize() + 2];
Chris@320 576 }
Chris@320 577
Chris@850 578 size_t stepSize = primaryTransform.getStepSize();
Chris@850 579 size_t blockSize = primaryTransform.getBlockSize();
Chris@350 580
Chris@320 581 bool frequencyDomain = (m_plugin->getInputDomain() ==
Chris@320 582 Vamp::Plugin::FrequencyDomain);
Chris@320 583 std::vector<FFTModel *> fftModels;
Chris@320 584
Chris@320 585 if (frequencyDomain) {
Chris@320 586 for (size_t ch = 0; ch < channelCount; ++ch) {
Chris@320 587 FFTModel *model = new FFTModel
Chris@350 588 (getConformingInput(),
Chris@350 589 channelCount == 1 ? m_input.getChannel() : ch,
Chris@850 590 primaryTransform.getWindowType(),
Chris@350 591 blockSize,
Chris@350 592 stepSize,
Chris@350 593 blockSize,
Chris@334 594 false,
Chris@334 595 StorageAdviser::PrecisionCritical);
Chris@320 596 if (!model->isOK()) {
Chris@320 597 delete model;
Chris@850 598 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@850 599 setCompletion(j, 100);
Chris@850 600 }
Chris@387 601 //!!! 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 602 throw AllocationFailed("Failed to create the FFT model for this feature extraction model transformer");
Chris@320 603 }
Chris@320 604 model->resume();
Chris@320 605 fftModels.push_back(model);
Chris@320 606 }
Chris@320 607 }
Chris@320 608
Chris@350 609 long startFrame = m_input.getModel()->getStartFrame();
Chris@350 610 long endFrame = m_input.getModel()->getEndFrame();
Chris@320 611
Chris@850 612 RealTime contextStartRT = primaryTransform.getStartTime();
Chris@850 613 RealTime contextDurationRT = primaryTransform.getDuration();
Chris@350 614
Chris@350 615 long contextStart =
Chris@350 616 RealTime::realTime2Frame(contextStartRT, sampleRate);
Chris@350 617
Chris@350 618 long contextDuration =
Chris@350 619 RealTime::realTime2Frame(contextDurationRT, sampleRate);
Chris@320 620
Chris@320 621 if (contextStart == 0 || contextStart < startFrame) {
Chris@320 622 contextStart = startFrame;
Chris@320 623 }
Chris@320 624
Chris@320 625 if (contextDuration == 0) {
Chris@320 626 contextDuration = endFrame - contextStart;
Chris@320 627 }
Chris@320 628 if (contextStart + contextDuration > endFrame) {
Chris@320 629 contextDuration = endFrame - contextStart;
Chris@320 630 }
Chris@320 631
Chris@320 632 long blockFrame = contextStart;
Chris@320 633
Chris@320 634 long prevCompletion = 0;
Chris@320 635
Chris@850 636 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@850 637 setCompletion(j, 0);
Chris@850 638 }
Chris@320 639
Chris@556 640 float *reals = 0;
Chris@556 641 float *imaginaries = 0;
Chris@556 642 if (frequencyDomain) {
Chris@556 643 reals = new float[blockSize/2 + 1];
Chris@556 644 imaginaries = new float[blockSize/2 + 1];
Chris@556 645 }
Chris@556 646
Chris@678 647 QString error = "";
Chris@678 648
Chris@320 649 while (!m_abandoned) {
Chris@320 650
Chris@320 651 if (frequencyDomain) {
Chris@350 652 if (blockFrame - int(blockSize)/2 >
Chris@320 653 contextStart + contextDuration) break;
Chris@320 654 } else {
Chris@320 655 if (blockFrame >=
Chris@320 656 contextStart + contextDuration) break;
Chris@320 657 }
Chris@320 658
Chris@690 659 // SVDEBUG << "FeatureExtractionModelTransformer::run: blockFrame "
Chris@320 660 // << blockFrame << ", endFrame " << endFrame << ", blockSize "
Chris@687 661 // << blockSize << endl;
Chris@320 662
Chris@320 663 long completion =
Chris@350 664 (((blockFrame - contextStart) / stepSize) * 99) /
Chris@557 665 (contextDuration / stepSize + 1);
Chris@320 666
Chris@350 667 // channelCount is either m_input.getModel()->channelCount or 1
Chris@320 668
Chris@363 669 if (frequencyDomain) {
Chris@363 670 for (size_t ch = 0; ch < channelCount; ++ch) {
Chris@350 671 int column = (blockFrame - startFrame) / stepSize;
Chris@556 672 fftModels[ch]->getValuesAt(column, reals, imaginaries);
Chris@350 673 for (size_t i = 0; i <= blockSize/2; ++i) {
Chris@556 674 buffers[ch][i*2] = reals[i];
Chris@556 675 buffers[ch][i*2+1] = imaginaries[i];
Chris@320 676 }
Chris@678 677 error = fftModels[ch]->getError();
Chris@678 678 if (error != "") {
Chris@843 679 cerr << "FeatureExtractionModelTransformer::run: Abandoning, error is " << error << endl;
Chris@678 680 m_abandoned = true;
Chris@678 681 m_message = error;
Chris@678 682 }
Chris@363 683 }
Chris@363 684 } else {
Chris@363 685 getFrames(channelCount, blockFrame, blockSize, buffers);
Chris@320 686 }
Chris@320 687
Chris@497 688 if (m_abandoned) break;
Chris@497 689
Chris@320 690 Vamp::Plugin::FeatureSet features = m_plugin->process
Chris@320 691 (buffers, Vamp::RealTime::frame2RealTime(blockFrame, sampleRate));
Chris@320 692
Chris@497 693 if (m_abandoned) break;
Chris@497 694
Chris@850 695 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@850 696 for (size_t fi = 0; fi < features[m_outputNos[j]].size(); ++fi) {
Chris@850 697 Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi];
Chris@850 698 addFeature(j, blockFrame, feature);
Chris@850 699 }
Chris@850 700 }
Chris@320 701
Chris@320 702 if (blockFrame == contextStart || completion > prevCompletion) {
Chris@850 703 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@850 704 setCompletion(j, completion);
Chris@850 705 }
Chris@320 706 prevCompletion = completion;
Chris@320 707 }
Chris@320 708
Chris@350 709 blockFrame += stepSize;
Chris@320 710 }
Chris@320 711
Chris@497 712 if (!m_abandoned) {
Chris@497 713 Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures();
Chris@320 714
Chris@850 715 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@850 716 for (size_t fi = 0; fi < features[m_outputNos[j]].size(); ++fi) {
Chris@850 717 Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi];
Chris@850 718 addFeature(j, blockFrame, feature);
Chris@850 719 }
Chris@497 720 }
Chris@497 721 }
Chris@320 722
Chris@850 723 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@850 724 setCompletion(j, 100);
Chris@850 725 }
Chris@320 726
Chris@320 727 if (frequencyDomain) {
Chris@320 728 for (size_t ch = 0; ch < channelCount; ++ch) {
Chris@320 729 delete fftModels[ch];
Chris@320 730 }
Chris@556 731 delete[] reals;
Chris@556 732 delete[] imaginaries;
Chris@320 733 }
Chris@320 734 }
Chris@320 735
Chris@320 736 void
Chris@363 737 FeatureExtractionModelTransformer::getFrames(int channelCount,
Chris@363 738 long startFrame, long size,
Chris@363 739 float **buffers)
Chris@320 740 {
Chris@320 741 long offset = 0;
Chris@320 742
Chris@320 743 if (startFrame < 0) {
Chris@363 744 for (int c = 0; c < channelCount; ++c) {
Chris@363 745 for (int i = 0; i < size && startFrame + i < 0; ++i) {
Chris@363 746 buffers[c][i] = 0.0f;
Chris@363 747 }
Chris@320 748 }
Chris@320 749 offset = -startFrame;
Chris@320 750 size -= offset;
Chris@320 751 if (size <= 0) return;
Chris@320 752 startFrame = 0;
Chris@320 753 }
Chris@320 754
Chris@350 755 DenseTimeValueModel *input = getConformingInput();
Chris@350 756 if (!input) return;
Chris@363 757
Chris@363 758 long got = 0;
Chris@350 759
Chris@363 760 if (channelCount == 1) {
Chris@363 761
Chris@363 762 got = input->getData(m_input.getChannel(), startFrame, size,
Chris@363 763 buffers[0] + offset);
Chris@363 764
Chris@363 765 if (m_input.getChannel() == -1 && input->getChannelCount() > 1) {
Chris@363 766 // use mean instead of sum, as plugin input
Chris@363 767 float cc = float(input->getChannelCount());
Chris@363 768 for (long i = 0; i < size; ++i) {
Chris@363 769 buffers[0][i + offset] /= cc;
Chris@363 770 }
Chris@363 771 }
Chris@363 772
Chris@363 773 } else {
Chris@363 774
Chris@363 775 float **writebuf = buffers;
Chris@363 776 if (offset > 0) {
Chris@363 777 writebuf = new float *[channelCount];
Chris@363 778 for (int i = 0; i < channelCount; ++i) {
Chris@363 779 writebuf[i] = buffers[i] + offset;
Chris@363 780 }
Chris@363 781 }
Chris@363 782
Chris@363 783 got = input->getData(0, channelCount-1, startFrame, size, writebuf);
Chris@363 784
Chris@363 785 if (writebuf != buffers) delete[] writebuf;
Chris@363 786 }
Chris@320 787
Chris@320 788 while (got < size) {
Chris@363 789 for (int c = 0; c < channelCount; ++c) {
Chris@363 790 buffers[c][got + offset] = 0.0;
Chris@363 791 }
Chris@320 792 ++got;
Chris@320 793 }
Chris@320 794 }
Chris@320 795
Chris@320 796 void
Chris@850 797 FeatureExtractionModelTransformer::addFeature(int n,
Chris@850 798 size_t blockFrame,
Chris@850 799 const Vamp::Plugin::Feature &feature)
Chris@320 800 {
Chris@350 801 size_t inputRate = m_input.getModel()->getSampleRate();
Chris@320 802
Chris@843 803 // cerr << "FeatureExtractionModelTransformer::addFeature: blockFrame = "
Chris@712 804 // << blockFrame << ", hasTimestamp = " << feature.hasTimestamp
Chris@712 805 // << ", timestamp = " << feature.timestamp << ", hasDuration = "
Chris@712 806 // << feature.hasDuration << ", duration = " << feature.duration
Chris@843 807 // << endl;
Chris@320 808
Chris@320 809 int binCount = 1;
Chris@849 810 if (m_descriptors[n]->hasFixedBinCount) {
Chris@849 811 binCount = m_descriptors[n]->binCount;
Chris@320 812 }
Chris@320 813
Chris@320 814 size_t frame = blockFrame;
Chris@320 815
Chris@849 816 if (m_descriptors[n]->sampleType ==
Chris@320 817 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
Chris@320 818
Chris@320 819 if (!feature.hasTimestamp) {
Chris@843 820 cerr
Chris@331 821 << "WARNING: FeatureExtractionModelTransformer::addFeature: "
Chris@320 822 << "Feature has variable sample rate but no timestamp!"
Chris@843 823 << endl;
Chris@320 824 return;
Chris@320 825 } else {
Chris@320 826 frame = Vamp::RealTime::realTime2Frame(feature.timestamp, inputRate);
Chris@320 827 }
Chris@320 828
Chris@849 829 } else if (m_descriptors[n]->sampleType ==
Chris@320 830 Vamp::Plugin::OutputDescriptor::FixedSampleRate) {
Chris@320 831
Chris@779 832 if (!feature.hasTimestamp) {
Chris@849 833 ++m_fixedRateFeatureNos[n];
Chris@779 834 } else {
Chris@779 835 RealTime ts(feature.timestamp.sec, feature.timestamp.nsec);
Chris@849 836 m_fixedRateFeatureNos[n] =
Chris@849 837 lrint(ts.toDouble() * m_descriptors[n]->sampleRate);
Chris@779 838 }
Chris@779 839
Chris@849 840 frame = lrintf((m_fixedRateFeatureNos[n] / m_descriptors[n]->sampleRate)
Chris@779 841 * inputRate);
Chris@320 842 }
Chris@320 843
Chris@441 844 // Rather than repeat the complicated tests from the constructor
Chris@441 845 // to determine what sort of model we must be adding the features
Chris@441 846 // to, we instead test what sort of model the constructor decided
Chris@441 847 // to create.
Chris@320 848
Chris@849 849 if (isOutput<SparseOneDimensionalModel>(n)) {
Chris@441 850
Chris@441 851 SparseOneDimensionalModel *model =
Chris@849 852 getConformingOutput<SparseOneDimensionalModel>(n);
Chris@320 853 if (!model) return;
Chris@350 854
Chris@441 855 model->addPoint(SparseOneDimensionalModel::Point
Chris@441 856 (frame, feature.label.c_str()));
Chris@320 857
Chris@849 858 } else if (isOutput<SparseTimeValueModel>(n)) {
Chris@320 859
Chris@350 860 SparseTimeValueModel *model =
Chris@849 861 getConformingOutput<SparseTimeValueModel>(n);
Chris@320 862 if (!model) return;
Chris@350 863
Chris@454 864 for (int i = 0; i < feature.values.size(); ++i) {
Chris@454 865
Chris@454 866 float value = feature.values[i];
Chris@454 867
Chris@454 868 QString label = feature.label.c_str();
Chris@454 869 if (feature.values.size() > 1) {
Chris@454 870 label = QString("[%1] %2").arg(i+1).arg(label);
Chris@454 871 }
Chris@454 872
Chris@876 873 SparseTimeValueModel *targetModel = model;
Chris@876 874
Chris@876 875 if (m_needAdditionalModels[n] && i > 0) {
Chris@876 876 targetModel = getAdditionalModel(n, i);
Chris@876 877 if (!targetModel) targetModel = model;
Chris@876 878 std::cerr << "adding point to model " << targetModel
Chris@876 879 << " for output " << n << " bin " << i << std::endl;
Chris@876 880 }
Chris@876 881
Chris@876 882 targetModel->addPoint
Chris@876 883 (SparseTimeValueModel::Point(frame, value, label));
Chris@454 884 }
Chris@320 885
Chris@849 886 } else if (isOutput<FlexiNoteModel>(n) || isOutput<NoteModel>(n) || isOutput<RegionModel>(n)) { //GF: Added Note Model
Chris@320 887
Chris@441 888 int index = 0;
Chris@441 889
Chris@441 890 float value = 0.0;
Chris@441 891 if (feature.values.size() > index) {
Chris@441 892 value = feature.values[index++];
Chris@441 893 }
Chris@320 894
Chris@320 895 float duration = 1;
Chris@441 896 if (feature.hasDuration) {
Chris@441 897 duration = Vamp::RealTime::realTime2Frame(feature.duration, inputRate);
Chris@441 898 } else {
Chris@441 899 if (feature.values.size() > index) {
Chris@441 900 duration = feature.values[index++];
Chris@441 901 }
Chris@441 902 }
gyorgyf@786 903
Chris@849 904 if (isOutput<FlexiNoteModel>(n)) { // GF: added for flexi note model
gyorgyf@786 905
gyorgyf@786 906 float velocity = 100;
gyorgyf@786 907 if (feature.values.size() > index) {
gyorgyf@786 908 velocity = feature.values[index++];
gyorgyf@786 909 }
gyorgyf@786 910 if (velocity < 0) velocity = 127;
gyorgyf@786 911 if (velocity > 127) velocity = 127;
gyorgyf@786 912
Chris@849 913 FlexiNoteModel *model = getConformingOutput<FlexiNoteModel>(n);
gyorgyf@786 914 if (!model) return;
gyorgyf@786 915 model->addPoint(FlexiNoteModel::Point(frame, value, // value is pitch
gyorgyf@786 916 lrintf(duration),
gyorgyf@786 917 velocity / 127.f,
gyorgyf@786 918 feature.label.c_str()));
gyorgyf@786 919 // GF: end -- added for flexi note model
Chris@849 920 } else if (isOutput<NoteModel>(n)) {
Chris@320 921
Chris@441 922 float velocity = 100;
Chris@441 923 if (feature.values.size() > index) {
Chris@441 924 velocity = feature.values[index++];
Chris@441 925 }
Chris@441 926 if (velocity < 0) velocity = 127;
Chris@441 927 if (velocity > 127) velocity = 127;
Chris@320 928
Chris@849 929 NoteModel *model = getConformingOutput<NoteModel>(n);
Chris@441 930 if (!model) return;
Chris@441 931 model->addPoint(NoteModel::Point(frame, value, // value is pitch
Chris@441 932 lrintf(duration),
Chris@441 933 velocity / 127.f,
Chris@441 934 feature.label.c_str()));
Chris@441 935 } else {
gyorgyf@786 936
Chris@849 937 RegionModel *model = getConformingOutput<RegionModel>(n);
Chris@454 938 if (!model) return;
Chris@454 939
Chris@474 940 if (feature.hasDuration && !feature.values.empty()) {
Chris@454 941
Chris@454 942 for (int i = 0; i < feature.values.size(); ++i) {
Chris@454 943
Chris@454 944 float value = feature.values[i];
Chris@454 945
Chris@454 946 QString label = feature.label.c_str();
Chris@454 947 if (feature.values.size() > 1) {
Chris@454 948 label = QString("[%1] %2").arg(i+1).arg(label);
Chris@454 949 }
Chris@454 950
Chris@454 951 model->addPoint(RegionModel::Point(frame, value,
Chris@454 952 lrintf(duration),
Chris@454 953 label));
Chris@454 954 }
Chris@454 955 } else {
Chris@454 956
Chris@441 957 model->addPoint(RegionModel::Point(frame, value,
Chris@441 958 lrintf(duration),
Chris@441 959 feature.label.c_str()));
Chris@454 960 }
Chris@441 961 }
Chris@320 962
Chris@849 963 } else if (isOutput<EditableDenseThreeDimensionalModel>(n)) {
Chris@320 964
Chris@533 965 DenseThreeDimensionalModel::Column values =
Chris@533 966 DenseThreeDimensionalModel::Column::fromStdVector(feature.values);
Chris@320 967
Chris@320 968 EditableDenseThreeDimensionalModel *model =
Chris@849 969 getConformingOutput<EditableDenseThreeDimensionalModel>(n);
Chris@320 970 if (!model) return;
Chris@320 971
Chris@320 972 model->setColumn(frame / model->getResolution(), values);
Chris@441 973
Chris@441 974 } else {
Chris@690 975 SVDEBUG << "FeatureExtractionModelTransformer::addFeature: Unknown output model type!" << endl;
Chris@320 976 }
Chris@320 977 }
Chris@320 978
Chris@320 979 void
Chris@850 980 FeatureExtractionModelTransformer::setCompletion(int n, int completion)
Chris@320 981 {
Chris@690 982 // SVDEBUG << "FeatureExtractionModelTransformer::setCompletion("
Chris@687 983 // << completion << ")" << endl;
Chris@320 984
Chris@849 985 if (isOutput<SparseOneDimensionalModel>(n)) {
Chris@320 986
Chris@350 987 SparseOneDimensionalModel *model =
Chris@849 988 getConformingOutput<SparseOneDimensionalModel>(n);
Chris@320 989 if (!model) return;
Chris@441 990 model->setCompletion(completion, true);
Chris@320 991
Chris@849 992 } else if (isOutput<SparseTimeValueModel>(n)) {
Chris@320 993
Chris@350 994 SparseTimeValueModel *model =
Chris@849 995 getConformingOutput<SparseTimeValueModel>(n);
Chris@320 996 if (!model) return;
Chris@441 997 model->setCompletion(completion, true);
Chris@320 998
Chris@849 999 } else if (isOutput<NoteModel>(n)) {
Chris@320 1000
Chris@849 1001 NoteModel *model = getConformingOutput<NoteModel>(n);
Chris@320 1002 if (!model) return;
Chris@441 1003 model->setCompletion(completion, true);
gyorgyf@786 1004
Chris@849 1005 } else if (isOutput<FlexiNoteModel>(n)) {
gyorgyf@786 1006
Chris@849 1007 FlexiNoteModel *model = getConformingOutput<FlexiNoteModel>(n);
gyorgyf@786 1008 if (!model) return;
gyorgyf@786 1009 model->setCompletion(completion, true);
Chris@320 1010
Chris@849 1011 } else if (isOutput<RegionModel>(n)) {
Chris@441 1012
Chris@849 1013 RegionModel *model = getConformingOutput<RegionModel>(n);
Chris@441 1014 if (!model) return;
Chris@441 1015 model->setCompletion(completion, true);
Chris@441 1016
Chris@849 1017 } else if (isOutput<EditableDenseThreeDimensionalModel>(n)) {
Chris@320 1018
Chris@320 1019 EditableDenseThreeDimensionalModel *model =
Chris@849 1020 getConformingOutput<EditableDenseThreeDimensionalModel>(n);
Chris@320 1021 if (!model) return;
Chris@350 1022 model->setCompletion(completion, true); //!!!m_context.updates);
Chris@320 1023 }
Chris@320 1024 }
Chris@320 1025