annotate transform/FeatureExtractionModelTransformer.cpp @ 1290:fa574c909c3d 3.0-integration

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