summaryrefslogtreecommitdiffstats
path: root/src/base/Composition.cpp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
commit145364a8af6a1fec06556221e66d4b724a62fc9a (patch)
tree53bd71a544008c518034f208d64c932dc2883f50 /src/base/Composition.cpp
downloadrosegarden-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/Composition.cpp')
-rw-r--r--src/base/Composition.cpp2225
1 files changed, 2225 insertions, 0 deletions
diff --git a/src/base/Composition.cpp b/src/base/Composition.cpp
new file mode 100644
index 0000000..cde3a8b
--- /dev/null
+++ b/src/base/Composition.cpp
@@ -0,0 +1,2225 @@
+// -*- 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 "Composition.h"
+#include "misc/Debug.h"
+#include "Segment.h"
+#include "FastVector.h"
+#include "BaseProperties.h"
+#include "BasicQuantizer.h"
+#include "NotationQuantizer.h"
+
+#include <iostream>
+#include <iomanip>
+#include <algorithm>
+#include <cmath>
+#include <typeinfo>
+
+#if (__GNUC__ < 3)
+#include <strstream>
+#define stringstream strstream
+#else
+#include <sstream>
+#endif
+
+using std::cerr;
+using std::endl;
+
+//#define DEBUG_BAR_STUFF 1
+//#define DEBUG_TEMPO_STUFF 1
+
+
+namespace Rosegarden
+{
+
+const PropertyName Composition::NoAbsoluteTimeProperty = "NoAbsoluteTime";
+const PropertyName Composition::BarNumberProperty = "BarNumber";
+
+const std::string Composition::TempoEventType = "tempo";
+const PropertyName Composition::TempoProperty = "Tempo";
+const PropertyName Composition::TargetTempoProperty = "TargetTempo";
+const PropertyName Composition::TempoTimestampProperty = "TimestampSec";
+
+
+bool
+Composition::ReferenceSegmentEventCmp::operator()(const Event &e1,
+ const Event &e2) const
+{
+ if (e1.has(NoAbsoluteTimeProperty) ||
+ e2.has(NoAbsoluteTimeProperty)) {
+ RealTime r1 = getTempoTimestamp(&e1);
+ RealTime r2 = getTempoTimestamp(&e2);
+ return r1 < r2;
+ } else {
+ return e1 < e2;
+ }
+}
+
+Composition::ReferenceSegment::ReferenceSegment(std::string eventType) :
+ m_eventType(eventType)
+{
+ // nothing
+}
+
+Composition::ReferenceSegment::~ReferenceSegment()
+{
+ clear();
+}
+
+void
+Composition::ReferenceSegment::clear()
+{
+ for (iterator it = begin(); it != end(); ++it) delete (*it);
+ Impl::erase(begin(), end());
+}
+
+timeT
+Composition::ReferenceSegment::getDuration() const
+{
+ const_iterator i = end();
+ if (i == begin()) return 0;
+ --i;
+
+ return (*i)->getAbsoluteTime() + (*i)->getDuration();
+}
+
+Composition::ReferenceSegment::iterator
+Composition::ReferenceSegment::find(Event *e)
+{
+ return std::lower_bound
+ (begin(), end(), e, ReferenceSegmentEventCmp());
+}
+
+Composition::ReferenceSegment::iterator
+Composition::ReferenceSegment::insert(Event *e)
+{
+ if (!e->isa(m_eventType)) {
+ throw Event::BadType(std::string("event in ReferenceSegment"),
+ m_eventType, e->getType(), __FILE__, __LINE__);
+ }
+
+ iterator i = find(e);
+
+ if (i != end() && (*i)->getAbsoluteTime() == e->getAbsoluteTime()) {
+
+ Event *old = (*i);
+ (*i) = e;
+ delete old;
+ return i;
+
+ } else {
+ return Impl::insert(i, e);
+ }
+}
+
+void
+Composition::ReferenceSegment::erase(Event *e)
+{
+ iterator i = find(e);
+ if (i != end()) Impl::erase(i);
+}
+
+Composition::ReferenceSegment::iterator
+Composition::ReferenceSegment::findTime(timeT t)
+{
+ Event dummy("dummy", t, 0, MIN_SUBORDERING);
+ return find(&dummy);
+}
+
+Composition::ReferenceSegment::iterator
+Composition::ReferenceSegment::findRealTime(RealTime t)
+{
+ Event dummy("dummy", 0, 0, MIN_SUBORDERING);
+ dummy.set<Bool>(NoAbsoluteTimeProperty, true);
+ setTempoTimestamp(&dummy, t);
+ return find(&dummy);
+}
+
+Composition::ReferenceSegment::iterator
+Composition::ReferenceSegment::findNearestTime(timeT t)
+{
+ iterator i = findTime(t);
+ if (i == end() || (*i)->getAbsoluteTime() > t) {
+ if (i == begin()) return end();
+ else --i;
+ }
+ return i;
+}
+
+Composition::ReferenceSegment::iterator
+Composition::ReferenceSegment::findNearestRealTime(RealTime t)
+{
+ iterator i = findRealTime(t);
+ if (i == end() || (getTempoTimestamp(*i) > t)) {
+ if (i == begin()) return end();
+ else --i;
+ }
+ return i;
+}
+
+
+
+int Composition::m_defaultNbBars = 100;
+
+Composition::Composition() :
+ m_solo(false), // default is not soloing
+ m_selectedTrack(0),
+ m_timeSigSegment(TimeSignature::EventType),
+ m_tempoSegment(TempoEventType),
+ m_barPositionsNeedCalculating(true),
+ m_tempoTimestampsNeedCalculating(true),
+ m_basicQuantizer(new BasicQuantizer()),
+ m_notationQuantizer(new NotationQuantizer()),
+ m_position(0),
+ m_defaultTempo(getTempoForQpm(120.0)),
+ m_minTempo(0),
+ m_maxTempo(0),
+ m_startMarker(0),
+ m_endMarker(getBarRange(m_defaultNbBars).first),
+ m_loopStart(0),
+ m_loopEnd(0),
+ m_playMetronome(false),
+ m_recordMetronome(true),
+ m_nextTriggerSegmentId(0)
+{
+ // nothing else
+}
+
+Composition::~Composition()
+{
+ if (!m_observers.empty()) {
+ cerr << "Warning: Composition::~Composition() 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();
+ clear();
+ delete m_basicQuantizer;
+ delete m_notationQuantizer;
+}
+
+Composition::iterator
+Composition::addSegment(Segment *segment)
+{
+ iterator res = weakAddSegment(segment);
+
+ if (res != end()) {
+ updateRefreshStatuses();
+ notifySegmentAdded(segment);
+ }
+
+ return res;
+}
+
+Composition::iterator
+Composition::weakAddSegment(Segment *segment)
+{
+ if (!segment) return end();
+
+ iterator res = m_segments.insert(segment);
+ segment->setComposition(this);
+
+ return res;
+}
+
+void
+Composition::deleteSegment(Composition::iterator i)
+{
+ if (i == end()) return;
+
+ Segment *p = (*i);
+ p->setComposition(0);
+
+ m_segments.erase(i);
+ notifySegmentRemoved(p);
+ delete p;
+
+ updateRefreshStatuses();
+}
+
+bool
+Composition::deleteSegment(Segment *segment)
+{
+ iterator i = findSegment(segment);
+ if (i == end()) return false;
+
+ deleteSegment(i);
+ return true;
+}
+
+bool
+Composition::detachSegment(Segment *segment)
+{
+ bool res = weakDetachSegment(segment);
+
+ if (res) {
+ notifySegmentRemoved(segment);
+ updateRefreshStatuses();
+ }
+
+ return res;
+}
+
+bool
+Composition::weakDetachSegment(Segment *segment)
+{
+ iterator i = findSegment(segment);
+ if (i == end()) return false;
+
+ segment->setComposition(0);
+ m_segments.erase(i);
+
+ return true;
+}
+
+bool
+Composition::contains(const Segment *s)
+{
+ iterator i = findSegment(s);
+ return (i != end());
+}
+
+Composition::iterator
+Composition::findSegment(const Segment *s)
+{
+ iterator i = m_segments.lower_bound(const_cast<Segment*>(s));
+
+ while (i != end()) {
+ if (*i == s) break;
+ if ((*i)->getStartTime() > s->getStartTime()) return end();
+ ++i;
+ }
+
+ return i;
+}
+
+void Composition::setSegmentStartTime(Segment *segment, timeT startTime)
+{
+ // remove the segment from the multiset
+ iterator i = findSegment(segment);
+ if (i == end()) return;
+
+ m_segments.erase(i);
+
+ segment->setStartTimeDataMember(startTime);
+
+ // re-add it
+ m_segments.insert(segment);
+}
+
+int
+Composition::getMaxContemporaneousSegmentsOnTrack(TrackId track) const
+{
+ // Could be made faster, but only if it needs to be.
+
+ // This is similar to the polyphony calculation in
+ // DocumentMetaConfigurationPage ctor.
+
+ std::set<Segment *> simultaneous;
+ std::multimap<timeT, Segment *> ends;
+
+ int maximum = 0;
+
+ for (const_iterator i = begin(); i != end(); ++i) {
+ if ((*i)->getTrack() != track) continue;
+ timeT t0 = (*i)->getStartTime();
+ timeT t1 = (*i)->getRepeatEndTime();
+// std::cerr << "getMaxContemporaneousSegmentsOnTrack(" << track << "): segment " << *i << " from " << t0 << " to " << t1 << std::endl;
+ while (!ends.empty() && t0 >= ends.begin()->first) {
+ simultaneous.erase(ends.begin()->second);
+ ends.erase(ends.begin());
+ }
+ simultaneous.insert(*i);
+ ends.insert(std::multimap<timeT, Segment *>::value_type(t1, *i));
+ int current = simultaneous.size();
+ if (current > maximum) maximum = current;
+ }
+
+ return maximum;
+}
+
+int
+Composition::getSegmentVoiceIndex(const Segment *segment) const
+{
+ TrackId track = segment->getTrack();
+
+ // See function above
+
+ std::map<Segment *, int> indices;
+ std::set<int> used;
+ std::multimap<timeT, Segment *> ends;
+
+ int maximum = 0;
+
+ for (const_iterator i = begin(); i != end(); ++i) {
+ if ((*i)->getTrack() != track) continue;
+ timeT t0 = (*i)->getStartTime();
+ timeT t1 = (*i)->getRepeatEndTime();
+ int index;
+ while (!ends.empty() && t0 >= ends.begin()->first) {
+ index = indices[ends.begin()->second];
+ used.erase(index);
+ indices.erase(ends.begin()->second);
+ ends.erase(ends.begin());
+ }
+ for (index = 0; ; ++index) {
+ if (used.find(index) == used.end()) break;
+ }
+ if (*i == segment) return index;
+ indices[*i] = index;
+ used.insert(index);
+ ends.insert(std::multimap<timeT, Segment *>::value_type(t1, *i));
+ }
+
+ std::cerr << "WARNING: Composition::getSegmentVoiceIndex: segment "
+ << segment << " not found in composition" << std::endl;
+ return 0;
+}
+
+TriggerSegmentRec *
+Composition::addTriggerSegment(Segment *s, int pitch, int velocity)
+{
+ TriggerSegmentId id = m_nextTriggerSegmentId;
+ return addTriggerSegment(s, id, pitch, velocity);
+}
+
+TriggerSegmentRec *
+Composition::addTriggerSegment(Segment *s, TriggerSegmentId id, int pitch, int velocity)
+{
+ TriggerSegmentRec *rec = getTriggerSegmentRec(id);
+ if (rec) return 0;
+ rec = new TriggerSegmentRec(id, s, pitch, velocity);
+ m_triggerSegments.insert(rec);
+ s->setComposition(this);
+ if (m_nextTriggerSegmentId <= id) m_nextTriggerSegmentId = id + 1;
+ return rec;
+}
+
+void
+Composition::deleteTriggerSegment(TriggerSegmentId id)
+{
+ TriggerSegmentRec dummyRec(id, 0);
+ triggersegmentcontaineriterator i = m_triggerSegments.find(&dummyRec);
+ if (i == m_triggerSegments.end()) return;
+ (*i)->getSegment()->setComposition(0);
+ delete (*i)->getSegment();
+ delete *i;
+ m_triggerSegments.erase(i);
+}
+
+void
+Composition::detachTriggerSegment(TriggerSegmentId id)
+{
+ TriggerSegmentRec dummyRec(id, 0);
+ triggersegmentcontaineriterator i = m_triggerSegments.find(&dummyRec);
+ if (i == m_triggerSegments.end()) return;
+ (*i)->getSegment()->setComposition(0);
+ delete *i;
+ m_triggerSegments.erase(i);
+}
+
+void
+Composition::clearTriggerSegments()
+{
+ for (triggersegmentcontaineriterator i = m_triggerSegments.begin();
+ i != m_triggerSegments.end(); ++i) {
+ delete (*i)->getSegment();
+ delete *i;
+ }
+ m_triggerSegments.clear();
+}
+
+int
+Composition::getTriggerSegmentId(Segment *s)
+{
+ for (triggersegmentcontaineriterator i = m_triggerSegments.begin();
+ i != m_triggerSegments.end(); ++i) {
+ if ((*i)->getSegment() == s) return (*i)->getId();
+ }
+ return -1;
+}
+
+Segment *
+Composition::getTriggerSegment(TriggerSegmentId id)
+{
+ TriggerSegmentRec *rec = getTriggerSegmentRec(id);
+ if (!rec) return 0;
+ return rec->getSegment();
+}
+
+TriggerSegmentRec *
+Composition::getTriggerSegmentRec(TriggerSegmentId id)
+{
+ TriggerSegmentRec dummyRec(id, 0);
+ triggersegmentcontaineriterator i = m_triggerSegments.find(&dummyRec);
+ if (i == m_triggerSegments.end()) return 0;
+ return *i;
+}
+
+TriggerSegmentId
+Composition::getNextTriggerSegmentId() const
+{
+ return m_nextTriggerSegmentId;
+}
+
+void
+Composition::setNextTriggerSegmentId(TriggerSegmentId id)
+{
+ m_nextTriggerSegmentId = id;
+}
+
+void
+Composition::updateTriggerSegmentReferences()
+{
+ std::map<TriggerSegmentId, TriggerSegmentRec::SegmentRuntimeIdSet> refs;
+
+ for (iterator i = begin(); i != end(); ++i) {
+ for (Segment::iterator j = (*i)->begin(); j != (*i)->end(); ++j) {
+ if ((*j)->has(BaseProperties::TRIGGER_SEGMENT_ID)) {
+ TriggerSegmentId id =
+ (*j)->get<Int>(BaseProperties::TRIGGER_SEGMENT_ID);
+ refs[id].insert((*i)->getRuntimeId());
+ }
+ }
+ }
+
+ for (std::map<TriggerSegmentId,
+ TriggerSegmentRec::SegmentRuntimeIdSet>::iterator i = refs.begin();
+ i != refs.end(); ++i) {
+ TriggerSegmentRec *rec = getTriggerSegmentRec(i->first);
+ if (rec) rec->setReferences(i->second);
+ }
+}
+
+
+timeT
+Composition::getDuration() const
+{
+ timeT maxDuration = 0;
+
+ for (segmentcontainer::const_iterator i = m_segments.begin();
+ i != m_segments.end(); ++i) {
+
+ timeT segmentTotal = (*i)->getEndTime();
+
+ if (segmentTotal > maxDuration) {
+ maxDuration = segmentTotal;
+ }
+ }
+
+ return maxDuration;
+}
+
+void
+Composition::setStartMarker(const timeT &sM)
+{
+ m_startMarker = sM;
+ updateRefreshStatuses();
+}
+
+void
+Composition::setEndMarker(const timeT &eM)
+{
+ bool shorten = (eM < m_endMarker);
+ m_endMarker = eM;
+ updateRefreshStatuses();
+ notifyEndMarkerChange(shorten);
+}
+
+void
+Composition::clear()
+{
+ while (m_segments.size() > 0) {
+ deleteSegment(begin());
+ }
+
+ clearTracks();
+ clearMarkers();
+ clearTriggerSegments();
+
+ m_timeSigSegment.clear();
+ m_tempoSegment.clear();
+ m_defaultTempo = getTempoForQpm(120.0);
+ m_minTempo = 0;
+ m_maxTempo = 0;
+ m_loopStart = 0;
+ m_loopEnd = 0;
+ m_position = 0;
+ m_startMarker = 0;
+ m_endMarker = getBarRange(m_defaultNbBars).first;
+ m_solo = false;
+ m_selectedTrack = 0;
+ updateRefreshStatuses();
+}
+
+void
+Composition::calculateBarPositions() const
+{
+ if (!m_barPositionsNeedCalculating) return;
+
+#ifdef DEBUG_BAR_STUFF
+ cerr << "Composition::calculateBarPositions" << endl;
+#endif
+
+ ReferenceSegment &t = m_timeSigSegment;
+ ReferenceSegment::iterator i;
+
+ timeT lastBarNo = 0;
+ timeT lastSigTime = 0;
+ timeT barDuration = TimeSignature().getBarDuration();
+
+ if (getStartMarker() < 0) {
+ if (!t.empty() && (*t.begin())->getAbsoluteTime() <= 0) {
+ barDuration = TimeSignature(**t.begin()).getBarDuration();
+ }
+ lastBarNo = getStartMarker() / barDuration;
+ lastSigTime = getStartMarker();
+#ifdef DEBUG_BAR_STUFF
+ cerr << "Composition::calculateBarPositions: start marker = " << getStartMarker() << ", so initial bar number = " << lastBarNo << endl;
+#endif
+ }
+
+ for (i = t.begin(); i != t.end(); ++i) {
+
+ timeT myTime = (*i)->getAbsoluteTime();
+ int n = (myTime - lastSigTime) / barDuration;
+
+ // should only happen for first time sig, when it's at time < 0:
+ if (myTime < lastSigTime) --n;
+
+ // would there be a new bar here anyway?
+ if (barDuration * n + lastSigTime == myTime) { // yes
+ n += lastBarNo;
+ } else { // no
+ n += lastBarNo + 1;
+ }
+
+#ifdef DEBUG_BAR_STUFF
+ cerr << "Composition::calculateBarPositions: bar " << n
+ << " at " << myTime << endl;
+#endif
+
+ (*i)->set<Int>(BarNumberProperty, n);
+
+ lastBarNo = n;
+ lastSigTime = myTime;
+ barDuration = TimeSignature(**i).getBarDuration();
+ }
+
+ m_barPositionsNeedCalculating = false;
+}
+
+int
+Composition::getNbBars() const
+{
+ calculateBarPositions();
+
+ // the "-1" is a small kludge to deal with the case where the
+ // composition has a duration that's an exact number of bars
+ int bars = getBarNumber(getDuration() - 1) + 1;
+
+#ifdef DEBUG_BAR_STUFF
+ cerr << "Composition::getNbBars: returning " << bars << endl;
+#endif
+ return bars;
+}
+
+int
+Composition::getBarNumber(timeT t) const
+{
+ calculateBarPositions();
+ ReferenceSegment::iterator i = m_timeSigSegment.findNearestTime(t);
+ int n;
+
+ if (i == m_timeSigSegment.end()) { // precedes any time signatures
+
+ timeT bd = TimeSignature().getBarDuration();
+ if (t < 0) { // see comment in getTimeSignatureAtAux
+ i = m_timeSigSegment.begin();
+ if (i != m_timeSigSegment.end() && (*i)->getAbsoluteTime() <= 0) {
+ bd = TimeSignature(**i).getBarDuration();
+ }
+ }
+
+ n = t / bd;
+ if (t < 0) {
+ // negative bars should be rounded down, except where
+ // the time is on a barline in which case we already
+ // have the right value (i.e. time -1920 is bar -1,
+ // but time -3840 is also bar -1, in 4/4)
+ if (n * bd != t) --n;
+ }
+
+ } else {
+
+ n = (*i)->get<Int>(BarNumberProperty);
+ timeT offset = t - (*i)->getAbsoluteTime();
+ n += offset / TimeSignature(**i).getBarDuration();
+ }
+
+#ifdef DEBUG_BAR_STUFF
+ cerr << "Composition::getBarNumber(" << t << "): returning " << n << endl;
+#endif
+ return n;
+}
+
+
+std::pair<timeT, timeT>
+Composition::getBarRangeForTime(timeT t) const
+{
+ return getBarRange(getBarNumber(t));
+}
+
+
+std::pair<timeT, timeT>
+Composition::getBarRange(int n) const
+{
+ calculateBarPositions();
+
+ Event dummy("dummy", 0);
+ dummy.set<Int>(BarNumberProperty, n);
+
+ ReferenceSegment::iterator j = std::lower_bound
+ (m_timeSigSegment.begin(), m_timeSigSegment.end(),
+ &dummy, BarNumberComparator());
+ ReferenceSegment::iterator i = j;
+
+ if (i == m_timeSigSegment.end() || (*i)->get<Int>(BarNumberProperty) > n) {
+ if (i == m_timeSigSegment.begin()) i = m_timeSigSegment.end();
+ else --i;
+ } else ++j; // j needs to point to following barline
+
+ timeT start, finish;
+
+ if (i == m_timeSigSegment.end()) { // precedes any time sig changes
+
+ timeT barDuration = TimeSignature().getBarDuration();
+ if (n < 0) { // see comment in getTimeSignatureAtAux
+ i = m_timeSigSegment.begin();
+ if (i != m_timeSigSegment.end() && (*i)->getAbsoluteTime() <= 0) {
+ barDuration = TimeSignature(**i).getBarDuration();
+ }
+ }
+
+ start = n * barDuration;
+ finish = start + barDuration;
+
+#ifdef DEBUG_BAR_STUFF
+ cerr << "Composition::getBarRange[1]: bar " << n << ": (" << start
+ << " -> " << finish << ")" << endl;
+#endif
+
+ } else {
+
+ timeT barDuration = TimeSignature(**i).getBarDuration();
+ start = (*i)->getAbsoluteTime() +
+ (n - (*i)->get<Int>(BarNumberProperty)) * barDuration;
+ finish = start + barDuration;
+
+#ifdef DEBUG_BAR_STUFF
+ cerr << "Composition::getBarRange[2]: bar " << n << ": (" << start
+ << " -> " << finish << ")" << endl;
+#endif
+ }
+
+ // partial bar
+ if (j != m_timeSigSegment.end() && finish > (*j)->getAbsoluteTime()) {
+ finish = (*j)->getAbsoluteTime();
+#ifdef DEBUG_BAR_STUFF
+ cerr << "Composition::getBarRange[3]: bar " << n << ": (" << start
+ << " -> " << finish << ")" << endl;
+#endif
+ }
+
+ return std::pair<timeT, timeT>(start, finish);
+}
+
+
+int
+Composition::addTimeSignature(timeT t, TimeSignature timeSig)
+{
+#ifdef DEBUG_BAR_STUFF
+ cerr << "Composition::addTimeSignature(" << t << ", " << timeSig.getNumerator() << "/" << timeSig.getDenominator() << ")" << endl;
+#endif
+
+ ReferenceSegment::iterator i =
+ m_timeSigSegment.insert(timeSig.getAsEvent(t));
+ m_barPositionsNeedCalculating = true;
+
+ updateRefreshStatuses();
+ notifyTimeSignatureChanged();
+
+ return std::distance(m_timeSigSegment.begin(), i);
+}
+
+TimeSignature
+Composition::getTimeSignatureAt(timeT t) const
+{
+ TimeSignature timeSig;
+ (void)getTimeSignatureAt(t, timeSig);
+ return timeSig;
+}
+
+timeT
+Composition::getTimeSignatureAt(timeT t, TimeSignature &timeSig) const
+{
+ ReferenceSegment::iterator i = getTimeSignatureAtAux(t);
+
+ if (i == m_timeSigSegment.end()) {
+ timeSig = TimeSignature();
+ return 0;
+ } else {
+ timeSig = TimeSignature(**i);
+ return (*i)->getAbsoluteTime();
+ }
+}
+
+TimeSignature
+Composition::getTimeSignatureInBar(int barNo, bool &isNew) const
+{
+ isNew = false;
+ timeT t = getBarRange(barNo).first;
+
+ ReferenceSegment::iterator i = getTimeSignatureAtAux(t);
+
+ if (i == m_timeSigSegment.end()) return TimeSignature();
+ if (t == (*i)->getAbsoluteTime()) isNew = true;
+
+ return TimeSignature(**i);
+}
+
+Composition::ReferenceSegment::iterator
+Composition::getTimeSignatureAtAux(timeT t) const
+{
+ ReferenceSegment::iterator i = m_timeSigSegment.findNearestTime(t);
+
+ // In negative time, if there's no time signature actually defined
+ // prior to the point of interest then we use the next time
+ // signature after it, so long as it's no later than time zero.
+ // This is the only rational way to deal with count-in bars where
+ // the correct time signature otherwise won't appear until we hit
+ // bar zero.
+
+ if (t < 0 && i == m_timeSigSegment.end()) {
+ i = m_timeSigSegment.begin();
+ if (i != m_timeSigSegment.end() && (*i)->getAbsoluteTime() > 0) {
+ i = m_timeSigSegment.end();
+ }
+ }
+
+ return i;
+}
+
+int
+Composition::getTimeSignatureCount() const
+{
+ return m_timeSigSegment.size();
+}
+
+int
+Composition::getTimeSignatureNumberAt(timeT t) const
+{
+ ReferenceSegment::iterator i = getTimeSignatureAtAux(t);
+ if (i == m_timeSigSegment.end()) return -1;
+ else return std::distance(m_timeSigSegment.begin(), i);
+}
+
+std::pair<timeT, TimeSignature>
+Composition::getTimeSignatureChange(int n) const
+{
+ return std::pair<timeT, TimeSignature>
+ (m_timeSigSegment[n]->getAbsoluteTime(),
+ TimeSignature(*m_timeSigSegment[n]));
+}
+
+void
+Composition::removeTimeSignature(int n)
+{
+ m_timeSigSegment.erase(m_timeSigSegment[n]);
+ m_barPositionsNeedCalculating = true;
+ updateRefreshStatuses();
+ notifyTimeSignatureChanged();
+}
+
+
+tempoT
+Composition::getTempoAtTime(timeT t) const
+{
+ ReferenceSegment::iterator i = m_tempoSegment.findNearestTime(t);
+
+ // In negative time, if there's no tempo event actually defined
+ // prior to the point of interest then we use the next one after
+ // it, so long as it's no later than time zero. This is the only
+ // rational way to deal with count-in bars where the correct
+ // tempo otherwise won't appear until we hit bar zero. See also
+ // getTimeSignatureAt
+
+ if (i == m_tempoSegment.end()) {
+ if (t < 0) {
+#ifdef DEBUG_TEMPO_STUFF
+ cerr << "Composition: Negative time " << t << " for tempo, using 0" << endl;
+#endif
+ return getTempoAtTime(0);
+ }
+ else return m_defaultTempo;
+ }
+
+ tempoT tempo = (tempoT)((*i)->get<Int>(TempoProperty));
+
+ if ((*i)->has(TargetTempoProperty)) {
+
+ tempoT target = (tempoT)((*i)->get<Int>(TargetTempoProperty));
+ ReferenceSegment::iterator j = i;
+ ++j;
+
+ if (target > 0 || (target == 0 && j != m_tempoSegment.end())) {
+
+ timeT t0 = (*i)->getAbsoluteTime();
+ timeT t1 = (j != m_tempoSegment.end() ?
+ (*j)->getAbsoluteTime() : getEndMarker());
+
+ if (t1 < t0) return tempo;
+
+ if (target == 0) {
+ target = (tempoT)((*j)->get<Int>(TempoProperty));
+ }
+
+ // tempo ramps are linear in 1/tempo
+ double s0 = 1.0 / double(tempo);
+ double s1 = 1.0 / double(target);
+ double s = s0 + (t - t0) * ((s1 - s0) / (t1 - t0));
+
+ tempoT result = tempoT((1.0 / s) + 0.01);
+
+#ifdef DEBUG_TEMPO_STUFF
+ cerr << "Composition: Calculated tempo " << result << " at " << t << endl;
+#endif
+
+ return result;
+ }
+ }
+
+#ifdef DEBUG_TEMPO_STUFF
+ cerr << "Composition: Found tempo " << tempo << " at " << t << endl;
+#endif
+ return tempo;
+}
+
+int
+Composition::addTempoAtTime(timeT time, tempoT tempo, tempoT targetTempo)
+{
+ // If there's an existing tempo at this time, the ReferenceSegment
+ // object will remove the duplicate, but we have to ensure that
+ // the minimum and maximum tempos are updated if necessary.
+
+ bool fullTempoUpdate = false;
+
+ int n = getTempoChangeNumberAt(time);
+ if (n >= 0) {
+ std::pair<timeT, tempoT> tc = getTempoChange(n);
+ if (tc.first == time) {
+ if (tc.second == m_minTempo || tc.second == m_maxTempo) {
+ fullTempoUpdate = true;
+ } else {
+ std::pair<bool, tempoT> tr = getTempoRamping(n);
+ if (tr.first &&
+ (tr.second == m_minTempo || tr.second == m_maxTempo)) {
+ fullTempoUpdate = true;
+ }
+ }
+ }
+ }
+
+ Event *tempoEvent = new Event(TempoEventType, time);
+ tempoEvent->set<Int>(TempoProperty, tempo);
+
+ if (targetTempo >= 0) {
+ tempoEvent->set<Int>(TargetTempoProperty, targetTempo);
+ }
+
+ ReferenceSegment::iterator i = m_tempoSegment.insert(tempoEvent);
+
+ if (fullTempoUpdate) {
+
+ updateExtremeTempos();
+
+ } else {
+
+ if (tempo < m_minTempo || m_minTempo == 0) m_minTempo = tempo;
+ if (targetTempo > 0 && targetTempo < m_minTempo) m_minTempo = targetTempo;
+
+ if (tempo > m_maxTempo || m_maxTempo == 0) m_maxTempo = tempo;
+ if (targetTempo > 0 && targetTempo > m_maxTempo) m_maxTempo = targetTempo;
+ }
+
+ m_tempoTimestampsNeedCalculating = true;
+ updateRefreshStatuses();
+
+#ifdef DEBUG_TEMPO_STUFF
+ cerr << "Composition: Added tempo " << tempo << " at " << time << endl;
+#endif
+ notifyTempoChanged();
+
+ return std::distance(m_tempoSegment.begin(), i);
+}
+
+int
+Composition::getTempoChangeCount() const
+{
+ return m_tempoSegment.size();
+}
+
+int
+Composition::getTempoChangeNumberAt(timeT t) const
+{
+ ReferenceSegment::iterator i = m_tempoSegment.findNearestTime(t);
+ if (i == m_tempoSegment.end()) return -1;
+ else return std::distance(m_tempoSegment.begin(), i);
+}
+
+std::pair<timeT, tempoT>
+Composition::getTempoChange(int n) const
+{
+ return std::pair<timeT, tempoT>
+ (m_tempoSegment[n]->getAbsoluteTime(),
+ tempoT(m_tempoSegment[n]->get<Int>(TempoProperty)));
+}
+
+std::pair<bool, tempoT>
+Composition::getTempoRamping(int n, bool calculate) const
+{
+ tempoT target = -1;
+ if (m_tempoSegment[n]->has(TargetTempoProperty)) {
+ target = m_tempoSegment[n]->get<Int>(TargetTempoProperty);
+ }
+ bool ramped = (target >= 0);
+ if (target == 0) {
+ if (calculate) {
+ if (m_tempoSegment.size() > n+1) {
+ target = m_tempoSegment[n+1]->get<Int>(TempoProperty);
+ }
+ }
+ }
+ if (target < 0 || (calculate && (target == 0))) {
+ target = m_tempoSegment[n]->get<Int>(TempoProperty);
+ }
+ return std::pair<bool, tempoT>(ramped, target);
+}
+
+void
+Composition::removeTempoChange(int n)
+{
+ tempoT oldTempo = m_tempoSegment[n]->get<Int>(TempoProperty);
+ tempoT oldTarget = -1;
+
+ if (m_tempoSegment[n]->has(TargetTempoProperty)) {
+ oldTarget = m_tempoSegment[n]->get<Int>(TargetTempoProperty);
+ }
+
+ m_tempoSegment.erase(m_tempoSegment[n]);
+ m_tempoTimestampsNeedCalculating = true;
+
+ if (oldTempo == m_minTempo ||
+ oldTempo == m_maxTempo ||
+ (oldTarget > 0 && oldTarget == m_minTempo) ||
+ (oldTarget > 0 && oldTarget == m_maxTempo)) {
+ updateExtremeTempos();
+ }
+
+ updateRefreshStatuses();
+ notifyTempoChanged();
+}
+
+void
+Composition::updateExtremeTempos()
+{
+ m_minTempo = 0;
+ m_maxTempo = 0;
+ for (ReferenceSegment::iterator i = m_tempoSegment.begin();
+ i != m_tempoSegment.end(); ++i) {
+ tempoT tempo = (*i)->get<Int>(TempoProperty);
+ tempoT target = -1;
+ if ((*i)->has(TargetTempoProperty)) {
+ target = (*i)->get<Int>(TargetTempoProperty);
+ }
+ if (tempo < m_minTempo || m_minTempo == 0) m_minTempo = tempo;
+ if (target > 0 && target < m_minTempo) m_minTempo = target;
+ if (tempo > m_maxTempo || m_maxTempo == 0) m_maxTempo = tempo;
+ if (target > 0 && target > m_maxTempo) m_maxTempo = target;
+ }
+ if (m_minTempo == 0) {
+ m_minTempo = m_defaultTempo;
+ m_maxTempo = m_defaultTempo;
+ }
+}
+
+RealTime
+Composition::getElapsedRealTime(timeT t) const
+{
+ calculateTempoTimestamps();
+
+ ReferenceSegment::iterator i = m_tempoSegment.findNearestTime(t);
+ if (i == m_tempoSegment.end()) {
+ i = m_tempoSegment.begin();
+ if (t >= 0 ||
+ (i == m_tempoSegment.end() || (*i)->getAbsoluteTime() > 0)) {
+ return time2RealTime(t, m_defaultTempo);
+ }
+ }
+
+ RealTime elapsed;
+
+ tempoT target = -1;
+ timeT nextTempoTime = t;
+
+ if (!getTempoTarget(i, target, nextTempoTime)) target = -1;
+
+ if (target > 0) {
+ elapsed = getTempoTimestamp(*i) +
+ time2RealTime(t - (*i)->getAbsoluteTime(),
+ tempoT((*i)->get<Int>(TempoProperty)),
+ nextTempoTime - (*i)->getAbsoluteTime(),
+ target);
+ } else {
+ elapsed = getTempoTimestamp(*i) +
+ time2RealTime(t - (*i)->getAbsoluteTime(),
+ tempoT((*i)->get<Int>(TempoProperty)));
+ }
+
+#ifdef DEBUG_TEMPO_STUFF
+ cerr << "Composition::getElapsedRealTime: " << t << " -> "
+ << elapsed << " (last tempo change at " << (*i)->getAbsoluteTime() << ")" << endl;
+#endif
+
+ return elapsed;
+}
+
+timeT
+Composition::getElapsedTimeForRealTime(RealTime t) const
+{
+ calculateTempoTimestamps();
+
+ ReferenceSegment::iterator i = m_tempoSegment.findNearestRealTime(t);
+ if (i == m_tempoSegment.end()) {
+ i = m_tempoSegment.begin();
+ if (t >= RealTime::zeroTime ||
+ (i == m_tempoSegment.end() || (*i)->getAbsoluteTime() > 0)) {
+ return realTime2Time(t, m_defaultTempo);
+ }
+ }
+
+ timeT elapsed;
+
+ tempoT target = -1;
+ timeT nextTempoTime = 0;
+ if (!getTempoTarget(i, target, nextTempoTime)) target = -1;
+
+ if (target > 0) {
+ elapsed = (*i)->getAbsoluteTime() +
+ realTime2Time(t - getTempoTimestamp(*i),
+ (tempoT)((*i)->get<Int>(TempoProperty)),
+ nextTempoTime - (*i)->getAbsoluteTime(),
+ target);
+ } else {
+ elapsed = (*i)->getAbsoluteTime() +
+ realTime2Time(t - getTempoTimestamp(*i),
+ (tempoT)((*i)->get<Int>(TempoProperty)));
+ }
+
+#ifdef DEBUG_TEMPO_STUFF
+ static int doError = true;
+ if (doError) {
+ doError = false;
+ RealTime cfReal = getElapsedRealTime(elapsed);
+ timeT cfTimeT = getElapsedTimeForRealTime(cfReal);
+ doError = true;
+ cerr << "getElapsedTimeForRealTime: " << t << " -> "
+ << elapsed << " (error " << (cfReal - t)
+ << " or " << (cfTimeT - elapsed) << ", tempo "
+ << (*i)->getAbsoluteTime() << ":"
+ << (tempoT)((*i)->get<Int>(TempoProperty)) << ")" << endl;
+ }
+#endif
+ return elapsed;
+}
+
+void
+Composition::calculateTempoTimestamps() const
+{
+ if (!m_tempoTimestampsNeedCalculating) return;
+
+ timeT lastTimeT = 0;
+ RealTime lastRealTime;
+
+ tempoT tempo = m_defaultTempo;
+ tempoT target = -1;
+
+#ifdef DEBUG_TEMPO_STUFF
+ cerr << "Composition::calculateTempoTimestamps: Tempo events are:" << endl;
+#endif
+
+ for (ReferenceSegment::iterator i = m_tempoSegment.begin();
+ i != m_tempoSegment.end(); ++i) {
+
+ RealTime myTime;
+
+ if (target > 0) {
+ myTime = lastRealTime +
+ time2RealTime((*i)->getAbsoluteTime() - lastTimeT, tempo,
+ (*i)->getAbsoluteTime() - lastTimeT, target);
+ } else {
+ myTime = lastRealTime +
+ time2RealTime((*i)->getAbsoluteTime() - lastTimeT, tempo);
+ }
+
+ setTempoTimestamp(*i, myTime);
+
+#ifdef DEBUG_TEMPO_STUFF
+ (*i)->dump(cerr);
+#endif
+
+ lastRealTime = myTime;
+ lastTimeT = (*i)->getAbsoluteTime();
+ tempo = tempoT((*i)->get<Int>(TempoProperty));
+
+ target = -1;
+ timeT nextTempoTime = 0;
+ if (!getTempoTarget(i, target, nextTempoTime)) target = -1;
+ }
+
+ m_tempoTimestampsNeedCalculating = false;
+}
+
+#ifdef DEBUG_TEMPO_STUFF
+static int DEBUG_silence_recursive_tempo_printout = 0;
+#endif
+
+RealTime
+Composition::time2RealTime(timeT t, tempoT tempo) const
+{
+ static timeT cdur = Note(Note::Crotchet).getDuration();
+
+ double dt = (double(t) * 100000 * 60) / (double(tempo) * cdur);
+
+ int sec = int(dt);
+ int nsec = int((dt - sec) * 1000000000);
+
+ RealTime rt(sec, nsec);
+
+#ifdef DEBUG_TEMPO_STUFF
+ if (!DEBUG_silence_recursive_tempo_printout) {
+ cerr << "Composition::time2RealTime: t " << t << ", sec " << sec << ", nsec "
+ << nsec << ", tempo " << tempo
+ << ", cdur " << cdur << ", dt " << dt << ", rt " << rt << endl;
+ DEBUG_silence_recursive_tempo_printout = 1;
+ timeT ct = realTime2Time(rt, tempo);
+ timeT et = t - ct;
+ RealTime ert = time2RealTime(et, tempo);
+ cerr << "cf. realTime2Time(" << rt << ") -> " << ct << " [err " << et << " (" << ert << "?)]" << endl;
+ DEBUG_silence_recursive_tempo_printout=0;
+ }
+#endif
+
+ return rt;
+}
+
+RealTime
+Composition::time2RealTime(timeT time, tempoT tempo,
+ timeT targetTime, tempoT targetTempo) const
+{
+ static timeT cdur = Note(Note::Crotchet).getDuration();
+
+ // The real time elapsed at musical time t, in seconds, during a
+ // smooth tempo change from "tempo" at musical time zero to
+ // "targetTempo" at musical time "targetTime", is
+ //
+ // 2
+ // at + t (b - a)
+ // ---------
+ // 2n
+ // where
+ //
+ // a is the initial tempo in seconds per tick
+ // b is the target tempo in seconds per tick
+ // n is targetTime in ticks
+
+ if (targetTime == 0 || targetTempo == tempo) {
+ return time2RealTime(time, targetTempo);
+ }
+
+ double a = (100000 * 60) / (double(tempo) * cdur);
+ double b = (100000 * 60) / (double(targetTempo) * cdur);
+ double t = time;
+ double n = targetTime;
+ double result = (a * t) + (t * t * (b - a)) / (2 * n);
+
+ int sec = int(result);
+ int nsec = int((result - sec) * 1000000000);
+
+ RealTime rt(sec, nsec);
+
+#ifdef DEBUG_TEMPO_STUFF
+ if (!DEBUG_silence_recursive_tempo_printout) {
+ cerr << "Composition::time2RealTime[2]: time " << time << ", tempo "
+ << tempo << ", targetTime " << targetTime << ", targetTempo "
+ << targetTempo << ": rt " << rt << endl;
+ DEBUG_silence_recursive_tempo_printout = 1;
+// RealTime nextRt = time2RealTime(targetTime, tempo, targetTime, targetTempo);
+ timeT ct = realTime2Time(rt, tempo, targetTime, targetTempo);
+ cerr << "cf. realTime2Time: rt " << rt << " -> " << ct << endl;
+ DEBUG_silence_recursive_tempo_printout=0;
+ }
+#endif
+
+ return rt;
+}
+
+timeT
+Composition::realTime2Time(RealTime rt, tempoT tempo) const
+{
+ static timeT cdur = Note(Note::Crotchet).getDuration();
+
+ double tsec = (double(rt.sec) * cdur) * (tempo / (60.0 * 100000.0));
+ double tnsec = (double(rt.nsec) * cdur) * (tempo / 100000.0);
+
+ double dt = tsec + (tnsec / 60000000000.0);
+ timeT t = (timeT)(dt + (dt < 0 ? -1e-6 : 1e-6));
+
+#ifdef DEBUG_TEMPO_STUFF
+ if (!DEBUG_silence_recursive_tempo_printout) {
+ cerr << "Composition::realTime2Time: rt.sec " << rt.sec << ", rt.nsec "
+ << rt.nsec << ", tempo " << tempo
+ << ", cdur " << cdur << ", tsec " << tsec << ", tnsec " << tnsec << ", dt " << dt << ", t " << t << endl;
+ DEBUG_silence_recursive_tempo_printout = 1;
+ RealTime crt = time2RealTime(t, tempo);
+ RealTime ert = rt - crt;
+ timeT et = realTime2Time(ert, tempo);
+ cerr << "cf. time2RealTime(" << t << ") -> " << crt << " [err " << ert << " (" << et << "?)]" << endl;
+ DEBUG_silence_recursive_tempo_printout = 0;
+ }
+#endif
+
+ return t;
+}
+
+timeT
+Composition::realTime2Time(RealTime rt, tempoT tempo,
+ timeT targetTime, tempoT targetTempo) const
+{
+ static timeT cdur = Note(Note::Crotchet).getDuration();
+
+ // Inverse of the expression in time2RealTime above.
+ //
+ // The musical time elapsed at real time t, in ticks, during a
+ // smooth tempo change from "tempo" at real time zero to
+ // "targetTempo" at real time "targetTime", is
+ //
+ // 2na (+/-) sqrt((2nb)^2 + 8(b-a)tn)
+ // - ----------------------------------
+ // 2(b-a)
+ // where
+ //
+ // a is the initial tempo in seconds per tick
+ // b is the target tempo in seconds per tick
+ // n is target real time in ticks
+
+ if (targetTempo == tempo) return realTime2Time(rt, tempo);
+
+ double a = (100000 * 60) / (double(tempo) * cdur);
+ double b = (100000 * 60) / (double(targetTempo) * cdur);
+ double t = double(rt.sec) + double(rt.nsec) / 1e9;
+ double n = targetTime;
+
+ double term1 = 2.0 * n * a;
+ double term2 = (2.0 * n * a) * (2.0 * n * a) + 8 * (b - a) * t * n;
+
+ if (term2 < 0) {
+ // We're screwed, but at least let's not crash
+ std::cerr << "ERROR: Composition::realTime2Time: term2 < 0 (it's " << term2 << ")" << std::endl;
+#ifdef DEBUG_TEMPO_STUFF
+ std::cerr << "rt = " << rt << ", tempo = " << tempo << ", targetTime = " << targetTime << ", targetTempo = " << targetTempo << std::endl;
+ std::cerr << "n = " << n << ", b = " << b << ", a = " << a << ", t = " << t <<std::endl;
+ std::cerr << "that's sqrt( (" << ((2.0*n*a*2.0*n*a)) << ") + "
+ << (8*(b-a)*t*n) << " )" << endl;
+
+ std::cerr << "so our original expression was " << rt << " = "
+ << a << "t + (t^2 * (" << b << " - " << a << ")) / " << 2*n << std::endl;
+#endif
+
+ return realTime2Time(rt, tempo);
+ }
+
+ double term3 = sqrt(term2);
+
+ // We only want the positive root
+ if (term3 > 0) term3 = -term3;
+
+ double result = - (term1 + term3) / (2 * (b - a));
+
+#ifdef DEBUG_TEMPO_STUFF
+ std::cerr << "Composition::realTime2Time:" <<endl;
+ std::cerr << "n = " << n << ", b = " << b << ", a = " << a << ", t = " << t <<std::endl;
+ std::cerr << "+/-sqrt(term2) = " << term3 << std::endl;
+ std::cerr << "result = " << result << endl;
+#endif
+
+ return long(result + 0.1);
+}
+
+bool
+Composition::getTempoTarget(ReferenceSegment::const_iterator i,
+ tempoT &target,
+ timeT &targetTime) const
+{
+ target = -1;
+ targetTime = 0;
+ bool have = false;
+
+ if ((*i)->has(TargetTempoProperty)) {
+ target = (*i)->get<Int>(TargetTempoProperty);
+ if (target >= 0) {
+ ReferenceSegment::const_iterator j(i);
+ if (++j != m_tempoSegment.end()) {
+ if (target == 0) target = (*j)->get<Int>(TempoProperty);
+ targetTime = (*j)->getAbsoluteTime();
+ } else {
+ targetTime = getEndMarker();
+ if (targetTime < (*i)->getAbsoluteTime()) {
+ target = -1;
+ }
+ }
+ if (target > 0) have = true;
+ }
+ }
+
+ return have;
+}
+
+RealTime
+Composition::getTempoTimestamp(const Event *e)
+{
+ RealTime res;
+ e->get<RealTimeT>(TempoTimestampProperty, res);
+ return res;
+}
+
+void
+Composition::setTempoTimestamp(Event *e, RealTime t)
+{
+ e->setMaybe<RealTimeT>(TempoTimestampProperty, t);
+}
+
+void
+Composition::getMusicalTimeForAbsoluteTime(timeT absTime,
+ int &bar, int &beat,
+ int &fraction, int &remainder)
+{
+ bar = getBarNumber(absTime);
+
+ TimeSignature timeSig = getTimeSignatureAt(absTime);
+ timeT barStart = getBarStart(bar);
+ timeT beatDuration = timeSig.getBeatDuration();
+ beat = (absTime - barStart) / beatDuration + 1;
+
+ remainder = (absTime - barStart) % beatDuration;
+ timeT fractionDuration = Note(Note::Shortest).getDuration();
+ fraction = remainder / fractionDuration;
+ remainder = remainder % fractionDuration;
+}
+
+void
+Composition::getMusicalTimeForDuration(timeT absTime, timeT duration,
+ int &bars, int &beats,
+ int &fractions, int &remainder)
+{
+ TimeSignature timeSig = getTimeSignatureAt(absTime);
+ timeT barDuration = timeSig.getBarDuration();
+ timeT beatDuration = timeSig.getBeatDuration();
+
+ bars = duration / barDuration;
+ beats = (duration % barDuration) / beatDuration;
+ remainder = (duration % barDuration) % beatDuration;
+ timeT fractionDuration = Note(Note::Shortest).getDuration();
+ fractions = remainder / fractionDuration;
+ remainder = remainder % fractionDuration;
+}
+
+timeT
+Composition::getAbsoluteTimeForMusicalTime(int bar, int beat,
+ int fraction, int remainder)
+{
+ timeT t = getBarStart(bar - 1);
+ TimeSignature timesig = getTimeSignatureAt(t);
+ t += (beat-1) * timesig.getBeatDuration();
+ t += Note(Note::Shortest).getDuration() * fraction;
+ t += remainder;
+ return t;
+}
+
+timeT
+Composition::getDurationForMusicalTime(timeT absTime,
+ int bars, int beats,
+ int fractions, int remainder)
+{
+ TimeSignature timeSig = getTimeSignatureAt(absTime);
+ timeT barDuration = timeSig.getBarDuration();
+ timeT beatDuration = timeSig.getBeatDuration();
+ timeT t = bars * barDuration + beats * beatDuration + fractions *
+ Note(Note::Shortest).getDuration() + remainder;
+ return t;
+}
+
+void
+Composition::setPosition(timeT position)
+{
+ m_position = position;
+}
+
+void Composition::setPlayMetronome(bool value)
+{
+ m_playMetronome = value;
+ notifyMetronomeChanged();
+}
+
+void Composition::setRecordMetronome(bool value)
+{
+ m_recordMetronome = value;
+ notifyMetronomeChanged();
+}
+
+
+
+#ifdef TRACK_DEBUG
+// track debug convenience function
+//
+static void dumpTracks(Composition::trackcontainer& tracks)
+{
+ Composition::trackiterator it = tracks.begin();
+ for (; it != tracks.end(); ++it) {
+ std::cerr << "tracks[" << (*it).first << "] = "
+ << (*it).second << std::endl;
+ }
+}
+#endif
+
+Track* Composition::getTrackById(TrackId track) const
+{
+ trackconstiterator i = m_tracks.find(track);
+
+ if (i != m_tracks.end())
+ return (*i).second;
+
+ std::cerr << "Composition::getTrackById("
+ << track << ") - WARNING - track id not found, this is probably a BUG "
+ << __FILE__ << ":" << __LINE__ << std::endl;
+ std::cerr << "Available track ids are: " << std::endl;
+ for (trackconstiterator i = m_tracks.begin(); i != m_tracks.end(); ++i) {
+ std::cerr << (*i).second->getId() << std::endl;
+ }
+
+ return 0;
+}
+
+// Move a track object to a new id and position in the container -
+// used when deleting and undoing deletion of tracks.
+//
+//
+void Composition::resetTrackIdAndPosition(TrackId oldId, TrackId newId,
+ int position)
+{
+ trackiterator titerator = m_tracks.find(oldId);
+
+ if (titerator != m_tracks.end())
+ {
+ // detach old track
+ Track *track = (*titerator).second;
+ m_tracks.erase(titerator);
+
+ // set new position and
+ track->setId(newId);
+ track->setPosition(position);
+ m_tracks[newId] = track;
+
+ // modify segment mappings
+ //
+ for (segmentcontainer::const_iterator i = m_segments.begin();
+ i != m_segments.end(); ++i)
+ {
+ if ((*i)->getTrack() == oldId) (*i)->setTrack(newId);
+ }
+
+ checkSelectedAndRecordTracks();
+ updateRefreshStatuses();
+ notifyTrackChanged(getTrackById(newId));
+ }
+ else
+ std::cerr << "Composition::resetTrackIdAndPosition - "
+ << "can't move track " << oldId << " to " << newId
+ << std::endl;
+}
+
+void Composition::setSelectedTrack(TrackId track)
+{
+ m_selectedTrack = track;
+ notifySoloChanged();
+}
+
+void Composition::setSolo(bool value)
+{
+ m_solo = value;
+ notifySoloChanged();
+}
+
+// Insert a Track representation into the Composition
+//
+void Composition::addTrack(Track *track)
+{
+ // make sure a track with the same id isn't already there
+ //
+ if (m_tracks.find(track->getId()) == m_tracks.end()) {
+
+ m_tracks[track->getId()] = track;
+ track->setOwningComposition(this);
+ updateRefreshStatuses();
+ notifyTrackChanged(track);
+
+ } else {
+ std::cerr << "Composition::addTrack("
+ << track << "), id = " << track->getId()
+ << " - WARNING - track id already present "
+ << __FILE__ << ":" << __LINE__ << std::endl;
+ // throw Exception("track id already present");
+ }
+}
+
+
+void Composition::deleteTrack(Rosegarden::TrackId track)
+{
+ trackiterator titerator = m_tracks.find(track);
+
+ if (titerator == m_tracks.end()) {
+
+ std::cerr << "Composition::deleteTrack : no track of id " << track << std::endl;
+ throw Exception("track id not found");
+
+ } else {
+
+ delete ((*titerator).second);
+ m_tracks.erase(titerator);
+ checkSelectedAndRecordTracks();
+ updateRefreshStatuses();
+ notifyTrackDeleted(track);
+ }
+
+}
+
+bool Composition::detachTrack(Rosegarden::Track *track)
+{
+ trackiterator it = m_tracks.begin();
+
+ for (; it != m_tracks.end(); ++it)
+ {
+ if ((*it).second == track)
+ break;
+ }
+
+ if (it == m_tracks.end()) {
+ std::cerr << "Composition::detachTrack() : no such track " << track << std::endl;
+ throw Exception("track id not found");
+ return false;
+ }
+
+ ((*it).second)->setOwningComposition(0);
+
+ m_tracks.erase(it);
+ updateRefreshStatuses();
+ checkSelectedAndRecordTracks();
+
+ return true;
+}
+
+void Composition::checkSelectedAndRecordTracks()
+{
+ // reset m_selectedTrack and m_recordTrack to the next valid track id
+ // if the track they point to has been deleted
+
+ if (m_tracks.find(m_selectedTrack) == m_tracks.end()) {
+
+ m_selectedTrack = getClosestValidTrackId(m_selectedTrack);
+ notifySoloChanged();
+
+ }
+
+ for (recordtrackcontainer::iterator i = m_recordTracks.begin();
+ i != m_recordTracks.end(); ++i) {
+ if (m_tracks.find(*i) == m_tracks.end()) {
+ m_recordTracks.erase(i);
+ }
+ }
+
+}
+
+TrackId
+Composition::getClosestValidTrackId(TrackId id) const
+{
+ long diff = LONG_MAX;
+ TrackId closestValidTrackId = 0;
+
+ for (trackcontainer::const_iterator i = getTracks().begin();
+ i != getTracks().end(); ++i) {
+
+ long cdiff = labs(i->second->getId() - id);
+
+ if (cdiff < diff) {
+ diff = cdiff;
+ closestValidTrackId = i->second->getId();
+
+ } else break; // std::map is sorted, so if the diff increases, we're passed closest valid id
+
+ }
+
+ return closestValidTrackId;
+}
+
+TrackId
+Composition::getMinTrackId() const
+{
+ if (getTracks().size() == 0) return 0;
+
+ trackcontainer::const_iterator i = getTracks().begin();
+ return i->first;
+}
+
+TrackId
+Composition::getMaxTrackId() const
+{
+ if (getTracks().size() == 0) return 0;
+
+ trackcontainer::const_iterator i = getTracks().end();
+ --i;
+
+ return i->first;
+}
+
+void
+Composition::setTrackRecording(TrackId track, bool recording)
+{
+ if (recording) {
+ m_recordTracks.insert(track);
+ } else {
+ m_recordTracks.erase(track);
+ }
+ Track *t = getTrackById(track);
+ if (t) {
+ t->setArmed(recording);
+ }
+}
+
+bool
+Composition::isTrackRecording(TrackId track) const
+{
+ return m_recordTracks.find(track) != m_recordTracks.end();
+}
+
+
+// Export the Composition as XML, also iterates through
+// Tracks and any further sub-objects
+//
+//
+std::string Composition::toXmlString()
+{
+ std::stringstream composition;
+
+ composition << "<composition recordtracks=\"";
+ for (recordtrackiterator i = m_recordTracks.begin();
+ i != m_recordTracks.end(); ) {
+ composition << *i;
+ if (++i != m_recordTracks.end()) {
+ composition << ",";
+ }
+ }
+ composition << "\" pointer=\"" << m_position;
+ composition << "\" defaultTempo=\"";
+ composition << std::setiosflags(std::ios::fixed)
+ << std::setprecision(4)
+ << getTempoQpm(m_defaultTempo);
+ composition << "\" compositionDefaultTempo=\"";
+ composition << m_defaultTempo;
+
+ if (m_loopStart != m_loopEnd)
+ {
+ composition << "\" loopstart=\"" << m_loopStart;
+ composition << "\" loopend=\"" << m_loopEnd;
+ }
+
+ composition << "\" startMarker=\"" << m_startMarker;
+ composition << "\" endMarker=\"" << m_endMarker;
+
+ // Add the Solo if set
+ //
+ if (m_solo)
+ composition << "\" solo=\"" << m_solo;
+
+ composition << "\" selected=\"" << m_selectedTrack;
+ composition << "\" playmetronome=\"" << m_playMetronome;
+ composition << "\" recordmetronome=\"" << m_recordMetronome;
+ composition << "\" nexttriggerid=\"" << m_nextTriggerSegmentId;
+ composition << "\">" << endl << endl;
+
+ composition << endl;
+
+ for (trackiterator tit = getTracks().begin();
+ tit != getTracks().end();
+ ++tit)
+ {
+ if ((*tit).second)
+ composition << " " << (*tit).second->toXmlString() << endl;
+ }
+
+ composition << endl;
+
+ for (ReferenceSegment::iterator i = m_timeSigSegment.begin();
+ i != m_timeSigSegment.end(); ++i) {
+
+ // Might be nice just to stream the events, but that's
+ // normally done by XmlStorableEvent in gui/ at the
+ // moment. Still, this isn't too much of a hardship
+
+ composition << " <timesignature time=\"" << (*i)->getAbsoluteTime()
+ << "\" numerator=\""
+ << (*i)->get<Int>(TimeSignature::NumeratorPropertyName)
+ << "\" denominator=\""
+ << (*i)->get<Int>(TimeSignature::DenominatorPropertyName)
+ << "\"";
+
+ bool common = false;
+ (*i)->get<Bool>(TimeSignature::ShowAsCommonTimePropertyName, common);
+ if (common) composition << " common=\"true\"";
+
+ bool hidden = false;
+ (*i)->get<Bool>(TimeSignature::IsHiddenPropertyName, hidden);
+ if (hidden) composition << " hidden=\"true\"";
+
+ bool hiddenBars = false;
+ (*i)->get<Bool>(TimeSignature::HasHiddenBarsPropertyName, hiddenBars);
+ if (hiddenBars) composition << " hiddenbars=\"true\"";
+
+ composition << "/>" << endl;
+ }
+
+ composition << endl;
+
+ for (ReferenceSegment::iterator i = m_tempoSegment.begin();
+ i != m_tempoSegment.end(); ++i) {
+
+ tempoT tempo = tempoT((*i)->get<Int>(TempoProperty));
+ tempoT target = -1;
+ if ((*i)->has(TargetTempoProperty)) {
+ target = tempoT((*i)->get<Int>(TargetTempoProperty));
+ }
+ composition << " <tempo time=\"" << (*i)->getAbsoluteTime()
+ << "\" bph=\"" << ((tempo * 6) / 10000)
+ << "\" tempo=\"" << tempo;
+ if (target >= 0) {
+ composition << "\" target=\"" << target;
+ }
+ composition << "\"/>" << endl;
+ }
+
+ composition << endl;
+
+ composition << "<metadata>" << endl
+ << m_metadata.toXmlString() << endl
+ << "</metadata>" << endl << endl;
+
+ composition << "<markers>" << endl;
+ for (markerconstiterator mIt = m_markers.begin();
+ mIt != m_markers.end(); ++mIt)
+ {
+ composition << (*mIt)->toXmlString();
+ }
+ composition << "</markers>" << endl;
+
+
+#if (__GNUC__ < 3)
+ composition << "</composition>" << std::ends;
+#else
+ composition << "</composition>";
+#endif
+
+ return composition.str();
+}
+
+void
+Composition::clearTracks()
+{
+ trackiterator it = m_tracks.begin();
+
+ for (; it != m_tracks.end(); it++)
+ delete ((*it).second);
+
+ m_tracks.erase(m_tracks.begin(), m_tracks.end());
+}
+
+Track*
+Composition::getTrackByPosition(int position) const
+{
+ trackconstiterator it = m_tracks.begin();
+
+ for (; it != m_tracks.end(); it++)
+ {
+ if ((*it).second->getPosition() == position)
+ return (*it).second;
+ }
+
+ return 0;
+
+}
+
+int
+Composition::getTrackPositionById(TrackId id) const
+{
+ Track *track = getTrackById(id);
+ if (!track) return -1;
+ return track->getPosition();
+}
+
+
+Rosegarden::TrackId
+Composition::getNewTrackId() const
+{
+ // Re BR #1070325: another track deletion problem
+ // Formerly this was returning the count of tracks currently in
+ // existence -- returning a duplicate ID if some had been deleted
+ // from the middle. Let's find one that's really available instead.
+
+ TrackId highWater = 0;
+
+ trackconstiterator it = m_tracks.begin();
+
+ for (; it != m_tracks.end(); it++)
+ {
+ if ((*it).second->getId() >= highWater)
+ highWater = (*it).second->getId() + 1;
+ }
+
+ return highWater;
+}
+
+
+void
+Composition::notifySegmentAdded(Segment *s) const
+{
+ // If there is an earlier repeating segment on the same track, we
+ // need to notify the change of its repeat end time
+
+ for (const_iterator i = begin(); i != end(); ++i) {
+
+ if (((*i)->getTrack() == s->getTrack())
+ && ((*i)->isRepeating())
+ && ((*i)->getStartTime() < s->getStartTime())) {
+
+ notifySegmentRepeatEndChanged(*i, (*i)->getRepeatEndTime());
+ }
+ }
+
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->segmentAdded(this, s);
+ }
+}
+
+
+void
+Composition::notifySegmentRemoved(Segment *s) const
+{
+ // If there is an earlier repeating segment on the same track, we
+ // need to notify the change of its repeat end time
+
+ for (const_iterator i = begin(); i != end(); ++i) {
+
+ if (((*i)->getTrack() == s->getTrack())
+ && ((*i)->isRepeating())
+ && ((*i)->getStartTime() < s->getStartTime())) {
+
+ notifySegmentRepeatEndChanged(*i, (*i)->getRepeatEndTime());
+ }
+ }
+
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->segmentRemoved(this, s);
+ }
+}
+
+void
+Composition::notifySegmentRepeatChanged(Segment *s, bool repeat) const
+{
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->segmentRepeatChanged(this, s, repeat);
+ }
+}
+
+void
+Composition::notifySegmentRepeatEndChanged(Segment *s, timeT t) const
+{
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->segmentRepeatEndChanged(this, s, t);
+ }
+}
+
+void
+Composition::notifySegmentEventsTimingChanged(Segment *s, timeT delay, RealTime rtDelay) const
+{
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->segmentEventsTimingChanged(this, s, delay, rtDelay);
+ }
+}
+
+void
+Composition::notifySegmentTransposeChanged(Segment *s, int transpose) const
+{
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->segmentTransposeChanged(this, s, transpose);
+ }
+}
+
+void
+Composition::notifySegmentTrackChanged(Segment *s, TrackId oldId, TrackId newId) const
+{
+ // If there is an earlier repeating segment on either the
+ // origin or destination track, we need to notify the change
+ // of its repeat end time
+
+ for (const_iterator i = begin(); i != end(); ++i) {
+
+ if (((*i)->getTrack() == oldId || (*i)->getTrack() == newId)
+ && ((*i)->isRepeating())
+ && ((*i)->getStartTime() < s->getStartTime())) {
+
+ notifySegmentRepeatEndChanged(*i, (*i)->getRepeatEndTime());
+ }
+ }
+
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->segmentTrackChanged(this, s, newId);
+ }
+}
+
+void
+Composition::notifySegmentStartChanged(Segment *s, timeT t)
+{
+ updateRefreshStatuses(); // not ideal, but best way to ensure track heights are recomputed
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->segmentStartChanged(this, s, t);
+ }
+}
+
+void
+Composition::notifySegmentEndMarkerChange(Segment *s, bool shorten)
+{
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->segmentEndMarkerChanged(this, s, shorten);
+ }
+}
+
+void
+Composition::notifyEndMarkerChange(bool shorten) const
+{
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->endMarkerTimeChanged(this, shorten);
+ }
+}
+
+void
+Composition::notifyTrackChanged(Track *t) const
+{
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->trackChanged(this, t);
+ }
+}
+
+void
+Composition::notifyTrackDeleted(TrackId t) const
+{
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->trackDeleted(this, t);
+ }
+}
+
+void
+Composition::notifyMetronomeChanged() const
+{
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->metronomeChanged(this);
+ }
+}
+
+void
+Composition::notifyTimeSignatureChanged() const
+{
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->timeSignatureChanged(this);
+ }
+}
+
+void
+Composition::notifySoloChanged() const
+{
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->soloChanged(this, isSolo(), getSelectedTrack());
+ }
+}
+
+void
+Composition::notifyTempoChanged() const
+{
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->tempoChanged(this);
+ }
+}
+
+
+void
+Composition::notifySourceDeletion() const
+{
+ for (ObserverSet::const_iterator i = m_observers.begin();
+ i != m_observers.end(); ++i) {
+ (*i)->compositionDeleted(this);
+ }
+}
+
+
+void breakpoint()
+{
+ //std::cerr << "breakpoint()\n";
+}
+
+// Just empty out the markers
+void
+Composition::clearMarkers()
+{
+ markerconstiterator it = m_markers.begin();
+
+ for (; it != m_markers.end(); ++it)
+ {
+ delete *it;
+ }
+
+ m_markers.clear();
+}
+
+void
+Composition::addMarker(Rosegarden::Marker *marker)
+{
+ m_markers.push_back(marker);
+ updateRefreshStatuses();
+}
+
+bool
+Composition::detachMarker(Rosegarden::Marker *marker)
+{
+ markeriterator it = m_markers.begin();
+
+ for (; it != m_markers.end(); ++it)
+ {
+ if (*it == marker)
+ {
+ m_markers.erase(it);
+ updateRefreshStatuses();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+Composition::isMarkerAtPosition(Rosegarden::timeT time) const
+{
+ markerconstiterator it = m_markers.begin();
+
+ for (; it != m_markers.end(); ++it)
+ if ((*it)->getTime() == time) return true;
+
+ return false;
+}
+
+void
+Composition::setSegmentColourMap(Rosegarden::ColourMap &newmap)
+{
+ m_segmentColourMap = newmap;
+
+ updateRefreshStatuses();
+}
+
+void
+Composition::setGeneralColourMap(Rosegarden::ColourMap &newmap)
+{
+ m_generalColourMap = newmap;
+
+ updateRefreshStatuses();
+}
+
+void
+Composition::dump(std::ostream& out, bool) const
+{
+ out << "Composition segments : " << endl;
+
+ for(iterator i = begin(); i != end(); ++i) {
+ Segment* s = *i;
+
+ out << "Segment start : " << s->getStartTime() << " - end : " << s->getEndMarkerTime()
+ << " - repeating : " << s->isRepeating()
+ << " - track id : " << s->getTrack()
+ << " - label : " << s->getLabel()
+ << endl;
+
+ }
+
+}
+
+
+
+}
+
+