Chris@150: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@150: Chris@150: /* Chris@150: Sonic Visualiser Chris@150: An audio file viewer and annotation editor. Chris@150: Centre for Digital Music, Queen Mary, University of London. Chris@150: This file copyright 2006 Chris Cannam. Chris@150: Chris@150: This program is free software; you can redistribute it and/or Chris@150: modify it under the terms of the GNU General Public License as Chris@150: published by the Free Software Foundation; either version 2 of the Chris@150: License, or (at your option) any later version. See the file Chris@150: COPYING included with this distribution for more information. Chris@150: */ Chris@150: Chris@1500: #ifndef SV_MODEL_H Chris@1500: #define SV_MODEL_H Chris@150: Chris@150: #include Chris@1798: #include Chris@1798: Chris@150: #include Chris@1798: #include Chris@150: Chris@1731: #include "base/ById.h" Chris@150: #include "base/XmlExportable.h" Chris@391: #include "base/Playable.h" Chris@1038: #include "base/BaseTypes.h" Chris@1060: #include "base/DataExportOptions.h" Chris@150: Chris@179: class ZoomConstraint; Chris@319: class AlignmentModel; Chris@179: Chris@150: /** Chris@150: * Model is the base class for all data models that represent any sort Chris@150: * of data on a time scale based on an audio frame rate. Chris@1798: * Chris@1798: * Model classes are expected to be thread-safe, particularly with Chris@1798: * regard to content rather than metadata (e.g. populating a model Chris@1798: * from a transform running in a background thread while displaying it Chris@1798: * in a UI layer). Chris@1798: * Chris@1798: * Never store a pointer to a model unless it is completely private to Chris@1798: * the code owning it. Models should be referred to using their Chris@1798: * ModelId id and looked up from the ById pool when needed. This Chris@1798: * returns a shared pointer, which ensures a sufficient lifespan for a Chris@1798: * transient operation locally. See notes in ById.h. The document Chris@1798: * unregisters models when it is closed, and then they are deleted Chris@1798: * when the last shared pointer instance expires. Chris@150: */ Chris@179: class Model : public QObject, Chris@1742: public WithTypedId, Chris@1429: public XmlExportable, Chris@391: public Playable Chris@150: { Chris@150: Q_OBJECT Chris@150: Chris@150: public: Chris@1735: typedef Id ModelId; Chris@1735: Chris@150: virtual ~Model(); Chris@150: Chris@150: /** Chris@150: * Return true if the model was constructed successfully. Classes Chris@150: * that refer to the model should always test this before use. Chris@150: */ Chris@150: virtual bool isOK() const = 0; Chris@150: Chris@150: /** Chris@150: * Return the first audio frame spanned by the model. Chris@150: */ Chris@1038: virtual sv_frame_t getStartFrame() const = 0; Chris@150: Chris@150: /** Chris@1611: * Return the audio frame at the end of the model, i.e. the final Chris@1659: * frame contained within the model plus 1 (rounded up to the Chris@1659: * model's "resolution" granularity, if more than 1). The end Chris@1659: * frame minus the start frame should yield the total duration in Chris@1659: * frames (as a multiple of the resolution) spanned by the Chris@1659: * model. This is broadly consistent with the definition of the Chris@1659: * end frame of a Selection object. Chris@1725: * Chris@1725: * If the end has been extended by extendEndFrame() beyond the Chris@1725: * true end frame, return the extended end instead. This is Chris@1725: * usually the behaviour you want. Chris@150: */ Chris@1725: sv_frame_t getEndFrame() const { Chris@1725: sv_frame_t trueEnd = getTrueEndFrame(); Chris@1725: if (m_extendTo > trueEnd) { Chris@1725: return m_extendTo; Chris@1725: } else { Chris@1725: return trueEnd; Chris@1725: } Chris@1725: } Chris@1725: Chris@1725: /** Chris@1725: * Return the audio frame at the end of the model. This is Chris@1725: * identical to getEndFrame(), except that it ignores any extended Chris@1725: * duration set with extendEndFrame(). Chris@1725: */ Chris@1725: virtual sv_frame_t getTrueEndFrame() const = 0; Chris@1725: Chris@1725: /** Chris@1725: * Extend the end of the model. If this is set to something beyond Chris@1725: * the true end of the data within the model, then getEndFrame() Chris@1725: * will return this value instead of the true end. (This is used Chris@1725: * by the Tony application.) Chris@1725: */ Chris@1725: void extendEndFrame(sv_frame_t to) { Chris@1725: m_extendTo = to; Chris@1725: } Chris@150: Chris@150: /** Chris@150: * Return the frame rate in frames per second. Chris@150: */ Chris@1040: virtual sv_samplerate_t getSampleRate() const = 0; Chris@150: Chris@150: /** Chris@297: * Return the frame rate of the underlying material, if the model Chris@297: * itself has already been resampled. Chris@297: */ Chris@1040: virtual sv_samplerate_t getNativeRate() const { return getSampleRate(); } Chris@297: Chris@297: /** Chris@333: * Return the "work title" of the model, if known. Chris@333: */ Chris@333: virtual QString getTitle() const; Chris@333: Chris@333: /** Chris@333: * Return the "artist" or "maker" of the model, if known. Chris@333: */ Chris@333: virtual QString getMaker() const; Chris@333: Chris@333: /** Chris@345: * Return the location of the data in this model (e.g. source Chris@345: * URL). This should not normally be returned for editable models Chris@345: * that have been edited. Chris@345: */ Chris@345: virtual QString getLocation() const; Chris@345: Chris@345: /** Chris@345: * Return the type of the model. For display purposes only. Chris@345: */ Chris@345: virtual QString getTypeName() const = 0; Chris@345: Chris@345: /** cannam@1452: * Return true if this is a sparse model. cannam@1452: */ cannam@1452: virtual bool isSparse() const { return false; } Chris@1500: Chris@1500: /** Chris@150: * Return true if the model has finished loading or calculating Chris@150: * all its data, for a model that is capable of calculating in a Chris@1671: * background thread. Chris@150: * Chris@1671: * If "completion" is non-NULL, return through it an estimated Chris@1671: * percentage value showing how far through the background Chris@1671: * operation it thinks it is (for progress reporting). This should Chris@1671: * be identical to the value returned by getCompletion(). Chris@1671: * Chris@1671: * A model that carries out all its calculation from the Chris@1671: * constructor or accessor functions would typically return true Chris@1671: * (and completion == 100) as long as isOK() is true. Other models Chris@1671: * may make the return value here depend on the internal Chris@1671: * completion status. Chris@1669: * Chris@1669: * See also getCompletion(). Chris@150: */ Chris@1671: virtual bool isReady(int *cp = nullptr) const { Chris@1671: int c = getCompletion(); Chris@1671: if (cp) *cp = c; Chris@1671: if (!isOK()) return false; Chris@1671: else return (c == 100); Chris@150: } Chris@1671: Chris@1671: /** Chris@1671: * Return an estimated percentage value showing how far through Chris@1671: * any background operation used to calculate or load the model Chris@1671: * data the model thinks it is. Must return 100 when the model is Chris@1671: * complete. Chris@1671: * Chris@1671: * A model that carries out all its calculation from the Chris@1671: * constructor or accessor functions might return 0 if isOK() is Chris@1671: * false and 100 if isOK() is true. Other models may make the Chris@1671: * return value here depend on the internal completion status. Chris@1671: * Chris@1671: * See also isReady(). Chris@1671: */ Chris@1671: virtual int getCompletion() const = 0; Chris@150: Chris@179: /** Chris@179: * If this model imposes a zoom constraint, i.e. some limit to the Chris@179: * set of resolutions at which its data can meaningfully be Chris@179: * displayed, then return it. Chris@179: */ Chris@179: virtual const ZoomConstraint *getZoomConstraint() const { Chris@179: return 0; Chris@179: } Chris@179: Chris@297: /** Chris@1735: * If this model was derived from another, return the id of the Chris@1735: * model it was derived from. The assumption is that the source Chris@1735: * model's alignment will also apply to this model, unless some Chris@1735: * other property (such as a specific alignment model set on this Chris@1735: * model) indicates otherwise. Chris@297: */ Chris@1735: virtual ModelId getSourceModel() const { Chris@297: return m_sourceModel; Chris@297: } Chris@297: Chris@297: /** Chris@297: * Set the source model for this model. Chris@297: */ Chris@1735: virtual void setSourceModel(ModelId model); Chris@319: Chris@319: /** Chris@1735: * Specify an alignment between this model's timeline and that of Chris@1735: * a reference model. The alignment model, of type AlignmentModel, Chris@1761: * records both the reference and the alignment. Chris@319: */ Chris@1735: virtual void setAlignment(ModelId alignmentModel); Chris@319: Chris@319: /** Chris@407: * Retrieve the alignment model for this model. This is not a Chris@407: * generally useful function, as the alignment you really want may Chris@407: * be performed by the source model instead. You should normally Chris@407: * use getAlignmentReference, alignToReference and Chris@407: * alignFromReference instead of this. The main intended Chris@407: * application for this function is in streaming out alignments to Chris@407: * the session file. Chris@407: */ Chris@1735: virtual const ModelId getAlignment() const; Chris@407: Chris@407: /** Chris@319: * Return the reference model for the current alignment timeline, Chris@319: * if any. Chris@319: */ Chris@1735: virtual const ModelId getAlignmentReference() const; Chris@319: Chris@319: /** Chris@319: * Return the frame number of the reference model that corresponds Chris@319: * to the given frame number in this model. Chris@319: */ Chris@1038: virtual sv_frame_t alignToReference(sv_frame_t frame) const; Chris@319: Chris@319: /** Chris@319: * Return the frame number in this model that corresponds to the Chris@319: * given frame number of the reference model. Chris@319: */ Chris@1038: virtual sv_frame_t alignFromReference(sv_frame_t referenceFrame) const; Chris@319: Chris@319: /** Chris@319: * Return the completion percentage for the alignment model: 100 Chris@319: * if there is no alignment model or it has been entirely Chris@319: * calculated, or less than 100 if it is still being calculated. Chris@319: */ Chris@319: virtual int getAlignmentCompletion() const; Chris@297: Chris@558: /** Chris@558: * Set the event, feature, or signal type URI for the features Chris@558: * contained in this model, according to the Audio Features RDF Chris@558: * ontology. Chris@558: */ Chris@558: void setRDFTypeURI(QString uri) { m_typeUri = uri; } Chris@558: Chris@558: /** Chris@558: * Retrieve the event, feature, or signal type URI for the Chris@558: * features contained in this model, if previously set with Chris@558: * setRDFTypeURI. Chris@558: */ Chris@558: QString getRDFTypeURI() const { return m_typeUri; } Chris@558: Chris@1580: void toXml(QTextStream &stream, Chris@1608: QString indent = "", Chris@1608: QString extraAttributes = "") const override; Chris@150: Chris@1815: /** Chris@1833: * Return a label for each column that would be written by Chris@1833: * toStringExportRows. Chris@1815: */ Chris@1833: virtual QVector Chris@1833: getStringExportHeaders(DataExportOptions options) const = 0; Chris@1815: Chris@1815: /** Chris@1833: * Emit events starting within the given range as string rows Chris@1833: * ready for conversion to an e.g. comma-separated data format. Chris@1815: */ Chris@1833: virtual QVector> Chris@1833: toStringExportRows(DataExportOptions options, Chris@1833: sv_frame_t startFrame, Chris@1833: sv_frame_t duration) const = 0; Chris@150: Chris@150: signals: Chris@150: /** Chris@150: * Emitted when a model has been edited (or more data retrieved Chris@150: * from cache, in the case of a cached model that generates slowly) Chris@150: */ Chris@1752: void modelChanged(ModelId myId); Chris@150: Chris@150: /** Chris@150: * Emitted when a model has been edited (or more data retrieved Chris@150: * from cache, in the case of a cached model that generates slowly) Chris@150: */ Chris@1752: void modelChangedWithin(ModelId myId, sv_frame_t startFrame, sv_frame_t endFrame); Chris@150: Chris@150: /** Chris@150: * Emitted when some internal processing has advanced a stage, but Chris@150: * the model has not changed externally. Views should respond by Chris@150: * updating any progress meters or other monitoring, but not Chris@150: * refreshing the actual view. Chris@150: */ Chris@1752: void completionChanged(ModelId myId); Chris@150: Chris@319: /** Chris@411: * Emitted when internal processing is complete (i.e. when Chris@411: * isReady() would return true, with completion at 100). Chris@411: */ Chris@1752: void ready(ModelId myId); Chris@411: Chris@411: /** Chris@319: * Emitted when the completion percentage changes for the Chris@1767: * calculation of this model's alignment model. (The ModelId Chris@1767: * provided is that of this model, not the alignment model.) Chris@319: */ Chris@1752: void alignmentCompletionChanged(ModelId myId); Chris@319: Chris@1767: private slots: Chris@1767: void alignmentModelCompletionChanged(ModelId); Chris@1767: Chris@150: protected: Chris@1798: Model() : m_extendTo(0) { } Chris@150: Chris@150: // Not provided. Chris@1735: Model(const Model &) =delete; Chris@1735: Model &operator=(const Model &) =delete; Chris@297: Chris@1798: mutable QMutex m_mutex; Chris@1735: ModelId m_sourceModel; Chris@1735: ModelId m_alignmentModel; Chris@558: QString m_typeUri; Chris@1798: std::atomic m_extendTo; Chris@150: }; Chris@150: Chris@1741: typedef Model::Id ModelId; Chris@1742: typedef TypedById ModelById; Chris@1731: Chris@150: #endif