|
|
|
// -*- c-basic-offset: 4 -*-
|
|
|
|
|
|
|
|
/*
|
|
|
|
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 _SETS_H_
|
|
|
|
#define _SETS_H_
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
#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 Element, class Container>
|
|
|
|
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 tqcontains(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 Element, class Container, bool singleStaff>
|
|
|
|
class GenericChord : public AbstractSet<Element, Container>,
|
|
|
|
public std::vector<typename Container::iterator>
|
|
|
|
{
|
|
|
|
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<Mark> getMarksForChord() const;
|
|
|
|
virtual std::vector<int> getPitches() const;
|
|
|
|
virtual bool tqcontains(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 <class Element, class Container>
|
|
|
|
AbstractSet<Element, Container>::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 <class Element, class Container>
|
|
|
|
void
|
|
|
|
AbstractSet<Element, Container>::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 <class Element, class Container>
|
|
|
|
bool
|
|
|
|
AbstractSet<Element, Container>::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 <class Element, class Container, bool singleStaff>
|
|
|
|
GenericChord<Element, Container, singleStaff>::GenericChord(Container &c,
|
|
|
|
Iterator i,
|
|
|
|
const Quantizer *q,
|
|
|
|
PropertyName stemUpProperty) :
|
|
|
|
AbstractSet<Element, Container>(c, i, q),
|
|
|
|
m_stemUpProperty(stemUpProperty),
|
|
|
|
m_time(q->getQuantizedAbsoluteTime(getAsEvent(i))),
|
|
|
|
m_subordering(getAsEvent(i)->getSubOrdering()),
|
|
|
|
m_firstReject(c.end())
|
|
|
|
{
|
|
|
|
AbstractSet<Element, Container>::initialise();
|
|
|
|
|
|
|
|
if (std::vector<typename Container::iterator>::size() > 1) {
|
|
|
|
std::stable_sort(std::vector<typename Container::iterator>::begin(),
|
|
|
|
std::vector<typename Container::iterator>::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<Int>(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 <class Element, class Container, bool singleStaff>
|
|
|
|
GenericChord<Element, Container, singleStaff>::~GenericChord()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Element, class Container, bool singleStaff>
|
|
|
|
bool
|
|
|
|
GenericChord<Element, Container, singleStaff>::test(const Iterator &i)
|
|
|
|
{
|
|
|
|
Event *e = getAsEvent(i);
|
|
|
|
if (AbstractSet<Element, Container>::
|
|
|
|
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 <class Element, class Container, bool singleStaff>
|
|
|
|
bool
|
|
|
|
GenericChord<Element, Container, singleStaff>::sample(const Iterator &i,
|
|
|
|
bool goingForwards)
|
|
|
|
{
|
|
|
|
Event *e1 = getAsEvent(i);
|
|
|
|
if (!e1->isa(Note::EventType)) {
|
|
|
|
if (goingForwards && m_firstReject == AbstractSet<Element, Container>::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<Element, Container>::m_baseIterator != AbstractSet<Element, Container>::getContainer().end()) {
|
|
|
|
|
|
|
|
Event *e0 = getAsEvent(AbstractSet<Element, Container>::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<Element, Container>::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<Element, Container>::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<Element, Container>::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<Element, Container>::sample(i, goingForwards);
|
|
|
|
push_back(i);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Element, class Container, bool singleStaff>
|
|
|
|
void
|
|
|
|
GenericChord<Element, Container, singleStaff>::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 <class Element, class Container, bool singleStaff>
|
|
|
|
int
|
|
|
|
GenericChord<Element, Container, singleStaff>::getMarkCountForChord() const
|
|
|
|
{
|
|
|
|
// need to weed out duplicates
|
|
|
|
|
|
|
|
std::set<Mark> cmarks;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < std::vector<typename Container::iterator>::size(); ++i) {
|
|
|
|
|
|
|
|
Event *e = getAsEvent((*this)[i]);
|
|
|
|
std::vector<Mark> marks(Marks::getMarks(*e));
|
|
|
|
|
|
|
|
for (std::vector<Mark>::iterator j = marks.begin(); j != marks.end(); ++j) {
|
|
|
|
cmarks.insert(*j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmarks.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <class Element, class Container, bool singleStaff>
|
|
|
|
std::vector<Mark>
|
|
|
|
GenericChord<Element, Container, singleStaff>::getMarksForChord() const
|
|
|
|
{
|
|
|
|
std::vector<Mark> cmarks;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < std::vector<typename Container::iterator>::size(); ++i) {
|
|
|
|
|
|
|
|
Event *e = getAsEvent((*this)[i]);
|
|
|
|
std::vector<Mark> marks(Marks::getMarks(*e));
|
|
|
|
|
|
|
|
|
|
|
|
for (std::vector<Mark>::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 <class Element, class Container, bool singleStaff>
|
|
|
|
std::vector<int>
|
|
|
|
GenericChord<Element, Container, singleStaff>::getPitches() const
|
|
|
|
{
|
|
|
|
std::vector<int> pitches;
|
|
|
|
|
|
|
|
for (typename std::vector<typename Container::iterator>::const_iterator
|
|
|
|
i = std::vector<typename Container::iterator>::begin(); i != std::vector<typename Container::iterator>::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 <class Element, class Container, bool singleStaff>
|
|
|
|
bool
|
|
|
|
GenericChord<Element, Container, singleStaff>::tqcontains(const Iterator &itr) const
|
|
|
|
{
|
|
|
|
for (typename std::vector<typename Container::iterator>::const_iterator
|
|
|
|
i = std::vector<typename Container::iterator>::begin();
|
|
|
|
i != std::vector<typename Container::iterator>::end(); ++i) {
|
|
|
|
if (*i == itr) return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <class Element, class Container, bool singleStaff>
|
|
|
|
typename GenericChord<Element, Container, singleStaff>::Iterator
|
|
|
|
GenericChord<Element, Container, singleStaff>::getPreviousNote()
|
|
|
|
{
|
|
|
|
Iterator i(AbstractSet<Element, Container>::getInitialElement());
|
|
|
|
while (1) {
|
|
|
|
if (i == AbstractSet<Element, Container>::getContainer().begin()) return AbstractSet<Element, Container>::getContainer().end();
|
|
|
|
--i;
|
|
|
|
if (getAsEvent(i)->isa(Note::EventType)) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <class Element, class Container, bool singleStaff>
|
|
|
|
typename GenericChord<Element, Container, singleStaff>::Iterator
|
|
|
|
GenericChord<Element, Container, singleStaff>::getNextNote()
|
|
|
|
{
|
|
|
|
Iterator i(AbstractSet<Element, Container>::getFinalElement());
|
|
|
|
while ( i != AbstractSet<Element, Container>::getContainer().end() &&
|
|
|
|
++i != AbstractSet<Element, Container>::getContainer().end()) {
|
|
|
|
if (getAsEvent(i)->isa(Note::EventType)) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return AbstractSet<Element, Container>::getContainer().end();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <class Element, class Container, bool singleStaff>
|
|
|
|
typename GenericChord<Element, Container, singleStaff>::Iterator
|
|
|
|
GenericChord<Element, Container, singleStaff>::getFirstElementNotInChord()
|
|
|
|
{
|
|
|
|
return m_firstReject;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <class Element, class Container, bool singleStaff>
|
|
|
|
bool
|
|
|
|
GenericChord<Element, Container, singleStaff>::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<Event, Segment, true> Chord;
|
|
|
|
typedef GenericChord<Event, CompositionTimeSliceAdapter, false> GlobalChord;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|