summaryrefslogtreecommitdiffstats
path: root/src/base/Segment.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/base/Segment.h')
-rw-r--r--src/base/Segment.h783
1 files changed, 783 insertions, 0 deletions
diff --git a/src/base/Segment.h b/src/base/Segment.h
new file mode 100644
index 0000000..564d118
--- /dev/null
+++ b/src/base/Segment.h
@@ -0,0 +1,783 @@
+// -*- 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 _SEGMENT_H_
+#define _SEGMENT_H_
+
+#include <set>
+#include <list>
+#include <string>
+
+#include "Track.h"
+#include "Event.h"
+#include "NotationTypes.h"
+#include "RefreshStatus.h"
+#include "RealTime.h"
+#include "MidiProgram.h"
+
+namespace Rosegarden
+{
+
+class SegmentRefreshStatus : public RefreshStatus
+{
+public:
+ SegmentRefreshStatus() : m_from(0), m_to(0) {}
+
+ void push(timeT from, timeT to);
+
+ timeT from() const { return m_from; }
+ timeT to() const { return m_to; }
+
+protected:
+ timeT m_from;
+ timeT m_to;
+};
+
+
+/**
+ * Segment is the container for a set of Events that are all played on
+ * the same track. Each event has an absolute starting time,
+ * which is used as the index within the segment. Multiple events may
+ * have the same absolute time.
+ *
+ * (For example, chords are represented simply as a sequence of notes
+ * that share a starting time. The Segment can contain counterpoint --
+ * notes that overlap, rather than starting and ending together -- but
+ * in practice it's probably too hard to display so we should make
+ * more than one Segment if we want to represent true counterpoint.)
+ *
+ * If you want to carry out notation-related editing operations on
+ * a Segment, take a look at SegmentNotationHelper. If you want to play a
+ * Segment, try SegmentPerformanceHelper for duration calculations.
+ *
+ * The Segment owns the Events its items are pointing at.
+ */
+
+class SegmentObserver;
+class Quantizer;
+class BasicQuantizer;
+class Composition;
+
+class Segment : public std::multiset<Event*, Event::EventCmp>
+{
+public:
+ /// A Segment contains either Internal representation or Audio
+ typedef enum {
+ Internal,
+ Audio
+ } SegmentType;
+
+ /**
+ * Construct a Segment of a given type with a given formal starting time.
+ */
+ Segment(SegmentType segmentType = Internal,
+ timeT startTime = 0);
+ /**
+ * Copy constructor
+ */
+ Segment(const Segment&);
+
+ virtual ~Segment();
+
+
+ //////
+ //
+ // BASIC SEGMENT ATTRIBUTES
+
+ /**
+ * Get the Segment type (Internal or Audio)
+ */
+ SegmentType getType() const { return m_type; }
+
+ /**
+ * Note that a Segment does not have to be in a Composition;
+ * if it isn't, this will return zero
+ */
+ Composition *getComposition() const {
+ return m_composition;
+ }
+
+ /**
+ * Get the track number this Segment is associated with.
+ */
+ TrackId getTrack() const { return m_track; }
+
+ /**
+ * Set the track number this Segment is associated with.
+ */
+ void setTrack(TrackId i);
+
+ // label
+ //
+ void setLabel(const std::string &label);
+ std::string getLabel() const { return m_label; }
+
+ // Colour information
+ void setColourIndex(const unsigned int input);
+ unsigned int getColourIndex() const { return m_colourIndex; }
+
+ /**
+ * Returns a numeric id of some sort
+ * The id is guaranteed to be unique within the segment, but not to
+ * have any other interesting properties
+ */
+ int getNextId() const;
+
+ /**
+ * Returns a MIDI pitch representing the highest suggested playable note for
+ * notation contained in this segment, as a convenience reminder to composers.
+ *
+ * This property, and its corresponding lowest note counterpart, initialize by
+ * default such that no limitation is imposed. (lowest = 0, highest = 127)
+ */
+ int getHighestPlayable() { return m_highestPlayable; }
+
+ /**
+ * Set the highest suggested playable note for this segment
+ */
+ void setHighestPlayable(int pitch) { m_highestPlayable = pitch; }
+
+ /**
+ * Returns a MIDI pitch representing the lowest suggested playable note for
+ * notation contained in this segment, as a convenience reminder to composers
+ */
+ int getLowestPlayable() { return m_lowestPlayable; }
+
+ /**
+ * Set the highest suggested playable note for this segment
+ */
+ void setLowestPlayable(int pitch) { m_lowestPlayable = pitch; }
+
+
+
+ //////
+ //
+ // TIME & DURATION VALUES
+
+ /**
+ * Return the start time of the Segment. For a non-audio
+ * Segment, this is the start time of the first event in it.
+ */
+ timeT getStartTime() const;
+
+ /**
+ * Return the nominal end time of the Segment. This must
+ * be the same as or earlier than the getEndTime() value.
+ * The return value will not necessarily be that last set
+ * with setEndMarkerTime, as if there is a Composition its
+ * end marker will also be used for clipping.
+ */
+ timeT getEndMarkerTime() const;
+
+ /**
+ * Return the time of the end of the last event stored in the
+ * Segment. This time may be outside the audible/editable
+ * range of the Segment, depending on the location of the end
+ * marker.
+ */
+ timeT getEndTime() const;
+
+ /**
+ * Shift the start time of the Segment by moving the start
+ * times of all the events in the Segment.
+ */
+ void setStartTime(timeT);
+
+ /**
+ * DO NOT USE THIS METHOD
+ * Simple accessor for the m_startTime member. Used by
+ * Composition#setSegmentStartTime
+ */
+ void setStartTimeDataMember(timeT t) { m_startTime = t; }
+
+ /**
+ * Set the end marker (nominal end time) of this Segment.
+ *
+ * If the given time is later than the current end of the
+ * Segment's storage, extend the Segment by filling it with
+ * rests; if earlier, simply move the end marker. The end
+ * marker time may not precede the start time.
+ */
+ void setEndMarkerTime(timeT);
+
+ /**
+ * Set the end time of the Segment.
+ *
+ * If the given time is later than the current end of the
+ * Segment's storage, extend the Segment by filling it with
+ * rests; if earlier, shorten it by throwing away events as
+ * necessary (though do not truncate any events) and also move
+ * the end marker to the given time. The end time may not
+ * precede the start time.
+ *
+ * Note that simply inserting an event beyond the end of the
+ * Segment will also change the end time, although it does
+ * not fill with rests in the desirable way.
+ *
+ * Consider using setEndMarkerTime in preference to this.
+ */
+ void setEndTime(timeT);
+
+ /**
+ * Return an iterator pointing to the nominal end of the
+ * Segment. This may be earlier than the end() iterator.
+ */
+ iterator getEndMarker();
+
+ /**
+ * Return true if the given iterator points earlier in the
+ * Segment than the nominal end marker. You can use this
+ * as an extent test in code such as
+ *
+ * while (segment.isBeforeEndMarker(my_iterator)) {
+ * // ...
+ * ++my_iterator;
+ * }
+ *
+ * It is not generally safe to write
+ *
+ * while (my_iterator != segment.getEndMarker()) {
+ * // ...
+ * ++my_iterator;
+ * }
+ *
+ * as the loop will not terminate if my_iterator's initial
+ * value is already beyond the end marker. (Also takes the
+ * Composition's end marker into account.)
+ */
+ bool isBeforeEndMarker(const_iterator) const;
+
+ /**
+ * Remove the end marker, thus making the Segment end
+ * at its storage end time (unless the Composition's
+ * end marker is earlier).
+ */
+ void clearEndMarker();
+
+ /**
+ * Return the end marker in raw form, that is, a pointer to
+ * its value or null if none is set. Does not take the
+ * composition's end marker into account.
+ */
+ const timeT *getRawEndMarkerTime() const;
+
+
+ //////
+ //
+ // QUANTIZATION
+
+ /**
+ * Switch quantization on or off.
+ */
+ void setQuantization(bool quantize);
+
+ /**
+ * Find out whether quantization is on or off.
+ */
+ bool hasQuantization() const;
+
+ /**
+ * Set the quantization level.
+ * (This does not switch quantization on, if it's currently off,
+ * it only changes the level that will be used when it's next
+ * switched on.)
+ */
+ void setQuantizeLevel(timeT unit);
+
+ /**
+ * Get the quantizer currently in (or not in) use.
+ */
+ const BasicQuantizer *getQuantizer() const;
+
+
+
+ //////
+ //
+ // EVENT MANIPULATION
+
+ /**
+ * Inserts a single Event
+ */
+ iterator insert(Event *e);
+
+ /**
+ * Erases a single Event
+ */
+ void erase(iterator pos);
+
+ /**
+ * Erases a set of Events
+ */
+ void erase(iterator from, iterator to);
+
+ /**
+ * Clear the segment.
+ */
+ void clear() { erase(begin(), end()); }
+
+ /**
+ * Looks up an Event and if it finds it, erases it.
+ * @return true if the event was found and erased, false otherwise.
+ */
+ bool eraseSingle(Event*);
+
+ /**
+ * Returns an iterator pointing to that specific element,
+ * end() otherwise
+ */
+ iterator findSingle(Event*);
+
+ const_iterator findSingle(Event *e) const {
+ return const_iterator(((Segment *)this)->findSingle(e));
+ }
+
+ /**
+ * Returns an iterator pointing to the first element starting at
+ * or beyond the given absolute time
+ */
+ iterator findTime(timeT time);
+
+ const_iterator findTime(timeT time) const {
+ return const_iterator(((Segment *)this)->findTime(time));
+ }
+
+ /**
+ * Returns an iterator pointing to the first element starting at
+ * or before the given absolute time (so returns end() if the
+ * time precedes the first event, not if it follows the last one)
+ */
+ iterator findNearestTime(timeT time);
+
+ const_iterator findNearestTime(timeT time) const {
+ return const_iterator(((Segment *)this)->findNearestTime(time));
+ }
+
+
+ //////
+ //
+ // ADVANCED, ESOTERIC, or PLAIN STUPID MANIPULATION
+
+ /**
+ * Returns the range [start, end[ of events which are at absoluteTime
+ */
+ void getTimeSlice(timeT absoluteTime, iterator &start, iterator &end);
+
+ /**
+ * Returns the range [start, end[ of events which are at absoluteTime
+ */
+ void getTimeSlice(timeT absoluteTime, const_iterator &start, const_iterator &end) const;
+
+ /**
+ * Return the starting time of the bar that contains time t. This
+ * differs from Composition's bar methods in that it will truncate
+ * to the start and end times of this Segment, and is guaranteed
+ * to return the start time of a bar that is at least partially
+ * within this Segment.
+ *
+ * (See Composition for most of the generally useful bar methods.)
+ */
+ timeT getBarStartForTime(timeT t) const;
+
+ /**
+ * Return the ending time of the bar that contains time t. This
+ * differs from Composition's bar methods in that it will truncate
+ * to the start and end times of this Segment, and is guaranteed
+ * to return the end time of a bar that is at least partially
+ * within this Segment.
+ *
+ * (See Composition for most of the generally useful bar methods.)
+ */
+ timeT getBarEndForTime(timeT t) const;
+
+ /**
+ * Fill up the segment with rests, from the end of the last event
+ * currently on the segment to the endTime given. Actually, this
+ * does much the same as setEndTime does when it extends a segment.
+ */
+ void fillWithRests(timeT endTime);
+
+ /**
+ * Fill up a section within a segment with rests, from the
+ * startTime given to the endTime given. This may be useful if
+ * you have a pathological segment that contains notes already but
+ * not rests, but it is is likely to be dangerous unless you're
+ * quite careful about making sure the given range doesn't overlap
+ * any notes.
+ */
+ void fillWithRests(timeT startTime, timeT endTime);
+
+ /**
+ * For each series of contiguous rests found between the start and
+ * end time, replace the series of rests with another series of
+ * the same duration but composed of the theoretically "correct"
+ * rest durations to fill the gap, in the current time signature.
+ * The start and end time should be the raw absolute times of the
+ * events, not the notation-quantized versions, although the code
+ * will use the notation quantizations if it finds them.
+ */
+ void normalizeRests(timeT startTime, timeT endTime);
+
+ /**
+ * Return the clef in effect at the given time. This is a
+ * reasonably quick call.
+ */
+ Clef getClefAtTime(timeT time) const;
+
+ /**
+ * Return the clef in effect at the given time, and set ctime to
+ * the time of the clef change. This is a reasonably quick call.
+ */
+ Clef getClefAtTime(timeT time, timeT &ctime) const;
+
+ /**
+ * Return the key signature in effect at the given time. This is
+ * a reasonably quick call.
+ */
+ Key getKeyAtTime(timeT time) const;
+
+ /**
+ * Return the key signature in effect at the given time, and set
+ * ktime to the time of the key change. This is a reasonably
+ * quick call.
+ */
+ Key getKeyAtTime(timeT time, timeT &ktime) const;
+
+ /**
+ * Return the clef and key signature in effect at the beginning of the
+ * segment using the following rules :
+ *
+ * - Return the default clef if no clef change is preceding the first
+ * note or rest event,
+ * - else return the first clef event in the segment,
+ * - else return the default clef if the segment has no note event nor
+ * clef change in it.
+ *
+ * - Use the same rules with the key signature.
+ */
+ void getFirstClefAndKey(Clef &clef, Key &key);
+
+
+ //////
+ //
+ // REPEAT, DELAY, TRANSPOSE
+
+ // Is this Segment repeating?
+ //
+ bool isRepeating() const { return m_repeating; }
+ void setRepeating(bool value);
+
+ /**
+ * If this Segment is repeating, calculate and return the time at
+ * which the repeating stops. This is the start time of the
+ * following Segment on the same Track, if any, or else the end
+ * time of the Composition. If this Segment does not repeat, or
+ * the time calculated would precede the end time of the Segment,
+ * instead return the end time of the Segment.
+ */
+ timeT getRepeatEndTime() const;
+
+ timeT getDelay() const { return m_delay; }
+ void setDelay(timeT delay);
+
+ RealTime getRealTimeDelay() const { return m_realTimeDelay; }
+ void setRealTimeDelay(RealTime delay);
+
+ int getTranspose() const { return m_transpose; }
+ void setTranspose(int transpose);
+
+
+
+ //////
+ //
+ // AUDIO
+
+ // Get and set Audio file Id (see the AudioFileManager)
+ //
+ unsigned int getAudioFileId() const { return m_audioFileId; }
+ void setAudioFileId(unsigned int id);
+
+ unsigned int getUnstretchedFileId() const { return m_unstretchedFileId; }
+ void setUnstretchedFileId(unsigned int id);
+
+ float getStretchRatio() const { return m_stretchRatio; }
+ void setStretchRatio(float ratio);
+
+ // The audio start and end times tell us how far into
+ // audio file "m_audioFileId" this Segment starts and
+ // how far into the sample the Segment finishes.
+ //
+ RealTime getAudioStartTime() const { return m_audioStartTime; }
+ RealTime getAudioEndTime() const { return m_audioEndTime; }
+ void setAudioStartTime(const RealTime &time);
+ void setAudioEndTime(const RealTime &time);
+
+ bool isAutoFading() const { return m_autoFade; }
+ void setAutoFade(bool value);
+
+ RealTime getFadeInTime() const { return m_fadeInTime; }
+ void setFadeInTime(const RealTime &time);
+
+ RealTime getFadeOutTime() const { return m_fadeOutTime; }
+ void setFadeOutTime(const RealTime &time);
+
+ //////
+ //
+ // MISCELLANEOUS
+
+ /// Should only be called by Composition
+ void setComposition(Composition *composition) {
+ m_composition = composition;
+ }
+
+ // The runtime id for this segment
+ //
+ int getRuntimeId() const { return m_runtimeSegmentId; }
+
+ // Grid size for matrix view (and others probably)
+ //
+ void setSnapGridSize(int size) { m_snapGridSize = size; }
+ int getSnapGridSize() const { return m_snapGridSize; }
+
+ // Other view features we might want to set on this Segment
+ //
+ void setViewFeatures(int features) { m_viewFeatures = features; }
+ int getViewFeatures() const { return m_viewFeatures; }
+
+ /**
+ * The compare class used by Composition
+ */
+ struct SegmentCmp
+ {
+ bool operator()(const Segment* a, const Segment* b) const
+ {
+ if (a->getTrack() == b->getTrack())
+ return a->getStartTime() < b->getStartTime();
+
+ return a->getTrack() < b->getTrack();
+ }
+ };
+
+
+ /// For use by SegmentObserver objects like Composition & Staff
+ void addObserver(SegmentObserver *obs) { m_observers.push_back(obs); }
+
+ /// For use by SegmentObserver objects like Composition & Staff
+ void removeObserver(SegmentObserver *obs) { m_observers.remove(obs); }
+
+ // List of visible EventRulers attached to this segment
+ //
+ class EventRuler
+ {
+ public:
+ EventRuler(const std::string &type, int controllerValue, bool active):
+ m_type(type), m_controllerValue(controllerValue), m_active(active) {;}
+
+ std::string m_type; // Event Type
+ int m_controllerValue; // if controller event, then which value
+ bool m_active; // is this Ruler active?
+ };
+
+ typedef std::vector<EventRuler*> EventRulerList;
+ typedef std::vector<EventRuler*>::iterator EventRulerListIterator;
+ typedef std::vector<EventRuler*>::const_iterator EventRulerListConstIterator;
+
+ EventRulerList& getEventRulerList() { return m_eventRulerList; }
+ EventRuler* getEventRuler(const std::string &type, int controllerValue = -1);
+
+ void addEventRuler(const std::string &type, int controllerValue = -1, bool active = 0);
+ bool deleteEventRuler(const std::string &type, int controllerValue = -1);
+
+ //////
+ //
+ // REFRESH STATUS
+
+ // delegate part of the RefreshStatusArray API
+
+ unsigned int getNewRefreshStatusId() {
+ return m_refreshStatusArray.getNewRefreshStatusId();
+ }
+
+ SegmentRefreshStatus &getRefreshStatus(unsigned int id) {
+ return m_refreshStatusArray.getRefreshStatus(id);
+ }
+
+ void updateRefreshStatuses(timeT startTime, timeT endTime);
+
+private:
+ Composition *m_composition; // owns me, if it exists
+
+ timeT m_startTime;
+ timeT *m_endMarkerTime; // points to end time, or null if none
+ timeT m_endTime;
+
+ void updateEndTime(); // called after erase of item at end
+
+ TrackId m_track;
+ SegmentType m_type; // identifies Segment type
+ std::string m_label; // segment label
+
+ unsigned int m_colourIndex; // identifies Colour Index (default == 0)
+
+ mutable int m_id; // not id of Segment, but a value for return by getNextId
+
+ unsigned int m_audioFileId; // audio file ID (see AudioFileManager)
+ unsigned int m_unstretchedFileId;
+ float m_stretchRatio;
+ RealTime m_audioStartTime; // start time relative to start of audio file
+ RealTime m_audioEndTime; // end time relative to start of audio file
+
+ bool m_repeating; // is this segment repeating?
+
+ BasicQuantizer *const m_quantizer;
+ bool m_quantize;
+
+ int m_transpose; // all Events tranpose
+ timeT m_delay; // all Events delay
+ RealTime m_realTimeDelay; // all Events delay (the delays are cumulative)
+
+ int m_highestPlayable; // suggestion for highest playable note (notation)
+ int m_lowestPlayable; // suggestion for lowest playable note (notation)
+
+ RefreshStatusArray<SegmentRefreshStatus> m_refreshStatusArray;
+
+ struct ClefKeyCmp {
+ bool operator()(const Event *e1, const Event *e2) const;
+ };
+ typedef std::multiset<Event*, ClefKeyCmp> ClefKeyList;
+ mutable ClefKeyList *m_clefKeyList;
+
+ // EventRulers currently selected as visible on this segment
+ //
+ EventRulerList m_eventRulerList;
+
+private: // stuff to support SegmentObservers
+
+ typedef std::list<SegmentObserver *> ObserverSet;
+ ObserverSet m_observers;
+
+ void notifyAdd(Event *) const;
+ void notifyRemove(Event *) const;
+ void notifyAppearanceChange() const;
+ void notifyStartChanged(timeT);
+ void notifyEndMarkerChange(bool shorten);
+ void notifySourceDeletion() const;
+
+private: // assignment operator not provided
+
+ Segment &operator=(const Segment &);
+
+ // Used for mapping the segment to runtime things like PlayableAudioFiles at
+ // the sequencer.
+ //
+ int m_runtimeSegmentId;
+
+ // Remember the last used snap grid size for this segment
+ //
+ int m_snapGridSize;
+
+ // Switch for other view-specific features we want to remember in the segment
+ //
+ int m_viewFeatures;
+
+ // Audio autofading
+ //
+ bool m_autoFade;
+ RealTime m_fadeInTime;
+ RealTime m_fadeOutTime;
+
+};
+
+
+class SegmentObserver
+{
+public:
+ virtual ~SegmentObserver() {}
+
+ /**
+ * Called after the event has been added to the segment
+ */
+ virtual void eventAdded(const Segment *, Event *) { }
+
+ /**
+ * Called after the event has been removed from the segment,
+ * and just before it is deleted
+ */
+ virtual void eventRemoved(const Segment *, Event *) { }
+
+ /**
+ * Called after a change in the segment that will change the way its displays,
+ * like a label change for instance
+ */
+ virtual void appearanceChanged(const Segment *) { }
+
+ /**
+ * Called after a change that affects the start time of the segment
+ */
+ virtual void startChanged(const Segment *, timeT) { }
+
+ /**
+ * Called after the segment's end marker time has been
+ * changed
+ *
+ * @param shorten true if the marker change shortens the segment's duration
+ */
+ virtual void endMarkerTimeChanged(const Segment *, bool /*shorten*/) { }
+
+ /**
+ * Called from the segment dtor
+ * MUST BE IMPLEMENTED BY ALL OBSERVERS
+ */
+ virtual void segmentDeleted(const Segment *) = 0;
+};
+
+
+
+// an abstract base
+
+class SegmentHelper
+{
+protected:
+ SegmentHelper(Segment &t) : m_segment(t) { }
+ virtual ~SegmentHelper();
+
+ typedef Segment::iterator iterator;
+
+ Segment &segment() { return m_segment; }
+
+ Segment::iterator begin() { return segment().begin(); }
+ Segment::iterator end() { return segment().end(); }
+
+ bool isBeforeEndMarker(Segment::const_iterator i) {
+ return segment().isBeforeEndMarker(i);
+ }
+
+ Segment::iterator insert(Event *e) { return segment().insert(e); }
+ void erase(Segment::iterator i) { segment().erase(i); }
+
+private:
+ Segment &m_segment;
+};
+
+}
+
+
+#endif