annotate transform/FeatureExtractionModelTransformer.cpp @ 1008:d9e0e59a1581

When using an aggregate model to pass data to a transform, zero-pad the shorter input to the duration of the longer rather than truncating the longer. (This is better behaviour for e.g. MATCH, and in any case the code was previously truncating incorrectly and ending up with garbage data at the end.)
author Chris Cannam
date Fri, 14 Nov 2014 13:51:33 +0000
parents 65494d0d9ded
children b14064bd1f97
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@1008 682 if (fftModels[ch]->getValuesAt(column, reals, imaginaries)) {
Chris@1008 683 for (int i = 0; i <= blockSize/2; ++i) {
Chris@1008 684 buffers[ch][i*2] = reals[i];
Chris@1008 685 buffers[ch][i*2+1] = imaginaries[i];
Chris@1008 686 }
Chris@1008 687 } else {
Chris@1008 688 for (int i = 0; i <= blockSize/2; ++i) {
Chris@1008 689 buffers[ch][i*2] = 0.f;
Chris@1008 690 buffers[ch][i*2+1] = 0.f;
Chris@1008 691 }
Chris@1008 692 }
Chris@678 693 error = fftModels[ch]->getError();
Chris@678 694 if (error != "") {
Chris@843 695 cerr << "FeatureExtractionModelTransformer::run: Abandoning, error is " << error << endl;
Chris@678 696 m_abandoned = true;
Chris@678 697 m_message = error;
Chris@678 698 }
Chris@363 699 }
Chris@363 700 } else {
Chris@363 701 getFrames(channelCount, blockFrame, blockSize, buffers);
Chris@320 702 }
Chris@320 703
Chris@497 704 if (m_abandoned) break;
Chris@497 705
Chris@320 706 Vamp::Plugin::FeatureSet features = m_plugin->process
Chris@320 707 (buffers, Vamp::RealTime::frame2RealTime(blockFrame, sampleRate));
Chris@320 708
Chris@497 709 if (m_abandoned) break;
Chris@497 710
Chris@850 711 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@930 712 for (int fi = 0; fi < (int)features[m_outputNos[j]].size(); ++fi) {
Chris@850 713 Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi];
Chris@850 714 addFeature(j, blockFrame, feature);
Chris@850 715 }
Chris@850 716 }
Chris@320 717
Chris@320 718 if (blockFrame == contextStart || completion > prevCompletion) {
Chris@850 719 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@850 720 setCompletion(j, completion);
Chris@850 721 }
Chris@320 722 prevCompletion = completion;
Chris@320 723 }
Chris@320 724
Chris@350 725 blockFrame += stepSize;
Chris@320 726 }
Chris@320 727
Chris@497 728 if (!m_abandoned) {
Chris@497 729 Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures();
Chris@320 730
Chris@850 731 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@930 732 for (int fi = 0; fi < (int)features[m_outputNos[j]].size(); ++fi) {
Chris@850 733 Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi];
Chris@850 734 addFeature(j, blockFrame, feature);
Chris@850 735 }
Chris@497 736 }
Chris@497 737 }
Chris@320 738
Chris@850 739 for (int j = 0; j < (int)m_outputNos.size(); ++j) {
Chris@850 740 setCompletion(j, 100);
Chris@850 741 }
Chris@320 742
Chris@320 743 if (frequencyDomain) {
Chris@930 744 for (int ch = 0; ch < channelCount; ++ch) {
Chris@320 745 delete fftModels[ch];
Chris@320 746 }
Chris@556 747 delete[] reals;
Chris@556 748 delete[] imaginaries;
Chris@320 749 }
Chris@974 750
Chris@974 751 for (int ch = 0; ch < channelCount; ++ch) {
Chris@974 752 delete[] buffers[ch];
Chris@974 753 }
Chris@974 754 delete[] buffers;
Chris@320 755 }
Chris@320 756
Chris@320 757 void
Chris@363 758 FeatureExtractionModelTransformer::getFrames(int channelCount,
Chris@363 759 long startFrame, long size,
Chris@363 760 float **buffers)
Chris@320 761 {
Chris@320 762 long offset = 0;
Chris@320 763
Chris@320 764 if (startFrame < 0) {
Chris@363 765 for (int c = 0; c < channelCount; ++c) {
Chris@363 766 for (int i = 0; i < size && startFrame + i < 0; ++i) {
Chris@363 767 buffers[c][i] = 0.0f;
Chris@363 768 }
Chris@320 769 }
Chris@320 770 offset = -startFrame;
Chris@320 771 size -= offset;
Chris@320 772 if (size <= 0) return;
Chris@320 773 startFrame = 0;
Chris@320 774 }
Chris@320 775
Chris@350 776 DenseTimeValueModel *input = getConformingInput();
Chris@350 777 if (!input) return;
Chris@363 778
Chris@363 779 long got = 0;
Chris@350 780
Chris@363 781 if (channelCount == 1) {
Chris@363 782
Chris@363 783 got = input->getData(m_input.getChannel(), startFrame, size,
Chris@363 784 buffers[0] + offset);
Chris@363 785
Chris@363 786 if (m_input.getChannel() == -1 && input->getChannelCount() > 1) {
Chris@363 787 // use mean instead of sum, as plugin input
Chris@363 788 float cc = float(input->getChannelCount());
Chris@363 789 for (long i = 0; i < size; ++i) {
Chris@363 790 buffers[0][i + offset] /= cc;
Chris@363 791 }
Chris@363 792 }
Chris@363 793
Chris@363 794 } else {
Chris@363 795
Chris@363 796 float **writebuf = buffers;
Chris@363 797 if (offset > 0) {
Chris@363 798 writebuf = new float *[channelCount];
Chris@363 799 for (int i = 0; i < channelCount; ++i) {
Chris@363 800 writebuf[i] = buffers[i] + offset;
Chris@363 801 }
Chris@363 802 }
Chris@363 803
Chris@363 804 got = input->getData(0, channelCount-1, startFrame, size, writebuf);
Chris@363 805
Chris@363 806 if (writebuf != buffers) delete[] writebuf;
Chris@363 807 }
Chris@320 808
Chris@320 809 while (got < size) {
Chris@363 810 for (int c = 0; c < channelCount; ++c) {
Chris@363 811 buffers[c][got + offset] = 0.0;
Chris@363 812 }
Chris@320 813 ++got;
Chris@320 814 }
Chris@320 815 }
Chris@320 816
Chris@320 817 void
Chris@850 818 FeatureExtractionModelTransformer::addFeature(int n,
Chris@930 819 int blockFrame,
Chris@850 820 const Vamp::Plugin::Feature &feature)
Chris@320 821 {
Chris@930 822 int inputRate = m_input.getModel()->getSampleRate();
Chris@320 823
Chris@843 824 // cerr << "FeatureExtractionModelTransformer::addFeature: blockFrame = "
Chris@712 825 // << blockFrame << ", hasTimestamp = " << feature.hasTimestamp
Chris@712 826 // << ", timestamp = " << feature.timestamp << ", hasDuration = "
Chris@712 827 // << feature.hasDuration << ", duration = " << feature.duration
Chris@843 828 // << endl;
Chris@320 829
Chris@862 830 int frame = blockFrame;
Chris@320 831
Chris@849 832 if (m_descriptors[n]->sampleType ==
Chris@320 833 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
Chris@320 834
Chris@320 835 if (!feature.hasTimestamp) {
Chris@843 836 cerr
Chris@331 837 << "WARNING: FeatureExtractionModelTransformer::addFeature: "
Chris@320 838 << "Feature has variable sample rate but no timestamp!"
Chris@843 839 << endl;
Chris@320 840 return;
Chris@320 841 } else {
Chris@320 842 frame = Vamp::RealTime::realTime2Frame(feature.timestamp, inputRate);
Chris@320 843 }
Chris@320 844
Chris@849 845 } else if (m_descriptors[n]->sampleType ==
Chris@320 846 Vamp::Plugin::OutputDescriptor::FixedSampleRate) {
Chris@320 847
Chris@779 848 if (!feature.hasTimestamp) {
Chris@849 849 ++m_fixedRateFeatureNos[n];
Chris@779 850 } else {
Chris@779 851 RealTime ts(feature.timestamp.sec, feature.timestamp.nsec);
Chris@849 852 m_fixedRateFeatureNos[n] =
Chris@849 853 lrint(ts.toDouble() * m_descriptors[n]->sampleRate);
Chris@779 854 }
Chris@862 855
Chris@862 856 // cerr << "m_fixedRateFeatureNo = " << m_fixedRateFeatureNo
Chris@862 857 // << ", m_descriptor->sampleRate = " << m_descriptor->sampleRate
Chris@862 858 // << ", inputRate = " << inputRate
Chris@862 859 // << " giving frame = ";
Chris@849 860 frame = lrintf((m_fixedRateFeatureNos[n] / m_descriptors[n]->sampleRate)
Chris@862 861 * int(inputRate));
Chris@320 862 }
Chris@862 863
Chris@862 864 if (frame < 0) {
Chris@862 865 cerr
Chris@862 866 << "WARNING: FeatureExtractionModelTransformer::addFeature: "
Chris@862 867 << "Negative frame counts are not supported (frame = " << frame
Chris@862 868 << " from timestamp " << feature.timestamp
Chris@862 869 << "), dropping feature"
Chris@862 870 << endl;
Chris@862 871 return;
Chris@862 872 }
Chris@862 873
Chris@441 874 // Rather than repeat the complicated tests from the constructor
Chris@441 875 // to determine what sort of model we must be adding the features
Chris@441 876 // to, we instead test what sort of model the constructor decided
Chris@441 877 // to create.
Chris@320 878
Chris@849 879 if (isOutput<SparseOneDimensionalModel>(n)) {
Chris@441 880
Chris@441 881 SparseOneDimensionalModel *model =
Chris@849 882 getConformingOutput<SparseOneDimensionalModel>(n);
Chris@320 883 if (!model) return;
Chris@350 884
Chris@441 885 model->addPoint(SparseOneDimensionalModel::Point
Chris@441 886 (frame, feature.label.c_str()));
Chris@320 887
Chris@849 888 } else if (isOutput<SparseTimeValueModel>(n)) {
Chris@320 889
Chris@350 890 SparseTimeValueModel *model =
Chris@849 891 getConformingOutput<SparseTimeValueModel>(n);
Chris@320 892 if (!model) return;
Chris@350 893
Chris@930 894 for (int i = 0; i < (int)feature.values.size(); ++i) {
Chris@454 895
Chris@454 896 float value = feature.values[i];
Chris@454 897
Chris@454 898 QString label = feature.label.c_str();
Chris@454 899 if (feature.values.size() > 1) {
Chris@454 900 label = QString("[%1] %2").arg(i+1).arg(label);
Chris@454 901 }
Chris@454 902
Chris@876 903 SparseTimeValueModel *targetModel = model;
Chris@876 904
Chris@876 905 if (m_needAdditionalModels[n] && i > 0) {
Chris@876 906 targetModel = getAdditionalModel(n, i);
Chris@876 907 if (!targetModel) targetModel = model;
Chris@893 908 // std::cerr << "adding point to model " << targetModel
Chris@893 909 // << " for output " << n << " bin " << i << std::endl;
Chris@876 910 }
Chris@876 911
Chris@876 912 targetModel->addPoint
Chris@876 913 (SparseTimeValueModel::Point(frame, value, label));
Chris@454 914 }
Chris@320 915
Chris@849 916 } else if (isOutput<FlexiNoteModel>(n) || isOutput<NoteModel>(n) || isOutput<RegionModel>(n)) { //GF: Added Note Model
Chris@320 917
Chris@441 918 int index = 0;
Chris@441 919
Chris@441 920 float value = 0.0;
Chris@930 921 if ((int)feature.values.size() > index) {
Chris@441 922 value = feature.values[index++];
Chris@441 923 }
Chris@320 924
Chris@320 925 float duration = 1;
Chris@441 926 if (feature.hasDuration) {
Chris@441 927 duration = Vamp::RealTime::realTime2Frame(feature.duration, inputRate);
Chris@441 928 } else {
Chris@930 929 if ((int)feature.values.size() > index) {
Chris@441 930 duration = feature.values[index++];
Chris@441 931 }
Chris@441 932 }
gyorgyf@786 933
Chris@891 934 if (isOutput<FlexiNoteModel>(n)) { // GF: added for flexi note model
gyorgyf@786 935
gyorgyf@786 936 float velocity = 100;
Chris@930 937 if ((int)feature.values.size() > index) {
gyorgyf@786 938 velocity = feature.values[index++];
gyorgyf@786 939 }
gyorgyf@786 940 if (velocity < 0) velocity = 127;
gyorgyf@786 941 if (velocity > 127) velocity = 127;
gyorgyf@786 942
Chris@849 943 FlexiNoteModel *model = getConformingOutput<FlexiNoteModel>(n);
gyorgyf@786 944 if (!model) return;
gyorgyf@786 945 model->addPoint(FlexiNoteModel::Point(frame, value, // value is pitch
gyorgyf@786 946 lrintf(duration),
gyorgyf@786 947 velocity / 127.f,
gyorgyf@786 948 feature.label.c_str()));
gyorgyf@786 949 // GF: end -- added for flexi note model
Chris@849 950 } else if (isOutput<NoteModel>(n)) {
Chris@320 951
Chris@441 952 float velocity = 100;
Chris@930 953 if ((int)feature.values.size() > index) {
Chris@441 954 velocity = feature.values[index++];
Chris@441 955 }
Chris@441 956 if (velocity < 0) velocity = 127;
Chris@441 957 if (velocity > 127) velocity = 127;
Chris@320 958
Chris@849 959 NoteModel *model = getConformingOutput<NoteModel>(n);
Chris@441 960 if (!model) return;
Chris@441 961 model->addPoint(NoteModel::Point(frame, value, // value is pitch
Chris@441 962 lrintf(duration),
Chris@441 963 velocity / 127.f,
Chris@441 964 feature.label.c_str()));
Chris@441 965 } else {
gyorgyf@786 966
Chris@849 967 RegionModel *model = getConformingOutput<RegionModel>(n);
Chris@454 968 if (!model) return;
Chris@454 969
Chris@474 970 if (feature.hasDuration && !feature.values.empty()) {
Chris@454 971
Chris@930 972 for (int i = 0; i < (int)feature.values.size(); ++i) {
Chris@454 973
Chris@454 974 float value = feature.values[i];
Chris@454 975
Chris@454 976 QString label = feature.label.c_str();
Chris@454 977 if (feature.values.size() > 1) {
Chris@454 978 label = QString("[%1] %2").arg(i+1).arg(label);
Chris@454 979 }
Chris@454 980
Chris@454 981 model->addPoint(RegionModel::Point(frame, value,
Chris@454 982 lrintf(duration),
Chris@454 983 label));
Chris@454 984 }
Chris@454 985 } else {
Chris@454 986
Chris@441 987 model->addPoint(RegionModel::Point(frame, value,
Chris@441 988 lrintf(duration),
Chris@441 989 feature.label.c_str()));
Chris@454 990 }
Chris@441 991 }
Chris@320 992
Chris@849 993 } else if (isOutput<EditableDenseThreeDimensionalModel>(n)) {
Chris@320 994
Chris@533 995 DenseThreeDimensionalModel::Column values =
Chris@533 996 DenseThreeDimensionalModel::Column::fromStdVector(feature.values);
Chris@320 997
Chris@320 998 EditableDenseThreeDimensionalModel *model =
Chris@849 999 getConformingOutput<EditableDenseThreeDimensionalModel>(n);
Chris@320 1000 if (!model) return;
Chris@320 1001
Chris@889 1002 // cerr << "(note: model resolution = " << model->getResolution() << ")"
Chris@889 1003 // << endl;
Chris@889 1004
Chris@891 1005 if (!feature.hasTimestamp && m_fixedRateFeatureNos[n] >= 0) {
Chris@891 1006 model->setColumn(m_fixedRateFeatureNos[n], values);
Chris@889 1007 } else {
Chris@889 1008 model->setColumn(frame / model->getResolution(), values);
Chris@889 1009 }
Chris@441 1010
Chris@441 1011 } else {
Chris@690 1012 SVDEBUG << "FeatureExtractionModelTransformer::addFeature: Unknown output model type!" << endl;
Chris@320 1013 }
Chris@320 1014 }
Chris@320 1015
Chris@320 1016 void
Chris@850 1017 FeatureExtractionModelTransformer::setCompletion(int n, int completion)
Chris@320 1018 {
Chris@690 1019 // SVDEBUG << "FeatureExtractionModelTransformer::setCompletion("
Chris@687 1020 // << completion << ")" << endl;
Chris@320 1021
Chris@849 1022 if (isOutput<SparseOneDimensionalModel>(n)) {
Chris@320 1023
Chris@350 1024 SparseOneDimensionalModel *model =
Chris@849 1025 getConformingOutput<SparseOneDimensionalModel>(n);
Chris@320 1026 if (!model) return;
Chris@923 1027 if (model->isAbandoning()) abandon();
Chris@441 1028 model->setCompletion(completion, true);
Chris@320 1029
Chris@849 1030 } else if (isOutput<SparseTimeValueModel>(n)) {
Chris@320 1031
Chris@350 1032 SparseTimeValueModel *model =
Chris@849 1033 getConformingOutput<SparseTimeValueModel>(n);
Chris@320 1034 if (!model) return;
Chris@923 1035 if (model->isAbandoning()) abandon();
Chris@441 1036 model->setCompletion(completion, true);
Chris@320 1037
Chris@849 1038 } else if (isOutput<NoteModel>(n)) {
Chris@320 1039
Chris@849 1040 NoteModel *model = getConformingOutput<NoteModel>(n);
Chris@320 1041 if (!model) return;
Chris@923 1042 if (model->isAbandoning()) abandon();
Chris@441 1043 model->setCompletion(completion, true);
gyorgyf@786 1044
Chris@923 1045 } else if (isOutput<FlexiNoteModel>(n)) {
gyorgyf@786 1046
Chris@849 1047 FlexiNoteModel *model = getConformingOutput<FlexiNoteModel>(n);
gyorgyf@786 1048 if (!model) return;
Chris@923 1049 if (model->isAbandoning()) abandon();
gyorgyf@786 1050 model->setCompletion(completion, true);
Chris@320 1051
Chris@849 1052 } else if (isOutput<RegionModel>(n)) {
Chris@441 1053
Chris@849 1054 RegionModel *model = getConformingOutput<RegionModel>(n);
Chris@441 1055 if (!model) return;
Chris@923 1056 if (model->isAbandoning()) abandon();
Chris@441 1057 model->setCompletion(completion, true);
Chris@441 1058
Chris@849 1059 } else if (isOutput<EditableDenseThreeDimensionalModel>(n)) {
Chris@320 1060
Chris@320 1061 EditableDenseThreeDimensionalModel *model =
Chris@849 1062 getConformingOutput<EditableDenseThreeDimensionalModel>(n);
Chris@320 1063 if (!model) return;
Chris@923 1064 if (model->isAbandoning()) abandon();
Chris@350 1065 model->setCompletion(completion, true); //!!!m_context.updates);
Chris@320 1066 }
Chris@320 1067 }
Chris@320 1068