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" gyorgyf@270: #include "transform/FeatureExtractionModelTransformer.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@588: class AggregateWaveModel; Chris@45: Chris@329: class AdditionalModelConverter; Chris@329: Chris@423: class Align; Chris@423: 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@297: * Create and return suitable layers for the given transforms, Chris@297: * which must be identical apart from the output (i.e. must use Chris@297: * the same plugin and configuration). The layers are returned in Chris@329: * the same order as the transforms are supplied. matthiasm@269: */ Chris@297: std::vector createDerivedLayers(const Transforms &, Chris@297: const ModelTransformer::Input &); matthiasm@269: Chris@363: typedef void *LayerCreationAsyncHandle; Chris@363: Chris@329: class LayerCreationHandler { Chris@329: public: Chris@329: virtual ~LayerCreationHandler() { } Chris@329: Chris@329: /** Chris@329: * The primary layers are those corresponding 1-1 to the input Chris@329: * models, listed in the same order as the input models. The Chris@329: * additional layers vector contains any layers (from all Chris@329: * models) that were returned separately at the end of Chris@363: * processing. The handle indicates which async process this Chris@363: * callback was initiated by. It must not be used again after Chris@363: * this function returns. Chris@329: */ Chris@363: virtual void layersCreated(LayerCreationAsyncHandle handle, Chris@363: std::vector primary, Chris@329: std::vector additional) = 0; Chris@329: }; Chris@329: Chris@329: /** Chris@329: * Create suitable layers for the given transforms, which must be Chris@329: * identical apart from the output (i.e. must use the same plugin Chris@329: * and configuration). This method returns after initialising the Chris@329: * transformer process, and the layers are returned through a Chris@363: * subsequent call to the provided handler (which must be Chris@363: * non-null). The handle returned will be passed through to the Chris@363: * handler callback, and may be also used for cancelling the task. Chris@329: */ Chris@363: LayerCreationAsyncHandle createDerivedLayersAsync(const Transforms &, Chris@363: const ModelTransformer::Input &, Chris@363: LayerCreationHandler *handler); Chris@363: Chris@363: /** Chris@363: * Indicate that the async layer creation task associated with the Chris@363: * given handle should be cancelled. There is no guarantee about Chris@363: * what this will mean, and the handler callback may still be Chris@363: * called. Chris@363: */ Chris@363: void cancelAsyncLayerCreation(LayerCreationAsyncHandle handle); Chris@329: matthiasm@269: /** 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@296: QString &returnedMessage); Chris@45: Chris@45: /** Chris@297: * Add derived models associated with the given set of related Chris@297: * transforms, running the transforms and returning the resulting Chris@297: * models. Chris@297: */ Chris@329: friend class AdditionalModelConverter; Chris@297: std::vector addDerivedModels(const Transforms &transforms, Chris@297: const ModelTransformer::Input &input, Chris@329: QString &returnedMessage, Chris@329: AdditionalModelConverter *); Chris@297: Chris@297: /** 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@329: void addAlreadyDerivedModel(const Transform &transform, Chris@329: const ModelTransformer::Input &input, Chris@329: 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@588: Chris@588: /** Chris@588: * Add an aggregate model (one which represents a set of component Chris@588: * wave models as one model per channel in a single wave Chris@588: * model). Aggregate models are unusual in that they are created Chris@588: * for a single transform each and have no refcount. (This Chris@588: * probably isn't ideal!) They are recorded separately from other Chris@588: * models, and will be deleted if any of their component models Chris@588: * are removed. Chris@588: */ Chris@588: void addAggregateModel(AggregateWaveModel *); 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@333: * Remove the given layer from the given view. Ownership of the Chris@333: * layer is transferred to a command object on the undo stack, and Chris@333: * the layer will be deleted when the undo stack is pruned. 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@226: void toXmlAsTemplate(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@428: Chris@428: void alignmentComplete(AlignmentModel *); Chris@420: void alignmentFailed(QString message); Chris@45: Chris@160: void activity(QString); Chris@160: Chris@588: protected slots: Chris@588: void aggregateModelInvalidated(); Chris@588: 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@329: bool additional; 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@329: /** Chris@329: * Add an extra derived model (returned at the end of processing a Chris@329: * transform). Chris@329: */ Chris@329: void addAdditionalModel(Model *); Chris@329: Chris@588: std::set m_aggregateModels; Chris@588: 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@159: virtual QString getName() const; 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@159: virtual QString getName() const; 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@339: bool m_wasDormant; 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@226: void toXml(QTextStream &, QString, QString, bool asTemplate) const; Chris@226: void writePlaceholderMainModel(QTextStream &, QString) const; Chris@226: Chris@329: std::vector createLayersForDerivedModels(std::vector, Chris@329: QStringList names); Chris@329: 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@423: Align *m_align; Chris@45: }; Chris@45: Chris@45: #endif