annotate transform/FeatureExtractionModelTransformer.cpp @ 392:183ee2a55fc7

* More work to abstract out interactive components used in the data library, so that it does not need to depend on QtGui.
author Chris Cannam
date Fri, 14 Mar 2008 17:14:21 +0000
parents 370aa9714ef5
children 115f60df1e4d
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@320 20 #include "vamp-sdk/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"
Chris@320 30 #include "data/model/FFTModel.h"
Chris@320 31 #include "data/model/WaveFileModel.h"
Chris@320 32
Chris@350 33 #include "TransformFactory.h"
Chris@350 34
Chris@320 35 #include <iostream>
Chris@320 36
Chris@350 37 FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Input in,
Chris@350 38 const Transform &transform) :
Chris@350 39 ModelTransformer(in, transform),
Chris@320 40 m_plugin(0),
Chris@320 41 m_descriptor(0),
Chris@320 42 m_outputFeatureNo(0)
Chris@320 43 {
Chris@350 44 // std::cerr << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << pluginId.toStdString() << ", outputName " << m_transform.getOutput().toStdString() << std::endl;
Chris@350 45
Chris@350 46 QString pluginId = transform.getPluginIdentifier();
Chris@320 47
Chris@320 48 FeatureExtractionPluginFactory *factory =
Chris@320 49 FeatureExtractionPluginFactory::instanceFor(pluginId);
Chris@320 50
Chris@320 51 if (!factory) {
Chris@361 52 m_message = tr("No factory available for feature extraction plugin id \"%1\" (unknown plugin type, or internal error?)").arg(pluginId);
Chris@320 53 return;
Chris@320 54 }
Chris@320 55
Chris@350 56 DenseTimeValueModel *input = getConformingInput();
Chris@350 57 if (!input) {
Chris@361 58 m_message = tr("Input model for feature extraction plugin \"%1\" is of wrong type (internal error?)").arg(pluginId);
Chris@350 59 return;
Chris@350 60 }
Chris@320 61
Chris@350 62 m_plugin = factory->instantiatePlugin(pluginId, input->getSampleRate());
Chris@320 63 if (!m_plugin) {
Chris@361 64 m_message = tr("Failed to instantiate plugin \"%1\"").arg(pluginId);
Chris@320 65 return;
Chris@320 66 }
Chris@320 67
Chris@350 68 TransformFactory::getInstance()->makeContextConsistentWithPlugin
Chris@350 69 (m_transform, m_plugin);
Chris@343 70
Chris@350 71 TransformFactory::getInstance()->setPluginParameters
Chris@350 72 (m_transform, m_plugin);
Chris@320 73
Chris@320 74 size_t channelCount = input->getChannelCount();
Chris@320 75 if (m_plugin->getMaxChannelCount() < channelCount) {
Chris@320 76 channelCount = 1;
Chris@320 77 }
Chris@320 78 if (m_plugin->getMinChannelCount() > channelCount) {
Chris@361 79 m_message = tr("Cannot provide enough channels to feature extraction plugin \"%1\" (plugin min is %2, max %3; input model has %4)")
Chris@361 80 .arg(pluginId)
Chris@361 81 .arg(m_plugin->getMinChannelCount())
Chris@361 82 .arg(m_plugin->getMaxChannelCount())
Chris@361 83 .arg(input->getChannelCount());
Chris@320 84 return;
Chris@320 85 }
Chris@320 86
Chris@320 87 std::cerr << "Initialising feature extraction plugin with channels = "
Chris@350 88 << channelCount << ", step = " << m_transform.getStepSize()
Chris@350 89 << ", block = " << m_transform.getBlockSize() << std::endl;
Chris@320 90
Chris@320 91 if (!m_plugin->initialise(channelCount,
Chris@350 92 m_transform.getStepSize(),
Chris@350 93 m_transform.getBlockSize())) {
Chris@361 94
Chris@361 95 size_t pstep = m_transform.getStepSize();
Chris@361 96 size_t pblock = m_transform.getBlockSize();
Chris@361 97
Chris@361 98 m_transform.setStepSize(0);
Chris@361 99 m_transform.setBlockSize(0);
Chris@361 100 TransformFactory::getInstance()->makeContextConsistentWithPlugin
Chris@361 101 (m_transform, m_plugin);
Chris@361 102
Chris@361 103 if (m_transform.getStepSize() != pstep ||
Chris@361 104 m_transform.getBlockSize() != pblock) {
Chris@361 105
Chris@361 106 if (!m_plugin->initialise(channelCount,
Chris@361 107 m_transform.getStepSize(),
Chris@361 108 m_transform.getBlockSize())) {
Chris@361 109
Chris@361 110 m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId);
Chris@361 111 return;
Chris@361 112
Chris@361 113 } else {
Chris@361 114
Chris@361 115 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 116 .arg(pluginId)
Chris@361 117 .arg(pstep)
Chris@361 118 .arg(pblock)
Chris@361 119 .arg(m_transform.getStepSize())
Chris@361 120 .arg(m_transform.getBlockSize());
Chris@361 121 }
Chris@361 122
Chris@361 123 } else {
Chris@361 124
Chris@361 125 m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId);
Chris@361 126 return;
Chris@361 127 }
Chris@320 128 }
Chris@320 129
Chris@366 130 if (m_transform.getPluginVersion() != "") {
Chris@366 131 QString pv = QString("%1").arg(m_plugin->getPluginVersion());
Chris@366 132 if (pv != m_transform.getPluginVersion()) {
Chris@366 133 QString vm = tr("Transform was configured for version %1 of plugin \"%2\", but the plugin being used is version %3")
Chris@366 134 .arg(m_transform.getPluginVersion())
Chris@366 135 .arg(pluginId)
Chris@366 136 .arg(pv);
Chris@366 137 if (m_message != "") {
Chris@366 138 m_message = QString("%1; %2").arg(vm).arg(m_message);
Chris@366 139 } else {
Chris@366 140 m_message = vm;
Chris@366 141 }
Chris@366 142 }
Chris@366 143 }
Chris@366 144
Chris@320 145 Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors();
Chris@320 146
Chris@320 147 if (outputs.empty()) {
Chris@361 148 m_message = tr("Plugin \"%1\" has no outputs").arg(pluginId);
Chris@320 149 return;
Chris@320 150 }
Chris@320 151
Chris@320 152 for (size_t i = 0; i < outputs.size(); ++i) {
Chris@350 153 if (m_transform.getOutput() == "" ||
Chris@350 154 outputs[i].identifier == m_transform.getOutput().toStdString()) {
Chris@320 155 m_outputFeatureNo = i;
Chris@320 156 m_descriptor = new Vamp::Plugin::OutputDescriptor
Chris@320 157 (outputs[i]);
Chris@320 158 break;
Chris@320 159 }
Chris@320 160 }
Chris@320 161
Chris@320 162 if (!m_descriptor) {
Chris@361 163 m_message = tr("Plugin \"%1\" has no output named \"%2\"")
Chris@361 164 .arg(pluginId)
Chris@361 165 .arg(m_transform.getOutput());
Chris@320 166 return;
Chris@320 167 }
Chris@320 168
Chris@331 169 // std::cerr << "FeatureExtractionModelTransformer: output sample type "
Chris@320 170 // << m_descriptor->sampleType << std::endl;
Chris@320 171
Chris@320 172 int binCount = 1;
Chris@320 173 float minValue = 0.0, maxValue = 0.0;
Chris@320 174 bool haveExtents = false;
Chris@320 175
Chris@320 176 if (m_descriptor->hasFixedBinCount) {
Chris@320 177 binCount = m_descriptor->binCount;
Chris@320 178 }
Chris@320 179
Chris@331 180 // std::cerr << "FeatureExtractionModelTransformer: output bin count "
Chris@320 181 // << binCount << std::endl;
Chris@320 182
Chris@320 183 if (binCount > 0 && m_descriptor->hasKnownExtents) {
Chris@320 184 minValue = m_descriptor->minValue;
Chris@320 185 maxValue = m_descriptor->maxValue;
Chris@320 186 haveExtents = true;
Chris@320 187 }
Chris@320 188
Chris@350 189 size_t modelRate = input->getSampleRate();
Chris@320 190 size_t modelResolution = 1;
Chris@320 191
Chris@320 192 switch (m_descriptor->sampleType) {
Chris@320 193
Chris@320 194 case Vamp::Plugin::OutputDescriptor::VariableSampleRate:
Chris@320 195 if (m_descriptor->sampleRate != 0.0) {
Chris@320 196 modelResolution = size_t(modelRate / m_descriptor->sampleRate + 0.001);
Chris@320 197 }
Chris@320 198 break;
Chris@320 199
Chris@320 200 case Vamp::Plugin::OutputDescriptor::OneSamplePerStep:
Chris@350 201 modelResolution = m_transform.getStepSize();
Chris@320 202 break;
Chris@320 203
Chris@320 204 case Vamp::Plugin::OutputDescriptor::FixedSampleRate:
Chris@320 205 modelRate = size_t(m_descriptor->sampleRate + 0.001);
Chris@320 206 break;
Chris@320 207 }
Chris@320 208
Chris@320 209 if (binCount == 0) {
Chris@320 210
Chris@320 211 m_output = new SparseOneDimensionalModel(modelRate, modelResolution,
Chris@320 212 false);
Chris@320 213
Chris@320 214 } else if (binCount == 1) {
Chris@320 215
Chris@320 216 SparseTimeValueModel *model;
Chris@320 217 if (haveExtents) {
Chris@320 218 model = new SparseTimeValueModel
Chris@320 219 (modelRate, modelResolution, minValue, maxValue, false);
Chris@320 220 } else {
Chris@320 221 model = new SparseTimeValueModel
Chris@320 222 (modelRate, modelResolution, false);
Chris@320 223 }
Chris@320 224 model->setScaleUnits(outputs[m_outputFeatureNo].unit.c_str());
Chris@320 225
Chris@320 226 m_output = model;
Chris@320 227
Chris@320 228 } else if (m_descriptor->sampleType ==
Chris@320 229 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
Chris@320 230
Chris@320 231 // We don't have a sparse 3D model, so interpret this as a
Chris@320 232 // note model. There's nothing to define which values to use
Chris@320 233 // as which parameters of the note -- for the moment let's
Chris@320 234 // treat the first as pitch, second as duration in frames,
Chris@320 235 // third (if present) as velocity. (Our note model doesn't
Chris@320 236 // yet store velocity.)
Chris@320 237 //!!! todo: ask the user!
Chris@320 238
Chris@320 239 NoteModel *model;
Chris@320 240 if (haveExtents) {
Chris@320 241 model = new NoteModel
Chris@320 242 (modelRate, modelResolution, minValue, maxValue, false);
Chris@320 243 } else {
Chris@320 244 model = new NoteModel
Chris@320 245 (modelRate, modelResolution, false);
Chris@320 246 }
Chris@320 247 model->setScaleUnits(outputs[m_outputFeatureNo].unit.c_str());
Chris@320 248
Chris@320 249 m_output = model;
Chris@320 250
Chris@320 251 } else {
Chris@320 252
Chris@320 253 EditableDenseThreeDimensionalModel *model =
Chris@320 254 new EditableDenseThreeDimensionalModel
Chris@320 255 (modelRate, modelResolution, binCount, false);
Chris@320 256
Chris@320 257 if (!m_descriptor->binNames.empty()) {
Chris@320 258 std::vector<QString> names;
Chris@320 259 for (size_t i = 0; i < m_descriptor->binNames.size(); ++i) {
Chris@320 260 names.push_back(m_descriptor->binNames[i].c_str());
Chris@320 261 }
Chris@320 262 model->setBinNames(names);
Chris@320 263 }
Chris@320 264
Chris@320 265 m_output = model;
Chris@320 266 }
Chris@333 267
Chris@350 268 if (m_output) m_output->setSourceModel(input);
Chris@320 269 }
Chris@320 270
Chris@331 271 FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()
Chris@320 272 {
Chris@331 273 std::cerr << "FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()" << std::endl;
Chris@320 274 delete m_plugin;
Chris@320 275 delete m_descriptor;
Chris@320 276 }
Chris@320 277
Chris@320 278 DenseTimeValueModel *
Chris@350 279 FeatureExtractionModelTransformer::getConformingInput()
Chris@320 280 {
Chris@320 281 DenseTimeValueModel *dtvm =
Chris@320 282 dynamic_cast<DenseTimeValueModel *>(getInputModel());
Chris@320 283 if (!dtvm) {
Chris@350 284 std::cerr << "FeatureExtractionModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl;
Chris@320 285 }
Chris@320 286 return dtvm;
Chris@320 287 }
Chris@320 288
Chris@320 289 void
Chris@331 290 FeatureExtractionModelTransformer::run()
Chris@320 291 {
Chris@350 292 DenseTimeValueModel *input = getConformingInput();
Chris@320 293 if (!input) return;
Chris@320 294
Chris@320 295 if (!m_output) return;
Chris@320 296
Chris@320 297 while (!input->isReady()) {
Chris@320 298 /*
Chris@320 299 if (dynamic_cast<WaveFileModel *>(input)) {
Chris@331 300 std::cerr << "FeatureExtractionModelTransformer::run: Model is not ready, but it's not a WaveFileModel (it's a " << typeid(input).name() << "), so that's OK" << std::endl;
Chris@320 301 sleep(2);
Chris@320 302 break; // no need to wait
Chris@320 303 }
Chris@320 304 */
Chris@331 305 std::cerr << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << std::endl;
Chris@320 306 sleep(1);
Chris@320 307 }
Chris@320 308
Chris@350 309 size_t sampleRate = input->getSampleRate();
Chris@320 310
Chris@320 311 size_t channelCount = input->getChannelCount();
Chris@320 312 if (m_plugin->getMaxChannelCount() < channelCount) {
Chris@320 313 channelCount = 1;
Chris@320 314 }
Chris@320 315
Chris@320 316 float **buffers = new float*[channelCount];
Chris@320 317 for (size_t ch = 0; ch < channelCount; ++ch) {
Chris@350 318 buffers[ch] = new float[m_transform.getBlockSize() + 2];
Chris@320 319 }
Chris@320 320
Chris@350 321 size_t stepSize = m_transform.getStepSize();
Chris@350 322 size_t blockSize = m_transform.getBlockSize();
Chris@350 323
Chris@320 324 bool frequencyDomain = (m_plugin->getInputDomain() ==
Chris@320 325 Vamp::Plugin::FrequencyDomain);
Chris@320 326 std::vector<FFTModel *> fftModels;
Chris@320 327
Chris@320 328 if (frequencyDomain) {
Chris@320 329 for (size_t ch = 0; ch < channelCount; ++ch) {
Chris@320 330 FFTModel *model = new FFTModel
Chris@350 331 (getConformingInput(),
Chris@350 332 channelCount == 1 ? m_input.getChannel() : ch,
Chris@350 333 m_transform.getWindowType(),
Chris@350 334 blockSize,
Chris@350 335 stepSize,
Chris@350 336 blockSize,
Chris@334 337 false,
Chris@334 338 StorageAdviser::PrecisionCritical);
Chris@320 339 if (!model->isOK()) {
Chris@320 340 delete model;
Chris@320 341 setCompletion(100);
Chris@387 342 //!!! 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 343 throw AllocationFailed("Failed to create the FFT model for this feature extraction model transformer");
Chris@320 344 }
Chris@320 345 model->resume();
Chris@320 346 fftModels.push_back(model);
Chris@320 347 }
Chris@320 348 }
Chris@320 349
Chris@350 350 long startFrame = m_input.getModel()->getStartFrame();
Chris@350 351 long endFrame = m_input.getModel()->getEndFrame();
Chris@320 352
Chris@350 353 RealTime contextStartRT = m_transform.getStartTime();
Chris@350 354 RealTime contextDurationRT = m_transform.getDuration();
Chris@350 355
Chris@350 356 long contextStart =
Chris@350 357 RealTime::realTime2Frame(contextStartRT, sampleRate);
Chris@350 358
Chris@350 359 long contextDuration =
Chris@350 360 RealTime::realTime2Frame(contextDurationRT, sampleRate);
Chris@320 361
Chris@320 362 if (contextStart == 0 || contextStart < startFrame) {
Chris@320 363 contextStart = startFrame;
Chris@320 364 }
Chris@320 365
Chris@320 366 if (contextDuration == 0) {
Chris@320 367 contextDuration = endFrame - contextStart;
Chris@320 368 }
Chris@320 369 if (contextStart + contextDuration > endFrame) {
Chris@320 370 contextDuration = endFrame - contextStart;
Chris@320 371 }
Chris@320 372
Chris@320 373 long blockFrame = contextStart;
Chris@320 374
Chris@320 375 long prevCompletion = 0;
Chris@320 376
Chris@320 377 setCompletion(0);
Chris@320 378
Chris@320 379 while (!m_abandoned) {
Chris@320 380
Chris@320 381 if (frequencyDomain) {
Chris@350 382 if (blockFrame - int(blockSize)/2 >
Chris@320 383 contextStart + contextDuration) break;
Chris@320 384 } else {
Chris@320 385 if (blockFrame >=
Chris@320 386 contextStart + contextDuration) break;
Chris@320 387 }
Chris@320 388
Chris@331 389 // std::cerr << "FeatureExtractionModelTransformer::run: blockFrame "
Chris@320 390 // << blockFrame << ", endFrame " << endFrame << ", blockSize "
Chris@350 391 // << blockSize << std::endl;
Chris@320 392
Chris@320 393 long completion =
Chris@350 394 (((blockFrame - contextStart) / stepSize) * 99) /
Chris@350 395 (contextDuration / stepSize);
Chris@320 396
Chris@350 397 // channelCount is either m_input.getModel()->channelCount or 1
Chris@320 398
Chris@363 399 if (frequencyDomain) {
Chris@363 400 for (size_t ch = 0; ch < channelCount; ++ch) {
Chris@350 401 int column = (blockFrame - startFrame) / stepSize;
Chris@350 402 for (size_t i = 0; i <= blockSize/2; ++i) {
Chris@320 403 fftModels[ch]->getValuesAt
Chris@320 404 (column, i, buffers[ch][i*2], buffers[ch][i*2+1]);
Chris@320 405 }
Chris@363 406 }
Chris@363 407 } else {
Chris@363 408 getFrames(channelCount, blockFrame, blockSize, buffers);
Chris@320 409 }
Chris@320 410
Chris@320 411 Vamp::Plugin::FeatureSet features = m_plugin->process
Chris@320 412 (buffers, Vamp::RealTime::frame2RealTime(blockFrame, sampleRate));
Chris@320 413
Chris@320 414 for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) {
Chris@320 415 Vamp::Plugin::Feature feature =
Chris@320 416 features[m_outputFeatureNo][fi];
Chris@320 417 addFeature(blockFrame, feature);
Chris@320 418 }
Chris@320 419
Chris@320 420 if (blockFrame == contextStart || completion > prevCompletion) {
Chris@320 421 setCompletion(completion);
Chris@320 422 prevCompletion = completion;
Chris@320 423 }
Chris@320 424
Chris@350 425 blockFrame += stepSize;
Chris@320 426 }
Chris@320 427
Chris@320 428 if (m_abandoned) return;
Chris@320 429
Chris@320 430 Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures();
Chris@320 431
Chris@320 432 for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) {
Chris@320 433 Vamp::Plugin::Feature feature =
Chris@320 434 features[m_outputFeatureNo][fi];
Chris@320 435 addFeature(blockFrame, feature);
Chris@320 436 }
Chris@320 437
Chris@320 438 if (frequencyDomain) {
Chris@320 439 for (size_t ch = 0; ch < channelCount; ++ch) {
Chris@320 440 delete fftModels[ch];
Chris@320 441 }
Chris@320 442 }
Chris@320 443
Chris@320 444 setCompletion(100);
Chris@320 445 }
Chris@320 446
Chris@320 447 void
Chris@363 448 FeatureExtractionModelTransformer::getFrames(int channelCount,
Chris@363 449 long startFrame, long size,
Chris@363 450 float **buffers)
Chris@320 451 {
Chris@320 452 long offset = 0;
Chris@320 453
Chris@320 454 if (startFrame < 0) {
Chris@363 455 for (int c = 0; c < channelCount; ++c) {
Chris@363 456 for (int i = 0; i < size && startFrame + i < 0; ++i) {
Chris@363 457 buffers[c][i] = 0.0f;
Chris@363 458 }
Chris@320 459 }
Chris@320 460 offset = -startFrame;
Chris@320 461 size -= offset;
Chris@320 462 if (size <= 0) return;
Chris@320 463 startFrame = 0;
Chris@320 464 }
Chris@320 465
Chris@350 466 DenseTimeValueModel *input = getConformingInput();
Chris@350 467 if (!input) return;
Chris@363 468
Chris@363 469 long got = 0;
Chris@350 470
Chris@363 471 if (channelCount == 1) {
Chris@363 472
Chris@363 473 got = input->getData(m_input.getChannel(), startFrame, size,
Chris@363 474 buffers[0] + offset);
Chris@363 475
Chris@363 476 if (m_input.getChannel() == -1 && input->getChannelCount() > 1) {
Chris@363 477 // use mean instead of sum, as plugin input
Chris@363 478 float cc = float(input->getChannelCount());
Chris@363 479 for (long i = 0; i < size; ++i) {
Chris@363 480 buffers[0][i + offset] /= cc;
Chris@363 481 }
Chris@363 482 }
Chris@363 483
Chris@363 484 } else {
Chris@363 485
Chris@363 486 float **writebuf = buffers;
Chris@363 487 if (offset > 0) {
Chris@363 488 writebuf = new float *[channelCount];
Chris@363 489 for (int i = 0; i < channelCount; ++i) {
Chris@363 490 writebuf[i] = buffers[i] + offset;
Chris@363 491 }
Chris@363 492 }
Chris@363 493
Chris@363 494 got = input->getData(0, channelCount-1, startFrame, size, writebuf);
Chris@363 495
Chris@363 496 if (writebuf != buffers) delete[] writebuf;
Chris@363 497 }
Chris@320 498
Chris@320 499 while (got < size) {
Chris@363 500 for (int c = 0; c < channelCount; ++c) {
Chris@363 501 buffers[c][got + offset] = 0.0;
Chris@363 502 }
Chris@320 503 ++got;
Chris@320 504 }
Chris@320 505 }
Chris@320 506
Chris@320 507 void
Chris@331 508 FeatureExtractionModelTransformer::addFeature(size_t blockFrame,
Chris@320 509 const Vamp::Plugin::Feature &feature)
Chris@320 510 {
Chris@350 511 size_t inputRate = m_input.getModel()->getSampleRate();
Chris@320 512
Chris@331 513 // std::cerr << "FeatureExtractionModelTransformer::addFeature("
Chris@320 514 // << blockFrame << ")" << std::endl;
Chris@320 515
Chris@320 516 int binCount = 1;
Chris@320 517 if (m_descriptor->hasFixedBinCount) {
Chris@320 518 binCount = m_descriptor->binCount;
Chris@320 519 }
Chris@320 520
Chris@320 521 size_t frame = blockFrame;
Chris@320 522
Chris@320 523 if (m_descriptor->sampleType ==
Chris@320 524 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
Chris@320 525
Chris@320 526 if (!feature.hasTimestamp) {
Chris@320 527 std::cerr
Chris@331 528 << "WARNING: FeatureExtractionModelTransformer::addFeature: "
Chris@320 529 << "Feature has variable sample rate but no timestamp!"
Chris@320 530 << std::endl;
Chris@320 531 return;
Chris@320 532 } else {
Chris@320 533 frame = Vamp::RealTime::realTime2Frame(feature.timestamp, inputRate);
Chris@320 534 }
Chris@320 535
Chris@320 536 } else if (m_descriptor->sampleType ==
Chris@320 537 Vamp::Plugin::OutputDescriptor::FixedSampleRate) {
Chris@320 538
Chris@320 539 if (feature.hasTimestamp) {
Chris@320 540 //!!! warning: sampleRate may be non-integral
Chris@320 541 frame = Vamp::RealTime::realTime2Frame(feature.timestamp,
Chris@320 542 lrintf(m_descriptor->sampleRate));
Chris@320 543 } else {
Chris@320 544 frame = m_output->getEndFrame();
Chris@320 545 }
Chris@320 546 }
Chris@320 547
Chris@320 548 if (binCount == 0) {
Chris@320 549
Chris@350 550 SparseOneDimensionalModel *model =
Chris@350 551 getConformingOutput<SparseOneDimensionalModel>();
Chris@320 552 if (!model) return;
Chris@350 553
Chris@320 554 model->addPoint(SparseOneDimensionalModel::Point(frame, feature.label.c_str()));
Chris@320 555
Chris@320 556 } else if (binCount == 1) {
Chris@320 557
Chris@320 558 float value = 0.0;
Chris@320 559 if (feature.values.size() > 0) value = feature.values[0];
Chris@320 560
Chris@350 561 SparseTimeValueModel *model =
Chris@350 562 getConformingOutput<SparseTimeValueModel>();
Chris@320 563 if (!model) return;
Chris@350 564
Chris@320 565 model->addPoint(SparseTimeValueModel::Point(frame, value, feature.label.c_str()));
Chris@320 566 // std::cerr << "SparseTimeValueModel::addPoint(" << frame << ", " << value << "), " << feature.label.c_str() << std::endl;
Chris@320 567
Chris@320 568 } else if (m_descriptor->sampleType ==
Chris@320 569 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
Chris@320 570
Chris@320 571 float pitch = 0.0;
Chris@320 572 if (feature.values.size() > 0) pitch = feature.values[0];
Chris@320 573
Chris@320 574 float duration = 1;
Chris@320 575 if (feature.values.size() > 1) duration = feature.values[1];
Chris@320 576
Chris@320 577 float velocity = 100;
Chris@320 578 if (feature.values.size() > 2) velocity = feature.values[2];
Chris@340 579 if (velocity < 0) velocity = 127;
Chris@340 580 if (velocity > 127) velocity = 127;
Chris@320 581
Chris@350 582 NoteModel *model = getConformingOutput<NoteModel>();
Chris@320 583 if (!model) return;
Chris@320 584
Chris@320 585 model->addPoint(NoteModel::Point(frame, pitch,
Chris@320 586 lrintf(duration),
Chris@340 587 velocity / 127.f,
Chris@320 588 feature.label.c_str()));
Chris@320 589
Chris@320 590 } else {
Chris@320 591
Chris@320 592 DenseThreeDimensionalModel::Column values = feature.values;
Chris@320 593
Chris@320 594 EditableDenseThreeDimensionalModel *model =
Chris@350 595 getConformingOutput<EditableDenseThreeDimensionalModel>();
Chris@320 596 if (!model) return;
Chris@320 597
Chris@320 598 model->setColumn(frame / model->getResolution(), values);
Chris@320 599 }
Chris@320 600 }
Chris@320 601
Chris@320 602 void
Chris@331 603 FeatureExtractionModelTransformer::setCompletion(int completion)
Chris@320 604 {
Chris@320 605 int binCount = 1;
Chris@320 606 if (m_descriptor->hasFixedBinCount) {
Chris@320 607 binCount = m_descriptor->binCount;
Chris@320 608 }
Chris@320 609
Chris@331 610 // std::cerr << "FeatureExtractionModelTransformer::setCompletion("
Chris@320 611 // << completion << ")" << std::endl;
Chris@320 612
Chris@320 613 if (binCount == 0) {
Chris@320 614
Chris@350 615 SparseOneDimensionalModel *model =
Chris@350 616 getConformingOutput<SparseOneDimensionalModel>();
Chris@320 617 if (!model) return;
Chris@350 618 model->setCompletion(completion, true); //!!!m_context.updates);
Chris@320 619
Chris@320 620 } else if (binCount == 1) {
Chris@320 621
Chris@350 622 SparseTimeValueModel *model =
Chris@350 623 getConformingOutput<SparseTimeValueModel>();
Chris@320 624 if (!model) return;
Chris@350 625 model->setCompletion(completion, true); //!!!m_context.updates);
Chris@320 626
Chris@320 627 } else if (m_descriptor->sampleType ==
Chris@320 628 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
Chris@320 629
Chris@350 630 NoteModel *model =
Chris@350 631 getConformingOutput<NoteModel>();
Chris@320 632 if (!model) return;
Chris@350 633 model->setCompletion(completion, true); //!!!m_context.updates);
Chris@320 634
Chris@320 635 } else {
Chris@320 636
Chris@320 637 EditableDenseThreeDimensionalModel *model =
Chris@350 638 getConformingOutput<EditableDenseThreeDimensionalModel>();
Chris@320 639 if (!model) return;
Chris@350 640 model->setCompletion(completion, true); //!!!m_context.updates);
Chris@320 641 }
Chris@320 642 }
Chris@320 643