changeset 691:c8ba09756eff by-id

Work on management of alignment-related models
author Chris Cannam
date Fri, 12 Jul 2019 13:58:02 +0100
parents 827a522a5da4
children ad5917362158
files framework/Align.cpp framework/Align.h framework/Document.cpp framework/Document.h framework/MainWindowBase.cpp framework/SVFileReader.cpp
diffstat 6 files changed, 179 insertions(+), 129 deletions(-) [+]
line wrap: on
line diff
--- a/framework/Align.cpp	Fri Jul 12 09:40:56 2019 +0100
+++ b/framework/Align.cpp	Fri Jul 12 13:58:02 2019 +0100
@@ -103,40 +103,47 @@
 
     if (!reference || !other) return false;
    
-    // This involves creating either three or four new models:
+    // This involves creating a number of new models:
     //
     // 1. an AggregateWaveModel to provide the mixdowns of the main
     // model and the new model in its two channels, as input to the
-    // MATCH plugin
+    // MATCH plugin. We just call this one aggregateModel
     //
     // 2a. a SparseTimeValueModel which will be automatically created
     // by FeatureExtractionModelTransformer when running the
     // TuningDifference plugin to receive the relative tuning of the
     // second model (if pitch-aware alignment is enabled in the
-    // preferences)
+    // preferences). We call this tuningDiffOutputModel.
     //
     // 2b. a SparseTimeValueModel which will be automatically created
     // by FeatureExtractionPluginTransformer when running the MATCH
-    // plugin to perform alignment (so containing the alignment path)
+    // plugin to perform alignment (so containing the alignment path).
+    // We call this one pathOutputModel.
     //
-    // 3. an AlignmentModel, which stores the path model and carries
-    // out alignment lookups on it.
+    // 2c. a SparseTimeValueModel used solely to provide faked
+    // completion information to the AlignmentModel while a
+    // TuningDifference calculation is going on. We call this
+    // preparatoryModel.
     //
-    // The AggregateWaveModel [1] is registered with the document,
-    // which deletes it when it is invalidated (when one of its
-    // components is deleted). The SparseTimeValueModel [2a] is reused
-    // by us when starting the alignment process proper, and is then
-    // deleted by us. The SparseTimeValueModel [2b] is passed to the
-    // AlignmentModel, which takes ownership of it. The AlignmentModel
-    // 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
+    // 3. an AlignmentModel, which stores the path and carries out
+    // alignment lookups on it. We just call this one alignmentModel.
     //
-    // (We also create a sneaky additional SparseTimeValueModel
-    // temporarily so we can attach completion information to it -
-    // this is quite unnecessary from the perspective of simply
-    // producing the results.)
+    // Models 1 and 3 are registered with the document, which will
+    // eventually release them. We don't release them here except in
+    // the case where an activity fails before the point where we
+    // would otherwise have registered them with the document.
+    //
+    // Models 2a (tuningDiffOutputModel), 2b (pathOutputModel) and 2c
+    // (preparatoryModel) are not registered with the document. Model
+    // 2b (pathOutputModel) is not registered because we do not have a
+    // stable reference to the document at the point where it is
+    // created. Model 2c (preparatoryModel) is not registered because
+    // it is a bodge that we are embarrassed about, so we try to
+    // manage it ourselves without anyone else noticing. Model 2a is
+    // not registered for symmetry with the other two. These have to
+    // be released by us when finished with, but their lifespans do
+    // not extend beyond the end of the alignment procedure, so this
+    // should be ok.
 
     AggregateWaveModel::ChannelSpecList components;
 
@@ -148,7 +155,7 @@
 
     auto aggregateModel = std::make_shared<AggregateWaveModel>(components);
     auto aggregateModelId = ModelById::add(aggregateModel);
-    doc->addAggregateModel(aggregateModelId);
+    doc->addNonDerivedModel(aggregateModelId);
 
     auto alignmentModel = std::make_shared<AlignmentModel>
         (referenceId, otherId, ModelId());
