Mercurial > hg > svapp
changeset 683:0736beb8b852 by-id
Toward updating Document for ModelById
author | Chris Cannam |
---|---|
date | Wed, 03 Jul 2019 13:01:26 +0100 |
parents | 161063152ddd |
children | 5e9b1956b609 |
files | framework/Align.cpp framework/Align.h framework/Document.cpp framework/Document.h |
diffstat | 4 files changed, 451 insertions(+), 491 deletions(-) [+] |
line wrap: on
line diff
--- a/framework/Align.cpp Tue Jul 02 21:10:25 2019 +0100 +++ b/framework/Align.cpp Wed Jul 03 13:01:26 2019 +0100 @@ -33,7 +33,7 @@ #include <QApplication> bool -Align::alignModel(Document *doc, Model *ref, Model *other, QString &error) +Align::alignModel(Document *doc, ModelId ref, ModelId other, QString &error) { QSettings settings; settings.beginGroup("Preferences"); @@ -89,18 +89,14 @@ } bool -Align::alignModelViaTransform(Document *doc, Model *ref, Model *other, +Align::alignModelViaTransform(Document *doc, ModelId ref, ModelId other, QString &error) { QMutexLocker locker (&m_mutex); - - RangeSummarisableTimeValueModel *reference = qobject_cast - <RangeSummarisableTimeValueModel *>(ref); - - RangeSummarisableTimeValueModel *rm = qobject_cast - <RangeSummarisableTimeValueModel *>(other); - if (!reference || !rm) return false; // but this should have been tested already + auto reference = ModelById::getAs<RangeSummarisableTimeValueModel>(ref); + auto rm = ModelById::getAs<RangeSummarisableTimeValueModel>(other); + if (!reference || !rm) return false; // This involves creating either three or four new models: // @@ -130,6 +126,7 @@ // is attached to the new model we are aligning, which also takes // ownership of it. The only one of these models that we need to // delete here is the SparseTimeValueModel [2a]. + //!!! todo: review the above, especially management of AlignmentModel // // (We also create a sneaky additional SparseTimeValueModel // temporarily so we can attach completion information to it - @@ -144,21 +141,24 @@ components.push_back(AggregateWaveModel::ModelChannelSpec (rm->getId(), -1)); - AggregateWaveModel *aggregateModel = new AggregateWaveModel(components); - doc->addAggregateModel(aggregateModel); + auto aggregateModel = std::make_shared<AggregateWaveModel>(components); + ModelById::add(aggregateModel); + doc->addAggregateModel(aggregateModel->getId()); - AlignmentModel *alignmentModel = - new AlignmentModel(reference, other, nullptr); + auto alignmentModel = std::make_shared<AlignmentModel>(ref, other, + ModelId()); + ModelById::add(alignmentModel); TransformId tdId = getTuningDifferenceTransformName(); if (tdId == "") { - if (beginTransformDrivenAlignment(aggregateModel, alignmentModel)) { - rm->setAlignment(alignmentModel); + if (beginTransformDrivenAlignment(aggregateModel->getId(), + alignmentModel->getId())) { + rm->setAlignment(alignmentModel->getId()); } else { error = alignmentModel->getError(); - delete alignmentModel; + ModelById::release(alignmentModel); return false; } @@ -181,82 +181,76 @@ ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); QString message; - Model *transformOutput = mtf->transform(transform, aggregateModel, message); + ModelId transformOutput = mtf->transform(transform, + aggregateModel->getId(), + message); - SparseTimeValueModel *tdout = dynamic_cast<SparseTimeValueModel *> - (transformOutput); - + auto tdout = ModelById::getAs<SparseTimeValueModel>(transformOutput); if (!tdout) { SVCERR << "Align::alignModel: ERROR: Failed to create tuning-difference output model (no Tuning Difference plugin?)" << endl; - delete tdout; error = message; return false; } - rm->setAlignment(alignmentModel); + rm->setAlignment(alignmentModel->getId()); - connect(tdout, SIGNAL(completionChanged()), + connect(tdout.get(), SIGNAL(completionChanged()), this, SLOT(tuningDifferenceCompletionChanged())); TuningDiffRec rec; - rec.input = aggregateModel; - rec.alignment = alignmentModel; - - connect(aggregateModel, SIGNAL(aboutToBeDeleted()), - this, SLOT(aggregateModelAboutToBeDeleted())); + rec.input = aggregateModel->getId(); + rec.alignment = alignmentModel->getId(); // This model exists only so that the AlignmentModel can get a // completion value from somewhere while the tuning difference // calculation is going on - rec.preparatory = new SparseTimeValueModel - (aggregateModel->getSampleRate(), 1);; - rec.preparatory->setCompletion(0); + auto preparatoryModel = std::make_shared<SparseTimeValueModel> + (aggregateModel->getSampleRate(), 1); + ModelById::add(preparatoryModel); + preparatoryModel->setCompletion(0); + rec.preparatory = preparatoryModel->getId(); alignmentModel->setPathFrom(rec.preparatory); - m_pendingTuningDiffs[tdout] = rec; + m_pendingTuningDiffs[transformOutput] = rec; } return true; } void -Align::aggregateModelAboutToBeDeleted() -{ - SVCERR << "Align::aggregateModelAboutToBeDeleted" << endl; - - QObject *s = sender(); - AggregateWaveModel *awm = qobject_cast<AggregateWaveModel *>(s); - if (!awm) return; - QMutexLocker locker(&m_mutex); - - SVCERR << "Align::aggregateModelAboutToBeDeleted: awm = " << awm - << endl; - - for (const auto &p : m_pendingTuningDiffs) { - if (p.second.input == awm) { - SVCERR << "we have a record of this, getting rid of it" << endl; - m_pendingTuningDiffs.erase(p.first); - return; - } - } -} - -void Align::tuningDifferenceCompletionChanged() { QMutexLocker locker (&m_mutex); - - SparseTimeValueModel *td = qobject_cast<SparseTimeValueModel *>(sender()); - if (!td) return; - if (m_pendingTuningDiffs.find(td) == m_pendingTuningDiffs.end()) { - SVCERR << "ERROR: Align::tuningDifferenceCompletionChanged: Model " - << td << " not found in pending tuning diff map!" << endl; + ModelId tdId; + if (Model *modelPtr = qobject_cast<Model *>(sender())) { + tdId = modelPtr->getId(); + } else { return; } - TuningDiffRec rec = m_pendingTuningDiffs[td]; + if (m_pendingTuningDiffs.find(tdId) == m_pendingTuningDiffs.end()) { + SVCERR << "ERROR: Align::tuningDifferenceCompletionChanged: Model " + << tdId << " not found in pending tuning diff map!" << endl; + return; + } + auto td = ModelById::getAs<SparseTimeValueModel>(tdId); + if (!td) { + SVCERR << "WARNING: Align::tuningDifferenceCompletionChanged: Model " + << tdId << " not known as SparseTimeValueModel" << endl; + return; + } + + TuningDiffRec rec = m_pendingTuningDiffs[tdId]; + + auto alignment = ModelById::getAs<AlignmentModel>(rec.alignment); + if (!alignment) { + SVCERR << "WARNING: Align::tuningDifferenceCompletionChanged:" + << "alignment model has disappeared" << endl; + return; + } + int completion = 0; bool done = td->isReady(&completion); @@ -269,7 +263,11 @@ // calculating the actual path in the following phase int clamped = (completion == 100 ? 99 : completion); // SVCERR << "Align::tuningDifferenceCompletionChanged: setting rec.preparatory completion to " << clamped << endl; - rec.preparatory->setCompletion(clamped); + auto preparatory = ModelById::getAs<SparseTimeValueModel> + (rec.preparatory); + if (preparatory) { + preparatory->setCompletion(clamped); + } return; } @@ -282,25 +280,32 @@ SVCERR << "Align::tuningDifferenceCompletionChanged: No tuning frequency reported" << endl; } - m_pendingTuningDiffs.erase(td); - td->aboutToDelete(); - delete td; - - rec.alignment->setPathFrom(nullptr); + m_pendingTuningDiffs.erase(tdId); + ModelById::release(tdId); + + alignment->setPathFrom({}); beginTransformDrivenAlignment (rec.input, rec.alignment, tuningFrequency); } bool -Align::beginTransformDrivenAlignment(AggregateWaveModel *aggregateModel, - AlignmentModel *alignmentModel, +Align::beginTransformDrivenAlignment(ModelId aggregateModelId, + ModelId alignmentModelId, float tuningFrequency) { TransformId id = getAlignmentTransformName(); TransformFactory *tf = TransformFactory::getInstance(); + auto aggregateModel = ModelById::getAs<AggregateWaveModel>(aggregateModelId); + auto alignmentModel = ModelById::getAs<AlignmentModel>(alignmentModelId); + + if (!aggregateModel || !alignmentModel) { + SVCERR << "Align::alignModel: ERROR: One or other of the aggregate & alignment models has disappeared" << endl; + return false; + } + Transform transform = tf->getDefaultTransformFor (id, aggregateModel->getSampleRate()); @@ -317,32 +322,31 @@ ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); QString message; - Model *transformOutput = mtf->transform - (transform, aggregateModel, message); + ModelId transformOutput = mtf->transform + (transform, aggregateModelId, message); - if (!transformOutput) { + if (transformOutput.isNone()) { transform.setStepSize(0); transformOutput = mtf->transform - (transform, aggregateModel, message); + (transform, aggregateModelId, message); } - SparseTimeValueModel *path = dynamic_cast<SparseTimeValueModel *> - (transformOutput); + auto path = ModelById::getAs<SparseTimeValueModel>(transformOutput); //!!! callers will need to be updated to get error from //!!! alignment model after initial call if (!path) { SVCERR << "Align::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl; - delete transformOutput; + ModelById::release(transformOutput); alignmentModel->setError(message); return false; } path->setCompletion(0); - alignmentModel->setPathFrom(path); + alignmentModel->setPathFrom(transformOutput); //!!! who releases transformOutput? - connect(alignmentModel, SIGNAL(completionChanged()), + connect(alignmentModel.get(), SIGNAL(completionChanged()), this, SLOT(alignmentCompletionChanged())); return true; @@ -352,28 +356,27 @@ Align::alignmentCompletionChanged() { QMutexLocker locker (&m_mutex); - - AlignmentModel *am = qobject_cast<AlignmentModel *>(sender()); - if (!am) return; - if (am->isReady()) { - disconnect(am, SIGNAL(completionChanged()), - this, SLOT(alignmentCompletionChanged())); - emit alignmentComplete(am); + + if (AlignmentModel *amPtr = qobject_cast<AlignmentModel *>(sender())) { + + auto am = ModelById::getAs<AlignmentModel>(amPtr->getId()); + if (am && am->isReady()) { + disconnect(am.get(), SIGNAL(completionChanged()), + this, SLOT(alignmentCompletionChanged())); + emit alignmentComplete(am->getId()); + } } } bool -Align::alignModelViaProgram(Document *, Model *ref, Model *other, +Align::alignModelViaProgram(Document *, ModelId ref, ModelId other, QString program, QString &error) { QMutexLocker locker (&m_mutex); - - WaveFileModel *reference = qobject_cast<WaveFileModel *>(ref); - WaveFileModel *rm = qobject_cast<WaveFileModel *>(other); - if (!reference || !rm) { - return false; // but this should have been tested already - } + auto reference = ModelById::getAs<RangeSummarisableTimeValueModel>(ref); + auto rm = ModelById::getAs<RangeSummarisableTimeValueModel>(other); + if (!reference || !rm) return false; while (!reference->isReady(nullptr) || !rm->isReady(nullptr)) { qApp->processEvents(); @@ -383,8 +386,8 @@ // model's audio file and the new model's audio file. It returns // the path in CSV form through stdout. - ReadOnlyWaveFileModel *roref = qobject_cast<ReadOnlyWaveFileModel *>(reference); - ReadOnlyWaveFileModel *rorm = qobject_cast<ReadOnlyWaveFileModel *>(rm); + auto roref = ModelById::getAs<ReadOnlyWaveFileModel>(ref); + auto rorm = ModelById::getAs<ReadOnlyWaveFileModel>(other); if (!roref || !rorm) { SVCERR << "ERROR: Align::alignModelViaProgram: Can't align non-read-only models via program (no local filename available)" << endl; return false; @@ -398,9 +401,10 @@ return false; } - AlignmentModel *alignmentModel = - new AlignmentModel(reference, other, nullptr); - rm->setAlignment(alignmentModel); + auto alignmentModel = std::make_shared<AlignmentModel>(ref, other, + ModelId()); + ModelById::add(alignmentModel); + rm->setAlignment(alignmentModel->getId()); QProcess *process = new QProcess; QStringList args; @@ -409,7 +413,7 @@ connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(alignmentProgramFinished(int, QProcess::ExitStatus))); - m_pendingProcesses[process] = alignmentModel; + m_pendingProcesses[process] = alignmentModel->getId(); process->start(program, args); bool success = process->waitForStarted(); @@ -419,7 +423,8 @@ << endl; error = "Alignment program could not be started"; m_pendingProcesses.erase(process); - rm->setAlignment(nullptr); // deletes alignmentModel as well + //!!! who releases alignmentModel? does this? review + rm->setAlignment({}); delete process; } @@ -441,7 +446,9 @@ return; } - AlignmentModel *alignmentModel = m_pendingProcesses[process]; + ModelId alignmentModelId = m_pendingProcesses[process]; + auto alignmentModel = ModelById::getAs<AlignmentModel>(alignmentModelId); + if (!alignmentModel) return; if (exitCode == 0 && status == 0) { @@ -471,6 +478,8 @@ goto done; } + //!!! to use ById? + Model *csvOutput = reader.load(); SparseTimeValueModel *path = qobject_cast<SparseTimeValueModel *>(csvOutput); @@ -479,23 +488,26 @@ << endl; alignmentModel->setError ("Output of program did not produce sparse time-value model"); + delete csvOutput; goto done; } - + if (path->isEmpty()) { SVCERR << "ERROR: Align::alignmentProgramFinished: Output contained no mappings" << endl; alignmentModel->setError ("Output of alignment program contained no mappings"); + delete path; goto done; } SVCERR << "Align::alignmentProgramFinished: Setting alignment path (" << path->getEventCount() << " point(s))" << endl; - alignmentModel->setPathFrom(path); + ModelById::add(std::shared_ptr<SparseTimeValueModel>(path)); + alignmentModel->setPathFrom(path->getId()); - emit alignmentComplete(alignmentModel); + emit alignmentComplete(alignmentModelId); } else { SVCERR << "ERROR: Align::alignmentProgramFinished: Aligner program "
--- a/framework/Align.h Tue Jul 02 21:10:25 2019 +0100 +++ b/framework/Align.h Wed Jul 03 13:01:26 2019 +0100 @@ -4,7 +4,6 @@ Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -22,7 +21,8 @@ #include <QMutex> #include <set> -class Model; +#include "data/model/Model.h" + class AlignmentModel; class SparseTimeValueModel; class AggregateWaveModel; @@ -63,18 +63,18 @@ * lifespan. */ bool alignModel(Document *doc, - Model *reference, - Model *toAlign, + ModelId reference, + ModelId toAlign, QString &error); bool alignModelViaTransform(Document *doc, - Model *reference, - Model *toAlign, + ModelId reference, + ModelId toAlign, QString &error); bool alignModelViaProgram(Document *doc, - Model *reference, - Model *toAlign, + ModelId reference, + ModelId toAlign, QString program, QString &error); @@ -90,35 +90,36 @@ * reference and other models can be queried from the alignment * model. */ - void alignmentComplete(AlignmentModel *alignment); + void alignmentComplete(ModelId alignmentModel); // an AlignmentModel private slots: void alignmentCompletionChanged(); void tuningDifferenceCompletionChanged(); void alignmentProgramFinished(int, QProcess::ExitStatus); - void aggregateModelAboutToBeDeleted(); private: static QString getAlignmentTransformName(); static QString getTuningDifferenceTransformName(); - bool beginTransformDrivenAlignment(AggregateWaveModel *, - AlignmentModel *, + bool beginTransformDrivenAlignment(ModelId, // an AggregateWaveModel + ModelId, // an AlignmentModel float tuningFrequency = 0.f); QMutex m_mutex; struct TuningDiffRec { - AggregateWaveModel *input; - AlignmentModel *alignment; - SparseTimeValueModel *preparatory; + ModelId input; // an AggregateWaveModel + ModelId alignment; // an AlignmentModel + ModelId preparatory; // a SparseTimeValueModel }; - // tuning-difference output model -> data needed for subsequent alignment - std::map<SparseTimeValueModel *, TuningDiffRec> m_pendingTuningDiffs; + // tuning-difference output model (a SparseTimeValueModel) -> data + // needed for subsequent alignment + std::map<ModelId, TuningDiffRec> m_pendingTuningDiffs; - // external alignment subprocess -> model into which to stuff the results - std::map<QProcess *, AlignmentModel *> m_pendingProcesses; + // external alignment subprocess -> model into which to stuff the + // results (an AlignmentModel) + std::map<QProcess *, ModelId> m_pendingProcesses; }; #endif
--- a/framework/Document.cpp Tue Jul 02 21:10:25 2019 +0100 +++ b/framework/Document.cpp Wed Jul 03 13:01:26 2019 +0100 @@ -48,23 +48,17 @@ //!!! still need to handle command history, documentRestored/documentModified Document::Document() : - m_mainModel(nullptr), m_autoAlignment(false), m_align(new Align()), m_isIncomplete(false) { - connect(this, - SIGNAL(modelAboutToBeDeleted(Model *)), - ModelTransformerFactory::getInstance(), - SLOT(modelAboutToBeDeleted(Model *))); - connect(ModelTransformerFactory::getInstance(), SIGNAL(transformFailed(QString, QString)), this, SIGNAL(modelGenerationFailed(QString, QString))); - connect(m_align, SIGNAL(alignmentComplete(AlignmentModel *)), - this, SIGNAL(alignmentComplete(AlignmentModel *))); + connect(m_align, SIGNAL(alignmentComplete(ModelId)), + this, SIGNAL(alignmentComplete(ModelId))); } Document::~Document() @@ -85,37 +79,17 @@ deleteLayer(*m_layers.begin(), true); } - if (!m_models.empty()) { - SVDEBUG << "Document::~Document: WARNING: " - << m_models.size() << " model(s) still remain -- " - << "should have been garbage collected when deleting layers" - << endl; - for (ModelRecord &rec: m_models) { - Model *model = rec.model; - if (model == m_mainModel) { - // just in case! - SVDEBUG << "Document::~Document: WARNING: Main model is also" - << " in models list!" << endl; - } else if (model) { - model->aboutToDelete(); - emit modelAboutToBeDeleted(model); - delete model; - } - } - m_models.clear(); + for (auto mr: m_models) { + ModelById::release(mr.first); + } + for (auto m: m_aggregateModels) { + ModelById::release(m); } -#ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::~Document: About to get rid of main model" - << endl; -#endif - if (m_mainModel) { - m_mainModel->aboutToDelete(); - emit modelAboutToBeDeleted(m_mainModel); - } - - emit mainModelChanged(nullptr); - delete m_mainModel; + auto mainModel = m_mainModel; + m_mainModel = {}; + emit mainModelChanged(m_mainModel); + ModelById::release(mainModel); } Layer * @@ -148,10 +122,10 @@ } Layer * -Document::createImportedLayer(Model *model) +Document::createImportedLayer(ModelId modelId) { LayerFactory::LayerTypeSet types = - LayerFactory::getInstance()->getValidLayerTypes(model); + LayerFactory::getInstance()->getValidLayerTypes(modelId); if (types.empty()) { SVCERR << "WARNING: Document::importLayer: no valid display layer for model" << endl; @@ -166,8 +140,8 @@ newLayer->setObjectName(getUniqueLayerName(newLayer->objectName())); - addImportedModel(model); - setModel(newLayer, model); + addImportedModel(modelId); + setModel(newLayer, modelId); //!!! and all channels setChannel(newLayer, -1); @@ -186,20 +160,20 @@ Layer * Document::createEmptyLayer(LayerFactory::LayerType type) { - if (!m_mainModel) return nullptr; + if (m_mainModel.isNone()) return nullptr; - Model *newModel = + auto newModel = LayerFactory::getInstance()->createEmptyModel(type, m_mainModel); if (!newModel) return nullptr; - + Layer *newLayer = createLayer(type); if (!newLayer) { - delete newModel; return nullptr; } - addImportedModel(newModel); - setModel(newLayer, newModel); + ModelById::add(newModel); + addImportedModel(newModel->getId()); + setModel(newLayer, newModel->getId()); return newLayer; } @@ -234,7 +208,8 @@ const ModelTransformer::Input &input) { QString message; - vector<Model *> newModels = addDerivedModels(transforms, input, message, nullptr); + vector<ModelId> newModels = + addDerivedModels(transforms, input, message, nullptr); if (newModels.empty()) { //!!! This identifier may be wrong! @@ -246,7 +221,7 @@ } QStringList names; - for (int i = 0; i < (int)newModels.size(); ++i) { + for (int i = 0; in_range_for(newModels, i); ++i) { names.push_back(getUniqueLayerName (TransformFactory::getInstance()-> getTransformFriendlyName @@ -275,13 +250,13 @@ } void - moreModelsAvailable(vector<Model *> models) override { + moreModelsAvailable(vector<ModelId> models) override { SVDEBUG << "AdditionalModelConverter::moreModelsAvailable: " << models.size() << " model(s)" << endl; // We can't automatically regenerate the additional models on - // reload -- we should delete them instead + // reload - so they go in m_additionalModels instead of m_models QStringList names; - foreach (Model *model, models) { - m_doc->addAdditionalModel(model); + foreach (ModelId modelId, models) { + m_doc->addAdditionalModel(modelId); names.push_back(QString()); } vector<Layer *> layers = m_doc->createLayersForDerivedModels @@ -298,12 +273,15 @@ } void cancel() { +/*!!! todo: restore foreach (Layer *layer, m_primary) { - Model *model = layer->getModel(); + ModelId model = layer->getModel(); + //!!! todo: restore this behaviour if (model) { model->abandon(); } } +*/ } private: @@ -321,11 +299,11 @@ AdditionalModelConverter *amc = new AdditionalModelConverter(this, handler); - vector<Model *> newModels = addDerivedModels + vector<ModelId> newModels = addDerivedModels (transforms, input, message, amc); QStringList names; - for (int i = 0; i < (int)newModels.size(); ++i) { + for (int i = 0; in_range_for(newModels, i); ++i) { names.push_back(getUniqueLayerName (TransformFactory::getInstance()-> getTransformFriendlyName @@ -356,29 +334,30 @@ } vector<Layer *> -Document::createLayersForDerivedModels(vector<Model *> newModels, +Document::createLayersForDerivedModels(vector<ModelId> newModels, QStringList names) { vector<Layer *> layers; - for (int i = 0; i < (int)newModels.size(); ++i) { + for (int i = 0; in_range_for(newModels, i); ++i) { - Model *newModel = newModels[i]; + ModelId newModelId = newModels[i]; LayerFactory::LayerTypeSet types = - LayerFactory::getInstance()->getValidLayerTypes(newModel); + LayerFactory::getInstance()->getValidLayerTypes(newModelId); if (types.empty()) { SVCERR << "WARNING: Document::createLayerForTransformer: no valid display layer for output of transform " << names[i] << endl; //!!! inadequate cleanup: - deleteModelFromList(newModel); + //!!! review: -> +// deleteModelFromList(newModel); return vector<Layer *>(); } //!!! for now, just use the first suitable layer type Layer *newLayer = createLayer(*types.begin()); - setModel(newLayer, newModel); + setModel(newLayer, newModelId); //!!! We need to clone the model when adding the layer, so that it //can be edited without affecting other layers that are based on @@ -407,13 +386,14 @@ } void -Document::setMainModel(WaveFileModel *model) +Document::setMainModel(ModelId modelId) { - Model *oldMainModel = m_mainModel; - m_mainModel = model; + ModelId oldMainModel = m_mainModel; + m_mainModel = modelId; emit modelAdded(m_mainModel); - if (model) { + + if (auto model = ModelById::get(modelId)) { emit activity(tr("Set main model to %1").arg(model->objectName())); } else { emit activity(tr("Clear main model")); @@ -433,7 +413,7 @@ << m_layers.size() << " layers" << endl; SVDEBUG << "Models now: "; for (const auto &r: m_models) { - SVDEBUG << r.model << " "; + SVDEBUG << r.first << " "; } SVDEBUG << endl; SVDEBUG << "Old main model: " << oldMainModel << endl; @@ -441,15 +421,14 @@ for (Layer *layer: m_layers) { - Model *model = layer->getModel(); + ModelId modelId = layer->getModel(); #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::setMainModel: inspecting model " - << (model ? model->objectName(): "(null)") << " in layer " - << layer->objectName() << endl; + << modelId << " in layer " << layer->objectName() << endl; #endif - if (model == oldMainModel) { + if (modelId == oldMainModel) { #ifdef DEBUG_DOCUMENT SVDEBUG << "... it uses the old main model, replacing" << endl; #endif @@ -457,7 +436,7 @@ continue; } - if (!model) { + if (modelId.isNone()) { SVCERR << "WARNING: Document::setMainModel: Null model in layer " << layer << endl; // get rid of this hideous degenerate @@ -465,19 +444,17 @@ continue; } - auto mitr = findModelInList(model); - - if (mitr == m_models.end()) { + if (m_models.find(modelId) == m_models.end()) { SVCERR << "WARNING: Document::setMainModel: Unknown model " - << model << " in layer " << layer << endl; + << modelId << " in layer " << layer << endl; // and this one obsoleteLayers.push_back(layer); continue; } - ModelRecord record = *mitr; + ModelRecord record = m_models[modelId]; - if (record.source && (record.source == oldMainModel)) { + if (!record.source.isNone() && (record.source == oldMainModel)) { #ifdef DEBUG_DOCUMENT SVDEBUG << "... it uses a model derived from the old main model, regenerating" << endl; @@ -493,13 +470,13 @@ //the main model has changed. QString message; - Model *replacementModel = + ModelId replacementModel = addDerivedModel(transform, ModelTransformer::Input (m_mainModel, record.channel), message); - if (!replacementModel) { + if (replacementModel.isNone()) { SVCERR << "WARNING: Document::setMainModel: Failed to regenerate model for transform \"" << transformId << "\"" << " in layer " << layer << endl; if (failedTransformers.find(transformId) @@ -517,15 +494,12 @@ message); } #ifdef DEBUG_DOCUMENT - SVDEBUG << "Replacing model " << model << " (type " - << typeid(*model).name() << ") with model " - << replacementModel << " (type " - << typeid(*replacementModel).name() << ") in layer " + SVDEBUG << "Replacing model " << modelId << ") with model " + << replacementModel << ") in layer " << layer << " (name " << layer->objectName() << ")" << endl; - RangeSummarisableTimeValueModel *rm = - dynamic_cast<RangeSummarisableTimeValueModel *>(replacementModel); + auto rm = ModelById::getAs<RangeSummarisableTimeValueModel>(replacementModel); if (rm) { SVDEBUG << "new model has " << rm->getChannelCount() << " channels " << endl; } else { @@ -541,41 +515,36 @@ deleteLayer(obsoleteLayers[k], true); } - std::set<Model *> additionalModels; + std::set<ModelId> additionalModels; for (const auto &rec : m_models) { - if (rec.additional) { - additionalModels.insert(rec.model); + if (rec.second.additional) { + additionalModels.insert(rec.first); } } - for (Model *a: additionalModels) { - deleteModelFromList(a); + for (ModelId a: additionalModels) { + m_models.erase(a); } for (const auto &rec : m_models) { - Model *m = rec.model; + auto m = ModelById::get(rec.first); + if (!m) continue; #ifdef DEBUG_DOCUMENT - SVDEBUG << "considering alignment for model " << m << " (name \"" - << m->objectName() << "\")" << endl; + SVDEBUG << "considering alignment for model " << rec.first << endl; #endif if (m_autoAlignment) { - alignModel(m); + alignModel(rec.first); - } else if (oldMainModel && + } else if (!oldMainModel.isNone() && (m->getAlignmentReference() == oldMainModel)) { - alignModel(m); + alignModel(rec.first); } } - if (oldMainModel) { - oldMainModel->aboutToDelete(); - emit modelAboutToBeDeleted(oldMainModel); - } - if (m_autoAlignment) { SVDEBUG << "Document::setMainModel: auto-alignment is on, aligning model if possible" << endl; alignModel(m_mainModel); @@ -585,45 +554,41 @@ emit mainModelChanged(m_mainModel); - delete oldMainModel; + ModelById::release(oldMainModel); } void Document::addAlreadyDerivedModel(const Transform &transform, const ModelTransformer::Input &input, - Model *outputModelToAdd) + ModelId outputModelToAdd) { - if (findModelInList(outputModelToAdd) != m_models.end()) { + if (m_models.find(outputModelToAdd) != m_models.end()) { SVCERR << "WARNING: Document::addAlreadyDerivedModel: Model already added" - << endl; + << endl; return; } - + #ifdef DEBUG_DOCUMENT - if (input.getModel()) { - SVDEBUG << "Document::addAlreadyDerivedModel: source is " << input.getModel() << " \"" << input.getModel()->objectName() << "\"" << endl; - } else { - SVDEBUG << "Document::addAlreadyDerivedModel: source is " << input.getModel() << endl; - } + SVDEBUG << "Document::addAlreadyDerivedModel: source is " << input.getModel() << endl; #endif ModelRecord rec; - rec.model = outputModelToAdd; rec.source = input.getModel(); rec.channel = input.getChannel(); rec.transform = transform; rec.additional = false; - rec.refcount = 0; - outputModelToAdd->setSourceModel(input.getModel()); + if (auto m = ModelById::get(outputModelToAdd)) { + m->setSourceModel(input.getModel()); + } - m_models.push_back(rec); + m_models[outputModelToAdd] = rec; #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::addAlreadyDerivedModel: Added model " << outputModelToAdd << endl; SVDEBUG << "Models now: "; for (const auto &rec : m_models) { - SVDEBUG << rec.model << " "; + SVDEBUG << rec.first << " "; } SVDEBUG << endl; #endif @@ -631,131 +596,121 @@ emit modelAdded(outputModelToAdd); } - void -Document::addImportedModel(Model *model) +Document::addImportedModel(ModelId modelId) { - if (findModelInList(model) != m_models.end()) { + if (m_models.find(modelId) != m_models.end()) { SVCERR << "WARNING: Document::addImportedModel: Model already added" - << endl; + << endl; return; } ModelRecord rec; - rec.model = model; - rec.source = nullptr; + rec.source = {}; rec.channel = 0; - rec.refcount = 0; rec.additional = false; - m_models.push_back(rec); + m_models[modelId] = rec; #ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::addImportedModel: Added model " << model << endl; + SVDEBUG << "Document::addImportedModel: Added model " << modelId << endl; SVDEBUG << "Models now: "; for (const auto &rec : m_models) { - SVDEBUG << rec.model << " "; + SVDEBUG << rec.first << " "; } SVDEBUG << endl; #endif if (m_autoAlignment) { SVDEBUG << "Document::addImportedModel: auto-alignment is on, aligning model if possible" << endl; - alignModel(model); + alignModel(modelId); } else { SVDEBUG << "Document(" << this << "): addImportedModel: auto-alignment is off" << endl; } - emit modelAdded(model); + emit modelAdded(modelId); } void -Document::addAdditionalModel(Model *model) +Document::addAdditionalModel(ModelId modelId) { - if (findModelInList(model) != m_models.end()) { + if (m_models.find(modelId) != m_models.end()) { SVCERR << "WARNING: Document::addAdditionalModel: Model already added" - << endl; + << endl; return; } ModelRecord rec; - rec.model = model; - rec.source = nullptr; + rec.source = {}; rec.channel = 0; - rec.refcount = 0; rec.additional = true; - m_models.push_back(rec); + m_models[modelId] = rec; #ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::addAdditionalModel: Added model " << model << endl; + SVDEBUG << "Document::addAdditionalModel: Added model " << modelId << endl; SVDEBUG << "Models now: "; for (const auto &rec : m_models) { - SVDEBUG << rec.model << " "; + SVDEBUG << rec.first << " "; } SVDEBUG << endl; #endif if (m_autoAlignment) { SVDEBUG << "Document::addAdditionalModel: auto-alignment is on, aligning model if possible" << endl; - alignModel(model); + alignModel(modelId); } - emit modelAdded(model); + emit modelAdded(modelId); } void -Document::addAggregateModel(AggregateWaveModel *model) +Document::addAggregateModel(ModelId modelId) { - connect(model, SIGNAL(modelInvalidated()), - this, SLOT(aggregateModelInvalidated())); - m_aggregateModels.insert(model); - SVDEBUG << "Document::addAggregateModel(" << model << ")" << endl; + m_aggregateModels.insert(modelId); + SVDEBUG << "Document::addAggregateModel(" << modelId << ")" << endl; } -void -Document::aggregateModelInvalidated() -{ - QObject *s = sender(); - AggregateWaveModel *aggregate = qobject_cast<AggregateWaveModel *>(s); - SVDEBUG << "Document::aggregateModelInvalidated(" << aggregate << ")" << endl; - if (aggregate) releaseModel(aggregate); -} - -Model * +ModelId Document::addDerivedModel(const Transform &transform, const ModelTransformer::Input &input, QString &message) { for (auto &rec : m_models) { - if (rec.transform == transform && - rec.source == input.getModel() && - rec.channel == input.getChannel()) { + if (rec.second.transform == transform && + rec.second.source == input.getModel() && + rec.second.channel == input.getChannel()) { SVDEBUG << "derived model taken from map " << endl; - return rec.model; + return rec.first; } } Transforms tt; tt.push_back(transform); - vector<Model *> mm = addDerivedModels(tt, input, message, nullptr); - if (mm.empty()) return nullptr; + vector<ModelId> mm = addDerivedModels(tt, input, message, nullptr); + if (mm.empty()) return {}; else return mm[0]; } -vector<Model *> +vector<ModelId> Document::addDerivedModels(const Transforms &transforms, const ModelTransformer::Input &input, QString &message, AdditionalModelConverter *amc) { - vector<Model *> mm = + vector<ModelId> mm = ModelTransformerFactory::getInstance()->transformMultiple (transforms, input, message, amc); - for (int j = 0; j < (int)mm.size(); ++j) { + for (int j = 0; in_range_for(mm, j); ++j) { - Model *model = mm[j]; + ModelId modelId = mm[j]; + Transform applied = transforms[j]; + + if (modelId.isNone()) { + SVCERR << "WARNING: Document::addDerivedModel: no output model for transform " << applied.getIdentifier() << endl; + continue; + } // The transform we actually used was presumably identical to // the one asked for, except that the version of the plugin @@ -768,47 +723,50 @@ //!!! would be nice to short-circuit this -- the version is //!!! static data, shouldn't have to construct a plugin for it //!!! (which may be expensive in Piper-world) - - Transform applied = transforms[j]; applied.setPluginVersion (TransformFactory::getInstance()-> getDefaultTransformFor(applied.getIdentifier(), applied.getSampleRate()) .getPluginVersion()); - if (!model) { - SVCERR << "WARNING: Document::addDerivedModel: no output model for transform " << applied.getIdentifier() << endl; - } else { - addAlreadyDerivedModel(applied, input, model); - } + addAlreadyDerivedModel(applied, input, modelId); } return mm; } void -Document::releaseModel(Model *model) // Will _not_ release main model! +Document::releaseModel(ModelId modelId) // Will _not_ release main model! { - if (model == nullptr) { + //!!! + + SVCERR << "Document::releaseModel(" << modelId << "): STILL TO REVIEW" << endl; + +#ifdef NOT_DEFINED + + if (modelId.isNone()) { return; } + auto model = ModelById::get(modelId); + if (!model) { + return; + } + #ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::releaseModel(" << model << ", type " + SVDEBUG << "Document::releaseModel(" << modelId << ", type " << model->getTypeName() << ", name \"" << model->objectName() << "\")" << endl; #endif - if (model == m_mainModel) { + if (modelId == m_mainModel) { return; } bool toDelete = false; bool isInModelList = false; // should become true for any "normal" model - ModelList::iterator mitr = findModelInList(model); - - if (mitr != m_models.end()) { + if (m_models.find(modelId) != m_models.end()) { if (mitr->refcount == 0) { SVCERR << "WARNING: Document::releaseModel: model " << model @@ -873,6 +831,8 @@ #endif } } + +#endif } void @@ -936,47 +896,44 @@ } void -Document::setModel(Layer *layer, Model *model) +Document::setModel(Layer *layer, ModelId modelId) { - if (model && - model != m_mainModel && - findModelInList(model) == m_models.end()) { + if (!modelId.isNone() && + modelId != m_mainModel && + m_models.find(modelId) == m_models.end()) { SVCERR << "ERROR: Document::setModel: Layer " << layer - << " (\"" << layer->objectName() - << "\") wants to use unregistered model " << model - << ": register the layer's model before setting it!" - << endl; + << " (\"" << layer->objectName() + << "\") wants to use unregistered model " << modelId + << ": register the layer's model before setting it!" + << endl; return; } - Model *previousModel = layer->getModel(); + ModelId previousModel = layer->getModel(); - if (previousModel == model) { + if (previousModel == modelId) { SVDEBUG << "NOTE: Document::setModel: Layer " << layer << " (\"" - << layer->objectName() << "\") is already set to model " - << model << " (\"" - << (model ? model->objectName(): "(null)") - << "\")" << endl; + << layer->objectName() + << "\") is already set to model " + << modelId << endl; return; } - +/*!!! if (model && model != m_mainModel) { ModelList::iterator mitr = findModelInList(model); if (mitr != m_models.end()) { mitr->refcount ++; } } - - if (model && previousModel) { +*/ + if (!modelId.isNone() && !previousModel.isNone()) { PlayParameterRepository::getInstance()->copyParameters - (previousModel, model); + (previousModel.untyped, modelId.untyped); } - LayerFactory::getInstance()->setModel(layer, model); + LayerFactory::getInstance()->setModel(layer, modelId); - if (previousModel) { - releaseModel(previousModel); - } + releaseModel(previousModel); } void @@ -988,8 +945,8 @@ void Document::addLayerToView(View *view, Layer *layer) { - Model *model = layer->getModel(); - if (!model) { + ModelId modelId = layer->getModel(); + if (modelId.isNone()) { #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::addLayerToView: Layer (\"" << layer->objectName() @@ -997,10 +954,10 @@ << "normally you want to set the model first" << endl; #endif } else { - if (model != m_mainModel && - findModelInList(model) == m_models.end()) { + if (modelId != m_mainModel && + m_models.find(modelId) == m_models.end()) { SVCERR << "ERROR: Document::addLayerToView: Layer " << layer - << " has unregistered model " << model + << " has unregistered model " << modelId << " -- register the layer's model before adding the layer!" << endl; return; } @@ -1075,37 +1032,38 @@ } } -std::vector<Model *> +std::vector<ModelId> Document::getTransformInputModels() { - std::vector<Model *> models; + std::vector<ModelId> models; - if (!m_mainModel) return models; + if (m_mainModel.isNone()) return models; models.push_back(m_mainModel); //!!! This will pick up all models, including those that aren't visible... - for (ModelRecord &rec: m_models) { + for (auto rec: m_models) { - Model *model = rec.model; - if (!model || model == m_mainModel) continue; - DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model); - + ModelId modelId = rec.first; + if (modelId == m_mainModel) continue; + + auto dtvm = ModelById::getAs<DenseTimeValueModel>(modelId); if (dtvm) { - models.push_back(dtvm); + models.push_back(modelId); } } return models; } +//!!! what is this used for? bool -Document::isKnownModel(const Model *model) const +Document::isKnownModel(const ModelId modelId) const { - if (model == m_mainModel) return true; - for (const ModelRecord &rec: m_models) { - if (rec.model == model) return true; + if (modelId == m_mainModel) return true; + for (auto rec: m_models) { + if (rec.first == modelId) return true; } return false; } @@ -1117,30 +1075,29 @@ } void -Document::alignModel(Model *model, bool forceRecalculate) +Document::alignModel(ModelId modelId, bool forceRecalculate) { - SVDEBUG << "Document::alignModel(" << model << ", " << forceRecalculate + SVDEBUG << "Document::alignModel(" << modelId << ", " << forceRecalculate << ") (main model is " << m_mainModel << ")" << endl; - - RangeSummarisableTimeValueModel *rm = - dynamic_cast<RangeSummarisableTimeValueModel *>(model); + + auto rm = ModelById::getAs<RangeSummarisableTimeValueModel>(modelId); if (!rm) { - SVDEBUG << "(model " << rm << " is not an alignable sort)" << endl; + SVDEBUG << "(model " << modelId << " is not an alignable sort)" << endl; return; } - if (!m_mainModel) { + if (m_mainModel.isNone()) { SVDEBUG << "(no main model to align to)" << endl; - if (forceRecalculate && rm->getAlignment()) { + if (forceRecalculate && !rm->getAlignment().isNone()) { SVDEBUG << "(but model is aligned, and forceRecalculate is true, " << "so resetting alignment to nil)" << endl; - rm->setAlignment(nullptr); + rm->setAlignment({}); } return; } if (rm->getAlignmentReference() == m_mainModel) { - SVDEBUG << "(model " << rm << " is already aligned to main model " + SVDEBUG << "(model " << modelId << " is already aligned to main model " << m_mainModel << ")" << endl; if (!forceRecalculate) { return; @@ -1150,37 +1107,40 @@ } } - if (model == m_mainModel) { + if (modelId == m_mainModel) { // The reference has an empty alignment to itself. This makes // it possible to distinguish between the reference and any // unaligned model just by looking at the model itself, // without also knowing what the main model is - SVDEBUG << "Document::alignModel(" << model + SVDEBUG << "Document::alignModel(" << modelId << "): is main model, setting alignment to itself" << endl; - rm->setAlignment(new AlignmentModel(model, model, nullptr)); + auto alignment = std::make_shared<AlignmentModel>(modelId, modelId, + ModelId()); + ModelById::add(alignment); + //!!! hang on, who tracks alignment models? + rm->setAlignment(alignment->getId()); return; } - WritableWaveFileModel *w = - dynamic_cast<WritableWaveFileModel *>(model); + auto w = ModelById::getAs<WritableWaveFileModel>(modelId); if (w && w->getWriteProportion() < 100) { - SVDEBUG << "Document::alignModel(" << model + SVDEBUG << "Document::alignModel(" << modelId << "): model write is not complete, deferring" << endl; - connect(w, SIGNAL(writeCompleted()), + connect(w.get(), SIGNAL(writeCompleted()), this, SLOT(performDeferredAlignment())); return; } SVDEBUG << "Document::alignModel: aligning..." << endl; - if (rm->getAlignmentReference() != nullptr) { + if (!rm->getAlignmentReference().isNone()) { SVDEBUG << "(Note: model " << rm << " is currently aligned to model " << rm->getAlignmentReference() << "; this will replace that)" << endl; } QString err; - if (!m_align->alignModel(this, m_mainModel, rm, err)) { + if (!m_align->alignModel(this, m_mainModel, modelId, err)) { SVCERR << "Alignment failed: " << err << endl; emit alignmentFailed(err); } @@ -1189,21 +1149,23 @@ void Document::performDeferredAlignment() { - QObject *s = sender(); - Model *m = dynamic_cast<Model *>(s); - if (!m) { + ModelId modelId; + if (Model *m = qobject_cast<Model *>(sender())) { + modelId = m->getId(); + } else { SVDEBUG << "Document::performDeferredAlignment: sender is not a Model" << endl; - } else { - SVDEBUG << "Document::performDeferredAlignment: aligning..." << endl; - alignModel(m); + return; } + + SVDEBUG << "Document::performDeferredAlignment: aligning..." << endl; + alignModel(modelId); } void Document::alignModels() { - for (const ModelRecord &rec: m_models) { - alignModel(rec.model); + for (auto rec: m_models) { + alignModel(rec.first); } alignModel(m_mainModel); } @@ -1211,8 +1173,8 @@ void Document::realignModels() { - for (const ModelRecord &rec: m_models) { - alignModel(rec.model, true); + for (auto rec: m_models) { + alignModel(rec.first, true); } alignModel(m_mainModel); } @@ -1362,7 +1324,8 @@ out << indent + QString("<data%1%2>\n") .arg(extraAttributes == "" ? "" : " ").arg(extraAttributes); - if (m_mainModel) { + auto mainModel = ModelById::getAs<WaveFileModel>(m_mainModel); + if (mainModel) { #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::toXml: writing main model" << endl; @@ -1371,16 +1334,17 @@ if (asTemplate) { writePlaceholderMainModel(out, indent + " "); } else { - m_mainModel->toXml(out, indent + " ", "mainModel=\"true\""); + mainModel->toXml(out, indent + " ", "mainModel=\"true\""); } PlayParameters *playParameters = - PlayParameterRepository::getInstance()->getPlayParameters(m_mainModel); + PlayParameterRepository::getInstance()->getPlayParameters + (m_mainModel.untyped); if (playParameters) { playParameters->toXml (out, indent + " ", QString("model=\"%1\"") - .arg(m_mainModel->getExportId())); + .arg(mainModel->getExportId())); } } else { #ifdef DEBUG_DOCUMENT @@ -1391,17 +1355,17 @@ // Models that are not used in a layer that is in a view should // not be written. Get our list of required models first. - std::set<const Model *> used; + std::set<ModelId> used; for (LayerViewMap::const_iterator i = m_layerViewMap.begin(); i != m_layerViewMap.end(); ++i) { if (i->first && !i->second.empty()) { // Layer exists, is in views - Model *m = i->first->getModel(); - if (m) { - used.insert(m); - if (m->getSourceModel()) { - used.insert(m->getSourceModel()); + ModelId modelId = i->first->getModel(); + if (auto model = ModelById::get(modelId)) { + used.insert(modelId); + if (!model->getSourceModel().isNone()) { + used.insert(model->getSourceModel()); } } } @@ -1420,16 +1384,16 @@ // that, so existing sessions will always have the aggregate // models first and we might as well stick with that. - for (std::set<Model *>::iterator i = m_aggregateModels.begin(); - i != m_aggregateModels.end(); ++i) { + for (auto modelId: m_aggregateModels) { #ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::toXml: checking aggregate model " << *i << endl; + SVDEBUG << "Document::toXml: checking aggregate model " + << modelId << endl; #endif - - AggregateWaveModel *aggregate = qobject_cast<AggregateWaveModel *>(*i); + + auto aggregate = ModelById::getAs<AggregateWaveModel>(modelId); if (!aggregate) continue; - if (used.find(aggregate) == used.end()) { + if (used.find(modelId) == used.end()) { #ifdef DEBUG_DOCUMENT SVDEBUG << "(unused, skipping)" << endl; #endif @@ -1443,7 +1407,7 @@ aggregate->toXml(out, indent + " "); } - std::set<Model *> written; + std::set<ModelId> written; // Now write the other models in two passes: first the models that // aren't derived from anything (in case they are source @@ -1455,17 +1419,18 @@ const int nonDerivedPass = 0, derivedPass = 1; for (int pass = nonDerivedPass; pass <= derivedPass; ++pass) { - for (const ModelRecord &rec: m_models) { + for (auto rec: m_models) { - Model *model = rec.model; + ModelId modelId = rec.first; - if (used.find(model) == used.end()) continue; + if (used.find(modelId) == used.end()) continue; + auto model = ModelById::get(modelId); + if (!model) continue; + #ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::toXml: looking at model " << model - << " (" << model->getTypeName() << ", \"" - << model->objectName() << "\") [pass = " - << pass << "]" << endl; + SVDEBUG << "Document::toXml: looking at model " << modelId + << " [pass = " << pass << "]" << endl; #endif // We need an intelligent way to determine which models @@ -1485,7 +1450,8 @@ bool writeModel = true; bool haveDerivation = false; - if (rec.source && rec.transform.getIdentifier() != "") { + if (!rec.second.source.isNone() && + rec.second.transform.getIdentifier() != "") { haveDerivation = true; } @@ -1502,26 +1468,26 @@ } if (haveDerivation) { - if (dynamic_cast<const WritableWaveFileModel *>(model)) { - writeModel = false; - } else if (dynamic_cast<const DenseThreeDimensionalModel *>(model)) { + if (ModelById::isa<WritableWaveFileModel>(modelId) || + ModelById::isa<DenseThreeDimensionalModel>(modelId)) { writeModel = false; } } if (writeModel) { model->toXml(out, indent + " "); - written.insert(model); + written.insert(modelId); } if (haveDerivation) { writeBackwardCompatibleDerivation(out, indent + " ", - model, rec); + modelId, rec.second); } //!!! We should probably own the PlayParameterRepository PlayParameters *playParameters = - PlayParameterRepository::getInstance()->getPlayParameters(model); + PlayParameterRepository::getInstance()->getPlayParameters + (modelId.untyped); if (playParameters) { playParameters->toXml (out, indent + " ", @@ -1537,11 +1503,12 @@ // this will only work when the alignment is complete, so we // should probably wait for it if it isn't already by this point. - for (std::set<Model *>::const_iterator i = written.begin(); - i != written.end(); ++i) { + for (auto modelId: written) { - const Model *model = *i; - const AlignmentModel *alignment = model->getAlignment(); + auto model = ModelById::get(modelId); + if (!model) continue; + + auto alignment = ModelById::get(model->getAlignment()); if (!alignment) continue; alignment->toXml(out, indent + " "); @@ -1557,15 +1524,17 @@ void Document::writePlaceholderMainModel(QTextStream &out, QString indent) const { + auto mainModel = ModelById::get(m_mainModel); + if (!mainModel) return; out << indent; out << QString("<model id=\"%1\" name=\"placeholder\" sampleRate=\"%2\" type=\"wavefile\" file=\":samples/silent.wav\" mainModel=\"true\"/>\n") - .arg(m_mainModel->getExportId()) - .arg(m_mainModel->getSampleRate()); + .arg(mainModel->getExportId()) + .arg(mainModel->getSampleRate()); } void Document::writeBackwardCompatibleDerivation(QTextStream &out, QString indent, - Model *targetModel, + ModelId targetModelId, const ModelRecord &rec) const { // There is a lot of redundancy in the XML we output here, because @@ -1592,6 +1561,13 @@ const Transform &transform = rec.transform; + //!!! in cases like these, where we think we have the model handle + //!!! and nobody else should be releasing it, we probably ought to + //!!! throw std::logic_error if !targetModel + + auto targetModel = ModelById::get(targetModelId); + if (!targetModel) return; + // Just for reference, this is what we would write if we didn't // have to be backward compatible: // @@ -1623,7 +1599,7 @@ "model=\"%2\" channel=\"%3\" domain=\"%4\" " "stepSize=\"%5\" blockSize=\"%6\" %7windowType=\"%8\" " "transform=\"%9\">\n") - .arg(rec.source->getExportId()) + .arg(ModelById::getExportId(rec.source)) .arg(targetModel->getExportId()) .arg(rec.channel) .arg(TransformFactory::getInstance()->getTransformInputDomain
--- a/framework/Document.h Tue Jul 02 21:10:25 2019 +0100 +++ b/framework/Document.h Wed Jul 03 13:01:26 2019 +0100 @@ -95,7 +95,7 @@ * Create and return a new layer associated with the given model, * and register the model as an imported model. */ - Layer *createImportedLayer(Model *); + Layer *createImportedLayer(ModelId); /** * Create and return a new layer of the given type, with an @@ -183,39 +183,38 @@ /** * Set the main model (the source for playback sample rate, etc) * to the given wave file model. This will regenerate any derived - * models that were based on the previous main model. + * models that were based on the previous main model. The model + * must have been added to ModelById already, and Document will + * take responsibility for releasing it later. */ - void setMainModel(WaveFileModel *); + void setMainModel(ModelId); // a WaveFileModel /** * Get the main model (the source for playback sample rate, etc). */ - WaveFileModel *getMainModel() { return m_mainModel; } + ModelId getMainModel() { return m_mainModel; } + + std::vector<ModelId> getTransformInputModels(); - /** - * Get the main model (the source for playback sample rate, etc). - */ - const WaveFileModel *getMainModel() const { return m_mainModel; } - - std::vector<Model *> getTransformInputModels(); - - bool isKnownModel(const Model *) const; + //??? investigate & document + bool isKnownModel(ModelId) const; /** * Add a derived model associated with the given transform, * running the transform and returning the resulting model. + * The model is added to ModelById before returning. */ - Model *addDerivedModel(const Transform &transform, - const ModelTransformer::Input &input, - QString &returnedMessage); + ModelId addDerivedModel(const Transform &transform, + const ModelTransformer::Input &input, + QString &returnedMessage); /** * Add derived models associated with the given set of related * transforms, running the transforms and returning the resulting - * models. + * models. The models are added to ModelById before returning. */ friend class AdditionalModelConverter; - std::vector<Model *> addDerivedModels(const Transforms &transforms, + std::vector<ModelId> addDerivedModels(const Transforms &transforms, const ModelTransformer::Input &input, QString &returnedMessage, AdditionalModelConverter *); @@ -223,20 +222,27 @@ /** * Add a derived model associated with the given transform. This * is necessary to register any derived model that was not created - * by the document using createDerivedModel or createDerivedLayer. + * by the document using createDerivedModel or + * createDerivedLayer. The model must have been added to ModelById + * already, and Document will take responsibility for releasing it + * later. */ void addAlreadyDerivedModel(const Transform &transform, const ModelTransformer::Input &input, - Model *outputModelToAdd); + ModelId outputModelToAdd); /** * Add an imported (non-derived, non-main) model. This is * necessary to register any imported model that is associated - * with a layer. + * with a layer. The model must have been added to ModelById + * already, and Document will take responsibility for releasing it + * later. */ - void addImportedModel(Model *); + void addImportedModel(ModelId); /** + *!!! todo: do we still need this to be separate? + * * Add an aggregate model (one which represents a set of component * wave models as one model per channel in a single wave * model). Aggregate models are unusual in that they are created @@ -244,15 +250,18 @@ * probably isn't ideal!) They are recorded separately from other * models, and will be deleted if any of their component models * are removed. + * + * The model must have been added to ModelById already, and + * Document will take responsibility for releasing it later. */ - void addAggregateModel(AggregateWaveModel *); + void addAggregateModel(ModelId); // an AggregateWaveModel /** * Associate the given model with the given layer. The model must * have already been registered using one of the addXXModel * methods above. */ - void setModel(Layer *, Model *); + void setModel(Layer *, ModelId); /** * Set the given layer to use the given channel of its model (-1 @@ -320,9 +329,8 @@ // last removed from a view void layerInAView(Layer *, bool); - void modelAdded(Model *); - void mainModelChanged(WaveFileModel *); // emitted after modelAdded - void modelAboutToBeDeleted(Model *); + void modelAdded(ModelId); + void mainModelChanged(ModelId); // a WaveFileModel; emitted after modelAdded void modelGenerationFailed(QString transformName, QString message); void modelGenerationWarning(QString transformName, QString message); @@ -331,17 +339,17 @@ void modelRegenerationWarning(QString layerName, QString transformName, QString message); - void alignmentComplete(AlignmentModel *); + void alignmentComplete(ModelId); // an AlignmentModel void alignmentFailed(QString message); void activity(QString); protected slots: - void aggregateModelInvalidated(); +//!!! void aggregateModelInvalidated(); void performDeferredAlignment(); protected: - void releaseModel(Model *model); + void releaseModel(ModelId model); /** * If model is suitable for alignment, align it against the main @@ -349,7 +357,7 @@ * alignment already for the current main model, leave it * unchanged unless forceRecalculate is true. */ - void alignModel(Model *, bool forceRecalculate = false); + void alignModel(ModelId, bool forceRecalculate = false); /* * Every model that is in use by a layer in the document must be @@ -362,75 +370,38 @@ * model is not reference counted for layers, and is not freed * unless it is replaced or the document is deleted. */ - WaveFileModel *m_mainModel; + ModelId m_mainModel; // a WaveFileModel struct ModelRecord { // Information associated with a non-main model. If this - // model is derived from another, then source will be non-NULL - // and the transform name will be set appropriately. If the - // transform name is set but source is NULL, then there was a - // transform involved but the (target) model has been modified - // since being generated from it. + // model is derived from another, then source will be + // something other than None and the transform name will be + // set appropriately. If the transform is set but source is + // None, then there was a transform involved but the (target) + // model has been modified since being generated from it. // This does not use ModelTransformer::Input, because it would - // be confusing to have Input objects hanging around with NULL + // be confusing to have Input objects hanging around with None // models in them. - Model *model; - const Model *source; + ModelId source; // may be None int channel; Transform transform; bool additional; - - // Count of the number of layers using this model. - int refcount; }; - // This used to be a map<Model *, ModelRecord>, but a vector - // ensures that models are consistently recorded in order of - // creation rather than at the whim of heap allocation, and that's - // useful for automated testing. We don't expect ever to have so - // many models that there is any significant overhead there. - typedef std::vector<ModelRecord> ModelList; - ModelList m_models; - - ModelList::iterator findModelInList(Model *m) { - for (ModelList::iterator i = m_models.begin(); - i != m_models.end(); ++i) { - if (i->model == m) { - return i; - } - } - return m_models.end(); - } + // These must be stored in increasing order of id (as in the + // ordered std::map), to ensure repeatability for automated tests + std::map<ModelId, ModelRecord> m_models; + std::set<ModelId> m_aggregateModels; - void deleteModelFromList(Model *m) { - ModelList keep; - bool found = false; - for (const ModelRecord &rec: m_models) { - if (rec.model == m) { - found = true; - m->aboutToDelete(); - emit modelAboutToBeDeleted(m); - } else { - keep.push_back(rec); - } - } - m_models = keep; - if (found) { - delete m; - } - } - /** * Add an extra derived model (returned at the end of processing a * transform). */ - void addAdditionalModel(Model *); + void addAdditionalModel(ModelId); - std::set<Model *> m_aggregateModels; - class AddLayerCommand : public Command { public: @@ -475,13 +446,13 @@ void removeFromLayerViewMap(Layer *, View *); QString getUniqueLayerName(QString candidate); - void writeBackwardCompatibleDerivation(QTextStream &, QString, Model *, + void writeBackwardCompatibleDerivation(QTextStream &, QString, ModelId, const ModelRecord &) const; void toXml(QTextStream &, QString, QString, bool asTemplate) const; void writePlaceholderMainModel(QTextStream &, QString) const; - std::vector<Layer *> createLayersForDerivedModels(std::vector<Model *>, + std::vector<Layer *> createLayersForDerivedModels(std::vector<ModelId>, QStringList names); /**