# HG changeset patch # User Chris Cannam # Date 1385999226 0 # Node ID c9846844ac11f79cda6110ee139a00231ae592d7 # Parent 2d53205f70cd8985126b491673b419a8c0f00d67# Parent dba8a02b0413335ee34cf302aef7754daebd0b8d Merge branch tonioni_multi_transform diff -r 2d53205f70cd -r c9846844ac11 transform/FeatureExtractionModelTransformer.cpp --- a/transform/FeatureExtractionModelTransformer.cpp Tue Nov 26 14:37:01 2013 +0000 +++ b/transform/FeatureExtractionModelTransformer.cpp Mon Dec 02 15:47:06 2013 +0000 @@ -39,43 +39,80 @@ FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Input in, const Transform &transform, - const PreferredOutputModel outputmodel) : + const PreferredOutputModel outputmodel) : ModelTransformer(in, transform), m_plugin(0), - m_descriptor(0), - m_outputNo(0), - m_fixedRateFeatureNo(-1), // we increment before use m_preferredOutputModel(outputmodel) { // SVDEBUG << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << pluginId << ", outputName " << m_transform.getOutput() << endl; - QString pluginId = transform.getPluginIdentifier(); + initialise(); +} + +FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Input in, + const Transforms &transforms, + const PreferredOutputModel outputmodel) : + ModelTransformer(in, transforms), + m_plugin(0), + m_preferredOutputModel(outputmodel) +{ +// SVDEBUG << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << pluginId << ", outputName " << m_transform.getOutput() << endl; + + initialise(); +} + +static bool +areTransformsSimilar(const Transform &t1, const Transform &t2) +{ + Transform t2o(t2); + t2o.setOutput(t1.getOutput()); + return t1 == t2o; +} + +bool +FeatureExtractionModelTransformer::initialise() +{ + // All transforms must use the same plugin, parameters, and + // inputs: they can differ only in choice of plugin output. So we + // initialise based purely on the first transform in the list (but + // first check that they are actually similar as promised) + + for (int j = 1; j < (int)m_transforms.size(); ++j) { + if (!areTransformsSimilar(m_transforms[0], m_transforms[j])) { + m_message = tr("Transforms supplied to a single FeatureExtractionModelTransformer instance must be similar in every respect except plugin output"); + return false; + } + } + + Transform primaryTransform = m_transforms[0]; + + QString pluginId = primaryTransform.getPluginIdentifier(); FeatureExtractionPluginFactory *factory = FeatureExtractionPluginFactory::instanceFor(pluginId); if (!factory) { m_message = tr("No factory available for feature extraction plugin id \"%1\" (unknown plugin type, or internal error?)").arg(pluginId); - return; + return false; } DenseTimeValueModel *input = getConformingInput(); if (!input) { m_message = tr("Input model for feature extraction plugin \"%1\" is of wrong type (internal error?)").arg(pluginId); - return; + return false; } m_plugin = factory->instantiatePlugin(pluginId, input->getSampleRate()); if (!m_plugin) { m_message = tr("Failed to instantiate plugin \"%1\"").arg(pluginId); - return; + return false; } TransformFactory::getInstance()->makeContextConsistentWithPlugin - (m_transform, m_plugin); + (primaryTransform, m_plugin); TransformFactory::getInstance()->setPluginParameters - (m_transform, m_plugin); + (primaryTransform, m_plugin); size_t channelCount = input->getChannelCount(); if (m_plugin->getMaxChannelCount() < channelCount) { @@ -87,34 +124,35 @@ .arg(m_plugin->getMinChannelCount()) .arg(m_plugin->getMaxChannelCount()) .arg(input->getChannelCount()); - return; + return false; } SVDEBUG << "Initialising feature extraction plugin with channels = " - << channelCount << ", step = " << m_transform.getStepSize() - << ", block = " << m_transform.getBlockSize() << endl; + << channelCount << ", step = " << primaryTransform.getStepSize() + << ", block = " << primaryTransform.getBlockSize() << endl; if (!m_plugin->initialise(channelCount, - m_transform.getStepSize(), - m_transform.getBlockSize())) { + primaryTransform.getStepSize(), + primaryTransform.getBlockSize())) { - size_t pstep = m_transform.getStepSize(); - size_t pblock = m_transform.getBlockSize(); + size_t pstep = primaryTransform.getStepSize(); + size_t pblock = primaryTransform.getBlockSize(); - m_transform.setStepSize(0); - m_transform.setBlockSize(0); +///!!! hang on, this isn't right -- we're modifying a copy + primaryTransform.setStepSize(0); + primaryTransform.setBlockSize(0); TransformFactory::getInstance()->makeContextConsistentWithPlugin - (m_transform, m_plugin); + (primaryTransform, m_plugin); - if (m_transform.getStepSize() != pstep || - m_transform.getBlockSize() != pblock) { + if (primaryTransform.getStepSize() != pstep || + primaryTransform.getBlockSize() != pblock) { if (!m_plugin->initialise(channelCount, - m_transform.getStepSize(), - m_transform.getBlockSize())) { + primaryTransform.getStepSize(), + primaryTransform.getBlockSize())) { m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId); - return; + return false; } else { @@ -122,22 +160,22 @@ .arg(pluginId) .arg(pstep) .arg(pblock) - .arg(m_transform.getStepSize()) - .arg(m_transform.getBlockSize()); + .arg(primaryTransform.getStepSize()) + .arg(primaryTransform.getBlockSize()); } } else { m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId); - return; + return false; } } - if (m_transform.getPluginVersion() != "") { + if (primaryTransform.getPluginVersion() != "") { QString pv = QString("%1").arg(m_plugin->getPluginVersion()); - if (pv != m_transform.getPluginVersion()) { + if (pv != primaryTransform.getPluginVersion()) { QString vm = tr("Transform was configured for version %1 of plugin \"%2\", but the plugin being used is version %3") - .arg(m_transform.getPluginVersion()) + .arg(primaryTransform.getPluginVersion()) .arg(pluginId) .arg(pv); if (m_message != "") { @@ -152,77 +190,85 @@ if (outputs.empty()) { m_message = tr("Plugin \"%1\" has no outputs").arg(pluginId); - return; - } - - for (size_t i = 0; i < outputs.size(); ++i) { -// SVDEBUG << "comparing output " << i << " name \"" << outputs[i].identifier << "\" with expected \"" << m_transform.getOutput() << "\"" << endl; - if (m_transform.getOutput() == "" || - outputs[i].identifier == m_transform.getOutput().toStdString()) { - m_outputNo = i; - m_descriptor = new Vamp::Plugin::OutputDescriptor(outputs[i]); - break; - } + return false; } - if (!m_descriptor) { - m_message = tr("Plugin \"%1\" has no output named \"%2\"") - .arg(pluginId) - .arg(m_transform.getOutput()); - return; + for (int j = 0; j < (int)m_transforms.size(); ++j) { + + for (int i = 0; i < (int)outputs.size(); ++i) { +// SVDEBUG << "comparing output " << i << " name \"" << outputs[i].identifier << "\" with expected \"" << m_transform.getOutput() << "\"" << endl; + if (m_transforms[j].getOutput() == "" || + outputs[i].identifier == m_transforms[j].getOutput().toStdString()) { + m_outputNos.push_back(i); + m_descriptors.push_back(new Vamp::Plugin::OutputDescriptor(outputs[i])); + m_fixedRateFeatureNos.push_back(-1); // we increment before use + break; + } + } + + if (m_descriptors.size() <= j) { + m_message = tr("Plugin \"%1\" has no output named \"%2\"") + .arg(pluginId) + .arg(m_transforms[j].getOutput()); + return false; + } } - createOutputModel(); + for (int j = 0; j < (int)m_transforms.size(); ++j) { + createOutputModel(j); + } + + return true; } void -FeatureExtractionModelTransformer::createOutputModel() +FeatureExtractionModelTransformer::createOutputModel(int n) { DenseTimeValueModel *input = getConformingInput(); // cerr << "FeatureExtractionModelTransformer::createOutputModel: sample type " << m_descriptor->sampleType << ", rate " << m_descriptor->sampleRate << endl; - PluginRDFDescription description(m_transform.getPluginIdentifier()); - QString outputId = m_transform.getOutput(); + PluginRDFDescription description(m_transforms[n].getPluginIdentifier()); + QString outputId = m_transforms[n].getOutput(); int binCount = 1; float minValue = 0.0, maxValue = 0.0; bool haveExtents = false; - if (m_descriptor->hasFixedBinCount) { - binCount = m_descriptor->binCount; + if (m_descriptors[n]->hasFixedBinCount) { + binCount = m_descriptors[n]->binCount; } // cerr << "FeatureExtractionModelTransformer: output bin count " // << binCount << endl; - if (binCount > 0 && m_descriptor->hasKnownExtents) { - minValue = m_descriptor->minValue; - maxValue = m_descriptor->maxValue; + if (binCount > 0 && m_descriptors[n]->hasKnownExtents) { + minValue = m_descriptors[n]->minValue; + maxValue = m_descriptors[n]->maxValue; haveExtents = true; } size_t modelRate = input->getSampleRate(); size_t modelResolution = 1; - if (m_descriptor->sampleType != + if (m_descriptors[n]->sampleType != Vamp::Plugin::OutputDescriptor::OneSamplePerStep) { - if (m_descriptor->sampleRate > input->getSampleRate()) { + if (m_descriptors[n]->sampleRate > input->getSampleRate()) { cerr << "WARNING: plugin reports output sample rate as " - << m_descriptor->sampleRate << " (can't display features with finer resolution than the input rate of " << input->getSampleRate() << ")" << endl; + << m_descriptors[n]->sampleRate << " (can't display features with finer resolution than the input rate of " << input->getSampleRate() << ")" << endl; } } - switch (m_descriptor->sampleType) { + switch (m_descriptors[n]->sampleType) { case Vamp::Plugin::OutputDescriptor::VariableSampleRate: - if (m_descriptor->sampleRate != 0.0) { - modelResolution = size_t(modelRate / m_descriptor->sampleRate + 0.001); + if (m_descriptors[n]->sampleRate != 0.0) { + modelResolution = size_t(modelRate / m_descriptors[n]->sampleRate + 0.001); } break; case Vamp::Plugin::OutputDescriptor::OneSamplePerStep: - modelResolution = m_transform.getStepSize(); + modelResolution = m_transforms[n].getStepSize(); break; case Vamp::Plugin::OutputDescriptor::FixedSampleRate: @@ -231,33 +277,33 @@ //!!! the model rate to be the input model's rate, and adjust //!!! the resolution appropriately. We can't properly display //!!! data with a higher resolution than the base model at all -// modelRate = size_t(m_descriptor->sampleRate + 0.001); - if (m_descriptor->sampleRate > input->getSampleRate()) { +// modelRate = size_t(m_descriptors[n]->sampleRate + 0.001); + if (m_descriptors[n]->sampleRate > input->getSampleRate()) { modelResolution = 1; } else { modelResolution = size_t(input->getSampleRate() / - m_descriptor->sampleRate); + m_descriptors[n]->sampleRate); } break; } bool preDurationPlugin = (m_plugin->getVampApiVersion() < 2); + Model *out = 0; + if (binCount == 0 && - (preDurationPlugin || !m_descriptor->hasDuration)) { + (preDurationPlugin || !m_descriptors[n]->hasDuration)) { // Anything with no value and no duration is an instant - m_output = new SparseOneDimensionalModel(modelRate, modelResolution, - false); - + out = new SparseOneDimensionalModel(modelRate, modelResolution, false); QString outputEventTypeURI = description.getOutputEventTypeURI(outputId); - m_output->setRDFTypeURI(outputEventTypeURI); + out->setRDFTypeURI(outputEventTypeURI); } else if ((preDurationPlugin && binCount > 1 && - (m_descriptor->sampleType == + (m_descriptors[n]->sampleType == Vamp::Plugin::OutputDescriptor::VariableSampleRate)) || - (!preDurationPlugin && m_descriptor->hasDuration)) { + (!preDurationPlugin && m_descriptors[n]->hasDuration)) { // For plugins using the old v1 API without explicit duration, // we treat anything that has multiple bins (i.e. that has the @@ -288,9 +334,9 @@ // Regions do not have units of Hz or MIDI things (a sweeping // assumption!) - if (m_descriptor->unit == "Hz" || - m_descriptor->unit.find("MIDI") != std::string::npos || - m_descriptor->unit.find("midi") != std::string::npos) { + if (m_descriptors[n]->unit == "Hz" || + m_descriptors[n]->unit.find("MIDI") != std::string::npos || + m_descriptors[n]->unit.find("midi") != std::string::npos) { isNoteModel = true; } @@ -306,8 +352,8 @@ } else { model = new NoteModel (modelRate, modelResolution, false); } - model->setScaleUnits(m_descriptor->unit.c_str()); - m_output = model; + model->setScaleUnits(m_descriptors[n]->unit.c_str()); + out = model; // GF: FlexiNoteModel is selected if the m_preferredOutputModel is set } else if (isNoteModel && m_preferredOutputModel == FlexiNoteOutputModel) { @@ -318,8 +364,8 @@ } else { model = new FlexiNoteModel (modelRate, modelResolution, false); } - model->setScaleUnits(m_descriptor->unit.c_str()); - m_output = model; + model->setScaleUnits(m_descriptors[n]->unit.c_str()); + out = model; } else { @@ -331,15 +377,15 @@ model = new RegionModel (modelRate, modelResolution, false); } - model->setScaleUnits(m_descriptor->unit.c_str()); - m_output = model; + model->setScaleUnits(m_descriptors[n]->unit.c_str()); + out = model; } QString outputEventTypeURI = description.getOutputEventTypeURI(outputId); - m_output->setRDFTypeURI(outputEventTypeURI); + out->setRDFTypeURI(outputEventTypeURI); } else if (binCount == 1 || - (m_descriptor->sampleType == + (m_descriptors[n]->sampleType == Vamp::Plugin::OutputDescriptor::VariableSampleRate)) { // Anything that is not a 1D, note, or interval model and that @@ -361,12 +407,12 @@ } Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors(); - model->setScaleUnits(outputs[m_outputNo].unit.c_str()); + model->setScaleUnits(outputs[m_outputNos[n]].unit.c_str()); - m_output = model; + out = model; QString outputEventTypeURI = description.getOutputEventTypeURI(outputId); - m_output->setRDFTypeURI(outputEventTypeURI); + out->setRDFTypeURI(outputEventTypeURI); } else { @@ -380,28 +426,33 @@ EditableDenseThreeDimensionalModel::BasicMultirateCompression, false); - if (!m_descriptor->binNames.empty()) { + if (!m_descriptors[n]->binNames.empty()) { std::vector names; - for (size_t i = 0; i < m_descriptor->binNames.size(); ++i) { - names.push_back(m_descriptor->binNames[i].c_str()); + for (size_t i = 0; i < m_descriptors[n]->binNames.size(); ++i) { + names.push_back(m_descriptors[n]->binNames[i].c_str()); } model->setBinNames(names); } - m_output = model; + out = model; QString outputSignalTypeURI = description.getOutputSignalTypeURI(outputId); - m_output->setRDFTypeURI(outputSignalTypeURI); + out->setRDFTypeURI(outputSignalTypeURI); } - if (m_output) m_output->setSourceModel(input); + if (out) { + out->setSourceModel(input); + m_outputs.push_back(out); + } } FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer() { // SVDEBUG << "FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()" << endl; delete m_plugin; - delete m_descriptor; + for (int j = 0; j < m_descriptors.size(); ++j) { + delete m_descriptors[j]; + } } DenseTimeValueModel * @@ -423,7 +474,9 @@ DenseTimeValueModel *input = getConformingInput(); if (!input) return; - if (!m_output) return; + if (m_outputs.empty()) return; + + Transform primaryTransform = m_transforms[0]; while (!input->isReady() && !m_abandoned) { SVDEBUG << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << endl; @@ -440,11 +493,11 @@ float **buffers = new float*[channelCount]; for (size_t ch = 0; ch < channelCount; ++ch) { - buffers[ch] = new float[m_transform.getBlockSize() + 2]; + buffers[ch] = new float[primaryTransform.getBlockSize() + 2]; } - size_t stepSize = m_transform.getStepSize(); - size_t blockSize = m_transform.getBlockSize(); + size_t stepSize = primaryTransform.getStepSize(); + size_t blockSize = primaryTransform.getBlockSize(); bool frequencyDomain = (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain); @@ -455,7 +508,7 @@ FFTModel *model = new FFTModel (getConformingInput(), channelCount == 1 ? m_input.getChannel() : ch, - m_transform.getWindowType(), + primaryTransform.getWindowType(), blockSize, stepSize, blockSize, @@ -463,7 +516,9 @@ StorageAdviser::PrecisionCritical); if (!model->isOK()) { delete model; - setCompletion(100); + for (int j = 0; j < (int)m_outputNos.size(); ++j) { + setCompletion(j, 100); + } //!!! need a better way to handle this -- previously we were using a QMessageBox but that isn't an appropriate thing to do here either throw AllocationFailed("Failed to create the FFT model for this feature extraction model transformer"); } @@ -475,8 +530,8 @@ long startFrame = m_input.getModel()->getStartFrame(); long endFrame = m_input.getModel()->getEndFrame(); - RealTime contextStartRT = m_transform.getStartTime(); - RealTime contextDurationRT = m_transform.getDuration(); + RealTime contextStartRT = primaryTransform.getStartTime(); + RealTime contextDurationRT = primaryTransform.getDuration(); long contextStart = RealTime::realTime2Frame(contextStartRT, sampleRate); @@ -499,7 +554,9 @@ long prevCompletion = 0; - setCompletion(0); + for (int j = 0; j < (int)m_outputNos.size(); ++j) { + setCompletion(j, 0); + } float *reals = 0; float *imaginaries = 0; @@ -556,13 +613,17 @@ if (m_abandoned) break; - for (size_t fi = 0; fi < features[m_outputNo].size(); ++fi) { - Vamp::Plugin::Feature feature = features[m_outputNo][fi]; - addFeature(blockFrame, feature); - } + for (int j = 0; j < (int)m_outputNos.size(); ++j) { + for (size_t fi = 0; fi < features[m_outputNos[j]].size(); ++fi) { + Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi]; + addFeature(j, blockFrame, feature); + } + } if (blockFrame == contextStart || completion > prevCompletion) { - setCompletion(completion); + for (int j = 0; j < (int)m_outputNos.size(); ++j) { + setCompletion(j, completion); + } prevCompletion = completion; } @@ -572,13 +633,17 @@ if (!m_abandoned) { Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures(); - for (size_t fi = 0; fi < features[m_outputNo].size(); ++fi) { - Vamp::Plugin::Feature feature = features[m_outputNo][fi]; - addFeature(blockFrame, feature); + for (int j = 0; j < (int)m_outputNos.size(); ++j) { + for (size_t fi = 0; fi < features[m_outputNos[j]].size(); ++fi) { + Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi]; + addFeature(j, blockFrame, feature); + } } } - setCompletion(100); + for (int j = 0; j < (int)m_outputNos.size(); ++j) { + setCompletion(j, 100); + } if (frequencyDomain) { for (size_t ch = 0; ch < channelCount; ++ch) { @@ -650,8 +715,9 @@ } void -FeatureExtractionModelTransformer::addFeature(size_t blockFrame, - const Vamp::Plugin::Feature &feature) +FeatureExtractionModelTransformer::addFeature(int n, + size_t blockFrame, + const Vamp::Plugin::Feature &feature) { size_t inputRate = m_input.getModel()->getSampleRate(); @@ -662,13 +728,13 @@ // << endl; int binCount = 1; - if (m_descriptor->hasFixedBinCount) { - binCount = m_descriptor->binCount; + if (m_descriptors[n]->hasFixedBinCount) { + binCount = m_descriptors[n]->binCount; } size_t frame = blockFrame; - if (m_descriptor->sampleType == + if (m_descriptors[n]->sampleType == Vamp::Plugin::OutputDescriptor::VariableSampleRate) { if (!feature.hasTimestamp) { @@ -681,18 +747,18 @@ frame = Vamp::RealTime::realTime2Frame(feature.timestamp, inputRate); } - } else if (m_descriptor->sampleType == + } else if (m_descriptors[n]->sampleType == Vamp::Plugin::OutputDescriptor::FixedSampleRate) { if (!feature.hasTimestamp) { - ++m_fixedRateFeatureNo; + ++m_fixedRateFeatureNos[n]; } else { RealTime ts(feature.timestamp.sec, feature.timestamp.nsec); - m_fixedRateFeatureNo = - lrint(ts.toDouble() * m_descriptor->sampleRate); + m_fixedRateFeatureNos[n] = + lrint(ts.toDouble() * m_descriptors[n]->sampleRate); } - frame = lrintf((m_fixedRateFeatureNo / m_descriptor->sampleRate) + frame = lrintf((m_fixedRateFeatureNos[n] / m_descriptors[n]->sampleRate) * inputRate); } @@ -701,19 +767,19 @@ // to, we instead test what sort of model the constructor decided // to create. - if (isOutput()) { + if (isOutput(n)) { SparseOneDimensionalModel *model = - getConformingOutput(); + getConformingOutput(n); if (!model) return; model->addPoint(SparseOneDimensionalModel::Point (frame, feature.label.c_str())); - } else if (isOutput()) { + } else if (isOutput(n)) { SparseTimeValueModel *model = - getConformingOutput(); + getConformingOutput(n); if (!model) return; for (int i = 0; i < feature.values.size(); ++i) { @@ -728,7 +794,7 @@ model->addPoint(SparseTimeValueModel::Point(frame, value, label)); } - } else if (isOutput() || isOutput() || isOutput()) { //GF: Added Note Model + } else if (isOutput(n) || isOutput(n) || isOutput(n)) { //GF: Added Note Model int index = 0; @@ -746,7 +812,7 @@ } } - if (isOutput()) { // GF: added for flexi note model + if (isOutput(n)) { // GF: added for flexi note model float velocity = 100; if (feature.values.size() > index) { @@ -755,14 +821,14 @@ if (velocity < 0) velocity = 127; if (velocity > 127) velocity = 127; - FlexiNoteModel *model = getConformingOutput(); + FlexiNoteModel *model = getConformingOutput(n); if (!model) return; model->addPoint(FlexiNoteModel::Point(frame, value, // value is pitch lrintf(duration), velocity / 127.f, feature.label.c_str())); // GF: end -- added for flexi note model - } else if (isOutput()) { + } else if (isOutput(n)) { float velocity = 100; if (feature.values.size() > index) { @@ -771,7 +837,7 @@ if (velocity < 0) velocity = 127; if (velocity > 127) velocity = 127; - NoteModel *model = getConformingOutput(); + NoteModel *model = getConformingOutput(n); if (!model) return; model->addPoint(NoteModel::Point(frame, value, // value is pitch lrintf(duration), @@ -779,7 +845,7 @@ feature.label.c_str())); } else { - RegionModel *model = getConformingOutput(); + RegionModel *model = getConformingOutput(n); if (!model) return; if (feature.hasDuration && !feature.values.empty()) { @@ -805,13 +871,13 @@ } } - } else if (isOutput()) { + } else if (isOutput(n)) { DenseThreeDimensionalModel::Column values = DenseThreeDimensionalModel::Column::fromStdVector(feature.values); EditableDenseThreeDimensionalModel *model = - getConformingOutput(); + getConformingOutput(n); if (!model) return; model->setColumn(frame / model->getResolution(), values); @@ -822,52 +888,52 @@ } void -FeatureExtractionModelTransformer::setCompletion(int completion) +FeatureExtractionModelTransformer::setCompletion(int n, int completion) { int binCount = 1; - if (m_descriptor->hasFixedBinCount) { - binCount = m_descriptor->binCount; + if (m_descriptors[n]->hasFixedBinCount) { + binCount = m_descriptors[n]->binCount; } // SVDEBUG << "FeatureExtractionModelTransformer::setCompletion(" // << completion << ")" << endl; - if (isOutput()) { + if (isOutput(n)) { SparseOneDimensionalModel *model = - getConformingOutput(); + getConformingOutput(n); if (!model) return; model->setCompletion(completion, true); - } else if (isOutput()) { + } else if (isOutput(n)) { SparseTimeValueModel *model = - getConformingOutput(); + getConformingOutput(n); if (!model) return; model->setCompletion(completion, true); - } else if (isOutput()) { + } else if (isOutput(n)) { - NoteModel *model = getConformingOutput(); + NoteModel *model = getConformingOutput(n); if (!model) return; model->setCompletion(completion, true); - } else if (isOutput()) { + } else if (isOutput(n)) { - FlexiNoteModel *model = getConformingOutput(); + FlexiNoteModel *model = getConformingOutput(n); if (!model) return; model->setCompletion(completion, true); - } else if (isOutput()) { + } else if (isOutput(n)) { - RegionModel *model = getConformingOutput(); + RegionModel *model = getConformingOutput(n); if (!model) return; model->setCompletion(completion, true); - } else if (isOutput()) { + } else if (isOutput(n)) { EditableDenseThreeDimensionalModel *model = - getConformingOutput(); + getConformingOutput(n); if (!model) return; model->setCompletion(completion, true); //!!!m_context.updates); } diff -r 2d53205f70cd -r c9846844ac11 transform/FeatureExtractionModelTransformer.h --- a/transform/FeatureExtractionModelTransformer.h Tue Nov 26 14:37:01 2013 +0000 +++ b/transform/FeatureExtractionModelTransformer.h Mon Dec 02 15:47:06 2013 +0000 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _FEATURE_EXTRACTION_PLUGIN_TRANSFORMER_H_ -#define _FEATURE_EXTRACTION_PLUGIN_TRANSFORMER_H_ +#ifndef _FEATURE_EXTRACTION_MODEL_TRANSFORMER_H_ +#define _FEATURE_EXTRACTION_MODEL_TRANSFORMER_H_ #include "ModelTransformer.h" @@ -31,33 +31,43 @@ Q_OBJECT public: - enum PreferredOutputModel { - NoteOutputModel, - FlexiNoteOutputModel, - UndefinedOutputModel = 255 - }; + enum PreferredOutputModel { + NoteOutputModel, + FlexiNoteOutputModel, + UndefinedOutputModel = 255 + }; FeatureExtractionModelTransformer(Input input, const Transform &transform, - const PreferredOutputModel outputmodel); + const PreferredOutputModel outputmodel); + + // Obtain outputs for a set of transforms that all use the same + // plugin and input (but with different outputs). i.e. run the + // plugin once only and collect more than one output from it. + FeatureExtractionModelTransformer(Input input, + const Transforms &relatedTransforms, + const PreferredOutputModel outputmodel); virtual ~FeatureExtractionModelTransformer(); protected: + bool initialise(); + virtual void run(); Vamp::Plugin *m_plugin; - Vamp::Plugin::OutputDescriptor *m_descriptor; - int m_fixedRateFeatureNo; // to assign times to FixedSampleRate features - int m_outputNo; + std::vector m_descriptors; // per transform + std::vector m_fixedRateFeatureNos; // to assign times to FixedSampleRate features + std::vector m_outputNos; PreferredOutputModel m_preferredOutputModel; - void createOutputModel(); + void createOutputModel(int n); - void addFeature(size_t blockFrame, + void addFeature(int n, + size_t blockFrame, const Vamp::Plugin::Feature &feature); - void setCompletion(int); + void setCompletion(int, int); void getFrames(int channelCount, long startFrame, long size, float **buffer); @@ -66,16 +76,21 @@ DenseTimeValueModel *getConformingInput(); - template bool isOutput() { - return dynamic_cast(m_output) != 0; + template bool isOutput(int n) { + return dynamic_cast(m_outputs[n]) != 0; } - template ModelClass *getConformingOutput() { - ModelClass *mc = dynamic_cast(m_output); - if (!mc) { - std::cerr << "FeatureExtractionModelTransformer::getOutput: Output model not conformable" << std::endl; - } - return mc; + template ModelClass *getConformingOutput(int n) { + if ((int)m_outputs.size() > n) { + ModelClass *mc = dynamic_cast(m_outputs[n]); + if (!mc) { + std::cerr << "FeatureExtractionModelTransformer::getOutput: Output model not conformable" << std::endl; + } + return mc; + } else { + std::cerr << "FeatureExtractionModelTransformer::getOutput: No such output number " << n << std::endl; + return 0; + } } }; diff -r 2d53205f70cd -r c9846844ac11 transform/ModelTransformer.cpp --- a/transform/ModelTransformer.cpp Tue Nov 26 14:37:01 2013 +0000 +++ b/transform/ModelTransformer.cpp Mon Dec 02 15:47:06 2013 +0000 @@ -16,9 +16,16 @@ #include "ModelTransformer.h" ModelTransformer::ModelTransformer(Input input, const Transform &transform) : - m_transform(transform), m_input(input), - m_output(0), + m_detached(false), + m_abandoned(false) +{ + m_transforms.push_back(transform); +} + +ModelTransformer::ModelTransformer(Input input, const Transforms &transforms) : + m_transforms(transforms), + m_input(input), m_detached(false), m_abandoned(false) { @@ -28,6 +35,10 @@ { m_abandoned = true; wait(); - if (!m_detached) delete m_output; + if (!m_detached) { + foreach (Model *m, m_outputs) { + delete m; + } + } } diff -r 2d53205f70cd -r c9846844ac11 transform/ModelTransformer.h --- a/transform/ModelTransformer.h Tue Nov 26 14:37:01 2013 +0000 +++ b/transform/ModelTransformer.h Mon Dec 02 15:47:06 2013 +0000 @@ -40,6 +40,8 @@ public: virtual ~ModelTransformer(); + typedef std::vector Models; + class Input { public: Input(Model *m) : m_model(m), m_channel(-1) { } @@ -76,18 +78,19 @@ int getInputChannel() { return m_input.getChannel(); } /** - * Return the output model created by the transform. Returns a - * null model if the transform could not be initialised; an error - * message may be available via getMessage() in this situation. + * Return the set of output models created by the transform or + * transforms. Returns an empty list if any transform could not + * be initialised; an error message may be available via + * getMessage() in this situation. */ - Model *getOutputModel() { return m_output; } + Models getOutputModels() { return m_outputs; } /** - * Return the output model, also detaching it from the transformer - * so that it will not be deleted when the transformer is. The - * caller takes ownership of the model. + * Return the set of output models, also detaching them from the + * transformer so that they will not be deleted when the + * transformer is. The caller takes ownership of the models. */ - Model *detachOutputModel() { m_detached = true; return m_output; } + Models detachOutputModels() { m_detached = true; return m_outputs; } /** * Return a warning or error message. If getOutputModel returned @@ -99,10 +102,11 @@ protected: ModelTransformer(Input input, const Transform &transform); + ModelTransformer(Input input, const Transforms &transforms); - Transform m_transform; + Transforms m_transforms; Input m_input; // I don't own the model in this - Model *m_output; // I own this, unless... + Models m_outputs; // I own this, unless... bool m_detached; // ... this is true. bool m_abandoned; QString m_message; diff -r 2d53205f70cd -r c9846844ac11 transform/ModelTransformerFactory.cpp --- a/transform/ModelTransformerFactory.cpp Tue Nov 26 14:37:01 2013 +0000 +++ b/transform/ModelTransformerFactory.cpp Mon Dec 02 15:47:06 2013 +0000 @@ -35,6 +35,8 @@ #include +using std::vector; + ModelTransformerFactory * ModelTransformerFactory::m_instance = new ModelTransformerFactory; @@ -163,66 +165,79 @@ } ModelTransformer * -ModelTransformerFactory::createTransformer(const Transform &transform, +ModelTransformerFactory::createTransformer(const Transforms &transforms, const ModelTransformer::Input &input) { ModelTransformer *transformer = 0; - QString id = transform.getPluginIdentifier(); + QString id = transforms[0].getPluginIdentifier(); if (FeatureExtractionPluginFactory::instanceFor(id)) { transformer = - new FeatureExtractionModelTransformer(input, transform, m_preferredOutputModel); + new FeatureExtractionModelTransformer(input, transforms, FeatureExtractionModelTransformer::FlexiNoteOutputModel); //!!! gross } else if (RealTimePluginFactory::instanceFor(id)) { transformer = - new RealTimeEffectModelTransformer(input, transform); + new RealTimeEffectModelTransformer(input, transforms[0]); } else { SVDEBUG << "ModelTransformerFactory::createTransformer: Unknown transform \"" - << transform.getIdentifier() << "\"" << endl; + << transforms[0].getIdentifier() << "\"" << endl; return transformer; } - if (transformer) transformer->setObjectName(transform.getIdentifier()); + if (transformer) transformer->setObjectName(transforms[0].getIdentifier()); return transformer; } Model * ModelTransformerFactory::transform(const Transform &transform, const ModelTransformer::Input &input, - QString &message, - /* outputmodel default value = FeatureExtractionModelTransformer::NoteOutputModel */ - FeatureExtractionModelTransformer::PreferredOutputModel outputmodel) + QString &message) { SVDEBUG << "ModelTransformerFactory::transform: Constructing transformer with input model " << input.getModel() << endl; - m_preferredOutputModel = outputmodel; - ModelTransformer *t = createTransformer(transform, input); - if (!t) return 0; + Transforms transforms; + transforms.push_back(transform); + vector mm = transformMultiple(transforms, input, message); + if (mm.empty()) return 0; + else return mm[0]; +} + +vector +ModelTransformerFactory::transformMultiple(const Transforms &transforms, + const ModelTransformer::Input &input, + QString &message) +{ + SVDEBUG << "ModelTransformerFactory::transformMultiple: Constructing transformer with input model " << input.getModel() << endl; + + ModelTransformer *t = createTransformer(transforms, input); + if (!t) return vector(); connect(t, SIGNAL(finished()), this, SLOT(transformerFinished())); m_runningTransformers.insert(t); t->start(); - Model *model = t->detachOutputModel(); + vector models = t->detachOutputModels(); - if (model) { + if (!models.empty()) { QString imn = input.getModel()->objectName(); QString trn = TransformFactory::getInstance()->getTransformFriendlyName - (transform.getIdentifier()); - if (imn != "") { - if (trn != "") { - model->setObjectName(tr("%1: %2").arg(imn).arg(trn)); - } else { - model->setObjectName(imn); + (transforms[0].getIdentifier()); + for (int i = 0; i < models.size(); ++i) { + if (imn != "") { + if (trn != "") { + models[i]->setObjectName(tr("%1: %2").arg(imn).arg(trn)); + } else { + models[i]->setObjectName(imn); + } + } else if (trn != "") { + models[i]->setObjectName(trn); } - } else if (trn != "") { - model->setObjectName(trn); } } else { t->wait(); @@ -230,7 +245,7 @@ message = t->getMessage(); - return model; + return models; } void @@ -269,8 +284,13 @@ ModelTransformer *t = *i; - if (t->getInputModel() == m || t->getOutputModel() == m) { + if (t->getInputModel() == m) { affected.insert(t); + } else { + vector mm = t->getOutputModels(); + for (int i = 0; i < (int)mm.size(); ++i) { + if (mm[i] == m) affected.insert(t); + } } } diff -r 2d53205f70cd -r c9846844ac11 transform/ModelTransformerFactory.h --- a/transform/ModelTransformerFactory.h Tue Nov 26 14:37:01 2013 +0000 +++ b/transform/ModelTransformerFactory.h Mon Dec 02 15:47:06 2013 +0000 @@ -27,6 +27,7 @@ #include #include #include +#include class AudioPlaySource; @@ -84,10 +85,33 @@ * The returned model is owned by the caller and must be deleted * when no longer needed. */ - Model *transform(const Transform &transform, - const ModelTransformer::Input &input, - QString &message, - const FeatureExtractionModelTransformer::PreferredOutputModel outputmodel = FeatureExtractionModelTransformer::NoteOutputModel); + Model *transform(const Transform &transform, + const ModelTransformer::Input &input, + QString &message); + + /** + * Return the multiple output models resulting from applying the + * named transforms to the given input model. The transforms may + * differ only in output identifier for the plugin: they must all + * use the same plugin, parameters, and programs. The plugin will + * be run once only, but more than one output will be harvested + * (as appropriate). Models will be returned in the same order as + * the transforms were given. The plugin may still be working in + * the background when the model is returned; check the output + * models' isReady completion statuses for more details. + * + * If a transform is unknown or the transforms are insufficiently + * closely related or the input model is not an appropriate type + * for the given transform, or if some other problem occurs, + * return 0. Set message if there is any error or warning to + * report. + * + * The returned models are owned by the caller and must be deleted + * when no longer needed. + */ + std::vector transformMultiple(const Transforms &transform, + const ModelTransformer::Input &input, + QString &message); protected slots: void transformerFinished(); @@ -95,7 +119,7 @@ void modelAboutToBeDeleted(Model *); protected: - ModelTransformer *createTransformer(const Transform &transform, + ModelTransformer *createTransformer(const Transforms &transforms, const ModelTransformer::Input &input); typedef std::map TransformerConfigurationMap; diff -r 2d53205f70cd -r c9846844ac11 transform/RealTimeEffectModelTransformer.cpp --- a/transform/RealTimeEffectModelTransformer.cpp Tue Nov 26 14:37:01 2013 +0000 +++ b/transform/RealTimeEffectModelTransformer.cpp Mon Dec 02 15:47:06 2013 +0000 @@ -30,10 +30,16 @@ #include RealTimeEffectModelTransformer::RealTimeEffectModelTransformer(Input in, - const Transform &transform) : - ModelTransformer(in, transform), + const Transform &t) : + ModelTransformer(in, t), m_plugin(0) { + Transform transform(t); + if (!transform.getBlockSize()) { + transform.setBlockSize(1024); + m_transforms[0] = transform; + } + m_units = TransformFactory::getInstance()->getTransformUnits (transform.getIdentifier()); m_outputNo = @@ -41,8 +47,6 @@ QString pluginId = transform.getPluginIdentifier(); - if (!m_transform.getBlockSize()) m_transform.setBlockSize(1024); - // SVDEBUG << "RealTimeEffectModelTransformer::RealTimeEffectModelTransformer: plugin " << pluginId << ", output " << output << endl; RealTimePluginFactory *factory = @@ -59,16 +63,16 @@ m_plugin = factory->instantiatePlugin(pluginId, 0, 0, input->getSampleRate(), - m_transform.getBlockSize(), + transform.getBlockSize(), input->getChannelCount()); if (!m_plugin) { cerr << "RealTimeEffectModelTransformer: Failed to instantiate plugin \"" - << pluginId << "\"" << endl; + << pluginId << "\"" << endl; return; } - TransformFactory::getInstance()->setPluginParameters(m_transform, m_plugin); + TransformFactory::getInstance()->setPluginParameters(transform, m_plugin); if (m_outputNo >= 0 && m_outputNo >= int(m_plugin->getControlOutputCount())) { @@ -86,16 +90,16 @@ WritableWaveFileModel *model = new WritableWaveFileModel (input->getSampleRate(), outputChannels); - m_output = model; + m_outputs.push_back(model); } else { SparseTimeValueModel *model = new SparseTimeValueModel - (input->getSampleRate(), m_transform.getBlockSize(), 0.0, 0.0, false); + (input->getSampleRate(), transform.getBlockSize(), 0.0, 0.0, false); if (m_units != "") model->setScaleUnits(m_units); - m_output = model; + m_outputs.push_back(model); } } @@ -127,8 +131,8 @@ } if (m_abandoned) return; - SparseTimeValueModel *stvm = dynamic_cast(m_output); - WritableWaveFileModel *wwfm = dynamic_cast(m_output); + SparseTimeValueModel *stvm = dynamic_cast(m_outputs[0]); + WritableWaveFileModel *wwfm = dynamic_cast(m_outputs[0]); if (!stvm && !wwfm) return; if (stvm && (m_outputNo >= int(m_plugin->getControlOutputCount()))) return; @@ -143,9 +147,11 @@ long startFrame = m_input.getModel()->getStartFrame(); long endFrame = m_input.getModel()->getEndFrame(); + + Transform transform = m_transforms[0]; - RealTime contextStartRT = m_transform.getStartTime(); - RealTime contextDurationRT = m_transform.getDuration(); + RealTime contextStartRT = transform.getStartTime(); + RealTime contextDurationRT = transform.getDuration(); long contextStart = RealTime::realTime2Frame(contextStartRT, sampleRate); diff -r 2d53205f70cd -r c9846844ac11 transform/RealTimeEffectModelTransformer.h --- a/transform/RealTimeEffectModelTransformer.h Tue Nov 26 14:37:01 2013 +0000 +++ b/transform/RealTimeEffectModelTransformer.h Mon Dec 02 15:47:06 2013 +0000 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _REAL_TIME_PLUGIN_TRANSFORMER_H_ -#define _REAL_TIME_PLUGIN_TRANSFORMER_H_ +#ifndef _REAL_TIME_EFFECT_TRANSFORMER_H_ +#define _REAL_TIME_EFFECT_TRANSFORMER_H_ #include "ModelTransformer.h" #include "plugin/RealTimePluginInstance.h" diff -r 2d53205f70cd -r c9846844ac11 transform/Transform.h --- a/transform/Transform.h Tue Nov 26 14:37:01 2013 +0000 +++ b/transform/Transform.h Mon Dec 02 15:47:06 2013 +0000 @@ -25,6 +25,7 @@ #include #include +#include typedef QString TransformId; @@ -196,5 +197,7 @@ float m_sampleRate; }; +typedef std::vector Transforms; + #endif