annotate transform/FeatureExtractionPluginTransform.cpp @ 117:c30728d5625c sv1-v0.9rc1

* Make vertical scale alignment modes work in note layer as well as time-value layer, and several significant fixes to it * Make it possible to draw notes properly on the note layer * Show units (and frequencies etc in note layer's case) in the time-value and note layer description boxes * Minor fix to item edit dialog layout * Some minor menu rearrangement * Comment out a lot of debug output * Add SV website and reference URLs to Help menu, and add code to (attempt to) open them in the user's preferred browser
author Chris Cannam
date Fri, 12 May 2006 14:40:43 +0000
parents 90ade4fa63be
children 4170b21773cf
rev   line source
Chris@0 1
Chris@49 2 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 3
Chris@0 4 /*
Chris@52 5 Sonic Visualiser
Chris@52 6 An audio file viewer and annotation editor.
Chris@52 7 Centre for Digital Music, Queen Mary, University of London.
Chris@52 8 This file copyright 2006 Chris Cannam.
Chris@0 9
Chris@52 10 This program is free software; you can redistribute it and/or
Chris@52 11 modify it under the terms of the GNU General Public License as
Chris@52 12 published by the Free Software Foundation; either version 2 of the
Chris@52 13 License, or (at your option) any later version. See the file
Chris@52 14 COPYING included with this distribution for more information.
Chris@0 15 */
Chris@0 16
Chris@0 17 #include "FeatureExtractionPluginTransform.h"
Chris@0 18
Chris@0 19 #include "plugin/FeatureExtractionPluginFactory.h"
Chris@66 20 #include "plugin/PluginXml.h"
Chris@66 21 #include "vamp-sdk/Plugin.h"
Chris@0 22
Chris@0 23 #include "base/Model.h"
Chris@67 24 #include "base/Window.h"
Chris@0 25 #include "model/SparseOneDimensionalModel.h"
Chris@0 26 #include "model/SparseTimeValueModel.h"
Chris@0 27 #include "model/DenseThreeDimensionalModel.h"
Chris@0 28 #include "model/DenseTimeValueModel.h"
Chris@115 29 #include "model/NoteModel.h"
Chris@0 30
Chris@67 31 #include <fftw3.h>
Chris@67 32
Chris@0 33 #include <iostream>
Chris@0 34
Chris@0 35 FeatureExtractionPluginTransform::FeatureExtractionPluginTransform(Model *inputModel,
Chris@0 36 QString pluginId,
Chris@64 37 int channel,
Chris@56 38 QString configurationXml,
Chris@0 39 QString outputName) :
Chris@0 40 Transform(inputModel),
Chris@0 41 m_plugin(0),
Chris@64 42 m_channel(channel),
Chris@68 43 m_stepSize(0),
Chris@68 44 m_blockSize(0),
Chris@0 45 m_descriptor(0),
Chris@0 46 m_outputFeatureNo(0)
Chris@0 47 {
Chris@117 48 // std::cerr << "FeatureExtractionPluginTransform::FeatureExtractionPluginTransform: plugin " << pluginId.toStdString() << ", outputName " << outputName.toStdString() << std::endl;
Chris@0 49
Chris@0 50 FeatureExtractionPluginFactory *factory =
Chris@0 51 FeatureExtractionPluginFactory::instanceFor(pluginId);
Chris@0 52
Chris@0 53 if (!factory) {
Chris@0 54 std::cerr << "FeatureExtractionPluginTransform: No factory available for plugin id \""
Chris@0 55 << pluginId.toStdString() << "\"" << std::endl;
Chris@0 56 return;
Chris@0 57 }
Chris@0 58
Chris@0 59 m_plugin = factory->instantiatePlugin(pluginId, m_input->getSampleRate());
Chris@0 60
Chris@0 61 if (!m_plugin) {
Chris@0 62 std::cerr << "FeatureExtractionPluginTransform: Failed to instantiate plugin \""
Chris@0 63 << pluginId.toStdString() << "\"" << std::endl;
Chris@0 64 return;
Chris@0 65 }
Chris@0 66
Chris@56 67 if (configurationXml != "") {
Chris@66 68 PluginXml(m_plugin).setParametersFromXml(configurationXml);
Chris@56 69 }
Chris@56 70
Chris@68 71 m_blockSize = m_plugin->getPreferredBlockSize();
Chris@68 72 m_stepSize = m_plugin->getPreferredStepSize();
Chris@68 73
Chris@68 74 if (m_blockSize == 0) m_blockSize = 1024; //!!! todo: ask user
Chris@68 75 if (m_stepSize == 0) m_stepSize = m_blockSize; //!!! likewise
Chris@68 76
Chris@114 77 DenseTimeValueModel *input = getInput();
Chris@114 78 if (!input) return;
Chris@114 79
Chris@114 80 size_t channelCount = input->getChannelCount();
Chris@114 81 if (m_plugin->getMaxChannelCount() < channelCount) {
Chris@114 82 channelCount = 1;
Chris@114 83 }
Chris@114 84 if (m_plugin->getMinChannelCount() > channelCount) {
Chris@114 85 std::cerr << "FeatureExtractionPluginTransform:: "
Chris@114 86 << "Can't provide enough channels to plugin (plugin min "
Chris@114 87 << m_plugin->getMinChannelCount() << ", max "
Chris@114 88 << m_plugin->getMaxChannelCount() << ", input model has "
Chris@114 89 << input->getChannelCount() << ")" << std::endl;
Chris@114 90 return;
Chris@114 91 }
Chris@114 92
Chris@114 93 if (!m_plugin->initialise(channelCount, m_stepSize, m_blockSize)) {
Chris@114 94 std::cerr << "FeatureExtractionPluginTransform: Plugin "
Chris@114 95 << m_plugin->getName() << " failed to initialise!" << std::endl;
Chris@114 96 return;
Chris@114 97 }
Chris@114 98
Chris@74 99 //!!! cope with plugins that request non-power-of-2 block sizes in
Chris@74 100 // the frequency domain!
Chris@74 101
Chris@74 102 Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors();
Chris@0 103
Chris@0 104 if (outputs.empty()) {
Chris@0 105 std::cerr << "FeatureExtractionPluginTransform: Plugin \""
Chris@0 106 << pluginId.toStdString() << "\" has no outputs" << std::endl;
Chris@0 107 return;
Chris@0 108 }
Chris@0 109
Chris@0 110 for (size_t i = 0; i < outputs.size(); ++i) {
Chris@0 111 if (outputName == "" || outputs[i].name == outputName.toStdString()) {
Chris@0 112 m_outputFeatureNo = i;
Chris@66 113 m_descriptor = new Vamp::Plugin::OutputDescriptor
Chris@0 114 (outputs[i]);
Chris@0 115 break;
Chris@0 116 }
Chris@0 117 }
Chris@0 118
Chris@0 119 if (!m_descriptor) {
Chris@0 120 std::cerr << "FeatureExtractionPluginTransform: Plugin \""
Chris@0 121 << pluginId.toStdString() << "\" has no output named \""
Chris@0 122 << outputName.toStdString() << "\"" << std::endl;
Chris@0 123 return;
Chris@0 124 }
Chris@0 125
Chris@117 126 // std::cerr << "FeatureExtractionPluginTransform: output sample type "
Chris@117 127 // << m_descriptor->sampleType << std::endl;
Chris@0 128
Chris@70 129 int binCount = 1;
Chris@0 130 float minValue = 0.0, maxValue = 0.0;
Chris@0 131
Chris@70 132 if (m_descriptor->hasFixedBinCount) {
Chris@70 133 binCount = m_descriptor->binCount;
Chris@0 134 }
Chris@0 135
Chris@117 136 // std::cerr << "FeatureExtractionPluginTransform: output bin count "
Chris@117 137 // << binCount << std::endl;
Chris@114 138
Chris@70 139 if (binCount > 0 && m_descriptor->hasKnownExtents) {
Chris@0 140 minValue = m_descriptor->minValue;
Chris@0 141 maxValue = m_descriptor->maxValue;
Chris@0 142 }
Chris@0 143
Chris@0 144 size_t modelRate = m_input->getSampleRate();
Chris@0 145 size_t modelResolution = 1;
Chris@0 146
Chris@0 147 switch (m_descriptor->sampleType) {
Chris@0 148
Chris@66 149 case Vamp::Plugin::OutputDescriptor::VariableSampleRate:
Chris@0 150 if (m_descriptor->sampleRate != 0.0) {
Chris@0 151 modelResolution = size_t(modelRate / m_descriptor->sampleRate + 0.001);
Chris@0 152 }
Chris@0 153 break;
Chris@0 154
Chris@66 155 case Vamp::Plugin::OutputDescriptor::OneSamplePerStep:
Chris@72 156 modelResolution = m_stepSize;
Chris@0 157 break;
Chris@0 158
Chris@66 159 case Vamp::Plugin::OutputDescriptor::FixedSampleRate:
Chris@73 160 modelRate = size_t(m_descriptor->sampleRate + 0.001);
Chris@0 161 break;
Chris@0 162 }
Chris@0 163
Chris@70 164 if (binCount == 0) {
Chris@0 165
Chris@20 166 m_output = new SparseOneDimensionalModel(modelRate, modelResolution,
Chris@20 167 false);
Chris@0 168
Chris@115 169 } else if (binCount == 1) {
Chris@0 170
Chris@115 171 SparseTimeValueModel *model = new SparseTimeValueModel
Chris@115 172 (modelRate, modelResolution, minValue, maxValue, false);
Chris@115 173 model->setScaleUnits(outputs[m_outputFeatureNo].unit.c_str());
Chris@115 174
Chris@115 175 m_output = model;
Chris@115 176
Chris@115 177 } else if (m_descriptor->sampleType ==
Chris@66 178 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
Chris@115 179
Chris@115 180 // We don't have a sparse 3D model, so interpret this as a
Chris@115 181 // note model. There's nothing to define which values to use
Chris@115 182 // as which parameters of the note -- for the moment let's
Chris@115 183 // treat the first as pitch, second as duration in frames,
Chris@115 184 // third (if present) as velocity. (Our note model doesn't
Chris@115 185 // yet store velocity.)
Chris@115 186 //!!! todo: ask the user!
Chris@0 187
Chris@115 188 NoteModel *model = new NoteModel
Chris@63 189 (modelRate, modelResolution, minValue, maxValue, false);
Chris@63 190 model->setScaleUnits(outputs[m_outputFeatureNo].unit.c_str());
Chris@63 191
Chris@63 192 m_output = model;
Chris@0 193
Chris@0 194 } else {
Chris@0 195
Chris@0 196 m_output = new DenseThreeDimensionalModel(modelRate, modelResolution,
Chris@70 197 binCount, false);
Chris@20 198
Chris@70 199 if (!m_descriptor->binNames.empty()) {
Chris@20 200 std::vector<QString> names;
Chris@70 201 for (size_t i = 0; i < m_descriptor->binNames.size(); ++i) {
Chris@70 202 names.push_back(m_descriptor->binNames[i].c_str());
Chris@20 203 }
Chris@20 204 (dynamic_cast<DenseThreeDimensionalModel *>(m_output))
Chris@20 205 ->setBinNames(names);
Chris@20 206 }
Chris@0 207 }
Chris@0 208 }
Chris@0 209
Chris@0 210 FeatureExtractionPluginTransform::~FeatureExtractionPluginTransform()
Chris@0 211 {
Chris@0 212 delete m_plugin;
Chris@0 213 delete m_descriptor;
Chris@0 214 }
Chris@0 215
Chris@0 216 DenseTimeValueModel *
Chris@0 217 FeatureExtractionPluginTransform::getInput()
Chris@0 218 {
Chris@0 219 DenseTimeValueModel *dtvm =
Chris@0 220 dynamic_cast<DenseTimeValueModel *>(getInputModel());
Chris@0 221 if (!dtvm) {
Chris@0 222 std::cerr << "FeatureExtractionPluginTransform::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl;
Chris@0 223 }
Chris@0 224 return dtvm;
Chris@0 225 }
Chris@0 226
Chris@0 227 void
Chris@0 228 FeatureExtractionPluginTransform::run()
Chris@0 229 {
Chris@0 230 DenseTimeValueModel *input = getInput();
Chris@0 231 if (!input) return;
Chris@0 232
Chris@0 233 if (!m_output) return;
Chris@0 234
Chris@114 235 size_t sampleRate = m_input->getSampleRate();
Chris@114 236
Chris@0 237 size_t channelCount = input->getChannelCount();
Chris@0 238 if (m_plugin->getMaxChannelCount() < channelCount) {
Chris@0 239 channelCount = 1;
Chris@0 240 }
Chris@0 241
Chris@0 242 float **buffers = new float*[channelCount];
Chris@0 243 for (size_t ch = 0; ch < channelCount; ++ch) {
Chris@68 244 buffers[ch] = new float[m_blockSize];
Chris@0 245 }
Chris@0 246
Chris@67 247 double *fftInput = 0;
Chris@67 248 fftw_complex *fftOutput = 0;
Chris@67 249 fftw_plan fftPlan = 0;
Chris@68 250 Window<double> windower(HanningWindow, m_blockSize);
Chris@67 251
Chris@67 252 if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
Chris@67 253
Chris@68 254 fftInput = (double *)fftw_malloc(m_blockSize * sizeof(double));
Chris@68 255 fftOutput = (fftw_complex *)fftw_malloc(m_blockSize * sizeof(fftw_complex));
Chris@68 256 fftPlan = fftw_plan_dft_r2c_1d(m_blockSize, fftInput, fftOutput,
Chris@67 257 FFTW_ESTIMATE);
Chris@67 258 if (!fftPlan) {
Chris@67 259 std::cerr << "ERROR: FeatureExtractionPluginTransform::run(): fftw_plan failed! Results will be garbage" << std::endl;
Chris@67 260 }
Chris@67 261 }
Chris@67 262
Chris@70 263 long startFrame = m_input->getStartFrame();
Chris@70 264 long endFrame = m_input->getEndFrame();
Chris@70 265 long blockFrame = startFrame;
Chris@0 266
Chris@70 267 long prevCompletion = 0;
Chris@0 268
Chris@70 269 while (1) {
Chris@70 270
Chris@70 271 if (fftPlan) {
Chris@73 272 if (blockFrame - int(m_blockSize)/2 > endFrame) break;
Chris@70 273 } else {
Chris@70 274 if (blockFrame >= endFrame) break;
Chris@70 275 }
Chris@0 276
Chris@0 277 // std::cerr << "FeatureExtractionPluginTransform::run: blockFrame "
Chris@0 278 // << blockFrame << std::endl;
Chris@0 279
Chris@70 280 long completion =
Chris@68 281 (((blockFrame - startFrame) / m_stepSize) * 99) /
Chris@68 282 ( (endFrame - startFrame) / m_stepSize);
Chris@0 283
Chris@0 284 // channelCount is either m_input->channelCount or 1
Chris@0 285
Chris@73 286 for (size_t ch = 0; ch < channelCount; ++ch) {
Chris@70 287 if (fftPlan) {
Chris@70 288 getFrames(ch, channelCount,
Chris@70 289 blockFrame - m_blockSize/2, m_blockSize, buffers[ch]);
Chris@70 290 } else {
Chris@70 291 getFrames(ch, channelCount,
Chris@70 292 blockFrame, m_blockSize, buffers[ch]);
Chris@70 293 }
Chris@70 294 }
Chris@70 295
Chris@67 296 if (fftPlan) {
Chris@73 297 for (size_t ch = 0; ch < channelCount; ++ch) {
Chris@73 298 for (size_t i = 0; i < m_blockSize; ++i) {
Chris@67 299 fftInput[i] = buffers[ch][i];
Chris@67 300 }
Chris@67 301 windower.cut(fftInput);
Chris@73 302 for (size_t i = 0; i < m_blockSize/2; ++i) {
Chris@67 303 double temp = fftInput[i];
Chris@68 304 fftInput[i] = fftInput[i + m_blockSize/2];
Chris@68 305 fftInput[i + m_blockSize/2] = temp;
Chris@67 306 }
Chris@67 307 fftw_execute(fftPlan);
Chris@73 308 for (size_t i = 0; i < m_blockSize/2; ++i) {
Chris@67 309 buffers[ch][i*2] = fftOutput[i][0];
Chris@67 310 buffers[ch][i*2 + 1] = fftOutput[i][1];
Chris@67 311 }
Chris@67 312 }
Chris@67 313 }
Chris@67 314
Chris@66 315 Vamp::Plugin::FeatureSet features = m_plugin->process
Chris@66 316 (buffers, Vamp::RealTime::frame2RealTime(blockFrame, sampleRate));
Chris@0 317
Chris@0 318 for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) {
Chris@66 319 Vamp::Plugin::Feature feature =
Chris@0 320 features[m_outputFeatureNo][fi];
Chris@0 321 addFeature(blockFrame, feature);
Chris@0 322 }
Chris@0 323
Chris@0 324 if (blockFrame == startFrame || completion > prevCompletion) {
Chris@0 325 setCompletion(completion);
Chris@0 326 prevCompletion = completion;
Chris@0 327 }
Chris@0 328
Chris@68 329 blockFrame += m_stepSize;
Chris@0 330 }
Chris@0 331
Chris@67 332 if (fftPlan) {
Chris@67 333 fftw_destroy_plan(fftPlan);
Chris@67 334 fftw_free(fftInput);
Chris@67 335 fftw_free(fftOutput);
Chris@67 336 }
Chris@67 337
Chris@66 338 Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures();
Chris@0 339
Chris@0 340 for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) {
Chris@66 341 Vamp::Plugin::Feature feature =
Chris@0 342 features[m_outputFeatureNo][fi];
Chris@0 343 addFeature(blockFrame, feature);
Chris@0 344 }
Chris@0 345
Chris@0 346 setCompletion(100);
Chris@0 347 }
Chris@0 348
Chris@70 349 void
Chris@70 350 FeatureExtractionPluginTransform::getFrames(int channel, int channelCount,
Chris@70 351 long startFrame, long size,
Chris@70 352 float *buffer)
Chris@70 353 {
Chris@70 354 long offset = 0;
Chris@70 355
Chris@70 356 if (startFrame < 0) {
Chris@70 357 for (int i = 0; i < size && startFrame + i < 0; ++i) {
Chris@70 358 buffer[i] = 0.0f;
Chris@70 359 }
Chris@70 360 offset = -startFrame;
Chris@70 361 size -= offset;
Chris@70 362 if (size <= 0) return;
Chris@70 363 startFrame = 0;
Chris@70 364 }
Chris@70 365
Chris@73 366 long got = getInput()->getValues
Chris@70 367 ((channelCount == 1 ? m_channel : channel),
Chris@70 368 startFrame, startFrame + size, buffer + offset);
Chris@70 369
Chris@70 370 while (got < size) {
Chris@70 371 buffer[offset + got] = 0.0;
Chris@70 372 ++got;
Chris@70 373 }
Chris@74 374
Chris@74 375 if (m_channel == -1 && channelCount == 1 &&
Chris@74 376 getInput()->getChannelCount() > 1) {
Chris@74 377 // use mean instead of sum, as plugin input
Chris@74 378 int cc = getInput()->getChannelCount();
Chris@74 379 for (long i = 0; i < size; ++i) {
Chris@74 380 buffer[i] /= cc;
Chris@74 381 }
Chris@74 382 }
Chris@70 383 }
Chris@0 384
Chris@0 385 void
Chris@0 386 FeatureExtractionPluginTransform::addFeature(size_t blockFrame,
Chris@66 387 const Vamp::Plugin::Feature &feature)
Chris@0 388 {
Chris@0 389 size_t inputRate = m_input->getSampleRate();
Chris@0 390
Chris@0 391 // std::cerr << "FeatureExtractionPluginTransform::addFeature("
Chris@0 392 // << blockFrame << ")" << std::endl;
Chris@0 393
Chris@70 394 int binCount = 1;
Chris@70 395 if (m_descriptor->hasFixedBinCount) {
Chris@70 396 binCount = m_descriptor->binCount;
Chris@0 397 }
Chris@0 398
Chris@0 399 size_t frame = blockFrame;
Chris@0 400
Chris@0 401 if (m_descriptor->sampleType ==
Chris@66 402 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
Chris@0 403
Chris@0 404 if (!feature.hasTimestamp) {
Chris@0 405 std::cerr
Chris@0 406 << "WARNING: FeatureExtractionPluginTransform::addFeature: "
Chris@0 407 << "Feature has variable sample rate but no timestamp!"
Chris@0 408 << std::endl;
Chris@0 409 return;
Chris@0 410 } else {
Chris@66 411 frame = Vamp::RealTime::realTime2Frame(feature.timestamp, inputRate);
Chris@0 412 }
Chris@0 413
Chris@0 414 } else if (m_descriptor->sampleType ==
Chris@66 415 Vamp::Plugin::OutputDescriptor::FixedSampleRate) {
Chris@0 416
Chris@0 417 if (feature.hasTimestamp) {
Chris@0 418 //!!! warning: sampleRate may be non-integral
Chris@66 419 frame = Vamp::RealTime::realTime2Frame(feature.timestamp,
Chris@66 420 m_descriptor->sampleRate);
Chris@0 421 } else {
Chris@0 422 frame = m_output->getEndFrame() + 1;
Chris@0 423 }
Chris@0 424 }
Chris@0 425
Chris@70 426 if (binCount == 0) {
Chris@0 427
Chris@0 428 SparseOneDimensionalModel *model = getOutput<SparseOneDimensionalModel>();
Chris@0 429 if (!model) return;
Chris@0 430 model->addPoint(SparseOneDimensionalModel::Point(frame, feature.label.c_str()));
Chris@0 431
Chris@115 432 } else if (binCount == 1) {
Chris@0 433
Chris@0 434 float value = 0.0;
Chris@0 435 if (feature.values.size() > 0) value = feature.values[0];
Chris@0 436
Chris@0 437 SparseTimeValueModel *model = getOutput<SparseTimeValueModel>();
Chris@0 438 if (!model) return;
Chris@0 439 model->addPoint(SparseTimeValueModel::Point(frame, value, feature.label.c_str()));
Chris@115 440
Chris@115 441 } else if (m_descriptor->sampleType ==
Chris@115 442 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
Chris@115 443
Chris@115 444 float pitch = 0.0;
Chris@115 445 if (feature.values.size() > 0) pitch = feature.values[0];
Chris@115 446
Chris@115 447 float duration = 1;
Chris@115 448 if (feature.values.size() > 1) duration = feature.values[1];
Chris@115 449
Chris@115 450 float velocity = 100;
Chris@115 451 if (feature.values.size() > 2) velocity = feature.values[2];
Chris@115 452
Chris@115 453 NoteModel *model = getOutput<NoteModel>();
Chris@115 454 if (!model) return;
Chris@115 455
Chris@115 456 model->addPoint(NoteModel::Point(frame, pitch, duration, feature.label.c_str()));
Chris@0 457
Chris@0 458 } else {
Chris@0 459
Chris@0 460 DenseThreeDimensionalModel::BinValueSet values = feature.values;
Chris@0 461
Chris@0 462 DenseThreeDimensionalModel *model = getOutput<DenseThreeDimensionalModel>();
Chris@0 463 if (!model) return;
Chris@0 464
Chris@0 465 model->setBinValues(frame, values);
Chris@0 466 }
Chris@0 467 }
Chris@0 468
Chris@0 469 void
Chris@0 470 FeatureExtractionPluginTransform::setCompletion(int completion)
Chris@0 471 {
Chris@70 472 int binCount = 1;
Chris@70 473 if (m_descriptor->hasFixedBinCount) {
Chris@70 474 binCount = m_descriptor->binCount;
Chris@0 475 }
Chris@0 476
Chris@70 477 if (binCount == 0) {
Chris@0 478
Chris@0 479 SparseOneDimensionalModel *model = getOutput<SparseOneDimensionalModel>();
Chris@0 480 if (!model) return;
Chris@0 481 model->setCompletion(completion);
Chris@0 482
Chris@115 483 } else if (binCount == 1) {
Chris@115 484
Chris@115 485 SparseTimeValueModel *model = getOutput<SparseTimeValueModel>();
Chris@115 486 if (!model) return;
Chris@115 487 model->setCompletion(completion);
Chris@115 488
Chris@115 489 } else if (m_descriptor->sampleType ==
Chris@66 490 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
Chris@0 491
Chris@115 492 NoteModel *model = getOutput<NoteModel>();
Chris@0 493 if (!model) return;
Chris@0 494 model->setCompletion(completion);
Chris@0 495
Chris@0 496 } else {
Chris@0 497
Chris@19 498 DenseThreeDimensionalModel *model = getOutput<DenseThreeDimensionalModel>();
Chris@19 499 if (!model) return;
Chris@19 500 model->setCompletion(completion);
Chris@0 501 }
Chris@0 502 }
Chris@0 503