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/SegmentNotationHelper.h

589 lines
22 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 _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