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

Composition.C

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

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

    This program is Copyright 2000-2006
        Guillaume Laurent   <glaurent@telegraph-road.org>,
        Chris Cannam        <cannam@all-day-breakfast.com>,
        Richard Bown        <bownie@bownie.com>

    The moral right of the authors to claim authorship of this work
    has been asserted.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.  See the file
    COPYING included with this distribution for more information.
*/

#include "Composition.h"
#include "Segment.h"
#include "FastVector.h"
#include "BaseProperties.h"
#include "Quantizer.h"

#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cmath>

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

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

    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)
{
    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 = 0;
      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());
    updateRefreshStatuses();
}

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::notifySegmentEndMarkerChange(Segment *s, bool shorten) const
{
    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;
        
    }
    
}



}



Generated by  Doxygen 1.6.0   Back to index