timeT
MatrixPercussionInsertionCommand::getEffectiveStartTime(Segment &segment,
        timeT time,
        Event &event)
{
    timeT startTime = time;

    int pitch = 0;
    if (event.has(PITCH)) {
        pitch = event.get<Int>(PITCH);
    }

    Segment::iterator i = segment.findTime(time);
    while (i != segment.begin()) {
        --i;

        if ((*i)->has(PITCH) &&
                (*i)->get
                <Int>(PITCH) == pitch) {

            if ((*i)->getAbsoluteTime() < time &&
                    (*i)->isa(Note::EventType)) {
                if ((*i)->getAbsoluteTime() + (*i)->getDuration() > time) {
                    startTime = (*i)->getAbsoluteTime();
                } else {
                    break;
                }
            }
        }
    }

    return startTime;
}
Beispiel #2
0
void 
SegmentLinker::refreshSegment(Segment *seg)
{
    timeT startTime = seg->getStartTime();
    eraseNonIgnored(seg,seg->begin(),seg->end());
    
    //find another segment
    Segment *sourceSeg = 0;
    Segment *tempClone = 0;
    
    LinkedSegmentParamsList::iterator itr;
    for (itr = m_linkedSegmentParamsList.begin(); 
        itr!= m_linkedSegmentParamsList.end(); ++itr) {
        
        LinkedSegmentParams &linkedSegParams = *itr;
        Segment *other = linkedSegParams.m_linkedSegment;
        if (other != seg) {
            sourceSeg = other;
            break;
        }
    }
    
    if (!sourceSeg) {
        //make a temporary clone
        tempClone = createLinkedSegment(seg);
        sourceSeg = tempClone;
    }
        
    timeT sourceSegStartTime = sourceSeg->getStartTime();
    Segment::const_iterator segitr;
    for(segitr=sourceSeg->begin(); segitr!=sourceSeg->end(); ++segitr) {
        const Event *refEvent = *segitr;

        timeT refEventTime = refEvent->getAbsoluteTime() - sourceSegStartTime;
        timeT freshEventTime = refEventTime+startTime;
        
        insertMappedEvent(seg,refEvent,freshEventTime,
                          seg->getLinkTransposeParams().m_semitones,
                          seg->getLinkTransposeParams().m_steps);
    }
    
    if (tempClone) {
        delete tempClone;
    }
}
Beispiel #3
0
Segment *
Clipboard::newSegment(const EventSelection *copyFrom)
{
    // create with clone function so as to inherit track, instrument etc
    // but clone as a segment only, even if it's actually a linked segment
    Segment *s = copyFrom->getSegment().clone(false);
    s->erase(s->begin(), s->end());

    const EventSelection::eventcontainer &events(copyFrom->getSegmentEvents());
    for (EventSelection::eventcontainer::const_iterator i = events.begin();
         i != events.end(); ++i) {
        s->insert(new Event(**i));
    }

    m_segments.insert(s);
    m_partial = true;
    return s;
}
Beispiel #4
0
void
Clipboard::newSegment(const Segment *copyFrom, timeT from, timeT to,
    bool expandRepeats)
{
    // create with copy ctor so as to inherit track, instrument etc
    Segment *s = copyFrom->clone(false);

    // If the segment is within the time range
    if (from <= s->getStartTime() && to >= s->getEndMarkerTime()) {
        // Insert the whole thing.
        m_segments.insert(s);

        // don't change m_partial as we are inserting a complete segment

        return;
    }

    // Only a portion of the source segment will be used.

    const timeT segStart = copyFrom->getStartTime();
    const timeT segEndMarker = copyFrom->getEndMarkerTime();
    timeT segDuration = segEndMarker - segStart;
    
    // We can't copy starting prior to the start of the segment.
    if (from < segStart)
        from = segStart;

    int firstRepeat = 0;
    int lastRepeat = 0;

    if (!copyFrom->isRepeating() || segDuration <= 0) {
        expandRepeats = false;
    }

    if (expandRepeats) {
        firstRepeat = (from - segStart) / segDuration;
        to = std::min(to, copyFrom->getRepeatEndTime());
        lastRepeat = (to - segStart) / segDuration;
    }

    s->setRepeating(false);
    
    if (s->getType() == Segment::Audio) {

        Composition *c = copyFrom->getComposition();

        for (int repeat = firstRepeat; repeat <= lastRepeat; ++repeat) {

            timeT wrappedFrom = segStart;
            timeT wrappedTo = segEndMarker;

            if (!expandRepeats) {
                wrappedFrom = from;
                wrappedTo = to;
            } else {
                if (repeat == firstRepeat) {
                    wrappedFrom = segStart + (from - segStart) % segDuration;
                }
                if (repeat == lastRepeat) {
                    wrappedTo = segStart + (to - segStart) % segDuration;
                }
            }

            if (wrappedFrom > segStart) {
                if (c) {
                    s->setAudioStartTime
                        (s->getAudioStartTime() +
                         c->getRealTimeDifference(segStart + repeat * segDuration,
                                                  from));
                }
                s->setStartTime(from);
            } else {
                s->setStartTime(segStart + repeat * segDuration);
            }

            if (wrappedTo < segEndMarker) {
                s->setEndMarkerTime(to);
                if (c) {
                    s->setAudioEndTime
                        (s->getAudioStartTime() +
                         c->getRealTimeDifference(segStart + repeat * segDuration,
                                                  to));
                }
            } else {
                s->setEndMarkerTime(segStart + (repeat + 1) * segDuration);
            }

            m_segments.insert(s);
            if (repeat < lastRepeat) {
                s = copyFrom->clone(false);
                s->setRepeating(false);
            }
        }

        m_partial = true;
        return;
    }

    // We have a normal (MIDI) segment.

    s->erase(s->begin(), s->end());
    
    for (int repeat = firstRepeat; repeat <= lastRepeat; ++repeat) {

        Segment::const_iterator ifrom = copyFrom->begin();
        Segment::const_iterator ito = copyFrom->end();

        if (!expandRepeats) {
            ifrom = copyFrom->findTime(from);
            ito = copyFrom->findTime(to);
        } else {
            if (repeat == firstRepeat) {
                ifrom = copyFrom->findTime
                    (segStart + (from - segStart) % segDuration);
            }
            if (repeat == lastRepeat) {
                ito = copyFrom->findTime
                    (segStart + (to - segStart) % segDuration);
            }
        }

        // For each event in the time range and before the end marker.
        for (Segment::const_iterator i = ifrom;
             i != ito && copyFrom->isBeforeEndMarker(i); ++i) {

            Event *e = (*i)->copyMoving(repeat * segDuration);

            s->insert(e);
        }
    }

    if (expandRepeats)
        s->setEndMarkerTime(to);

    // Make sure the end of the segment doesn't go past the end of the range.
    // Need to use the end marker time from the original segment, not s, 
    // because its value may depend on the composition it's in.
    if (segEndMarker > to)
        s->setEndMarkerTime(to);

    // Fix the beginning.
    
    timeT firstEventTime = s->getStartTime();
    
    // if the beginning was chopped off and the first event isn't at the start
    if (from > segStart  &&  firstEventTime > from) {
        // Expand the beginning to the left so that it starts at the expected
        // time (from).
        s->fillWithRests(from, firstEventTime);
    }

    // Fix zero-length segments.
    
    // if s is zero length
    if (s->getStartTime() == s->getEndMarkerTime()) {
        // Figure out what start and end time would look right.
        timeT finalStartTime = ((segStart > from) ? segStart : from);
        timeT finalEndTime = ((segEndMarker < to) ? segEndMarker : to);
        // Fill it up so it appears.
        s->fillWithRests(finalStartTime, finalEndTime);
    }

    m_segments.insert(s);

    m_partial = true;
}
Segment *
SegmentJoinCommand::makeSegment(SegmentVec oldSegments)
{
    // We can proceed even if composition is NULL, just normalize
    // rests will do less.
    Composition *composition = oldSegments[0]->getComposition();

    // Find the leftmost segment's index and the rightmost segment's
    // index.
    timeT t0 = oldSegments[0]->getStartTime();
    timeT t1 = oldSegments[0]->getEndMarkerTime();
    size_t leftmostIndex = 0;
    size_t rightmostIndex = 0;
    for (size_t i = 1; i < oldSegments.size(); ++i) {
        timeT startTime = oldSegments[i]->getStartTime();
        if (startTime < t0) {
            t0 = startTime;
            leftmostIndex = i;
        }
        timeT endMarkerTime = oldSegments[i]->getEndMarkerTime();
        if (endMarkerTime > t1) {
            t1 = endMarkerTime;
            rightmostIndex = i;
        }
    }

    // Always begin with the leftmost segment to keep in the new segment
    // any clef or key change found at the start of this segment.
    Segment *newSegment = oldSegments[leftmostIndex]->clone(false);

    // drop any events beyond the end marker
    newSegment->setEndTime(newSegment->getEndMarkerTime());

    // that duplicated the leftmost segment; now do the rest

    // we normalize rests in any overlapping areas
    timeT overlapStart = 0, overlapEnd = 0;
    bool haveOverlap = false;
    for (size_t i = 0; i < oldSegments.size(); ++i) {

        // Don't add twice the first old segment
        if (i == leftmostIndex) continue;

        Segment *s = oldSegments[i];

        timeT start = s->getStartTime(), end = s->getEndMarkerTime();

        timeT os = 0, oe = 0;
        bool haveOverlapHere = false;

        if (start < newSegment->getEndMarkerTime() &&
            end > newSegment->getStartTime()) {
            haveOverlapHere = true;
            os = std::max(start, newSegment->getStartTime());
            oe = std::min(end, newSegment->getEndMarkerTime());
            RG_DEBUG << "overlap here, os = " << os << ", oe = " << oe;
        }

        if (haveOverlapHere) {
            if (haveOverlap) {
                overlapStart = std::min(overlapStart, os);
                overlapEnd = std::max(overlapEnd, oe);
            } else {
                overlapStart = os;
                overlapEnd = oe;
                haveOverlap = true;
            }
        }

        if (start > newSegment->getEndMarkerTime()) {
            newSegment->setEndMarkerTime(start);
        }

        for (Segment::iterator si = s->begin(); ; ++si) {

            // If we're in the rightmost segment
            if (i == rightmostIndex) {
                // Copy all of the events to the end
                if (si == s->end())
                    break;
            } else {
                // Just copy up to the End Marker of this segment.
                if (!s->isBeforeEndMarker(si))
                    break;
            }

            // weed out duplicate clefs and keys

            if ((*si)->isa(Clef::EventType)) {
                try {
                    Clef newClef(**si);
                    if (newSegment->getClefAtTime
                        ((*si)->getAbsoluteTime() + 1) == newClef) {
                        continue;
                    }
                } catch (...) { }
            }

            if ((*si)->isa(Key::EventType)) {
                try {
                    Key newKey(**si);
                    if (newSegment->getKeyAtTime
                        ((*si)->getAbsoluteTime() + 1) == newKey) {
                        continue;
                    }
                } catch (...) { }
            }

            newSegment->insert(new Event(**si));
        }

        if (end > newSegment->getEndMarkerTime()) {
            newSegment->setEndMarkerTime(end);
        }
    }

    if (haveOverlap) {
        /// normalizeRests doesn't require Composition, but is less
        /// than ideal without it;
        newSegment->setComposition(composition);
        newSegment->normalizeRests(overlapStart, overlapEnd);
        newSegment->setComposition(0);        
    }

    return newSegment;
}
Beispiel #6
0
void
MatrixMover::handleLeftButtonPress(const MatrixMouseEvent *e)
{
    MATRIX_DEBUG << "MatrixMover::handleLeftButtonPress() : snapped time = " << e->snappedLeftTime << ", el = " << e->element << endl;

    if (!e->element) return;

    // Check the scene's current segment (apparently not necessarily the same
    // segment referred to by the scene's current view segment) for this event;
    // return if not found, indicating that this event is from some other,
    // non-active segment.
    //
    // I think notation just makes whatever segment active when you click an
    // event outside the active segment, and I think that's what this code
    // attempted to do too.  I couldn't get that to work at all.  This is better
    // than being able to click on non-active elements to create new events by
    // accident, and will probably fly.  Especially since the multi-segment
    // matrix is new, and we're defining the terms of how it works.
    Segment *segment = m_scene->getCurrentSegment();
    if (!segment) return;
    bool found = false;
    for (Segment::iterator i = segment->begin(); i != segment->end(); ++i) {
        if ((*i) == e->element->event()) found = true;
    }

    if (!found) {
        MATRIX_DEBUG << "Clicked element not owned by active segment.  Returning..." << endl;
        return;
    }

    m_currentViewSegment = e->viewSegment;

    m_currentElement = e->element;
    m_clickSnappedLeftTime = e->snappedLeftTime;

    m_quickCopy = (e->modifiers & Qt::ControlModifier);

    if (!m_duplicateElements.empty()) {
        for (size_t i = 0; i < m_duplicateElements.size(); ++i) {
            delete m_duplicateElements[i]->event();
            delete m_duplicateElements[i];
        }
        m_duplicateElements.clear();
    }

    // Add this element and allow movement
    //
    EventSelection* selection = m_scene->getSelection();
    Event *event = m_currentElement->event();

    if (selection) {
        EventSelection *newSelection;
        
        if ((e->modifiers & Qt::ShiftModifier) ||
            selection->contains(event)) {
            newSelection = new EventSelection(*selection);
        } else {
            newSelection = new EventSelection(m_currentViewSegment->getSegment());
        }
        
        // if the selection already contains the event, remove it from the
        // selection if shift is pressed
        if (selection->contains(event)) {
            if (e->modifiers & Qt::ShiftModifier) {
                newSelection->removeEvent(event);
            }
        } else {
            newSelection->addEvent(event);
        }
        m_scene->setSelection(newSelection, true);
        selection = newSelection;
    } else {
        m_scene->setSingleSelectedEvent(m_currentViewSegment,
                                        m_currentElement, true);
    }
    
    long velocity = m_widget->getCurrentVelocity();
    event->get<Int>(BaseProperties::VELOCITY, velocity);

    long pitchOffset = m_currentViewSegment->getSegment().getTranspose();

    long pitch = 60;
    event->get<Int>(BaseProperties::PITCH, pitch);

    // We used to m_scene->playNote() here, but the new concert pitch matrix was
    // playing chords the first time I clicked a note.  Investigation with
    // KMidiMon revealed two notes firing nearly simultaneously, and with
    // segments of 0 transpose, they were simply identical to each other.  One
    // of them came from here, and this was the one sounding at the wrong pitch
    // in transposed segments.  I've simply removed it with no apparent ill side
    // effects, and a problem solved super cheap.

    m_lastPlayedPitch = pitch;

    if (m_quickCopy && selection) {
        for (EventSelection::eventcontainer::iterator i =
                 selection->getSegmentEvents().begin();
             i != selection->getSegmentEvents().end(); ++i) {

            MatrixElement *duplicate = new MatrixElement
                (m_scene, new Event(**i),
                 m_widget->isDrumMode(), pitchOffset);

            m_duplicateElements.push_back(duplicate);
        }
    }
}
/// Initialize ExpandFigurationCommand
/// @author Tom Breton (Tehom)
void
ExpandFigurationCommand::initialise(SegmentSelection selection)
{
    FigurationVector figs;
    int figSourceID = -1;
    bool gotFigSource = false;

    RG_DEBUG << "Initializing ExpandFigurationCommand" << endl;

    // Update, because if we need new IDs they mustn't duplicate old
    // IDs, so we must be up to date on what IDs are there.
    SegmentFigData::SegmentFigDataMap segMap = 
        SegmentFigData::getInvolvedSegments(true, this);
    for (SegmentSelection::iterator i = selection.begin();
         i != selection.end();
         ++i) {
        SegmentFigData& segmentData =
            SegmentFigData::findOrAdd(segMap, *i);
        
        // If it's used here, it's not uninvolved, so unless it's a
        // figuration, it's a chord source.
        if (segmentData.isa(SegmentFigData::Uninvolved)) {
            segmentData.convertType(SegmentFigData::ChordSource);
        }
        segmentData.addTagIfNeeded(*i, this);
        if (gotFigSource ||
            !segmentData.isa(SegmentFigData::FigurationSource))
            { continue; }

        figSourceID = segmentData.getID();
        figs = FigurationSourceMap::getFigurations(*i);
        if (!figs.empty())
            { gotFigSource = true; }
    }
    // If we didn't find a real figuration, there's nothing to do.
    if (!gotFigSource) { return; }

    SourcedFiguration sourcedfigs(figSourceID, figs);
    
    // Expand figuration in each segment in selection except the
    // figuration segment itself.
    for (SegmentSelection::iterator i = selection.begin();
         i != selection.end();
         ++i) {
        Segment *s = (*i);
        SegmentFigData::SegmentFigDataMap::iterator it = segMap.find(*i);
        Q_ASSERT_X(it != segMap.end(),
                   "ExpandFigurationCommand::initialise",
                   "didn't find the segment");
        SegmentFigData& segmentData = it->second;
        
        if (!segmentData.isa(SegmentFigData::ChordSource))
            { continue; }

        // Make a target segment
        Segment *target = s->clone(false);
        // Temporarily attach it to composition so that expand can
        // work.
        m_composition->weakAddSegment(target);

        target->clear();
        target->fillWithRests(s->getEndTime());
        SegmentFigData::addTag(target, this, SegmentID::Target);
        m_newSegments.insert(target);

        /** Add notes to target segment **/
        for (Segment::iterator e = s->begin();
             e != s->end();
             ++e) {
            // Non-notes they don't imply there's a chord here.
            // We add them to target segment in case they are
            // clefs or key changes.
            if ((*e)->isa(SegmentID::EventType)) {
                continue;
            }
            if (!(*e)->isa(Note::EventType)) {
                target->insert(new Event(**e));
            }
        }


        // rawStartTime is the apparent start time before we take bars
        // into account.  We step it along the composition, finding
        // valid places to expand.  Specifically, on bar lines not
        // already part of an expansion.
        timeT rawStartTime = s->getStartTime();
        while (1) {
            timeT figurationStartTime = rawStartTimeToExact(rawStartTime);

            if (rawStartTime >= s->getEndTime())
                { break; }

            timeT timePastFiguration =
                SegmentFigData::expand(sourcedfigs,
                                       ChordSegment(s, segmentData.getID()),
                                       target, figurationStartTime);

            // If we didn't expand, ensure we don't try endlessly at
            // the same place.
            if (timePastFiguration == figurationStartTime) {
                ++timePastFiguration;
            }
            rawStartTime = timePastFiguration;
        }

        // Detach from composition, because SegmentInsertCommand does
        // the actual placing
        m_composition->weakDetachSegment(target);

        
        Command *c =
            new SegmentInsertCommand(m_composition, target, s->getTrack());
        addCommand(c);
    }

}
DocumentMetaConfigurationPage::DocumentMetaConfigurationPage(RosegardenDocument *doc,
        QWidget *parent) :
    TabbedConfigurationPage(doc, parent)
{
    m_headersPage = new HeadersConfigurationPage(this, doc);
    addTab(m_headersPage, tr("Headers"));

    Composition &comp = doc->getComposition();
    std::set
    <TrackId> usedTracks;

    int audioSegments = 0, internalSegments = 0;
    for (Composition::iterator ci = comp.begin();
            ci != comp.end(); ++ci) {
        usedTracks.insert((*ci)->getTrack());
        if ((*ci)->getType() == Segment::Audio)
            ++audioSegments;
        else
            ++internalSegments;
    }

    QFrame *frame = new QFrame(m_tabWidget);
    frame->setContentsMargins(10, 10, 10, 10);
    QGridLayout *layout = new QGridLayout(frame);
    layout->setSpacing(5);

    layout->addWidget(new QLabel(tr("Filename:"), frame), 0, 0);
    layout->addWidget(new QLabel(doc->getTitle(), frame), 0, 1);

    layout->addWidget(new QLabel(tr("Formal duration (to end marker):"), frame), 1, 0);
    timeT d = comp.getEndMarker();
    RealTime rtd = comp.getElapsedRealTime(d);
    layout->addWidget(new QLabel(durationToString(comp, 0, d, rtd), frame), 1, 1);

    layout->addWidget(new QLabel(tr("Playing duration:"), frame), 2, 0);
    d = comp.getDuration();
    rtd = comp.getElapsedRealTime(d);
    layout->addWidget(new QLabel(durationToString(comp, 0, d, rtd), frame), 2, 1);

    layout->addWidget(new QLabel(tr("Tracks:"), frame), 3, 0);
    layout->addWidget(new QLabel(tr("%1 used, %2 total")
                                 .arg(usedTracks.size())
                                 .arg(comp.getNbTracks()),
                                 frame), 3, 1);

    layout->addWidget(new QLabel(tr("Segments:"), frame), 4, 0);
    layout->addWidget(new QLabel(tr("%1 MIDI, %2 audio, %3 total")
                                 .arg(internalSegments)
                                 .arg(audioSegments)
                                 .arg(internalSegments + audioSegments),
                                 frame), 4, 1);

    layout->setRowStretch(5, 2);

    addTab(frame, tr("Statistics"));

    frame = new QFrame(m_tabWidget);
    frame->setContentsMargins(10, 10, 10, 10);
    layout = new QGridLayout(frame);
    layout->setSpacing(5);

    QTableWidget *table = new QTableWidget(1, 11, frame); // , "Segment Table"
    table->setObjectName("StyledTable");
    table->setAlternatingRowColors(true);
    //table->setSelectionMode(QTableWidget::NoSelection);
    table->setSelectionBehavior( QAbstractItemView::SelectRows );
    table->setSelectionMode( QAbstractItemView::SingleSelection );
    table->setSortingEnabled(true);

    table->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr("Type")));    // p1=column
    table->setHorizontalHeaderItem( 1, new QTableWidgetItem( tr("Track")));
    table->setHorizontalHeaderItem( 2, new QTableWidgetItem( tr("Label")));
    table->setHorizontalHeaderItem( 3, new QTableWidgetItem( tr("Time")));
    table->setHorizontalHeaderItem( 4, new QTableWidgetItem( tr("Duration")));
    table->setHorizontalHeaderItem( 5, new QTableWidgetItem( tr("Events")));
    table->setHorizontalHeaderItem( 6, new QTableWidgetItem( tr("Polyphony")));
    table->setHorizontalHeaderItem( 7, new QTableWidgetItem( tr("Repeat")));
    table->setHorizontalHeaderItem( 8, new QTableWidgetItem( tr("Quantize")));
    table->setHorizontalHeaderItem( 9, new QTableWidgetItem( tr("Transpose")));
    table->setHorizontalHeaderItem( 10, new QTableWidgetItem( tr("Delay")));

    //table->setNumRows(audioSegments + internalSegments);
    table->setRowCount(audioSegments + internalSegments);

    table->setColumnWidth(0, 50);
    table->setColumnWidth(1, 50);
    table->setColumnWidth(2, 150);
    table->setColumnWidth(3, 80);
    table->setColumnWidth(4, 80);
    table->setColumnWidth(5, 80);
    table->setColumnWidth(6, 80);
    table->setColumnWidth(7, 80);
    table->setColumnWidth(8, 80);
    table->setColumnWidth(9, 80);
    table->setColumnWidth(10, 80);

    int i = 0;

    for (Composition::iterator ci = comp.begin();
            ci != comp.end(); ++ci) {

        Segment *s = *ci;

        table->setItem(i, 0, new SegmentDataItem
                       (table,
                        s->getType() == Segment::Audio ?
                        tr("Audio") : tr("MIDI")));

        table->setItem(i, 1, new SegmentDataItem
                       (table,
                        QString("%1").arg(s->getTrack() + 1)));

        QPixmap colourPixmap(16, 16);
        Colour colour =
            comp.getSegmentColourMap().getColourByIndex(s->getColourIndex());
        colourPixmap.fill(GUIPalette::convertColour(colour));

        table->setItem(i, 2,
                       new QTableWidgetItem( colourPixmap, strtoqstr(s->getLabel())) );
//        new QTableWidgetItem(table, QTableWidgetItem::Never,
        //                         strtoqstr(s->getLabel()),
        //                               colourPixmap));

        table->setItem(i, 3, new SegmentDataItem
                       (table,
                        QString("%1").arg(s->getStartTime())));

        table->setItem(i, 4, new SegmentDataItem
                       (table,
                        QString("%1").arg(s->getEndMarkerTime() -
                                          s->getStartTime())));

        std::set<long> notesOn;
        std::multimap<timeT, long> noteOffs;
        int events = 0, notes = 0, poly = 0, maxPoly = 0;

        for (Segment::iterator si = s->begin();
                s->isBeforeEndMarker(si); ++si) {
            ++events;
            if ((*si)->isa(Note::EventType)) {
                ++notes;
                timeT startTime = (*si)->getAbsoluteTime();
                timeT endTime = startTime + (*si)->getDuration();
                if (endTime == startTime) continue;
                while (!noteOffs.empty() &&
                        (startTime >= noteOffs.begin()->first)) {
                    notesOn.erase(noteOffs.begin()->second);
                    noteOffs.erase(noteOffs.begin());
                }
                long pitch = 0;
                (*si)->get<Int>(BaseProperties::PITCH, pitch);
                notesOn.insert(pitch);
                noteOffs.insert(std::multimap<timeT, long>::value_type(endTime, pitch));
                poly = notesOn.size();
                if (poly > maxPoly) maxPoly = poly;
            }
        }

        table->setItem(i, 5, new SegmentDataItem
                       (table,
                        QString("%1").arg(events)));

        table->setItem(i, 6, new SegmentDataItem
                       (table,
                        QString("%1").arg(maxPoly)));

        table->setItem(i, 7, new SegmentDataItem
                       (table,
                        s->isRepeating() ? tr("Yes") : tr("No")));

        timeT discard;

        if (s->getQuantizer() && s->hasQuantization()) {
            timeT unit = s->getQuantizer()->getUnit();
            table->setItem(i, 8, new SegmentDataItem
                           (table,
                            NotationStrings::makeNoteMenuLabel
                            (unit, true, discard, false)));
        } else {
            table->setItem(i, 8, new SegmentDataItem
                           (table,
                            tr("Off")));
        }

        table->setItem(i, 9, new SegmentDataItem
                       (table,
                        QString("%1").arg(s->getTranspose())));

        if (s->getDelay() != 0) {
            if (s->getRealTimeDelay() != RealTime::zeroTime) {
                table->setItem(i, 10, new SegmentDataItem
                               (table,
                                QString("%1 + %2 ms")
                                .arg(NotationStrings::makeNoteMenuLabel
                                     (s->getDelay(), true, discard, false))
                                .arg(s->getRealTimeDelay().sec * 1000 +
                                     s->getRealTimeDelay().msec())));
            } else {
                table->setItem(i, 10, new SegmentDataItem
                               (table,
                                NotationStrings::makeNoteMenuLabel
                                (s->getDelay(), true, discard, false)));
            }
        } else if (s->getRealTimeDelay() != RealTime::zeroTime) {
            table->setItem(i, 10, new SegmentDataItem
                           (table,
                            QString("%2 ms")
                            .arg(s->getRealTimeDelay().sec * 1000 +
                                 s->getRealTimeDelay().msec())));
        } else {
            table->setItem(i, 10, new SegmentDataItem
                           (table,
                            tr("None")));
        }

        ++i;
    }

    layout->addWidget(table, 0, 0);

    addTab(frame, tr("Segment Summary"));

}
void
AddLayerCommand::execute()
{ 
    if (!m_segment) return;

    Segment *layer = new Segment();

    layer->setTrack(m_segment->getTrack());
    layer->setStartTime(m_segment->getStartTime());
    m_composition.addSegment(layer);
    layer->setEndTime(m_segment->getEndTime());

    std::string label = m_segment->getLabel();
    label += tr(" - layer").toStdString();
    layer->setLabel(label);

    layer->setHighestPlayable(m_segment->getHighestPlayable());
    layer->setLowestPlayable(m_segment->getLowestPlayable());
    layer->setTranspose(m_segment->getTranspose());

    // fill the segment with rests, so we can make them invisible
    layer->fillWithRests(m_segment->getStartTime(), m_segment->getEndTime());

    for (Segment::iterator i = m_segment->begin(); i != m_segment->end(); ++i) {
        // copy over any clefs or key signatures, as these are needed to
        // maintain compatibility between segments
        if ((*i)->isa(Clef::EventType) ||
            (*i)->isa(Key::EventType)) {

            layer->insert(new Event(**i));

        }
    }

    // set everything in the layer segment invisible
    for (Segment::iterator i = layer->begin(); i != layer->end(); ++i) {
        (*i)->set<Bool>(BaseProperties::INVISIBLE, true);

        // raise the heights of alternate voice rests to get them out of the
        // middle of the staff, where they may be easier to deal with
        if ((*i)->isa(Note::EventRestType)) (*i)->setMaybe<Int>(BaseProperties::DISPLACED_Y, -1000);

    }

    // get the total number of colors in the map
    int maxColors = m_composition.getSegmentColourMap().size();

    // get the color index for the segment used as the template for the new
    // empty one we're creating
    int index = m_segment->getColourIndex();

    // with the default color map (the only one I care about anymore) a
    // difference of +5 guarantees enough contrast to make the segment changer
    // widget, raw note ruler, and track headers show enough contrast to be
    // useful as an indication that this segment is not the same as the one it
    // is patterned after
    index += 5;

    // if we went past the end of the color map, just roll back to 0, because
    // this will happen so infrequently in use it's not worth a lot of fancy
    // handling, and anyway 0 will be a contrast from what's sitting on the end
    // of the standard color map, so it will still be functional
    if (index > maxColors) index = 0;
    layer->setColourIndex(index);
    
    // now what other gotchas are there?

    // now m_segment goes from being the input template to what we'll return if
    // asked
    m_segment = layer;
    m_detached = false;
}