/* 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. */ #include "SegmentNotationHelper.h" #include "NotationTypes.h" #include "Quantizer.h" #include "BasicQuantizer.h" #include "NotationQuantizer.h" #include "BaseProperties.h" #include "Composition.h" #include #include #include #include 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(BEAMED_GROUP_TUPLED_COUNT); int ucount = (*i)->get(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(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(NOTE_TYPE, n.getNoteType()); (*i)->setMaybe(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(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(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(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(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(forwards ? BaseProperties::TIED_FORWARD : BaseProperties::TIED_BACKWARD, tied) || !tied) { return end(); } timeT myTime = note->getAbsoluteTime(); timeT myDuration = note->getDuration(); int myPitch = note->get(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(forwards ? BaseProperties::TIED_BACKWARD : BaseProperties::TIED_FORWARD, tied) || !tied) { continue; } if ((*i)->get(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(BEAMED_GROUP_ID, firstGroupId); long nextGroupId = -1; iterator ni(to); if (segment().isBeforeEndMarker(ni) && segment().isBeforeEndMarker(++ni)) { (*ni)->get(BEAMED_GROUP_ID, nextGroupId); } list toInsert; list 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 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(TIED_BACKWARD, true); eva->set(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 i = toErase.begin(); i != toErase.end(); ++i) { segment().erase(*i); } from = end(); iterator last = end(); // now insert the new events for (list::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 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(TIED_FORWARD, lastTiedForward); e->set(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 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(TIED_BACKWARD, true); } delete e; } for (std::vector::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(PITCH, pitch); e->set(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(BEAMED_GROUP_TUPLED_COUNT) / (*i)->get(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(BEAMED_GROUP_TUPLED_COUNT) / (*i)->get(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(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(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(BEAMED_GROUP_TYPE); if (type != GROUP_TYPE_TUPLED && !(*i)->isa(Note::EventType)) { if ((*i)->isa(Note::EventRestType)) return; else { ++i; continue; } } e->set(BEAMED_GROUP_ID, (*i)->get(BEAMED_GROUP_ID)); e->set(BEAMED_GROUP_TYPE, type); if ((*i)->has(BEAMED_GROUP_TUPLET_BASE)) { e->set(BEAMED_GROUP_TUPLET_BASE, (*i)->get(BEAMED_GROUP_TUPLET_BASE)); e->set(BEAMED_GROUP_TUPLED_COUNT, (*i)->get(BEAMED_GROUP_TUPLED_COUNT)); e->set(BEAMED_GROUP_UNTUPLED_COUNT, (*i)->get(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(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(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" <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(BEAMED_GROUP_TYPE) != GROUP_TYPE_BEAMED) { continue; } if (!groupGraces) { if ((*i)->has(IS_GRACE_NOTE) && (*i)->get(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" <getType() == Note::EventType && (*j)->getNotationAbsoluteTime() > (*i)->getNotationAbsoluteTime() && (*j)->getNotationDuration() < Note(Note::Crotchet).getDuration()) { somethingLeft = true; break; } } if (!somethingLeft) continue; } // std::cerr << "beaming it" <set(BEAMED_GROUP_ID, groupId); (*i)->set(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 toInsert; list 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(BEAMED_GROUP_ID, groupId); e->set(BEAMED_GROUP_TYPE, GROUP_TYPE_TUPLED); e->set(BEAMED_GROUP_TUPLET_BASE, unit); e->set(BEAMED_GROUP_TUPLED_COUNT, tupled); e->set(BEAMED_GROUP_UNTUPLED_COUNT, untupled); toInsert.push_back(e); toErase.push_back(i); } for (list::iterator i = toErase.begin(); i != toErase.end(); ++i) { segment().erase(*i); } for (list::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 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(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 erasable; std::vector 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 &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 &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 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(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(TIED_FORWARD, true); e2->set(TIED_BACKWARD, true); return std::pair(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"<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 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(TIED_FORWARD, true); e2->set(TIED_BACKWARD, true); #ifdef DEBUG_DECOUNTERPOINT std::cerr<<"Erasing:"<dump(std::cerr); #endif segment().erase(toGo); #ifdef DEBUG_DECOUNTERPOINT std::cerr<<"Inserting:"<dump(std::cerr); #endif segment().insert(e1); #ifdef DEBUG_DECOUNTERPOINT std::cerr<<"Inserting:"<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"<getNotationAbsoluteTime(); long newGroupId = -1; if ((*i)->get(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