diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
commit | 145364a8af6a1fec06556221e66d4b724a62fc9a (patch) | |
tree | 53bd71a544008c518034f208d64c932dc2883f50 /src/base/Segment.cpp | |
download | rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip |
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/base/Segment.cpp')
-rw-r--r-- | src/base/Segment.cpp | 1294 |
1 files changed, 1294 insertions, 0 deletions
diff --git a/src/base/Segment.cpp b/src/base/Segment.cpp new file mode 100644 index 0000000..2f65acd --- /dev/null +++ b/src/base/Segment.cpp @@ -0,0 +1,1294 @@ +// -*- c-basic-offset: 4 -*- + +/* + Rosegarden + A sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + 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 "Segment.h" +#include "NotationTypes.h" +#include "BaseProperties.h" +#include "Composition.h" +#include "BasicQuantizer.h" +#include "Profiler.h" + +#include <iostream> +#include <algorithm> +#include <iterator> +#include <cstdio> +#include <typeinfo> + +namespace Rosegarden +{ +using std::cerr; +using std::endl; +using std::string; + +//#define DEBUG_NORMALIZE_RESTS 1 + +static int _runtimeSegmentId = 0; + +Segment::Segment(SegmentType segmentType, timeT startTime) : + std::multiset<Event*, Event::EventCmp>(), + m_composition(0), + m_startTime(startTime), + m_endMarkerTime(0), + m_endTime(startTime), + m_track(0), + m_type(segmentType), + m_colourIndex(0), + m_id(0), + m_audioFileId(0), + m_unstretchedFileId(0), + m_stretchRatio(1.0), + m_audioStartTime(0, 0), + m_audioEndTime(0, 0), + m_repeating(false), + m_quantizer(new BasicQuantizer()), + m_quantize(false), + m_transpose(0), + m_delay(0), + m_realTimeDelay(0, 0), + m_clefKeyList(0), + m_runtimeSegmentId(_runtimeSegmentId++), + m_snapGridSize(-1), + m_viewFeatures(0), + m_autoFade(false), + m_fadeInTime(Rosegarden::RealTime::zeroTime), + m_fadeOutTime(Rosegarden::RealTime::zeroTime), + m_highestPlayable(127), + m_lowestPlayable(0) +{ +} + +Segment::Segment(const Segment &segment): + std::multiset<Event*, Event::EventCmp>(), + m_composition(0), // Composition should decide what's in it and what's not + m_startTime(segment.getStartTime()), + m_endMarkerTime(segment.m_endMarkerTime ? + new timeT(*segment.m_endMarkerTime) : 0), + m_endTime(segment.getEndTime()), + m_track(segment.getTrack()), + m_type(segment.getType()), + m_label(segment.getLabel()), + m_colourIndex(segment.getColourIndex()), + m_id(0), + m_audioFileId(segment.getAudioFileId()), + m_unstretchedFileId(segment.getUnstretchedFileId()), + m_stretchRatio(segment.getStretchRatio()), + m_audioStartTime(segment.getAudioStartTime()), + m_audioEndTime(segment.getAudioEndTime()), + m_repeating(segment.isRepeating()), + m_quantizer(new BasicQuantizer(segment.m_quantizer->getUnit(), + segment.m_quantizer->getDoDurations())), + m_quantize(segment.hasQuantization()), + m_transpose(segment.getTranspose()), + m_delay(segment.getDelay()), + m_realTimeDelay(segment.getRealTimeDelay()), + m_clefKeyList(0), + m_runtimeSegmentId(_runtimeSegmentId++), + m_snapGridSize(-1), + m_viewFeatures(0), + m_autoFade(segment.isAutoFading()), + m_fadeInTime(segment.getFadeInTime()), + m_fadeOutTime(segment.getFadeOutTime()), + m_highestPlayable(127), + m_lowestPlayable(0) +{ + for (const_iterator it = segment.begin(); + segment.isBeforeEndMarker(it); ++it) { + insert(new Event(**it)); + } +} + +Segment::~Segment() +{ + if (!m_observers.empty()) { + cerr << "Warning: Segment::~Segment() with " << m_observers.size() + << " observers still extant" << endl; + cerr << "Observers are:"; + for (ObserverSet::const_iterator i = m_observers.begin(); + i != m_observers.end(); ++i) { + cerr << " " << (void *)(*i); + cerr << " [" << typeid(**i).name() << "]"; + } + cerr << endl; + } + + notifySourceDeletion(); + + if (m_composition) m_composition->detachSegment(this); + + if (m_clefKeyList) { + // don't delete contents of m_clefKeyList: the pointers + // are just aliases for events in the main segment + delete m_clefKeyList; + } + + // Clear EventRulers + // + EventRulerListIterator it; + for (it = m_eventRulerList.begin(); it != m_eventRulerList.end(); ++it) delete *it; + m_eventRulerList.clear(); + + // delete content + for (iterator it = begin(); it != end(); ++it) delete (*it); + + delete m_endMarkerTime; +} + + +void +Segment::setTrack(TrackId id) +{ + Composition *c = m_composition; + if (c) c->weakDetachSegment(this); // sets m_composition to 0 + TrackId oldTrack = m_track; + m_track = id; + if (c) { + c->weakAddSegment(this); + c->updateRefreshStatuses(); + c->notifySegmentTrackChanged(this, oldTrack, id); + } +} + +timeT +Segment::getStartTime() const +{ + return m_startTime; +} + +timeT +Segment::getEndMarkerTime() const +{ + timeT endTime; + + if (m_type == Audio && m_composition) { + + RealTime startRT = m_composition->getElapsedRealTime(m_startTime); + RealTime endRT = startRT - m_audioStartTime + m_audioEndTime; + endTime = m_composition->getElapsedTimeForRealTime(endRT); + + } else { + + if (m_endMarkerTime) { + endTime = *m_endMarkerTime; + } else { + endTime = getEndTime(); + } + + if (m_composition) { + endTime = std::min(endTime, m_composition->getEndMarker()); + } + } + + return endTime; +} + +timeT +Segment::getEndTime() const +{ + if (m_type == Audio && m_composition) { + RealTime startRT = m_composition->getElapsedRealTime(m_startTime); + RealTime endRT = startRT - m_audioStartTime + m_audioEndTime; + return m_composition->getElapsedTimeForRealTime(endRT); + } else { + return m_endTime; + } +} + +void +Segment::setStartTime(timeT t) +{ + int dt = t - m_startTime; + if (dt == 0) return; + + // reset the time of all events. can't just setAbsoluteTime on these, + // partly 'cos we're not allowed, partly 'cos it might screw up the + // quantizer (which is why we're not allowed) + + // still, this is rather unsatisfactory + + FastVector<Event *> events; + + for (iterator i = begin(); i != end(); ++i) { + events.push_back((*i)->copyMoving(dt)); + } + + timeT previousEndTime = m_endTime; + + erase(begin(), end()); + + m_endTime = previousEndTime + dt; + if (m_endMarkerTime) *m_endMarkerTime += dt; + + if (m_composition) m_composition->setSegmentStartTime(this, t); + else m_startTime = t; + + for (int i = 0; i < events.size(); ++i) { + insert(events[i]); + } + + notifyStartChanged(m_startTime); + updateRefreshStatuses(m_startTime, m_endTime); +} + +void +Segment::setEndMarkerTime(timeT t) +{ + if (t < m_startTime) t = m_startTime; + + if (m_type == Audio) { + if (m_endMarkerTime) *m_endMarkerTime = t; + else m_endMarkerTime = new timeT(t); + RealTime oldAudioEndTime = m_audioEndTime; + if (m_composition) { + m_audioEndTime = m_audioStartTime + + m_composition->getRealTimeDifference(m_startTime, t); + if (oldAudioEndTime != m_audioEndTime) { + notifyEndMarkerChange(m_audioEndTime < oldAudioEndTime); + } + } + } else { + + timeT endTime = getEndTime(); + timeT oldEndMarker = getEndMarkerTime(); + bool shorten = (t < oldEndMarker); + + if (t > endTime) { + fillWithRests(endTime, t); + if (oldEndMarker < endTime) { + updateRefreshStatuses(oldEndMarker, t); + } + } else { + // only need to do this if we aren't inserting or + // deleting any actual events + if (oldEndMarker < t) { + updateRefreshStatuses(oldEndMarker, t); + } + updateRefreshStatuses(t, endTime); + } + + if (m_endMarkerTime) *m_endMarkerTime = t; + else m_endMarkerTime = new timeT(t); + notifyEndMarkerChange(shorten); + } +} + +void +Segment::setEndTime(timeT t) +{ + timeT endTime = getEndTime(); + if (t < m_startTime) t = m_startTime; + + if (m_type == Audio) { + setEndMarkerTime(t); + } else { + if (t < endTime) { + erase(findTime(t), end()); + endTime = getEndTime(); + if (m_endMarkerTime && endTime < *m_endMarkerTime) { + *m_endMarkerTime = endTime; + notifyEndMarkerChange(true); + } + } else if (t > endTime) { + fillWithRests(endTime, t); + } + } +} + +Segment::iterator +Segment::getEndMarker() +{ + if (m_endMarkerTime) { + return findTime(*m_endMarkerTime); + } else { + return end(); + } +} + +bool +Segment::isBeforeEndMarker(const_iterator i) const +{ + if (i == end()) return false; + + timeT absTime = (*i)->getAbsoluteTime(); + timeT endTime = getEndMarkerTime(); + + return ((absTime < endTime) || + (absTime == endTime && (*i)->getDuration() == 0)); +} + +void +Segment::clearEndMarker() +{ + delete m_endMarkerTime; + m_endMarkerTime = 0; + notifyEndMarkerChange(false); +} + +const timeT * +Segment::getRawEndMarkerTime() const +{ + return m_endMarkerTime; +} + + +void +Segment::updateRefreshStatuses(timeT startTime, timeT endTime) +{ + for(unsigned int i = 0; i < m_refreshStatusArray.size(); ++i) + m_refreshStatusArray.getRefreshStatus(i).push(startTime, endTime); +} + + +Segment::iterator +Segment::insert(Event *e) +{ + assert(e); + + timeT t0 = e->getAbsoluteTime(); + timeT t1 = t0 + e->getDuration(); + + if (t0 < m_startTime || + (begin() == end() && t0 > m_startTime)) { + + if (m_composition) m_composition->setSegmentStartTime(this, t0); + else m_startTime = t0; + notifyStartChanged(m_startTime); + } + + if (t1 > m_endTime || + begin() == end()) { + timeT oldTime = m_endTime; + m_endTime = t1; + notifyEndMarkerChange(m_endTime < oldTime); + } + + iterator i = std::multiset<Event*, Event::EventCmp>::insert(e); + notifyAdd(e); + updateRefreshStatuses(e->getAbsoluteTime(), + e->getAbsoluteTime() + e->getDuration()); + return i; +} + + +void +Segment::updateEndTime() +{ + m_endTime = m_startTime; + for (iterator i = begin(); i != end(); ++i) { + timeT t = (*i)->getAbsoluteTime() + (*i)->getDuration(); + if (t > m_endTime) m_endTime = t; + } +} + + +void +Segment::erase(iterator pos) +{ + Event *e = *pos; + + assert(e); + + timeT t0 = e->getAbsoluteTime(); + timeT t1 = t0 + e->getDuration(); + + std::multiset<Event*, Event::EventCmp>::erase(pos); + notifyRemove(e); + delete e; + updateRefreshStatuses(t0, t1); + + if (t0 == m_startTime && begin() != end()) { + timeT startTime = (*begin())->getAbsoluteTime(); + if (m_composition) m_composition->setSegmentStartTime(this, startTime); + else m_startTime = startTime; + notifyStartChanged(m_startTime); + } + if (t1 == m_endTime) { + updateEndTime(); + } +} + + +void +Segment::erase(iterator from, iterator to) +{ + timeT startTime = 0, endTime = m_endTime; + if (from != end()) startTime = (*from)->getAbsoluteTime(); + if (to != end()) endTime = (*to)->getAbsoluteTime() + (*to)->getDuration(); + + // Not very efficient, but without an observer event for + // multiple erase we can't do any better. + + for (Segment::iterator i = from; i != to; ) { + + Segment::iterator j(i); + ++j; + + Event *e = *i; + assert(e); + + std::multiset<Event*, Event::EventCmp>::erase(i); + notifyRemove(e); + delete e; + + i = j; + } + + if (startTime == m_startTime && begin() != end()) { + timeT startTime = (*begin())->getAbsoluteTime(); + if (m_composition) m_composition->setSegmentStartTime(this, startTime); + else m_startTime = startTime; + notifyStartChanged(m_startTime); + } + + if (endTime == m_endTime) { + updateEndTime(); + } + + updateRefreshStatuses(startTime, endTime); +} + + +bool +Segment::eraseSingle(Event* e) +{ + iterator elPos = findSingle(e); + + if (elPos != end()) { + + erase(elPos); + return true; + + } else return false; + +} + + +Segment::iterator +Segment::findSingle(Event* e) +{ + iterator res = end(); + + std::pair<iterator, iterator> interval = equal_range(e); + + for (iterator i = interval.first; i != interval.second; ++i) { + if (*i == e) { + res = i; + break; + } + } + return res; +} + + +Segment::iterator +Segment::findTime(timeT t) +{ + Event dummy("dummy", t, 0, MIN_SUBORDERING); + return lower_bound(&dummy); +} + + +Segment::iterator +Segment::findNearestTime(timeT t) +{ + iterator i = findTime(t); + if (i == end() || (*i)->getAbsoluteTime() > t) { + if (i == begin()) return end(); + else --i; + } + return i; +} + + +timeT +Segment::getBarStartForTime(timeT t) const +{ + if (t < getStartTime()) t = getStartTime(); + return getComposition()->getBarStartForTime(t); +} + + +timeT +Segment::getBarEndForTime(timeT t) const +{ + if (t > getEndMarkerTime()) t = getEndMarkerTime(); + return getComposition()->getBarEndForTime(t); +} + + +int Segment::getNextId() const +{ + return m_id++; +} + + +void +Segment::fillWithRests(timeT endTime) +{ + fillWithRests(getEndTime(), endTime); +} + +void +Segment::fillWithRests(timeT startTime, timeT endTime) +{ + if (startTime < m_startTime) { + if (m_composition) m_composition->setSegmentStartTime(this, startTime); + else m_startTime = startTime; + notifyStartChanged(m_startTime); + } + + TimeSignature ts; + timeT sigTime = 0; + + if (getComposition()) { + sigTime = getComposition()->getTimeSignatureAt(startTime, ts); + } + + timeT restDuration = endTime - startTime; + if (restDuration <= 0) return; + +#ifdef DEBUG_NORMALIZE_RESTS + cerr << "fillWithRests (" << startTime << "->" << endTime << "), composition " + << (getComposition() ? "exists" : "does not exist") << ", sigTime " + << sigTime << ", timeSig duration " << ts.getBarDuration() << ", restDuration " << restDuration << endl; +#endif + + DurationList dl; + ts.getDurationListForInterval(dl, restDuration, 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); + insert(e); + acc += *i; + } +} + +void +Segment::normalizeRests(timeT startTime, timeT endTime) +{ + Profiler profiler("Segment::normalizeRests"); + +#ifdef DEBUG_NORMALIZE_RESTS + cerr << "normalizeRests (" << startTime << "->" << endTime << "), segment starts at " << m_startTime << endl; +#endif + + if (startTime < m_startTime) { +#ifdef DEBUG_NORMALIZE_RESTS + cerr << "normalizeRests: pulling start time back from " + << m_startTime << " to " << startTime << endl; +#endif + if (m_composition) m_composition->setSegmentStartTime(this, startTime); + else m_startTime = startTime; + notifyStartChanged(m_startTime); + } + + //!!! Need to remove the rests then relocate the start time + // and get the notation end time for the nearest note before that + // (?) + + //!!! We need to insert rests at fictitious unquantized times that + //are broadly correct, so as to maintain ordering of notes and + //rests in the unquantized segment. The quantized times should go + //in notation-prefix properties. + + // Preliminary: If there are any time signature changes between + // the start and end times, consider separately each of the sections + // they divide the range up into. + + Composition *composition = getComposition(); + if (composition) { + int timeSigNo = composition->getTimeSignatureNumberAt(startTime); + if (timeSigNo < composition->getTimeSignatureCount() - 1) { + timeT nextSigTime = + composition->getTimeSignatureChange(timeSigNo + 1).first; + if (nextSigTime < endTime) { +#ifdef DEBUG_NORMALIZE_RESTS + cerr << "normalizeRests: divide-and-conquer on timesig at " << nextSigTime << endl; +#endif + normalizeRests(startTime, nextSigTime); + normalizeRests(nextSigTime, endTime); + return; + } + } + } + + // First stage: erase all existing non-tupleted rests in this range. + + timeT segmentEndTime = m_endTime; + + iterator ia = findNearestTime(startTime); + if (ia == end()) ia = begin(); + if (ia == end()) { // the segment is empty +#ifdef DEBUG_NORMALIZE_RESTS + cerr << "normalizeRests: empty segment" << endl; +#endif + fillWithRests(startTime, endTime); + return; + } else { + if (startTime > (*ia)->getNotationAbsoluteTime()) { + startTime = (*ia)->getNotationAbsoluteTime(); + } + } + + iterator ib = findTime(endTime); + if (ib == end()) { + if (ib != begin()) { + --ib; + // if we're pointing at the real-end-time of the last event, + // use its notation-end-time instead + if (endTime == (*ib)->getAbsoluteTime() + (*ib)->getDuration()) { + endTime = + (*ib)->getNotationAbsoluteTime() + + (*ib)->getNotationDuration(); + } + ++ib; + } + } else { + endTime = (*ib)->getNotationAbsoluteTime(); + } + + // If there's a rest preceding the start time, with no notes + // between us and it, and if it doesn't have precisely the + // right duration, then we need to normalize it too + + //!!! needs modification for new scheme + + iterator scooter = ia; + while (scooter-- != begin()) { +// if ((*scooter)->isa(Note::EventRestType)) { //!!! experimental + if ((*scooter)->getDuration() > 0) { + if ((*scooter)->getNotationAbsoluteTime() + + (*scooter)->getNotationDuration() != + startTime) { + startTime = (*scooter)->getNotationAbsoluteTime(); +#ifdef DEBUG_NORMALIZE_RESTS + cerr << "normalizeRests: scooting back to " << startTime << endl; +#endif + ia = scooter; + } + break; +/*!!! + } else if ((*scooter)->getDuration() > 0) { + break; +*/ + } + } + + for (iterator i = ia, j = i; i != ib && i != end(); i = j) { + ++j; + if ((*i)->isa(Note::EventRestType) && + !(*i)->has(BaseProperties::BEAMED_GROUP_TUPLET_BASE)) { +#ifdef DEBUG_NORMALIZE_RESTS + cerr << "normalizeRests: erasing rest at " << (*i)->getAbsoluteTime() << endl; +#endif + erase(i); + } + } + + // It's possible we've just removed all the events between here + // and the end of the segment, if they were all rests. Check. + + if (endTime < segmentEndTime && m_endTime < segmentEndTime) { + endTime = segmentEndTime; + } + + // Second stage: find the gaps that need to be filled with + // rests. We don't mind about the case where two simultaneous + // notes end at different times -- we're only interested in + // the one ending sooner. Each time an event ends, we start + // a candidate gap. + + std::vector<std::pair<timeT, timeT> > gaps; + + timeT lastNoteStarts = startTime; + timeT lastNoteEnds = startTime; + + // Re-find this, as it might have been erased + ia = findNearestTime(startTime); + + if (ia == end()) { + // already have good lastNoteStarts, lastNoteEnds + ia = begin(); + } else { + lastNoteStarts = (*ia)->getNotationAbsoluteTime(); + lastNoteEnds = lastNoteStarts; + } + + if (ib != end()) { + //!!! This and related code really need to get a quantized + // absolute time of a note event that has the same unquantized + // time as ib, not necessarily of ib itself... or else the + // quantizer needs to set the quantized times of all non-note + // events that happen at the same unquantized time as a note + // event to the same as that of the note event... yeah, that's + // probably the right thing + endTime = (*ib)->getNotationAbsoluteTime(); + + // was this just a nasty hack? + ++ib; + } + + iterator i = ia; + + for (; i != ib && i != end(); ++i) { + + // Boundary events for sets of rests may be notes (obviously), + // text events (because they need to be "attached" to + // something that has the correct timing), or rests (any + // remaining rests in this area have tuplet data so should be + // treated as "hard" rests); + if (!((*i)->isa(Note::EventType) || + (*i)->isa(Text::EventType) || + (*i)->isa(Note::EventRestType))) { + continue; + } + + timeT thisNoteStarts = (*i)->getNotationAbsoluteTime(); + +#ifdef DEBUG_NORMALIZE_RESTS + cerr << "normalizeRests: scanning: thisNoteStarts " << thisNoteStarts + << ", lastNoteStarts " << lastNoteStarts + << ", lastNoteEnds " << lastNoteEnds << endl; +#endif + + /* BR #988185: "Notation: Rest can be simultaneous with note but follow it" + + This conditional tested whether a note started before the + preceding note ended, and if so inserted rests simultaneous + with the preceding note to make up the gap. Without the + ability to lay out those rests partwise, this is never any + better than plain confusing. Revert the change. + + if (thisNoteStarts < lastNoteEnds && + thisNoteStarts > lastNoteStarts) { + gaps.push_back(std::pair<timeT, timeT> + (lastNoteStarts, + thisNoteStarts - lastNoteStarts)); + } + */ + + if (thisNoteStarts > lastNoteEnds) { + gaps.push_back(std::pair<timeT, timeT> + (lastNoteEnds, + thisNoteStarts - lastNoteEnds)); + } + + lastNoteStarts = thisNoteStarts; + lastNoteEnds = thisNoteStarts + (*i)->getNotationDuration(); + } + + if (endTime > lastNoteEnds) { + gaps.push_back(std::pair<timeT, timeT> + (lastNoteEnds, endTime - lastNoteEnds)); + } + + timeT duration; + + for (unsigned int gi = 0; gi < gaps.size(); ++gi) { + +#ifdef DEBUG_NORMALIZE_RESTS + cerr << "normalizeRests: gap " << gi << ": " << gaps[gi].first << " -> " << (gaps[gi].first + gaps[gi].second) << endl; +#endif + + startTime = gaps[gi].first; + duration = gaps[gi].second; + + if (duration >= Note(Note::Shortest).getDuration()) { + fillWithRests(startTime, startTime + duration); + } + } +} + + + +void Segment::getTimeSlice(timeT absoluteTime, iterator &start, iterator &end) +{ + Event dummy("dummy", absoluteTime, 0, MIN_SUBORDERING); + + // No, this won't work -- we need to include things that don't + // compare equal because they have different suborderings, as long + // as they have the same times + +// std::pair<iterator, iterator> res = equal_range(&dummy); + +// start = res.first; +// end = res.second; + + // Got to do this instead: + + start = end = lower_bound(&dummy); + + while (end != this->end() && + (*end)->getAbsoluteTime() == (*start)->getAbsoluteTime()) + ++end; +} + +void Segment::getTimeSlice(timeT absoluteTime, const_iterator &start, const_iterator &end) + const +{ + Event dummy("dummy", absoluteTime, 0, MIN_SUBORDERING); + + start = end = lower_bound(&dummy); + + while (end != this->end() && + (*end)->getAbsoluteTime() == (*start)->getAbsoluteTime()) + ++end; +} + +void +Segment::setQuantization(bool quantize) +{ + if (m_quantize != quantize) { + m_quantize = quantize; + if (m_quantize) { + m_quantizer->quantize(this, begin(), end()); + } else { + m_quantizer->unquantize(this, begin(), end()); + } + } +} + +bool +Segment::hasQuantization() const +{ + return m_quantize; +} + +void +Segment::setQuantizeLevel(timeT unit) +{ + if (m_quantizer->getUnit() == unit) return; + + m_quantizer->setUnit(unit); + if (m_quantize) m_quantizer->quantize(this, begin(), end()); +} + +const BasicQuantizer * +Segment::getQuantizer() const +{ + return m_quantizer; +} + + +void +Segment::setRepeating(bool value) +{ + m_repeating = value; + if (m_composition) { + m_composition->updateRefreshStatuses(); + m_composition->notifySegmentRepeatChanged(this, value); + } +} + +void +Segment::setDelay(timeT delay) +{ + m_delay = delay; + if (m_composition) { + // don't updateRefreshStatuses() - affects playback only + m_composition->notifySegmentEventsTimingChanged(this, delay, RealTime::zeroTime); + } +} + +void +Segment::setRealTimeDelay(RealTime delay) +{ + m_realTimeDelay = delay; + if (m_composition) { + // don't updateRefreshStatuses() - affects playback only + m_composition->notifySegmentEventsTimingChanged(this, 0, delay); + } +} + +void +Segment::setTranspose(int transpose) +{ + m_transpose = transpose; + if (m_composition) { + // don't updateRefreshStatuses() - affects playback only + m_composition->notifySegmentTransposeChanged(this, transpose); + } +} + +void +Segment::setAudioFileId(unsigned int id) +{ + m_audioFileId = id; + updateRefreshStatuses(getStartTime(), getEndTime()); +} + +void +Segment::setUnstretchedFileId(unsigned int id) +{ + m_unstretchedFileId = id; +} + +void +Segment::setStretchRatio(float ratio) +{ + m_stretchRatio = ratio; +} + +void +Segment::setAudioStartTime(const RealTime &time) +{ + m_audioStartTime = time; + updateRefreshStatuses(getStartTime(), getEndTime()); +} + +void +Segment::setAudioEndTime(const RealTime &time) +{ + RealTime oldAudioEndTime = m_audioEndTime; + m_audioEndTime = time; + updateRefreshStatuses(getStartTime(), getEndTime()); + notifyEndMarkerChange(time < oldAudioEndTime); +} + +void +Segment::setAutoFade(bool value) +{ + m_autoFade = value; + updateRefreshStatuses(getStartTime(), getEndTime()); +} + +void +Segment::setFadeInTime(const RealTime &time) +{ + m_fadeInTime = time; + updateRefreshStatuses(getStartTime(), getEndTime()); +} + +void +Segment::setFadeOutTime(const RealTime &time) +{ + m_fadeOutTime = time; + updateRefreshStatuses(getStartTime(), getEndTime()); +} + +void +Segment::setLabel(const std::string &label) +{ + m_label = label; + if (m_composition) m_composition->updateRefreshStatuses(); + notifyAppearanceChange(); +} + +bool +Segment::ClefKeyCmp::operator()(const Event *e1, const Event *e2) const +{ + if (e1->getType() == e2->getType()) return Event::EventCmp()(e1, e2); + else return e1->getType() < e2->getType(); +} + +Clef +Segment::getClefAtTime(timeT time) const +{ + timeT ctime; + return getClefAtTime(time, ctime); +} + +Clef +Segment::getClefAtTime(timeT time, timeT &ctime) const +{ + if (!m_clefKeyList) return Clef(); + + Event ec(Clef::EventType, time); + ClefKeyList::iterator i = m_clefKeyList->lower_bound(&ec); + + while (i == m_clefKeyList->end() || + (*i)->getAbsoluteTime() > time || + (*i)->getType() != Clef::EventType) { + + if (i == m_clefKeyList->begin()) { + ctime = getStartTime(); + return Clef(); + } + --i; + } + + try { + ctime = (*i)->getAbsoluteTime(); + return Clef(**i); + } catch (const Exception &e) { + std::cerr << "Segment::getClefAtTime(" << time + << "): bogus clef in ClefKeyList: event dump follows:" + << std::endl; + (*i)->dump(std::cerr); + return Clef(); + } +} + +Key +Segment::getKeyAtTime(timeT time) const +{ + timeT ktime; + return getKeyAtTime(time, ktime); +} + +Key +Segment::getKeyAtTime(timeT time, timeT &ktime) const +{ + if (!m_clefKeyList) return Key(); + + Event ek(Key::EventType, time); + ClefKeyList::iterator i = m_clefKeyList->lower_bound(&ek); + + while (i == m_clefKeyList->end() || + (*i)->getAbsoluteTime() > time || + (*i)->getType() != Key::EventType) { + + if (i == m_clefKeyList->begin()) { + ktime = getStartTime(); + return Key(); + } + --i; + } + + try { + ktime = (*i)->getAbsoluteTime(); + return Key(**i); + } catch (const Exception &e) { + std::cerr << "Segment::getClefAtTime(" << time + << "): bogus key in ClefKeyList: event dump follows:" + << std::endl; + (*i)->dump(std::cerr); + return Key(); + } +} + +void +Segment::getFirstClefAndKey(Clef &clef, Key &key) +{ + bool keyFound = false; + bool clefFound = false; + clef = Clef(); // Default clef + key = Key(); // Default key signature + + iterator i = begin(); + while (i!=end()) { + // Keep current clef and key as soon as a note or rest event is found + if ((*i)->isa(Note::EventRestType) || (*i)->isa(Note::EventType)) return; + + // Remember the first clef event found + if ((*i)->isa(Clef::EventType)) { + clef = Clef(*(*i)); + // and return if a key has already been found + if (keyFound) return; + clefFound = true; + } + + // Remember the first key event found + if ((*i)->isa(Key::EventType)) { + key = Key(*(*i)); + // and return if a clef has already been found + if (clefFound) return; + keyFound = true; + } + + ++i; + } +} + +timeT +Segment::getRepeatEndTime() const +{ + timeT endMarker = getEndMarkerTime(); + + if (m_repeating && m_composition) { + Composition::iterator i(m_composition->findSegment(this)); + assert(i != m_composition->end()); + ++i; + if (i != m_composition->end() && (*i)->getTrack() == getTrack()) { + timeT t = (*i)->getStartTime(); + if (t < endMarker) return endMarker; + else return t; + } else { + return m_composition->getEndMarker(); + } + } + + return endMarker; +} + + +void +Segment::notifyAdd(Event *e) const +{ + if (e->isa(Clef::EventType) || e->isa(Key::EventType)) { + if (!m_clefKeyList) m_clefKeyList = new ClefKeyList; + m_clefKeyList->insert(e); + } + + for (ObserverSet::const_iterator i = m_observers.begin(); + i != m_observers.end(); ++i) { + (*i)->eventAdded(this, e); + } +} + + +void +Segment::notifyRemove(Event *e) const +{ + if (m_clefKeyList && (e->isa(Clef::EventType) || e->isa(Key::EventType))) { + ClefKeyList::iterator i; + for (i = m_clefKeyList->find(e); i != m_clefKeyList->end(); ++i) { + // fix for bug#1485643 (crash erasing a duplicated key signature) + if ((*i) == e) { + m_clefKeyList->erase(i); + break; + } + } + } + + for (ObserverSet::const_iterator i = m_observers.begin(); + i != m_observers.end(); ++i) { + (*i)->eventRemoved(this, e); + } +} + + +void +Segment::notifyAppearanceChange() const +{ + for (ObserverSet::const_iterator i = m_observers.begin(); + i != m_observers.end(); ++i) { + (*i)->appearanceChanged(this); + } +} + +void +Segment::notifyStartChanged(timeT newTime) +{ + for (ObserverSet::const_iterator i = m_observers.begin(); + i != m_observers.end(); ++i) { + (*i)->startChanged(this, newTime); + } + if (m_composition) { + m_composition->notifySegmentStartChanged(this, newTime); + } +} + + +void +Segment::notifyEndMarkerChange(bool shorten) +{ + for (ObserverSet::const_iterator i = m_observers.begin(); + i != m_observers.end(); ++i) { + (*i)->endMarkerTimeChanged(this, shorten); + } + if (m_composition) { + m_composition->notifySegmentEndMarkerChange(this, shorten); + } +} + + +void +Segment::notifySourceDeletion() const +{ + for (ObserverSet::const_iterator i = m_observers.begin(); + i != m_observers.end(); ++i) { + (*i)->segmentDeleted(this); + } +} + + +void +Segment::setColourIndex(const unsigned int input) +{ + m_colourIndex = input; + updateRefreshStatuses(getStartTime(), getEndTime()); + if (m_composition) m_composition->updateRefreshStatuses(); + notifyAppearanceChange(); +} + +void +Segment::addEventRuler(const std::string &type, int controllerValue, bool active) +{ + EventRulerListConstIterator it; + + for (it = m_eventRulerList.begin(); it != m_eventRulerList.end(); ++it) + if ((*it)->m_type == type && (*it)->m_controllerValue == controllerValue) + return; + + m_eventRulerList.push_back(new EventRuler(type, controllerValue, active)); +} + +bool +Segment::deleteEventRuler(const std::string &type, int controllerValue) +{ + EventRulerListIterator it; + + for (it = m_eventRulerList.begin(); it != m_eventRulerList.end(); ++it) + { + if ((*it)->m_type == type && (*it)->m_controllerValue == controllerValue) + { + delete *it; + m_eventRulerList.erase(it); + return true; + } + } + + return false; +} + +Segment::EventRuler* +Segment::getEventRuler(const std::string &type, int controllerValue) +{ + EventRulerListConstIterator it; + for (it = m_eventRulerList.begin(); it != m_eventRulerList.end(); ++it) + if ((*it)->m_type == type && (*it)->m_controllerValue == controllerValue) + return *it; + + return 0; +} + + + +SegmentHelper::~SegmentHelper() { } + + +void +SegmentRefreshStatus::push(timeT from, timeT to) +{ + if (!needsRefresh()) { // don't do anything subtle - just erase the old data + + m_from = from; + m_to = to; + + } else { // accumulate on what was already there + + if (from < m_from) m_from = from; + if (to > m_to) m_to = to; + + } + + if (m_to < m_from) std::swap(m_from, m_to); + + setNeedsRefresh(true); +} + + + + +} |