summaryrefslogtreecommitdiffstats
path: root/src/base/SegmentNotationHelper.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/base/SegmentNotationHelper.h')
-rw-r--r--src/base/SegmentNotationHelper.h591
1 files changed, 591 insertions, 0 deletions
diff --git a/src/base/SegmentNotationHelper.h b/src/base/SegmentNotationHelper.h
new file mode 100644
index 0000000..5094929
--- /dev/null
+++ b/src/base/SegmentNotationHelper.h
@@ -0,0 +1,591 @@
+// -*- 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_NOTATION_HELPER_H_
+#define _SEGMENT_NOTATION_HELPER_H_
+
+#include "Segment.h"
+
+namespace Rosegarden
+{
+
+class SegmentNotationHelper : protected SegmentHelper
+{
+public:
+ SegmentNotationHelper(Segment &t) : SegmentHelper(t) { }
+ virtual ~SegmentNotationHelper();
+
+ SegmentHelper::segment;
+
+ /**
+ * Set the NOTE_TYPE and NOTE_DOTS properties on the events
+ * in the segment. If startTime and endTime are equal, operates
+ * on the whole segment.
+ */
+ void setNotationProperties(timeT startTime = 0, timeT endTime = 0);
+
+ /**
+ * Return the notation absolute time plus the notation duration.
+ */
+ timeT getNotationEndTime(Event *e);
+
+ /**
+ * Return an iterator pointing at the first event in the segment
+ * to have an absolute time of t or later. (Most of the time, the
+ * non-notation absolute times should be used as reference
+ * timings; this and the next function are provided for
+ * completeness, but in most cases if you're about to call them
+ * you should ask yourself why.)
+ */
+ iterator findNotationAbsoluteTime(timeT t);
+
+ /**
+ * Return an iterator pointing at the last event in the segment
+ * to have an absolute time of t or earlier. (Most of the time,
+ * the non-notation absolute times should be used as reference
+ * timings; this and the previous function are provided for
+ * completeness, but in most cases if you're about to call them
+ * you should ask yourself why.)
+ */
+ iterator findNearestNotationAbsoluteTime(timeT t);
+
+
+ /**
+ * Looks for another note immediately following the one pointed to
+ * by the given iterator, and (if matchPitch is true) of the same
+ * pitch, and returns an iterator pointing to that note. Returns
+ * end() if there is no such note.
+ *
+ * The notes are considered "adjacent" if the quantized start
+ * time of one matches the quantized end time of the other, unless
+ * allowOverlap is true in which case overlapping notes are also
+ * considered adjacent so long as one does not completely enclose
+ * the other.
+ */
+ iterator getNextAdjacentNote(iterator i,
+ bool matchPitch = true,
+ bool allowOverlap = true);
+
+
+ /**
+ * Looks for another note immediately preceding the one pointed to
+ * by the given iterator, and (if matchPitch is true) of the same
+ * pitch, and returns an iterator pointing to that note. Returns
+ * end() if there is no such note.
+ *
+ * rangeStart gives a bound to the distance that will be scanned
+ * to find events -- no event with starting time earlier than that
+ * will be considered. (This method has no other way to know when
+ * to stop scanning; potentially the very first note in the segment
+ * could turn out to be adjacent to the very last one.)
+ *
+ * The notes are considered "adjacent" if the quantized start
+ * time of one matches the quantized end time of the other, unless
+ * allowOverlap is true in which case overlapping notes are also
+ * considered adjacent so long as one does not completely enclose
+ * the other.
+ */
+ iterator getPreviousAdjacentNote(iterator i,
+ timeT rangeStart = 0,
+ bool matchPitch = true,
+ bool allowOverlap = true);
+
+
+ /**
+ * Returns an iterator pointing to the next contiguous element of
+ * the same type (note or rest) as the one passed as argument, if
+ * any. Returns end() otherwise.
+ *
+ * (for instance if the argument points to a note and the next
+ * element is a rest, end() will be returned)
+ *
+ * Note that if the iterator points to a note, the "contiguous"
+ * iterator returned may point to a note that follows the first
+ * one, overlaps with it, shares a starting time (i.e. they're
+ * both in the same chord) or anything else. "Contiguous" refers
+ * only to their locations in the segment's event container,
+ * which normally means what you expect for rests but not notes.
+ *
+ * See also SegmentNotationHelper::getNextAdjacentNote.
+ */
+ iterator findContiguousNext(iterator);
+
+ /**
+ * Returns an iterator pointing to the previous contiguous element
+ * of the same type (note or rest) as the one passed as argument,
+ * if any. Returns end() otherwise.
+ *
+ * (for instance if the argument points to a note and the previous
+ * element is a rest, end() will be returned)
+ *
+ * Note that if the iterator points to a note, the "contiguous"
+ * iterator returned may point to a note that precedes the first
+ * one, overlaps with it, shares a starting time (i.e. they're
+ * both in the same chord) or anything else. "Contiguous" refers
+ * only to their locations in the segment's event container,
+ * which normally means what you expect for rests but not notes.
+ *
+ * See also SegmentNotationHelper::getPreviousAdjacentNote.
+ */
+ iterator findContiguousPrevious(iterator);
+
+ /**
+ * Returns true if the iterator points at a note in a chord
+ * e.g. if there are more notes at the same absolute time
+ */
+ bool noteIsInChord(Event *note);
+
+ /**
+ * Returns an iterator pointing to the note that this one is tied
+ * with, in the forward direction if goForwards or back otherwise.
+ * Returns end() if none.
+ *
+ * Untested and probably marked-for-expiry -- prefer
+ * SegmentPerformanceHelper::getTiedNotes
+ */
+ iterator getNoteTiedWith(Event *note, bool goForwards);
+
+
+ /**
+ * Checks whether it's reasonable to split a single event
+ * of duration a+b into two events of durations a and b, for some
+ * working definition of "reasonable".
+ *
+ * You should pass note-quantized durations into this method
+ */
+ bool isSplitValid(timeT a, timeT b);
+
+
+ /**
+ * Splits events in the [from, to[ interval into
+ * tied events of duration baseDuration + events of duration R,
+ * with R being equal to the events' initial duration minus baseDuration
+ *
+ * The events in [from, to[ must all be at the same absolute time
+ *
+ * Does not check "reasonableness" of expansion first
+ *
+ * Events may be notes or rests (rests will obviously not be tied)
+ *
+ * @return iterator pointing at the last inserted event. Also
+ * modifies from to point at the first split event (the original
+ * iterator would have been invalidated).
+ */
+ iterator splitIntoTie(iterator &from, iterator to, timeT baseDuration);
+
+
+ /**
+ * Splits (splits) events in the same timeslice as that pointed
+ * to by i into tied events of duration baseDuration + events of
+ * duration R, with R being equal to the events' initial duration
+ * minus baseDuration
+ *
+ * Does not check "reasonableness" of expansion first
+ *
+ * Events may be notes or rests (rests will obviously not be tied)
+ *
+ * @return iterator pointing at the last inserted event. Also
+ * modifies i to point at the first split event (the original
+ * iterator would have been invalidated).
+ */
+ iterator splitIntoTie(iterator &i, timeT baseDuration);
+
+
+ /**
+ * Returns true if Events of durations a and b can reasonably be
+ * collapsed into a single one of duration a+b, for some
+ * definition of "reasonably". For use by collapseRestsIfValid
+ *
+ * You should pass note-quantized durations into this method
+ */
+ bool isCollapseValid(timeT a, timeT b);
+
+ /**
+ * If possible, collapses the rest event with the following or
+ * previous one.
+ *
+ * @return true if collapse was done, false if it wasn't reasonable
+ *
+ * collapseForward is set to true if the collapse was with the
+ * following element, false if it was with the previous one
+ */
+ bool collapseRestsIfValid(Event*, bool& collapseForward);
+
+ /**
+ * Inserts a note, doing all the clever split/merge stuff as
+ * appropriate. Requires segment to be in a composition. Returns
+ * iterator pointing to last event inserted (there may be more
+ * than one, as note may have had to be split)
+ *
+ * This method will only work correctly if there is a note or
+ * rest event already starting at absoluteTime.
+ */
+ iterator insertNote(timeT absoluteTime, Note note, int pitch,
+ Accidental explicitAccidental);
+
+ /**
+ * Inserts a note, doing all the clever split/merge stuff as
+ * appropriate. Requires segment to be in a composition. Returns
+ * iterator pointing to last event inserted (there may be more
+ * than one, as note may have had to be split)
+ *
+ * This method will only work correctly if there is a note or
+ * rest event already starting at the model event's absoluteTime.
+ *
+ * Passing a model event has the advantage over the previous
+ * method of allowing additional properties to be supplied. The
+ * model event will be copied but not itself used; the caller
+ * continues to own it and should release it after return.
+ */
+ iterator insertNote(Event *modelEvent);
+
+ /**
+ * Inserts a rest, doing all the clever split/merge stuff as
+ * appropriate. Requires segment to be in a composition.
+ * Returns iterator pointing to last event inserted (there
+ * may be more than one, as rest may have had to be split)
+ *
+ * This method will only work correctly if there is a note or
+ * rest event already starting at absoluteTime.
+ */
+ iterator insertRest(timeT absoluteTime, Note note);
+
+ /**
+ * Insert a clef.
+ * Returns iterator pointing to clef.
+ */
+ iterator insertClef(timeT absoluteTime, Clef clef);
+
+ /**
+ * Insert a key.
+ * Returns iterator pointing to key.
+ */
+ iterator insertKey(timeT absoluteTime, Key key);
+
+ /**
+ * Insert a text event.
+ * Returns iterator pointing to text event.
+ */
+ iterator insertText(timeT absoluteTime, Text text);
+
+ /**
+ * Deletes a note, doing all the clever split/merge stuff as
+ * appropriate. Requires segment to be in a composition.
+ */
+ void deleteNote(Event *e, bool collapseRest = false);
+
+ /**
+ * Deletes a rest, doing all the clever split/merge stuff as
+ * appropriate. Requires segment to be in a composition.
+ *
+ * @return whether the rest could be deleted -- a rest can only
+ * be deleted if there's a suitable rest next to it to merge it
+ * with.
+ */
+ bool deleteRest(Event *e);
+
+ /**
+ * Deletes an event. If the event is a note or a rest, calls
+ * deleteNote or deleteRest.
+ *
+ * @return whether the event was deleted (always true, unless the
+ * event is a rest).
+ *
+ * @see deleteRest, deleteNote
+ */
+ bool deleteEvent(Event *e, bool collapseRest = false);
+
+ /**
+ * Check whether a note or rest event has a duration that can be
+ * represented by a single note-type. (If not, the code that's
+ * doing the check might wish to split the event.)
+ *
+ * If dots is specified, a true value will only be returned if the
+ * best-fit note has no more than that number of dots. e.g. if
+ * dots = 0, only notes that are viable without the use of dots
+ * will be acceptable. The default is whatever the segment's
+ * quantizer considers acceptable (probably either 1 or 2 dots).
+ */
+ bool isViable(Event *e, int dots = -1) {
+ return isViable(e->getDuration(), dots);
+ }
+
+ /**
+ * Check whether a duration can be represented by a single
+ * note-type. (If not, the code that's doing the check might wish
+ * to split the duration.)
+ *
+ * If dots is specified, a true value will only be returned if the
+ * best-fit note has no more than that number of dots. e.g. if
+ * dots = 0, only notes that are viable without the use of dots
+ * will be acceptable. The default is whatever the segment's
+ * quantizer considers acceptable (probably either 1 or 2 dots).
+ */
+ bool isViable(timeT duration, int dots = -1);
+
+
+ /**
+ * Given an iterator pointing to a rest, split that rest up
+ * according to the durations returned by TimeSignature's
+ * getDurationListForInterval
+ */
+ void makeRestViable(iterator i);
+
+
+ /**
+ * Split notes and rests up into tied notes or shorter rests of
+ * viable lengths (longest possible viable duration first, then
+ * longest possible viable component of remainder &c). Also
+ * optionally splits notes and rests at barlines -- this is
+ * actually the most common user-visible use of this function.
+ */
+ void makeNotesViable(iterator i, iterator j, bool splitAtBars = true);
+
+
+ /**
+ * As above but given a range in time rather than iterators.
+ */
+ void makeNotesViable(timeT startTime, timeT endTime,
+ bool splitAtBars = true);
+
+
+ /**
+ * Give all events between the start of the timeslice containing
+ * from and the start of the timeslice containing to the same new
+ * group id and the given type.
+ *
+ * Do not use this for making tuplet groups, unless the events
+ * in the group already have the other tuplet properties or you
+ * intend to add those yourself. Use makeTupletGroup instead.
+ */
+ void makeBeamedGroup(timeT from, timeT to, std::string type);
+
+ /**
+ * Give all events between the start of the timeslice containing
+ * from and the start of the timeslice containing to the same new
+ * group id and the given type.
+ *
+ * Do not use this for making tuplet groups, unless the events
+ * in the group already have the other tuplet properties or you
+ * intend to add those yourself. Use makeTupletGroup instead.
+ */
+ void makeBeamedGroup(iterator from, iterator to, std::string type);
+
+ /**
+ * Give all events between from and to the same new group id and
+ * the given type.
+ *
+ * Use makeBeamedGroup for normal notes. This function is usually
+ * used for groups of grace notes, which are equal in time and
+ * distinguished by subordering.
+ *
+ * Do not use this for making tuplet groups, unless the events
+ * in the group already have the other tuplet properties or you
+ * intend to add those yourself.
+ */
+ void makeBeamedGroupExact(iterator from, iterator to, std::string type);
+
+
+ /**
+ * Make a beamed group of tuplet type, whose tuplet properties are
+ * specified as "(untupled-count) notes of duration (unit) played
+ * in the time of (tupled-count)". For example, a quaver triplet
+ * group could be specified with untupled = 3, tupled = 2, unit =
+ * (the duration of a quaver).
+ *
+ * The group will start at the beginning of the timeslice containing
+ * the time t, and will be constructed by compressing the appropriate
+ * number of following notes into the tuplet time, and filling the
+ * space that this compression left behind (after the group) with
+ * rests. The results may be unexpected if overlapping events are
+ * present.
+ */
+ void makeTupletGroup(timeT t, int untupled, int tupled, timeT unit);
+
+
+ /**
+ * Divide the notes between the start of the bar containing
+ * from and the end of the bar containing to up into sensible
+ * beamed groups and give each group the right group properties
+ * using makeBeamedGroup. Requires segment to be in a composition.
+ */
+ void autoBeam(timeT from, timeT to, std::string type);
+
+ /**
+ * Divide the notes between the start of the bar containing
+ * from and the end of the bar containing to up into sensible
+ * beamed groups and give each group the right group properties
+ * using makeBeamedGroup. Requires segment to be in a composition.
+ */
+ void autoBeam(iterator from, iterator to, std::string type);
+
+
+ /**
+ * Clear the group id and group type from all events between the
+ * start of the timeslice containing from and the start of the
+ * timeslice containing to
+ */
+ void unbeam(timeT from, timeT to);
+
+ /**
+ * Clear the group id and group type from all events between the
+ * start of the timeslice containing from and the start of the
+ * timeslice containing to
+ */
+ void unbeam(iterator from, iterator to);
+
+ /**
+ * Guess which clef a section of music is supposed to be in,
+ * ignoring any clef events actually found in the section.
+ */
+ Clef guessClef(iterator from, iterator to);
+
+
+ /**
+ * Removes all rests starting at \a time for \a duration,
+ * splitting the last rest if needed.
+ *
+ * Modifies duration to the actual duration of the series
+ * of rests that has been changed by this action (i.e. if
+ * the last rest was split, duration will be extended to
+ * include the second half of this rest). This is intended
+ * to be of use when calculating the extents of a command
+ * for undo/refresh purposes.
+ *
+ * If there's an event which is not a rest in this interval,
+ * returns false and sets duration to the maximum duration
+ * that would have succeeded.
+ *
+ * If testOnly is true, does not actually remove any rests;
+ * just checks whether the rests can be removed and sets
+ * duration and the return value appropriately.
+ *
+ * (Used for Event pasting.)
+ */
+ bool removeRests(timeT time, timeT &duration, bool testOnly = false);
+
+
+ /**
+ * 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 longest possible valid
+ * rest plus the remainder
+ */
+ void collapseRestsAggressively(timeT startTime, timeT endTime);
+
+
+ /**
+ * Locate the given event and, if it's a note, collapse it with
+ * any following adjacent note of the same pitch, so long as its
+ * start time is before the the given limit. Does not care
+ * whether the resulting note is viable.
+ *
+ * Returns an iterator pointing to the event that replaced the
+ * original one if a collapse happened, segment.end() if no
+ * collapse or event not found
+ */
+ iterator collapseNoteAggressively(Event *, timeT rangeEnd);
+
+
+
+ std::pair<Event *, Event *> splitPreservingPerformanceTimes(Event *e,
+ timeT q1);
+
+ /**
+ * Look for examples of overlapping notes within the given range,
+ * and split each into chords with some tied notes.
+ */
+ void deCounterpoint(timeT startTime, timeT endTime);
+
+ /**
+ * A rather specialised function: Add a slur to every beamed group.
+ * If legatoOnly is true, add a slur only to those beamed groups
+ * in which every note except the last has a tenuto mark already
+ * (and remove that mark).
+ * This is basically intended as a post-notation-quantization-auto-
+ * beam step.
+ */
+ void autoSlur(timeT startTime, timeT endTime, bool legatoOnly);
+
+
+protected:
+ const Quantizer &basicQuantizer();
+ const Quantizer &notationQuantizer();
+
+ /**
+ * Collapse multiple consecutive rests into one, in preparation
+ * for insertion of a note (whose duration may exceed that of the
+ * first rest) at the given position. The resulting rest event
+ * may have a duration that is not expressible as a single note
+ * type, and may therefore require splitting again after the
+ * insertion.
+ *
+ * Returns position at which the collapse ended (i.e. the first
+ * uncollapsed event)
+ */
+ iterator collapseRestsForInsert(iterator firstRest, timeT desiredDuration);
+
+
+ /// for use by insertNote and insertRest
+ iterator insertSomething(iterator position, int duration,
+ Event *modelEvent, bool tiedBack);
+
+ /// for use by insertSomething
+ iterator insertSingleSomething(iterator position, int duration,
+ Event *modelEvent, bool tiedBack);
+
+ /// for use by insertSingleSomething
+ void setInsertedNoteGroup(Event *e, iterator i);
+
+ /// for use by makeBeamedGroup
+ void makeBeamedGroupAux(iterator from, iterator to, std::string type,
+ bool groupGraces);
+
+ /// for use by unbeam
+ void unbeamAux(iterator from, iterator to);
+
+ /// for use by autoBeam
+
+ void autoBeamBar(iterator from, iterator to, TimeSignature timesig,
+ std::string type);
+
+ void autoBeamBar(iterator from, iterator to, timeT average,
+ timeT minimum, timeT maximum, std::string type);
+
+ /// used by autoBeamAux (duplicate of private method in Segment)
+ bool hasEffectiveDuration(iterator i);
+
+ typedef void (SegmentNotationHelper::*Reorganizer)(timeT, timeT,
+ std::vector<Event *>&);
+
+ void reorganizeRests(timeT, timeT, Reorganizer);
+
+ /// for use by normalizeRests
+ void normalizeContiguousRests(timeT, timeT, std::vector<Event *>&);
+
+ /// for use by collapseRestsAggressively
+ void mergeContiguousRests(timeT, timeT, std::vector<Event *>&);
+};
+
+}
+
+#endif