diff options
Diffstat (limited to 'src/base/Segment.h')
-rw-r--r-- | src/base/Segment.h | 783 |
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 |