Logo Search packages:      
Sourcecode: rosegarden version File versions  Download package

SegmentNotationHelper.C

// -*- c-basic-offset: 4 -*-

/*
    Rosegarden-4
    A sequencer and musical notation editor.

    This program is Copyright 2000-2006
        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 "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);
}

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);
}

void
SegmentNotationHelper::makeBeamedGroupAux(iterator from, iterator to,
                                string type)
{
    int groupId = segment().getNextId();
    bool beamedSomething = false;

    for (iterator i = from; i != to; ++i) {

      // 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;
      }

      // 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()) {
          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;
      }

        (*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);
    
//    cerr << "SegmentNotationHelper::removeRests(" << time
//         << ", " << duration << ")\n";

    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


Generated by  Doxygen 1.6.0   Back to index