You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rosegarden/src/base/SegmentNotationHelper.cpp

2128 lines
57 KiB

/*
Rosegarden
A sequencer and musical notation editor.
This program is Copyright 2000-2008
Guillaume Laurent <glaurent@telegraph-road.org>,
Chris Cannam <cannam@all-day-breakfast.com>,
Richard Bown <bownie@bownie.com>
The moral right of the authors to claim authorship of this work
has been asserted.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#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