Chris@45: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@45: Chris@45: /* Chris@45: Sonic Visualiser Chris@45: An audio file viewer and annotation editor. Chris@45: Centre for Digital Music, Queen Mary, University of London. Chris@45: This file copyright 2006 Chris Cannam and QMUL. Chris@45: Chris@45: This program is free software; you can redistribute it and/or Chris@45: modify it under the terms of the GNU General Public License as Chris@45: published by the Free Software Foundation; either version 2 of the Chris@45: License, or (at your option) any later version. See the file Chris@45: COPYING included with this distribution for more information. Chris@45: */ Chris@45: Chris@45: #ifndef _DOCUMENT_H_ Chris@45: #define _DOCUMENT_H_ Chris@45: Chris@45: #include "layer/LayerFactory.h" Chris@106: #include "transform/Transform.h" Chris@106: #include "transform/ModelTransformer.h" Chris@45: #include "base/Command.h" Chris@45: Chris@45: #include Chris@45: #include Chris@45: Chris@45: class Model; Chris@45: class Layer; Chris@45: class View; Chris@45: class WaveFileModel; Chris@45: Chris@45: /** Chris@45: * A Sonic Visualiser document consists of a set of data models, and Chris@45: * also the visualisation layers used to display them. Changes to the Chris@45: * layers and their layout need to be stored and managed in much the Chris@45: * same way as changes to the underlying data. Chris@45: * Chris@45: * The document manages: Chris@45: * Chris@45: * - A main data Model, which provides the underlying sample rate and Chris@45: * such like. This must be a WaveFileModel. Chris@45: * Chris@45: * - Any number of imported Model objects, which contain data without any Chris@45: * requirement to remember where the data came from or how to Chris@45: * regenerate it. Chris@45: * Chris@53: * - Any number of Model objects that were generated by a Transformer Chris@54: * such as FeatureExtractionModelTransformer. For these, we also Chris@45: * record the source model and the name of the transform used to Chris@45: * generate the model so that we can regenerate it (potentially Chris@45: * from a different source) on demand. Chris@45: * Chris@45: * - A flat list of Layer objects. Elsewhere, the GUI may distribute these Chris@45: * across any number of View widgets. A layer may be viewable on more Chris@45: * than one view at once, in principle. A layer refers to one model, Chris@45: * but the same model can be in use in more than one layer. Chris@45: * Chris@45: * The document does *not* manage the existence or structure of Pane Chris@45: * and other view widgets. However, it does provide convenience Chris@45: * methods for reference-counted command-based management of the Chris@45: * association between layers and views (addLayerToView, Chris@45: * removeLayerFromView). Chris@45: */ Chris@45: Chris@45: class Document : public QObject, Chris@45: public XmlExportable Chris@45: { Chris@45: Q_OBJECT Chris@45: Chris@45: public: Chris@45: Document(); Chris@45: virtual ~Document(); Chris@45: Chris@45: /** Chris@45: * Create and return a new layer of the given type, associated Chris@45: * with no model. The caller may set any model on this layer, but Chris@45: * the model must also be registered with the document via the Chris@45: * add-model methods below. Chris@45: */ Chris@45: Layer *createLayer(LayerFactory::LayerType); Chris@45: Chris@45: /** Chris@45: * Create and return a new layer of the given type, associated Chris@45: * with the current main model (if appropriate to the layer type). Chris@45: */ Chris@45: Layer *createMainModelLayer(LayerFactory::LayerType); Chris@45: Chris@45: /** Chris@45: * Create and return a new layer associated with the given model, Chris@45: * and register the model as an imported model. Chris@45: */ Chris@45: Layer *createImportedLayer(Model *); Chris@45: Chris@45: /** Chris@45: * Create and return a new layer of the given type, with an Chris@45: * appropriate empty model. If the given type is not one for Chris@45: * which an empty model can meaningfully be created, return 0. Chris@45: */ Chris@45: Layer *createEmptyLayer(LayerFactory::LayerType); Chris@45: Chris@45: /** Chris@45: * Create and return a new layer of the given type, associated Chris@45: * with the given transform name. This method does not run the Chris@45: * transform itself, nor create a model. The caller can safely Chris@45: * add a model to the layer later, but note that all models used Chris@45: * by a transform layer _must_ be registered with the document Chris@45: * using addDerivedModel below. Chris@45: */ Chris@54: Layer *createDerivedLayer(LayerFactory::LayerType, TransformId); Chris@45: Chris@45: /** Chris@45: * Create and return a suitable layer for the given transform, Chris@45: * running the transform and associating the resulting model with Chris@45: * the new layer. Chris@45: */ Chris@72: Layer *createDerivedLayer(const Transform &, Chris@72: const ModelTransformer::Input &); Chris@45: Chris@45: /** Chris@52: * Delete the given layer, and also its associated model if no Chris@52: * longer used by any other layer. In general, this should be the Chris@52: * only method used to delete layers -- doing so directly is a bit Chris@52: * of a social gaffe. Chris@52: */ Chris@52: void deleteLayer(Layer *, bool force = false); Chris@52: Chris@52: /** Chris@45: * Set the main model (the source for playback sample rate, etc) Chris@45: * to the given wave file model. This will regenerate any derived Chris@45: * models that were based on the previous main model. Chris@45: */ Chris@45: void setMainModel(WaveFileModel *); Chris@45: Chris@45: /** Chris@45: * Get the main model (the source for playback sample rate, etc). Chris@45: */ Chris@45: WaveFileModel *getMainModel() { return m_mainModel; } Chris@45: Chris@45: /** Chris@45: * Get the main model (the source for playback sample rate, etc). Chris@45: */ Chris@45: const WaveFileModel *getMainModel() const { return m_mainModel; } Chris@45: Chris@72: std::vector getTransformInputModels(); Chris@45: Chris@77: bool isKnownModel(const Model *) const; Chris@77: Chris@45: /** Chris@45: * Add a derived model associated with the given transform, Chris@45: * running the transform and returning the resulting model. Chris@45: */ Chris@72: Model *addDerivedModel(const Transform &transform, Chris@78: const ModelTransformer::Input &input, Chris@78: QString &returnedMessage); Chris@45: Chris@45: /** Chris@45: * Add a derived model associated with the given transform. This Chris@45: * is necessary to register any derived model that was not created Chris@45: * by the document using createDerivedModel or createDerivedLayer. Chris@45: */ Chris@72: void addDerivedModel(const Transform &transform, Chris@72: const ModelTransformer::Input &input, Chris@72: Model *outputModelToAdd); Chris@45: Chris@45: /** Chris@45: * Add an imported (non-derived, non-main) model. This is Chris@45: * necessary to register any imported model that is associated Chris@45: * with a layer. Chris@45: */ Chris@45: void addImportedModel(Model *); Chris@45: Chris@45: /** Chris@45: * Associate the given model with the given layer. The model must Chris@45: * have already been registered using one of the addXXModel Chris@45: * methods above. Chris@45: */ Chris@45: void setModel(Layer *, Model *); Chris@45: Chris@45: /** Chris@45: * Set the given layer to use the given channel of its model (-1 Chris@45: * means all available channels). Chris@45: */ Chris@45: void setChannel(Layer *, int); Chris@45: Chris@45: /** Chris@45: * Add the given layer to the given view. If the layer is Chris@45: * intended to show a particular model, the model should normally Chris@45: * be set using setModel before this method is called. Chris@45: */ Chris@45: void addLayerToView(View *, Layer *); Chris@45: Chris@45: /** Chris@45: * Remove the given layer from the given view. Chris@45: */ Chris@45: void removeLayerFromView(View *, Layer *); Chris@45: Chris@48: /** Chris@50: * Return true if alignment is supported (i.e. if the necessary Chris@50: * plugin is found). Chris@50: */ Chris@51: static bool canAlign(); Chris@50: Chris@50: /** Chris@48: * Specify whether models added via addImportedModel should be Chris@48: * automatically aligned against the main model if appropriate. Chris@48: */ Chris@48: void setAutoAlignment(bool on) { m_autoAlignment = on; } Chris@48: Chris@48: /** Chris@48: * Generate alignments for all appropriate models against the main Chris@48: * model. Existing alignments will not be re-calculated unless Chris@48: * the main model has changed since they were calculated. Chris@48: */ Chris@48: void alignModels(); Chris@48: Chris@45: void toXml(QTextStream &, QString indent, QString extraAttributes) const; Chris@47: Chris@45: signals: Chris@45: void layerAdded(Layer *); Chris@45: void layerRemoved(Layer *); Chris@45: void layerAboutToBeDeleted(Layer *); Chris@45: Chris@45: // Emitted when a layer is first added to a view, or when it is Chris@45: // last removed from a view Chris@45: void layerInAView(Layer *, bool); Chris@45: Chris@45: void modelAdded(Model *); Chris@45: void mainModelChanged(WaveFileModel *); // emitted after modelAdded Chris@45: void modelAboutToBeDeleted(Model *); Chris@45: Chris@78: void modelGenerationFailed(QString transformName, QString message); Chris@78: void modelGenerationWarning(QString transformName, QString message); Chris@78: void modelRegenerationFailed(QString layerName, QString transformName, Chris@78: QString message); Chris@78: void modelRegenerationWarning(QString layerName, QString transformName, Chris@78: QString message); Chris@78: void alignmentFailed(QString transformName, QString message); Chris@45: Chris@45: protected: Chris@45: void releaseModel(Model *model); Chris@45: Chris@45: /** Chris@45: * If model is suitable for alignment, align it against the main Chris@48: * model and store the alignment in the model. (If the model has Chris@48: * an alignment already for the current main model, leave it Chris@48: * unchanged.) Chris@45: */ Chris@45: void alignModel(Model *); Chris@45: Chris@45: /* Chris@45: * Every model that is in use by a layer in the document must be Chris@45: * found in either m_mainModel or m_models. We own and control Chris@45: * the lifespan of all of these models. Chris@45: */ Chris@45: Chris@45: /** Chris@45: * The model that provides the underlying sample rate, etc. This Chris@45: * model is not reference counted for layers, and is not freed Chris@45: * unless it is replaced or the document is deleted. Chris@45: */ Chris@45: WaveFileModel *m_mainModel; Chris@45: Chris@45: struct ModelRecord Chris@45: { Chris@45: // Information associated with a non-main model. If this Chris@45: // model is derived from another, then source will be non-NULL Chris@45: // and the transform name will be set appropriately. If the Chris@45: // transform name is set but source is NULL, then there was a Chris@45: // transform involved but the (target) model has been modified Chris@45: // since being generated from it. Chris@72: Chris@72: // This does not use ModelTransformer::Input, because it would Chris@72: // be confusing to have Input objects hanging around with NULL Chris@72: // models in them. Chris@72: Chris@45: const Model *source; Chris@72: int channel; Chris@72: Transform transform; Chris@45: Chris@45: // Count of the number of layers using this model. Chris@45: int refcount; Chris@45: }; Chris@45: Chris@45: typedef std::map ModelMap; Chris@45: ModelMap m_models; Chris@45: Chris@45: class AddLayerCommand : public Command Chris@45: { Chris@45: public: Chris@45: AddLayerCommand(Document *d, View *view, Layer *layer); Chris@45: virtual ~AddLayerCommand(); Chris@45: Chris@45: virtual void execute(); Chris@45: virtual void unexecute(); Chris@45: virtual QString getName() const { return m_name; } Chris@45: Chris@45: protected: Chris@45: Document *m_d; Chris@45: View *m_view; // I don't own this Chris@83: Layer *m_layer; // Document owns this, but I determine its lifespan Chris@45: QString m_name; Chris@45: bool m_added; Chris@45: }; Chris@45: Chris@45: class RemoveLayerCommand : public Command Chris@45: { Chris@45: public: Chris@45: RemoveLayerCommand(Document *d, View *view, Layer *layer); Chris@45: virtual ~RemoveLayerCommand(); Chris@45: Chris@45: virtual void execute(); Chris@45: virtual void unexecute(); Chris@45: virtual QString getName() const { return m_name; } Chris@45: Chris@45: protected: Chris@45: Document *m_d; Chris@45: View *m_view; // I don't own this Chris@45: Layer *m_layer; // Document owns this, but I determine its lifespan Chris@45: QString m_name; Chris@45: bool m_added; Chris@45: }; Chris@45: Chris@45: typedef std::map > LayerViewMap; Chris@45: LayerViewMap m_layerViewMap; Chris@45: Chris@45: void addToLayerViewMap(Layer *, View *); Chris@45: void removeFromLayerViewMap(Layer *, View *); Chris@45: Chris@45: QString getUniqueLayerName(QString candidate); Chris@72: void writeBackwardCompatibleDerivation(QTextStream &, QString, Model *, Chris@72: const ModelRecord &) const; Chris@90: Chris@90: static TransformId getAlignmentTransformName(); Chris@45: Chris@45: /** Chris@45: * And these are the layers. We also control the lifespans of Chris@45: * these (usually through the commands used to add and remove them). Chris@45: */ Chris@45: typedef std::set LayerSet; Chris@45: LayerSet m_layers; Chris@47: Chris@47: bool m_autoAlignment; Chris@45: }; Chris@45: Chris@45: #endif