annotate framework/Document.cpp @ 77:0535c49069ba

* Various fixes to object lifetime management, particularly in the spectrum layer and for notification of main model deletion. The main purpose of this is to improve the behaviour of the spectrum, but I think it may also help with #1840922 Various crashes in Layer Summary window.
author Chris Cannam
date Wed, 23 Jan 2008 15:43:27 +0000
parents 4aa40182321f
children 58bfaaed07ed
rev   line source
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"
Chris@45 22 #include "layer/Layer.h"
Chris@45 23 #include "base/CommandHistory.h"
Chris@45 24 #include "base/Command.h"
Chris@45 25 #include "view/View.h"
Chris@45 26 #include "base/PlayParameterRepository.h"
Chris@45 27 #include "base/PlayParameters.h"
Chris@54 28 #include "plugin/transform/TransformFactory.h"
Chris@54 29 #include "plugin/transform/ModelTransformerFactory.h"
Chris@45 30 #include <QApplication>
Chris@45 31 #include <QTextStream>
Chris@45 32 #include <iostream>
Chris@45 33
Chris@45 34 // For alignment:
Chris@45 35 #include "data/model/AggregateWaveModel.h"
Chris@45 36 #include "data/model/SparseTimeValueModel.h"
Chris@45 37 #include "data/model/AlignmentModel.h"
Chris@45 38
Chris@77 39 #define DEBUG_DOCUMENT 1
Chris@77 40
Chris@45 41 //!!! still need to handle command history, documentRestored/documentModified
Chris@45 42
Chris@45 43 Document::Document() :
Chris@47 44 m_mainModel(0),
Chris@47 45 m_autoAlignment(false)
Chris@45 46 {
Chris@45 47 connect(this, SIGNAL(modelAboutToBeDeleted(Model *)),
Chris@54 48 ModelTransformerFactory::getInstance(),
Chris@45 49 SLOT(modelAboutToBeDeleted(Model *)));
Chris@45 50 }
Chris@45 51
Chris@45 52 Document::~Document()
Chris@45 53 {
Chris@45 54 //!!! Document should really own the command history. atm we
Chris@45 55 //still refer to it in various places that don't have access to
Chris@45 56 //the document, be nice to fix that
Chris@45 57
Chris@77 58 #ifdef DEBUG_DOCUMENT
Chris@77 59 std::cerr << "\n\nDocument::~Document: about to clear command history" << std::endl;
Chris@77 60 #endif
Chris@45 61 CommandHistory::getInstance()->clear();
Chris@45 62
Chris@77 63 #ifdef DEBUG_DOCUMENT
Chris@52 64 std::cerr << "Document::~Document: about to delete layers" << std::endl;
Chris@77 65 #endif
Chris@45 66 while (!m_layers.empty()) {
Chris@45 67 deleteLayer(*m_layers.begin(), true);
Chris@45 68 }
Chris@45 69
Chris@45 70 if (!m_models.empty()) {
Chris@45 71 std::cerr << "Document::~Document: WARNING: "
Chris@45 72 << m_models.size() << " model(s) still remain -- "
Chris@45 73 << "should have been garbage collected when deleting layers"
Chris@45 74 << std::endl;
Chris@45 75 while (!m_models.empty()) {
Chris@45 76 Model *model = m_models.begin()->first;
Chris@45 77 if (model == m_mainModel) {
Chris@45 78 // just in case!
Chris@45 79 std::cerr << "Document::~Document: WARNING: Main model is also"
Chris@45 80 << " in models list!" << std::endl;
Chris@45 81 } else if (model) {
Chris@45 82 emit modelAboutToBeDeleted(model);
Chris@45 83 model->aboutToDelete();
Chris@45 84 delete model;
Chris@45 85 }
Chris@45 86 m_models.erase(m_models.begin());
Chris@45 87 }
Chris@45 88 }
Chris@45 89
Chris@77 90 #ifdef DEBUG_DOCUMENT
Chris@77 91 std::cerr << "Document::~Document: About to get rid of main model"
Chris@77 92 << std::endl;
Chris@77 93 #endif
Chris@45 94 if (m_mainModel) {
Chris@45 95 emit modelAboutToBeDeleted(m_mainModel);
Chris@45 96 m_mainModel->aboutToDelete();
Chris@45 97 }
Chris@45 98
Chris@45 99 emit mainModelChanged(0);
Chris@45 100 delete m_mainModel;
Chris@45 101
Chris@45 102 }
Chris@45 103
Chris@45 104 Layer *
Chris@45 105 Document::createLayer(LayerFactory::LayerType type)
Chris@45 106 {
Chris@45 107 Layer *newLayer = LayerFactory::getInstance()->createLayer(type);
Chris@45 108 if (!newLayer) return 0;
Chris@45 109
Chris@45 110 newLayer->setObjectName(getUniqueLayerName(newLayer->objectName()));
Chris@45 111
Chris@45 112 m_layers.insert(newLayer);
Chris@52 113
Chris@77 114 #ifdef DEBUG_DOCUMENT
Chris@77 115 std::cerr << "Document::createLayer: Added layer of type " << type
Chris@77 116 << ", now have " << m_layers.size() << " layers" << std::endl;
Chris@77 117 #endif
Chris@52 118
Chris@45 119 emit layerAdded(newLayer);
Chris@45 120
Chris@45 121 return newLayer;
Chris@45 122 }
Chris@45 123
Chris@45 124 Layer *
Chris@45 125 Document::createMainModelLayer(LayerFactory::LayerType type)
Chris@45 126 {
Chris@45 127 Layer *newLayer = createLayer(type);
Chris@45 128 if (!newLayer) return 0;
Chris@45 129 setModel(newLayer, m_mainModel);
Chris@45 130 return newLayer;
Chris@45 131 }
Chris@45 132
Chris@45 133 Layer *
Chris@45 134 Document::createImportedLayer(Model *model)
Chris@45 135 {
Chris@45 136 LayerFactory::LayerTypeSet types =
Chris@45 137 LayerFactory::getInstance()->getValidLayerTypes(model);
Chris@45 138
Chris@45 139 if (types.empty()) {
Chris@45 140 std::cerr << "WARNING: Document::importLayer: no valid display layer for model" << std::endl;
Chris@45 141 return 0;
Chris@45 142 }
Chris@45 143
Chris@45 144 //!!! for now, just use the first suitable layer type
Chris@45 145 LayerFactory::LayerType type = *types.begin();
Chris@45 146
Chris@45 147 Layer *newLayer = LayerFactory::getInstance()->createLayer(type);
Chris@45 148 if (!newLayer) return 0;
Chris@45 149
Chris@45 150 newLayer->setObjectName(getUniqueLayerName(newLayer->objectName()));
Chris@45 151
Chris@45 152 addImportedModel(model);
Chris@45 153 setModel(newLayer, model);
Chris@45 154
Chris@45 155 //!!! and all channels
Chris@45 156 setChannel(newLayer, -1);
Chris@45 157
Chris@45 158 m_layers.insert(newLayer);
Chris@52 159
Chris@77 160 #ifdef DEBUG_DOCUMENT
Chris@52 161 std::cerr << "Document::createImportedLayer: Added layer of type " << type
Chris@52 162 << ", now have " << m_layers.size() << " layers" << std::endl;
Chris@77 163 #endif
Chris@52 164
Chris@45 165 emit layerAdded(newLayer);
Chris@45 166 return newLayer;
Chris@45 167 }
Chris@45 168
Chris@45 169 Layer *
Chris@45 170 Document::createEmptyLayer(LayerFactory::LayerType type)
Chris@45 171 {
Chris@61 172 if (!m_mainModel) return 0;
Chris@61 173
Chris@45 174 Model *newModel =
Chris@45 175 LayerFactory::getInstance()->createEmptyModel(type, m_mainModel);
Chris@45 176 if (!newModel) return 0;
Chris@45 177
Chris@45 178 Layer *newLayer = createLayer(type);
Chris@45 179 if (!newLayer) {
Chris@45 180 delete newModel;
Chris@45 181 return 0;
Chris@45 182 }
Chris@45 183
Chris@45 184 addImportedModel(newModel);
Chris@45 185 setModel(newLayer, newModel);
Chris@45 186
Chris@45 187 return newLayer;
Chris@45 188 }
Chris@45 189
Chris@45 190 Layer *
Chris@45 191 Document::createDerivedLayer(LayerFactory::LayerType type,
Chris@54 192 TransformId transform)
Chris@45 193 {
Chris@45 194 Layer *newLayer = createLayer(type);
Chris@45 195 if (!newLayer) return 0;
Chris@45 196
Chris@45 197 newLayer->setObjectName(getUniqueLayerName
Chris@54 198 (TransformFactory::getInstance()->
Chris@54 199 getTransformFriendlyName(transform)));
Chris@45 200
Chris@45 201 return newLayer;
Chris@45 202 }
Chris@45 203
Chris@45 204 Layer *
Chris@72 205 Document::createDerivedLayer(const Transform &transform,
Chris@72 206 const ModelTransformer::Input &input)
Chris@45 207 {
Chris@72 208 Model *newModel = addDerivedModel(transform, input);
Chris@45 209 if (!newModel) {
Chris@45 210 // error already printed to stderr by addDerivedModel
Chris@72 211 emit modelGenerationFailed(transform.getIdentifier());
Chris@45 212 return 0;
Chris@45 213 }
Chris@45 214
Chris@45 215 LayerFactory::LayerTypeSet types =
Chris@45 216 LayerFactory::getInstance()->getValidLayerTypes(newModel);
Chris@45 217
Chris@45 218 if (types.empty()) {
Chris@72 219 std::cerr << "WARNING: Document::createLayerForTransformer: no valid display layer for output of transform " << transform.getIdentifier().toStdString() << std::endl;
Chris@45 220 delete newModel;
Chris@45 221 return 0;
Chris@45 222 }
Chris@45 223
Chris@45 224 //!!! for now, just use the first suitable layer type
Chris@45 225
Chris@45 226 Layer *newLayer = createLayer(*types.begin());
Chris@45 227 setModel(newLayer, newModel);
Chris@45 228
Chris@45 229 //!!! We need to clone the model when adding the layer, so that it
Chris@45 230 //can be edited without affecting other layers that are based on
Chris@45 231 //the same model. Unfortunately we can't just clone it now,
Chris@45 232 //because it probably hasn't been completed yet -- the transform
Chris@45 233 //runs in the background. Maybe the transform has to handle
Chris@45 234 //cloning and cacheing models itself.
Chris@45 235 //
Chris@45 236 // Once we do clone models here, of course, we'll have to avoid
Chris@45 237 // leaking them too.
Chris@45 238 //
Chris@45 239 // We want the user to be able to add a model to a second layer
Chris@45 240 // _while it's still being calculated in the first_ and have it
Chris@45 241 // work quickly. That means we need to put the same physical
Chris@45 242 // model pointer in both layers, so they can't actually be cloned.
Chris@45 243
Chris@45 244 if (newLayer) {
Chris@45 245 newLayer->setObjectName(getUniqueLayerName
Chris@54 246 (TransformFactory::getInstance()->
Chris@72 247 getTransformFriendlyName
Chris@72 248 (transform.getIdentifier())));
Chris@45 249 }
Chris@45 250
Chris@45 251 emit layerAdded(newLayer);
Chris@45 252 return newLayer;
Chris@45 253 }
Chris@45 254
Chris@45 255 void
Chris@45 256 Document::setMainModel(WaveFileModel *model)
Chris@45 257 {
Chris@45 258 Model *oldMainModel = m_mainModel;
Chris@45 259 m_mainModel = model;
Chris@45 260
Chris@45 261 emit modelAdded(m_mainModel);
Chris@45 262
Chris@45 263 std::vector<Layer *> obsoleteLayers;
Chris@53 264 std::set<QString> failedTransformers;
Chris@45 265
Chris@45 266 // We need to ensure that no layer is left using oldMainModel or
Chris@45 267 // any of the old derived models as its model. Either replace the
Chris@45 268 // model, or delete the layer for each layer that is currently
Chris@45 269 // using one of these. Carry out this replacement before we
Chris@45 270 // delete any of the models.
Chris@45 271
Chris@77 272 #ifdef DEBUG_DOCUMENT
Chris@77 273 std::cerr << "Document::setMainModel: Have "
Chris@77 274 << m_layers.size() << " layers" << std::endl;
Chris@77 275 #endif
Chris@52 276
Chris@45 277 for (LayerSet::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@45 278
Chris@45 279 Layer *layer = *i;
Chris@45 280 Model *model = layer->getModel();
Chris@45 281
Chris@77 282 #ifdef DEBUG_DOCUMENT
Chris@77 283 std::cerr << "Document::setMainModel: inspecting model "
Chris@77 284 << (model ? model->objectName().toStdString() : "(null)") << " in layer "
Chris@77 285 << layer->objectName().toStdString() << std::endl;
Chris@77 286 #endif
Chris@45 287
Chris@70 288 if (model == oldMainModel) {
Chris@77 289 #ifdef DEBUG_DOCUMENT
Chris@77 290 std::cerr << "... it uses the old main model, replacing" << std::endl;
Chris@77 291 #endif
Chris@45 292 LayerFactory::getInstance()->setModel(layer, m_mainModel);
Chris@45 293 continue;
Chris@45 294 }
Chris@45 295
Chris@70 296 if (model && (m_models.find(model) == m_models.end())) {
Chris@45 297 std::cerr << "WARNING: Document::setMainModel: Unknown model "
Chris@45 298 << model << " in layer " << layer << std::endl;
Chris@45 299 // get rid of this hideous degenerate
Chris@45 300 obsoleteLayers.push_back(layer);
Chris@45 301 continue;
Chris@45 302 }
Chris@45 303
Chris@70 304 if (m_models[model].source &&
Chris@70 305 (m_models[model].source == oldMainModel)) {
Chris@45 306
Chris@77 307 #ifdef DEBUG_DOCUMENT
Chris@77 308 std::cerr << "... it uses a model derived from the old main model, regenerating" << std::endl;
Chris@77 309 #endif
Chris@45 310
Chris@45 311 // This model was derived from the previous main
Chris@45 312 // model: regenerate it.
Chris@45 313
Chris@72 314 const Transform &transform = m_models[model].transform;
Chris@72 315 QString transformId = transform.getIdentifier();
Chris@45 316
Chris@72 317 //!!! We have a problem here if the number of channels in
Chris@72 318 //the main model has changed.
Chris@72 319
Chris@45 320 Model *replacementModel =
Chris@45 321 addDerivedModel(transform,
Chris@72 322 ModelTransformer::Input
Chris@72 323 (m_mainModel,
Chris@72 324 m_models[model].channel));
Chris@45 325
Chris@45 326 if (!replacementModel) {
Chris@45 327 std::cerr << "WARNING: Document::setMainModel: Failed to regenerate model for transform \""
Chris@72 328 << transformId.toStdString() << "\"" << " in layer " << layer << std::endl;
Chris@72 329 if (failedTransformers.find(transformId)
Chris@72 330 == failedTransformers.end()) {
Chris@45 331 emit modelRegenerationFailed(layer->objectName(),
Chris@72 332 transformId);
Chris@72 333 failedTransformers.insert(transformId);
Chris@45 334 }
Chris@45 335 obsoleteLayers.push_back(layer);
Chris@45 336 } else {
Chris@77 337 #ifdef DEBUG_DOCUMENT
Chris@77 338 std::cerr << "Replacing model " << model << " (type "
Chris@77 339 << typeid(*model).name() << ") with model "
Chris@77 340 << replacementModel << " (type "
Chris@77 341 << typeid(*replacementModel).name() << ") in layer "
Chris@77 342 << layer << " (name " << layer->objectName().toStdString() << ")"
Chris@77 343 << std::endl;
Chris@77 344 #endif
Chris@45 345 RangeSummarisableTimeValueModel *rm =
Chris@45 346 dynamic_cast<RangeSummarisableTimeValueModel *>(replacementModel);
Chris@77 347 #ifdef DEBUG_DOCUMENT
Chris@45 348 if (rm) {
Chris@45 349 std::cerr << "new model has " << rm->getChannelCount() << " channels " << std::endl;
Chris@45 350 } else {
Chris@45 351 std::cerr << "new model is not a RangeSummarisableTimeValueModel!" << std::endl;
Chris@45 352 }
Chris@77 353 #endif
Chris@45 354 setModel(layer, replacementModel);
Chris@45 355 }
Chris@45 356 }
Chris@45 357 }
Chris@45 358
Chris@45 359 for (size_t k = 0; k < obsoleteLayers.size(); ++k) {
Chris@45 360 deleteLayer(obsoleteLayers[k], true);
Chris@45 361 }
Chris@45 362
Chris@48 363 for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) {
Chris@68 364 if (oldMainModel &&
Chris@68 365 (i->first->getAlignmentReference() == oldMainModel)) {
Chris@48 366 alignModel(i->first);
Chris@48 367 }
Chris@48 368 }
Chris@48 369
Chris@77 370 if (oldMainModel) {
Chris@77 371 emit modelAboutToBeDeleted(oldMainModel);
Chris@77 372 oldMainModel->aboutToDelete();
Chris@77 373 }
Chris@77 374
Chris@45 375 emit mainModelChanged(m_mainModel);
Chris@45 376
Chris@45 377 delete oldMainModel;
Chris@45 378 }
Chris@45 379
Chris@45 380 void
Chris@72 381 Document::addDerivedModel(const Transform &transform,
Chris@72 382 const ModelTransformer::Input &input,
Chris@72 383 Model *outputModelToAdd)
Chris@45 384 {
Chris@45 385 if (m_models.find(outputModelToAdd) != m_models.end()) {
Chris@45 386 std::cerr << "WARNING: Document::addDerivedModel: Model already added"
Chris@45 387 << std::endl;
Chris@45 388 return;
Chris@45 389 }
Chris@45 390
Chris@77 391 #ifdef DEBUG_DOCUMENT
Chris@77 392 std::cerr << "Document::addDerivedModel: source is " << input.getModel() << " \"" << input.getModel()->objectName().toStdString() << "\"" << std::endl;
Chris@77 393 #endif
Chris@45 394
Chris@45 395 ModelRecord rec;
Chris@72 396 rec.source = input.getModel();
Chris@72 397 rec.channel = input.getChannel();
Chris@45 398 rec.transform = transform;
Chris@45 399 rec.refcount = 0;
Chris@45 400
Chris@72 401 outputModelToAdd->setSourceModel(input.getModel());
Chris@45 402
Chris@45 403 m_models[outputModelToAdd] = rec;
Chris@45 404
Chris@45 405 emit modelAdded(outputModelToAdd);
Chris@45 406 }
Chris@45 407
Chris@45 408
Chris@45 409 void
Chris@45 410 Document::addImportedModel(Model *model)
Chris@45 411 {
Chris@45 412 if (m_models.find(model) != m_models.end()) {
Chris@45 413 std::cerr << "WARNING: Document::addImportedModel: Model already added"
Chris@45 414 << std::endl;
Chris@45 415 return;
Chris@45 416 }
Chris@45 417
Chris@45 418 ModelRecord rec;
Chris@45 419 rec.source = 0;
Chris@45 420 rec.refcount = 0;
Chris@45 421
Chris@45 422 m_models[model] = rec;
Chris@45 423
Chris@47 424 if (m_autoAlignment) alignModel(model);
Chris@47 425
Chris@45 426 emit modelAdded(model);
Chris@45 427 }
Chris@45 428
Chris@45 429 Model *
Chris@72 430 Document::addDerivedModel(const Transform &transform,
Chris@72 431 const ModelTransformer::Input &input)
Chris@45 432 {
Chris@45 433 Model *model = 0;
Chris@45 434
Chris@45 435 for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) {
Chris@45 436 if (i->second.transform == transform &&
Chris@72 437 i->second.source == input.getModel() &&
Chris@72 438 i->second.channel == input.getChannel()) {
Chris@45 439 return i->first;
Chris@45 440 }
Chris@45 441 }
Chris@45 442
Chris@72 443 model = ModelTransformerFactory::getInstance()->transform(transform, input);
Chris@45 444
Chris@45 445 if (!model) {
Chris@72 446 std::cerr << "WARNING: Document::addDerivedModel: no output model for transform " << transform.getIdentifier().toStdString() << std::endl;
Chris@45 447 } else {
Chris@72 448 addDerivedModel(transform, input, model);
Chris@45 449 }
Chris@45 450
Chris@45 451 return model;
Chris@45 452 }
Chris@45 453
Chris@45 454 void
Chris@45 455 Document::releaseModel(Model *model) // Will _not_ release main model!
Chris@45 456 {
Chris@45 457 if (model == 0) {
Chris@45 458 return;
Chris@45 459 }
Chris@45 460
Chris@45 461 if (model == m_mainModel) {
Chris@45 462 return;
Chris@45 463 }
Chris@45 464
Chris@45 465 bool toDelete = false;
Chris@45 466
Chris@45 467 if (m_models.find(model) != m_models.end()) {
Chris@45 468
Chris@45 469 if (m_models[model].refcount == 0) {
Chris@45 470 std::cerr << "WARNING: Document::releaseModel: model " << model
Chris@45 471 << " reference count is zero already!" << std::endl;
Chris@45 472 } else {
Chris@45 473 if (--m_models[model].refcount == 0) {
Chris@45 474 toDelete = true;
Chris@45 475 }
Chris@45 476 }
Chris@45 477 } else {
Chris@45 478 std::cerr << "WARNING: Document::releaseModel: Unfound model "
Chris@45 479 << model << std::endl;
Chris@45 480 toDelete = true;
Chris@45 481 }
Chris@45 482
Chris@45 483 if (toDelete) {
Chris@45 484
Chris@45 485 int sourceCount = 0;
Chris@45 486
Chris@45 487 for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) {
Chris@45 488 if (i->second.source == model) {
Chris@45 489 ++sourceCount;
Chris@45 490 i->second.source = 0;
Chris@45 491 }
Chris@45 492 }
Chris@45 493
Chris@45 494 if (sourceCount > 0) {
Chris@45 495 std::cerr << "Document::releaseModel: Deleting model "
Chris@45 496 << model << " even though it is source for "
Chris@45 497 << sourceCount << " other derived model(s) -- resetting "
Chris@45 498 << "their source fields appropriately" << std::endl;
Chris@45 499 }
Chris@45 500
Chris@45 501 emit modelAboutToBeDeleted(model);
Chris@45 502 model->aboutToDelete();
Chris@45 503 m_models.erase(model);
Chris@45 504 delete model;
Chris@45 505 }
Chris@45 506 }
Chris@45 507
Chris@45 508 void
Chris@45 509 Document::deleteLayer(Layer *layer, bool force)
Chris@45 510 {
Chris@45 511 if (m_layerViewMap.find(layer) != m_layerViewMap.end() &&
Chris@45 512 m_layerViewMap[layer].size() > 0) {
Chris@45 513
Chris@45 514 std::cerr << "WARNING: Document::deleteLayer: Layer "
Chris@45 515 << layer << " [" << layer->objectName().toStdString() << "]"
Chris@45 516 << " is still used in " << m_layerViewMap[layer].size()
Chris@45 517 << " views!" << std::endl;
Chris@45 518
Chris@45 519 if (force) {
Chris@45 520
Chris@77 521 #ifdef DEBUG_DOCUMENT
Chris@45 522 std::cerr << "(force flag set -- deleting from all views)" << std::endl;
Chris@77 523 #endif
Chris@45 524
Chris@45 525 for (std::set<View *>::iterator j = m_layerViewMap[layer].begin();
Chris@45 526 j != m_layerViewMap[layer].end(); ++j) {
Chris@45 527 // don't use removeLayerFromView, as it issues a command
Chris@45 528 layer->setLayerDormant(*j, true);
Chris@45 529 (*j)->removeLayer(layer);
Chris@45 530 }
Chris@45 531
Chris@45 532 m_layerViewMap.erase(layer);
Chris@45 533
Chris@45 534 } else {
Chris@45 535 return;
Chris@45 536 }
Chris@45 537 }
Chris@45 538
Chris@45 539 if (m_layers.find(layer) == m_layers.end()) {
Chris@45 540 std::cerr << "Document::deleteLayer: Layer "
Chris@45 541 << layer << " does not exist, or has already been deleted "
Chris@45 542 << "(this may not be as serious as it sounds)" << std::endl;
Chris@45 543 return;
Chris@45 544 }
Chris@45 545
Chris@45 546 m_layers.erase(layer);
Chris@45 547
Chris@52 548 std::cerr << "Document::deleteLayer: Removing, now have "
Chris@52 549 << m_layers.size() << " layers" << std::endl;
Chris@52 550
Chris@45 551 releaseModel(layer->getModel());
Chris@45 552 emit layerRemoved(layer);
Chris@45 553 emit layerAboutToBeDeleted(layer);
Chris@45 554 delete layer;
Chris@45 555 }
Chris@45 556
Chris@45 557 void
Chris@45 558 Document::setModel(Layer *layer, Model *model)
Chris@45 559 {
Chris@45 560 if (model &&
Chris@45 561 model != m_mainModel &&
Chris@45 562 m_models.find(model) == m_models.end()) {
Chris@45 563 std::cerr << "ERROR: Document::setModel: Layer " << layer
Chris@45 564 << " (\"" << layer->objectName().toStdString()
Chris@45 565 << "\") wants to use unregistered model " << model
Chris@45 566 << ": register the layer's model before setting it!"
Chris@45 567 << std::endl;
Chris@45 568 return;
Chris@45 569 }
Chris@45 570
Chris@45 571 Model *previousModel = layer->getModel();
Chris@45 572
Chris@45 573 if (previousModel == model) {
Chris@45 574 std::cerr << "WARNING: Document::setModel: Layer " << layer << " (\""
Chris@45 575 << layer->objectName().toStdString()
Chris@45 576 << "\") is already set to model "
Chris@45 577 << model << " (\""
Chris@45 578 << (model ? model->objectName().toStdString() : "(null)")
Chris@45 579 << "\")" << std::endl;
Chris@45 580 return;
Chris@45 581 }
Chris@45 582
Chris@45 583 if (model && model != m_mainModel) {
Chris@45 584 m_models[model].refcount ++;
Chris@45 585 }
Chris@45 586
Chris@45 587 if (model && previousModel) {
Chris@45 588 PlayParameterRepository::getInstance()->copyParameters
Chris@45 589 (previousModel, model);
Chris@45 590 }
Chris@45 591
Chris@45 592 LayerFactory::getInstance()->setModel(layer, model);
Chris@45 593
Chris@45 594 if (previousModel) {
Chris@45 595 releaseModel(previousModel);
Chris@45 596 }
Chris@45 597 }
Chris@45 598
Chris@45 599 void
Chris@45 600 Document::setChannel(Layer *layer, int channel)
Chris@45 601 {
Chris@45 602 LayerFactory::getInstance()->setChannel(layer, channel);
Chris@45 603 }
Chris@45 604
Chris@45 605 void
Chris@45 606 Document::addLayerToView(View *view, Layer *layer)
Chris@45 607 {
Chris@45 608 Model *model = layer->getModel();
Chris@45 609 if (!model) {
Chris@77 610 #ifdef DEBUG_DOCUMENT
Chris@77 611 std::cerr << "Document::addLayerToView: Layer (\""
Chris@77 612 << layer->objectName().toStdString()
Chris@77 613 << "\") with no model being added to view: "
Chris@77 614 << "normally you want to set the model first" << std::endl;
Chris@77 615 #endif
Chris@45 616 } else {
Chris@45 617 if (model != m_mainModel &&
Chris@45 618 m_models.find(model) == m_models.end()) {
Chris@45 619 std::cerr << "ERROR: Document::addLayerToView: Layer " << layer
Chris@45 620 << " has unregistered model " << model
Chris@45 621 << " -- register the layer's model before adding the layer!" << std::endl;
Chris@45 622 return;
Chris@45 623 }
Chris@45 624 }
Chris@45 625
Chris@45 626 CommandHistory::getInstance()->addCommand
Chris@45 627 (new Document::AddLayerCommand(this, view, layer));
Chris@45 628 }
Chris@45 629
Chris@45 630 void
Chris@45 631 Document::removeLayerFromView(View *view, Layer *layer)
Chris@45 632 {
Chris@45 633 CommandHistory::getInstance()->addCommand
Chris@45 634 (new Document::RemoveLayerCommand(this, view, layer));
Chris@45 635 }
Chris@45 636
Chris@45 637 void
Chris@45 638 Document::addToLayerViewMap(Layer *layer, View *view)
Chris@45 639 {
Chris@45 640 bool firstView = (m_layerViewMap.find(layer) == m_layerViewMap.end() ||
Chris@45 641 m_layerViewMap[layer].empty());
Chris@45 642
Chris@45 643 if (m_layerViewMap[layer].find(view) !=
Chris@45 644 m_layerViewMap[layer].end()) {
Chris@45 645 std::cerr << "WARNING: Document::addToLayerViewMap:"
Chris@45 646 << " Layer " << layer << " -> view " << view << " already in"
Chris@45 647 << " layer view map -- internal inconsistency" << std::endl;
Chris@45 648 }
Chris@45 649
Chris@45 650 m_layerViewMap[layer].insert(view);
Chris@45 651
Chris@45 652 if (firstView) emit layerInAView(layer, true);
Chris@45 653 }
Chris@45 654
Chris@45 655 void
Chris@45 656 Document::removeFromLayerViewMap(Layer *layer, View *view)
Chris@45 657 {
Chris@45 658 if (m_layerViewMap[layer].find(view) ==
Chris@45 659 m_layerViewMap[layer].end()) {
Chris@45 660 std::cerr << "WARNING: Document::removeFromLayerViewMap:"
Chris@45 661 << " Layer " << layer << " -> view " << view << " not in"
Chris@45 662 << " layer view map -- internal inconsistency" << std::endl;
Chris@45 663 }
Chris@45 664
Chris@45 665 m_layerViewMap[layer].erase(view);
Chris@45 666
Chris@45 667 if (m_layerViewMap[layer].empty()) {
Chris@45 668 m_layerViewMap.erase(layer);
Chris@45 669 emit layerInAView(layer, false);
Chris@45 670 }
Chris@45 671 }
Chris@45 672
Chris@45 673 QString
Chris@45 674 Document::getUniqueLayerName(QString candidate)
Chris@45 675 {
Chris@45 676 for (int count = 1; ; ++count) {
Chris@45 677
Chris@45 678 QString adjusted =
Chris@45 679 (count > 1 ? QString("%1 <%2>").arg(candidate).arg(count) :
Chris@45 680 candidate);
Chris@45 681
Chris@45 682 bool duplicate = false;
Chris@45 683
Chris@45 684 for (LayerSet::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@45 685 if ((*i)->objectName() == adjusted) {
Chris@45 686 duplicate = true;
Chris@45 687 break;
Chris@45 688 }
Chris@45 689 }
Chris@45 690
Chris@45 691 if (!duplicate) return adjusted;
Chris@45 692 }
Chris@45 693 }
Chris@45 694
Chris@45 695 std::vector<Model *>
Chris@72 696 Document::getTransformInputModels()
Chris@45 697 {
Chris@45 698 std::vector<Model *> models;
Chris@45 699
Chris@45 700 if (!m_mainModel) return models;
Chris@45 701
Chris@45 702 models.push_back(m_mainModel);
Chris@45 703
Chris@45 704 //!!! This will pick up all models, including those that aren't visible...
Chris@45 705
Chris@45 706 for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) {
Chris@45 707
Chris@45 708 Model *model = i->first;
Chris@45 709 if (!model || model == m_mainModel) continue;
Chris@45 710 DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model);
Chris@45 711
Chris@45 712 if (dtvm) {
Chris@45 713 models.push_back(dtvm);
Chris@45 714 }
Chris@45 715 }
Chris@45 716
Chris@45 717 return models;
Chris@45 718 }
Chris@45 719
Chris@50 720 bool
Chris@77 721 Document::isKnownModel(const Model *model) const
Chris@77 722 {
Chris@77 723 if (model == m_mainModel) return true;
Chris@77 724 return (m_models.find(const_cast<Model *>(model)) != m_models.end());
Chris@77 725 }
Chris@77 726
Chris@77 727 bool
Chris@51 728 Document::canAlign()
Chris@50 729 {
Chris@54 730 TransformId id = "vamp:match-vamp-plugin:match:path";
Chris@54 731 TransformFactory *factory = TransformFactory::getInstance();
Chris@54 732 return factory->haveTransform(id);
Chris@50 733 }
Chris@50 734
Chris@45 735 void
Chris@45 736 Document::alignModel(Model *model)
Chris@45 737 {
Chris@45 738 if (!m_mainModel || model == m_mainModel) return;
Chris@45 739
Chris@45 740 RangeSummarisableTimeValueModel *rm =
Chris@45 741 dynamic_cast<RangeSummarisableTimeValueModel *>(model);
Chris@45 742 if (!rm) return;
Chris@48 743
Chris@48 744 if (rm->getAlignmentReference() == m_mainModel) return;
Chris@45 745
Chris@45 746 // This involves creating three new models:
Chris@45 747
Chris@45 748 // 1. an AggregateWaveModel to provide the mixdowns of the main
Chris@45 749 // model and the new model in its two channels, as input to the
Chris@45 750 // MATCH plugin
Chris@45 751
Chris@45 752 // 2. a SparseTimeValueModel, which is the model automatically
Chris@53 753 // created by FeatureExtractionPluginTransformer when running the
Chris@45 754 // MATCH plugin (thus containing the alignment path)
Chris@45 755
Chris@45 756 // 3. an AlignmentModel, which stores the path model and carries
Chris@45 757 // out alignment lookups on it.
Chris@45 758
Chris@45 759 // The first two of these are provided as arguments to the
Chris@45 760 // constructor for the third, which takes responsibility for
Chris@45 761 // deleting them. The AlignmentModel, meanwhile, is passed to the
Chris@45 762 // new model we are aligning, which also takes responsibility for
Chris@45 763 // it. We should not have to delete any of these new models here.
Chris@45 764
Chris@45 765 AggregateWaveModel::ChannelSpecList components;
Chris@45 766
Chris@45 767 components.push_back(AggregateWaveModel::ModelChannelSpec
Chris@45 768 (m_mainModel, -1));
Chris@45 769
Chris@45 770 components.push_back(AggregateWaveModel::ModelChannelSpec
Chris@45 771 (rm, -1));
Chris@45 772
Chris@45 773 Model *aggregate = new AggregateWaveModel(components);
Chris@45 774
Chris@72 775 TransformId id = "vamp:match-vamp-plugin:match:path"; //!!! configure
Chris@45 776
Chris@72 777 TransformFactory *tf = TransformFactory::getInstance();
Chris@45 778
Chris@72 779 Transform transform = tf->getDefaultTransformFor
Chris@72 780 (id, aggregate->getSampleRate());
Chris@57 781
Chris@72 782 transform.setStepSize(transform.getBlockSize()/2);
Chris@72 783 transform.setParameter("serialise", 1);
Chris@64 784
Chris@72 785 //!!! QString args = "<plugin param-serialise=\"1\"/>";
Chris@72 786 // Model *transformOutput = factory->transform(id, aggregate, context, args);
Chris@72 787
Chris@72 788 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance();
Chris@72 789
Chris@72 790 Model *transformOutput = mtf->transform(transform, aggregate);
Chris@64 791
Chris@64 792 if (!transformOutput) {
Chris@72 793 transform.setStepSize(0);
Chris@72 794 transformOutput = mtf->transform(transform, aggregate);
Chris@64 795 }
Chris@45 796
Chris@45 797 SparseTimeValueModel *path = dynamic_cast<SparseTimeValueModel *>
Chris@45 798 (transformOutput);
Chris@45 799
Chris@45 800 if (!path) {
Chris@45 801 std::cerr << "Document::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << std::endl;
Chris@45 802 delete transformOutput;
Chris@45 803 delete aggregate;
Chris@45 804 return;
Chris@45 805 }
Chris@45 806
Chris@45 807 AlignmentModel *alignmentModel = new AlignmentModel
Chris@45 808 (m_mainModel, model, aggregate, path);
Chris@45 809
Chris@45 810 rm->setAlignment(alignmentModel);
Chris@45 811 }
Chris@45 812
Chris@45 813 void
Chris@45 814 Document::alignModels()
Chris@45 815 {
Chris@45 816 for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) {
Chris@45 817 alignModel(i->first);
Chris@45 818 }
Chris@45 819 }
Chris@45 820
Chris@45 821 Document::AddLayerCommand::AddLayerCommand(Document *d,
Chris@45 822 View *view,
Chris@45 823 Layer *layer) :
Chris@45 824 m_d(d),
Chris@45 825 m_view(view),
Chris@45 826 m_layer(layer),
Chris@45 827 m_name(qApp->translate("AddLayerCommand", "Add %1 Layer").arg(layer->objectName())),
Chris@45 828 m_added(false)
Chris@45 829 {
Chris@45 830 }
Chris@45 831
Chris@45 832 Document::AddLayerCommand::~AddLayerCommand()
Chris@45 833 {
Chris@77 834 #ifdef DEBUG_DOCUMENT
Chris@77 835 std::cerr << "Document::AddLayerCommand::~AddLayerCommand" << std::endl;
Chris@77 836 #endif
Chris@45 837 if (!m_added) {
Chris@45 838 m_d->deleteLayer(m_layer);
Chris@45 839 }
Chris@45 840 }
Chris@45 841
Chris@45 842 void
Chris@45 843 Document::AddLayerCommand::execute()
Chris@45 844 {
Chris@45 845 for (int i = 0; i < m_view->getLayerCount(); ++i) {
Chris@45 846 if (m_view->getLayer(i) == m_layer) {
Chris@45 847 // already there
Chris@45 848 m_layer->setLayerDormant(m_view, false);
Chris@45 849 m_added = true;
Chris@45 850 return;
Chris@45 851 }
Chris@45 852 }
Chris@45 853
Chris@45 854 m_view->addLayer(m_layer);
Chris@45 855 m_layer->setLayerDormant(m_view, false);
Chris@45 856
Chris@45 857 m_d->addToLayerViewMap(m_layer, m_view);
Chris@45 858 m_added = true;
Chris@45 859 }
Chris@45 860
Chris@45 861 void
Chris@45 862 Document::AddLayerCommand::unexecute()
Chris@45 863 {
Chris@45 864 m_view->removeLayer(m_layer);
Chris@45 865 m_layer->setLayerDormant(m_view, true);
Chris@45 866
Chris@45 867 m_d->removeFromLayerViewMap(m_layer, m_view);
Chris@45 868 m_added = false;
Chris@45 869 }
Chris@45 870
Chris@45 871 Document::RemoveLayerCommand::RemoveLayerCommand(Document *d,
Chris@45 872 View *view,
Chris@45 873 Layer *layer) :
Chris@45 874 m_d(d),
Chris@45 875 m_view(view),
Chris@45 876 m_layer(layer),
Chris@45 877 m_name(qApp->translate("RemoveLayerCommand", "Delete %1 Layer").arg(layer->objectName())),
Chris@45 878 m_added(true)
Chris@45 879 {
Chris@45 880 }
Chris@45 881
Chris@45 882 Document::RemoveLayerCommand::~RemoveLayerCommand()
Chris@45 883 {
Chris@77 884 #ifdef DEBUG_DOCUMENT
Chris@77 885 std::cerr << "Document::RemoveLayerCommand::~RemoveLayerCommand" << std::endl;
Chris@77 886 #endif
Chris@45 887 if (!m_added) {
Chris@45 888 m_d->deleteLayer(m_layer);
Chris@45 889 }
Chris@45 890 }
Chris@45 891
Chris@45 892 void
Chris@45 893 Document::RemoveLayerCommand::execute()
Chris@45 894 {
Chris@45 895 bool have = false;
Chris@45 896 for (int i = 0; i < m_view->getLayerCount(); ++i) {
Chris@45 897 if (m_view->getLayer(i) == m_layer) {
Chris@45 898 have = true;
Chris@45 899 break;
Chris@45 900 }
Chris@45 901 }
Chris@45 902
Chris@45 903 if (!have) { // not there!
Chris@45 904 m_layer->setLayerDormant(m_view, true);
Chris@45 905 m_added = false;
Chris@45 906 return;
Chris@45 907 }
Chris@45 908
Chris@45 909 m_view->removeLayer(m_layer);
Chris@45 910 m_layer->setLayerDormant(m_view, true);
Chris@45 911
Chris@45 912 m_d->removeFromLayerViewMap(m_layer, m_view);
Chris@45 913 m_added = false;
Chris@45 914 }
Chris@45 915
Chris@45 916 void
Chris@45 917 Document::RemoveLayerCommand::unexecute()
Chris@45 918 {
Chris@45 919 m_view->addLayer(m_layer);
Chris@45 920 m_layer->setLayerDormant(m_view, false);
Chris@45 921
Chris@45 922 m_d->addToLayerViewMap(m_layer, m_view);
Chris@45 923 m_added = true;
Chris@45 924 }
Chris@45 925
Chris@45 926 void
Chris@45 927 Document::toXml(QTextStream &out, QString indent, QString extraAttributes) const
Chris@45 928 {
Chris@45 929 out << indent + QString("<data%1%2>\n")
Chris@45 930 .arg(extraAttributes == "" ? "" : " ").arg(extraAttributes);
Chris@45 931
Chris@45 932 if (m_mainModel) {
Chris@45 933 m_mainModel->toXml(out, indent + " ", "mainModel=\"true\"");
Chris@45 934 }
Chris@45 935
Chris@45 936 // Models that are not used in a layer that is in a view should
Chris@45 937 // not be written. Get our list of required models first.
Chris@45 938
Chris@45 939 std::set<const Model *> used;
Chris@45 940
Chris@45 941 for (LayerViewMap::const_iterator i = m_layerViewMap.begin();
Chris@45 942 i != m_layerViewMap.end(); ++i) {
Chris@45 943
Chris@45 944 if (i->first && !i->second.empty() && i->first->getModel()) {
Chris@45 945 used.insert(i->first->getModel());
Chris@45 946 }
Chris@45 947 }
Chris@45 948
Chris@45 949 for (ModelMap::const_iterator i = m_models.begin();
Chris@45 950 i != m_models.end(); ++i) {
Chris@45 951
Chris@45 952 const Model *model = i->first;
Chris@45 953 const ModelRecord &rec = i->second;
Chris@45 954
Chris@45 955 if (used.find(model) == used.end()) continue;
Chris@45 956
Chris@45 957 // We need an intelligent way to determine which models need
Chris@45 958 // to be streamed (i.e. have been edited, or are small) and
Chris@45 959 // which should not be (i.e. remain as generated by a
Chris@45 960 // transform, and are large).
Chris@45 961 //
Chris@45 962 // At the moment we can get away with deciding not to stream
Chris@45 963 // dense 3d models or writable wave file models, provided they
Chris@45 964 // were generated from a transform, because at the moment there
Chris@45 965 // is no way to edit those model types so it should be safe to
Chris@45 966 // regenerate them. That won't always work in future though.
Chris@45 967 // It would be particularly nice to be able to ask the user,
Chris@45 968 // as well as making an intelligent guess.
Chris@45 969
Chris@45 970 bool writeModel = true;
Chris@45 971 bool haveDerivation = false;
Chris@45 972
Chris@72 973 if (rec.source && rec.transform.getIdentifier() != "") {
Chris@45 974 haveDerivation = true;
Chris@45 975 }
Chris@45 976
Chris@45 977 if (haveDerivation) {
Chris@45 978 if (dynamic_cast<const WritableWaveFileModel *>(model)) {
Chris@45 979 writeModel = false;
Chris@45 980 } else if (dynamic_cast<const DenseThreeDimensionalModel *>(model)) {
Chris@45 981 writeModel = false;
Chris@45 982 }
Chris@45 983 }
Chris@45 984
Chris@45 985 if (writeModel) {
Chris@45 986 i->first->toXml(out, indent + " ");
Chris@45 987 }
Chris@45 988
Chris@45 989 if (haveDerivation) {
Chris@72 990 writeBackwardCompatibleDerivation(out, indent + " ",
Chris@72 991 i->first, rec);
Chris@45 992 }
Chris@45 993
Chris@45 994 //!!! We should probably own the PlayParameterRepository
Chris@45 995 PlayParameters *playParameters =
Chris@45 996 PlayParameterRepository::getInstance()->getPlayParameters(i->first);
Chris@45 997 if (playParameters) {
Chris@45 998 playParameters->toXml
Chris@45 999 (out, indent + " ",
Chris@45 1000 QString("model=\"%1\"")
Chris@45 1001 .arg(XmlExportable::getObjectExportId(i->first)));
Chris@45 1002 }
Chris@45 1003 }
Chris@45 1004
Chris@45 1005 for (LayerSet::const_iterator i = m_layers.begin();
Chris@45 1006 i != m_layers.end(); ++i) {
Chris@45 1007
Chris@45 1008 (*i)->toXml(out, indent + " ");
Chris@45 1009 }
Chris@45 1010
Chris@45 1011 out << indent + "</data>\n";
Chris@45 1012 }
Chris@45 1013
Chris@72 1014 void
Chris@72 1015 Document::writeBackwardCompatibleDerivation(QTextStream &out, QString indent,
Chris@72 1016 Model *targetModel,
Chris@72 1017 const ModelRecord &rec) const
Chris@72 1018 {
Chris@72 1019 // There is a lot of redundancy in the XML we output here, because
Chris@72 1020 // we want it to work with older SV session file reading code as
Chris@72 1021 // well.
Chris@72 1022 //
Chris@72 1023 // Formerly, a transform was described using a derivation element
Chris@72 1024 // which set out the source and target models, execution context
Chris@72 1025 // (step size, input channel etc) and transform id, containing a
Chris@72 1026 // plugin element which set out the transform parameters and so
Chris@72 1027 // on. (The plugin element came from a "configurationXml" string
Chris@72 1028 // obtained from PluginXml.)
Chris@72 1029 //
Chris@72 1030 // This has been replaced by a derivation element setting out the
Chris@72 1031 // source and target models and input channel, containing a
Chris@72 1032 // transform element which sets out everything in the Transform.
Chris@72 1033 //
Chris@72 1034 // In order to retain compatibility with older SV code, however,
Chris@72 1035 // we have to write out the same stuff into the derivation as
Chris@72 1036 // before, and manufacture an appropriate plugin element as well
Chris@72 1037 // as the transform element. In order that newer code knows it's
Chris@72 1038 // dealing with a newer format, we will also write an attribute
Chris@72 1039 // 'type="transform"' in the derivation element.
Chris@45 1040
Chris@72 1041 const Transform &transform = rec.transform;
Chris@72 1042
Chris@72 1043 // Just for reference, this is what we would write if we didn't
Chris@72 1044 // have to be backward compatible:
Chris@72 1045 //
Chris@72 1046 // out << indent
Chris@72 1047 // << QString("<derivation type=\"transform\" source=\"%1\" "
Chris@72 1048 // "model=\"%2\" channel=\"%3\">\n")
Chris@72 1049 // .arg(XmlExportable::getObjectExportId(rec.source))
Chris@72 1050 // .arg(XmlExportable::getObjectExportId(targetModel))
Chris@72 1051 // .arg(rec.channel);
Chris@72 1052 //
Chris@72 1053 // transform.toXml(out, indent + " ");
Chris@72 1054 //
Chris@72 1055 // out << indent << "</derivation>\n";
Chris@72 1056 //
Chris@72 1057 // Unfortunately, we can't just do that. So we do this...
Chris@72 1058
Chris@72 1059 QString extentsAttributes;
Chris@72 1060 if (transform.getStartTime() != RealTime::zeroTime ||
Chris@72 1061 transform.getDuration() != RealTime::zeroTime) {
Chris@72 1062 extentsAttributes = QString("startFrame=\"%1\" duration=\"%2\" ")
Chris@72 1063 .arg(RealTime::realTime2Frame(transform.getStartTime(),
Chris@72 1064 targetModel->getSampleRate()))
Chris@72 1065 .arg(RealTime::realTime2Frame(transform.getDuration(),
Chris@72 1066 targetModel->getSampleRate()));
Chris@72 1067 }
Chris@72 1068
Chris@72 1069 out << indent;
Chris@72 1070 out << QString("<derivation type=\"transform\" source=\"%1\" "
Chris@72 1071 "model=\"%2\" channel=\"%3\" domain=\"%4\" "
Chris@72 1072 "stepSize=\"%5\" blockSize=\"%6\" %7windowType=\"%8\" "
Chris@72 1073 "transform=\"%9\">\n")
Chris@72 1074 .arg(XmlExportable::getObjectExportId(rec.source))
Chris@72 1075 .arg(XmlExportable::getObjectExportId(targetModel))
Chris@72 1076 .arg(rec.channel)
Chris@72 1077 .arg(TransformFactory::getInstance()->getTransformInputDomain
Chris@72 1078 (transform.getIdentifier()))
Chris@72 1079 .arg(transform.getStepSize())
Chris@72 1080 .arg(transform.getBlockSize())
Chris@72 1081 .arg(extentsAttributes)
Chris@72 1082 .arg(int(transform.getWindowType()))
Chris@72 1083 .arg(XmlExportable::encodeEntities(transform.getIdentifier()));
Chris@72 1084
Chris@72 1085 transform.toXml(out, indent + " ");
Chris@72 1086
Chris@72 1087 out << indent << " "
Chris@72 1088 << TransformFactory::getInstance()->getPluginConfigurationXml(transform);
Chris@72 1089
Chris@72 1090 out << indent << "</derivation>\n";
Chris@72 1091 }
Chris@72 1092