You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rosegarden/src/base/Composition.h

1133 lines
34 KiB

/*
Rosegarden
A sequencer and musical notation editor.
This program is Copyright 2000-2008
Guillaume Laurent <glaurent@telegraph-road.org>,
Chris Cannam <cannam@all-day-breakfast.com>,
Richard Bown <bownie@bownie.com>
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