summaryrefslogtreecommitdiffstats
path: root/src/base/SegmentNotationHelper.cpp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
commit145364a8af6a1fec06556221e66d4b724a62fc9a (patch)
tree53bd71a544008c518034f208d64c932dc2883f50 /src/base/SegmentNotationHelper.cpp
downloadrosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz
rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/base/SegmentNotationHelper.cpp')
-rw-r--r--src/base/SegmentNotationHelper.cpp2129
1 files changed, 2129 insertions, 0 deletions
diff --git a/src/base/SegmentNotationHelper.cpp b/src/base/SegmentNotationHelper.cpp
new file mode 100644
index 0000000..a6c8ab8
--- /dev/null
+++ b/src/base/SegmentNotationHelper.cpp
@@ -0,0 +1,2129 @@
+// -*- 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.
+*/
+
+#include "SegmentNotationHelper.h"
+#include "NotationTypes.h"
+#include "Quantizer.h"
+#include "BasicQuantizer.h"
+#include "NotationQuantizer.h"
+#include "BaseProperties.h"
+#include "Composition.h"
+
+#include <iostream>
+#include <algorithm>
+#include <iterator>
+#include <list>
+
+namespace Rosegarden
+{
+using std::cerr;
+using std::endl;
+using std::string;
+using std::list;
+
+using namespace BaseProperties;
+
+
+SegmentNotationHelper::~SegmentNotationHelper() { }
+
+
+const Quantizer &
+SegmentNotationHelper::basicQuantizer() {
+ return *(segment().getComposition()->getBasicQuantizer());
+}
+
+const Quantizer &
+SegmentNotationHelper::notationQuantizer() {
+ return *(segment().getComposition()->getNotationQuantizer());
+}
+
+
+//!!! we need to go very carefully through this file and check calls
+//to getAbsoluteTime/getDuration -- the vast majority should almost
+//certainly now be using getNotationAbsoluteTime/getNotationDuration
+
+Segment::iterator
+SegmentNotationHelper::findNotationAbsoluteTime(timeT t)
+{
+ iterator i(segment().findTime(t));
+
+ // We don't know whether the notation absolute time t will appear
+ // before or after the real absolute time t. First scan backwards
+ // until we find a notation absolute time prior to (or equal to)
+ // t, and then scan forwards until we find the first one that
+ // isn't prior to t
+
+ while (i != begin() &&
+ ((i == end() ? t + 1 : (*i)->getNotationAbsoluteTime()) > t))
+ --i;
+
+ while (i != end() &&
+ ((*i)->getNotationAbsoluteTime() < t))
+ ++i;
+
+ return i;
+}
+
+Segment::iterator
+SegmentNotationHelper::findNearestNotationAbsoluteTime(timeT t)
+{
+ iterator i(segment().findTime(t));
+
+ // Exactly findNotationAbsoluteTime, only with the two scan loops
+ // in the other order
+
+ while (i != end() &&
+ ((*i)->getNotationAbsoluteTime() < t))
+ ++i;
+
+ while (i != begin() &&
+ ((i == end() ? t + 1 : (*i)->getNotationAbsoluteTime()) > t))
+ --i;
+
+ return i;
+}
+
+void
+SegmentNotationHelper::setNotationProperties(timeT startTime, timeT endTime)
+{
+ Segment::iterator from = begin();
+ Segment::iterator to = end();
+
+ if (startTime != endTime) {
+ from = segment().findTime(startTime);
+ to = segment().findTime(endTime);
+ }
+/*!!!
+ bool justSeenGraceNote = false;
+ timeT graceNoteStart = 0;
+*/
+ for (Segment::iterator i = from;
+ i != to && segment().isBeforeEndMarker(i); ++i) {
+
+ if ((*i)->has(NOTE_TYPE) /*!!! && !(*i)->has(IS_GRACE_NOTE) */) continue;
+
+ timeT duration = (*i)->getNotationDuration();
+
+ if ((*i)->has(BEAMED_GROUP_TUPLET_BASE)) {
+ int tcount = (*i)->get<Int>(BEAMED_GROUP_TUPLED_COUNT);
+ int ucount = (*i)->get<Int>(BEAMED_GROUP_UNTUPLED_COUNT);
+
+ if (tcount == 0) {
+ std::cerr << "WARNING: SegmentNotationHelper::setNotationProperties: zero tuplet count:" << std::endl;
+ (*i)->dump(std::cerr);
+ } else {
+ // nominal duration is longer than actual (sounding) duration
+ duration = (duration / tcount) * ucount;
+ }
+ }
+
+ if ((*i)->isa(Note::EventType) || (*i)->isa(Note::EventRestType)) {
+
+ if ((*i)->isa(Note::EventType)) {
+/*!!!
+ if ((*i)->has(IS_GRACE_NOTE) &&
+ (*i)->get<Bool>(IS_GRACE_NOTE)) {
+
+ if (!justSeenGraceNote) {
+ graceNoteStart = (*i)->getNotationAbsoluteTime();
+ justSeenGraceNote = true;
+ }
+
+ } else if (justSeenGraceNote) {
+
+ duration += (*i)->getNotationAbsoluteTime() - graceNoteStart;
+ justSeenGraceNote = false;
+ }
+*/
+ }
+
+ Note n(Note::getNearestNote(duration));
+
+ (*i)->setMaybe<Int>(NOTE_TYPE, n.getNoteType());
+ (*i)->setMaybe<Int>(NOTE_DOTS, n.getDots());
+ }
+ }
+}
+
+timeT
+SegmentNotationHelper::getNotationEndTime(Event *e)
+{
+ return e->getNotationAbsoluteTime() + e->getNotationDuration();
+}
+
+
+Segment::iterator
+SegmentNotationHelper::getNextAdjacentNote(iterator i,
+ bool matchPitch,
+ bool allowOverlap)
+{
+ iterator j(i);
+ if (!isBeforeEndMarker(i)) return i;
+ if (!(*i)->isa(Note::EventType)) return end();
+
+ timeT iEnd = getNotationEndTime(*i);
+ long ip = 0, jp = 0;
+ if (!(*i)->get<Int>(PITCH, ip) && matchPitch) return end();
+
+ while (true) {
+ if (!isBeforeEndMarker(j) || !isBeforeEndMarker(++j)) return end();
+ if (!(*j)->isa(Note::EventType)) continue;
+
+ timeT jStart = (*j)->getNotationAbsoluteTime();
+ if (jStart > iEnd) return end();
+
+ if (matchPitch) {
+ if (!(*j)->get<Int>(PITCH, jp) || (jp != ip)) continue;
+ }
+
+ if (allowOverlap || (jStart == iEnd)) return j;
+ }
+}
+
+
+Segment::iterator
+SegmentNotationHelper::getPreviousAdjacentNote(iterator i,
+ timeT rangeStart,
+ bool matchPitch,
+ bool allowOverlap)
+{
+ iterator j(i);
+ if (!isBeforeEndMarker(i)) return i;
+ if (!(*i)->isa(Note::EventType)) return end();
+
+ timeT iStart = (*i)->getNotationAbsoluteTime();
+ timeT iEnd = getNotationEndTime(*i);
+ long ip = 0, jp = 0;
+ if (!(*i)->get<Int>(PITCH, ip) && matchPitch) return end();
+
+ while (true) {
+ if (j == begin()) return end(); else --j;
+ if (!(*j)->isa(Note::EventType)) continue;
+ if ((*j)->getAbsoluteTime() < rangeStart) return end();
+
+ timeT jEnd = getNotationEndTime(*j);
+
+ // don't consider notes that end after i ends or before i begins
+
+ if (jEnd > iEnd || jEnd < iStart) continue;
+
+ if (matchPitch) {
+ if (!(*j)->get<Int>(PITCH, jp) || (jp != ip)) continue;
+ }
+
+ if (allowOverlap || (jEnd == iStart)) return j;
+ }
+}
+
+
+Segment::iterator
+SegmentNotationHelper::findContiguousNext(iterator el)
+{
+ std::string elType = (*el)->getType(),
+ reject, accept;
+
+ if (elType == Note::EventType) {
+ accept = Note::EventType;
+ reject = Note::EventRestType;
+ } else if (elType == Note::EventRestType) {
+ accept = Note::EventRestType;
+ reject = Note::EventType;
+ } else {
+ accept = elType;
+ reject = "";
+ }
+
+ bool success = false;
+
+ iterator i = ++el;
+
+ for(; isBeforeEndMarker(i); ++i) {
+ std::string iType = (*i)->getType();
+
+ if (iType == reject) {
+ success = false;
+ break;
+ }
+ if (iType == accept) {
+ success = true;
+ break;
+ }
+ }
+
+ if (success) return i;
+ else return end();
+
+}
+
+Segment::iterator
+SegmentNotationHelper::findContiguousPrevious(iterator el)
+{
+ if (el == begin()) return end();
+
+ std::string elType = (*el)->getType(),
+ reject, accept;
+
+ if (elType == Note::EventType) {
+ accept = Note::EventType;
+ reject = Note::EventRestType;
+ } else if (elType == Note::EventRestType) {
+ accept = Note::EventRestType;
+ reject = Note::EventType;
+ } else {
+ accept = elType;
+ reject = "";
+ }
+
+ bool success = false;
+
+ iterator i = --el;
+
+ while (true) {
+ std::string iType = (*i)->getType();
+
+ if (iType == reject) {
+ success = false;
+ break;
+ }
+ if (iType == accept) {
+ success = true;
+ break;
+ }
+ if (i == begin()) break;
+ --i;
+ }
+
+ if (success) return i;
+ else return end();
+}
+
+
+bool
+SegmentNotationHelper::noteIsInChord(Event *note)
+{
+ iterator i = segment().findSingle(note);
+ timeT t = note->getNotationAbsoluteTime();
+
+ for (iterator j = i; j != end(); ++j) { // not isBeforeEndMarker, unnecessary here
+ if (j == i) continue;
+ if ((*j)->isa(Note::EventType)) {
+ timeT tj = (*j)->getNotationAbsoluteTime();
+ if (tj == t) return true;
+ else if (tj > t) break;
+ }
+ }
+
+ for (iterator j = i; ; ) {
+ if (j == begin()) break;
+ --j;
+ if ((*j)->isa(Note::EventType)) {
+ timeT tj = (*j)->getNotationAbsoluteTime();
+ if (tj == t) return true;
+ else if (tj < t) break;
+ }
+ }
+
+ return false;
+
+/*!!!
+ iterator first, second;
+ segment().getTimeSlice(note->getAbsoluteTime(), first, second);
+
+ int noteCount = 0;
+ for (iterator i = first; i != second; ++i) {
+ if ((*i)->isa(Note::EventType)) ++noteCount;
+ }
+
+ return noteCount > 1;
+*/
+}
+
+
+//!!! This doesn't appear to be used any more and may well not work.
+// Ties are calculated in several different places, and it's odd that
+// we don't have a decent API for them
+Segment::iterator
+SegmentNotationHelper::getNoteTiedWith(Event *note, bool forwards)
+{
+ bool tied = false;
+
+ if (!note->get<Bool>(forwards ?
+ BaseProperties::TIED_FORWARD :
+ BaseProperties::TIED_BACKWARD, tied) || !tied) {
+ return end();
+ }
+
+ timeT myTime = note->getAbsoluteTime();
+ timeT myDuration = note->getDuration();
+ int myPitch = note->get<Int>(BaseProperties::PITCH);
+
+ iterator i = segment().findSingle(note);
+ if (!isBeforeEndMarker(i)) return end();
+
+ for (;;) {
+ i = forwards ? findContiguousNext(i) : findContiguousPrevious(i);
+
+ if (!isBeforeEndMarker(i)) return end();
+ if ((*i)->getAbsoluteTime() == myTime) continue;
+
+ if (forwards && ((*i)->getAbsoluteTime() != myTime + myDuration)) {
+ return end();
+ }
+ if (!forwards &&
+ (((*i)->getAbsoluteTime() + (*i)->getDuration()) != myTime)) {
+ return end();
+ }
+
+ if (!(*i)->get<Bool>(forwards ?
+ BaseProperties::TIED_BACKWARD :
+ BaseProperties::TIED_FORWARD, tied) || !tied) {
+ continue;
+ }
+
+ if ((*i)->get<Int>(BaseProperties::PITCH) == myPitch) return i;
+ }
+
+ return end();
+}
+
+
+bool
+SegmentNotationHelper::collapseRestsIfValid(Event* e, bool& collapseForward)
+{
+ iterator elPos = segment().findSingle(e);
+ if (elPos == end()) return false;
+
+ timeT myDuration = (*elPos)->getNotationDuration();
+
+ // findContiguousNext won't return an iterator beyond the end marker
+ iterator nextEvent = findContiguousNext(elPos),
+ previousEvent = findContiguousPrevious(elPos);
+
+ // Remark: findContiguousXXX is inadequate for notes, we would
+ // need to check adjacency using e.g. getNextAdjacentNote if this
+ // method were to work for notes as well as rests.
+
+ // collapse to right if (a) not at end...
+ if (nextEvent != end() &&
+ // ...(b) rests can be merged to a single, valid unit
+ isCollapseValid((*nextEvent)->getNotationDuration(), myDuration) &&
+ // ...(c) event is in same bar (no cross-bar collapsing)
+ (*nextEvent)->getAbsoluteTime() <
+ segment().getBarEndForTime(e->getAbsoluteTime())) {
+
+ // collapse right is OK; collapse e with nextEvent
+ Event *e1(new Event(*e, e->getAbsoluteTime(),
+ e->getDuration() + (*nextEvent)->getDuration()));
+
+ collapseForward = true;
+ erase(elPos);
+ erase(nextEvent);
+ insert(e1);
+ return true;
+ }
+
+ // logic is exactly backwards from collapse to right logic above
+ if (previousEvent != end() &&
+ isCollapseValid((*previousEvent)->getNotationDuration(), myDuration) &&
+ (*previousEvent)->getAbsoluteTime() >
+ segment().getBarStartForTime(e->getAbsoluteTime())) {
+
+ // collapse left is OK; collapse e with previousEvent
+ Event *e1(new Event(**previousEvent,
+ (*previousEvent)->getAbsoluteTime(),
+ e->getDuration() +
+ (*previousEvent)->getDuration()));
+
+ collapseForward = false;
+ erase(elPos);
+ erase(previousEvent);
+ insert(e1);
+ return true;
+ }
+
+ return false;
+}
+
+
+bool
+SegmentNotationHelper::isCollapseValid(timeT a, timeT b)
+{
+ return (isViable(a + b));
+}
+
+
+bool
+SegmentNotationHelper::isSplitValid(timeT a, timeT b)
+{
+ return (isViable(a) && isViable(b));
+}
+
+Segment::iterator
+SegmentNotationHelper::splitIntoTie(iterator &i, timeT baseDuration)
+{
+ if (i == end()) return end();
+ iterator i2;
+ segment().getTimeSlice((*i)->getAbsoluteTime(), i, i2);
+ return splitIntoTie(i, i2, baseDuration);
+}
+
+Segment::iterator
+SegmentNotationHelper::splitIntoTie(iterator &from, iterator to,
+ timeT baseDuration)
+{
+ // so long as we do the quantization checks for validity before
+ // calling this method, we should be fine splitting precise times
+ // in this method. only problem is deciding not to split something
+ // if its duration is very close to requested duration, but that's
+ // probably not a task for this function
+
+ timeT eventDuration = (*from)->getDuration();
+ timeT baseTime = (*from)->getAbsoluteTime();
+
+ long firstGroupId = -1;
+ (*from)->get<Int>(BEAMED_GROUP_ID, firstGroupId);
+
+ long nextGroupId = -1;
+ iterator ni(to);
+
+ if (segment().isBeforeEndMarker(ni) && segment().isBeforeEndMarker(++ni)) {
+ (*ni)->get<Int>(BEAMED_GROUP_ID, nextGroupId);
+ }
+
+ list<Event *> toInsert;
+ list<iterator> toErase;
+
+ // Split all the note and rest events in range [from, to[
+ //
+ for (iterator i = from; i != to; ++i) {
+
+ if (!(*i)->isa(Note::EventType) &&
+ !(*i)->isa(Note::EventRestType)) continue;
+
+ if ((*i)->getAbsoluteTime() != baseTime) {
+ // no way to really cope with an error, because at this
+ // point we may already have splut some events. Best to
+ // skip this event
+ cerr << "WARNING: SegmentNotationHelper::splitIntoTie(): (*i)->getAbsoluteTime() != baseTime (" << (*i)->getAbsoluteTime() << " vs " << baseTime << "), ignoring this event\n";
+ continue;
+ }
+
+ if ((*i)->getDuration() != eventDuration) {
+ if ((*i)->getDuration() == 0) continue;
+ cerr << "WARNING: SegmentNotationHelper::splitIntoTie(): (*i)->getDuration() != eventDuration (" << (*i)->getDuration() << " vs " << eventDuration << "), changing eventDuration to match\n";
+ eventDuration = (*i)->getDuration();
+ }
+
+ if (baseDuration >= eventDuration) {
+// cerr << "SegmentNotationHelper::splitIntoTie() : baseDuration >= eventDuration, ignoring event\n";
+ continue;
+ }
+
+ std::pair<Event *, Event *> split =
+ splitPreservingPerformanceTimes(*i, baseDuration);
+
+ Event *eva = split.first;
+ Event *evb = split.second;
+
+ if (!eva || !evb) {
+ cerr << "WARNING: SegmentNotationHelper::splitIntoTie(): No valid split for event of duration " << eventDuration << " at " << baseTime << " (baseDuration " << baseDuration << "), ignoring this event\n";
+ continue;
+ }
+
+ // we only want to tie Note events:
+
+ if (eva->isa(Note::EventType)) {
+
+ // if the first event was already tied forward, the
+ // second one will now be marked as tied forward
+ // (which is good). set up the relationship between
+ // the original (now shorter) event and the new one.
+
+ evb->set<Bool>(TIED_BACKWARD, true);
+ eva->set<Bool>(TIED_FORWARD, true);
+ }
+
+ // we may also need to change some group information: if
+ // the first event is in a beamed group but the event
+ // following the insertion is not or is in a different
+ // group, then the new second event should not be in a
+ // group. otherwise, it should inherit the grouping info
+ // from the first event (as it already does, because it
+ // was created using the copy constructor).
+
+ // (this doesn't apply to tupled groups, which we want
+ // to persist wherever possible.)
+
+ if (firstGroupId != -1 &&
+ nextGroupId != firstGroupId &&
+ !evb->has(BEAMED_GROUP_TUPLET_BASE)) {
+ evb->unset(BEAMED_GROUP_ID);
+ evb->unset(BEAMED_GROUP_TYPE);
+ }
+
+ toInsert.push_back(eva);
+ toInsert.push_back(evb);
+ toErase.push_back(i);
+ }
+
+ // erase the old events
+ for (list<iterator>::iterator i = toErase.begin();
+ i != toErase.end(); ++i) {
+ segment().erase(*i);
+ }
+
+ from = end();
+ iterator last = end();
+
+ // now insert the new events
+ for (list<Event *>::iterator i = toInsert.begin();
+ i != toInsert.end(); ++i) {
+ last = insert(*i);
+ if (from == end()) from = last;
+ }
+
+ return last;
+}
+
+bool
+SegmentNotationHelper::isViable(timeT duration, int dots)
+{
+ bool viable;
+
+/*!!!
+ duration = basicQuantizer().quantizeDuration(duration);
+
+ if (dots >= 0) {
+ viable = (duration == Quantizer(Quantizer::RawEventData,
+ Quantizer::DefaultTarget,
+ Quantizer::NoteQuantize, 1, dots).
+ quantizeDuration(duration));
+ } else {
+ viable = (duration == notationQuantizer().quantizeDuration(duration));
+ }
+*/
+
+ //!!! what to do about this?
+
+ timeT nearestDuration =
+ Note::getNearestNote(duration, dots >= 0 ? dots : 2).getDuration();
+
+// std::cerr << "SegmentNotationHelper::isViable: nearestDuration is " << nearestDuration << ", duration is " << duration << std::endl;
+ viable = (nearestDuration == duration);
+
+ return viable;
+}
+
+
+void
+SegmentNotationHelper::makeRestViable(iterator i)
+{
+ timeT absTime = (*i)->getAbsoluteTime();
+ timeT duration = (*i)->getDuration();
+ erase(i);
+ segment().fillWithRests(absTime, absTime + duration);
+}
+
+
+void
+SegmentNotationHelper::makeNotesViable(iterator from, iterator to,
+ bool splitAtBars)
+{
+ // We don't use quantized values here; we want a precise division.
+ // Even if it doesn't look precise on the score (because the score
+ // is quantized), we want any playback to produce exactly the same
+ // duration of note as was originally recorded
+
+ std::vector<Event *> toInsert;
+
+ for (Segment::iterator i = from, j = i;
+ segment().isBeforeEndMarker(i) && i != to; i = j) {
+
+ ++j;
+
+ if (!(*i)->isa(Note::EventType) && !(*i)->isa(Note::EventRestType)) {
+ continue;
+ }
+
+ if ((*i)->has(BEAMED_GROUP_TUPLET_BASE)) {
+ continue;
+ }
+
+ DurationList dl;
+
+ // Behaviour differs from TimeSignature::getDurationListForInterval
+
+ timeT acc = 0;
+ timeT required = (*i)->getNotationDuration();
+
+ while (acc < required) {
+ timeT remaining = required - acc;
+ if (splitAtBars) {
+ timeT thisNoteStart = (*i)->getNotationAbsoluteTime() + acc;
+ timeT toNextBar =
+ segment().getBarEndForTime(thisNoteStart) - thisNoteStart;
+ if (toNextBar > 0 && remaining > toNextBar) remaining = toNextBar;
+ }
+ timeT component = Note::getNearestNote(remaining).getDuration();
+ if (component > (required - acc)) dl.push_back(required - acc);
+ else dl.push_back(component);
+ acc += component;
+ }
+
+ if (dl.size() < 2) {
+ // event is already of the correct duration
+ continue;
+ }
+
+ acc = (*i)->getNotationAbsoluteTime();
+ Event *e = new Event(*(*i));
+
+ bool lastTiedForward = false;
+ e->get<Bool>(TIED_FORWARD, lastTiedForward);
+
+ e->set<Bool>(TIED_FORWARD, true);
+ erase(i);
+
+ for (DurationList::iterator dli = dl.begin(); dli != dl.end(); ++dli) {
+
+ DurationList::iterator dlj(dli);
+ if (++dlj == dl.end()) {
+ // end of duration list
+ if (!lastTiedForward) e->unset(TIED_FORWARD);
+ toInsert.push_back(e);
+ e = 0;
+ break;
+ }
+
+ std::pair<Event *, Event *> splits =
+ splitPreservingPerformanceTimes(e, *dli);
+
+ if (!splits.first || !splits.second) {
+ cerr << "WARNING: SegmentNotationHelper::makeNoteViable(): No valid split for event of duration " << e->getDuration() << " at " << e->getAbsoluteTime() << " (split duration " << *dli << "), ignoring remainder\n";
+ cerr << "WARNING: This is probably a bug; fix required" << std::endl;
+ toInsert.push_back(e);
+ e = 0;
+ break;
+ }
+
+ toInsert.push_back(splits.first);
+ delete e;
+ e = splits.second;
+
+ acc += *dli;
+
+ e->set<Bool>(TIED_BACKWARD, true);
+ }
+
+ delete e;
+ }
+
+ for (std::vector<Event *>::iterator ei = toInsert.begin();
+ ei != toInsert.end(); ++ei) {
+ insert(*ei);
+ }
+}
+
+void
+SegmentNotationHelper::makeNotesViable(timeT startTime, timeT endTime,
+ bool splitAtBars)
+{
+ Segment::iterator from = segment().findTime(startTime);
+ Segment::iterator to = segment().findTime(endTime);
+
+ makeNotesViable(from, to, splitAtBars);
+}
+
+
+Segment::iterator
+SegmentNotationHelper::insertNote(timeT absoluteTime, Note note, int pitch,
+ Accidental explicitAccidental)
+{
+ Event *e = new Event(Note::EventType, absoluteTime, note.getDuration());
+ e->set<Int>(PITCH, pitch);
+ e->set<String>(ACCIDENTAL, explicitAccidental);
+ iterator i = insertNote(e);
+ delete e;
+ return i;
+}
+
+Segment::iterator
+SegmentNotationHelper::insertNote(Event *modelEvent)
+{
+ timeT absoluteTime = modelEvent->getAbsoluteTime();
+ iterator i = segment().findNearestTime(absoluteTime);
+
+ // If our insertion time doesn't match up precisely with any
+ // existing event, and if we're inserting over a rest, split the
+ // rest at the insertion time first.
+
+ if (i != end() &&
+ (*i)->getAbsoluteTime() < absoluteTime &&
+ (*i)->getAbsoluteTime() + (*i)->getDuration() > absoluteTime &&
+ (*i)->isa(Note::EventRestType)) {
+ i = splitIntoTie(i, absoluteTime - (*i)->getAbsoluteTime());
+ }
+
+ timeT duration = modelEvent->getDuration();
+
+ if (i != end() && (*i)->has(BEAMED_GROUP_TUPLET_BASE)) {
+ duration = duration * (*i)->get<Int>(BEAMED_GROUP_TUPLED_COUNT) /
+ (*i)->get<Int>(BEAMED_GROUP_UNTUPLED_COUNT);
+ }
+
+ //!!! Deal with end-of-bar issues!
+
+ return insertSomething(i, duration, modelEvent, false);
+}
+
+
+Segment::iterator
+SegmentNotationHelper::insertRest(timeT absoluteTime, Note note)
+{
+ iterator i, j;
+ segment().getTimeSlice(absoluteTime, i, j);
+
+ //!!! Deal with end-of-bar issues!
+
+ timeT duration(note.getDuration());
+
+ if (i != end() && (*i)->has(BEAMED_GROUP_TUPLET_BASE)) {
+ duration = duration * (*i)->get<Int>(BEAMED_GROUP_TUPLED_COUNT) /
+ (*i)->get<Int>(BEAMED_GROUP_UNTUPLED_COUNT);
+ }
+
+ Event *modelEvent = new Event(Note::EventRestType, absoluteTime,
+ note.getDuration(),
+ Note::EventRestSubOrdering);
+
+ i = insertSomething(i, duration, modelEvent, false);
+ delete modelEvent;
+ return i;
+}
+
+
+// return an iterator pointing to the "same" event as the original
+// iterator (which will have been replaced)
+
+Segment::iterator
+SegmentNotationHelper::collapseRestsForInsert(iterator i,
+ timeT desiredDuration)
+{
+ // collapse at most once, then recurse
+
+ if (!segment().isBeforeEndMarker(i) ||
+ !(*i)->isa(Note::EventRestType)) return i;
+
+ timeT d = (*i)->getDuration();
+ iterator j = findContiguousNext(i); // won't return itr after end marker
+ if (d >= desiredDuration || j == end()) return i;
+
+ Event *e(new Event(**i, (*i)->getAbsoluteTime(), d + (*j)->getDuration()));
+ iterator ii(insert(e));
+ erase(i);
+ erase(j);
+
+ return collapseRestsForInsert(ii, desiredDuration);
+}
+
+
+Segment::iterator
+SegmentNotationHelper::insertSomething(iterator i, int duration,
+ Event *modelEvent, bool tiedBack)
+{
+ // Rules:
+ //
+ // 1. If we hit a bar line in the course of the intended inserted
+ // note, we should split the note rather than make the bar the
+ // wrong length. (Not implemented yet)
+ //
+ // 2. If there's nothing at the insertion point but rests (and
+ // enough of them to cover the entire duration of the new note),
+ // then we should insert the new note/rest literally and remove
+ // rests as appropriate. Rests should never prevent us from
+ // inserting what the user asked for.
+ //
+ // 3. If there are notes in the way of an inserted note, however,
+ // we split whenever "reasonable" and truncate our user's note if
+ // not reasonable to split. We can't always give users the Right
+ // Thing here, so to hell with them.
+
+ while (i != end() &&
+ ((*i)->getDuration() == 0 ||
+ !((*i)->isa(Note::EventType) || (*i)->isa(Note::EventRestType))))
+ ++i;
+
+ if (i == end()) {
+ return insertSingleSomething(i, duration, modelEvent, tiedBack);
+ }
+
+ // If there's a rest at the insertion position, merge it with any
+ // following rests, if available, until we have at least the
+ // duration of the new note.
+ i = collapseRestsForInsert(i, duration);
+
+ timeT existingDuration = (*i)->getNotationDuration();
+
+// cerr << "SegmentNotationHelper::insertSomething: asked to insert duration " << duration
+// << " over event of duration " << existingDuration << ":" << endl;
+ (*i)->dump(cerr);
+
+ if (duration == existingDuration) {
+
+ // 1. If the new note or rest is the same length as an
+ // existing note or rest at that position, chord the existing
+ // note or delete the existing rest and insert.
+
+// cerr << "Durations match; doing simple insert" << endl;
+
+ return insertSingleSomething(i, duration, modelEvent, tiedBack);
+
+ } else if (duration < existingDuration) {
+
+ // 2. If the new note or rest is shorter than an existing one,
+ // split the existing one and chord or replace the first part.
+
+ if ((*i)->isa(Note::EventType)) {
+
+ if (!isSplitValid(duration, existingDuration - duration)) {
+
+// cerr << "Bad split, coercing new note" << endl;
+
+ // not reasonable to split existing note, so force new one
+ // to same duration instead
+ duration = (*i)->getNotationDuration();
+
+ } else {
+// cerr << "Good split, splitting old event" << endl;
+ splitIntoTie(i, duration);
+ }
+ } else if ((*i)->isa(Note::EventRestType)) {
+
+// cerr << "Found rest, splitting" << endl;
+ iterator last = splitIntoTie(i, duration);
+
+ // Recover viability for the second half of any split rest
+ // (we duck out of this if we find we're in a tupleted zone)
+
+ if (last != end() && !(*last)->has(BEAMED_GROUP_TUPLET_BASE)) {
+ makeRestViable(last);
+ }
+ }
+
+ return insertSingleSomething(i, duration, modelEvent, tiedBack);
+
+ } else { // duration > existingDuration
+
+ // 3. If the new note is longer, split the new note so that
+ // the first part is the same duration as the existing note or
+ // rest, and recurse to step 1 with both the first and the
+ // second part in turn.
+
+ bool needToSplit = true;
+
+ // special case: existing event is a rest, and it's at the end
+ // of the segment
+
+ if ((*i)->isa(Note::EventRestType)) {
+ iterator j;
+ for (j = i; j != end(); ++j) {
+ if ((*j)->isa(Note::EventType)) break;
+ }
+ if (j == end()) needToSplit = false;
+ }
+
+ if (needToSplit) {
+
+ //!!! This is not quite right for rests. Because they
+ //replace (rather than chording with) any events already
+ //present, they don't need to be split in the case where
+ //their duration spans several note-events. Worry about
+ //that later, I guess. We're actually getting enough
+ //is-note/is-rest decisions here to make it possibly worth
+ //splitting this method into note and rest versions again
+
+// cerr << "Need to split new note" << endl;
+
+ i = insertSingleSomething
+ (i, existingDuration, modelEvent, tiedBack);
+
+ if (modelEvent->isa(Note::EventType))
+ (*i)->set<Bool>(TIED_FORWARD, true);
+
+ timeT insertedTime = (*i)->getAbsoluteTime();
+ while (i != end() &&
+ ((*i)->getNotationAbsoluteTime() <
+ (insertedTime + existingDuration))) ++i;
+
+ return insertSomething
+ (i, duration - existingDuration, modelEvent, true);
+
+ } else {
+// cerr << "No need to split new note" << endl;
+ return insertSingleSomething(i, duration, modelEvent, tiedBack);
+ }
+ }
+}
+
+Segment::iterator
+SegmentNotationHelper::insertSingleSomething(iterator i, int duration,
+ Event *modelEvent, bool tiedBack)
+{
+ timeT time;
+ timeT notationTime;
+ bool eraseI = false;
+ timeT effectiveDuration(duration);
+
+ if (i == end()) {
+ time = segment().getEndTime();
+ notationTime = time;
+ } else {
+ time = (*i)->getAbsoluteTime();
+ notationTime = (*i)->getNotationAbsoluteTime();
+ if (modelEvent->isa(Note::EventRestType) ||
+ (*i)->isa(Note::EventRestType)) eraseI = true;
+ }
+
+ Event *e = new Event(*modelEvent, time, effectiveDuration,
+ modelEvent->getSubOrdering(), notationTime);
+
+ // If the model event already has group info, I guess we'd better use it!
+ if (!e->has(BEAMED_GROUP_ID)) {
+ setInsertedNoteGroup(e, i);
+ }
+
+ if (tiedBack && e->isa(Note::EventType)) {
+ e->set<Bool>(TIED_BACKWARD, true);
+ }
+
+ if (eraseI) {
+ // erase i and all subsequent events with the same type and
+ // absolute time
+ timeT time((*i)->getAbsoluteTime());
+ std::string type((*i)->getType());
+ iterator j(i);
+ while (j != end() && (*j)->getAbsoluteTime() == time) {
+ ++j;
+ if ((*i)->isa(type)) erase(i);
+ i = j;
+ }
+ }
+
+ return insert(e);
+}
+
+void
+SegmentNotationHelper::setInsertedNoteGroup(Event *e, iterator i)
+{
+ // Formerly this was posited on the note being inserted between
+ // two notes in the same group, but that's quite wrong-headed: we
+ // want to place it in the same group as any existing note at the
+ // same time, and otherwise leave it alone.
+
+ e->unset(BEAMED_GROUP_ID);
+ e->unset(BEAMED_GROUP_TYPE);
+
+ while (isBeforeEndMarker(i) &&
+ (!((*i)->isa(Note::EventRestType)) ||
+ (*i)->has(BEAMED_GROUP_TUPLET_BASE)) &&
+ (*i)->getNotationAbsoluteTime() == e->getAbsoluteTime()) {
+
+ if ((*i)->has(BEAMED_GROUP_ID)) {
+
+ string type = (*i)->get<String>(BEAMED_GROUP_TYPE);
+ if (type != GROUP_TYPE_TUPLED && !(*i)->isa(Note::EventType)) {
+ if ((*i)->isa(Note::EventRestType)) return;
+ else {
+ ++i;
+ continue;
+ }
+ }
+
+ e->set<Int>(BEAMED_GROUP_ID, (*i)->get<Int>(BEAMED_GROUP_ID));
+ e->set<String>(BEAMED_GROUP_TYPE, type);
+
+ if ((*i)->has(BEAMED_GROUP_TUPLET_BASE)) {
+
+ e->set<Int>(BEAMED_GROUP_TUPLET_BASE,
+ (*i)->get<Int>(BEAMED_GROUP_TUPLET_BASE));
+ e->set<Int>(BEAMED_GROUP_TUPLED_COUNT,
+ (*i)->get<Int>(BEAMED_GROUP_TUPLED_COUNT));
+ e->set<Int>(BEAMED_GROUP_UNTUPLED_COUNT,
+ (*i)->get<Int>(BEAMED_GROUP_UNTUPLED_COUNT));
+ }
+
+ return;
+ }
+
+ ++i;
+ }
+}
+
+
+Segment::iterator
+SegmentNotationHelper::insertClef(timeT absoluteTime, Clef clef)
+{
+ return insert(clef.getAsEvent(absoluteTime));
+}
+
+
+Segment::iterator
+SegmentNotationHelper::insertKey(timeT absoluteTime, Key key)
+{
+ return insert(key.getAsEvent(absoluteTime));
+}
+
+
+Segment::iterator
+SegmentNotationHelper::insertText(timeT absoluteTime, Text text)
+{
+ return insert(text.getAsEvent(absoluteTime));
+}
+
+
+void
+SegmentNotationHelper::deleteNote(Event *e, bool collapseRest)
+{
+ iterator i = segment().findSingle(e);
+
+ if (i == end()) return;
+
+ if ((*i)->has(TIED_BACKWARD) && (*i)->get<Bool>(TIED_BACKWARD)) {
+ iterator j = getPreviousAdjacentNote(i, segment().getStartTime(),
+ true, false);
+ if (j != end()) {
+ (*j)->unset(TIED_FORWARD); // don't even check if it has it set
+ }
+ }
+
+ if ((*i)->has(TIED_FORWARD) && (*i)->get<Bool>(TIED_FORWARD)) {
+ iterator j = getNextAdjacentNote(i, true, false);
+ if (j != end()) {
+ (*j)->unset(TIED_BACKWARD); // don't even check if it has it set
+ }
+ }
+
+ // If any notes start at the same time as this one but end first,
+ // or start after this one starts but before it ends, then we go
+ // for the delete-event-and-normalize-rests option. Otherwise
+ // (the notationally simpler case) we go for the
+ // replace-note-by-rest option. We still lose in the case where
+ // another note starts before this one, overlaps it, but then also
+ // ends before it does -- but I think we can live with that.
+
+ iterator j = i;
+ timeT endTime = (*i)->getAbsoluteTime() + (*i)->getDuration();
+
+ while (j != end() && (*j)->getAbsoluteTime() < endTime) {
+
+ bool complicatedOverlap = false;
+
+ if ((*j)->getAbsoluteTime() != (*i)->getAbsoluteTime()) {
+ complicatedOverlap = true;
+ } else if (((*j)->getAbsoluteTime() + (*j)->getDuration()) < endTime) {
+ complicatedOverlap = true;
+ }
+
+ if (complicatedOverlap) {
+ timeT startTime = (*i)->getAbsoluteTime();
+ segment().erase(i);
+ segment().normalizeRests(startTime, endTime);
+ return;
+ }
+
+ ++j;
+ }
+
+ if (noteIsInChord(e)) {
+
+ erase(i);
+
+ } else {
+
+ // replace with a rest
+ Event *newRest = new Event(Note::EventRestType,
+ e->getAbsoluteTime(), e->getDuration(),
+ Note::EventRestSubOrdering);
+ insert(newRest);
+ erase(i);
+
+ // collapse the new rest
+ if (collapseRest) {
+ bool dummy;
+ collapseRestsIfValid(newRest, dummy);
+ }
+
+ }
+}
+
+bool
+SegmentNotationHelper::deleteRest(Event *e)
+{
+ bool collapseForward;
+ return collapseRestsIfValid(e, collapseForward);
+}
+
+bool
+SegmentNotationHelper::deleteEvent(Event *e, bool collapseRest)
+{
+ bool res = true;
+
+ if (e->isa(Note::EventType)) deleteNote(e, collapseRest);
+ else if (e->isa(Note::EventRestType)) res = deleteRest(e);
+ else {
+ // just plain delete
+ iterator i = segment().findSingle(e);
+ if (i != end()) erase(i);
+ }
+
+ return res;
+}
+
+
+bool
+SegmentNotationHelper::hasEffectiveDuration(iterator i)
+{
+ bool hasDuration = ((*i)->getDuration() > 0);
+
+ if ((*i)->isa(Note::EventType)) {
+ iterator i0(i);
+ if (++i0 != end() &&
+ (*i0)->isa(Note::EventType) &&
+ (*i0)->getNotationAbsoluteTime() ==
+ (*i)->getNotationAbsoluteTime()) {
+ // we're in a chord or something
+ hasDuration = false;
+ }
+ }
+
+ return hasDuration;
+}
+
+
+void
+SegmentNotationHelper::makeBeamedGroup(timeT from, timeT to, string type)
+{
+ makeBeamedGroupAux(segment().findTime(from), segment().findTime(to),
+ type, false);
+}
+
+void
+SegmentNotationHelper::makeBeamedGroup(iterator from, iterator to, string type)
+{
+ makeBeamedGroupAux
+ ((from == end()) ? from : segment().findTime((*from)->getAbsoluteTime()),
+ (to == end()) ? to : segment().findTime((*to )->getAbsoluteTime()),
+ type, false);
+}
+
+void
+SegmentNotationHelper::makeBeamedGroupExact(iterator from, iterator to, string type)
+{
+ makeBeamedGroupAux(from, to, type, true);
+}
+
+void
+SegmentNotationHelper::makeBeamedGroupAux(iterator from, iterator to,
+ string type, bool groupGraces)
+{
+// cerr << "SegmentNotationHelper::makeBeamedGroupAux: type " << type << endl;
+// if (from == to) cerr << "from == to" <<endl;
+
+ int groupId = segment().getNextId();
+ bool beamedSomething = false;
+
+ for (iterator i = from; i != to; ++i) {
+// std::cerr << "looking at " << (*i)->getType() << " at " << (*i)->getAbsoluteTime() << std::endl;
+
+ // don't permit ourselves to change the type of an
+ // already-grouped event here
+ if ((*i)->has(BEAMED_GROUP_TYPE) &&
+ (*i)->get<String>(BEAMED_GROUP_TYPE) != GROUP_TYPE_BEAMED) {
+ continue;
+ }
+
+ if (!groupGraces) {
+ if ((*i)->has(IS_GRACE_NOTE) &&
+ (*i)->get<Bool>(IS_GRACE_NOTE)) {
+ continue;
+ }
+ }
+
+ // don't beam anything longer than a quaver unless it's
+ // between beamed quavers -- in which case marking it as
+ // beamed will ensure that it gets re-stemmed appropriately
+
+ if ((*i)->isa(Note::EventType) &&
+ (*i)->getNotationDuration() >= Note(Note::Crotchet).getDuration()) {
+// std::cerr << "too long" <<std::endl;
+ if (!beamedSomething) continue;
+ iterator j = i;
+ bool somethingLeft = false;
+ while (++j != to) {
+ if ((*j)->getType() == Note::EventType &&
+ (*j)->getNotationAbsoluteTime() > (*i)->getNotationAbsoluteTime() &&
+ (*j)->getNotationDuration() < Note(Note::Crotchet).getDuration()) {
+ somethingLeft = true;
+ break;
+ }
+ }
+ if (!somethingLeft) continue;
+ }
+
+// std::cerr << "beaming it" <<std::endl;
+ (*i)->set<Int>(BEAMED_GROUP_ID, groupId);
+ (*i)->set<String>(BEAMED_GROUP_TYPE, type);
+ }
+}
+
+void
+SegmentNotationHelper::makeTupletGroup(timeT t, int untupled, int tupled,
+ timeT unit)
+{
+ int groupId = segment().getNextId();
+
+ cerr << "SegmentNotationHelper::makeTupletGroup: time " << t << ", unit "<< unit << ", params " << untupled << "/" << tupled << ", id " << groupId << endl;
+
+ list<Event *> toInsert;
+ list<iterator> toErase;
+ timeT notationTime = t;
+ timeT fillWithRestsTo = t;
+ bool haveStartNotationTime = false;
+
+ for (iterator i = segment().findTime(t); i != end(); ++i) {
+
+ if (!haveStartNotationTime) {
+ notationTime = (*i)->getNotationAbsoluteTime();
+ fillWithRestsTo = notationTime + (untupled * unit);
+ haveStartNotationTime = true;
+ }
+
+ if ((*i)->getNotationAbsoluteTime() >=
+ notationTime + (untupled * unit)) break;
+
+ timeT offset = (*i)->getNotationAbsoluteTime() - notationTime;
+ timeT duration = (*i)->getNotationDuration();
+
+ if ((*i)->isa(Note::EventRestType) &&
+ ((offset + duration) > (untupled * unit))) {
+ fillWithRestsTo = std::max(fillWithRestsTo,
+ notationTime + offset + duration);
+ duration = (untupled * unit) - offset;
+ if (duration <= 0) {
+ toErase.push_back(i);
+ continue;
+ }
+ }
+
+ Event *e = new Event(**i,
+ notationTime + (offset * tupled / untupled),
+ duration * tupled / untupled);
+
+ cerr << "SegmentNotationHelper::makeTupletGroup: made event at time " << e->getAbsoluteTime() << ", duration " << e->getDuration() << endl;
+
+ e->set<Int>(BEAMED_GROUP_ID, groupId);
+ e->set<String>(BEAMED_GROUP_TYPE, GROUP_TYPE_TUPLED);
+
+ e->set<Int>(BEAMED_GROUP_TUPLET_BASE, unit);
+ e->set<Int>(BEAMED_GROUP_TUPLED_COUNT, tupled);
+ e->set<Int>(BEAMED_GROUP_UNTUPLED_COUNT, untupled);
+
+ toInsert.push_back(e);
+ toErase.push_back(i);
+ }
+
+ for (list<iterator>::iterator i = toErase.begin();
+ i != toErase.end(); ++i) {
+ segment().erase(*i);
+ }
+
+ for (list<Event *>::iterator i = toInsert.begin();
+ i != toInsert.end(); ++i) {
+ segment().insert(*i);
+ }
+
+ if (haveStartNotationTime) {
+ segment().fillWithRests(notationTime + (tupled * unit),
+ fillWithRestsTo);
+ }
+}
+
+
+
+
+void
+SegmentNotationHelper::unbeam(timeT from, timeT to)
+{
+ unbeamAux(segment().findTime(from), segment().findTime(to));
+}
+
+void
+SegmentNotationHelper::unbeam(iterator from, iterator to)
+{
+ unbeamAux
+ ((from == end()) ? from : segment().findTime((*from)->getAbsoluteTime()),
+ (to == end()) ? to : segment().findTime((*to )->getAbsoluteTime()));
+}
+
+void
+SegmentNotationHelper::unbeamAux(iterator from, iterator to)
+{
+ for (iterator i = from; i != to; ++i) {
+ (*i)->unset(BEAMED_GROUP_ID);
+ (*i)->unset(BEAMED_GROUP_TYPE);
+ (*i)->clearNonPersistentProperties();
+ }
+}
+
+
+
+/*
+
+ Auto-beaming code derived from Rosegarden 2.1's ItemListAutoBeam
+ and ItemListAutoBeamSub in editor/src/ItemList.c.
+
+*/
+
+void
+SegmentNotationHelper::autoBeam(timeT from, timeT to, string type)
+{
+ /*
+ std::cerr << "autoBeam from " << from << " to " << to << " on segment start time " << segment().getStartTime() << ", end time " << segment().getEndTime() << ", end marker " << segment().getEndMarkerTime() << std::endl;
+ */
+
+ autoBeam(segment().findTime(from), segment().findTime(to), type);
+}
+
+void
+SegmentNotationHelper::autoBeam(iterator from, iterator to, string type)
+{
+ // This can only manage whole bars at a time, and it will split
+ // the from-to range out to encompass the whole bars in which they
+ // each occur
+
+ if (!segment().getComposition()) {
+ cerr << "WARNING: SegmentNotationHelper::autoBeam requires Segment be in a Composition" << endl;
+ return;
+ }
+
+ if (!segment().isBeforeEndMarker(from)) return;
+
+ Composition *comp = segment().getComposition();
+
+ int fromBar = comp->getBarNumber((*from)->getAbsoluteTime());
+ int toBar = comp->getBarNumber(segment().isBeforeEndMarker(to) ?
+ (*to)->getAbsoluteTime() :
+ segment().getEndMarkerTime());
+
+ for (int barNo = fromBar; barNo <= toBar; ++barNo) {
+
+ std::pair<timeT, timeT> barRange = comp->getBarRange(barNo);
+ iterator barStart = segment().findTime(barRange.first);
+ iterator barEnd = segment().findTime(barRange.second);
+
+ // Make sure we're examining the notes defined to be within
+ // the bar in notation terms rather than raw terms
+
+ while (barStart != segment().end() &&
+ (*barStart)->getNotationAbsoluteTime() < barRange.first) ++barStart;
+
+ iterator scooter = barStart;
+ if (barStart != segment().end()) {
+ while (scooter != segment().begin()) {
+ --scooter;
+ if ((*scooter)->getNotationAbsoluteTime() < barRange.first) break;
+ barStart = scooter;
+ }
+ }
+
+ while (barEnd != segment().end() &&
+ (*barEnd)->getNotationAbsoluteTime() < barRange.second) ++barEnd;
+
+ scooter = barEnd;
+ if (barEnd != segment().end()) {
+ while (scooter != segment().begin()) {
+ --scooter;
+ if ((*scooter)->getNotationAbsoluteTime() < barRange.second) break;
+ barEnd = scooter;
+ }
+ }
+
+ TimeSignature timeSig =
+ segment().getComposition()->getTimeSignatureAt(barRange.first);
+
+ autoBeamBar(barStart, barEnd, timeSig, type);
+ }
+}
+
+
+/*
+
+ Derived from (and no less mystifying than) Rosegarden 2.1's
+ ItemListAutoBeamSub in editor/src/ItemList.c.
+
+ "Today I want to celebrate "Montreal" by Autechre, because of
+ its sleep-disturbing aura, because it sounds like the sort of music
+ which would be going around in the gunman's head as he trains a laser
+ sight into your bedroom through the narrow gap in your curtains and
+ dances the little red dot around nervously on your wall."
+
+*/
+
+void
+SegmentNotationHelper::autoBeamBar(iterator from, iterator to,
+ TimeSignature tsig, string type)
+{
+ int num = tsig.getNumerator();
+ int denom = tsig.getDenominator();
+
+ timeT average;
+ timeT minimum = 0;
+
+ // If the denominator is 2 or 4, beam in twos (3/4, 6/2 etc).
+
+ if (denom == 2 || denom == 4) {
+
+ if (num % 3) {
+ average = Note(Note::Quaver).getDuration();
+ } else {
+ average = Note(Note::Semiquaver).getDuration();
+ minimum = average;
+ }
+
+ } else {
+
+ if (num == 6 && denom == 8) { // special hack for 6/8
+ average = 3 * Note(Note::Quaver).getDuration();
+
+ } else {
+ // find a divisor (at least 2) for the numerator
+ int n = 2;
+ while (num >= n && num % n != 0) ++n;
+ average = n * Note(Note::Semiquaver).getDuration();
+ }
+ }
+
+ if (minimum == 0) minimum = average / 2;
+ if (denom > 4) average /= 2;
+
+ autoBeamBar(from, to, average, minimum, average * 4, type);
+}
+
+
+void
+SegmentNotationHelper::autoBeamBar(iterator from, iterator to,
+ timeT average, timeT minimum,
+ timeT maximum, string type)
+{
+ timeT accumulator = 0;
+ timeT crotchet = Note(Note::Crotchet).getDuration();
+ timeT semiquaver = Note(Note::Semiquaver).getDuration();
+
+ iterator e = end();
+
+ for (iterator i = from; i != to && i != e; ++i) {
+
+ // only look at one note in each chord, and at rests
+ if (!hasEffectiveDuration(i)) continue;
+ timeT idur = (*i)->getNotationDuration();
+
+ if (accumulator % average == 0 && // "beamable duration" threshold
+ idur < crotchet) {
+
+ // This could be the start of a beamed group. We maintain
+ // two sorts of state as we scan along here: data about
+ // the best group we've found so far (beamDuration,
+ // prospective, k etc), and data about the items we're
+ // looking at (count, beamable, longerThanDemi etc) just
+ // in case we find a better candidate group before the
+ // eight-line conditional further down makes us give up
+ // the search, beam our best shot, and start again.
+
+ // I hope this is clear.
+
+ iterator k = end(); // best-so-far last item in group;
+ // end() indicates that we've found nothing
+
+ timeT tmin = minimum;
+ timeT count = 0;
+ timeT prospective = 0;
+ timeT beamDuration = 0;
+
+ int beamable = 0;
+ int longerThanDemi = 0;
+
+ for (iterator j = i; j != to; ++j) {
+
+ if (!hasEffectiveDuration(j)) continue;
+ timeT jdur = (*j)->getNotationDuration();
+
+ if ((*j)->isa(Note::EventType)) {
+ if (jdur < crotchet) ++beamable;
+ if (jdur >= semiquaver) ++longerThanDemi;
+ }
+
+ count += jdur;
+
+ if (count % tmin == 0) {
+
+ k = j;
+ beamDuration = count;
+ prospective = accumulator + count;
+
+ // found a group; now accept only double this
+ // group's length for a better one
+ tmin *= 2;
+ }
+
+ // Stop scanning and make the group if our scan has
+ // reached the maximum length of beamed group, we have
+ // more than 4 semis or quavers, we're at the end of
+ // our run, the next chord is longer than the current
+ // one, or there's a rest ahead. (We used to check
+ // that the rest had non-zero duration, but the new
+ // quantization regime should ensure that this doesn't
+ // happen unless we really are displaying completely
+ // unquantized data in which case anything goes.)
+
+ iterator jnext(j);
+
+ if ((count > maximum)
+ || (longerThanDemi > 4)
+ || (++jnext == to)
+ || ((*j )->isa(Note::EventType) &&
+ (*jnext)->isa(Note::EventType) &&
+ (*jnext)->getNotationDuration() > jdur)
+ || ((*jnext)->isa(Note::EventRestType))) {
+
+ if (k != end() && beamable >= 2) {
+
+ iterator knext(k);
+ ++knext;
+
+ makeBeamedGroup(i, knext, type);
+ }
+
+ // If this group is at least as long as the check
+ // threshold ("average"), its length must be a
+ // multiple of the threshold and hence we can
+ // continue scanning from the end of the group
+ // without losing the modulo properties of the
+ // accumulator.
+
+ if (k != end() && beamDuration >= average) {
+
+ i = k;
+ accumulator = prospective;
+
+ } else {
+
+ // Otherwise, we continue from where we were.
+ // (This must be safe because we can't get
+ // another group starting half-way through, as
+ // we know the last group is shorter than the
+ // check threshold.)
+
+ accumulator += idur;
+ }
+
+ break;
+ }
+ }
+ } else {
+
+ accumulator += idur;
+ }
+ }
+}
+
+
+// based on Rosegarden 2.1's GuessItemListClef in editor/src/MidiIn.c
+
+Clef
+SegmentNotationHelper::guessClef(iterator from, iterator to)
+{
+ long totalHeight = 0;
+ int noteCount = 0;
+
+ // just the defaults:
+ Clef clef;
+ Key key;
+
+ for (iterator i = from; i != to; ++i) {
+ if ((*i)->isa(Note::EventType)) {
+//!!! NotationDisplayPitch p((*i)->get<Int>(PITCH), clef, key);
+ try {
+ Pitch p(**i);
+ totalHeight += p.getHeightOnStaff(clef, key);
+ ++noteCount;
+ } catch (Exception e) {
+ // no pitch in note
+ }
+ }
+ }
+
+ if (noteCount == 0) return Clef(Clef::Treble);
+
+ int average = totalHeight / noteCount;
+
+ if (average < -6) return Clef(Clef::Bass);
+ else if (average < -3) return Clef(Clef::Tenor);
+ else if (average < 1) return Clef(Clef::Alto);
+ else return Clef(Clef::Treble);
+}
+
+
+bool
+SegmentNotationHelper::removeRests(timeT time, timeT &duration, bool testOnly)
+{
+ Event dummy("dummy", time, 0, MIN_SUBORDERING);
+
+ std::cerr << "SegmentNotationHelper::removeRests(" << time
+ << ", " << duration << ")" << std::endl;
+
+ iterator from = segment().lower_bound(&dummy);
+
+ // ignore any number of zero-duration events at the start
+ while (from != segment().end() &&
+ (*from)->getAbsoluteTime() == time &&
+ (*from)->getDuration() == 0) ++from;
+ if (from == segment().end()) return false;
+
+ iterator to = from;
+
+ timeT eventTime = time;
+ timeT finalTime = time + duration;
+
+ //!!! We should probably not use an accumulator, but instead
+ // calculate based on each event's absolute time + duration --
+ // in case we've somehow ended up with overlapping rests
+
+ // Iterate on events, checking if all are rests
+ //
+ while ((eventTime < finalTime) && (to != end())) {
+
+ if (!(*to)->isa(Note::EventRestType)) {
+ // a non-rest was found
+ duration = (*to)->getAbsoluteTime() - time;
+ return false;
+ }
+
+ timeT nextEventDuration = (*to)->getDuration();
+
+ if ((eventTime + nextEventDuration) <= finalTime) {
+ eventTime += nextEventDuration;
+ duration = eventTime - time;
+ } else break;
+
+ ++to;
+ }
+
+ bool checkLastRest = false;
+ iterator lastEvent = to;
+
+ if (eventTime < finalTime) {
+ // shorten last event's duration, if possible
+
+
+ if (lastEvent == end()) {
+ duration = segment().getEndTime() - time;
+ return false;
+ }
+
+ if (!testOnly) {
+ // can't safely change the absolute time of an event in a segment
+ Event *newEvent = new Event(**lastEvent, finalTime,
+ (*lastEvent)->getDuration() -
+ (finalTime - eventTime));
+ duration = finalTime + (*lastEvent)->getDuration() - time;
+ bool same = (from == to);
+ segment().erase(lastEvent);
+ to = lastEvent = segment().insert(newEvent);
+ if (same) from = to;
+ checkLastRest = true;
+ }
+ }
+
+ if (testOnly) return true;
+
+ segment().erase(from, to);
+
+ // we must defer calling makeRestViable() until after erase,
+ // because it will invalidate 'to'
+ //
+ if (checkLastRest) makeRestViable(lastEvent);
+
+ return true;
+}
+
+
+void
+SegmentNotationHelper::collapseRestsAggressively(timeT startTime,
+ timeT endTime)
+{
+ reorganizeRests(startTime, endTime,
+ &SegmentNotationHelper::mergeContiguousRests);
+}
+
+
+void
+SegmentNotationHelper::reorganizeRests(timeT startTime, timeT endTime,
+ Reorganizer reorganizer)
+{
+ iterator ia = segment().findTime(startTime);
+ iterator ib = segment().findTime(endTime);
+
+ if (ia == end()) return;
+
+ std::vector<iterator> erasable;
+ std::vector<Event *> insertable;
+
+// cerr << "SegmentNotationHelper::reorganizeRests (" << startTime << ","
+// << endTime << ")" << endl;
+
+ for (iterator i = ia; i != ib; ++i) {
+
+ if ((*i)->isa(Note::EventRestType)) {
+
+ timeT startTime = (*i)->getAbsoluteTime();
+ timeT duration = 0;
+ iterator j = i;
+
+ for ( ; j != ib; ++j) {
+
+ if ((*j)->isa(Note::EventRestType)) {
+ duration += (*j)->getDuration();
+ erasable.push_back(j);
+ } else break;
+ }
+
+ (this->*reorganizer)(startTime, duration, insertable);
+ if (j == ib) break;
+ i = j;
+ }
+ }
+
+ for (unsigned int ei = 0; ei < erasable.size(); ++ei)
+ segment().erase(erasable[ei]);
+
+ for (unsigned int ii = 0; ii < insertable.size(); ++ii)
+ segment().insert(insertable[ii]);
+}
+
+
+void
+SegmentNotationHelper::normalizeContiguousRests(timeT startTime,
+ timeT duration,
+ std::vector<Event *> &toInsert)
+{
+ TimeSignature ts;
+ timeT sigTime =
+ segment().getComposition()->getTimeSignatureAt(startTime, ts);
+
+// cerr << "SegmentNotationHelper::normalizeContiguousRests:"
+// << " startTime = " << startTime << ", duration = "
+// << duration << endl;
+
+ DurationList dl;
+ ts.getDurationListForInterval(dl, duration, startTime - sigTime);
+
+ timeT acc = startTime;
+
+ for (DurationList::iterator i = dl.begin(); i != dl.end(); ++i) {
+ Event *e = new Event(Note::EventRestType, acc, *i,
+ Note::EventRestSubOrdering);
+ toInsert.push_back(e);
+ acc += *i;
+ }
+}
+
+
+void
+SegmentNotationHelper::mergeContiguousRests(timeT startTime,
+ timeT duration,
+ std::vector<Event *> &toInsert)
+{
+ while (duration > 0) {
+
+ timeT d = Note::getNearestNote(duration).getDuration();
+
+ Event *e = new Event(Note::EventRestType, startTime, d,
+ Note::EventRestSubOrdering);
+ toInsert.push_back(e);
+
+ startTime += d;
+ duration -= d;
+ }
+}
+
+
+Segment::iterator
+SegmentNotationHelper::collapseNoteAggressively(Event *note,
+ timeT rangeEnd)
+{
+ iterator i = segment().findSingle(note);
+ if (i == end()) return end();
+
+ iterator j = getNextAdjacentNote(i, true, true);
+ if (j == end() || (*j)->getAbsoluteTime() >= rangeEnd) return end();
+
+ timeT iEnd = (*i)->getAbsoluteTime() + (*i)->getDuration();
+ timeT jEnd = (*j)->getAbsoluteTime() + (*j)->getDuration();
+
+ Event *newEvent = new Event
+ (**i, (*i)->getAbsoluteTime(),
+ (std::max(iEnd, jEnd) - (*i)->getAbsoluteTime()));
+
+ newEvent->unset(TIED_BACKWARD);
+ newEvent->unset(TIED_FORWARD);
+
+ segment().erase(i);
+ segment().erase(j);
+ return segment().insert(newEvent);
+}
+
+std::pair<Event *, Event *>
+SegmentNotationHelper::splitPreservingPerformanceTimes(Event *e, timeT q1)
+{
+ timeT ut = e->getAbsoluteTime();
+ timeT ud = e->getDuration();
+ timeT qt = e->getNotationAbsoluteTime();
+ timeT qd = e->getNotationDuration();
+
+ timeT u1 = (qt + q1) - ut;
+ timeT u2 = (ut + ud) - (qt + q1);
+
+// std::cerr << "splitPreservingPerformanceTimes: (ut,ud) (" << ut << "," << ud << "), (qt,qd) (" << qt << "," << qd << ") q1 " << q1 << ", u1 " << u1 << ", u2 " << u2 << std::endl;
+
+ if (u1 <= 0 || u2 <= 0) { // can't do a meaningful split
+ return std::pair<Event *, Event *>(0, 0);
+ }
+
+ Event *e1 = new Event(*e, ut, u1, e->getSubOrdering(), qt, q1);
+ Event *e2 = new Event(*e, ut + u1, u2, e->getSubOrdering(), qt + q1, qd - q1);
+
+ e1->set<Bool>(TIED_FORWARD, true);
+ e2->set<Bool>(TIED_BACKWARD, true);
+
+ return std::pair<Event *, Event *>(e1, e2);
+}
+
+void
+SegmentNotationHelper::deCounterpoint(timeT startTime, timeT endTime)
+{
+ // How this should work: scan through the range and, for each
+ // note "n" found, if the next following note "m" not at the same
+ // absolute time as n starts before n ends, then split n at m-n.
+
+ // also, if m starts at the same time as n but has a different
+ // duration, we should split the longer of n and m at the shorter
+ // one's duration.
+
+ for (Segment::iterator i = segment().findTime(startTime);
+ segment().isBeforeEndMarker(i); ) {
+
+ timeT t = (*i)->getAbsoluteTime();
+ if (t >= endTime) break;
+
+#ifdef DEBUG_DECOUNTERPOINT
+ std::cerr << "SegmentNotationHelper::deCounterpoint: event at " << (*i)->getAbsoluteTime() << " notation " << (*i)->getNotationAbsoluteTime() << ", duration " << (*i)->getNotationDuration() << ", type " << (*i)->getType() << std::endl;
+#endif
+
+ if (!(*i)->isa(Note::EventType)) { ++i; continue; }
+
+ timeT ti = (*i)->getNotationAbsoluteTime();
+ timeT di = (*i)->getNotationDuration();
+
+#ifdef DEBUG_DECOUNTERPOINT
+ std::cerr<<"looking for k"<<std::endl;
+#endif
+
+ // find next event that's either at a different time or (if a
+ // note) has a different duration
+ Segment::iterator k = i;
+ while (segment().isBeforeEndMarker(k)) {
+ if ((*k)->isa(Note::EventType)) {
+#ifdef DEBUG_DECOUNTERPOINT
+ std::cerr<<"abstime "<<(*k)->getAbsoluteTime()<< std::endl;
+#endif
+ if ((*k)->getNotationAbsoluteTime() > ti ||
+ (*k)->getNotationDuration() != di) break;
+ }
+ ++k;
+ }
+
+ if (!segment().isBeforeEndMarker(k)) break; // no split, no more notes
+
+#ifdef DEBUG_DECOUNTERPOINT
+ std::cerr << "k is at " << (k == segment().end() ? -1 : (*k)->getAbsoluteTime()) << ", notation " << (*k)->getNotationAbsoluteTime() << ", duration " << (*k)->getNotationDuration() << std::endl;
+#endif
+
+ timeT tk = (*k)->getNotationAbsoluteTime();
+ timeT dk = (*k)->getNotationDuration();
+
+ Event *e1 = 0, *e2 = 0;
+ std::pair<Event *, Event *> splits;
+ Segment::iterator toGo = segment().end();
+
+ if (tk == ti && dk != di) {
+ // do the same-time-different-durations case
+ if (di > dk) { // split *i
+#ifdef DEBUG_DECOUNTERPOINT
+ std::cerr << "splitting i into " << dk << " and "<< (di-dk) << std::endl;
+#endif
+ splits = splitPreservingPerformanceTimes(*i, dk);
+
+ toGo = i;
+ } else { // split *k
+#ifdef DEBUG_DECOUNTERPOINT
+ std::cerr << "splitting k into " << di << " and "<< (dk-di) << std::endl;
+#endif
+ splits = splitPreservingPerformanceTimes(*k, di);
+
+ toGo = k;
+ }
+ } else if (tk - ti > 0 && tk - ti < di) { // split *i
+#ifdef DEBUG_DECOUNTERPOINT
+ std::cerr << "splitting i[*] into " << (tk-ti) << " and "<< (di-(tk-ti)) << std::endl;
+#endif
+ splits = splitPreservingPerformanceTimes(*i, tk - ti);
+
+ toGo = i;
+ }
+
+ e1 = splits.first;
+ e2 = splits.second;
+
+ if (e1 && e2) { // e2 is the new note
+
+ e1->set<Bool>(TIED_FORWARD, true);
+ e2->set<Bool>(TIED_BACKWARD, true);
+
+#ifdef DEBUG_DECOUNTERPOINT
+ std::cerr<<"Erasing:"<<std::endl;
+ (*toGo)->dump(std::cerr);
+#endif
+
+ segment().erase(toGo);
+
+#ifdef DEBUG_DECOUNTERPOINT
+ std::cerr<<"Inserting:"<<std::endl;
+ e1->dump(std::cerr);
+#endif
+
+ segment().insert(e1);
+
+#ifdef DEBUG_DECOUNTERPOINT
+ std::cerr<<"Inserting:"<<std::endl;
+ e2->dump(std::cerr);
+#endif
+
+ segment().insert(e2);
+
+ i = segment().findTime(t);
+
+#ifdef DEBUG_DECOUNTERPOINT
+ std::cerr<<"resync at " << t << ":" << std::endl;
+ if (i != segment().end()) (*i)->dump(std::cerr);
+ else std::cerr << "(end)" << std::endl;
+#endif
+
+ } else {
+
+ // no split here
+
+#ifdef DEBUG_DECOUNTERPOINT
+ std::cerr<<"no split"<<std::endl;
+#endif
+ ++i;
+ }
+ }
+
+ segment().normalizeRests(startTime, endTime);
+}
+
+
+void
+SegmentNotationHelper::autoSlur(timeT startTime, timeT endTime, bool legatoOnly)
+{
+ iterator from = segment().findTime(startTime);
+ iterator to = segment().findTime(endTime);
+
+ timeT potentialStart = segment().getEndTime();
+ long groupId = -1;
+ timeT prevTime = startTime;
+ int count = 0;
+ bool thisLegato = false, prevLegato = false;
+
+ for (iterator i = from; i != to && segment().isBeforeEndMarker(i); ++i) {
+
+ timeT t = (*i)->getNotationAbsoluteTime();
+
+ long newGroupId = -1;
+ if ((*i)->get<Int>(BEAMED_GROUP_ID, newGroupId)) {
+ if (groupId == newGroupId) { // group continuing
+ if (t > prevTime) {
+ ++count;
+ prevLegato = thisLegato;
+ thisLegato = Marks::hasMark(**i, Marks::Tenuto);
+ }
+ prevTime = t;
+ continue;
+ }
+ } else {
+ if (groupId == -1) continue; // no group
+ }
+
+ // a group has ended (and a new one might have begun)
+
+ if (groupId >= 0 && count > 1 && (!legatoOnly || prevLegato)) {
+ Indication ind(Indication::Slur, t - potentialStart);
+ segment().insert(ind.getAsEvent(potentialStart));
+ if (legatoOnly) {
+ for (iterator j = segment().findTime(potentialStart); j != i; ++j) {
+ Marks::removeMark(**j, Marks::Tenuto);
+ }
+ }
+ }
+
+ potentialStart = t;
+ groupId = newGroupId;
+ prevTime = t;
+ count = 0;
+ thisLegato = false;
+ prevLegato = false;
+ }
+
+ if (groupId >= 0 && count > 1 && (!legatoOnly || prevLegato)) {
+ Indication ind(Indication::Slur, endTime - potentialStart);
+ segment().insert(ind.getAsEvent(potentialStart));
+ if (legatoOnly) {
+ for (iterator j = segment().findTime(potentialStart);
+ segment().isBeforeEndMarker(j) && j != to; ++j) {
+ Marks::removeMark(**j, Marks::Tenuto);
+ }
+ }
+ }
+}
+
+
+} // end of namespace
+