diff framework/Document.cpp @ 683:0736beb8b852 by-id

Toward updating Document for ModelById
author Chris Cannam
date Wed, 03 Jul 2019 13:01:26 +0100
parents 1f18e0f64af8
children 610fa108fbcc
line wrap: on
line diff
--- 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