summaryrefslogtreecommitdiffstats
path: root/src/base/Composition.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/base/Composition.h')
-rw-r--r--src/base/Composition.h1134
1 files changed, 1134 insertions, 0 deletions
diff --git a/src/base/Composition.h b/src/base/Composition.h
new file mode 100644
index 0000000..24865dd
--- /dev/null
+++ b/src/base/Composition.h
@@ -0,0 +1,1134 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral right of the authors to claim authorship of this work
+ has been asserted.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _COMPOSITION_H_
+#define _COMPOSITION_H_
+
+#include <set>
+#include <map>
+
+#include "FastVector.h"
+
+#include "RealTime.h"
+#include "Segment.h"
+#include "Track.h"
+#include "Configuration.h"
+#include "XmlExportable.h"
+#include "ColourMap.h"
+#include "TriggerSegment.h"
+
+#include "Marker.h"
+
+namespace Rosegarden
+{
+// We store tempo in quarter-notes per minute * 10^5 (hundred
+// thousandths of a quarter-note per minute). This means the maximum
+// tempo in a 32-bit integer is about 21400 qpm. We use a signed int
+// for compatibility with the Event integer type -- but note that we
+// use 0 (rather than -1) to indicate "tempo not set", by convention
+// (though see usage of target tempo in e.g. addTempoAtTime).
+typedef int tempoT;
+
+class Quantizer;
+class BasicQuantizer;
+class NotationQuantizer;
+
+/**
+ * Composition contains a complete representation of a piece of music.
+ * It is a container for multiple Segments, as well as any associated
+ * non-Event data.
+ *
+ * The Composition owns the Segments it holds, and deletes them on
+ * destruction. When Segments are removed, it will also delete them.
+ */
+
+class CompositionObserver;
+
+class Composition : public XmlExportable
+{
+ friend class Track; // to call notifyTrackChanged()
+ friend class Segment; // to call notifySegmentRepeatChanged()
+
+public:
+ typedef std::multiset<Segment*, Segment::SegmentCmp> segmentcontainer;
+ typedef segmentcontainer::iterator iterator;
+ typedef segmentcontainer::const_iterator const_iterator;
+
+ typedef std::map<TrackId, Track*> trackcontainer;
+ typedef trackcontainer::iterator trackiterator;
+ typedef trackcontainer::const_iterator trackconstiterator;
+
+ typedef std::vector<Marker*> markercontainer;
+ typedef markercontainer::iterator markeriterator;
+ typedef markercontainer::const_iterator markerconstiterator;
+
+ typedef std::set<TriggerSegmentRec *, TriggerSegmentCmp> triggersegmentcontainer;
+ typedef triggersegmentcontainer::iterator triggersegmentcontaineriterator;
+ typedef triggersegmentcontainer::const_iterator triggersegmentcontainerconstiterator;
+
+ typedef std::set<TrackId> recordtrackcontainer;
+ typedef recordtrackcontainer::iterator recordtrackiterator;
+ typedef recordtrackcontainer::const_iterator recordtrackconstiterator;
+
+ Composition();
+ virtual ~Composition();
+
+private:
+ Composition(const Composition &);
+ Composition &operator=(const Composition &);
+public:
+
+ /**
+ * Remove all Segments from the Composition and destroy them
+ */
+ void clear();
+
+ /**
+ * Return the absolute end time of the segment that ends last
+ */
+ timeT getDuration() const;
+
+
+ //////
+ //
+ // START AND END MARKERS
+
+ timeT getStartMarker() const { return m_startMarker; }
+ timeT getEndMarker() const { return m_endMarker; }
+
+ void setStartMarker(const timeT &sM);
+ void setEndMarker(const timeT &eM);
+
+
+ //////
+ //
+ // INSTRUMENT & TRACK
+
+ Track* getTrackById(TrackId track) const;
+
+ Track* getTrackByPosition(int position) const;
+
+ int getTrackPositionById(TrackId track) const; // -1 if not found
+
+ trackcontainer& getTracks() { return m_tracks; }
+
+ const trackcontainer& getTracks() const { return m_tracks; }
+
+ // Reset id and position
+ void resetTrackIdAndPosition(TrackId oldId, TrackId newId, int position);
+
+ TrackId getMinTrackId() const;
+ TrackId getMaxTrackId() const;
+
+ const recordtrackcontainer &getRecordTracks() const { return m_recordTracks; }
+ void setTrackRecording(TrackId track, bool recording);
+ bool isTrackRecording(TrackId track) const;
+
+ // Get and set Solo Track
+ //
+ TrackId getSelectedTrack() const { return m_selectedTrack; }
+
+ void setSelectedTrack(TrackId track);
+
+ // Are we soloing a Track?
+ //
+ bool isSolo() const { return m_solo; }
+ void setSolo(bool value);
+
+ unsigned int getNbTracks() const { return m_tracks.size(); }
+
+ /**
+ * Clear out the Track container
+ */
+ void clearTracks();
+
+ /**
+ * Insert a new Track. The Composition takes over ownership of
+ * the track object.
+ */
+ void addTrack(Track *track);
+
+ /**
+ * Delete a Track by index
+ */
+ void deleteTrack(TrackId track);
+
+ /**
+ * Detach a Track (revert ownership of the Track object to the
+ * caller).
+ */
+ bool detachTrack(Track *track);
+
+ /**
+ * Get the highest running track id (generated and kept
+ * through addTrack)
+ */
+ TrackId getNewTrackId() const;
+
+
+ //////
+ //
+ // MARKERS
+
+ markercontainer& getMarkers() { return m_markers; }
+ const markercontainer& getMarkers() const { return m_markers; }
+
+ /**
+ * Add a new Marker. The Composition takes ownership of the
+ * marker object.
+ */
+ void addMarker(Marker *marker);
+
+ /**
+ * Detach a Marker (revert ownership of the Marker object to the
+ * caller).
+ */
+ bool detachMarker(Marker *marker);
+
+ bool isMarkerAtPosition(timeT time) const;
+
+ void clearMarkers();
+
+
+ //////
+ //
+ // SEGMENT
+
+ segmentcontainer& getSegments() { return m_segments; }
+ const segmentcontainer& getSegments() const { return m_segments; }
+
+ unsigned int getNbSegments() const { return m_segments.size(); }
+
+ /**
+ * Add a new Segment and return an iterator pointing to it
+ * The inserted Segment is owned by the Composition object
+ */
+ iterator addSegment(Segment*);
+
+ /**
+ * Delete the Segment pointed to by the specified iterator
+ *
+ * NOTE: The Segment is deleted from the Composition and
+ * destroyed
+ */
+ void deleteSegment(iterator);
+
+ /**
+ * Delete the Segment if it is part of the Composition
+ * \return true if the Segment was found and deleted
+ *
+ * NOTE: The Segment is deleted from the composition and
+ * destroyed
+ */
+ bool deleteSegment(Segment*);
+
+ /**
+ * DO NOT USE THIS METHOD
+ *
+ * Set a Segment's start time while keeping the integrity of the
+ * Composition multiset.
+ *
+ * The segment is removed and re-inserted from the composition
+ * so the ordering is preserved.
+ */
+ void setSegmentStartTime(Segment*, timeT);
+
+ /**
+ * Test whether a Segment exists in this Composition.
+ */
+ bool contains(const Segment *);
+
+ /**
+ * Return an iterator pointing at the given Segment, or end()
+ * if it does not exist in this Composition.
+ */
+ iterator findSegment(const Segment *);
+
+ /**
+ * Remove the Segment if it is part of the Composition,
+ * but do not destroy it (passing it to addSegment again
+ * would restore it correctly).
+ * \return true if the Segment was found and removed
+ *
+ * NOTE: Many of the Segment methods will fail if the
+ * Segment is not in a Composition. You should not
+ * expect to do anything meaningful with a Segment that
+ * has been detached from the Composition in this way.
+ */
+ bool detachSegment(Segment*);
+
+ /**
+ * Add a new Segment which has been "weakly detached"
+ *
+ * Like addSegment(), but doesn't send the segmentAdded signal
+ * nor updating refresh statuses
+ */
+ iterator weakAddSegment(Segment*);
+
+ /**
+ * Detach a segment which you're going to re-add (with weakAddSegment)
+ * later.
+ * Like detachSegment(), but without sending the segmentDeleted signal
+ * nor updating refresh statuses.
+ */
+ bool weakDetachSegment(Segment*);
+
+ /**
+ * Get the largest number of segments that "overlap" at any one
+ * time on the given track. I have given this function a nice
+ * long name to make it feel important.
+ */
+ int getMaxContemporaneousSegmentsOnTrack(TrackId track) const;
+
+ /**
+ * Retrieve a "vertical" index for this segment within its track.
+ * Currently this is based on studying the way that segments on
+ * the track overlap and returning the lowest integer such that no
+ * prior starting segment that overlaps with this one would use
+ * the same integer. In future this could use proper voice
+ * ordering.
+ */
+ int getSegmentVoiceIndex(const Segment *) const;
+
+
+ //////
+ //
+ // TRIGGER SEGMENTS
+
+ triggersegmentcontainer &getTriggerSegments() { return m_triggerSegments; }
+ const triggersegmentcontainer &getTriggerSegments() const { return m_triggerSegments; }
+
+ /**
+ * Add a new trigger Segment with a given base pitch and base
+ * velocity, and return its record. If pitch or velocity is -1,
+ * it will be taken from the first note event in the segment
+ */
+ TriggerSegmentRec *addTriggerSegment(Segment *, int pitch = -1, int velocity = -1);
+
+ /**
+ * Delete a trigger Segment.
+ */
+ void deleteTriggerSegment(TriggerSegmentId);
+
+ /**
+ * Detach a trigger Segment from the Composition.
+ */
+ void detachTriggerSegment(TriggerSegmentId);
+
+ /**
+ * Delete all trigger Segments.
+ */
+ void clearTriggerSegments();
+
+ /**
+ * Return the TriggerSegmentId for the given Segment, or -1 if it is
+ * not a trigger Segment.
+ */
+ int getTriggerSegmentId(Segment *);
+
+ /**
+ * Return the Segment for a given TriggerSegmentId
+ */
+ Segment *getTriggerSegment(TriggerSegmentId);
+
+ /**
+ * Return the TriggerSegmentRec (with Segment, base pitch, base velocity,
+ * references etc) for a given TriggerSegmentId
+ */
+ TriggerSegmentRec *getTriggerSegmentRec(TriggerSegmentId);
+
+ /**
+ * Add a new trigger Segment with a given ID and base pitch and
+ * velocity. Fails and returns 0 if the ID is already in use.
+ * This is intended for use from file load or from undo/redo.
+ */
+ TriggerSegmentRec *addTriggerSegment(Segment *, TriggerSegmentId,
+ int basePitch = -1, int baseVelocity = -1);
+
+ /**
+ * Get the ID of the next trigger segment that will be inserted.
+ */
+ TriggerSegmentId getNextTriggerSegmentId() const;
+
+ /**
+ * Specify the next trigger ID. This is intended for use from file
+ * load only. Do not use this function unless you know what you're
+ * doing.
+ */
+ void setNextTriggerSegmentId(TriggerSegmentId);
+
+ /**
+ * Update the trigger segment references for all trigger segments.
+ * To be called after file load.
+ */
+ void updateTriggerSegmentReferences();
+
+
+ //////
+ //
+ // BAR
+
+ /**
+ * Return the total number of bars in the composition
+ */
+ int getNbBars() const;
+
+ /**
+ * Return the number of the bar that starts at or contains time t.
+ *
+ * Will happily return computed bar numbers for times before
+ * the start or beyond the real end of the composition.
+ */
+ int getBarNumber(timeT t) const;
+
+ /**
+ * Return the starting time of bar n
+ */
+ timeT getBarStart(int n) const {
+ return getBarRange(n).first;
+ }
+
+ /**
+ * Return the ending time of bar n
+ */
+ timeT getBarEnd(int n) const {
+ return getBarRange(n).second;
+ }
+
+ /**
+ * Return the time range of bar n.
+ *
+ * Will happily return theoretical timings for bars before the
+ * start or beyond the end of composition (i.e. there is no
+ * requirement that 0 <= n < getNbBars()).
+ */
+ std::pair<timeT, timeT> getBarRange(int n) const;
+
+ /**
+ * Return the starting time of the bar that contains time t
+ */
+ timeT getBarStartForTime(timeT t) const {
+ return getBarRangeForTime(t).first;
+ }
+
+ /**
+ * Return the ending time of the bar that contains time t
+ */
+ timeT getBarEndForTime(timeT t) const {
+ return getBarRangeForTime(t).second;
+ }
+
+ /**
+ * Return the starting and ending times of the bar that contains
+ * time t.
+ *
+ * Will happily return theoretical timings for bars before the
+ * start or beyond the end of composition.
+ */
+ std::pair<timeT, timeT> getBarRangeForTime(timeT t) const;
+
+ /**
+ * Get the default number of bars in a new empty composition
+ */
+ static int getDefaultNbBars() { return m_defaultNbBars; }
+
+ /**
+ * Set the default number of bars in a new empty composition
+ */
+ static void setDefaultNbBars(int b) { m_defaultNbBars = b; }
+
+
+ //////
+ //
+ // TIME SIGNATURE
+
+ /**
+ * Add the given time signature at the given time. Returns the
+ * resulting index of the time signature (suitable for passing
+ * to removeTimeSignature, for example)
+ */
+ int addTimeSignature(timeT t, TimeSignature timeSig);
+
+ /**
+ * Return the time signature in effect at time t
+ */
+ TimeSignature getTimeSignatureAt(timeT t) const;
+
+ /**
+ * Return the time signature in effect at time t, and the time at
+ * which it came into effect
+ */
+ timeT getTimeSignatureAt(timeT, TimeSignature &) const;
+
+ /**
+ * Return the time signature in effect in bar n. Also sets
+ * isNew to true if the time signature is a new one that did
+ * not appear in the previous bar.
+ */
+ TimeSignature getTimeSignatureInBar(int n, bool &isNew) const;
+
+ /**
+ * Return the total number of time signature changes in the
+ * composition.
+ */
+ int getTimeSignatureCount() const;
+
+ /**
+ * Return the index of the last time signature change before
+ * or at the given time, in a range suitable for passing to
+ * getTimeSignatureChange. Return -1 if there has been no
+ * time signature by this time.
+ */
+ int getTimeSignatureNumberAt(timeT time) const;
+
+ /**
+ * Return the absolute time of and time signature introduced
+ * by time-signature change n.
+ */
+ std::pair<timeT, TimeSignature> getTimeSignatureChange(int n) const;
+
+ /**
+ * Remove time signature change event n from the composition.
+ */
+ void removeTimeSignature(int n);
+
+
+
+ //////
+ //
+ // TEMPO
+
+ /**
+ * Return the (approximate) number of quarters per minute for a
+ * given tempo.
+ */
+ static double getTempoQpm(tempoT tempo) { return double(tempo) / 100000.0; }
+ static tempoT getTempoForQpm(double qpm) { return tempoT(qpm * 100000 + 0.01); }
+
+ /**
+ * Return the tempo in effect at time t. If a ramped tempo change
+ * is in effect at the time, it will be properly interpolated and
+ * a computed value returned.
+ */
+ tempoT getTempoAtTime(timeT t) const;
+
+ /**
+ * Return the tempo in effect at the current playback position.
+ */
+ tempoT getCurrentTempo() const { return getTempoAtTime(getPosition()); }
+
+ /**
+ * Set a default tempo for the composition. This will be
+ * overridden by any tempo events encountered during playback.
+ */
+ void setCompositionDefaultTempo(tempoT tempo) { m_defaultTempo = tempo; }
+ tempoT getCompositionDefaultTempo() const { return m_defaultTempo; }
+
+ /**
+ * Add a tempo-change event at the given time, to the given tempo.
+ * Removes any existing tempo event at that time. Returns the
+ * index of the new tempo event in a form suitable for passing to
+ * removeTempoChange.
+ *
+ * If targetTempo == -1, adds a single constant tempo change.
+ * If targetTempo == 0, adds a smooth tempo ramp from this tempo
+ * change to the next.
+ * If targetTempo > 0, adds a smooth tempo ramp from this tempo
+ * ending at targetTempo at the time of the next tempo change.
+ */
+ int addTempoAtTime(timeT time, tempoT tempo, tempoT targetTempo = -1);
+
+ /**
+ * Return the number of tempo changes in the composition.
+ */
+ int getTempoChangeCount() const;
+
+ /**
+ * Return the index of the last tempo change before the given
+ * time, in a range suitable for passing to getTempoChange.
+ * Return -1 if the default tempo is in effect at this time.
+ */
+ int getTempoChangeNumberAt(timeT time) const;
+
+ /**
+ * Return the absolute time of and tempo introduced by tempo
+ * change number n. If the tempo is ramped, this returns only
+ * the starting tempo.
+ */
+ std::pair<timeT, tempoT> getTempoChange(int n) const;
+
+ /**
+ * Return whether the tempo change number n is a ramped tempo or
+ * not, and if it is, return the target tempo for the ramp.
+ *
+ * If calculate is false, return a target tempo of 0 if the tempo
+ * change is defined to ramp to the following tempo. If calculate
+ * is true, return a target tempo equal to the following tempo in
+ * this case.
+ */
+ std::pair<bool, tempoT> getTempoRamping(int n, bool calculate = true) const;
+
+ /**
+ * Remove tempo change event n from the composition.
+ */
+ void removeTempoChange(int n);
+
+ /**
+ * Get the slowest assigned tempo in the composition.
+ */
+ tempoT getMinTempo() const {
+ return ((m_minTempo != 0) ? m_minTempo : m_defaultTempo);
+ }
+
+ /**
+ * Get the fastest assigned tempo in the composition.
+ */
+ tempoT getMaxTempo() const {
+ return ((m_maxTempo != 0) ? m_maxTempo : m_defaultTempo);
+ }
+
+
+ //////
+ //
+ // REAL TIME
+
+ /**
+ * Return the number of microseconds elapsed between
+ * the beginning of the composition and the given timeT time.
+ * (timeT units are independent of tempo; this takes into
+ * account any tempo changes in the first t units of time.)
+ *
+ * This is a fairly efficient operation, not dependent on the
+ * magnitude of t or the number of tempo changes in the piece.
+ */
+ RealTime getElapsedRealTime(timeT t) const;
+
+ /**
+ * Return the nearest time in timeT units to the point at the
+ * given number of microseconds after the beginning of the
+ * composition. (timeT units are independent of tempo; this takes
+ * into account any tempo changes in the first t microseconds.)
+ * The result will be approximate, as timeT units are obviously
+ * less precise than microseconds.
+ *
+ * This is a fairly efficient operation, not dependent on the
+ * magnitude of t or the number of tempo changes in the piece.
+ */
+ timeT getElapsedTimeForRealTime(RealTime t) const;
+
+ /**
+ * Return the number of microseconds elapsed between
+ * the two given timeT indices into the composition, taking
+ * into account any tempo changes between the two times.
+ */
+ RealTime getRealTimeDifference(timeT t0, timeT t1) const {
+ if (t1 > t0) return getElapsedRealTime(t1) - getElapsedRealTime(t0);
+ else return getElapsedRealTime(t0) - getElapsedRealTime(t1);
+ }
+
+
+ //////
+ //
+ // OTHER TIME CONVERSIONS
+
+ /**
+ * Return (by reference) the bar number and beat/division values
+ * corresponding to a given absolute time.
+ */
+ void getMusicalTimeForAbsoluteTime(timeT absoluteTime,
+ int &bar, int &beat,
+ int &fraction, int &remainder);
+
+ /**
+ * Return (by reference) the number of bars and beats/divisions
+ * corresponding to a given duration. The absolute time at which
+ * the duration starts is also required, so as to know the correct
+ * time signature.
+ */
+ void getMusicalTimeForDuration(timeT absoluteTime, timeT duration,
+ int &bars, int &beats,
+ int &fractions, int &remainder);
+
+ /**
+ * Return the absolute time corresponding to a given bar number
+ * and beat/division values.
+ */
+ timeT getAbsoluteTimeForMusicalTime(int bar, int beat,
+ int fraction, int remainder);
+
+ /**
+ * Return the duration corresponding to a given number of bars and
+ * beats/divisions. The absolute time at which the duration
+ * starts is also required, so as to know the correct time
+ * signature.
+ */
+ timeT getDurationForMusicalTime(timeT absoluteTime,
+ int bars, int beats,
+ int fractions, int remainder);
+
+
+ /**
+ * Get the current playback position.
+ */
+ timeT getPosition() const { return m_position; }
+
+ /**
+ * Set the current playback position.
+ */
+ void setPosition(timeT position);
+
+
+
+ //////
+ //
+ // LOOP
+
+ timeT getLoopStart() const { return m_loopStart; }
+ timeT getLoopEnd() const { return m_loopEnd;}
+
+ void setLoopStart(const timeT &lS) { m_loopStart = lS; }
+ void setLoopEnd(const timeT &lE) { m_loopEnd = lE; }
+
+ // Determine if we're currently looping
+ //
+ bool isLooping() const { return (m_loopStart != m_loopEnd); }
+
+
+
+ //////
+ //
+ // OTHER STUFF
+
+
+ // Some set<> API delegation
+ iterator begin() { return m_segments.begin(); }
+ const_iterator begin() const { return m_segments.begin(); }
+ iterator end() { return m_segments.end(); }
+ const_iterator end() const { return m_segments.end(); }
+
+
+ // XML exportable method
+ //
+ virtual std::string toXmlString();
+
+ // Who's making this racket?
+ //
+ Configuration &getMetadata() {
+ return m_metadata;
+ }
+ const Configuration &getMetadata() const {
+ return m_metadata;
+ }
+
+ std::string getCopyrightNote() const {
+ return m_metadata.get<String>(CompositionMetadataKeys::Copyright,
+ "");
+ }
+ void setCopyrightNote(const std::string &cr) {
+ m_metadata.set<String>(CompositionMetadataKeys::Copyright, cr);
+ }
+
+
+ // We can have the metronome on or off while playing or
+ // recording - get and set values from here
+ //
+ bool usePlayMetronome() const { return m_playMetronome; }
+ bool useRecordMetronome() const { return m_recordMetronome; }
+
+ void setPlayMetronome(bool value);
+ void setRecordMetronome(bool value);
+
+
+ // Colour stuff
+ ColourMap& getSegmentColourMap() { return m_segmentColourMap; }
+ const ColourMap& getSegmentColourMap() const { return m_segmentColourMap; }
+ void setSegmentColourMap(ColourMap &newmap);
+
+ // General colourmap for non-segments
+ //
+ ColourMap& getGeneralColourMap() { return m_generalColourMap; }
+ void setGeneralColourMap(ColourMap &newmap);
+
+
+ //////
+ //
+ // QUANTIZERS
+
+ /**
+ * Return a quantizer that quantizes to the our most basic
+ * units (i.e. a unit quantizer whose unit is our shortest
+ * note duration).
+ */
+ const BasicQuantizer *getBasicQuantizer() const {
+ return m_basicQuantizer;
+ }
+
+ /**
+ * Return a quantizer that does quantization for notation
+ * only.
+ */
+ const NotationQuantizer *getNotationQuantizer() const {
+ return m_notationQuantizer;
+ }
+
+
+ //////
+ //
+ // REFRESH STATUS
+
+ // delegate RefreshStatusArray API
+ unsigned int getNewRefreshStatusId() {
+ return m_refreshStatusArray.getNewRefreshStatusId();
+ }
+
+ RefreshStatus& getRefreshStatus(unsigned int id) {
+ return m_refreshStatusArray.getRefreshStatus(id);
+ }
+
+ /// Set all refresh statuses to true
+ void updateRefreshStatuses() {
+ m_refreshStatusArray.updateRefreshStatuses();
+ }
+
+
+ void addObserver(CompositionObserver *obs) { m_observers.push_back(obs); }
+ void removeObserver(CompositionObserver *obs) { m_observers.remove(obs); }
+
+ //////
+ // DEBUG FACILITIES
+ void dump(std::ostream&, bool full=false) const;
+
+protected:
+
+ static const std::string TempoEventType;
+ static const PropertyName TempoProperty;
+ static const PropertyName TargetTempoProperty;
+
+ static const PropertyName NoAbsoluteTimeProperty;
+ static const PropertyName BarNumberProperty;
+ static const PropertyName TempoTimestampProperty;
+
+
+ struct ReferenceSegmentEventCmp
+ {
+ bool operator()(const Event &e1, const Event &e2) const;
+ bool operator()(const Event *e1, const Event *e2) const {
+ return operator()(*e1, *e2);
+ }
+ };
+
+ struct BarNumberComparator
+ {
+ bool operator()(const Event &e1, const Event &e2) const {
+ return (e1.get<Int>(BarNumberProperty) <
+ e2.get<Int>(BarNumberProperty));
+ }
+ bool operator()(const Event *e1, const Event *e2) const {
+ return operator()(*e1, *e2);
+ }
+ };
+
+ /**
+ * Ensure the selected and record trackids still point to something valid
+ * Must be called after deletion of detach of a track
+ */
+ void checkSelectedAndRecordTracks();
+ TrackId getClosestValidTrackId(TrackId id) const;
+
+
+ //--------------- Data members ---------------------------------
+ //
+ trackcontainer m_tracks;
+ segmentcontainer m_segments;
+
+ // The tracks we are armed for record on
+ //
+ recordtrackcontainer m_recordTracks;
+
+ // Are we soloing and if so which Track?
+ //
+ bool m_solo;
+ TrackId m_selectedTrack;
+
+ /**
+ * This is a bit like a segment, but can only contain one sort of
+ * event, and can only have one event at each absolute time
+ */
+ class ReferenceSegment :
+ public FastVector<Event *> // not a set: want random access for bars
+ {
+ typedef FastVector<Event *> Impl;
+
+ public:
+ ReferenceSegment(std::string eventType);
+ virtual ~ReferenceSegment();
+ private:
+ ReferenceSegment(const ReferenceSegment &);
+ ReferenceSegment& operator=(const ReferenceSegment &);
+ public:
+ typedef Impl::iterator iterator;
+ typedef Impl::size_type size_type;
+ typedef Impl::difference_type difference_type;
+
+ void clear();
+
+ timeT getDuration() const;
+
+ /// Inserts a single event, removing any existing one at that time
+ iterator insert(Event *e); // may throw Event::BadType
+
+ void erase(Event *e);
+
+ iterator findTime(timeT time);
+ iterator findNearestTime(timeT time);
+
+ iterator findRealTime(RealTime time);
+ iterator findNearestRealTime(RealTime time);
+
+ std::string getEventType() const { return m_eventType; }
+
+ private:
+ iterator find(Event *e);
+ std::string m_eventType;
+ };
+
+ /// Contains time signature events
+ mutable ReferenceSegment m_timeSigSegment;
+
+ /// Contains tempo events
+ mutable ReferenceSegment m_tempoSegment;
+
+ /// affects m_timeSigSegment
+ void calculateBarPositions() const;
+ mutable bool m_barPositionsNeedCalculating;
+ ReferenceSegment::iterator getTimeSignatureAtAux(timeT t) const;
+
+ /// affects m_tempoSegment
+ void calculateTempoTimestamps() const;
+ mutable bool m_tempoTimestampsNeedCalculating;
+ RealTime time2RealTime(timeT time, tempoT tempo) const;
+ RealTime time2RealTime(timeT time, tempoT tempo,
+ timeT targetTempoTime, tempoT targetTempo) const;
+ timeT realTime2Time(RealTime rtime, tempoT tempo) const;
+ timeT realTime2Time(RealTime rtime, tempoT tempo,
+ timeT targetTempoTime, tempoT targetTempo) const;
+
+ bool getTempoTarget(ReferenceSegment::const_iterator i,
+ tempoT &target,
+ timeT &targetTime) const;
+
+ static RealTime getTempoTimestamp(const Event *e);
+ static void setTempoTimestamp(Event *e, RealTime r);
+
+ typedef std::list<CompositionObserver *> ObserverSet;
+ ObserverSet m_observers;
+
+ void notifySegmentAdded(Segment *) const;
+ void notifySegmentRemoved(Segment *) const;
+ void notifySegmentRepeatChanged(Segment *, bool) const;
+ void notifySegmentRepeatEndChanged(Segment *, timeT) const;
+ void notifySegmentEventsTimingChanged(Segment *s, timeT delay, RealTime rtDelay) const;
+ void notifySegmentTransposeChanged(Segment *s, int transpose) const;
+ void notifySegmentTrackChanged(Segment *s, TrackId oldId, TrackId newId) const;
+ void notifySegmentStartChanged(Segment *, timeT);
+ void notifySegmentEndMarkerChange(Segment *s, bool shorten);
+ void notifyEndMarkerChange(bool shorten) const;
+ void notifyTrackChanged(Track*) const;
+ void notifyTrackDeleted(TrackId) const;
+ void notifyMetronomeChanged() const;
+ void notifyTimeSignatureChanged() const;
+ void notifySoloChanged() const;
+ void notifyTempoChanged() const;
+ void notifySourceDeletion() const;
+
+ void updateExtremeTempos();
+
+ BasicQuantizer *m_basicQuantizer;
+ NotationQuantizer *m_notationQuantizer;
+
+ timeT m_position;
+ tempoT m_defaultTempo;
+ tempoT m_minTempo; // cached from tempo segment
+ tempoT m_maxTempo; // cached from tempo segment
+
+ // Notional Composition markers - these define buffers for the
+ // start and end of the piece, Segments can still exist outside
+ // of these markers - these are for visual and playback cueing.
+ //
+ timeT m_startMarker;
+ timeT m_endMarker;
+
+ static int m_defaultNbBars;
+
+ // Loop start and end positions. If they're both the same
+ // value (usually 0) then there's no loop set.
+ //
+ timeT m_loopStart;
+ timeT m_loopEnd;
+
+ Configuration m_metadata;
+
+ bool m_playMetronome;
+ bool m_recordMetronome;
+
+ RefreshStatusArray<RefreshStatus> m_refreshStatusArray;
+
+ // User defined markers in the composition
+ //
+ markercontainer m_markers;
+
+ // Trigger segments (unsorted segments fired by events elsewhere)
+ //
+ triggersegmentcontainer m_triggerSegments;
+ TriggerSegmentId m_nextTriggerSegmentId;
+
+ ColourMap m_segmentColourMap;
+ ColourMap m_generalColourMap;
+};
+
+
+/**
+ * If you subclass from CompositionObserver, you can then attach to a
+ * Composition to receive notification when something changes.
+ *
+ * Normally all the methods in this class would be pure virtual. But
+ * because there are so many, that imposes far too much work on the
+ * subclass implementation in a case where it only really wants to
+ * know about one thing, such as segments being deleted. So we have
+ * empty default implementations, and you'll just have to take a bit
+ * more care to make sure you really are making the correct
+ * declarations in the subclass.
+ */
+
+class CompositionObserver
+{
+public:
+ CompositionObserver() : m_compositionDeleted(false) {}
+
+ virtual ~CompositionObserver() {}
+
+ /**
+ * Called after the segment has been added to the composition
+ */
+ virtual void segmentAdded(const Composition *, Segment *) { }
+
+ /**
+ * Called after the segment has been removed from the segment,
+ * and just before it is deleted
+ */
+ virtual void segmentRemoved(const Composition *, Segment *) { }
+
+ /**
+ * Called when the segment's repeat status has changed
+ */
+ virtual void segmentRepeatChanged(const Composition *, Segment *, bool) { }
+
+ /**
+ * Called when the segment's repeat end time has changed
+ */
+ virtual void segmentRepeatEndChanged(const Composition *, Segment *, timeT) { }
+
+ /**
+ * Called when the segment's delay timing has changed
+ */
+ virtual void segmentEventsTimingChanged(const Composition *, Segment *,
+ timeT /* delay */,
+ RealTime /* rtDelay */) { }
+
+ /**
+ * Called when the segment's transpose value has changed
+ */
+ virtual void segmentTransposeChanged(const Composition *, Segment *,
+ int /* transpose */) { }
+
+ /**
+ * Called when the segment's start time has changed
+ */
+ virtual void segmentStartChanged(const Composition *, Segment *,
+ timeT /* newStartTime */) { }
+
+ /**
+ * Called when the segment's end marker time has changed
+ */
+ virtual void segmentEndMarkerChanged(const Composition *, Segment *,
+ bool /* shorten */) { }
+
+ /**
+ * Called when the segment's track has changed
+ */
+ virtual void segmentTrackChanged(const Composition *, Segment *,
+ TrackId /* id */) { }
+
+ /**
+ * Called after the composition's end marker time has been
+ * changed
+ */
+ virtual void endMarkerTimeChanged(const Composition *, bool /* shorten */) { }
+
+ /**
+ * Called when a track is changed (instrument id, muted status...)
+ */
+ virtual void trackChanged(const Composition *, Track*) { }
+
+ /**
+ * Called when a track has been deleted
+ */
+ virtual void trackDeleted(const Composition *, TrackId) { }
+
+ /**
+ * Called when some time signature has changed
+ */
+ virtual void timeSignatureChanged(const Composition *) { }
+
+ /**
+ * Called when metronome status has changed (on/off)
+ */
+ virtual void metronomeChanged(const Composition *) { }
+
+ /**
+ * Called when solo status changes (solo on/off, and selected track)
+ */
+ virtual void soloChanged(const Composition *, bool /* solo */,
+ TrackId /* selectedTrack */) { }
+
+ /**
+ * Called when solo status changes (solo on/off, and selected track)
+ */
+ virtual void tempoChanged(const Composition *) { }
+
+ /**
+ * Called from the composition dtor
+ */
+ virtual void compositionDeleted(const Composition *) {
+ m_compositionDeleted = true;
+ }
+
+ bool isCompositionDeleted() { return m_compositionDeleted; }
+
+protected:
+ bool m_compositionDeleted;
+};
+
+}
+
+
+#endif
+