| 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@408 | 281 //    std::cerr << "FeatureExtractionModelTransformer::getConformingInput: input model is " << getInputModel() << std::endl; | 
| Chris@408 | 282 | 
| Chris@320 | 283     DenseTimeValueModel *dtvm = | 
| Chris@320 | 284 	dynamic_cast<DenseTimeValueModel *>(getInputModel()); | 
| Chris@320 | 285     if (!dtvm) { | 
| Chris@350 | 286 	std::cerr << "FeatureExtractionModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; | 
| Chris@320 | 287     } | 
| Chris@320 | 288     return dtvm; | 
| Chris@320 | 289 } | 
| Chris@320 | 290 | 
| Chris@320 | 291 void | 
| Chris@331 | 292 FeatureExtractionModelTransformer::run() | 
| Chris@320 | 293 { | 
| Chris@350 | 294     DenseTimeValueModel *input = getConformingInput(); | 
| Chris@320 | 295     if (!input) return; | 
| Chris@320 | 296 | 
| Chris@320 | 297     if (!m_output) return; | 
| Chris@320 | 298 | 
| Chris@320 | 299     while (!input->isReady()) { | 
| Chris@331 | 300         std::cerr << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << std::endl; | 
| Chris@320 | 301         sleep(1); | 
| Chris@320 | 302     } | 
| Chris@320 | 303 | 
| Chris@350 | 304     size_t sampleRate = input->getSampleRate(); | 
| Chris@320 | 305 | 
| Chris@320 | 306     size_t channelCount = input->getChannelCount(); | 
| Chris@320 | 307     if (m_plugin->getMaxChannelCount() < channelCount) { | 
| Chris@320 | 308 	channelCount = 1; | 
| Chris@320 | 309     } | 
| Chris@320 | 310 | 
| Chris@320 | 311     float **buffers = new float*[channelCount]; | 
| Chris@320 | 312     for (size_t ch = 0; ch < channelCount; ++ch) { | 
| Chris@350 | 313 	buffers[ch] = new float[m_transform.getBlockSize() + 2]; | 
| Chris@320 | 314     } | 
| Chris@320 | 315 | 
| Chris@350 | 316     size_t stepSize = m_transform.getStepSize(); | 
| Chris@350 | 317     size_t blockSize = m_transform.getBlockSize(); | 
| Chris@350 | 318 | 
| Chris@320 | 319     bool frequencyDomain = (m_plugin->getInputDomain() == | 
| Chris@320 | 320                             Vamp::Plugin::FrequencyDomain); | 
| Chris@320 | 321     std::vector<FFTModel *> fftModels; | 
| Chris@320 | 322 | 
| Chris@320 | 323     if (frequencyDomain) { | 
| Chris@320 | 324         for (size_t ch = 0; ch < channelCount; ++ch) { | 
| Chris@320 | 325             FFTModel *model = new FFTModel | 
| Chris@350 | 326                                   (getConformingInput(), | 
| Chris@350 | 327                                    channelCount == 1 ? m_input.getChannel() : ch, | 
| Chris@350 | 328                                    m_transform.getWindowType(), | 
| Chris@350 | 329                                    blockSize, | 
| Chris@350 | 330                                    stepSize, | 
| Chris@350 | 331                                    blockSize, | 
| Chris@334 | 332                                    false, | 
| Chris@334 | 333                                    StorageAdviser::PrecisionCritical); | 
| Chris@320 | 334             if (!model->isOK()) { | 
| Chris@320 | 335                 delete model; | 
| Chris@320 | 336                 setCompletion(100); | 
| Chris@387 | 337                 //!!! 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 | 338                 throw AllocationFailed("Failed to create the FFT model for this feature extraction model transformer"); | 
| Chris@320 | 339             } | 
| Chris@320 | 340             model->resume(); | 
| Chris@320 | 341             fftModels.push_back(model); | 
| Chris@320 | 342         } | 
| Chris@320 | 343     } | 
| Chris@320 | 344 | 
| Chris@350 | 345     long startFrame = m_input.getModel()->getStartFrame(); | 
| Chris@350 | 346     long   endFrame = m_input.getModel()->getEndFrame(); | 
| Chris@320 | 347 | 
| Chris@350 | 348     RealTime contextStartRT = m_transform.getStartTime(); | 
| Chris@350 | 349     RealTime contextDurationRT = m_transform.getDuration(); | 
| Chris@350 | 350 | 
| Chris@350 | 351     long contextStart = | 
| Chris@350 | 352         RealTime::realTime2Frame(contextStartRT, sampleRate); | 
| Chris@350 | 353 | 
| Chris@350 | 354     long contextDuration = | 
| Chris@350 | 355         RealTime::realTime2Frame(contextDurationRT, sampleRate); | 
| Chris@320 | 356 | 
| Chris@320 | 357     if (contextStart == 0 || contextStart < startFrame) { | 
| Chris@320 | 358         contextStart = startFrame; | 
| Chris@320 | 359     } | 
| Chris@320 | 360 | 
| Chris@320 | 361     if (contextDuration == 0) { | 
| Chris@320 | 362         contextDuration = endFrame - contextStart; | 
| Chris@320 | 363     } | 
| Chris@320 | 364     if (contextStart + contextDuration > endFrame) { | 
| Chris@320 | 365         contextDuration = endFrame - contextStart; | 
| Chris@320 | 366     } | 
| Chris@320 | 367 | 
| Chris@320 | 368     long blockFrame = contextStart; | 
| Chris@320 | 369 | 
| Chris@320 | 370     long prevCompletion = 0; | 
| Chris@320 | 371 | 
| Chris@320 | 372     setCompletion(0); | 
| Chris@320 | 373 | 
| Chris@320 | 374     while (!m_abandoned) { | 
| Chris@320 | 375 | 
| Chris@320 | 376         if (frequencyDomain) { | 
| Chris@350 | 377             if (blockFrame - int(blockSize)/2 > | 
| Chris@320 | 378                 contextStart + contextDuration) break; | 
| Chris@320 | 379         } else { | 
| Chris@320 | 380             if (blockFrame >= | 
| Chris@320 | 381                 contextStart + contextDuration) break; | 
| Chris@320 | 382         } | 
| Chris@320 | 383 | 
| Chris@331 | 384 //	std::cerr << "FeatureExtractionModelTransformer::run: blockFrame " | 
| Chris@320 | 385 //		  << blockFrame << ", endFrame " << endFrame << ", blockSize " | 
| Chris@350 | 386 //                  << blockSize << std::endl; | 
| Chris@320 | 387 | 
| Chris@320 | 388 	long completion = | 
| Chris@350 | 389 	    (((blockFrame - contextStart) / stepSize) * 99) / | 
| Chris@350 | 390 	    (contextDuration / stepSize); | 
| Chris@320 | 391 | 
| Chris@350 | 392 	// channelCount is either m_input.getModel()->channelCount or 1 | 
| Chris@320 | 393 | 
| Chris@363 | 394         if (frequencyDomain) { | 
| Chris@363 | 395             for (size_t ch = 0; ch < channelCount; ++ch) { | 
| Chris@350 | 396                 int column = (blockFrame - startFrame) / stepSize; | 
| Chris@350 | 397                 for (size_t i = 0; i <= blockSize/2; ++i) { | 
| Chris@320 | 398                     fftModels[ch]->getValuesAt | 
| Chris@320 | 399                         (column, i, buffers[ch][i*2], buffers[ch][i*2+1]); | 
| Chris@320 | 400                 } | 
| Chris@363 | 401             } | 
| Chris@363 | 402         } else { | 
| Chris@363 | 403             getFrames(channelCount, blockFrame, blockSize, buffers); | 
| Chris@320 | 404         } | 
| Chris@320 | 405 | 
| Chris@320 | 406 	Vamp::Plugin::FeatureSet features = m_plugin->process | 
| Chris@320 | 407 	    (buffers, Vamp::RealTime::frame2RealTime(blockFrame, sampleRate)); | 
| Chris@320 | 408 | 
| Chris@320 | 409 	for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) { | 
| Chris@320 | 410 	    Vamp::Plugin::Feature feature = | 
| Chris@320 | 411 		features[m_outputFeatureNo][fi]; | 
| Chris@320 | 412 	    addFeature(blockFrame, feature); | 
| Chris@320 | 413 	} | 
| Chris@320 | 414 | 
| Chris@320 | 415 	if (blockFrame == contextStart || completion > prevCompletion) { | 
| Chris@320 | 416 	    setCompletion(completion); | 
| Chris@320 | 417 	    prevCompletion = completion; | 
| Chris@320 | 418 	} | 
| Chris@320 | 419 | 
| Chris@350 | 420 	blockFrame += stepSize; | 
| Chris@320 | 421     } | 
| Chris@320 | 422 | 
| Chris@320 | 423     if (m_abandoned) return; | 
| Chris@320 | 424 | 
| Chris@320 | 425     Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures(); | 
| Chris@320 | 426 | 
| Chris@320 | 427     for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) { | 
| Chris@320 | 428 	Vamp::Plugin::Feature feature = | 
| Chris@320 | 429 	    features[m_outputFeatureNo][fi]; | 
| Chris@320 | 430 	addFeature(blockFrame, feature); | 
| Chris@320 | 431     } | 
| Chris@320 | 432 | 
| Chris@320 | 433     if (frequencyDomain) { | 
| Chris@320 | 434         for (size_t ch = 0; ch < channelCount; ++ch) { | 
| Chris@320 | 435             delete fftModels[ch]; | 
| Chris@320 | 436         } | 
| Chris@320 | 437     } | 
| Chris@320 | 438 | 
| Chris@320 | 439     setCompletion(100); | 
| Chris@320 | 440 } | 
| Chris@320 | 441 | 
| Chris@320 | 442 void | 
| Chris@363 | 443 FeatureExtractionModelTransformer::getFrames(int channelCount, | 
| Chris@363 | 444                                              long startFrame, long size, | 
| Chris@363 | 445                                              float **buffers) | 
| Chris@320 | 446 { | 
| Chris@320 | 447     long offset = 0; | 
| Chris@320 | 448 | 
| Chris@320 | 449     if (startFrame < 0) { | 
| Chris@363 | 450         for (int c = 0; c < channelCount; ++c) { | 
| Chris@363 | 451             for (int i = 0; i < size && startFrame + i < 0; ++i) { | 
| Chris@363 | 452                 buffers[c][i] = 0.0f; | 
| Chris@363 | 453             } | 
| Chris@320 | 454         } | 
| Chris@320 | 455         offset = -startFrame; | 
| Chris@320 | 456         size -= offset; | 
| Chris@320 | 457         if (size <= 0) return; | 
| Chris@320 | 458         startFrame = 0; | 
| Chris@320 | 459     } | 
| Chris@320 | 460 | 
| Chris@350 | 461     DenseTimeValueModel *input = getConformingInput(); | 
| Chris@350 | 462     if (!input) return; | 
| Chris@363 | 463 | 
| Chris@363 | 464     long got = 0; | 
| Chris@350 | 465 | 
| Chris@363 | 466     if (channelCount == 1) { | 
| Chris@363 | 467 | 
| Chris@363 | 468         got = input->getData(m_input.getChannel(), startFrame, size, | 
| Chris@363 | 469                              buffers[0] + offset); | 
| Chris@363 | 470 | 
| Chris@363 | 471         if (m_input.getChannel() == -1 && input->getChannelCount() > 1) { | 
| Chris@363 | 472             // use mean instead of sum, as plugin input | 
| Chris@363 | 473             float cc = float(input->getChannelCount()); | 
| Chris@363 | 474             for (long i = 0; i < size; ++i) { | 
| Chris@363 | 475                 buffers[0][i + offset] /= cc; | 
| Chris@363 | 476             } | 
| Chris@363 | 477         } | 
| Chris@363 | 478 | 
| Chris@363 | 479     } else { | 
| Chris@363 | 480 | 
| Chris@363 | 481         float **writebuf = buffers; | 
| Chris@363 | 482         if (offset > 0) { | 
| Chris@363 | 483             writebuf = new float *[channelCount]; | 
| Chris@363 | 484             for (int i = 0; i < channelCount; ++i) { | 
| Chris@363 | 485                 writebuf[i] = buffers[i] + offset; | 
| Chris@363 | 486             } | 
| Chris@363 | 487         } | 
| Chris@363 | 488 | 
| Chris@363 | 489         got = input->getData(0, channelCount-1, startFrame, size, writebuf); | 
| Chris@363 | 490 | 
| Chris@363 | 491         if (writebuf != buffers) delete[] writebuf; | 
| Chris@363 | 492     } | 
| Chris@320 | 493 | 
| Chris@320 | 494     while (got < size) { | 
| Chris@363 | 495         for (int c = 0; c < channelCount; ++c) { | 
| Chris@363 | 496             buffers[c][got + offset] = 0.0; | 
| Chris@363 | 497         } | 
| Chris@320 | 498         ++got; | 
| Chris@320 | 499     } | 
| Chris@320 | 500 } | 
| Chris@320 | 501 | 
| Chris@320 | 502 void | 
| Chris@331 | 503 FeatureExtractionModelTransformer::addFeature(size_t blockFrame, | 
| Chris@320 | 504 					     const Vamp::Plugin::Feature &feature) | 
| Chris@320 | 505 { | 
| Chris@350 | 506     size_t inputRate = m_input.getModel()->getSampleRate(); | 
| Chris@320 | 507 | 
| Chris@331 | 508 //    std::cerr << "FeatureExtractionModelTransformer::addFeature(" | 
| Chris@320 | 509 //	      << blockFrame << ")" << std::endl; | 
| Chris@320 | 510 | 
| Chris@320 | 511     int binCount = 1; | 
| Chris@320 | 512     if (m_descriptor->hasFixedBinCount) { | 
| Chris@320 | 513 	binCount = m_descriptor->binCount; | 
| Chris@320 | 514     } | 
| Chris@320 | 515 | 
| Chris@320 | 516     size_t frame = blockFrame; | 
| Chris@320 | 517 | 
| Chris@320 | 518     if (m_descriptor->sampleType == | 
| Chris@320 | 519 	Vamp::Plugin::OutputDescriptor::VariableSampleRate) { | 
| Chris@320 | 520 | 
| Chris@320 | 521 	if (!feature.hasTimestamp) { | 
| Chris@320 | 522 	    std::cerr | 
| Chris@331 | 523 		<< "WARNING: FeatureExtractionModelTransformer::addFeature: " | 
| Chris@320 | 524 		<< "Feature has variable sample rate but no timestamp!" | 
| Chris@320 | 525 		<< std::endl; | 
| Chris@320 | 526 	    return; | 
| Chris@320 | 527 	} else { | 
| Chris@320 | 528 	    frame = Vamp::RealTime::realTime2Frame(feature.timestamp, inputRate); | 
| Chris@320 | 529 	} | 
| Chris@320 | 530 | 
| Chris@320 | 531     } else if (m_descriptor->sampleType == | 
| Chris@320 | 532 	       Vamp::Plugin::OutputDescriptor::FixedSampleRate) { | 
| Chris@320 | 533 | 
| Chris@320 | 534 	if (feature.hasTimestamp) { | 
| Chris@320 | 535 	    //!!! warning: sampleRate may be non-integral | 
| Chris@320 | 536 	    frame = Vamp::RealTime::realTime2Frame(feature.timestamp, | 
| Chris@320 | 537                                                    lrintf(m_descriptor->sampleRate)); | 
| Chris@320 | 538 	} else { | 
| Chris@320 | 539 	    frame = m_output->getEndFrame(); | 
| Chris@320 | 540 	} | 
| Chris@320 | 541     } | 
| Chris@320 | 542 | 
| Chris@320 | 543     if (binCount == 0) { | 
| Chris@320 | 544 | 
| Chris@350 | 545 	SparseOneDimensionalModel *model = | 
| Chris@350 | 546             getConformingOutput<SparseOneDimensionalModel>(); | 
| Chris@320 | 547 	if (!model) return; | 
| Chris@350 | 548 | 
| Chris@320 | 549 	model->addPoint(SparseOneDimensionalModel::Point(frame, feature.label.c_str())); | 
| Chris@320 | 550 | 
| Chris@320 | 551     } else if (binCount == 1) { | 
| Chris@320 | 552 | 
| Chris@320 | 553 	float value = 0.0; | 
| Chris@320 | 554 	if (feature.values.size() > 0) value = feature.values[0]; | 
| Chris@320 | 555 | 
| Chris@350 | 556 	SparseTimeValueModel *model = | 
| Chris@350 | 557             getConformingOutput<SparseTimeValueModel>(); | 
| Chris@320 | 558 	if (!model) return; | 
| Chris@350 | 559 | 
| Chris@320 | 560 	model->addPoint(SparseTimeValueModel::Point(frame, value, feature.label.c_str())); | 
| Chris@320 | 561 //        std::cerr << "SparseTimeValueModel::addPoint(" << frame << ", " << value << "), " << feature.label.c_str() << std::endl; | 
| Chris@320 | 562 | 
| Chris@320 | 563     } else if (m_descriptor->sampleType == | 
| Chris@320 | 564 	       Vamp::Plugin::OutputDescriptor::VariableSampleRate) { | 
| Chris@320 | 565 | 
| Chris@320 | 566         float pitch = 0.0; | 
| Chris@320 | 567         if (feature.values.size() > 0) pitch = feature.values[0]; | 
| Chris@320 | 568 | 
| Chris@320 | 569         float duration = 1; | 
| Chris@320 | 570         if (feature.values.size() > 1) duration = feature.values[1]; | 
| Chris@320 | 571 | 
| Chris@320 | 572         float velocity = 100; | 
| Chris@320 | 573         if (feature.values.size() > 2) velocity = feature.values[2]; | 
| Chris@340 | 574         if (velocity < 0) velocity = 127; | 
| Chris@340 | 575         if (velocity > 127) velocity = 127; | 
| Chris@320 | 576 | 
| Chris@350 | 577         NoteModel *model = getConformingOutput<NoteModel>(); | 
| Chris@320 | 578         if (!model) return; | 
| Chris@320 | 579 | 
| Chris@320 | 580         model->addPoint(NoteModel::Point(frame, pitch, | 
| Chris@320 | 581                                          lrintf(duration), | 
| Chris@340 | 582                                          velocity / 127.f, | 
| Chris@320 | 583                                          feature.label.c_str())); | 
| Chris@320 | 584 | 
| Chris@320 | 585     } else { | 
| Chris@320 | 586 | 
| Chris@320 | 587 	DenseThreeDimensionalModel::Column values = feature.values; | 
| Chris@320 | 588 | 
| Chris@320 | 589 	EditableDenseThreeDimensionalModel *model = | 
| Chris@350 | 590             getConformingOutput<EditableDenseThreeDimensionalModel>(); | 
| Chris@320 | 591 	if (!model) return; | 
| Chris@320 | 592 | 
| Chris@320 | 593 	model->setColumn(frame / model->getResolution(), values); | 
| Chris@320 | 594     } | 
| Chris@320 | 595 } | 
| Chris@320 | 596 | 
| Chris@320 | 597 void | 
| Chris@331 | 598 FeatureExtractionModelTransformer::setCompletion(int completion) | 
| Chris@320 | 599 { | 
| Chris@320 | 600     int binCount = 1; | 
| Chris@320 | 601     if (m_descriptor->hasFixedBinCount) { | 
| Chris@320 | 602 	binCount = m_descriptor->binCount; | 
| Chris@320 | 603     } | 
| Chris@320 | 604 | 
| Chris@331 | 605 //    std::cerr << "FeatureExtractionModelTransformer::setCompletion(" | 
| Chris@320 | 606 //              << completion << ")" << std::endl; | 
| Chris@320 | 607 | 
| Chris@320 | 608     if (binCount == 0) { | 
| Chris@320 | 609 | 
| Chris@350 | 610 	SparseOneDimensionalModel *model = | 
| Chris@350 | 611             getConformingOutput<SparseOneDimensionalModel>(); | 
| Chris@320 | 612 	if (!model) return; | 
| Chris@350 | 613 	model->setCompletion(completion, true); //!!!m_context.updates); | 
| Chris@320 | 614 | 
| Chris@320 | 615     } else if (binCount == 1) { | 
| Chris@320 | 616 | 
| Chris@350 | 617 	SparseTimeValueModel *model = | 
| Chris@350 | 618             getConformingOutput<SparseTimeValueModel>(); | 
| Chris@320 | 619 	if (!model) return; | 
| Chris@350 | 620 	model->setCompletion(completion, true); //!!!m_context.updates); | 
| Chris@320 | 621 | 
| Chris@320 | 622     } else if (m_descriptor->sampleType == | 
| Chris@320 | 623 	       Vamp::Plugin::OutputDescriptor::VariableSampleRate) { | 
| Chris@320 | 624 | 
| Chris@350 | 625 	NoteModel *model = | 
| Chris@350 | 626             getConformingOutput<NoteModel>(); | 
| Chris@320 | 627 	if (!model) return; | 
| Chris@350 | 628 	model->setCompletion(completion, true); //!!!m_context.updates); | 
| Chris@320 | 629 | 
| Chris@320 | 630     } else { | 
| Chris@320 | 631 | 
| Chris@320 | 632 	EditableDenseThreeDimensionalModel *model = | 
| Chris@350 | 633             getConformingOutput<EditableDenseThreeDimensionalModel>(); | 
| Chris@320 | 634 	if (!model) return; | 
| Chris@350 | 635 	model->setCompletion(completion, true); //!!!m_context.updates); | 
| Chris@320 | 636     } | 
| Chris@320 | 637 } | 
| Chris@320 | 638 |