| Chris@45 | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@45 | 2 | 
| Chris@45 | 3 /* | 
| Chris@45 | 4     Sonic Visualiser | 
| Chris@45 | 5     An audio file viewer and annotation editor. | 
| Chris@45 | 6     Centre for Digital Music, Queen Mary, University of London. | 
| Chris@45 | 7     This file copyright 2006 Chris Cannam and QMUL. | 
| Chris@45 | 8 | 
| Chris@45 | 9     This program is free software; you can redistribute it and/or | 
| Chris@45 | 10     modify it under the terms of the GNU General Public License as | 
| Chris@45 | 11     published by the Free Software Foundation; either version 2 of the | 
| Chris@45 | 12     License, or (at your option) any later version.  See the file | 
| Chris@45 | 13     COPYING included with this distribution for more information. | 
| Chris@45 | 14 */ | 
| Chris@45 | 15 | 
| Chris@45 | 16 #include "Document.h" | 
| Chris@45 | 17 | 
| Chris@45 | 18 #include "data/model/WaveFileModel.h" | 
| Chris@45 | 19 #include "data/model/WritableWaveFileModel.h" | 
| Chris@45 | 20 #include "data/model/DenseThreeDimensionalModel.h" | 
| Chris@45 | 21 #include "data/model/DenseTimeValueModel.h" | 
| gyorgyf@270 | 22 #include "data/model/FlexiNoteModel.h" | 
| gyorgyf@270 | 23 | 
| Chris@45 | 24 #include "layer/Layer.h" | 
| Chris@105 | 25 #include "widgets/CommandHistory.h" | 
| Chris@45 | 26 #include "base/Command.h" | 
| Chris@45 | 27 #include "view/View.h" | 
| Chris@45 | 28 #include "base/PlayParameterRepository.h" | 
| Chris@45 | 29 #include "base/PlayParameters.h" | 
| Chris@106 | 30 #include "transform/TransformFactory.h" | 
| Chris@106 | 31 #include "transform/ModelTransformerFactory.h" | 
| gyorgyf@270 | 32 #include "transform/FeatureExtractionModelTransformer.h" | 
| Chris@45 | 33 #include <QApplication> | 
| Chris@45 | 34 #include <QTextStream> | 
| Chris@90 | 35 #include <QSettings> | 
| Chris@45 | 36 #include <iostream> | 
| Chris@212 | 37 #include <typeinfo> | 
| Chris@45 | 38 | 
| Chris@45 | 39 // For alignment: | 
| Chris@45 | 40 #include "data/model/AggregateWaveModel.h" | 
| Chris@45 | 41 #include "data/model/SparseTimeValueModel.h" | 
| Chris@45 | 42 #include "data/model/AlignmentModel.h" | 
| Chris@45 | 43 | 
| Chris@138 | 44 //#define DEBUG_DOCUMENT 1 | 
| Chris@77 | 45 | 
| Chris@45 | 46 //!!! still need to handle command history, documentRestored/documentModified | 
| Chris@45 | 47 | 
| Chris@45 | 48 Document::Document() : | 
| Chris@47 | 49     m_mainModel(0), | 
| Chris@47 | 50     m_autoAlignment(false) | 
| Chris@45 | 51 { | 
| Chris@45 | 52     connect(this, SIGNAL(modelAboutToBeDeleted(Model *)), | 
| Chris@54 | 53             ModelTransformerFactory::getInstance(), | 
| Chris@45 | 54             SLOT(modelAboutToBeDeleted(Model *))); | 
| Chris@45 | 55 } | 
| Chris@45 | 56 | 
| Chris@45 | 57 Document::~Document() | 
| Chris@45 | 58 { | 
| Chris@45 | 59     //!!! Document should really own the command history.  atm we | 
| Chris@45 | 60     //still refer to it in various places that don't have access to | 
| Chris@45 | 61     //the document, be nice to fix that | 
| Chris@45 | 62 | 
| Chris@77 | 63 #ifdef DEBUG_DOCUMENT | 
| Chris@77 | 64     std::cerr << "\n\nDocument::~Document: about to clear command history" << std::endl; | 
| Chris@77 | 65 #endif | 
| Chris@45 | 66     CommandHistory::getInstance()->clear(); | 
| Chris@45 | 67 | 
| Chris@77 | 68 #ifdef DEBUG_DOCUMENT | 
| Chris@233 | 69     SVDEBUG << "Document::~Document: about to delete layers" << endl; | 
| Chris@77 | 70 #endif | 
| Chris@45 | 71     while (!m_layers.empty()) { | 
| Chris@45 | 72 	deleteLayer(*m_layers.begin(), true); | 
| Chris@45 | 73     } | 
| Chris@45 | 74 | 
| Chris@45 | 75     if (!m_models.empty()) { | 
| Chris@233 | 76 	SVDEBUG << "Document::~Document: WARNING: " | 
| Chris@45 | 77 		  << m_models.size() << " model(s) still remain -- " | 
| Chris@45 | 78 		  << "should have been garbage collected when deleting layers" | 
| Chris@229 | 79 		  << endl; | 
| Chris@45 | 80 	while (!m_models.empty()) { | 
| Chris@45 | 81             Model *model = m_models.begin()->first; | 
| Chris@45 | 82 	    if (model == m_mainModel) { | 
| Chris@45 | 83 		// just in case! | 
| Chris@233 | 84 		SVDEBUG << "Document::~Document: WARNING: Main model is also" | 
| Chris@229 | 85 			  << " in models list!" << endl; | 
| Chris@45 | 86 	    } else if (model) { | 
| Chris@79 | 87                 model->aboutToDelete(); | 
| Chris@45 | 88 		emit modelAboutToBeDeleted(model); | 
| Chris@45 | 89 		delete model; | 
| Chris@45 | 90 	    } | 
| Chris@45 | 91 	    m_models.erase(m_models.begin()); | 
| Chris@45 | 92 	} | 
| Chris@45 | 93     } | 
| Chris@45 | 94 | 
| Chris@77 | 95 #ifdef DEBUG_DOCUMENT | 
| Chris@233 | 96     SVDEBUG << "Document::~Document: About to get rid of main model" | 
| Chris@229 | 97 	      << endl; | 
| Chris@77 | 98 #endif | 
| Chris@45 | 99     if (m_mainModel) { | 
| Chris@79 | 100         m_mainModel->aboutToDelete(); | 
| Chris@45 | 101         emit modelAboutToBeDeleted(m_mainModel); | 
| Chris@45 | 102     } | 
| Chris@45 | 103 | 
| Chris@45 | 104     emit mainModelChanged(0); | 
| Chris@45 | 105     delete m_mainModel; | 
| Chris@45 | 106 | 
| Chris@45 | 107 } | 
| Chris@45 | 108 | 
| Chris@45 | 109 Layer * | 
| Chris@45 | 110 Document::createLayer(LayerFactory::LayerType type) | 
| Chris@45 | 111 { | 
| Chris@45 | 112     Layer *newLayer = LayerFactory::getInstance()->createLayer(type); | 
| Chris@45 | 113     if (!newLayer) return 0; | 
| Chris@45 | 114 | 
| Chris@45 | 115     newLayer->setObjectName(getUniqueLayerName(newLayer->objectName())); | 
| Chris@45 | 116 | 
| Chris@45 | 117     m_layers.insert(newLayer); | 
| Chris@52 | 118 | 
| Chris@77 | 119 #ifdef DEBUG_DOCUMENT | 
| Chris@233 | 120     SVDEBUG << "Document::createLayer: Added layer of type " << type | 
| Chris@229 | 121               << ", now have " << m_layers.size() << " layers" << endl; | 
| Chris@77 | 122 #endif | 
| Chris@52 | 123 | 
| Chris@45 | 124     emit layerAdded(newLayer); | 
| Chris@45 | 125 | 
| Chris@45 | 126     return newLayer; | 
| Chris@45 | 127 } | 
| Chris@45 | 128 | 
| Chris@45 | 129 Layer * | 
| Chris@45 | 130 Document::createMainModelLayer(LayerFactory::LayerType type) | 
| Chris@45 | 131 { | 
| Chris@45 | 132     Layer *newLayer = createLayer(type); | 
| Chris@45 | 133     if (!newLayer) return 0; | 
| Chris@45 | 134     setModel(newLayer, m_mainModel); | 
| Chris@45 | 135     return newLayer; | 
| Chris@45 | 136 } | 
| Chris@45 | 137 | 
| Chris@45 | 138 Layer * | 
| Chris@45 | 139 Document::createImportedLayer(Model *model) | 
| Chris@45 | 140 { | 
| Chris@45 | 141     LayerFactory::LayerTypeSet types = | 
| Chris@45 | 142 	LayerFactory::getInstance()->getValidLayerTypes(model); | 
| Chris@45 | 143 | 
| Chris@45 | 144     if (types.empty()) { | 
| Chris@45 | 145 	std::cerr << "WARNING: Document::importLayer: no valid display layer for model" << std::endl; | 
| Chris@45 | 146 	return 0; | 
| Chris@45 | 147     } | 
| Chris@45 | 148 | 
| Chris@45 | 149     //!!! for now, just use the first suitable layer type | 
| Chris@45 | 150     LayerFactory::LayerType type = *types.begin(); | 
| Chris@45 | 151 | 
| Chris@45 | 152     Layer *newLayer = LayerFactory::getInstance()->createLayer(type); | 
| Chris@45 | 153     if (!newLayer) return 0; | 
| Chris@45 | 154 | 
| Chris@45 | 155     newLayer->setObjectName(getUniqueLayerName(newLayer->objectName())); | 
| Chris@45 | 156 | 
| Chris@45 | 157     addImportedModel(model); | 
| Chris@45 | 158     setModel(newLayer, model); | 
| Chris@45 | 159 | 
| Chris@45 | 160     //!!! and all channels | 
| Chris@45 | 161     setChannel(newLayer, -1); | 
| Chris@45 | 162 | 
| Chris@45 | 163     m_layers.insert(newLayer); | 
| Chris@52 | 164 | 
| Chris@77 | 165 #ifdef DEBUG_DOCUMENT | 
| Chris@233 | 166     SVDEBUG << "Document::createImportedLayer: Added layer of type " << type | 
| Chris@229 | 167               << ", now have " << m_layers.size() << " layers" << endl; | 
| Chris@77 | 168 #endif | 
| Chris@52 | 169 | 
| Chris@45 | 170     emit layerAdded(newLayer); | 
| Chris@45 | 171     return newLayer; | 
| Chris@45 | 172 } | 
| Chris@45 | 173 | 
| Chris@45 | 174 Layer * | 
| Chris@45 | 175 Document::createEmptyLayer(LayerFactory::LayerType type) | 
| Chris@45 | 176 { | 
| Chris@61 | 177     if (!m_mainModel) return 0; | 
| Chris@61 | 178 | 
| Chris@45 | 179     Model *newModel = | 
| Chris@45 | 180 	LayerFactory::getInstance()->createEmptyModel(type, m_mainModel); | 
| Chris@45 | 181     if (!newModel) return 0; | 
| Chris@45 | 182 | 
| Chris@45 | 183     Layer *newLayer = createLayer(type); | 
| Chris@45 | 184     if (!newLayer) { | 
| Chris@45 | 185 	delete newModel; | 
| Chris@45 | 186 	return 0; | 
| Chris@45 | 187     } | 
| Chris@45 | 188 | 
| Chris@45 | 189     addImportedModel(newModel); | 
| Chris@45 | 190     setModel(newLayer, newModel); | 
| Chris@45 | 191 | 
| Chris@45 | 192     return newLayer; | 
| Chris@45 | 193 } | 
| Chris@45 | 194 | 
| Chris@45 | 195 Layer * | 
| Chris@45 | 196 Document::createDerivedLayer(LayerFactory::LayerType type, | 
| Chris@54 | 197 			     TransformId transform) | 
| Chris@45 | 198 { | 
| Chris@45 | 199     Layer *newLayer = createLayer(type); | 
| Chris@45 | 200     if (!newLayer) return 0; | 
| Chris@45 | 201 | 
| Chris@45 | 202     newLayer->setObjectName(getUniqueLayerName | 
| Chris@54 | 203                             (TransformFactory::getInstance()-> | 
| Chris@54 | 204                              getTransformFriendlyName(transform))); | 
| Chris@45 | 205 | 
| Chris@45 | 206     return newLayer; | 
| Chris@45 | 207 } | 
| Chris@45 | 208 Layer * | 
| Chris@72 | 209 Document::createDerivedLayer(const Transform &transform, | 
| Chris@72 | 210                              const ModelTransformer::Input &input) | 
| Chris@45 | 211 { | 
| Chris@78 | 212     QString message; | 
| Chris@78 | 213     Model *newModel = addDerivedModel(transform, input, message); | 
| Chris@45 | 214     if (!newModel) { | 
| Chris@78 | 215         emit modelGenerationFailed(transform.getIdentifier(), message); | 
| Chris@45 | 216         return 0; | 
| Chris@78 | 217     } else if (message != "") { | 
| Chris@78 | 218         emit modelGenerationWarning(transform.getIdentifier(), message); | 
| Chris@45 | 219     } | 
| Chris@45 | 220 | 
| Chris@45 | 221     LayerFactory::LayerTypeSet types = | 
| Chris@45 | 222 	LayerFactory::getInstance()->getValidLayerTypes(newModel); | 
| Chris@45 | 223 | 
| Chris@45 | 224     if (types.empty()) { | 
| Chris@228 | 225 	std::cerr << "WARNING: Document::createLayerForTransformer: no valid display layer for output of transform " << transform.getIdentifier() << std::endl; | 
| Chris@136 | 226         newModel->aboutToDelete(); | 
| Chris@136 | 227         emit modelAboutToBeDeleted(newModel); | 
| Chris@136 | 228         m_models.erase(newModel); | 
| Chris@45 | 229 	delete newModel; | 
| Chris@45 | 230 	return 0; | 
| Chris@45 | 231     } | 
| Chris@45 | 232 | 
| Chris@45 | 233     //!!! for now, just use the first suitable layer type | 
| Chris@45 | 234 | 
| Chris@45 | 235     Layer *newLayer = createLayer(*types.begin()); | 
| Chris@45 | 236     setModel(newLayer, newModel); | 
| Chris@45 | 237 | 
| Chris@45 | 238     //!!! We need to clone the model when adding the layer, so that it | 
| Chris@45 | 239     //can be edited without affecting other layers that are based on | 
| Chris@45 | 240     //the same model.  Unfortunately we can't just clone it now, | 
| Chris@45 | 241     //because it probably hasn't been completed yet -- the transform | 
| Chris@45 | 242     //runs in the background.  Maybe the transform has to handle | 
| Chris@45 | 243     //cloning and cacheing models itself. | 
| Chris@45 | 244     // | 
| Chris@45 | 245     // Once we do clone models here, of course, we'll have to avoid | 
| Chris@45 | 246     // leaking them too. | 
| Chris@45 | 247     // | 
| Chris@45 | 248     // We want the user to be able to add a model to a second layer | 
| Chris@45 | 249     // _while it's still being calculated in the first_ and have it | 
| Chris@45 | 250     // work quickly.  That means we need to put the same physical | 
| Chris@45 | 251     // model pointer in both layers, so they can't actually be cloned. | 
| Chris@45 | 252 | 
| Chris@45 | 253     if (newLayer) { | 
| Chris@45 | 254 	newLayer->setObjectName(getUniqueLayerName | 
| Chris@54 | 255                                 (TransformFactory::getInstance()-> | 
| Chris@72 | 256                                  getTransformFriendlyName | 
| Chris@72 | 257                                  (transform.getIdentifier()))); | 
| Chris@45 | 258     } | 
| Chris@45 | 259 | 
| Chris@45 | 260     emit layerAdded(newLayer); | 
| Chris@45 | 261     return newLayer; | 
| Chris@45 | 262 } | 
| Chris@45 | 263 | 
| matthiasm@269 | 264 Layer * | 
| matthiasm@269 | 265 Document::createDerivedLayer(const Transform &transform, | 
| matthiasm@269 | 266                              const ModelTransformer::Input &input, | 
| gyorgyf@270 | 267                              const LayerFactory::LayerType type, | 
| gyorgyf@270 | 268 							 const FeatureExtractionModelTransformer::PreferredOutputModel outputmodel) | 
| matthiasm@269 | 269 { | 
| matthiasm@269 | 270 	// !!! THIS IS TOTALLY REDUNDANT CODE, EXCEPT FOR THE type SETTING | 
| matthiasm@269 | 271 | 
| gyorgyf@270 | 272 	    QString message; | 
| gyorgyf@270 | 273 	    Model *newModel = addDerivedModel(transform, input, message, outputmodel); | 
| gyorgyf@270 | 274 	    if (!newModel) { | 
| gyorgyf@270 | 275 	        emit modelGenerationFailed(transform.getIdentifier(), message); | 
| gyorgyf@270 | 276 	        return 0; | 
| gyorgyf@270 | 277 	    } else if (message != "") { | 
| gyorgyf@270 | 278 	        emit modelGenerationWarning(transform.getIdentifier(), message); | 
| gyorgyf@270 | 279 	    } | 
| gyorgyf@270 | 280 | 
| gyorgyf@270 | 281 	    LayerFactory::LayerTypeSet types = | 
| matthiasm@269 | 282 	LayerFactory::getInstance()->getValidLayerTypes(newModel); | 
| gyorgyf@270 | 283 | 
| gyorgyf@270 | 284 	    if (types.empty()) { | 
| matthiasm@269 | 285 	std::cerr << "WARNING: Document::createLayerForTransformer: no valid display layer for output of transform " << transform.getIdentifier() << std::endl; | 
| gyorgyf@270 | 286 	        newModel->aboutToDelete(); | 
| gyorgyf@270 | 287 	        emit modelAboutToBeDeleted(newModel); | 
| gyorgyf@270 | 288 	        m_models.erase(newModel); | 
| matthiasm@269 | 289 	delete newModel; | 
| matthiasm@269 | 290 	return 0; | 
| gyorgyf@270 | 291 	    } | 
| matthiasm@269 | 292 | 
| gyorgyf@270 | 293     //!!! creating layer with the specified type | 
| matthiasm@269 | 294 | 
| matthiasm@269 | 295     Layer *newLayer = createLayer(type); | 
| matthiasm@269 | 296     setModel(newLayer, newModel); | 
| matthiasm@269 | 297 | 
| matthiasm@269 | 298     //!!! We need to clone the model when adding the layer, so that it | 
| matthiasm@269 | 299     //can be edited without affecting other layers that are based on | 
| matthiasm@269 | 300     //the same model.  Unfortunately we can't just clone it now, | 
| matthiasm@269 | 301     //because it probably hasn't been completed yet -- the transform | 
| matthiasm@269 | 302     //runs in the background.  Maybe the transform has to handle | 
| matthiasm@269 | 303     //cloning and cacheing models itself. | 
| matthiasm@269 | 304     // | 
| matthiasm@269 | 305     // Once we do clone models here, of course, we'll have to avoid | 
| matthiasm@269 | 306     // leaking them too. | 
| matthiasm@269 | 307     // | 
| matthiasm@269 | 308     // We want the user to be able to add a model to a second layer | 
| matthiasm@269 | 309     // _while it's still being calculated in the first_ and have it | 
| matthiasm@269 | 310     // work quickly.  That means we need to put the same physical | 
| matthiasm@269 | 311     // model pointer in both layers, so they can't actually be cloned. | 
| matthiasm@269 | 312 | 
| matthiasm@269 | 313     if (newLayer) { | 
| matthiasm@269 | 314 	newLayer->setObjectName(getUniqueLayerName | 
| matthiasm@269 | 315                                 (TransformFactory::getInstance()-> | 
| matthiasm@269 | 316                                  getTransformFriendlyName | 
| matthiasm@269 | 317                                  (transform.getIdentifier()))); | 
| matthiasm@269 | 318     } | 
| matthiasm@269 | 319 | 
| matthiasm@269 | 320     emit layerAdded(newLayer); | 
| matthiasm@269 | 321     return newLayer; | 
| matthiasm@269 | 322 } | 
| matthiasm@269 | 323 | 
| matthiasm@269 | 324 | 
| Chris@45 | 325 void | 
| Chris@45 | 326 Document::setMainModel(WaveFileModel *model) | 
| Chris@45 | 327 { | 
| Chris@45 | 328     Model *oldMainModel = m_mainModel; | 
| Chris@45 | 329     m_mainModel = model; | 
| Chris@160 | 330 | 
| Chris@45 | 331     emit modelAdded(m_mainModel); | 
| Chris@160 | 332     if (model) { | 
| Chris@160 | 333         emit activity(tr("Set main model to %1").arg(model->objectName())); | 
| Chris@160 | 334     } else { | 
| Chris@160 | 335         emit activity(tr("Clear main model")); | 
| Chris@160 | 336     } | 
| Chris@45 | 337 | 
| Chris@45 | 338     std::vector<Layer *> obsoleteLayers; | 
| Chris@53 | 339     std::set<QString> failedTransformers; | 
| Chris@45 | 340 | 
| Chris@45 | 341     // We need to ensure that no layer is left using oldMainModel or | 
| Chris@45 | 342     // any of the old derived models as its model.  Either replace the | 
| Chris@45 | 343     // model, or delete the layer for each layer that is currently | 
| Chris@45 | 344     // using one of these.  Carry out this replacement before we | 
| Chris@45 | 345     // delete any of the models. | 
| Chris@45 | 346 | 
| Chris@77 | 347 #ifdef DEBUG_DOCUMENT | 
| Chris@248 | 348     std::cerr << "Document::setMainModel: Have " | 
| Chris@248 | 349               << m_layers.size() << " layers" << std::endl; | 
| Chris@137 | 350     std::cerr << "Models now: "; | 
| Chris@137 | 351     for (ModelMap::const_iterator i = m_models.begin(); i != m_models.end(); ++i) { | 
| Chris@137 | 352         std::cerr << i->first << " "; | 
| Chris@137 | 353     } | 
| Chris@137 | 354     std::cerr << std::endl; | 
| Chris@248 | 355     std::cerr << "Old main model: " << oldMainModel << std::endl; | 
| Chris@77 | 356 #endif | 
| Chris@52 | 357 | 
| Chris@45 | 358     for (LayerSet::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | 
| Chris@45 | 359 | 
| Chris@45 | 360 	Layer *layer = *i; | 
| Chris@45 | 361 	Model *model = layer->getModel(); | 
| Chris@45 | 362 | 
| Chris@77 | 363 #ifdef DEBUG_DOCUMENT | 
| Chris@248 | 364         std::cerr << "Document::setMainModel: inspecting model " | 
| Chris@229 | 365                   << (model ? model->objectName(): "(null)") << " in layer " | 
| Chris@248 | 366                   << layer->objectName() << std::endl; | 
| Chris@77 | 367 #endif | 
| Chris@45 | 368 | 
| Chris@70 | 369 	if (model == oldMainModel) { | 
| Chris@77 | 370 #ifdef DEBUG_DOCUMENT | 
| Chris@77 | 371             std::cerr << "... it uses the old main model, replacing" << std::endl; | 
| Chris@77 | 372 #endif | 
| Chris@45 | 373 	    LayerFactory::getInstance()->setModel(layer, m_mainModel); | 
| Chris@45 | 374 	    continue; | 
| Chris@45 | 375 	} | 
| Chris@45 | 376 | 
| Chris@137 | 377         if (!model) { | 
| Chris@137 | 378             std::cerr << "WARNING: Document::setMainModel: Null model in layer " | 
| Chris@137 | 379                       << layer << std::endl; | 
| Chris@137 | 380 	    // get rid of this hideous degenerate | 
| Chris@137 | 381 	    obsoleteLayers.push_back(layer); | 
| Chris@137 | 382 	    continue; | 
| Chris@137 | 383 	} | 
| Chris@137 | 384 | 
| Chris@137 | 385 	if (m_models.find(model) == m_models.end()) { | 
| Chris@45 | 386 	    std::cerr << "WARNING: Document::setMainModel: Unknown model " | 
| Chris@45 | 387 		      << model << " in layer " << layer << std::endl; | 
| Chris@137 | 388 	    // and this one | 
| Chris@45 | 389 	    obsoleteLayers.push_back(layer); | 
| Chris@45 | 390 	    continue; | 
| Chris@45 | 391 	} | 
| Chris@45 | 392 | 
| Chris@70 | 393 	if (m_models[model].source && | 
| Chris@70 | 394             (m_models[model].source == oldMainModel)) { | 
| Chris@45 | 395 | 
| Chris@77 | 396 #ifdef DEBUG_DOCUMENT | 
| Chris@77 | 397             std::cerr << "... it uses a model derived from the old main model, regenerating" << std::endl; | 
| Chris@77 | 398 #endif | 
| Chris@45 | 399 | 
| Chris@45 | 400 	    // This model was derived from the previous main | 
| Chris@45 | 401 	    // model: regenerate it. | 
| Chris@45 | 402 | 
| Chris@72 | 403 	    const Transform &transform = m_models[model].transform; | 
| Chris@72 | 404             QString transformId = transform.getIdentifier(); | 
| Chris@45 | 405 | 
| Chris@72 | 406             //!!! We have a problem here if the number of channels in | 
| Chris@72 | 407             //the main model has changed. | 
| Chris@72 | 408 | 
| Chris@78 | 409             QString message; | 
| Chris@45 | 410 	    Model *replacementModel = | 
| Chris@45 | 411                 addDerivedModel(transform, | 
| Chris@72 | 412                                 ModelTransformer::Input | 
| Chris@78 | 413                                 (m_mainModel, m_models[model].channel), | 
| Chris@78 | 414                                 message); | 
| Chris@45 | 415 | 
| Chris@45 | 416 	    if (!replacementModel) { | 
| Chris@45 | 417 		std::cerr << "WARNING: Document::setMainModel: Failed to regenerate model for transform \"" | 
| Chris@228 | 418 			  << transformId << "\"" << " in layer " << layer << std::endl; | 
| Chris@72 | 419                 if (failedTransformers.find(transformId) | 
| Chris@72 | 420                     == failedTransformers.end()) { | 
| Chris@45 | 421                     emit modelRegenerationFailed(layer->objectName(), | 
| Chris@78 | 422                                                  transformId, | 
| Chris@78 | 423                                                  message); | 
| Chris@72 | 424                     failedTransformers.insert(transformId); | 
| Chris@45 | 425                 } | 
| Chris@45 | 426 		obsoleteLayers.push_back(layer); | 
| Chris@45 | 427 	    } else { | 
| Chris@78 | 428                 if (message != "") { | 
| Chris@78 | 429                     emit modelRegenerationWarning(layer->objectName(), | 
| Chris@78 | 430                                                   transformId, | 
| Chris@78 | 431                                                   message); | 
| Chris@78 | 432                 } | 
| Chris@77 | 433 #ifdef DEBUG_DOCUMENT | 
| Chris@248 | 434                 std::cerr << "Replacing model " << model << " (type " | 
| Chris@77 | 435                           << typeid(*model).name() << ") with model " | 
| Chris@77 | 436                           << replacementModel << " (type " | 
| Chris@77 | 437                           << typeid(*replacementModel).name() << ") in layer " | 
| Chris@228 | 438                           << layer << " (name " << layer->objectName() << ")" | 
| Chris@248 | 439                           << std::endl; | 
| Chris@77 | 440 #endif | 
| Chris@45 | 441                 RangeSummarisableTimeValueModel *rm = | 
| Chris@45 | 442                     dynamic_cast<RangeSummarisableTimeValueModel *>(replacementModel); | 
| Chris@77 | 443 #ifdef DEBUG_DOCUMENT | 
| Chris@45 | 444                 if (rm) { | 
| Chris@45 | 445                     std::cerr << "new model has " << rm->getChannelCount() << " channels " << std::endl; | 
| Chris@45 | 446                 } else { | 
| Chris@137 | 447                     std::cerr << "new model " << replacementModel << " is not a RangeSummarisableTimeValueModel!" << std::endl; | 
| Chris@45 | 448                 } | 
| Chris@77 | 449 #endif | 
| Chris@45 | 450 		setModel(layer, replacementModel); | 
| Chris@45 | 451 	    } | 
| Chris@45 | 452 	} | 
| Chris@45 | 453     } | 
| Chris@45 | 454 | 
| Chris@45 | 455     for (size_t k = 0; k < obsoleteLayers.size(); ++k) { | 
| Chris@45 | 456 	deleteLayer(obsoleteLayers[k], true); | 
| Chris@45 | 457     } | 
| Chris@45 | 458 | 
| Chris@48 | 459     for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { | 
| Chris@86 | 460 | 
| Chris@137 | 461         Model *m = i->first; | 
| Chris@137 | 462 | 
| Chris@137 | 463 #ifdef DEBUG_DOCUMENT | 
| Chris@233 | 464         SVDEBUG << "considering alignment for model " << m << " (name \"" | 
| Chris@229 | 465                   << m->objectName() << "\")" << endl; | 
| Chris@137 | 466 #endif | 
| Chris@137 | 467 | 
| Chris@86 | 468         if (m_autoAlignment) { | 
| Chris@86 | 469 | 
| Chris@137 | 470             alignModel(m); | 
| Chris@86 | 471 | 
| Chris@86 | 472         } else if (oldMainModel && | 
| Chris@137 | 473                    (m->getAlignmentReference() == oldMainModel)) { | 
| Chris@86 | 474 | 
| Chris@137 | 475             alignModel(m); | 
| Chris@48 | 476         } | 
| Chris@48 | 477     } | 
| Chris@48 | 478 | 
| Chris@77 | 479     if (oldMainModel) { | 
| Chris@79 | 480         oldMainModel->aboutToDelete(); | 
| Chris@77 | 481         emit modelAboutToBeDeleted(oldMainModel); | 
| Chris@77 | 482     } | 
| Chris@77 | 483 | 
| Chris@86 | 484     if (m_autoAlignment) { | 
| Chris@86 | 485         alignModel(m_mainModel); | 
| Chris@86 | 486     } | 
| Chris@86 | 487 | 
| Chris@45 | 488     emit mainModelChanged(m_mainModel); | 
| Chris@45 | 489 | 
| Chris@45 | 490     delete oldMainModel; | 
| Chris@45 | 491 } | 
| Chris@45 | 492 | 
| Chris@45 | 493 void | 
| Chris@72 | 494 Document::addDerivedModel(const Transform &transform, | 
| Chris@72 | 495                           const ModelTransformer::Input &input, | 
| Chris@72 | 496                           Model *outputModelToAdd) | 
| Chris@45 | 497 { | 
| Chris@45 | 498     if (m_models.find(outputModelToAdd) != m_models.end()) { | 
| Chris@45 | 499 	std::cerr << "WARNING: Document::addDerivedModel: Model already added" | 
| Chris@45 | 500 		  << std::endl; | 
| Chris@45 | 501 	return; | 
| Chris@45 | 502     } | 
| Chris@45 | 503 | 
| Chris@77 | 504 #ifdef DEBUG_DOCUMENT | 
| Chris@248 | 505     if (input.getModel()) { | 
| Chris@248 | 506         std::cerr << "Document::addDerivedModel: source is " << input.getModel() << " \"" << input.getModel()->objectName() << "\"" << std::endl; | 
| Chris@248 | 507     } else { | 
| Chris@248 | 508         std::cerr << "Document::addDerivedModel: source is " << input.getModel() << std::endl; | 
| Chris@248 | 509     } | 
| Chris@77 | 510 #endif | 
| Chris@45 | 511 | 
| Chris@45 | 512     ModelRecord rec; | 
| Chris@72 | 513     rec.source = input.getModel(); | 
| Chris@72 | 514     rec.channel = input.getChannel(); | 
| Chris@45 | 515     rec.transform = transform; | 
| Chris@45 | 516     rec.refcount = 0; | 
| Chris@45 | 517 | 
| Chris@72 | 518     outputModelToAdd->setSourceModel(input.getModel()); | 
| Chris@45 | 519 | 
| Chris@45 | 520     m_models[outputModelToAdd] = rec; | 
| Chris@45 | 521 | 
| Chris@137 | 522 #ifdef DEBUG_DOCUMENT | 
| Chris@233 | 523     SVDEBUG << "Document::addDerivedModel: Added model " << outputModelToAdd << endl; | 
| Chris@137 | 524     std::cerr << "Models now: "; | 
| Chris@137 | 525     for (ModelMap::const_iterator i = m_models.begin(); i != m_models.end(); ++i) { | 
| Chris@137 | 526         std::cerr << i->first << " "; | 
| Chris@137 | 527     } | 
| Chris@137 | 528     std::cerr << std::endl; | 
| Chris@137 | 529 #endif | 
| Chris@137 | 530 | 
| Chris@45 | 531     emit modelAdded(outputModelToAdd); | 
| Chris@45 | 532 } | 
| Chris@45 | 533 | 
| Chris@45 | 534 | 
| Chris@45 | 535 void | 
| Chris@45 | 536 Document::addImportedModel(Model *model) | 
| Chris@45 | 537 { | 
| Chris@45 | 538     if (m_models.find(model) != m_models.end()) { | 
| Chris@45 | 539 	std::cerr << "WARNING: Document::addImportedModel: Model already added" | 
| Chris@45 | 540 		  << std::endl; | 
| Chris@45 | 541 	return; | 
| Chris@45 | 542     } | 
| Chris@45 | 543 | 
| Chris@45 | 544     ModelRecord rec; | 
| Chris@45 | 545     rec.source = 0; | 
| Chris@45 | 546     rec.refcount = 0; | 
| Chris@45 | 547 | 
| Chris@45 | 548     m_models[model] = rec; | 
| Chris@45 | 549 | 
| Chris@137 | 550 #ifdef DEBUG_DOCUMENT | 
| Chris@233 | 551     SVDEBUG << "Document::addImportedModel: Added model " << model << endl; | 
| Chris@137 | 552     std::cerr << "Models now: "; | 
| Chris@137 | 553     for (ModelMap::const_iterator i = m_models.begin(); i != m_models.end(); ++i) { | 
| Chris@137 | 554         std::cerr << i->first << " "; | 
| Chris@137 | 555     } | 
| Chris@137 | 556     std::cerr << std::endl; | 
| Chris@137 | 557 #endif | 
| Chris@137 | 558 | 
| Chris@47 | 559     if (m_autoAlignment) alignModel(model); | 
| Chris@47 | 560 | 
| Chris@45 | 561     emit modelAdded(model); | 
| Chris@45 | 562 } | 
| Chris@45 | 563 | 
| Chris@45 | 564 Model * | 
| Chris@72 | 565 Document::addDerivedModel(const Transform &transform, | 
| Chris@78 | 566                           const ModelTransformer::Input &input, | 
| gyorgyf@270 | 567                           QString &message, | 
| gyorgyf@270 | 568 						  const FeatureExtractionModelTransformer::PreferredOutputModel outputmodel) | 
| Chris@45 | 569 { | 
| Chris@45 | 570     Model *model = 0; | 
| gyorgyf@270 | 571 	// model = (Model) new FlexiNoteModel(); | 
| gyorgyf@270 | 572 	// return model; | 
| Chris@45 | 573 | 
| Chris@45 | 574     for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { | 
| Chris@45 | 575 	if (i->second.transform == transform && | 
| Chris@72 | 576 	    i->second.source == input.getModel() && | 
| Chris@72 | 577             i->second.channel == input.getChannel()) { | 
| gyorgyf@270 | 578 				std::cerr << "derived model taken from map " << std::endl; | 
| Chris@45 | 579 	    return i->first; | 
| Chris@45 | 580 	} | 
| Chris@45 | 581     } | 
| Chris@45 | 582 | 
| gyorgyf@270 | 583 	// GF: TODO: propagate preferredOutputModelSelection (done) | 
| Chris@78 | 584     model = ModelTransformerFactory::getInstance()->transform | 
| gyorgyf@270 | 585         (transform, input, message, outputmodel); //e.g. FeatureExtractionModelTransformer::FlexiNoteOutputModel | 
| Chris@45 | 586 | 
| Chris@83 | 587     // The transform we actually used was presumably identical to the | 
| Chris@83 | 588     // one asked for, except that the version of the plugin may | 
| Chris@83 | 589     // differ.  It's possible that the returned message contains a | 
| Chris@83 | 590     // warning about this; that doesn't concern us here, but we do | 
| Chris@83 | 591     // need to ensure that the transform we remember is correct for | 
| Chris@83 | 592     // what was actually applied, with the current plugin version. | 
| Chris@83 | 593 | 
| Chris@83 | 594     Transform applied = transform; | 
| Chris@83 | 595     applied.setPluginVersion | 
| Chris@83 | 596         (TransformFactory::getInstance()-> | 
| Chris@83 | 597          getDefaultTransformFor(transform.getIdentifier(), | 
| Chris@83 | 598                                 lrintf(transform.getSampleRate())) | 
| Chris@83 | 599          .getPluginVersion()); | 
| Chris@83 | 600 | 
| Chris@45 | 601     if (!model) { | 
| Chris@228 | 602 	std::cerr << "WARNING: Document::addDerivedModel: no output model for transform " << transform.getIdentifier() << std::endl; | 
| Chris@45 | 603     } else { | 
| Chris@83 | 604 	addDerivedModel(applied, input, model); | 
| Chris@45 | 605     } | 
| gyorgyf@272 | 606 	// std::cerr << "derived model name: " << model->getTypeName() << std::endl; | 
| gyorgyf@270 | 607 | 
| Chris@45 | 608     return model; | 
| Chris@45 | 609 } | 
| Chris@45 | 610 | 
| Chris@45 | 611 void | 
| Chris@45 | 612 Document::releaseModel(Model *model) // Will _not_ release main model! | 
| Chris@45 | 613 { | 
| Chris@45 | 614     if (model == 0) { | 
| Chris@45 | 615 	return; | 
| Chris@45 | 616     } | 
| Chris@45 | 617 | 
| Chris@45 | 618     if (model == m_mainModel) { | 
| Chris@45 | 619 	return; | 
| Chris@45 | 620     } | 
| Chris@45 | 621 | 
| Chris@45 | 622     bool toDelete = false; | 
| Chris@45 | 623 | 
| Chris@45 | 624     if (m_models.find(model) != m_models.end()) { | 
| Chris@45 | 625 | 
| Chris@45 | 626 	if (m_models[model].refcount == 0) { | 
| Chris@45 | 627 	    std::cerr << "WARNING: Document::releaseModel: model " << model | 
| Chris@45 | 628 		      << " reference count is zero already!" << std::endl; | 
| Chris@45 | 629 	} else { | 
| Chris@45 | 630 	    if (--m_models[model].refcount == 0) { | 
| Chris@45 | 631 		toDelete = true; | 
| Chris@45 | 632 	    } | 
| Chris@45 | 633 	} | 
| Chris@45 | 634     } else { | 
| Chris@45 | 635 	std::cerr << "WARNING: Document::releaseModel: Unfound model " | 
| Chris@45 | 636 		  << model << std::endl; | 
| Chris@45 | 637 	toDelete = true; | 
| Chris@45 | 638     } | 
| Chris@45 | 639 | 
| Chris@45 | 640     if (toDelete) { | 
| Chris@45 | 641 | 
| Chris@45 | 642 	int sourceCount = 0; | 
| Chris@45 | 643 | 
| Chris@45 | 644 	for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { | 
| Chris@45 | 645 	    if (i->second.source == model) { | 
| Chris@45 | 646 		++sourceCount; | 
| Chris@45 | 647 		i->second.source = 0; | 
| Chris@45 | 648 	    } | 
| Chris@45 | 649 	} | 
| Chris@45 | 650 | 
| Chris@45 | 651 	if (sourceCount > 0) { | 
| Chris@233 | 652 	    SVDEBUG << "Document::releaseModel: Deleting model " | 
| Chris@45 | 653 		      << model << " even though it is source for " | 
| Chris@45 | 654 		      << sourceCount << " other derived model(s) -- resetting " | 
| Chris@229 | 655 		      << "their source fields appropriately" << endl; | 
| Chris@45 | 656 	} | 
| Chris@45 | 657 | 
| Chris@79 | 658         model->aboutToDelete(); | 
| Chris@45 | 659 	emit modelAboutToBeDeleted(model); | 
| Chris@45 | 660 	m_models.erase(model); | 
| Chris@137 | 661 | 
| Chris@137 | 662 #ifdef DEBUG_DOCUMENT | 
| Chris@233 | 663         SVDEBUG << "Document::releaseModel: Deleted model " << model << endl; | 
| Chris@137 | 664         std::cerr << "Models now: "; | 
| Chris@137 | 665         for (ModelMap::const_iterator i = m_models.begin(); i != m_models.end(); ++i) { | 
| Chris@137 | 666             std::cerr << i->first << " "; | 
| Chris@137 | 667         } | 
| Chris@137 | 668         std::cerr << std::endl; | 
| Chris@137 | 669 #endif | 
| Chris@137 | 670 | 
| Chris@45 | 671 	delete model; | 
| Chris@45 | 672     } | 
| Chris@45 | 673 } | 
| Chris@45 | 674 | 
| Chris@45 | 675 void | 
| Chris@45 | 676 Document::deleteLayer(Layer *layer, bool force) | 
| Chris@45 | 677 { | 
| Chris@45 | 678     if (m_layerViewMap.find(layer) != m_layerViewMap.end() && | 
| Chris@45 | 679 	m_layerViewMap[layer].size() > 0) { | 
| Chris@45 | 680 | 
| Chris@45 | 681 	std::cerr << "WARNING: Document::deleteLayer: Layer " | 
| Chris@228 | 682 		  << layer << " [" << layer->objectName() << "]" | 
| Chris@45 | 683 		  << " is still used in " << m_layerViewMap[layer].size() | 
| Chris@45 | 684 		  << " views!" << std::endl; | 
| Chris@45 | 685 | 
| Chris@45 | 686 	if (force) { | 
| Chris@45 | 687 | 
| Chris@77 | 688 #ifdef DEBUG_DOCUMENT | 
| Chris@45 | 689 	    std::cerr << "(force flag set -- deleting from all views)" << std::endl; | 
| Chris@77 | 690 #endif | 
| Chris@45 | 691 | 
| Chris@45 | 692 	    for (std::set<View *>::iterator j = m_layerViewMap[layer].begin(); | 
| Chris@45 | 693 		 j != m_layerViewMap[layer].end(); ++j) { | 
| Chris@45 | 694 		// don't use removeLayerFromView, as it issues a command | 
| Chris@45 | 695 		layer->setLayerDormant(*j, true); | 
| Chris@45 | 696 		(*j)->removeLayer(layer); | 
| Chris@45 | 697 	    } | 
| Chris@45 | 698 | 
| Chris@45 | 699 	    m_layerViewMap.erase(layer); | 
| Chris@45 | 700 | 
| Chris@45 | 701 	} else { | 
| Chris@45 | 702 	    return; | 
| Chris@45 | 703 	} | 
| Chris@45 | 704     } | 
| Chris@45 | 705 | 
| Chris@45 | 706     if (m_layers.find(layer) == m_layers.end()) { | 
| Chris@233 | 707 	SVDEBUG << "Document::deleteLayer: Layer " | 
| Chris@212 | 708                   << layer << " (" << typeid(layer).name() << | 
| Chris@212 | 709                   ") does not exist, or has already been deleted " | 
| Chris@229 | 710 		  << "(this may not be as serious as it sounds)" << endl; | 
| Chris@45 | 711 	return; | 
| Chris@45 | 712     } | 
| Chris@45 | 713 | 
| Chris@45 | 714     m_layers.erase(layer); | 
| Chris@45 | 715 | 
| Chris@132 | 716 #ifdef DEBUG_DOCUMENT | 
| Chris@233 | 717     SVDEBUG << "Document::deleteLayer: Removing, now have " | 
| Chris@229 | 718               << m_layers.size() << " layers" << endl; | 
| Chris@132 | 719 #endif | 
| Chris@52 | 720 | 
| Chris@45 | 721     releaseModel(layer->getModel()); | 
| Chris@45 | 722     emit layerRemoved(layer); | 
| Chris@45 | 723     emit layerAboutToBeDeleted(layer); | 
| Chris@45 | 724     delete layer; | 
| Chris@45 | 725 } | 
| Chris@45 | 726 | 
| Chris@45 | 727 void | 
| Chris@45 | 728 Document::setModel(Layer *layer, Model *model) | 
| Chris@45 | 729 { | 
| Chris@45 | 730     if (model && | 
| Chris@45 | 731 	model != m_mainModel && | 
| Chris@45 | 732 	m_models.find(model) == m_models.end()) { | 
| Chris@45 | 733 	std::cerr << "ERROR: Document::setModel: Layer " << layer | 
| Chris@45 | 734 		  << " (\"" << layer->objectName().toStdString() | 
| Chris@45 | 735                   << "\") wants to use unregistered model " << model | 
| Chris@45 | 736 		  << ": register the layer's model before setting it!" | 
| Chris@45 | 737 		  << std::endl; | 
| Chris@45 | 738 	return; | 
| Chris@45 | 739     } | 
| Chris@45 | 740 | 
| Chris@45 | 741     Model *previousModel = layer->getModel(); | 
| Chris@45 | 742 | 
| Chris@45 | 743     if (previousModel == model) { | 
| Chris@233 | 744         SVDEBUG << "NOTE: Document::setModel: Layer " << layer << " (\"" | 
| Chris@229 | 745                   << layer->objectName()                  << "\") is already set to model " | 
| Chris@45 | 746                   << model << " (\"" | 
| Chris@229 | 747                   << (model ? model->objectName(): "(null)") | 
| Chris@229 | 748                   << "\")" << endl; | 
| Chris@45 | 749         return; | 
| Chris@45 | 750     } | 
| Chris@45 | 751 | 
| Chris@45 | 752     if (model && model != m_mainModel) { | 
| Chris@45 | 753 	m_models[model].refcount ++; | 
| Chris@45 | 754     } | 
| Chris@45 | 755 | 
| Chris@45 | 756     if (model && previousModel) { | 
| Chris@45 | 757         PlayParameterRepository::getInstance()->copyParameters | 
| Chris@45 | 758             (previousModel, model); | 
| Chris@45 | 759     } | 
| Chris@45 | 760 | 
| Chris@45 | 761     LayerFactory::getInstance()->setModel(layer, model); | 
| gyorgyf@272 | 762 	// std::cerr << "layer type: " << LayerFactory::getInstance()->getLayerTypeName(LayerFactory::getInstance()->getLayerType(layer)) << std::endl; | 
| Chris@45 | 763 | 
| Chris@45 | 764     if (previousModel) { | 
| Chris@45 | 765         releaseModel(previousModel); | 
| Chris@45 | 766     } | 
| Chris@45 | 767 } | 
| Chris@45 | 768 | 
| Chris@45 | 769 void | 
| Chris@45 | 770 Document::setChannel(Layer *layer, int channel) | 
| Chris@45 | 771 { | 
| Chris@45 | 772     LayerFactory::getInstance()->setChannel(layer, channel); | 
| Chris@45 | 773 } | 
| Chris@45 | 774 | 
| Chris@45 | 775 void | 
| Chris@45 | 776 Document::addLayerToView(View *view, Layer *layer) | 
| Chris@45 | 777 { | 
| Chris@45 | 778     Model *model = layer->getModel(); | 
| Chris@45 | 779     if (!model) { | 
| Chris@77 | 780 #ifdef DEBUG_DOCUMENT | 
| Chris@233 | 781 	SVDEBUG << "Document::addLayerToView: Layer (\"" | 
| Chris@229 | 782                   << layer->objectName()                  << "\") with no model being added to view: " | 
| Chris@229 | 783                   << "normally you want to set the model first" << endl; | 
| Chris@77 | 784 #endif | 
| Chris@45 | 785     } else { | 
| Chris@45 | 786 	if (model != m_mainModel && | 
| Chris@45 | 787 	    m_models.find(model) == m_models.end()) { | 
| Chris@45 | 788 	    std::cerr << "ERROR: Document::addLayerToView: Layer " << layer | 
| Chris@45 | 789 		      << " has unregistered model " << model | 
| Chris@45 | 790 		      << " -- register the layer's model before adding the layer!" << std::endl; | 
| Chris@45 | 791 	    return; | 
| Chris@45 | 792 	} | 
| Chris@45 | 793     } | 
| Chris@45 | 794 | 
| Chris@45 | 795     CommandHistory::getInstance()->addCommand | 
| Chris@45 | 796 	(new Document::AddLayerCommand(this, view, layer)); | 
| Chris@45 | 797 } | 
| Chris@45 | 798 | 
| Chris@45 | 799 void | 
| Chris@45 | 800 Document::removeLayerFromView(View *view, Layer *layer) | 
| Chris@45 | 801 { | 
| Chris@45 | 802     CommandHistory::getInstance()->addCommand | 
| Chris@45 | 803 	(new Document::RemoveLayerCommand(this, view, layer)); | 
| Chris@45 | 804 } | 
| Chris@45 | 805 | 
| Chris@45 | 806 void | 
| Chris@45 | 807 Document::addToLayerViewMap(Layer *layer, View *view) | 
| Chris@45 | 808 { | 
| Chris@45 | 809     bool firstView = (m_layerViewMap.find(layer) == m_layerViewMap.end() || | 
| Chris@45 | 810                       m_layerViewMap[layer].empty()); | 
| Chris@45 | 811 | 
| Chris@45 | 812     if (m_layerViewMap[layer].find(view) != | 
| Chris@45 | 813 	m_layerViewMap[layer].end()) { | 
| Chris@45 | 814 	std::cerr << "WARNING: Document::addToLayerViewMap:" | 
| Chris@45 | 815 		  << " Layer " << layer << " -> view " << view << " already in" | 
| Chris@45 | 816 		  << " layer view map -- internal inconsistency" << std::endl; | 
| Chris@45 | 817     } | 
| Chris@45 | 818 | 
| Chris@45 | 819     m_layerViewMap[layer].insert(view); | 
| Chris@45 | 820 | 
| Chris@45 | 821     if (firstView) emit layerInAView(layer, true); | 
| Chris@45 | 822 } | 
| Chris@45 | 823 | 
| Chris@45 | 824 void | 
| Chris@45 | 825 Document::removeFromLayerViewMap(Layer *layer, View *view) | 
| Chris@45 | 826 { | 
| Chris@45 | 827     if (m_layerViewMap[layer].find(view) == | 
| Chris@45 | 828 	m_layerViewMap[layer].end()) { | 
| Chris@45 | 829 	std::cerr << "WARNING: Document::removeFromLayerViewMap:" | 
| Chris@45 | 830 		  << " Layer " << layer << " -> view " << view << " not in" | 
| Chris@45 | 831 		  << " layer view map -- internal inconsistency" << std::endl; | 
| Chris@45 | 832     } | 
| Chris@45 | 833 | 
| Chris@45 | 834     m_layerViewMap[layer].erase(view); | 
| Chris@45 | 835 | 
| Chris@45 | 836     if (m_layerViewMap[layer].empty()) { | 
| Chris@45 | 837         m_layerViewMap.erase(layer); | 
| Chris@45 | 838         emit layerInAView(layer, false); | 
| Chris@45 | 839     } | 
| Chris@45 | 840 } | 
| Chris@45 | 841 | 
| Chris@45 | 842 QString | 
| Chris@45 | 843 Document::getUniqueLayerName(QString candidate) | 
| Chris@45 | 844 { | 
| Chris@45 | 845     for (int count = 1; ; ++count) { | 
| Chris@45 | 846 | 
| Chris@45 | 847         QString adjusted = | 
| Chris@45 | 848             (count > 1 ? QString("%1 <%2>").arg(candidate).arg(count) : | 
| Chris@45 | 849              candidate); | 
| Chris@45 | 850 | 
| Chris@45 | 851         bool duplicate = false; | 
| Chris@45 | 852 | 
| Chris@45 | 853         for (LayerSet::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | 
| Chris@45 | 854             if ((*i)->objectName() == adjusted) { | 
| Chris@45 | 855                 duplicate = true; | 
| Chris@45 | 856                 break; | 
| Chris@45 | 857             } | 
| Chris@45 | 858         } | 
| Chris@45 | 859 | 
| Chris@45 | 860         if (!duplicate) return adjusted; | 
| Chris@45 | 861     } | 
| Chris@45 | 862 } | 
| Chris@45 | 863 | 
| Chris@45 | 864 std::vector<Model *> | 
| Chris@72 | 865 Document::getTransformInputModels() | 
| Chris@45 | 866 { | 
| Chris@45 | 867     std::vector<Model *> models; | 
| Chris@45 | 868 | 
| Chris@45 | 869     if (!m_mainModel) return models; | 
| Chris@45 | 870 | 
| Chris@45 | 871     models.push_back(m_mainModel); | 
| Chris@45 | 872 | 
| Chris@45 | 873     //!!! This will pick up all models, including those that aren't visible... | 
| Chris@45 | 874 | 
| Chris@45 | 875     for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { | 
| Chris@45 | 876 | 
| Chris@45 | 877         Model *model = i->first; | 
| Chris@45 | 878         if (!model || model == m_mainModel) continue; | 
| Chris@45 | 879         DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model); | 
| Chris@45 | 880 | 
| Chris@45 | 881         if (dtvm) { | 
| Chris@45 | 882             models.push_back(dtvm); | 
| Chris@45 | 883         } | 
| Chris@45 | 884     } | 
| Chris@45 | 885 | 
| Chris@45 | 886     return models; | 
| Chris@45 | 887 } | 
| Chris@45 | 888 | 
| Chris@50 | 889 bool | 
| Chris@77 | 890 Document::isKnownModel(const Model *model) const | 
| Chris@77 | 891 { | 
| Chris@77 | 892     if (model == m_mainModel) return true; | 
| Chris@77 | 893     return (m_models.find(const_cast<Model *>(model)) != m_models.end()); | 
| Chris@77 | 894 } | 
| Chris@77 | 895 | 
| Chris@90 | 896 TransformId | 
| Chris@90 | 897 Document::getAlignmentTransformName() | 
| Chris@90 | 898 { | 
| Chris@90 | 899     QSettings settings; | 
| Chris@90 | 900     settings.beginGroup("Alignment"); | 
| Chris@90 | 901     TransformId id = | 
| Chris@90 | 902         settings.value("transform-id", | 
| Chris@90 | 903                        "vamp:match-vamp-plugin:match:path").toString(); | 
| Chris@90 | 904     settings.endGroup(); | 
| Chris@90 | 905     return id; | 
| Chris@90 | 906 } | 
| Chris@90 | 907 | 
| Chris@77 | 908 bool | 
| Chris@51 | 909 Document::canAlign() | 
| Chris@50 | 910 { | 
| Chris@90 | 911     TransformId id = getAlignmentTransformName(); | 
| Chris@54 | 912     TransformFactory *factory = TransformFactory::getInstance(); | 
| Chris@54 | 913     return factory->haveTransform(id); | 
| Chris@50 | 914 } | 
| Chris@50 | 915 | 
| Chris@45 | 916 void | 
| Chris@45 | 917 Document::alignModel(Model *model) | 
| Chris@45 | 918 { | 
| Chris@86 | 919     if (!m_mainModel) return; | 
| Chris@45 | 920 | 
| Chris@45 | 921     RangeSummarisableTimeValueModel *rm = | 
| Chris@45 | 922         dynamic_cast<RangeSummarisableTimeValueModel *>(model); | 
| Chris@45 | 923     if (!rm) return; | 
| Chris@48 | 924 | 
| Chris@86 | 925     if (rm->getAlignmentReference() == m_mainModel) { | 
| Chris@233 | 926         SVDEBUG << "Document::alignModel: model " << rm << " is already aligned to main model " << m_mainModel << endl; | 
| Chris@86 | 927         return; | 
| Chris@86 | 928     } | 
| Chris@45 | 929 | 
| Chris@86 | 930     if (model == m_mainModel) { | 
| Chris@86 | 931         // The reference has an empty alignment to itself.  This makes | 
| Chris@86 | 932         // it possible to distinguish between the reference and any | 
| Chris@86 | 933         // unaligned model just by looking at the model itself, | 
| Chris@86 | 934         // without also knowing what the main model is | 
| Chris@233 | 935         SVDEBUG << "Document::alignModel(" << model << "): is main model, setting appropriately" << endl; | 
| Chris@86 | 936         rm->setAlignment(new AlignmentModel(model, model, 0, 0)); | 
| Chris@86 | 937         return; | 
| Chris@86 | 938     } | 
| Chris@86 | 939 | 
| Chris@45 | 940     // This involves creating three new models: | 
| Chris@45 | 941 | 
| Chris@45 | 942     // 1. an AggregateWaveModel to provide the mixdowns of the main | 
| Chris@45 | 943     // model and the new model in its two channels, as input to the | 
| Chris@45 | 944     // MATCH plugin | 
| Chris@45 | 945 | 
| Chris@45 | 946     // 2. a SparseTimeValueModel, which is the model automatically | 
| Chris@53 | 947     // created by FeatureExtractionPluginTransformer when running the | 
| Chris@45 | 948     // MATCH plugin (thus containing the alignment path) | 
| Chris@45 | 949 | 
| Chris@45 | 950     // 3. an AlignmentModel, which stores the path model and carries | 
| Chris@45 | 951     // out alignment lookups on it. | 
| Chris@45 | 952 | 
| Chris@45 | 953     // The first two of these are provided as arguments to the | 
| Chris@45 | 954     // constructor for the third, which takes responsibility for | 
| Chris@45 | 955     // deleting them.  The AlignmentModel, meanwhile, is passed to the | 
| Chris@45 | 956     // new model we are aligning, which also takes responsibility for | 
| Chris@45 | 957     // it.  We should not have to delete any of these new models here. | 
| Chris@45 | 958 | 
| Chris@45 | 959     AggregateWaveModel::ChannelSpecList components; | 
| Chris@45 | 960 | 
| Chris@45 | 961     components.push_back(AggregateWaveModel::ModelChannelSpec | 
| Chris@45 | 962                          (m_mainModel, -1)); | 
| Chris@45 | 963 | 
| Chris@45 | 964     components.push_back(AggregateWaveModel::ModelChannelSpec | 
| Chris@45 | 965                          (rm, -1)); | 
| Chris@45 | 966 | 
| Chris@112 | 967     Model *aggregateModel = new AggregateWaveModel(components); | 
| Chris@112 | 968     ModelTransformer::Input aggregate(aggregateModel); | 
| Chris@45 | 969 | 
| Chris@72 | 970     TransformId id = "vamp:match-vamp-plugin:match:path"; //!!! configure | 
| Chris@45 | 971 | 
| Chris@72 | 972     TransformFactory *tf = TransformFactory::getInstance(); | 
| Chris@45 | 973 | 
| Chris@72 | 974     Transform transform = tf->getDefaultTransformFor | 
| Chris@112 | 975         (id, aggregateModel->getSampleRate()); | 
| Chris@57 | 976 | 
| Chris@72 | 977     transform.setStepSize(transform.getBlockSize()/2); | 
| Chris@72 | 978     transform.setParameter("serialise", 1); | 
| Chris@64 | 979 | 
| Chris@233 | 980     SVDEBUG << "Document::alignModel: Alignment transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl; | 
| Chris@81 | 981 | 
| Chris@72 | 982     ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); | 
| Chris@72 | 983 | 
| Chris@78 | 984     QString message; | 
| Chris@78 | 985     Model *transformOutput = mtf->transform(transform, aggregate, message); | 
| Chris@64 | 986 | 
| Chris@64 | 987     if (!transformOutput) { | 
| Chris@72 | 988         transform.setStepSize(0); | 
| Chris@78 | 989         transformOutput = mtf->transform(transform, aggregate, message); | 
| Chris@64 | 990     } | 
| Chris@45 | 991 | 
| Chris@45 | 992     SparseTimeValueModel *path = dynamic_cast<SparseTimeValueModel *> | 
| Chris@45 | 993         (transformOutput); | 
| Chris@45 | 994 | 
| Chris@45 | 995     if (!path) { | 
| Chris@45 | 996         std::cerr << "Document::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << std::endl; | 
| Chris@78 | 997         emit alignmentFailed(id, message); | 
| Chris@45 | 998         delete transformOutput; | 
| Chris@112 | 999         delete aggregateModel; | 
| Chris@45 | 1000         return; | 
| Chris@45 | 1001     } | 
| Chris@45 | 1002 | 
| Chris@112 | 1003     path->setCompletion(0); | 
| Chris@112 | 1004 | 
| Chris@45 | 1005     AlignmentModel *alignmentModel = new AlignmentModel | 
| Chris@112 | 1006         (m_mainModel, model, aggregateModel, path); | 
| Chris@45 | 1007 | 
| Chris@45 | 1008     rm->setAlignment(alignmentModel); | 
| Chris@45 | 1009 } | 
| Chris@45 | 1010 | 
| Chris@45 | 1011 void | 
| Chris@45 | 1012 Document::alignModels() | 
| Chris@45 | 1013 { | 
| Chris@45 | 1014     for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { | 
| Chris@45 | 1015         alignModel(i->first); | 
| Chris@45 | 1016     } | 
| Chris@86 | 1017     alignModel(m_mainModel); | 
| Chris@45 | 1018 } | 
| Chris@45 | 1019 | 
| Chris@45 | 1020 Document::AddLayerCommand::AddLayerCommand(Document *d, | 
| Chris@45 | 1021 					   View *view, | 
| Chris@45 | 1022 					   Layer *layer) : | 
| Chris@45 | 1023     m_d(d), | 
| Chris@45 | 1024     m_view(view), | 
| Chris@45 | 1025     m_layer(layer), | 
| Chris@45 | 1026     m_name(qApp->translate("AddLayerCommand", "Add %1 Layer").arg(layer->objectName())), | 
| Chris@45 | 1027     m_added(false) | 
| Chris@45 | 1028 { | 
| Chris@45 | 1029 } | 
| Chris@45 | 1030 | 
| Chris@45 | 1031 Document::AddLayerCommand::~AddLayerCommand() | 
| Chris@45 | 1032 { | 
| Chris@77 | 1033 #ifdef DEBUG_DOCUMENT | 
| Chris@233 | 1034     SVDEBUG << "Document::AddLayerCommand::~AddLayerCommand" << endl; | 
| Chris@77 | 1035 #endif | 
| Chris@45 | 1036     if (!m_added) { | 
| Chris@45 | 1037 	m_d->deleteLayer(m_layer); | 
| Chris@45 | 1038     } | 
| Chris@45 | 1039 } | 
| Chris@45 | 1040 | 
| Chris@159 | 1041 QString | 
| Chris@159 | 1042 Document::AddLayerCommand::getName() const | 
| Chris@159 | 1043 { | 
| Chris@165 | 1044 #ifdef DEBUG_DOCUMENT | 
| Chris@233 | 1045     SVDEBUG << "Document::AddLayerCommand::getName(): Name is " | 
| Chris@229 | 1046               << m_name << endl; | 
| Chris@165 | 1047 #endif | 
| Chris@159 | 1048     return m_name; | 
| Chris@159 | 1049 } | 
| Chris@159 | 1050 | 
| Chris@45 | 1051 void | 
| Chris@45 | 1052 Document::AddLayerCommand::execute() | 
| Chris@45 | 1053 { | 
| Chris@45 | 1054     for (int i = 0; i < m_view->getLayerCount(); ++i) { | 
| Chris@45 | 1055 	if (m_view->getLayer(i) == m_layer) { | 
| Chris@45 | 1056 	    // already there | 
| Chris@45 | 1057 	    m_layer->setLayerDormant(m_view, false); | 
| Chris@45 | 1058 	    m_added = true; | 
| Chris@45 | 1059 	    return; | 
| Chris@45 | 1060 	} | 
| Chris@45 | 1061     } | 
| Chris@45 | 1062 | 
| Chris@45 | 1063     m_view->addLayer(m_layer); | 
| Chris@45 | 1064     m_layer->setLayerDormant(m_view, false); | 
| Chris@45 | 1065 | 
| Chris@45 | 1066     m_d->addToLayerViewMap(m_layer, m_view); | 
| Chris@45 | 1067     m_added = true; | 
| Chris@45 | 1068 } | 
| Chris@45 | 1069 | 
| Chris@45 | 1070 void | 
| Chris@45 | 1071 Document::AddLayerCommand::unexecute() | 
| Chris@45 | 1072 { | 
| Chris@45 | 1073     m_view->removeLayer(m_layer); | 
| Chris@45 | 1074     m_layer->setLayerDormant(m_view, true); | 
| Chris@45 | 1075 | 
| Chris@45 | 1076     m_d->removeFromLayerViewMap(m_layer, m_view); | 
| Chris@45 | 1077     m_added = false; | 
| Chris@45 | 1078 } | 
| Chris@45 | 1079 | 
| Chris@45 | 1080 Document::RemoveLayerCommand::RemoveLayerCommand(Document *d, | 
| Chris@45 | 1081 						 View *view, | 
| Chris@45 | 1082 						 Layer *layer) : | 
| Chris@45 | 1083     m_d(d), | 
| Chris@45 | 1084     m_view(view), | 
| Chris@45 | 1085     m_layer(layer), | 
| Chris@45 | 1086     m_name(qApp->translate("RemoveLayerCommand", "Delete %1 Layer").arg(layer->objectName())), | 
| Chris@45 | 1087     m_added(true) | 
| Chris@45 | 1088 { | 
| Chris@45 | 1089 } | 
| Chris@45 | 1090 | 
| Chris@45 | 1091 Document::RemoveLayerCommand::~RemoveLayerCommand() | 
| Chris@45 | 1092 { | 
| Chris@77 | 1093 #ifdef DEBUG_DOCUMENT | 
| Chris@233 | 1094     SVDEBUG << "Document::RemoveLayerCommand::~RemoveLayerCommand" << endl; | 
| Chris@77 | 1095 #endif | 
| Chris@45 | 1096     if (!m_added) { | 
| Chris@45 | 1097 	m_d->deleteLayer(m_layer); | 
| Chris@45 | 1098     } | 
| Chris@45 | 1099 } | 
| Chris@45 | 1100 | 
| Chris@159 | 1101 QString | 
| Chris@159 | 1102 Document::RemoveLayerCommand::getName() const | 
| Chris@159 | 1103 { | 
| Chris@171 | 1104 #ifdef DEBUG_DOCUMENT | 
| Chris@233 | 1105     SVDEBUG << "Document::RemoveLayerCommand::getName(): Name is " | 
| Chris@229 | 1106               << m_name << endl; | 
| Chris@171 | 1107 #endif | 
| Chris@159 | 1108     return m_name; | 
| Chris@159 | 1109 } | 
| Chris@159 | 1110 | 
| Chris@45 | 1111 void | 
| Chris@45 | 1112 Document::RemoveLayerCommand::execute() | 
| Chris@45 | 1113 { | 
| Chris@45 | 1114     bool have = false; | 
| Chris@45 | 1115     for (int i = 0; i < m_view->getLayerCount(); ++i) { | 
| Chris@45 | 1116 	if (m_view->getLayer(i) == m_layer) { | 
| Chris@45 | 1117 	    have = true; | 
| Chris@45 | 1118 	    break; | 
| Chris@45 | 1119 	} | 
| Chris@45 | 1120     } | 
| Chris@45 | 1121 | 
| Chris@45 | 1122     if (!have) { // not there! | 
| Chris@45 | 1123 	m_layer->setLayerDormant(m_view, true); | 
| Chris@45 | 1124 	m_added = false; | 
| Chris@45 | 1125 	return; | 
| Chris@45 | 1126     } | 
| Chris@45 | 1127 | 
| Chris@45 | 1128     m_view->removeLayer(m_layer); | 
| Chris@45 | 1129     m_layer->setLayerDormant(m_view, true); | 
| Chris@45 | 1130 | 
| Chris@45 | 1131     m_d->removeFromLayerViewMap(m_layer, m_view); | 
| Chris@45 | 1132     m_added = false; | 
| Chris@45 | 1133 } | 
| Chris@45 | 1134 | 
| Chris@45 | 1135 void | 
| Chris@45 | 1136 Document::RemoveLayerCommand::unexecute() | 
| Chris@45 | 1137 { | 
| Chris@45 | 1138     m_view->addLayer(m_layer); | 
| Chris@45 | 1139     m_layer->setLayerDormant(m_view, false); | 
| Chris@45 | 1140 | 
| Chris@45 | 1141     m_d->addToLayerViewMap(m_layer, m_view); | 
| Chris@45 | 1142     m_added = true; | 
| Chris@45 | 1143 } | 
| Chris@45 | 1144 | 
| Chris@45 | 1145 void | 
| Chris@45 | 1146 Document::toXml(QTextStream &out, QString indent, QString extraAttributes) const | 
| Chris@45 | 1147 { | 
| Chris@226 | 1148     toXml(out, indent, extraAttributes, false); | 
| Chris@226 | 1149 } | 
| Chris@226 | 1150 | 
| Chris@226 | 1151 void | 
| Chris@226 | 1152 Document::toXmlAsTemplate(QTextStream &out, QString indent, QString extraAttributes) const | 
| Chris@226 | 1153 { | 
| Chris@226 | 1154     toXml(out, indent, extraAttributes, true); | 
| Chris@226 | 1155 } | 
| Chris@226 | 1156 | 
| Chris@226 | 1157 void | 
| Chris@226 | 1158 Document::toXml(QTextStream &out, QString indent, QString extraAttributes, | 
| Chris@226 | 1159                 bool asTemplate) const | 
| Chris@226 | 1160 { | 
| Chris@45 | 1161     out << indent + QString("<data%1%2>\n") | 
| Chris@45 | 1162         .arg(extraAttributes == "" ? "" : " ").arg(extraAttributes); | 
| Chris@45 | 1163 | 
| Chris@45 | 1164     if (m_mainModel) { | 
| Chris@108 | 1165 | 
| Chris@226 | 1166         if (asTemplate) { | 
| Chris@226 | 1167             writePlaceholderMainModel(out, indent + "  "); | 
| Chris@226 | 1168         } else { | 
| Chris@226 | 1169             m_mainModel->toXml(out, indent + "  ", "mainModel=\"true\""); | 
| Chris@226 | 1170         } | 
| Chris@108 | 1171 | 
| Chris@108 | 1172         PlayParameters *playParameters = | 
| Chris@108 | 1173             PlayParameterRepository::getInstance()->getPlayParameters(m_mainModel); | 
| Chris@108 | 1174         if (playParameters) { | 
| Chris@108 | 1175             playParameters->toXml | 
| Chris@108 | 1176                 (out, indent + "  ", | 
| Chris@108 | 1177                  QString("model=\"%1\"") | 
| Chris@108 | 1178                  .arg(XmlExportable::getObjectExportId(m_mainModel))); | 
| Chris@108 | 1179         } | 
| Chris@45 | 1180     } | 
| Chris@45 | 1181 | 
| Chris@45 | 1182     // Models that are not used in a layer that is in a view should | 
| Chris@45 | 1183     // not be written.  Get our list of required models first. | 
| Chris@45 | 1184 | 
| Chris@45 | 1185     std::set<const Model *> used; | 
| Chris@45 | 1186 | 
| Chris@45 | 1187     for (LayerViewMap::const_iterator i = m_layerViewMap.begin(); | 
| Chris@45 | 1188          i != m_layerViewMap.end(); ++i) { | 
| Chris@45 | 1189 | 
| Chris@45 | 1190         if (i->first && !i->second.empty() && i->first->getModel()) { | 
| Chris@45 | 1191             used.insert(i->first->getModel()); | 
| Chris@45 | 1192         } | 
| Chris@45 | 1193     } | 
| Chris@45 | 1194 | 
| Chris@111 | 1195     std::set<Model *> written; | 
| Chris@111 | 1196 | 
| Chris@45 | 1197     for (ModelMap::const_iterator i = m_models.begin(); | 
| Chris@45 | 1198 	 i != m_models.end(); ++i) { | 
| Chris@45 | 1199 | 
| Chris@111 | 1200         Model *model = i->first; | 
| Chris@45 | 1201 	const ModelRecord &rec = i->second; | 
| Chris@45 | 1202 | 
| Chris@45 | 1203         if (used.find(model) == used.end()) continue; | 
| Chris@45 | 1204 | 
| Chris@45 | 1205         // We need an intelligent way to determine which models need | 
| Chris@45 | 1206         // to be streamed (i.e. have been edited, or are small) and | 
| Chris@45 | 1207         // which should not be (i.e. remain as generated by a | 
| Chris@45 | 1208         // transform, and are large). | 
| Chris@45 | 1209         // | 
| Chris@45 | 1210         // At the moment we can get away with deciding not to stream | 
| Chris@45 | 1211         // dense 3d models or writable wave file models, provided they | 
| Chris@45 | 1212         // were generated from a transform, because at the moment there | 
| Chris@45 | 1213         // is no way to edit those model types so it should be safe to | 
| Chris@45 | 1214         // regenerate them.  That won't always work in future though. | 
| Chris@45 | 1215         // It would be particularly nice to be able to ask the user, | 
| Chris@45 | 1216         // as well as making an intelligent guess. | 
| Chris@45 | 1217 | 
| Chris@45 | 1218         bool writeModel = true; | 
| Chris@45 | 1219         bool haveDerivation = false; | 
| Chris@45 | 1220 | 
| Chris@72 | 1221         if (rec.source && rec.transform.getIdentifier() != "") { | 
| Chris@45 | 1222             haveDerivation = true; | 
| Chris@45 | 1223         } | 
| Chris@45 | 1224 | 
| Chris@45 | 1225         if (haveDerivation) { | 
| Chris@45 | 1226             if (dynamic_cast<const WritableWaveFileModel *>(model)) { | 
| Chris@45 | 1227                 writeModel = false; | 
| Chris@45 | 1228             } else if (dynamic_cast<const DenseThreeDimensionalModel *>(model)) { | 
| Chris@45 | 1229                 writeModel = false; | 
| Chris@45 | 1230             } | 
| Chris@45 | 1231         } | 
| Chris@45 | 1232 | 
| Chris@45 | 1233         if (writeModel) { | 
| Chris@111 | 1234             model->toXml(out, indent + "  "); | 
| Chris@111 | 1235             written.insert(model); | 
| Chris@45 | 1236         } | 
| Chris@45 | 1237 | 
| Chris@45 | 1238 	if (haveDerivation) { | 
| Chris@72 | 1239             writeBackwardCompatibleDerivation(out, indent + "  ", | 
| Chris@111 | 1240                                               model, rec); | 
| Chris@45 | 1241 	} | 
| Chris@45 | 1242 | 
| Chris@45 | 1243         //!!! We should probably own the PlayParameterRepository | 
| Chris@45 | 1244         PlayParameters *playParameters = | 
| Chris@111 | 1245             PlayParameterRepository::getInstance()->getPlayParameters(model); | 
| Chris@45 | 1246         if (playParameters) { | 
| Chris@45 | 1247             playParameters->toXml | 
| Chris@45 | 1248                 (out, indent + "  ", | 
| Chris@45 | 1249                  QString("model=\"%1\"") | 
| Chris@111 | 1250                  .arg(XmlExportable::getObjectExportId(model))); | 
| Chris@45 | 1251         } | 
| Chris@45 | 1252     } | 
| Chris@45 | 1253 | 
| Chris@111 | 1254     //!!! | 
| Chris@111 | 1255 | 
| Chris@111 | 1256     // We should write out the alignment models here.  AlignmentModel | 
| Chris@111 | 1257     // needs a toXml that writes out the export IDs of its reference | 
| Chris@111 | 1258     // and aligned models, and then streams its path model.  Note that | 
| Chris@111 | 1259     // this will only work when the alignment is complete, so we | 
| Chris@111 | 1260     // should probably wait for it if it isn't already by this point. | 
| Chris@111 | 1261 | 
| Chris@111 | 1262     for (std::set<Model *>::const_iterator i = written.begin(); | 
| Chris@111 | 1263 	 i != written.end(); ++i) { | 
| Chris@111 | 1264 | 
| Chris@111 | 1265         const Model *model = *i; | 
| Chris@111 | 1266         const AlignmentModel *alignment = model->getAlignment(); | 
| Chris@111 | 1267         if (!alignment) continue; | 
| Chris@111 | 1268 | 
| Chris@111 | 1269         alignment->toXml(out, indent + "  "); | 
| Chris@111 | 1270     } | 
| Chris@111 | 1271 | 
| Chris@45 | 1272     for (LayerSet::const_iterator i = m_layers.begin(); | 
| Chris@45 | 1273 	 i != m_layers.end(); ++i) { | 
| Chris@45 | 1274 | 
| Chris@45 | 1275 	(*i)->toXml(out, indent + "  "); | 
| Chris@45 | 1276     } | 
| Chris@45 | 1277 | 
| Chris@45 | 1278     out << indent + "</data>\n"; | 
| Chris@45 | 1279 } | 
| Chris@45 | 1280 | 
| Chris@72 | 1281 void | 
| Chris@226 | 1282 Document::writePlaceholderMainModel(QTextStream &out, QString indent) const | 
| Chris@226 | 1283 { | 
| Chris@226 | 1284     out << indent; | 
| Chris@226 | 1285     out << QString("<model id=\"%1\" name=\"placeholder\" sampleRate=\"%2\" type=\"wavefile\" file=\":samples/silent.wav\" mainModel=\"true\"/>\n") | 
| Chris@226 | 1286         .arg(getObjectExportId(m_mainModel)) | 
| Chris@226 | 1287         .arg(m_mainModel->getSampleRate()); | 
| Chris@226 | 1288 } | 
| Chris@226 | 1289 | 
| Chris@226 | 1290 void | 
| Chris@72 | 1291 Document::writeBackwardCompatibleDerivation(QTextStream &out, QString indent, | 
| Chris@72 | 1292                                             Model *targetModel, | 
| Chris@72 | 1293                                             const ModelRecord &rec) const | 
| Chris@72 | 1294 { | 
| Chris@72 | 1295     // There is a lot of redundancy in the XML we output here, because | 
| Chris@72 | 1296     // we want it to work with older SV session file reading code as | 
| Chris@72 | 1297     // well. | 
| Chris@72 | 1298     // | 
| Chris@72 | 1299     // Formerly, a transform was described using a derivation element | 
| Chris@72 | 1300     // which set out the source and target models, execution context | 
| Chris@72 | 1301     // (step size, input channel etc) and transform id, containing a | 
| Chris@72 | 1302     // plugin element which set out the transform parameters and so | 
| Chris@72 | 1303     // on.  (The plugin element came from a "configurationXml" string | 
| Chris@72 | 1304     // obtained from PluginXml.) | 
| Chris@72 | 1305     // | 
| Chris@72 | 1306     // This has been replaced by a derivation element setting out the | 
| Chris@72 | 1307     // source and target models and input channel, containing a | 
| Chris@72 | 1308     // transform element which sets out everything in the Transform. | 
| Chris@72 | 1309     // | 
| Chris@72 | 1310     // In order to retain compatibility with older SV code, however, | 
| Chris@72 | 1311     // we have to write out the same stuff into the derivation as | 
| Chris@72 | 1312     // before, and manufacture an appropriate plugin element as well | 
| Chris@72 | 1313     // as the transform element.  In order that newer code knows it's | 
| Chris@72 | 1314     // dealing with a newer format, we will also write an attribute | 
| Chris@72 | 1315     // 'type="transform"' in the derivation element. | 
| Chris@45 | 1316 | 
| Chris@72 | 1317     const Transform &transform = rec.transform; | 
| Chris@72 | 1318 | 
| Chris@72 | 1319     // Just for reference, this is what we would write if we didn't | 
| Chris@72 | 1320     // have to be backward compatible: | 
| Chris@72 | 1321     // | 
| Chris@72 | 1322     //    out << indent | 
| Chris@72 | 1323     //        << QString("<derivation type=\"transform\" source=\"%1\" " | 
| Chris@72 | 1324     //                   "model=\"%2\" channel=\"%3\">\n") | 
| Chris@72 | 1325     //        .arg(XmlExportable::getObjectExportId(rec.source)) | 
| Chris@72 | 1326     //        .arg(XmlExportable::getObjectExportId(targetModel)) | 
| Chris@72 | 1327     //        .arg(rec.channel); | 
| Chris@72 | 1328     // | 
| Chris@72 | 1329     //    transform.toXml(out, indent + "  "); | 
| Chris@72 | 1330     // | 
| Chris@72 | 1331     //    out << indent << "</derivation>\n"; | 
| Chris@72 | 1332     // | 
| Chris@72 | 1333     // Unfortunately, we can't just do that.  So we do this... | 
| Chris@72 | 1334 | 
| Chris@72 | 1335     QString extentsAttributes; | 
| Chris@72 | 1336     if (transform.getStartTime() != RealTime::zeroTime || | 
| Chris@72 | 1337         transform.getDuration() != RealTime::zeroTime) { | 
| Chris@72 | 1338         extentsAttributes = QString("startFrame=\"%1\" duration=\"%2\" ") | 
| Chris@72 | 1339             .arg(RealTime::realTime2Frame(transform.getStartTime(), | 
| Chris@72 | 1340                                           targetModel->getSampleRate())) | 
| Chris@72 | 1341             .arg(RealTime::realTime2Frame(transform.getDuration(), | 
| Chris@72 | 1342                                           targetModel->getSampleRate())); | 
| Chris@72 | 1343     } | 
| Chris@72 | 1344 | 
| Chris@72 | 1345     out << indent; | 
| Chris@72 | 1346     out << QString("<derivation type=\"transform\" source=\"%1\" " | 
| Chris@72 | 1347                    "model=\"%2\" channel=\"%3\" domain=\"%4\" " | 
| Chris@72 | 1348                    "stepSize=\"%5\" blockSize=\"%6\" %7windowType=\"%8\" " | 
| Chris@72 | 1349                    "transform=\"%9\">\n") | 
| Chris@72 | 1350         .arg(XmlExportable::getObjectExportId(rec.source)) | 
| Chris@72 | 1351         .arg(XmlExportable::getObjectExportId(targetModel)) | 
| Chris@72 | 1352         .arg(rec.channel) | 
| Chris@72 | 1353         .arg(TransformFactory::getInstance()->getTransformInputDomain | 
| Chris@72 | 1354              (transform.getIdentifier())) | 
| Chris@72 | 1355         .arg(transform.getStepSize()) | 
| Chris@72 | 1356         .arg(transform.getBlockSize()) | 
| Chris@72 | 1357         .arg(extentsAttributes) | 
| Chris@72 | 1358         .arg(int(transform.getWindowType())) | 
| Chris@72 | 1359         .arg(XmlExportable::encodeEntities(transform.getIdentifier())); | 
| Chris@72 | 1360 | 
| Chris@72 | 1361     transform.toXml(out, indent + "  "); | 
| Chris@72 | 1362 | 
| Chris@72 | 1363     out << indent << "  " | 
| Chris@72 | 1364         << TransformFactory::getInstance()->getPluginConfigurationXml(transform); | 
| Chris@72 | 1365 | 
| Chris@72 | 1366     out << indent << "</derivation>\n"; | 
| Chris@72 | 1367 } | 
| Chris@72 | 1368 |