@@ -161,6 +168,7 @@
         if (beginTransformDrivenAlignment(aggregateModelId,
                                           alignmentModelId)) {
             other->setAlignment(alignmentModelId);
+            doc->addNonDerivedModel(alignmentModelId);
         } else {
             error = alignmentModel->getError();
             ModelById::release(alignmentModel);
@@ -186,20 +194,24 @@
         ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance();
 
         QString message;
-        ModelId transformOutput = mtf->transform(transform,
-                                                 aggregateModelId,
-                                                 message);
+        ModelId tuningDiffOutputModelId = mtf->transform(transform,
+                                                         aggregateModelId,
+                                                         message);
 
-        auto tdout = ModelById::getAs<SparseTimeValueModel>(transformOutput);
-        if (!tdout) {
+        auto tuningDiffOutputModel =
+            ModelById::getAs<SparseTimeValueModel>(tuningDiffOutputModelId);
+        if (!tuningDiffOutputModel) {
             SVCERR << "Align::alignModel: ERROR: Failed to create tuning-difference output model (no Tuning Difference plugin?)" << endl;
             error = message;
+            ModelById::release(alignmentModel);
             return false;
         }
 
         other->setAlignment(alignmentModelId);
+        doc->addNonDerivedModel(alignmentModelId);
     
-        connect(tdout.get(), SIGNAL(completionChanged(ModelId)),
+        connect(tuningDiffOutputModel.get(),
+                SIGNAL(completionChanged(ModelId)),
                 this, SLOT(tuningDifferenceCompletionChanged(ModelId)));
 
         TuningDiffRec rec;
@@ -216,43 +228,45 @@
         rec.preparatory = preparatoryModelId;
         alignmentModel->setPathFrom(rec.preparatory);
         
-        m_pendingTuningDiffs[transformOutput] = rec;
+        m_pendingTuningDiffs[tuningDiffOutputModelId] = rec;
     }
 
     return true;
 }
 
 void
-Align::tuningDifferenceCompletionChanged(ModelId tdId)
+Align::tuningDifferenceCompletionChanged(ModelId tuningDiffOutputModelId)
 {
     QMutexLocker locker(&m_mutex);
 
-    if (m_pendingTuningDiffs.find(tdId) == m_pendingTuningDiffs.end()) {
+    if (m_pendingTuningDiffs.find(tuningDiffOutputModelId) ==
+        m_pendingTuningDiffs.end()) {
         SVCERR << "ERROR: Align::tuningDifferenceCompletionChanged: Model "
-               << tdId << " not found in pending tuning diff map!" << endl;
+               << tuningDiffOutputModelId
+               << " not found in pending tuning diff map!" << endl;
         return;
     }
 
-    auto td = ModelById::getAs<SparseTimeValueModel>(tdId);
-    if (!td) {
+    auto tuningDiffOutputModel =
+        ModelById::getAs<SparseTimeValueModel>(tuningDiffOutputModelId);
+    if (!tuningDiffOutputModel) {
         SVCERR << "WARNING: Align::tuningDifferenceCompletionChanged: Model "
-               << tdId << " not known as SparseTimeValueModel" << endl;
+               << tuningDiffOutputModelId
+               << " not known as SparseTimeValueModel" << endl;
         return;
     }
 
-    TuningDiffRec rec = m_pendingTuningDiffs[tdId];
+    TuningDiffRec rec = m_pendingTuningDiffs[tuningDiffOutputModelId];
 
-    auto alignment = ModelById::getAs<AlignmentModel>(rec.alignment);
-    if (!alignment) {
+    auto alignmentModel = ModelById::getAs<AlignmentModel>(rec.alignment);
+    if (!alignmentModel) {
         SVCERR << "WARNING: Align::tuningDifferenceCompletionChanged:"
                << "alignment model has disappeared" << endl;
         return;
     }
     
     int completion = 0;
-    bool done = td->isReady(&completion);
-
-//    SVCERR << "Align::tuningDifferenceCompletionChanged: done = " << done << ", completion = " << completion << endl;
+    bool done = tuningDiffOutputModel->isReady(&completion);
 
     if (!done) {
         // This will be the completion the alignment model reports,
@@ -260,28 +274,30 @@
         // 99 (not 100!) and then back to 0 again when we start
         // calculating the actual path in the following phase
         int clamped = (completion == 100 ? 99 : completion);
-//        SVCERR << "Align::tuningDifferenceCompletionChanged: setting rec.preparatory completion to " << clamped << endl;
-        auto preparatory = ModelById::getAs<SparseTimeValueModel>
-            (rec.preparatory);
-        if (preparatory) {
-            preparatory->setCompletion(clamped);
+        auto preparatoryModel =
+            ModelById::getAs<SparseTimeValueModel>(rec.preparatory);
+        if (preparatoryModel) {
+            preparatoryModel->setCompletion(clamped);
         }
         return;
     }
 
     float tuningFrequency = 440.f;
     
-    if (!td->isEmpty()) {
-        tuningFrequency = td->getAllEvents()[0].getValue();
+    if (!tuningDiffOutputModel->isEmpty()) {
+        tuningFrequency = tuningDiffOutputModel->getAllEvents()[0].getValue();
         SVCERR << "Align::tuningDifferenceCompletionChanged: Reported tuning frequency = " << tuningFrequency << endl;
     } else {
         SVCERR << "Align::tuningDifferenceCompletionChanged: No tuning frequency reported" << endl;
     }    
 
-    m_pendingTuningDiffs.erase(tdId);
-    ModelById::release(tdId);
+    ModelById::release(tuningDiffOutputModel);
     
-    alignment->setPathFrom({});
+    alignmentModel->setPathFrom({}); // replace preparatoryModel
+    ModelById::release(rec.preparatory);
+    rec.preparatory = {};
+    
+    m_pendingTuningDiffs.erase(tuningDiffOutputModelId);
     
     beginTransformDrivenAlignment
         (rec.input, rec.alignment, tuningFrequency);
@@ -296,8 +312,10 @@
     
     TransformFactory *tf = TransformFactory::getInstance();
 
-    auto aggregateModel = ModelById::getAs<AggregateWaveModel>(aggregateModelId);
-    auto alignmentModel = ModelById::getAs<AlignmentModel>(alignmentModelId);
+    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;
@@ -320,29 +338,31 @@
     ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance();
 
     QString message;
-    ModelId transformOutput = mtf->transform
+    ModelId pathOutputModelId = mtf->transform
         (transform, aggregateModelId, message);
 
-    if (transformOutput.isNone()) {
+    if (pathOutputModelId.isNone()) {
         transform.setStepSize(0);
-        transformOutput = mtf->transform
+        pathOutputModelId = mtf->transform
             (transform, aggregateModelId, message);
     }
 
-    auto path = ModelById::getAs<SparseTimeValueModel>(transformOutput);
+    auto pathOutputModel =
+        ModelById::getAs<SparseTimeValueModel>(pathOutputModelId);
 
     //!!! callers will need to be updated to get error from
     //!!! alignment model after initial call
         
-    if (!path) {
+    if (!pathOutputModel) {
         SVCERR << "Align::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl;
-        ModelById::release(transformOutput);
         alignmentModel->setError(message);
         return false;
     }
 
-    path->setCompletion(0);
-    alignmentModel->setPathFrom(transformOutput); //!!! who releases transformOutput?
+    pathOutputModel->setCompletion(0);
+    alignmentModel->setPathFrom(pathOutputModelId);
+
+    m_pendingAlignments[alignmentModelId] = pathOutputModelId;
 
     connect(alignmentModel.get(), SIGNAL(completionChanged(ModelId)),
             this, SLOT(alignmentCompletionChanged(ModelId)));
@@ -351,20 +371,30 @@
 }
 
 void
-Align::alignmentCompletionChanged(ModelId modelId)
+Align::alignmentCompletionChanged(ModelId alignmentModelId)
 {
     QMutexLocker locker (&m_mutex);
 
-    auto am = ModelById::getAs<AlignmentModel>(modelId);
-    if (am && am->isReady()) {
-        disconnect(am.get(), SIGNAL(completionChanged(ModelId)),
+    auto alignmentModel = ModelById::getAs<AlignmentModel>(alignmentModelId);
+
+    if (alignmentModel && alignmentModel->isReady()) {
+
+        if (m_pendingAlignments.find(alignmentModelId) !=
+            m_pendingAlignments.end()) {
+            ModelId pathOutputModelId = m_pendingAlignments[alignmentModelId];
+            ModelById::release(pathOutputModelId);
+            m_pendingAlignments.erase(alignmentModelId);
+        }
+        
+        disconnect(alignmentModel.get(),
+                   SIGNAL(completionChanged(ModelId)),
                    this, SLOT(alignmentCompletionChanged(ModelId)));
-        emit alignmentComplete(modelId);
+        emit alignmentComplete(alignmentModelId);
     }
 }
 
 bool
-Align::alignModelViaProgram(Document *,
+Align::alignModelViaProgram(Document *doc,
                             ModelId referenceId,
                             ModelId otherId,
                             QString program,
@@ -417,11 +447,12 @@
                << endl;
         error = "Alignment program could not be started";
         m_pendingProcesses.erase(process);
-        //!!! who releases alignmentModel? does this? review
         other->setAlignment({});
+        ModelById::release(alignmentModelId);
         delete process;
     }
 
+    doc->addNonDerivedModel(alignmentModelId);
     return success;
 }
 
--- a/framework/Align.h	Fri Jul 12 09:40:56 2019 +0100
+++ b/framework/Align.h	Fri Jul 12 13:58:02 2019 +0100
@@ -117,6 +117,9 @@
     // needed for subsequent alignment
     std::map<ModelId, TuningDiffRec> m_pendingTuningDiffs;
 
+    // alignment model id -> path output model id
+    std::map<ModelId, ModelId> m_pendingAlignments;
+    
     // external alignment subprocess -> model into which to stuff the
     // results (an AlignmentModel)
     std::map<QProcess *, ModelId> m_pendingProcesses;
--- a/framework/Document.cpp	Fri Jul 12 09:40:56 2019 +0100
+++ b/framework/Document.cpp	Fri Jul 12 13:58:02 2019 +0100
@@ -73,22 +73,40 @@
     CommandHistory::getInstance()->clear();
     
 #ifdef DEBUG_DOCUMENT
-    SVDEBUG << "Document::~Document: about to delete layers" << endl;
+    SVCERR << "Document::~Document: about to delete layers" << endl;
 #endif
     while (!m_layers.empty()) {
         deleteLayer(*m_layers.begin(), true);
     }
 
+#ifdef DEBUG_DOCUMENT
+    SVCERR << "Document::~Document: about to release normal models" << endl;
+#endif
     for (auto mr: m_models) {
         ModelById::release(mr.first);
     }
+
+#ifdef DEBUG_DOCUMENT
+    SVCERR << "Document::~Document: about to release aggregate models" << endl;
+#endif
     for (auto m: m_aggregateModels) {
         ModelById::release(m);
     }
 
+#ifdef DEBUG_DOCUMENT
+    SVCERR << "Document::~Document: about to release alignment models" << endl;
+#endif
+    for (auto m: m_alignmentModels) {
+        ModelById::release(m);
+    }
+
+#ifdef DEBUG_DOCUMENT
+    SVCERR << "Document::~Document: about to release main model" << endl;
+#endif
     if (!m_mainModel.isNone()) {
         ModelById::release(m_mainModel);
     }
+    
     m_mainModel = {};
     emit mainModelChanged({});
 }
@@ -141,7 +159,7 @@
 
     newLayer->setObjectName(getUniqueLayerName(newLayer->objectName()));
 
-    addImportedModel(modelId);
+    addNonDerivedModel(modelId);
     setModel(newLayer, modelId);
 
     //!!! and all channels
@@ -173,7 +191,7 @@
     }
 
     auto newModelId = ModelById::add(newModel);
-    addImportedModel(newModelId);
+    addNonDerivedModel(newModelId);
     setModel(newLayer, newModelId);
 
     return newLayer;
@@ -607,10 +625,25 @@
 }
 
 void
-Document::addImportedModel(ModelId modelId)
+Document::addNonDerivedModel(ModelId modelId)
 {
+    if (ModelById::isa<AggregateWaveModel>(modelId)) {
+#ifdef DEBUG_DOCUMENT
+        SVCERR << "Document::addNonDerivedModel: Model " << modelId << " is an aggregate model, adding it to aggregates" << endl;
+#endif
+        m_aggregateModels.insert(modelId);
+        return;
+    }
+    if (ModelById::isa<AlignmentModel>(modelId)) {
+#ifdef DEBUG_DOCUMENT
+        SVCERR << "Document::addNonDerivedModel: Model " << modelId << " is an alignment model, adding it to alignments" << endl;
+#endif
+        m_alignmentModels.insert(modelId);
+        return;
+    }
+    
     if (m_models.find(modelId) != m_models.end()) {
-        SVCERR << "WARNING: Document::addImportedModel: Model already added"
+        SVCERR << "WARNING: Document::addNonDerivedModel: Model already added"
                << endl;
         return;
     }
@@ -623,19 +656,19 @@
     m_models[modelId] = rec;
 
 #ifdef DEBUG_DOCUMENT
-    SVDEBUG << "Document::addImportedModel: Added model " << modelId << endl;
-    SVDEBUG << "Models now: ";
+    SVCERR << "Document::addNonDerivedModel: Added model " << modelId << endl;
+    SVCERR << "Models now: ";
     for (const auto &rec : m_models) {
-        SVDEBUG << rec.first << " ";
+        SVCERR << rec.first << " ";
     } 
-    SVDEBUG << endl;
+    SVCERR << endl;
 #endif
 
     if (m_autoAlignment) {
-        SVDEBUG << "Document::addImportedModel: auto-alignment is on, aligning model if possible" << endl;
+        SVDEBUG << "Document::addNonDerivedModel: auto-alignment is on, aligning model if possible" << endl;
         alignModel(modelId);
     } else {
-        SVDEBUG << "Document(" << this << "): addImportedModel: auto-alignment is off" << endl;
+        SVDEBUG << "Document(" << this << "): addNonDerivedModel: auto-alignment is off" << endl;
     }
 
     emit modelAdded(modelId);
@@ -666,21 +699,15 @@
     SVDEBUG << endl;
 #endif
 
-    if (m_autoAlignment) {
-        SVDEBUG << "Document::addAdditionalModel: auto-alignment is on, aligning model if possible" << endl;
+    if (m_autoAlignment &&
+        ModelById::isa<RangeSummarisableTimeValueModel>(modelId)) {
+        SVDEBUG << "Document::addAdditionalModel: auto-alignment is on and model is an alignable type, aligning it if possible" << endl;
         alignModel(modelId);
     }
 
     emit modelAdded(modelId);
 }
 
-void
-Document::addAggregateModel(ModelId modelId)
-{
-    m_aggregateModels.insert(modelId);
-    SVDEBUG << "Document::addAggregateModel(" << modelId << ")" << endl;
-}
-
 ModelId
 Document::addDerivedModel(const Transform &transform,
                           const ModelTransformer::Input &input,
@@ -758,7 +785,13 @@
     // borrowed-pointer mechanism will at least prevent memory errors,
     // although the other code will have to stop whatever it's doing.
 
-    SVCERR << "Document::releaseModel(" << modelId << ")" << endl;
+    if (auto model = ModelById::get(modelId)) {
+        SVCERR << "Document::releaseModel(" << modelId << "), name "
+               << model->objectName() << ", type "
+               << typeid(*model.get()).name() << endl;
+    } else {
+        SVCERR << "Document::releaseModel(" << modelId << ")" << endl;
+    }
     
     if (modelId.isNone()) {
         return;
@@ -777,7 +810,7 @@
     }
 
     if (m_models.find(modelId) == m_models.end()) {
-        // No point in releasing aggregate models and the like,
+        // No point in releasing aggregate and alignment models,
         // they're not large
 #ifdef DEBUG_DOCUMENT
         SVCERR << "Document::releaseModel: It's not a regular layer model, ignoring" << endl;
@@ -902,14 +935,7 @@
                 << modelId << endl;
         return;
     }
-/*!!!
-    if (model && model != m_mainModel) {
-        ModelList::iterator mitr = findModelInList(model);
-        if (mitr != m_models.end()) {
-            mitr->refcount ++;
-        }
-    }
-*/
+
     if (!modelId.isNone() && !previousModel.isNone()) {
         PlayParameterRepository::getInstance()->copyParameters
             (previousModel.untyped, modelId.untyped);
@@ -1100,9 +1126,10 @@
                 << "): is main model, setting alignment to itself" << endl;
         auto alignment = std::make_shared<AlignmentModel>(modelId, modelId,
                                                           ModelId());
-        
-        //!!! hang on, who tracks alignment models?
-        rm->setAlignment(ModelById::add(alignment));
+
+        ModelId alignmentModelId = ModelById::add(alignment);
+        rm->setAlignment(alignmentModelId);
+        m_alignmentModels.insert(alignmentModelId);
         return;
     }
 
--- a/framework/Document.h	Fri Jul 12 09:40:56 2019 +0100
+++ b/framework/Document.h	Fri Jul 12 13:58:02 2019 +0100
@@ -223,38 +223,25 @@
      * 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. The model must have been added to ModelById
-     * already, and Document will take responsibility for releasing it
-     * later.
+     * createDerivedLayer. Document will take responsibility for
+     * releasing the model later.
      */
     void addAlreadyDerivedModel(const Transform &transform,
                                 const ModelTransformer::Input &input,
                                 ModelId outputModelToAdd);
 
     /**
-     * Add an imported (non-derived, non-main) model.  This is
-     * necessary to register any imported model that is associated
-     * with a layer. The model must have been added to ModelById
-     * already, and Document will take responsibility for releasing it
-     * later.
+     * Add an imported model, i.e. any model (other than the main
+     * model) that has been created by any means other than as the
+     * output of a transform.  This is necessary to register any
+     * imported model that is to be associated with a layer, and also
+     * to make sure that the model is released by the Document
+     * later. Aggregate models, alignment models, and miscellaneous
+     * temporary models should also be added in this way, unless the
+     * temporary models are large enough to need managing in a way
+     * that guarantees the shortest possible lifespan.
      */
-    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
-     * for a single transform each and have no refcount. (This
-     * 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(ModelId); // an AggregateWaveModel
+    void addNonDerivedModel(ModelId);
 
     /**
      * Associate the given model with the given layer.  The model must
@@ -393,7 +380,9 @@
     // 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;
+    std::set<ModelId> m_alignmentModels;
     
     /**
      * Add an extra derived model (returned at the end of processing a
--- a/framework/MainWindowBase.cpp	Fri Jul 12 09:40:56 2019 +0100
+++ b/framework/MainWindowBase.cpp	Fri Jul 12 13:58:02 2019 +0100
@@ -1727,7 +1727,7 @@
         CommandHistory::getInstance()->startCompoundOperation
             (tr("Import \"%1\"").arg(source.getBasename()), true);
 
-        m_document->addImportedModel(newModel);
+        m_document->addNonDerivedModel(newModel);
 
         AddPaneCommand *command = new AddPaneCommand(this);
         CommandHistory::getInstance()->addCommand(command);
@@ -1771,7 +1771,7 @@
         CommandHistory::getInstance()->startCompoundOperation
             (tr("Import \"%1\"").arg(source.getBasename()), true);
 
-        m_document->addImportedModel(newModel);
+        m_document->addNonDerivedModel(newModel);
 
         if (replace) {
             m_document->removeLayerFromView(pane, replace);
@@ -3321,7 +3321,7 @@
         CommandHistory::getInstance()->startCompoundOperation
             (tr("Import Recorded Audio"), true);
 
-        m_document->addImportedModel(modelId);
+        m_document->addNonDerivedModel(modelId);
 
         AddPaneCommand *command = new AddPaneCommand(this);
         CommandHistory::getInstance()->addCommand(command);
--- a/framework/SVFileReader.cpp	Fri Jul 12 09:40:56 2019 +0100
+++ b/framework/SVFileReader.cpp	Fri Jul 12 13:58:02 2019 +0100
@@ -484,7 +484,7 @@
         // the .sv file.  (pity we don't have a nicer way to handle
         // this)
         if (!ModelById::isa<AlignmentModel>(modelId)) {
-            m_document->addImportedModel(modelId);
+            m_document->addNonDerivedModel(modelId);
         }
         
         // but we add all models including alignment ones to the added