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.
2128 lines
57 KiB
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
|
|
|