# HG changeset patch # User Chris Cannam # Date 1561557549 -3600 # Node ID 5655754637528fa940fce27f06dca07b1964d2c2 # Parent 4abc0f08adf90c75545536bb32bad46eb433ac0b Some work on models and transformers diff -r 4abc0f08adf9 -r 565575463752 base/ById.h --- a/base/ById.h Wed Jun 26 10:21:15 2019 +0100 +++ b/base/ById.h Wed Jun 26 14:59:09 2019 +0100 @@ -54,6 +54,19 @@ }; template +std::ostream & +operator<<(std::ostream &ostr, const SvId &id) +{ + // For diagnostic purposes only. Do not use these IDs for + // serialisation - see XmlExportable instead. + if (id.isNone()) { + return (ostr << ""); + } else { + return (ostr << "#" << id.id); + } +} + +template class WithId { public: @@ -113,6 +126,9 @@ void add(std::shared_ptr item) { QMutexLocker locker(&m_mutex); auto id = item->getId(); + if (id.isNone()) { + throw std::logic_error("item id should never be None"); + } if (m_items.find(id) != m_items.end()) { SVCERR << "WARNING: ById::add: item with id " << id << " is already recorded, replacing it (item type is " @@ -128,12 +144,13 @@ } std::shared_ptr get(Id id) const { + if (id.isNone()) return {}; // this id is never issued: avoid locking QMutexLocker locker(&m_mutex); const auto &itr = m_items.find(id); if (itr != m_items.end()) { return itr->second; } else { - return std::shared_ptr(); + return {}; } } @@ -144,9 +161,11 @@ /** * If the Item type is an XmlExportable, return the export ID of - * the given item ID. The export ID is a simple int, and is only - * allocated when first requested, so objects that are never - * exported don't get one. + * the given item ID. A call to this function will fail to compile + * if the Item is not an XmlExportable. + * + * The export ID is a simple int, and is only allocated when first + * requested, so objects that are never exported don't get one. */ int getExportId(Id id) const { auto exportable = getAs(id); diff -r 4abc0f08adf9 -r 565575463752 data/model/Dense3DModelPeakCache.cpp --- a/data/model/Dense3DModelPeakCache.cpp Wed Jun 26 10:21:15 2019 +0100 +++ b/data/model/Dense3DModelPeakCache.cpp Wed Jun 26 14:59:09 2019 +0100 @@ -19,34 +19,36 @@ #include "base/HitCount.h" -Dense3DModelPeakCache::Dense3DModelPeakCache(const DenseThreeDimensionalModel *source, +Dense3DModelPeakCache::Dense3DModelPeakCache(ModelId sourceId, int columnsPerPeak) : - m_source(source), + m_source(sourceId), m_columnsPerPeak(columnsPerPeak) { - m_cache = new EditableDenseThreeDimensionalModel - (source->getSampleRate(), - getResolution(), - source->getHeight(), - EditableDenseThreeDimensionalModel::NoCompression, - false); + auto source = ModelById::getAs(m_source); + if (!source) { + SVCERR << "WARNING: Dense3DModelPeakCache constructed for unknown or wrong-type source model id " << m_source << endl; + m_source = {}; + return; + } - connect(source, SIGNAL(modelChanged()), + m_cache.reset(new EditableDenseThreeDimensionalModel + (source->getSampleRate(), + source->getResolution() * m_columnsPerPeak, + source->getHeight(), + EditableDenseThreeDimensionalModel::NoCompression, + false)); + + connect(source.get(), SIGNAL(modelChanged()), this, SLOT(sourceModelChanged())); - connect(source, SIGNAL(aboutToBeDeleted()), - this, SLOT(sourceModelAboutToBeDeleted())); } Dense3DModelPeakCache::~Dense3DModelPeakCache() { - if (m_cache) m_cache->aboutToDelete(); - delete m_cache; } Dense3DModelPeakCache::Column Dense3DModelPeakCache::getColumn(int column) const { - if (!m_source) return Column(); if (!haveColumn(column)) fillColumn(column); return m_cache->getColumn(column); } @@ -54,7 +56,6 @@ float Dense3DModelPeakCache::getValueAt(int column, int n) const { - if (!m_source) return 0.f; if (!haveColumn(column)) fillColumn(column); return m_cache->getValueAt(column, n); } @@ -62,7 +63,6 @@ void Dense3DModelPeakCache::sourceModelChanged() { - if (!m_source) return; if (m_coverage.size() > 0) { // The last peak may have come from an incomplete read, which // may since have been filled, so reset it @@ -71,12 +71,6 @@ m_coverage.resize(getWidth(), false); // retaining data } -void -Dense3DModelPeakCache::sourceModelAboutToBeDeleted() -{ - m_source = nullptr; -} - bool Dense3DModelPeakCache::haveColumn(int column) const { @@ -104,7 +98,10 @@ m_coverage.resize(column + 1, false); } - int sourceWidth = m_source->getWidth(); + auto source = ModelById::getAs(m_source); + if (!source) return; + + int sourceWidth = source->getWidth(); Column peak; int n = 0; @@ -113,7 +110,7 @@ int sourceColumn = column * m_columnsPerPeak + i; if (sourceColumn >= sourceWidth) break; - Column here = m_source->getColumn(sourceColumn); + Column here = source->getColumn(sourceColumn); // cerr << "Dense3DModelPeakCache::fillColumn(" << column << "): source col " // << sourceColumn << " of " << sourceWidth diff -r 4abc0f08adf9 -r 565575463752 data/model/Dense3DModelPeakCache.h --- a/data/model/Dense3DModelPeakCache.h Wed Jun 26 10:21:15 2019 +0100 +++ b/data/model/Dense3DModelPeakCache.h Wed Jun 26 14:59:09 2019 +0100 @@ -24,28 +24,33 @@ Q_OBJECT public: - Dense3DModelPeakCache(const DenseThreeDimensionalModel *source, + Dense3DModelPeakCache(ModelId source, // a DenseThreeDimensionalModel int columnsPerPeak); ~Dense3DModelPeakCache(); bool isOK() const override { - return m_source && m_source->isOK(); + auto source = ModelById::get(m_source); + return source && source->isOK(); } sv_samplerate_t getSampleRate() const override { - return m_source->getSampleRate(); + auto source = ModelById::get(m_source); + return source ? source->getSampleRate() : 0; } sv_frame_t getStartFrame() const override { - return m_source->getStartFrame(); + auto source = ModelById::get(m_source); + return source ? source->getStartFrame() : 0; } sv_frame_t getTrueEndFrame() const override { - return m_source->getTrueEndFrame(); + auto source = ModelById::get(m_source); + return source ? source->getTrueEndFrame() : 0; } int getResolution() const override { - return m_source->getResolution() * m_columnsPerPeak; + auto source = ModelById::getAs(m_source); + return source ? source->getResolution() * m_columnsPerPeak : 1; } virtual int getColumnsPerPeak() const { @@ -53,7 +58,9 @@ } int getWidth() const override { - int sourceWidth = m_source->getWidth(); + auto source = ModelById::getAs(m_source); + if (!source) return 0; + int sourceWidth = source->getWidth(); if ((sourceWidth % m_columnsPerPeak) == 0) { return sourceWidth / m_columnsPerPeak; } else { @@ -62,15 +69,18 @@ } int getHeight() const override { - return m_source->getHeight(); + auto source = ModelById::getAs(m_source); + return source ? source->getHeight() : 0; } float getMinimumLevel() const override { - return m_source->getMinimumLevel(); + auto source = ModelById::getAs(m_source); + return source ? source->getMinimumLevel() : 0.f; } float getMaximumLevel() const override { - return m_source->getMaximumLevel(); + auto source = ModelById::getAs(m_source); + return source ? source->getMaximumLevel() : 1.f; } /** @@ -84,17 +94,20 @@ float getValueAt(int col, int n) const override; QString getBinName(int n) const override { - return m_source->getBinName(n); + auto source = ModelById::getAs(m_source); + return source ? source->getBinName(n) : ""; } bool shouldUseLogValueScale() const override { - return m_source->shouldUseLogValueScale(); + auto source = ModelById::getAs(m_source); + return source ? source->shouldUseLogValueScale() : false; } QString getTypeName() const override { return tr("Dense 3-D Peak Cache"); } int getCompletion() const override { - return m_source->getCompletion(); + auto source = ModelById::get(m_source); + return source ? source->getCompletion() : 100; } QString toDelimitedDataString(QString, DataExportOptions, @@ -104,11 +117,10 @@ protected slots: void sourceModelChanged(); - void sourceModelAboutToBeDeleted(); private: - const DenseThreeDimensionalModel *m_source; - mutable EditableDenseThreeDimensionalModel *m_cache; + ModelId m_source; + mutable std::unique_ptr m_cache; mutable std::vector m_coverage; // must be bool, for space efficiency // (vector of bool uses 1-bit elements) int m_columnsPerPeak; diff -r 4abc0f08adf9 -r 565575463752 data/model/Model.cpp --- a/data/model/Model.cpp Wed Jun 26 10:21:15 2019 +0100 +++ b/data/model/Model.cpp Wed Jun 26 14:59:09 2019 +0100 @@ -24,8 +24,8 @@ Model::~Model() { - SVDEBUG << "Model::~Model(" << this << ")" << endl; - + SVDEBUG << "Model::~Model: " << this << " with id " << getId() << endl; +/*!!! if (!m_aboutToDelete) { SVDEBUG << "NOTE: Model(" << this << ", \"" << objectName() << "\", type uri <" @@ -33,10 +33,13 @@ << "with no aboutToDelete notification" << endl; } - +*/ + //!!! see notes in header - sort this out + /* if (!m_alignmentModel.isNone()) { ModelById::release(m_alignmentModel); } + */ } void @@ -130,29 +133,39 @@ sv_frame_t Model::alignToReference(sv_frame_t frame) const { -// cerr << "Model(" << this << ")::alignToReference(" << frame << ")" << endl; - if (!m_alignment) { - if (m_sourceModel) return m_sourceModel->alignToReference(frame); - else return frame; + auto alignmentModel = ModelById::getAs(m_alignmentModel); + + if (!alignmentModel) { + auto sourceModel = ModelById::get(m_sourceModel); + if (sourceModel) { + return sourceModel->alignToReference(frame); + } + return frame; } - sv_frame_t refFrame = m_alignment->toReference(frame); - const Model *m = m_alignment->getReferenceModel(); - if (m && refFrame > m->getEndFrame()) refFrame = m->getEndFrame(); -// cerr << "have alignment, aligned is " << refFrame << endl; + + sv_frame_t refFrame = alignmentModel->toReference(frame); + auto refModel = ModelById::get(alignmentModel->getReferenceModel()); + if (refModel && refFrame > refModel->getEndFrame()) { + refFrame = refModel->getEndFrame(); + } return refFrame; } sv_frame_t Model::alignFromReference(sv_frame_t refFrame) const { -// cerr << "Model(" << this << ")::alignFromReference(" << refFrame << ")" << endl; - if (!m_alignment) { - if (m_sourceModel) return m_sourceModel->alignFromReference(refFrame); - else return refFrame; + auto alignmentModel = ModelById::getAs(m_alignmentModel); + + if (!alignmentModel) { + auto sourceModel = ModelById::get(m_sourceModel); + if (sourceModel) { + return sourceModel->alignFromReference(refFrame); + } + return refFrame; } - sv_frame_t frame = m_alignment->fromReference(refFrame); + + sv_frame_t frame = alignmentModel->fromReference(refFrame); if (frame > getEndFrame()) frame = getEndFrame(); -// cerr << "have alignment, aligned is " << frame << endl; return frame; } @@ -160,17 +173,26 @@ Model::getAlignmentCompletion() const { #ifdef DEBUG_COMPLETION - SVCERR << "Model(" << this << ")::getAlignmentCompletion: m_alignment = " - << m_alignment << endl; + SVCERR << "Model(" << this + << ")::getAlignmentCompletion: m_alignmentModel = " + << m_alignmentModel << endl; #endif - if (!m_alignment) { - if (m_sourceModel) return m_sourceModel->getAlignmentCompletion(); - else return 100; + + auto alignmentModel = ModelById::getAs(m_alignmentModel); + + if (!alignmentModel) { + auto sourceModel = ModelById::get(m_sourceModel); + if (sourceModel) { + return sourceModel->getAlignmentCompletion(); + } + return 100; } + int completion = 0; - (void)m_alignment->isReady(&completion); + (void)alignmentModel->isReady(&completion); #ifdef DEBUG_COMPLETION - SVCERR << "Model(" << this << ")::getAlignmentCompletion: completion = " << completion + SVCERR << "Model(" << this + << ")::getAlignmentCompletion: completion = " << completion << endl; #endif return completion; @@ -179,21 +201,24 @@ QString Model::getTitle() const { - if (m_sourceModel) return m_sourceModel->getTitle(); + auto source = ModelById::get(m_sourceModel); + if (source) return source->getTitle(); else return ""; } QString Model::getMaker() const { - if (m_sourceModel) return m_sourceModel->getMaker(); + auto source = ModelById::get(m_sourceModel); + if (source) return source->getMaker(); else return ""; } QString Model::getLocation() const { - if (m_sourceModel) return m_sourceModel->getLocation(); + auto source = ModelById::get(m_sourceModel); + if (source) return source->getLocation(); else return ""; } diff -r 4abc0f08adf9 -r 565575463752 rdf/RDFImporter.cpp --- a/rdf/RDFImporter.cpp Wed Jun 26 10:21:15 2019 +0100 +++ b/rdf/RDFImporter.cpp Wed Jun 26 14:59:09 2019 +0100 @@ -668,7 +668,7 @@ if (m_audioModelMap.find(source) != m_audioModelMap.end()) { cerr << "source model for " << model << " is " << m_audioModelMap[source] << endl; - model->setSourceModel(m_audioModelMap[source]); + model->setSourceModel(m_audioModelMap[source]->getId()); } QString title = m_store->complete diff -r 4abc0f08adf9 -r 565575463752 rdf/RDFImporter.h --- a/rdf/RDFImporter.h Wed Jun 26 10:21:15 2019 +0100 +++ b/rdf/RDFImporter.h Wed Jun 26 14:59:09 2019 +0100 @@ -47,6 +47,14 @@ bool isOK(); QString getErrorString() const; + /** + * Return a list of models imported from the RDF source. The + * models were heap-allocated by this class and are not registered + * with any other owner; the caller takes ownership and must + * arrange for them to be deleted manually or managed by a smart + * pointer. + */ + //!!! todo: ModelId-ise std::vector getDataModels(ProgressReporter *reporter); enum RDFDocumentType { diff -r 4abc0f08adf9 -r 565575463752 transform/FeatureExtractionModelTransformer.cpp --- a/transform/FeatureExtractionModelTransformer.cpp Wed Jun 26 10:21:15 2019 +0100 +++ b/transform/FeatureExtractionModelTransformer.cpp Wed Jun 26 14:59:09 2019 +0100 @@ -83,7 +83,7 @@ // 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) { + for (int j = 1; in_range_for(m_transforms, j); ++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"); SVCERR << m_message << endl; @@ -104,7 +104,7 @@ return false; } - DenseTimeValueModel *input = getConformingInput(); + auto input = ModelById::getAs(getInputModel()); if (!input) { m_message = tr("Input model for feature extraction plugin \"%1\" is of wrong type (internal error?)").arg(pluginId); SVCERR << m_message << endl; @@ -158,7 +158,9 @@ SVDEBUG << "Initialisation failed, trying again with preferred step = " << preferredStep << ", block = " << preferredBlock << endl; - if (!m_plugin->initialise(channelCount, preferredStep, preferredBlock)) { + if (!m_plugin->initialise(channelCount, + preferredStep, + preferredBlock)) { SVDEBUG << "Initialisation failed again" << endl; @@ -221,20 +223,22 @@ return false; } - for (int j = 0; j < (int)m_transforms.size(); ++j) { + for (int j = 0; in_range_for(m_transforms, j); ++j) { - for (int i = 0; i < (int)outputs.size(); ++i) { -// SVDEBUG << "comparing output " << i << " name \"" << outputs[i].identifier << "\" with expected \"" << m_transform.getOutput() << "\"" << endl; + for (int i = 0; in_range_for(outputs, i); ++i) { + if (m_transforms[j].getOutput() == "" || - outputs[i].identifier == m_transforms[j].getOutput().toStdString()) { + outputs[i].identifier == + m_transforms[j].getOutput().toStdString()) { + m_outputNos.push_back(i); - m_descriptors.push_back(new Vamp::Plugin::OutputDescriptor(outputs[i])); + m_descriptors.push_back(outputs[i]); m_fixedRateFeatureNos.push_back(-1); // we increment before use break; } } - if ((int)m_descriptors.size() <= j) { + if (!in_range_for(m_descriptors, j)) { m_message = tr("Plugin \"%1\" has no output named \"%2\"") .arg(pluginId) .arg(m_transforms[j].getOutput()); @@ -243,7 +247,7 @@ } } - for (int j = 0; j < (int)m_transforms.size(); ++j) { + for (int j = 0; in_range_for(m_transforms, j); ++j) { createOutputModels(j); } @@ -271,16 +275,15 @@ m_message = e.what(); } m_plugin = nullptr; - - for (int j = 0; j < (int)m_descriptors.size(); ++j) { - delete m_descriptors[j]; - } + + m_descriptors.clear(); } void FeatureExtractionModelTransformer::createOutputModels(int n) { - DenseTimeValueModel *input = getConformingInput(); + auto input = ModelById::getAs(getInputModel()); + if (!input) return; PluginRDFDescription description(m_transforms[n].getPluginIdentifier()); QString outputId = m_transforms[n].getOutput(); @@ -288,20 +291,17 @@ int binCount = 1; float minValue = 0.0, maxValue = 0.0; bool haveExtents = false; - bool haveBinCount = m_descriptors[n]->hasFixedBinCount; + bool haveBinCount = m_descriptors[n].hasFixedBinCount; if (haveBinCount) { - binCount = (int)m_descriptors[n]->binCount; + binCount = (int)m_descriptors[n].binCount; } m_needAdditionalModels[n] = false; -// cerr << "FeatureExtractionModelTransformer: output bin count " -// << binCount << endl; - - if (binCount > 0 && m_descriptors[n]->hasKnownExtents) { - minValue = m_descriptors[n]->minValue; - maxValue = m_descriptors[n]->maxValue; + if (binCount > 0 && m_descriptors[n].hasKnownExtents) { + minValue = m_descriptors[n].minValue; + maxValue = m_descriptors[n].maxValue; haveExtents = true; } @@ -309,10 +309,10 @@ sv_samplerate_t outputRate = modelRate; int modelResolution = 1; - if (m_descriptors[n]->sampleType != + if (m_descriptors[n].sampleType != Vamp::Plugin::OutputDescriptor::OneSamplePerStep) { - outputRate = m_descriptors[n]->sampleRate; + outputRate = m_descriptors[n].sampleRate; //!!! SV doesn't actually support display of models that have //!!! different underlying rates together -- so we always set @@ -328,7 +328,7 @@ } } - switch (m_descriptors[n]->sampleType) { + switch (m_descriptors[n].sampleType) { case Vamp::Plugin::OutputDescriptor::VariableSampleRate: if (outputRate != 0.0) { @@ -342,7 +342,7 @@ case Vamp::Plugin::OutputDescriptor::FixedSampleRate: if (outputRate <= 0.0) { - SVDEBUG << "WARNING: Fixed sample-rate plugin reports invalid sample rate " << m_descriptors[n]->sampleRate << "; defaulting to input rate of " << input->getSampleRate() << endl; + SVDEBUG << "WARNING: Fixed sample-rate plugin reports invalid sample rate " << m_descriptors[n].sampleRate << "; defaulting to input rate of " << input->getSampleRate() << endl; modelResolution = 1; } else { modelResolution = int(round(modelRate / outputRate)); @@ -353,21 +353,23 @@ bool preDurationPlugin = (m_plugin->getVampApiVersion() < 2); - Model *out = nullptr; + std::shared_ptr out; if (binCount == 0 && - (preDurationPlugin || !m_descriptors[n]->hasDuration)) { + (preDurationPlugin || !m_descriptors[n].hasDuration)) { // Anything with no value and no duration is an instant - out = new SparseOneDimensionalModel(modelRate, modelResolution, false); + out = std::make_shared + (modelRate, modelResolution, false); + QString outputEventTypeURI = description.getOutputEventTypeURI(outputId); out->setRDFTypeURI(outputEventTypeURI); } else if ((preDurationPlugin && binCount > 1 && - (m_descriptors[n]->sampleType == + (m_descriptors[n].sampleType == Vamp::Plugin::OutputDescriptor::VariableSampleRate)) || - (!preDurationPlugin && m_descriptors[n]->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 @@ -398,9 +400,9 @@ // Regions do not have units of Hz or MIDI things (a sweeping // assumption!) - if (m_descriptors[n]->unit == "Hz" || - m_descriptors[n]->unit.find("MIDI") != std::string::npos || - m_descriptors[n]->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; } @@ -420,8 +422,8 @@ model = new NoteModel (modelRate, modelResolution, false); } - model->setScaleUnits(m_descriptors[n]->unit.c_str()); - out = model; + model->setScaleUnits(m_descriptors[n].unit.c_str()); + out.reset(model); } else { @@ -433,15 +435,15 @@ model = new RegionModel (modelRate, modelResolution, false); } - model->setScaleUnits(m_descriptors[n]->unit.c_str()); - out = model; + model->setScaleUnits(m_descriptors[n].unit.c_str()); + out.reset(model); } QString outputEventTypeURI = description.getOutputEventTypeURI(outputId); out->setRDFTypeURI(outputEventTypeURI); } else if (binCount == 1 || - (m_descriptors[n]->sampleType == + (m_descriptors[n].sampleType == Vamp::Plugin::OutputDescriptor::VariableSampleRate)) { // Anything that is not a 1D, note, or interval model and that @@ -482,7 +484,7 @@ Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors(); model->setScaleUnits(outputs[m_outputNos[n]].unit.c_str()); - out = model; + out.reset(model); QString outputEventTypeURI = description.getOutputEventTypeURI(outputId); out->setRDFTypeURI(outputEventTypeURI); @@ -499,23 +501,24 @@ EditableDenseThreeDimensionalModel::BasicMultirateCompression, false); - if (!m_descriptors[n]->binNames.empty()) { + if (!m_descriptors[n].binNames.empty()) { std::vector names; - for (int i = 0; i < (int)m_descriptors[n]->binNames.size(); ++i) { - names.push_back(m_descriptors[n]->binNames[i].c_str()); + for (int i = 0; i < (int)m_descriptors[n].binNames.size(); ++i) { + names.push_back(m_descriptors[n].binNames[i].c_str()); } model->setBinNames(names); } - out = model; + out.reset(model); QString outputSignalTypeURI = description.getOutputSignalTypeURI(outputId); out->setRDFTypeURI(outputSignalTypeURI); } if (out) { - out->setSourceModel(input); - m_outputs.push_back(out); + out->setSourceModel(getInputModel()); + ModelById::add(out); + m_outputs.push_back(out->getId()); } } @@ -540,13 +543,9 @@ FeatureExtractionModelTransformer::getAdditionalOutputModels() { Models mm; - for (AdditionalModelMap::iterator i = m_additionalModels.begin(); - i != m_additionalModels.end(); ++i) { - for (std::map::iterator j = - i->second.begin(); - j != i->second.end(); ++j) { - SparseTimeValueModel *m = j->second; - if (m) mm.push_back(m); + for (auto mp : m_additionalModels) { + for (auto m: mp.second) { + mm.push_back(m.second); } } return mm; @@ -555,34 +554,45 @@ bool FeatureExtractionModelTransformer::willHaveAdditionalOutputModels() { - for (std::map::const_iterator i = - m_needAdditionalModels.begin(); - i != m_needAdditionalModels.end(); ++i) { - if (i->second) return true; + for (auto p : m_needAdditionalModels) { + if (p.second) return true; } return false; } -SparseTimeValueModel * +ModelId FeatureExtractionModelTransformer::getAdditionalModel(int n, int binNo) { -// std::cerr << "getAdditionalModel(" << n << ", " << binNo << ")" << std::endl; - if (binNo == 0) { - std::cerr << "Internal error: binNo == 0 in getAdditionalModel (should be using primary model)" << std::endl; - return nullptr; + SVCERR << "Internal error: binNo == 0 in getAdditionalModel (should be using primary model, not calling getAdditionalModel)" << endl; + return {}; } - if (!m_needAdditionalModels[n]) return nullptr; - if (!isOutput(n)) return nullptr; - if (m_additionalModels[n][binNo]) return m_additionalModels[n][binNo]; + if (!in_range_for(m_outputs, n)) { + SVCERR << "getAdditionalModel: Output " << n << " out of range" << endl; + return {}; + } - std::cerr << "getAdditionalModel(" << n << ", " << binNo << "): creating" << std::endl; + if (!in_range_for(m_needAdditionalModels, n) || + !m_needAdditionalModels[n]) { + return {}; + } + + if (!m_additionalModels[n][binNo].isNone()) { + return m_additionalModels[n][binNo]; + } - SparseTimeValueModel *baseModel = getConformingOutput(n); - if (!baseModel) return nullptr; + SVDEBUG << "getAdditionalModel(" << n << ", " << binNo + << "): creating" << endl; - std::cerr << "getAdditionalModel(" << n << ", " << binNo << "): (from " << baseModel << ")" << std::endl; + auto baseModel = ModelById::getAs(m_outputs[n]); + if (!baseModel) { + SVCERR << "getAdditionalModel: Output model not conformable, or has vanished" << endl; + return {}; + } + + SVDEBUG << "getAdditionalModel(" << n << ", " << binNo + << "): (from " << baseModel << ")" << endl; SparseTimeValueModel *additional = new SparseTimeValueModel(baseModel->getSampleRate(), @@ -594,21 +604,10 @@ additional->setScaleUnits(baseModel->getScaleUnits()); additional->setRDFTypeURI(baseModel->getRDFTypeURI()); - m_additionalModels[n][binNo] = additional; - return additional; -} - -DenseTimeValueModel * -FeatureExtractionModelTransformer::getConformingInput() -{ -// SVDEBUG << "FeatureExtractionModelTransformer::getConformingInput: input model is " << getInputModel() << endl; - - DenseTimeValueModel *dtvm = - dynamic_cast(getInputModel()); - if (!dtvm) { - SVDEBUG << "FeatureExtractionModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << endl; - } - return dtvm; + ModelId additionalId = additional->getId(); + ModelById::add(std::shared_ptr(additional)); + m_additionalModels[n][binNo] = additionalId; + return additionalId; } void @@ -624,12 +623,6 @@ m_message = e.what(); return; } - - DenseTimeValueModel *input = getConformingInput(); - if (!input) { - abandon(); - return; - } if (m_outputs.empty()) { abandon(); @@ -638,14 +631,30 @@ Transform primaryTransform = m_transforms[0]; - while (!input->isReady() && !m_abandoned) { - SVDEBUG << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << endl; - usleep(500000); + bool ready = false; + while (!ready && !m_abandoned) { + { // scope so as to release input shared_ptr before sleeping + auto input = ModelById::getAs(getInputModel()); + if (!input) { + abandon(); + return; + } + ready = input->isReady(); + } + if (!ready) { + SVDEBUG << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << endl; + usleep(500000); + } } - SVDEBUG << "FeatureExtractionModelTransformer::run: Waited, ready = " - << input->isReady() << ", m_abandoned = " << m_abandoned << endl; if (m_abandoned) return; + auto input = ModelById::getAs(getInputModel()); + if (!input) { + SVCERR << "FeatureExtractionModelTransformer::run: Input model not (no longer?) available, abandoning" << endl; + abandon(); + return; + } + sv_samplerate_t sampleRate = input->getSampleRate(); int channelCount = input->getChannelCount(); @@ -669,28 +678,28 @@ if (frequencyDomain) { for (int ch = 0; ch < channelCount; ++ch) { FFTModel *model = new FFTModel - (getConformingInput(), - channelCount == 1 ? m_input.getChannel() : ch, - primaryTransform.getWindowType(), - blockSize, - stepSize, - blockSize); +//!!! (input->getId(), + (nullptr, + channelCount == 1 ? m_input.getChannel() : ch, + primaryTransform.getWindowType(), + blockSize, + stepSize, + blockSize); if (!model->isOK() || model->getError() != "") { QString err = model->getError(); delete model; - for (int j = 0; j < (int)m_outputNos.size(); ++j) { + for (int j = 0; in_range_for(m_outputNos, j); ++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: error is: " + err); } fftModels.push_back(model); - cerr << "created model for channel " << ch << endl; } } - sv_frame_t startFrame = m_input.getModel()->getStartFrame(); - sv_frame_t endFrame = m_input.getModel()->getEndFrame(); + sv_frame_t startFrame = input->getStartFrame(); + sv_frame_t endFrame = input->getEndFrame(); RealTime contextStartRT = primaryTransform.getStartTime(); RealTime contextDurationRT = primaryTransform.getDuration(); @@ -716,7 +725,7 @@ long prevCompletion = 0; - for (int j = 0; j < (int)m_outputNos.size(); ++j) { + for (int j = 0; in_range_for(m_outputNos, j); ++j) { setCompletion(j, 0); } @@ -734,10 +743,14 @@ if (frequencyDomain) { if (blockFrame - int(blockSize)/2 > - contextStart + contextDuration) break; + contextStart + contextDuration) { + break; + } } else { if (blockFrame >= - contextStart + contextDuration) break; + contextStart + contextDuration) { + break; + } } #ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN @@ -750,7 +763,7 @@ ((((blockFrame - contextStart) / stepSize) * 99) / (contextDuration / stepSize + 1)); - // channelCount is either m_input.getModel()->channelCount or 1 + // channelCount is either input->channelCount or 1 if (frequencyDomain) { for (int ch = 0; ch < channelCount; ++ch) { @@ -863,8 +876,10 @@ startFrame = 0; } - DenseTimeValueModel *input = getConformingInput(); - if (!input) return; + auto input = ModelById::getAs(getInputModel()); + if (!input) { + return; + } sv_frame_t got = 0; @@ -907,7 +922,10 @@ sv_frame_t blockFrame, const Vamp::Plugin::Feature &feature) { - sv_samplerate_t inputRate = m_input.getModel()->getSampleRate(); + auto input = ModelById::get(getInputModel()); + if (!input) return; + + sv_samplerate_t inputRate = input->getSampleRate(); // cerr << "FeatureExtractionModelTransformer::addFeature: blockFrame = " // << blockFrame << ", hasTimestamp = " << feature.hasTimestamp @@ -917,7 +935,7 @@ sv_frame_t frame = blockFrame; - if (m_descriptors[n]->sampleType == + if (m_descriptors[n].sampleType == Vamp::Plugin::OutputDescriptor::VariableSampleRate) { if (!feature.hasTimestamp) { @@ -933,10 +951,10 @@ // cerr << "variable sample rate: timestamp = " << feature.timestamp // << " at input rate " << inputRate << " -> " << frame << endl; - } else if (m_descriptors[n]->sampleType == + } else if (m_descriptors[n].sampleType == Vamp::Plugin::OutputDescriptor::FixedSampleRate) { - sv_samplerate_t rate = m_descriptors[n]->sampleRate; + sv_samplerate_t rate = m_descriptors[n].sampleRate; if (rate <= 0.0) { rate = inputRate; } @@ -949,7 +967,7 @@ } // cerr << "m_fixedRateFeatureNo = " << m_fixedRateFeatureNos[n] -// << ", m_descriptor->sampleRate = " << m_descriptors[n]->sampleRate +// << ", m_descriptor->sampleRate = " << m_descriptors[n].sampleRate // << ", inputRate = " << inputRate // << " giving frame = "; frame = lrint((double(m_fixedRateFeatureNos[n]) / rate) * inputRate); @@ -971,122 +989,128 @@ // to, we instead test what sort of model the constructor decided // to create. - if (isOutput(n)) { + ModelId outputId = m_outputs[n]; + bool found = false; + + if (!found) { + auto model = ModelById::getAs(outputId); + if (model) { + found = true; + model->add(Event(frame, feature.label.c_str())); + } + } + + if (!found) { + auto model = ModelById::getAs(outputId); + if (model) { + found = true; - SparseOneDimensionalModel *model = - getConformingOutput(n); - if (!model) return; + for (int i = 0; in_range_for(feature.values, i); ++i) { - model->add(Event(frame, feature.label.c_str())); - - } else if (isOutput(n)) { + float value = feature.values[i]; - SparseTimeValueModel *model = - getConformingOutput(n); - if (!model) return; + QString label = feature.label.c_str(); + if (feature.values.size() > 1) { + label = QString("[%1] %2").arg(i+1).arg(label); + } - for (int i = 0; i < (int)feature.values.size(); ++i) { + auto targetModel = model; - float value = feature.values[i]; + if (m_needAdditionalModels[n] && i > 0) { + targetModel = ModelById::getAs + (getAdditionalModel(n, i)); + if (!targetModel) targetModel = model; + } - QString label = feature.label.c_str(); - if (feature.values.size() > 1) { - label = QString("[%1] %2").arg(i+1).arg(label); + targetModel->add(Event(frame, value, label)); + } + } + } + + if (!found) { + if (ModelById::getAs(outputId) || + ModelById::getAs(outputId)) { + found = true; + + int index = 0; + + float value = 0.0; + if ((int)feature.values.size() > index) { + value = feature.values[index++]; } - SparseTimeValueModel *targetModel = model; - - if (m_needAdditionalModels[n] && i > 0) { - targetModel = getAdditionalModel(n, i); - if (!targetModel) targetModel = model; -// std::cerr << "adding point to model " << targetModel -// << " for output " << n << " bin " << i << std::endl; + sv_frame_t duration = 1; + if (feature.hasDuration) { + duration = RealTime::realTime2Frame(feature.duration, inputRate); + } else { + if (in_range_for(feature.values, index)) { + duration = lrintf(feature.values[index++]); + } } - targetModel->add(Event(frame, value, label)); - } + auto noteModel = ModelById::getAs(outputId); + if (noteModel) { - } else if (isOutput(n) || isOutput(n)) { + float velocity = 100; + if ((int)feature.values.size() > index) { + velocity = feature.values[index++]; + } + if (velocity < 0) velocity = 127; + if (velocity > 127) velocity = 127; - int index = 0; + noteModel->add(Event(frame, value, // value is pitch + duration, + velocity / 127.f, + feature.label.c_str())); + } - float value = 0.0; - if ((int)feature.values.size() > index) { - value = feature.values[index++]; - } + auto regionModel = ModelById::getAs(outputId); + if (regionModel) { - sv_frame_t duration = 1; - if (feature.hasDuration) { - duration = RealTime::realTime2Frame(feature.duration, inputRate); - } else { - if (in_range_for(feature.values, index)) { - duration = lrintf(feature.values[index++]); + if (feature.hasDuration && !feature.values.empty()) { + + for (int i = 0; in_range_for(feature.values, i); ++i) { + + float value = feature.values[i]; + + QString label = feature.label.c_str(); + if (feature.values.size() > 1) { + label = QString("[%1] %2").arg(i+1).arg(label); + } + + regionModel->add(Event(frame, + value, + duration, + label)); + } + } else { + + regionModel->add(Event(frame, + value, + duration, + feature.label.c_str())); + } } } + } - if (isOutput(n)) { - - float velocity = 100; - if ((int)feature.values.size() > index) { - velocity = feature.values[index++]; - } - if (velocity < 0) velocity = 127; - if (velocity > 127) velocity = 127; - - NoteModel *model = getConformingOutput(n); - if (!model) return; - model->add(Event(frame, value, // value is pitch - duration, - velocity / 127.f, - feature.label.c_str())); - } else { - - RegionModel *model = getConformingOutput(n); - if (!model) return; - - if (feature.hasDuration && !feature.values.empty()) { - - for (int i = 0; i < (int)feature.values.size(); ++i) { - - float value = feature.values[i]; - - QString label = feature.label.c_str(); - if (feature.values.size() > 1) { - label = QString("[%1] %2").arg(i+1).arg(label); - } - - model->add(Event(frame, - value, - duration, - label)); - } + if (!found) { + auto model = ModelById::getAs + (outputId); + if (model) { + found = true; + + DenseThreeDimensionalModel::Column values = feature.values; + + if (!feature.hasTimestamp && m_fixedRateFeatureNos[n] >= 0) { + model->setColumn(m_fixedRateFeatureNos[n], values); } else { - - model->add(Event(frame, - value, - duration, - feature.label.c_str())); + model->setColumn(int(frame / model->getResolution()), values); } } - - } else if (isOutput(n)) { - - DenseThreeDimensionalModel::Column values = feature.values; - - EditableDenseThreeDimensionalModel *model = - getConformingOutput(n); - if (!model) return; + } -// cerr << "(note: model resolution = " << model->getResolution() << ")" -// << endl; - - if (!feature.hasTimestamp && m_fixedRateFeatureNos[n] >= 0) { - model->setColumn(m_fixedRateFeatureNos[n], values); - } else { - model->setColumn(int(frame / model->getResolution()), values); - } - - } else { + if (!found) { SVDEBUG << "FeatureExtractionModelTransformer::addFeature: Unknown output model type!" << endl; } } @@ -1099,43 +1123,47 @@ << completion << ")" << endl; #endif - if (isOutput(n)) { + ModelId outputId = m_outputs[n]; + bool found = false; + + if (!found) { + auto model = ModelById::getAs(outputId); + if (model) { + found = true; + model->setCompletion(completion, true); + } + } - SparseOneDimensionalModel *model = - getConformingOutput(n); - if (!model) return; - if (model->isAbandoning()) abandon(); - model->setCompletion(completion, true); + if (!found) { + auto model = ModelById::getAs(outputId); + if (model) { + found = true; + model->setCompletion(completion, true); + } + } - } else if (isOutput(n)) { + if (!found) { + auto model = ModelById::getAs(outputId); + if (model) { + found = true; + model->setCompletion(completion, true); + } + } - SparseTimeValueModel *model = - getConformingOutput(n); - if (!model) return; - if (model->isAbandoning()) abandon(); - model->setCompletion(completion, true); + if (!found) { + auto model = ModelById::getAs(outputId); + if (model) { + found = true; + model->setCompletion(completion, true); + } + } - } else if (isOutput(n)) { - - NoteModel *model = getConformingOutput(n); - if (!model) return; - if (model->isAbandoning()) abandon(); - model->setCompletion(completion, true); - - } else if (isOutput(n)) { - - RegionModel *model = getConformingOutput(n); - if (!model) return; - if (model->isAbandoning()) abandon(); - model->setCompletion(completion, true); - - } else if (isOutput(n)) { - - EditableDenseThreeDimensionalModel *model = - getConformingOutput(n); - if (!model) return; - if (model->isAbandoning()) abandon(); - model->setCompletion(completion, true); //!!!m_context.updates); + if (!found) { + auto model = ModelById::getAs(outputId); + if (model) { + found = true; + model->setCompletion(completion, true); + } } } diff -r 4abc0f08adf9 -r 565575463752 transform/FeatureExtractionModelTransformer.h --- a/transform/FeatureExtractionModelTransformer.h Wed Jun 26 10:21:15 2019 +0100 +++ b/transform/FeatureExtractionModelTransformer.h Wed Jun 26 14:59:09 2019 +0100 @@ -38,9 +38,11 @@ FeatureExtractionModelTransformer(Input input, const Transform &transform); - // 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. + /** + * 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); @@ -57,16 +59,27 @@ void run() override; Vamp::Plugin *m_plugin; - std::vector m_descriptors; // per transform - std::vector m_fixedRateFeatureNos; // to assign times to FixedSampleRate features - std::vector m_outputNos; // list of plugin output indexes required for this group of transforms + + // descriptors per transform + std::vector m_descriptors; + + // to assign times to FixedSampleRate features + std::vector m_fixedRateFeatureNos; + + // list of plugin output indexes required for this group of transforms + std::vector m_outputNos; void createOutputModels(int n); - std::map m_needAdditionalModels; // transformNo -> necessity - typedef std::map > AdditionalModelMap; + // map from transformNo -> necessity + std::map m_needAdditionalModels; + + // map from transformNo -> binNo -> SparseTimeValueModel id + typedef std::map > AdditionalModelMap; + AdditionalModelMap m_additionalModels; - SparseTimeValueModel *getAdditionalModel(int transformNo, int binNo); + + ModelId getAdditionalModel(int transformNo, int binNo); void addFeature(int n, sv_frame_t blockFrame, @@ -83,7 +96,7 @@ void awaitOutputModels() override; // just casts: - +/*!!! DenseTimeValueModel *getConformingInput(); template bool isOutput(int n) { @@ -102,6 +115,7 @@ return 0; } } +*/ }; #endif diff -r 4abc0f08adf9 -r 565575463752 transform/ModelTransformer.h --- a/transform/ModelTransformer.h Wed Jun 26 10:21:15 2019 +0100 +++ b/transform/ModelTransformer.h Wed Jun 26 14:59:09 2019 +0100 @@ -34,36 +34,34 @@ * available to the user of the ModelTransformer immediately, but may * be initially empty until the background thread has populated it. */ - class ModelTransformer : public Thread { public: virtual ~ModelTransformer(); - typedef std::vector Models; + typedef std::vector Models; class Input { public: - Input(Model *m) : m_model(m), m_channel(-1) { } - Input(Model *m, int c) : m_model(m), m_channel(c) { } + Input(ModelId m) : m_model(m), m_channel(-1) { } + Input(ModelId m, int c) : m_model(m), m_channel(c) { } - Model *getModel() const { return m_model; } - void setModel(Model *m) { m_model = m; } + ModelId getModel() const { return m_model; } + void setModel(ModelId m) { m_model = m; } int getChannel() const { return m_channel; } void setChannel(int c) { m_channel = c; } protected: - Model *m_model; + ModelId m_model; int m_channel; }; /** * Hint to the processing thread that it should give up, for - * example because the process is going to exit or we want to get - * rid of the input model. Caller should still wait() and/or - * delete the transform before assuming its input and output - * models are no longer required. + * example because the process is going to exit or the + * model/document context is being replaced. Caller should still + * wait() to be sure that processing has ended. */ void abandon() { m_abandoned = true; } @@ -76,7 +74,7 @@ /** * Return the input model for the transform. */ - Model *getInputModel() { return m_input.getModel(); } + ModelId getInputModel() { return m_input.getModel(); } /** * Return the input channel spec for the transform. @@ -84,10 +82,11 @@ int getInputChannel() { return m_input.getChannel(); } /** - * 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. + * Return the set of output model IDs 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. The returned models have been added to + * ModelById. */ Models getOutputModels() { awaitOutputModels(); @@ -95,17 +94,6 @@ } /** - * 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. - */ - Models detachOutputModels() { - awaitOutputModels(); - m_detached = true; - return m_outputs; - } - - /** * Return any additional models that were created during * processing. This might happen if, for example, a transform was * configured to split a multi-bin output into separate single-bin @@ -122,15 +110,6 @@ virtual bool willHaveAdditionalOutputModels() { return false; } /** - * Return the set of additional models, also detaching them from - * the transformer. The caller takes ownership of the models. - */ - virtual Models detachAdditionalOutputModels() { - m_detachedAdd = true; - return getAdditionalOutputModels(); - } - - /** * Return a warning or error message. If getOutputModel returned * a null pointer, this should contain a fatal error message for * the transformer; otherwise it may contain a warning to show to @@ -145,10 +124,8 @@ virtual void awaitOutputModels() = 0; Transforms m_transforms; - Input m_input; // I don't own the model in this - Models m_outputs; // I own this, unless... - bool m_detached; // ... this is true. - bool m_detachedAdd; + Input m_input; + Models m_outputs; bool m_abandoned; QString m_message; };