/* Rosegarden A sequencer and musical notation editor. This program is Copyright 2000-2008 Guillaume Laurent , Chris Cannam , Richard Bown 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 #include #include #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 { 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 EventRulerList; typedef std::vector::iterator EventRulerListIterator; typedef std::vector::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 m_refreshStatusArray; struct ClefKeyCmp { bool operator()(const Event *e1, const Event *e2) const; }; typedef std::multiset ClefKeyList; mutable ClefKeyList *m_clefKeyList; // EventRulers currently selected as visible on this segment // EventRulerList m_eventRulerList; private: // stuff to support SegmentObservers typedef std::list 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