// -*- c-basic-offset: 4 -*- /* 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 _SETS_H_ #define _SETS_H_ #include #include #include "Event.h" #include "Segment.h" #include "CompositionTimeSliceAdapter.h" #include "BaseProperties.h" #include "NotationTypes.h" #include "MidiTypes.h" #include "Quantizer.h" namespace Rosegarden { class Quantizer; /** * A "set" in Rosegarden terminology is a collection of elements found * in a container (indeed, a subset of that container) all of which * share a particular property and are located near to one another: * generally either contiguous or within the same bar. The elements * are most usually Events and the container most usually a Segment, * and although this does not have to be the case (for other examples * see gui/notationsets.h), the elements do have to be convertible to * Events somehow. * * To construct a set requires (at least) a container reference plus * an iterator into that container. The constructor (or more * precisely the initialise() method called by the constructor) then * scans the surrounding area of the list for the maximal set of * contiguous or within-the-same-bar elements before and after the * passed-in iterator such that all elements are in the same set * (i.e. Chord, BeamedGroup etc) as the one that the passed-in * iterator pointed to. * * The extents of the set within the list can then be discovered via * getInitialElement() and getFinalElement(). If the iterator passed * in to the constructor was at end() or did not point to an element * that could be a member of this kind of set, getInitialElement() * will return end(); if the passed-in iterator pointed to the only * member of this set, getInitialElement() and getFinalElement() will * be equal. * * These classes are not intended to be stored anywhere; all they * contain is iterators into the main container, and those might not * persist. Instead you should create these on-the-fly when you want, * for example, to consider a note as part of a chord; and then you * should let them expire when you've finished with them. */ template class AbstractSet // abstract base { public: typedef typename Container::iterator Iterator; virtual ~AbstractSet() { } /** * getInitialElement() returns end() if there are no elements in * the set. getInitialElement() == getFinalElement() if there is * only one element in the set */ Iterator getInitialElement() const { return m_initial; } Iterator getFinalElement() const { return m_final; } /// only return note elements; will return end() if there are none Iterator getInitialNote() const { return m_initialNote; } Iterator getFinalNote() const { return m_finalNote; } /** * only elements with duration > 0 are candidates for shortest and * longest; these will return end() if there are no such elements */ Iterator getLongestElement() const { return m_longest; } Iterator getShortestElement() const { return m_shortest; } /// these will return end() if there are no note elements in the set Iterator getHighestNote() const { return m_highest; } Iterator getLowestNote() const { return m_lowest; } virtual bool contains(const Iterator &) const = 0; /// Return the pointed-to element, in Event form (public to work around gcc-2.95 bug) static Event *getAsEvent(const Iterator &i); protected: AbstractSet(Container &c, Iterator elementInSet, const Quantizer *); void initialise(); /// Return true if this element is not definitely beyond bounds of set virtual bool test(const Iterator &i) = 0; /// Return true if this element, known to test() true, is a set member virtual bool sample(const Iterator &i, bool goingForwards); Container &getContainer() const { return m_container; } const Quantizer &getQuantizer() const { return *m_quantizer; } // Data members: Container &m_container; Iterator m_initial, m_final, m_initialNote, m_finalNote; Iterator m_shortest, m_longest, m_highest, m_lowest; Iterator m_baseIterator; const Quantizer *m_quantizer; }; /** * Chord is subclassed from a vector of iterators; this vector * contains iterators pointing at all the notes in the chord, in * ascending order of pitch. You can also track through all the * events in the chord by iterating from getInitialElement() to * getFinalElement(), but this will only get them in the order in * which they appear in the original container. * * However, the notes in a chord might not be contiguous events in the * container, as there could be other zero-duration events such as * controllers (or even conceivably some short rests) between notes in * the same chord, depending on the quantization settings. The Chord * itself only contains iterators pointing at the notes, so if you * want to iterate through all events spanned by the Chord, iterate * from getInitialElement() to getFinalElement() instead. * * This class can tell you various things about the chord it * describes, but not everything. It can't tell you whether the * chord has a stem, for example, because that depends on the * notation style and system in use. See gui/notationsets.h * for a NotationChord class (subclassed from this) that can. */ template class GenericChord : public AbstractSet, public std::vector { public: typedef typename Container::iterator Iterator; /* You only need to provide the clef and key if the notes making up your chord lack HEIGHT_ON_STAFF properties, in which case this constructor will write those properties in to the chord for you */ GenericChord(Container &c, Iterator elementInChord, const Quantizer *quantizer, PropertyName stemUpProperty = PropertyName::EmptyPropertyName); virtual ~GenericChord(); virtual int getMarkCountForChord() const; virtual std::vector getMarksForChord() const; virtual std::vector getPitches() const; virtual bool contains(const Iterator &) const; /** * Return an iterator pointing to the previous note before this * chord, or container's end() if there is no previous note. */ virtual Iterator getPreviousNote(); /** * Return an iterator pointing to the next note after this chord, * or container's end() if there is no next note. Remember this * class can't know about Segment end marker times, so if your * container is a Segment, check the returned note is actually * before the end marker. */ virtual Iterator getNextNote(); /** * It's possible for a chord to surround (in the segment) elements * that are not members of the chord. This function returns an * iterator pointing to the first of those after the iterator that * was passed to the chord's constructor. If there are none, it * returns the container's end(). */ virtual Iterator getFirstElementNotInChord(); virtual int getSubOrdering() { return m_subordering; } protected: virtual bool test(const Iterator&); virtual bool sample(const Iterator&, bool goingForwards); class PitchGreater { public: bool operator()(const Iterator &a, const Iterator &b); }; void copyGroupProperties(Event *e0, Event *e1) const; //--------------- Data members --------------------------------- PropertyName m_stemUpProperty; timeT m_time; int m_subordering; Iterator m_firstReject; }; /// /// Implementation only from here on. /// // forward declare hack functions -- see Sets.C for an explanation extern long get__Int(Event *e, const PropertyName &name); extern bool get__Bool(Event *e, const PropertyName &name); extern std::string get__String(Event *e, const PropertyName &name); extern bool get__Int(Event *e, const PropertyName &name, long &ref); extern bool get__Bool(Event *e, const PropertyName &name, bool &ref); extern bool get__String(Event *e, const PropertyName &name, std::string &ref); extern bool isPersistent__Bool(Event *e, const PropertyName &name); extern void setMaybe__Int(Event *e, const PropertyName &name, long value); extern void setMaybe__String(Event *e, const PropertyName &name, const std::string &value); template AbstractSet::AbstractSet(Container &c, Iterator i, const Quantizer *q): m_container(c), m_initial(c.end()), m_final(c.end()), m_initialNote(c.end()), m_finalNote(c.end()), m_shortest(c.end()), m_longest(c.end()), m_highest(c.end()), m_lowest(c.end()), m_baseIterator(i), m_quantizer(q) { // ... } template void AbstractSet::initialise() { if (m_baseIterator == getContainer().end() || !test(m_baseIterator)) return; m_initial = m_baseIterator; m_final = m_baseIterator; sample(m_baseIterator, true); if (getAsEvent(m_baseIterator)->isa(Note::EventType)) { m_initialNote = m_baseIterator; m_finalNote = m_baseIterator; } Iterator i, j; // first scan back to find an element not in the desired set, // sampling everything as far back as the one after it for (i = j = m_baseIterator; i != getContainer().begin() && test(--j); i = j){ if (sample(j, false)) { m_initial = j; if (getAsEvent(j)->isa(Note::EventType)) { m_initialNote = j; if (m_finalNote == getContainer().end()) { m_finalNote = j; } } } } j = m_baseIterator; // then scan forwards to find an element not in the desired set, // sampling everything as far forward as the one before it for (i = j = m_baseIterator; ++j != getContainer().end() && test(j); i = j) { if (sample(j, true)) { m_final = j; if (getAsEvent(j)->isa(Note::EventType)) { m_finalNote = j; if (m_initialNote == getContainer().end()) { m_initialNote = j; } } } } } template bool AbstractSet::sample(const Iterator &i, bool) { const Quantizer &q(getQuantizer()); Event *e = getAsEvent(i); timeT d(q.getQuantizedDuration(e)); if (e->isa(Note::EventType) || d > 0) { if (m_longest == getContainer().end() || d > q.getQuantizedDuration(getAsEvent(m_longest))) { // std::cerr << "New longest in set at duration " << d << " and time " << e->getAbsoluteTime() << std::endl; m_longest = i; } if (m_shortest == getContainer().end() || d < q.getQuantizedDuration(getAsEvent(m_shortest))) { // std::cerr << "New shortest in set at duration " << d << " and time " << e->getAbsoluteTime() << std::endl; m_shortest = i; } } if (e->isa(Note::EventType)) { long p = get__Int(e, BaseProperties::PITCH); if (m_highest == getContainer().end() || p > get__Int(getAsEvent(m_highest), BaseProperties::PITCH)) { // std::cerr << "New highest in set at pitch " << p << " and time " << e->getAbsoluteTime() << std::endl; m_highest = i; } if (m_lowest == getContainer().end() || p < get__Int(getAsEvent(m_lowest), BaseProperties::PITCH)) { // std::cerr << "New lowest in set at pitch " << p << " and time " << e->getAbsoluteTime() << std::endl; m_lowest = i; } } return true; } ////////////////////////////////////////////////////////////////////// template GenericChord::GenericChord(Container &c, Iterator i, const Quantizer *q, PropertyName stemUpProperty) : AbstractSet(c, i, q), m_stemUpProperty(stemUpProperty), m_time(q->getQuantizedAbsoluteTime(getAsEvent(i))), m_subordering(getAsEvent(i)->getSubOrdering()), m_firstReject(c.end()) { AbstractSet::initialise(); if (std::vector::size() > 1) { std::stable_sort(std::vector::begin(), std::vector::end(), PitchGreater()); } /*!!! this should all be removed ultimately // std::cerr << "GenericChord::GenericChord: pitches are:" << std::endl; int prevPitch = -999; for (unsigned int i = 0; i < size(); ++i) { try { int pitch = getAsEvent((*this)[i])->get(BaseProperties::PITCH); // std::cerr << i << ": " << pitch << std::endl; if (pitch < prevPitch) { cerr << "ERROR: Pitch less than previous pitch (" << pitch << " < " << prevPitch << ")" << std::endl; throw(1); } } catch (Event::NoData) { std::cerr << i << ": no pitch property" << std::endl; } } */ } template GenericChord::~GenericChord() { } template bool GenericChord::test(const Iterator &i) { Event *e = getAsEvent(i); if (AbstractSet:: getQuantizer().getQuantizedAbsoluteTime(e) != m_time) { return false; } if (e->getSubOrdering() != m_subordering) { return false; } // We permit note or rest events etc here, because if a chord is a // little staggered (for performance reasons) then it's not at all // unlikely we could get other events (even rests) in the middle // of it. So long as sample() only permits notes, we should be // okay with this. // // (We're really only refusing things like clef and key events // here, though it's slightly quicker [since most things are // notes] and perhaps a bit safer to do it by testing for // inclusion rather than exclusion.) std::string type(e->getType()); return (type == Note::EventType || type == Note::EventRestType || type == Text::EventType || type == Indication::EventType || type == PitchBend::EventType || type == Controller::EventType || type == KeyPressure::EventType || type == ChannelPressure::EventType); } template bool GenericChord::sample(const Iterator &i, bool goingForwards) { Event *e1 = getAsEvent(i); if (!e1->isa(Note::EventType)) { if (goingForwards && m_firstReject == AbstractSet::getContainer().end()) m_firstReject = i; return false; } if (singleStaff) { // Two notes that would otherwise be in a chord but are // explicitly in different groups, or have stems pointing in // different directions by design, or have substantially // different x displacements, count as separate chords. // Per #930473 ("Inserting notes into beamed chords is // broken"), if one note is in a group and the other isn't, // that's no problem. In fact we should actually modify the // one that isn't so as to suggest that it is. if (AbstractSet::m_baseIterator != AbstractSet::getContainer().end()) { Event *e0 = getAsEvent(AbstractSet::m_baseIterator); if (!(m_stemUpProperty == PropertyName::EmptyPropertyName)) { if (e0->has(m_stemUpProperty) && e1->has(m_stemUpProperty) && isPersistent__Bool(e0, m_stemUpProperty) && isPersistent__Bool(e1, m_stemUpProperty) && get__Bool(e0, m_stemUpProperty) != get__Bool(e1, m_stemUpProperty)) { if (goingForwards && m_firstReject == AbstractSet::getContainer().end()) m_firstReject = i; return false; } } long dx0 = 0, dx1 = 0; get__Int(e0, BaseProperties::DISPLACED_X, dx0); get__Int(e1, BaseProperties::DISPLACED_X, dx1); if (abs(dx0 - dx1) >= 700) { if (goingForwards && m_firstReject == AbstractSet::getContainer().end()) m_firstReject = i; return false; } if (e0->has(BaseProperties::BEAMED_GROUP_ID)) { if (e1->has(BaseProperties::BEAMED_GROUP_ID)) { if (get__Int(e1, BaseProperties::BEAMED_GROUP_ID) != get__Int(e0, BaseProperties::BEAMED_GROUP_ID)) { if (goingForwards && m_firstReject == AbstractSet::getContainer().end()) m_firstReject = i; return false; } } else { copyGroupProperties(e0, e1); // #930473 } } else { if (e1->has(BaseProperties::BEAMED_GROUP_ID)) { copyGroupProperties(e1, e0); // #930473 } } } } AbstractSet::sample(i, goingForwards); push_back(i); return true; } template void GenericChord::copyGroupProperties(Event *e0, Event *e1) const { if (e0->has(BaseProperties::BEAMED_GROUP_TYPE)) { setMaybe__String(e1, BaseProperties::BEAMED_GROUP_TYPE, get__String(e0, BaseProperties::BEAMED_GROUP_TYPE)); } if (e0->has(BaseProperties::BEAMED_GROUP_ID)) { setMaybe__Int(e1, BaseProperties::BEAMED_GROUP_ID, get__Int(e0, BaseProperties::BEAMED_GROUP_ID)); } if (e0->has(BaseProperties::BEAMED_GROUP_TUPLET_BASE)) { setMaybe__Int(e1, BaseProperties::BEAMED_GROUP_TUPLET_BASE, get__Int(e0, BaseProperties::BEAMED_GROUP_TUPLET_BASE)); } if (e0->has(BaseProperties::BEAMED_GROUP_TUPLED_COUNT)) { setMaybe__Int(e1, BaseProperties::BEAMED_GROUP_TUPLED_COUNT, get__Int(e0, BaseProperties::BEAMED_GROUP_TUPLED_COUNT)); } if (e0->has(BaseProperties::BEAMED_GROUP_UNTUPLED_COUNT)) { setMaybe__Int(e1, BaseProperties::BEAMED_GROUP_UNTUPLED_COUNT, get__Int(e0, BaseProperties::BEAMED_GROUP_UNTUPLED_COUNT)); } } template int GenericChord::getMarkCountForChord() const { // need to weed out duplicates std::set cmarks; for (unsigned int i = 0; i < std::vector::size(); ++i) { Event *e = getAsEvent((*this)[i]); std::vector marks(Marks::getMarks(*e)); for (std::vector::iterator j = marks.begin(); j != marks.end(); ++j) { cmarks.insert(*j); } } return cmarks.size(); } template std::vector GenericChord::getMarksForChord() const { std::vector cmarks; for (unsigned int i = 0; i < std::vector::size(); ++i) { Event *e = getAsEvent((*this)[i]); std::vector marks(Marks::getMarks(*e)); for (std::vector::iterator j = marks.begin(); j != marks.end(); ++j) { // We permit multiple identical fingering marks per chord, // but not any other sort if (Marks::isFingeringMark(*j) || std::find(cmarks.begin(), cmarks.end(), *j) == cmarks.end()) { cmarks.push_back(*j); } } } return cmarks; } template std::vector GenericChord::getPitches() const { std::vector pitches; for (typename std::vector::const_iterator i = std::vector::begin(); i != std::vector::end(); ++i) { if (getAsEvent(*i)->has(BaseProperties::PITCH)) { int pitch = get__Int (getAsEvent(*i), BaseProperties::PITCH); if (pitches.size() > 0 && pitches[pitches.size()-1] == pitch) continue; pitches.push_back(pitch); } } return pitches; } template bool GenericChord::contains(const Iterator &itr) const { for (typename std::vector::const_iterator i = std::vector::begin(); i != std::vector::end(); ++i) { if (*i == itr) return true; } return false; } template typename GenericChord::Iterator GenericChord::getPreviousNote() { Iterator i(AbstractSet::getInitialElement()); while (1) { if (i == AbstractSet::getContainer().begin()) return AbstractSet::getContainer().end(); --i; if (getAsEvent(i)->isa(Note::EventType)) { return i; } } } template typename GenericChord::Iterator GenericChord::getNextNote() { Iterator i(AbstractSet::getFinalElement()); while ( i != AbstractSet::getContainer().end() && ++i != AbstractSet::getContainer().end()) { if (getAsEvent(i)->isa(Note::EventType)) { return i; } } return AbstractSet::getContainer().end(); } template typename GenericChord::Iterator GenericChord::getFirstElementNotInChord() { return m_firstReject; } template bool GenericChord::PitchGreater::operator()(const Iterator &a, const Iterator &b) { try { long ap = get__Int(getAsEvent(a), BaseProperties::PITCH); long bp = get__Int(getAsEvent(b), BaseProperties::PITCH); return (ap < bp); } catch (Event::NoData) { std::cerr << "Bad karma: PitchGreater failed to find one or both pitches" << std::endl; return false; } } typedef GenericChord Chord; typedef GenericChord GlobalChord; } #endif