a huge set of changes to tempo+meter handling. testing feedback requested. the_CLA...
authorPaul Davis <paul@linuxaudiosystems.com>
Wed, 28 Dec 2011 21:02:31 +0000 (21:02 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Wed, 28 Dec 2011 21:02:31 +0000 (21:02 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@11103 d708f5d6-7413-0410-9779-e7cbd77b26cf

gtk2_ardour/editor_drag.cc
gtk2_ardour/editor_tempodisplay.cc
gtk2_ardour/insert_time_dialog.cc
gtk2_ardour/insert_time_dialog.h
gtk2_ardour/tempo_dialog.cc
libs/ardour/ardour/debug.h
libs/ardour/ardour/tempo.h
libs/ardour/debug.cc
libs/ardour/tempo.cc

index 2d6c98b37325254f80566184f7a0445e67e877ba..5482b265cf396f65173745d782df9d049ee6dbc7 100644 (file)
@@ -1972,7 +1972,7 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
                if (!_copy) {
                        TempoMap& map (_editor->session()->tempo_map());
                        /* remove the section while we drag it */
-                       map.remove_meter (section);
+                       map.remove_meter (section, true);
                }
        }
 
@@ -2090,7 +2090,7 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
                if (!_copy) {
                        TempoMap& map (_editor->session()->tempo_map());
                        /* remove the section while we drag it */
-                       map.remove_tempo (section);
+                       map.remove_tempo (section, true);
                }
        }
 
@@ -2143,7 +2143,7 @@ TempoMarkerDrag::aborted (bool moved)
        if (moved) {
                TempoMap& map (_editor->session()->tempo_map());
                /* we removed it before, so add it back now */
-               map.add_tempo (_marker->tempo(), _marker->tempo().frame());
+               map.add_tempo (_marker->tempo(), _marker->tempo().start());
                // delete the dummy marker we used for visual representation while moving.
                // a new visual marker will show up automatically.
                delete _marker;
index 5f9293d4e249078bc95b52ebd2604b38a2a9f603..89832c5d1367a7e2701c4a608ac0ebc50f720e4c 100644 (file)
@@ -330,9 +330,12 @@ Editor::edit_meter_section (MeterSection* section)
 
        double note_type = meter_dialog.get_note_type ();
 
+       Timecode::BBT_Time when;
+       meter_dialog.get_bbt_time(when);
+
        begin_reversible_command (_("replace tempo mark"));
         XMLNode &before = _session->tempo_map().get_state();
-       _session->tempo_map().replace_meter (*section, Meter (bpb, note_type));
+       _session->tempo_map().replace_meter (*section, Meter (bpb, note_type), when);
         XMLNode &after = _session->tempo_map().get_state();
        _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
        commit_reversible_command ();
@@ -360,14 +363,9 @@ Editor::edit_tempo_section (TempoSection* section)
        tempo_dialog.get_bbt_time(when);
        bpm = max (0.01, bpm);
 
-       cerr << "Editing tempo section to be at " << when << endl;
-       _session->tempo_map().dump (cerr);
        begin_reversible_command (_("replace tempo mark"));
        XMLNode &before = _session->tempo_map().get_state();
-       _session->tempo_map().replace_tempo (*section, Tempo (bpm,nt));
-       _session->tempo_map().dump (cerr);
-       _session->tempo_map().move_tempo (*section, when);
-       _session->tempo_map().dump (cerr);
+       _session->tempo_map().replace_tempo (*section, Tempo (bpm, nt), when);
        XMLNode &after = _session->tempo_map().get_state();
        _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
        commit_reversible_command ();
@@ -416,7 +414,7 @@ Editor::real_remove_tempo_marker (TempoSection *section)
 {
        begin_reversible_command (_("remove tempo mark"));
        XMLNode &before = _session->tempo_map().get_state();
-       _session->tempo_map().remove_tempo (*section);
+       _session->tempo_map().remove_tempo (*section, true);
        XMLNode &after = _session->tempo_map().get_state();
        _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
        commit_reversible_command ();
@@ -450,7 +448,7 @@ Editor::real_remove_meter_marker (MeterSection *section)
 {
        begin_reversible_command (_("remove tempo mark"));
        XMLNode &before = _session->tempo_map().get_state();
-       _session->tempo_map().remove_meter (*section);
+       _session->tempo_map().remove_meter (*section, true);
        XMLNode &after = _session->tempo_map().get_state();
        _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
        commit_reversible_command ();
index ae0e9a1f8ff05fa83d530e212655a708f097da09..a63d885fdc5813599325f728391cc7b5cce2c962 100644 (file)
@@ -80,8 +80,12 @@ InsertTimeDialog::InsertTimeDialog (PublicEditor& e)
        indent->set_padding (0, 0, 12, 0);
        indent->add (_move_locked_markers);
        get_vbox()->pack_start (*indent);
-       _move_tempos.set_label (_("Move tempo and meter changes"));
-       get_vbox()->pack_start (_move_tempos);
+       tempo_label.set_markup (_("Move tempo and meter changes\n<i>(may cause oddities in the tempo map)</i>"));
+       HBox* tempo_box = manage (new HBox);
+       tempo_box->set_spacing (6);
+       tempo_box->pack_start (_move_tempos, false, false);
+       tempo_box->pack_start (tempo_label, false, false);
+       get_vbox()->pack_start (*tempo_box);
 
        add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
        add_button (_("Insert time"), Gtk::RESPONSE_OK);
index 06fbc32b7e4913b521922bac51fcbf0a0e2c241b..3c365c1f6b64dbaddfc59d75ff88809d5063f8bd 100644 (file)
@@ -47,5 +47,6 @@ private:
        Gtk::CheckButton _move_glued_markers;
        Gtk::CheckButton _move_locked_markers;
        Gtk::CheckButton _move_tempos;
+       Gtk::Label tempo_label;
        AudioClock _clock;
 };
index 0edc098c86ba1f6bec2a9f797c88f60adfee804e..edac3bfbefad8cbf76b429d18f24f993bf33a3a5 100644 (file)
@@ -455,7 +455,6 @@ MeterDialog::get_bbt_time (Timecode::BBT_Time& requested)
        }
 
        requested.beats = 1;
-
        requested.ticks = 0;
 
        return true;
index 9397198838301a3c9edf20c4abf2da2bb8ff577a..b9ecabd309cc6b60de80025c9c2de78e10ab0e3d 100644 (file)
@@ -57,6 +57,8 @@ namespace PBD {
                extern uint64_t CycleTimers;
                extern uint64_t MidiTrackers;
                extern uint64_t Layering;
+               extern uint64_t TempoMath;
+               extern uint64_t TempoMap;
        }
 }
 
index 281f42443e7d0828076707d4aea4de29006d1bfc..c286d367c081fe0fe02821f112da10b74f8fdbdf 100644 (file)
 class XMLNode;
 
 namespace ARDOUR {
+
 class Meter;
+class TempoMap;
+
 class Tempo {
   public:
        Tempo (double bpm, double type=4.0) // defaulting to quarter note
@@ -105,7 +108,9 @@ class MetricSection {
        */
        virtual XMLNode& get_state() const = 0;
 
-       int compare (MetricSection *, bool) const;
+       int compare (const MetricSection&) const;
+       bool operator== (const MetricSection& other) const;
+       bool operator!= (const MetricSection& other) const;
 
   private:
        Timecode::BBT_Time _start;
@@ -129,14 +134,29 @@ class MeterSection : public MetricSection, public Meter {
 class TempoSection : public MetricSection, public Tempo {
   public:
        TempoSection (const Timecode::BBT_Time& start, double qpm, double note_type)
-               : MetricSection (start), Tempo (qpm, note_type) {}
+               : MetricSection (start), Tempo (qpm, note_type), _bar_offset (-1.0)  {}
        TempoSection (framepos_t start, double qpm, double note_type)
-               : MetricSection (start), Tempo (qpm, note_type) {}
+               : MetricSection (start), Tempo (qpm, note_type), _bar_offset (-1.0) {}
        TempoSection (const XMLNode&);
 
        static const std::string xml_state_node_name;
 
        XMLNode& get_state() const;
+
+       void update_bar_offset_from_bbt (const Meter&);
+       void update_bbt_time_from_bar_offset (const Meter&);
+       double bar_offset() const { return _bar_offset; }
+
+  private:
+       /* this value provides a fractional offset into the bar in which
+          the tempo section is located in. A value of 0.0 indicates that
+          it occurs on the first beat of the bar, a value of 0.5 indicates
+          that it occurs halfway through the bar and so on.
+          
+          this enables us to keep the tempo change at the same relative
+          position within the bar if/when the meter changes.
+       */
+       double _bar_offset;
 };
 
 typedef std::list<MetricSection*> Metrics;
@@ -215,17 +235,11 @@ class TempoMap : public PBD::StatefulDestructible
        void add_tempo(const Tempo&, Timecode::BBT_Time where);
        void add_meter(const Meter&, Timecode::BBT_Time where);
 
-       void add_tempo(const Tempo&, framepos_t where);
-       void add_meter(const Meter&, framepos_t where);
-
-       void move_tempo (TempoSection&, const Timecode::BBT_Time& to);
-       void move_meter (MeterSection&, const Timecode::BBT_Time& to);
-
-       void remove_tempo(const TempoSection&);
-       void remove_meter(const MeterSection&);
+       void remove_tempo(const TempoSection&, bool send_signal);
+       void remove_meter(const MeterSection&, bool send_signal);
 
-       void replace_tempo (TempoSection& existing, const Tempo& replacement);
-       void replace_meter (MeterSection& existing, const Meter& replacement);
+       void replace_tempo (const TempoSection&, const Tempo&, const Timecode::BBT_Time& where);
+       void replace_meter (const MeterSection&, const Meter&, const Timecode::BBT_Time& where);
 
        framepos_t round_to_bar  (framepos_t frame, int dir);
        framepos_t round_to_beat (framepos_t frame, int dir);
@@ -274,7 +288,8 @@ class TempoMap : public PBD::StatefulDestructible
        Timecode::BBT_Time   last_bbt;
        mutable Glib::RWLock lock;
 
-       void timestamp_metrics (bool use_bbt);
+       void timestamp_metrics (bool reassign_bar_references);
+       void timestamp_metrics_from_audio_time ();
 
        framepos_t round_to_type (framepos_t fr, int dir, BBTPointType);
 
@@ -288,11 +303,11 @@ class TempoMap : public PBD::StatefulDestructible
        const TempoSection& first_tempo() const;
 
        framecnt_t count_frames_between (const Timecode::BBT_Time&, const Timecode::BBT_Time&) const;
-       framecnt_t count_frames_between_metrics (const Meter&, const Tempo&,
-                                                const Timecode::BBT_Time&, const Timecode::BBT_Time&) const;
+       framecnt_t count_frames_with_metrics (const TempoMetric&, const TempoMetric&, const Timecode::BBT_Time&, const Timecode::BBT_Time&) const;
+       framecnt_t count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const Timecode::BBT_Time& start, const Timecode::BBT_Time& end) const;
 
        int move_metric_section (MetricSection&, const Timecode::BBT_Time& to);
-       void do_insert (MetricSection* section, bool with_bbt);
+       void do_insert (MetricSection* section);
 };
 
 }; /* namespace ARDOUR */
index 90793054ad83aa0a8c064ad97b9e943fe90a8b0b..a0fc09a2ad48f97779c5b304b5901751f0709844 100644 (file)
@@ -54,4 +54,7 @@ uint64_t PBD::DEBUG::ControlProtocols = PBD::new_debug_bit ("controlprotocols");
 uint64_t PBD::DEBUG::CycleTimers = PBD::new_debug_bit ("cycletimers");
 uint64_t PBD::DEBUG::MidiTrackers = PBD::new_debug_bit ("miditrackers");
 uint64_t PBD::DEBUG::Layering = PBD::new_debug_bit ("layering");
+uint64_t PBD::DEBUG::TempoMath = PBD::new_debug_bit ("tempomath");
+uint64_t PBD::DEBUG::TempoMap = PBD::new_debug_bit ("tempomap");
+
 
index 05a96c8dd8aa2668edb2c5eae4f0842f6229d995..43e83ecaab2ad187bdaff73cd1c14f2e53f99cbd 100644 (file)
@@ -117,6 +117,15 @@ TempoSection::TempoSection (const XMLNode& node)
        }
 
        set_movable (string_is_affirmative (prop->value()));
+
+       if ((prop = node.property ("bar-offset")) == 0) {
+               _bar_offset = -1.0;
+       } else {
+               if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
+                       error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
+                       throw failed_constructor();
+               }
+       }
 }
 
 XMLNode&
@@ -135,12 +144,45 @@ TempoSection::get_state() const
        root->add_property ("beats-per-minute", buf);
        snprintf (buf, sizeof (buf), "%f", _note_type);
        root->add_property ("note-type", buf);
+       // snprintf (buf, sizeof (buf), "%f", _bar_offset);
+       // root->add_property ("bar-offset", buf);
        snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
        root->add_property ("movable", buf);
 
        return *root;
 }
 
+void
+
+TempoSection::update_bar_offset_from_bbt (const Meter& m)
+{
+       _bar_offset = ((double) (start().beats - 1) + (start().ticks/Timecode::BBT_Time::ticks_per_bar_division)) /
+               m.divisions_per_bar();
+
+       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
+}
+
+void
+TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
+{
+       BBT_Time new_start;
+
+       if (_bar_offset < 0.0) {
+               /* not set yet */
+               return;
+       }
+
+       new_start.bars = start().bars;
+       /* remember the 1-based counting properties of beats */
+       new_start.beats = 1 + (uint32_t) floor (_bar_offset * meter.divisions_per_bar());
+       new_start.ticks = (uint32_t) floor (BBT_Time::ticks_per_bar_division * 
+                                           ((_bar_offset * meter.divisions_per_bar()) - (new_start.beats-1)));
+
+       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("tempo updated BBT time to %1 from bar offset %2\n", new_start,  _bar_offset));
+
+       set_start (new_start);
+}
+
 /***********************************************************************/
 
 const string MeterSection::xml_state_node_name = "Meter";
@@ -256,74 +298,8 @@ TempoMap::~TempoMap ()
 {
 }
 
-int
-TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
-{
-       if (when == section.start() || !section.movable()) {
-               return -1;
-       }
-
-       Glib::RWLock::WriterLock  lm (lock);
-       MetricSectionSorter cmp;
-
-       if (when.beats != 1) {
-
-               /* position by audio frame, then recompute BBT timestamps from the audio ones */
-
-               framepos_t frame = frame_time (when);
-               // cerr << "nominal frame time = " << frame << endl;
-
-               framepos_t prev_frame = round_to_type (frame, -1, Beat);
-               framepos_t next_frame = round_to_type (frame, 1, Beat);
-
-               // cerr << "previous beat at " << prev_frame << " next at " << next_frame << endl;
-
-               /* use the closest beat */
-
-               if ((frame - prev_frame) < (next_frame - frame)) {
-                       frame = prev_frame;
-               } else {
-                       frame = next_frame;
-               }
-
-               // cerr << "actual frame time = " << frame << endl;
-               section.set_frame (frame);
-               // cerr << "frame time = " << section.frame() << endl;
-               timestamp_metrics (false);
-               // cerr << "new BBT time = " << section.start() << endl;
-               metrics->sort (cmp);
-
-       } else {
-
-               /* positioned at bar start already, so just put it there */
-
-               section.set_start (when);
-               metrics->sort (cmp);
-               timestamp_metrics (true);
-       }
-
-
-       return 0;
-}
-
-void
-TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
-{
-       if (move_metric_section (tempo, when) == 0) {
-               PropertyChanged (PropertyChange ());
-       }
-}
-
 void
-TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
-{
-       if (move_metric_section (meter, when) == 0) {
-               PropertyChanged (PropertyChange ());
-       }
-}
-
-void
-TempoMap::remove_tempo (const TempoSection& tempo)
+TempoMap::remove_tempo (const TempoSection& tempo, bool send_signal)
 {
        bool removed = false;
 
@@ -345,13 +321,15 @@ TempoMap::remove_tempo (const TempoSection& tempo)
        }
 
        if (removed) {
-               timestamp_metrics (true);
-               PropertyChanged (PropertyChange ());
+               timestamp_metrics (false);
+               if (send_signal) {
+                       PropertyChanged (PropertyChange ());
+               }
        }
 }
 
 void
-TempoMap::remove_meter (const MeterSection& tempo)
+TempoMap::remove_meter (const MeterSection& tempo, bool send_signal)
 {
        bool removed = false;
 
@@ -374,30 +352,42 @@ TempoMap::remove_meter (const MeterSection& tempo)
 
        if (removed) {
                timestamp_metrics (true);
-               PropertyChanged (PropertyChange ());
+               if (send_signal) {
+                       PropertyChanged (PropertyChange ());
+               }
        }
 }
 
 void
-TempoMap::do_insert (MetricSection* section, bool with_bbt)
+TempoMap::do_insert (MetricSection* section)
 {
+       bool reassign_tempo_bbt = false;
+
        assert (section->start().ticks == 0);
 
        /* we only allow new meters to be inserted on beat 1 of an existing
         * measure. 
         */
 
-       if (dynamic_cast<MeterSection*>(section) && 
-           (section->start().beats != 1 || section->start().ticks != 0)) {
+       if (dynamic_cast<MeterSection*>(section)) {
 
-               BBT_Time corrected = section->start();
-               corrected.beats = 1;
-               corrected.ticks = 0;
+               /* we need to (potentially) update the BBT times of tempo
+                  sections based on this new meter.
+               */
                
-               warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
-                                          section->start(), corrected) << endmsg;
+               reassign_tempo_bbt = true;
 
-               section->set_start (corrected);
+               if ((section->start().beats != 1) || (section->start().ticks != 0)) {
+                       
+                       BBT_Time corrected = section->start();
+                       corrected.beats = 1;
+                       corrected.ticks = 0;
+                       
+                       warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
+                                                  section->start(), corrected) << endmsg;
+                       
+                       section->set_start (corrected);
+               }
        }
 
        Metrics::iterator i;
@@ -411,7 +401,7 @@ TempoMap::do_insert (MetricSection* section, bool with_bbt)
 
        for (i = metrics->begin(); i != metrics->end(); ++i) {
 
-               int const c = (*i)->compare (section, with_bbt);
+               int const c = (*i)->compare (*section);
 
                if (c < 0) {
                        /* this section is before the one to be added; go back round */
@@ -440,7 +430,7 @@ TempoMap::do_insert (MetricSection* section, bool with_bbt)
 
        for (i = metrics->begin(); i != metrics->end(); ++i) {
 
-               if ((*i)->compare (section, with_bbt) < 0) {
+               if ((*i)->compare (*section) < 0) {
                        continue;
                }
 
@@ -451,63 +441,89 @@ TempoMap::do_insert (MetricSection* section, bool with_bbt)
        if (i == metrics->end()) {
                metrics->insert (metrics->end(), section);
        }
-
-       timestamp_metrics (with_bbt);
+       
+       timestamp_metrics (reassign_tempo_bbt);
 }
 
 void
-TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
+TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
 {
-       {
-               Glib::RWLock::WriterLock lm (lock);
+       const TempoSection& first (first_tempo());
 
-               /* new tempos always start on a beat */
-               where.ticks = 0;
-
-               do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
+       if (ts != first) {
+               remove_tempo (ts, false);
+               add_tempo (tempo, where);
+       } else {
+               /* cannot move the first tempo section */
+               *((Tempo*)&first) = tempo;
+               timestamp_metrics (false);
        }
 
        PropertyChanged (PropertyChange ());
 }
 
 void
-TempoMap::add_tempo (const Tempo& tempo, framepos_t where)
+TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
 {
        {
                Glib::RWLock::WriterLock lm (lock);
-               do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false);
-       }
 
-       PropertyChanged (PropertyChange ());
-}
+               /* new tempos always start on a beat */
+               where.ticks = 0;
 
-void
-TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
-{
-       bool replaced = false;
+               TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
+               
+               /* find the meter to use to set the bar offset of this
+                * tempo section.
+                */
 
-       {
-               Glib::RWLock::WriterLock lm (lock);
-               Metrics::iterator i;
+               const Meter* meter = &first_meter();
+               
+               /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
+                  at something, because we insert the default tempo and meter during
+                  TempoMap construction.
+                  
+                  now see if we can find better candidates.
+               */
+               
+               for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+                       
+                       const MeterSection* m;
+                       
+                       if (where < (*i)->start()) {
+                               break;
+                       }
+                       
+                       if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+                               meter = m;
+                       }
+               }
 
-               for (i = metrics->begin(); i != metrics->end(); ++i) {
-                       TempoSection *ts;
+               ts->update_bar_offset_from_bbt (*meter);
 
-                       if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
+               /* and insert it */
 
-                                *((Tempo *) ts) = replacement;
+               do_insert (ts);
+       }
 
-                               replaced = true;
-                               timestamp_metrics (true);
+       PropertyChanged (PropertyChange ());
+}
 
-                               break;
-                       }
-               }
-       }
+void
+TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
+{
+       const MeterSection& first (first_meter());
 
-       if (replaced) {
-               PropertyChanged (PropertyChange ());
+       if (ms != first) {
+               remove_meter (ms, false);
+               add_meter (meter, where);
+       } else {
+               /* cannot move the first meter section */
+               *((Meter*)&first) = meter;
+               timestamp_metrics (true);
        }
+
+       PropertyChanged (PropertyChange ());
 }
 
 void
@@ -531,50 +547,18 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where)
                /* new meters *always* start on a beat. */
                where.ticks = 0;
 
-               do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()), true);
+               do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
        }
 
-       PropertyChanged (PropertyChange ());
-}
-
-void
-TempoMap::add_meter (const Meter& meter, framepos_t where)
-{
-       {
-               Glib::RWLock::WriterLock lm (lock);
-               do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()), false);
+#ifndef NDEBUG
+       if (DEBUG_ENABLED(DEBUG::TempoMap)) {
+               dump (std::cerr);
        }
+#endif
 
        PropertyChanged (PropertyChange ());
 }
 
-void
-TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
-{
-       bool replaced = false;
-
-       {
-               Glib::RWLock::WriterLock lm (lock);
-               Metrics::iterator i;
-
-               for (i = metrics->begin(); i != metrics->end(); ++i) {
-                       MeterSection *ms;
-                       if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
-
-                               *((Meter*) ms) = replacement;
-
-                               replaced = true;
-                               timestamp_metrics (true);
-                               break;
-                       }
-               }
-       }
-
-       if (replaced) {
-               PropertyChanged (PropertyChange ());
-       }
-}
-
 void
 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
 {
@@ -666,111 +650,166 @@ TempoMap::first_tempo () const
 }
 
 void
-TempoMap::timestamp_metrics (bool use_bbt)
+TempoMap::timestamp_metrics_from_audio_time ()
 {
        Metrics::iterator i;
-       const Meter* meter;
-       const Tempo* tempo;
-       Meter *m;
-       Tempo *t;
+       const MeterSection* meter;
+       const TempoSection* tempo;
+       MeterSection *m;
+       TempoSection *t;
 
        meter = &first_meter ();
        tempo = &first_tempo ();
 
-       if (use_bbt) {
-               
-               // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
-
-               framepos_t current = 0;
-               framepos_t section_frames;
-               BBT_Time start;
-               BBT_Time end;
+       BBT_Time start;
+       BBT_Time end;
+       
+       // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
 
-               for (i = metrics->begin(); i != metrics->end(); ++i) {
+       bool first = true;
+       MetricSection* prev = 0;
 
-                       end = (*i)->start();
+       for (i = metrics->begin(); i != metrics->end(); ++i) {
 
-                       section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
+               BBT_Time bbt;
+               TempoMetric metric (*meter, *tempo);
 
-                       current += section_frames;
+               if (prev) {
+                       metric.set_start (prev->start());
+                       metric.set_frame (prev->frame());
+               } else {
+                       // metric will be at frames=0 bbt=1|1|0 by default
+                       // which is correct for our purpose
+               }
 
-                       start = end;
+               bbt_time_with_metric ((*i)->frame(), bbt, metric);
+                       
+               // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
 
-                       (*i)->set_frame (current);
+               if (first) {
+                       first = false;
+               } else {
 
-                       if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
-                               tempo = t;
-                       } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
-                               meter = m;
-                       } else {
-                               fatal << _("programming error: unhandled MetricSection type") << endmsg;
-                               /*NOTREACHED*/
+                       if (bbt.ticks > BBT_Time::ticks_per_bar_division/2) {
+                               /* round up to next beat */
+                               bbt.beats += 1;
                        }
-               }
-
-       } else {
 
-               // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
+                       bbt.ticks = 0;
 
-               bool first = true;
-               MetricSection* prev = 0;
+                       if (bbt.beats != 1) {
+                               /* round up to next bar */
+                               bbt.bars += 1;
+                               bbt.beats = 1;
+                       }
+               }
 
-               for (i = metrics->begin(); i != metrics->end(); ++i) {
+               // cerr << bbt << endl;
 
-                       BBT_Time bbt;
-                       TempoMetric metric (*meter, *tempo);
+               (*i)->set_start (bbt);
+                       
+               if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+                       tempo = t;
+                       // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
+               } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
+                       meter = m;
+                       // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
+               } else {
+                       fatal << _("programming error: unhandled MetricSection type") << endmsg;
+                       /*NOTREACHED*/
+               }
 
-                       if (prev) {
-                               metric.set_start (prev->start());
-                               metric.set_frame (prev->frame());
-                       } else {
-                               // metric will be at frames=0 bbt=1|1|0 by default
-                               // which is correct for our purpose
-                       }
+               prev = (*i);
+       }
 
-                       bbt_time_with_metric ((*i)->frame(), bbt, metric);
-                       
-                       // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
+#ifndef NDEBUG
+       if (DEBUG_ENABLED(DEBUG::TempoMap)) {
+               dump (cerr);
+       }
+#endif
 
-                       if (first) {
-                               first = false;
-                       } else {
+}
 
-                               if (bbt.ticks > BBT_Time::ticks_per_bar_division/2) {
-                                       /* round up to next beat */
-                                       bbt.beats += 1;
-                               }
+void
+TempoMap::timestamp_metrics (bool reassign_tempo_bbt)
+{
+       Metrics::iterator i;
+       const MeterSection* meter;
+       const TempoSection* tempo;
+       MeterSection *m;
+       TempoSection *t;
 
-                               bbt.ticks = 0;
+       DEBUG_TRACE (DEBUG::TempoMath, "###################### TIMESTAMP via BBT ##############\n");
+       
+       framepos_t current = 0;
+       framepos_t section_frames;
+       BBT_Time start;
+       BBT_Time end;
+       
+       if (reassign_tempo_bbt) {
 
-                               if (bbt.beats != 1) {
-                                       /* round up to next bar */
-                                       bbt.bars += 1;
-                                       bbt.beats = 1;
-                               }
-                       }
+               DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
 
-                       // cerr << bbt << endl;
+               meter = &first_meter ();
+               tempo = &first_tempo ();
 
-                       (*i)->set_start (bbt);
+               for (i = metrics->begin(); i != metrics->end(); ++i) {
                        
                        if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+
+                               /* reassign the BBT time of this tempo section
+                                * based on its bar offset position.
+                                */
+
+                               t->update_bbt_time_from_bar_offset (*meter);
                                tempo = t;
-                               // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
+
                        } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
                                meter = m;
-                               // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
                        } else {
                                fatal << _("programming error: unhandled MetricSection type") << endmsg;
                                /*NOTREACHED*/
                        }
+               }
+       }
 
-                       prev = (*i);
+       meter = &first_meter ();
+       tempo = &first_tempo ();
+
+       for (i = metrics->begin(); i != metrics->end(); ++i) {
+
+               end = (*i)->start();
+                            
+               section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
+               current += section_frames;
+
+               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frames between %1 & %2 = %3 using %4 & %6 puts %7 at %8\n",
+                                                              start, end, section_frames, 
+                                                              *((Meter*) meter), *((Tempo*) tempo),
+                                                              (*i)->start(), current));
+
+               start = end;
+
+               (*i)->set_frame (current);
+
+               if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+                       tempo = t;
+               } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
+                       meter = m;
+               } else {
+                       fatal << _("programming error: unhandled MetricSection type") << endmsg;
+                       /*NOTREACHED*/
                }
+
        }
 
-       // dump (cerr);
-       // cerr << "###############################################\n\n\n" << endl;
+#ifndef NDEBUG
+       if (DEBUG_ENABLED(DEBUG::TempoMath)) {
+               dump (cerr);
+       }
+#endif
+
+       DEBUG_TRACE (DEBUG::TempoMath, "###############################################\n");
 
 }
 
@@ -805,8 +844,8 @@ TempoMap::metric_at (framepos_t frame) const
                m.set_frame ((*i)->frame ());
                m.set_start ((*i)->start ());
        }
-
-       // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << endl;
+       
+       // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl;
        return m;
 }
 
@@ -916,53 +955,60 @@ TempoMap::bbt_time_with_metric (framepos_t frame, BBT_Time& bbt, const TempoMetr
 framecnt_t
 TempoMap::count_frames_between (const BBT_Time& start, const BBT_Time& end) const
 {
-       /* for this to work with fractional measure types, start and end have to be
-          "legal" BBT types, that means that the beats and ticks should be inside
-          a bar
-       */
+       TempoMetric bm = metric_at (start);
+       TempoMetric em = metric_at (end);
 
+       return count_frames_with_metrics (bm, em, start, end);
+}
+
+framecnt_t
+TempoMap::count_frames_with_metrics (const TempoMetric& bm, const TempoMetric& em, const BBT_Time& start, const BBT_Time& end) const
+{
        framecnt_t frames = 0;
        framepos_t start_frame = 0;
        framepos_t end_frame = 0;
 
-       TempoMetric m = metric_at (start);
-
-       uint32_t bar_offset = start.bars - m.start().bars;
+       uint32_t bar_offset = start.bars - bm.start().bars;
 
-       double  beat_offset = bar_offset*m.meter().divisions_per_bar() - (m.start().beats-1) + (start.beats -1)
+       double  beat_offset = bar_offset*bm.meter().divisions_per_bar() - (bm.start().beats-1) + (start.beats -1)
                + start.ticks/BBT_Time::ticks_per_bar_division;
 
-       start_frame = m.frame() + (framepos_t) rint(beat_offset * m.meter().frames_per_division(m.tempo(),_frame_rate));
+       start_frame = bm.frame() + (framepos_t) rint(beat_offset * bm.meter().frames_per_division(bm.tempo(),_frame_rate));
 
-       // cerr << "from start " << start << " compute frame = " << start_frame 
-       // <<  " from metric at " << m.frame() << " tempo = " << m.tempo().beats_per_minute () << " meter " 
-       // << m.meter().divisions_per_bar() << '/' << m.meter().note_divisor() 
-       // << endl;
+#if 0
+       cerr << "from start " << start << " compute frame = " << start_frame 
+        <<  " from metric at " << bm.frame() << " tempo = " << bm.tempo().beats_per_minute () << " meter " 
+        << bm.meter().divisions_per_bar() << '/' << bm.meter().note_divisor() 
+        << endl;
+#endif
 
-       m =  metric_at(end);
+       bar_offset = end.bars - em.start().bars;
 
-       bar_offset = end.bars - m.start().bars;
-
-       beat_offset = bar_offset * m.meter().divisions_per_bar() - (m.start().beats -1) + (end.beats - 1)
+       beat_offset = bar_offset * em.meter().divisions_per_bar() - (em.start().beats -1) + (end.beats - 1)
                + end.ticks/BBT_Time::ticks_per_bar_division;
 
-       end_frame = m.frame() + (framepos_t) rint(beat_offset * m.meter().frames_per_division(m.tempo(),_frame_rate));
+       end_frame = em.frame() + (framepos_t) rint(beat_offset * em.meter().frames_per_division(em.tempo(),_frame_rate));
 
-       // cerr << "from end " << end << " compute frame = " << end_frame 
-       // <<  " from metric at " << m.frame() << " tempo = " << m.tempo().beats_per_minute () << " meter " 
-       // << m.meter().divisions_per_bar() << '/' << m.meter().note_divisor() 
-       // << endl;
+#if 0
+        cerr << "from end " << end << " compute frame = " << end_frame 
+        <<  " from metric at " << em.frame() << " tempo = " << em.tempo().beats_per_minute () << " meter " 
+        << em.meter().divisions_per_bar() << '/' << em.meter().note_divisor() 
+        << endl;
+#endif
 
        frames = end_frame - start_frame;
 
        return frames;
-
 }
 
 framecnt_t
 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
 {
-       /* this is used in timestamping the metrics by actually counting the beats */
+       /* this is used in timestamping the metrics by actually counting the
+        * beats between two metrics ONLY. this means that we know we have a
+        * fixed divisions_per_bar and frames_per_division for the entire
+        * computation. 
+        */
 
        framecnt_t frames = 0;
        uint32_t bar = start.bars;
@@ -970,40 +1016,61 @@ TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo,
        double divisions_counted = 0;
        double divisions_per_bar = 0;
        double division_frames = 0;
+       double max_divs;
+       int32_t ticks = 0;
 
        divisions_per_bar = meter.divisions_per_bar();
+       max_divs = ceil (divisions_per_bar);
        division_frames = meter.frames_per_division (tempo, _frame_rate);
 
+       if (start.ticks > 0) {
+               ticks = -start.ticks;
+               
+       }
+
        frames = 0;
 
        while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
 
-               if (beat >= divisions_per_bar) {
-                       beat = 1;
-                       ++bar;
-                       ++divisions_counted;
+               ++beat;
+               ++divisions_counted;
+               
+               if (beat > max_divs) {
 
                        if (beat > divisions_per_bar) {
-
+                               
                                /* this is a fractional beat at the end of a fractional bar
                                   so it should only count for the fraction
                                */
-
-                               divisions_counted -= (ceil(divisions_per_bar) - divisions_per_bar);
+                               
+                               divisions_counted -= (max_divs - divisions_per_bar);
                        }
 
-               } else {
-                       ++beat;
-                       ++divisions_counted;
+                       ++bar;
+                       beat = 1;
                }
        }
 
-       // cerr << "Counted " << beats_counted << " from " << start << " to " << end
-       // << " bpb were " << divisions_per_bar
-       // << " fpb was " << beat_frames
-       // << endl;
+       ticks += end.ticks;
+
+#if 0  
+       cerr << "for " << start.ticks << " and " << end.ticks << " adjust divs by " << ticks << " = " 
+            << (ticks/BBT_Time::ticks_per_bar_division) << " divs => " 
+            << ((ticks/BBT_Time::ticks_per_bar_division) * division_frames)
+            << " (fpd = " << division_frames << ')'
+            << endl;
+#endif
 
-       frames = (framecnt_t) llrint (floor (divisions_counted * division_frames));
+       frames = (framecnt_t) llrint (floor ((divisions_counted + (ticks/BBT_Time::ticks_per_bar_division)) * division_frames));
+
+#if 0
+       cerr << "Counted " << divisions_counted << " + " << ticks << " from " << start << " to " << end
+            << " dpb were " << divisions_per_bar
+            << " fpd was " << division_frames
+            << " => " 
+            << frames 
+            << endl;
+#endif
 
        return frames;
 
@@ -1370,8 +1437,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
 TempoMap::BBTPointList *
 TempoMap::get_points (framepos_t lower, framepos_t upper) const
 {
-
-       Metrics::const_iterator i;
+       Metrics::const_iterator next_metric;
        BBTPointList *points;
        double current;
        const MeterSection* meter;
@@ -1394,15 +1460,15 @@ TempoMap::get_points (framepos_t lower, framepos_t upper) const
 
        /* find the starting point */
 
-       for (i = metrics->begin(); i != metrics->end(); ++i) {
+       for (next_metric = metrics->begin(); next_metric != metrics->end(); ++next_metric) {
 
-               if ((*i)->frame() > lower) {
+               if ((*next_metric)->frame() > lower) {
                        break;
                }
 
-               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+               if ((t = dynamic_cast<const TempoSection*>(*next_metric)) != 0) {
                        tempo = t;
-               } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+               } else if ((m = dynamic_cast<const MeterSection*>(*next_metric)) != 0) {
                        meter = m;
                }
        }
@@ -1419,8 +1485,8 @@ TempoMap::get_points (framepos_t lower, framepos_t upper) const
        divisions_per_bar = meter->divisions_per_bar ();
        frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
        beat_frames = meter->frames_per_division (*tempo,_frame_rate);
-
-       // cerr << "Start with beat frames = " << beat_frames <<  " bar = " << frames_per_bar << endl;
+       
+       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Start with beat frames = %1 bar = %2\n", beat_frames, frames_per_bar));
 
        if (meter->frame() > tempo->frame()) {
                bar = meter->start().bars;
@@ -1449,24 +1515,35 @@ TempoMap::get_points (framepos_t lower, framepos_t upper) const
 
        do {
 
-               if (i == metrics->end()) {
+               /* we're going to add bar or beat points until we hit the
+                    earlier of:
+                    
+                   (1) the end point of this request
+                   (2) the next metric section
+               */
+               
+               if (next_metric == metrics->end()) {
                        limit = upper;
-                       // cerr << "== limit set to end of request @ " << limit << endl;
+                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("== limit set to end of request @ %1\n", limit));
                } else {
-                       // cerr << "== limit set to next meter section @ " << (*i)->frame() << endl;
-                       limit = (*i)->frame();
+                       limit = (*next_metric)->frame();
+                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("== limit set to next metric section @ %1\n", limit));
                }
 
                limit = min (limit, upper);
 
+               bool reset_current_to_metric_section = true;
+               bool bar_adjusted = false;
+               TempoSection* ts;
+
                while (current < limit) {
 
                        /* if we're at the start of a bar, add bar point */
 
                        if (beat == 1) {
                                if (current >= lower) {
-                                       // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
-                                       points->push_back (BBTPoint (*meter, *tempo,(framepos_t)rint(current), Bar, bar, 1));
+                                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", bar, current));
+                                       points->push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current), Bar, bar, 1));
 
                                }
                        }
@@ -1475,26 +1552,71 @@ TempoMap::get_points (framepos_t lower, framepos_t upper) const
 
                        beat_frame = current;
 
-                       while (beat <= ceil(divisions_per_bar) && beat_frame < limit) {
+                       const uint32_t max_divs = ceil (divisions_per_bar);
+
+                       while (beat <= max_divs && beat_frame < limit) {
+
                                if (beat_frame >= lower) {
-                                       // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
-                                       points->push_back (BBTPoint (*meter, *tempo, (framepos_t) rint(beat_frame), Beat, bar, beat));
+                                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", bar, beat, beat_frame));
+                                       points->push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(beat_frame), Beat, bar, beat));
                                }
-                               beat_frame += beat_frames;
-                               current+= beat_frames;
 
+                               beat_frame += beat_frames;
+                               current = beat_frame;
                                beat++;
                        }
 
-                       // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
-                       // << (beat > ceil(divisions_per_bar))
-                       // << " beat frame @ " << beat_frame << " vs. " << limit
-                       // << endl;
+                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("break in beats addition @ end ? %1 out of bpb ? %2 beat frame @ %3 vs %4 beat @ %5 vs %6\n",
+                                                                      (next_metric == metrics->end()), (beat > max_divs), beat_frame, limit, beat, max_divs));
 
-                        if (beat > ceil(divisions_per_bar) || (i != metrics->end() && dynamic_cast<MeterSection*>(*i))) {
+                       if (beat <= max_divs) {
+
+                               /* we didn't reach the end of the bar. 
+
+                                  this could be be because we hit "upper"
+                                  or a new metric section.
+
+                                  meter sections are always at the start 
+                                  of a measure. put differently, if a meter
+                                  indicates N divisions per bar, the next
+                                  meter must be a multiple of N divisions
+                                  after it.
+
+                                  so, we've hit a tempo section, which may or
+                                  may not be precisely on a beat.
+                               */
+                               
+                               if (next_metric != metrics->end() && limit == (*next_metric)->frame() && ((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0) && ts->start().ticks != 0) {
+
+                                       /* compute current at the *next* beat,
+                                          using the tempo section we just
+                                          bumped into.
+
+                                          also, avoid resetting it to position
+                                          of the next metric section as we
+                                          move to that below.
+                                       */
+
+                                       reset_current_to_metric_section = false;
+
+                                       /* recompute how many frames per
+                                        * division using the tempo we've just
+                                        * found
+                                        */
+                                       
+                                       double next_beat_frames = meter->frames_per_division (*ts,_frame_rate);                                 
+
+                                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
+                                                                                      (*next_metric)->start(), (*next_metric)->frame(), ts->bar_offset()));
+                                       
+                                       current -= beat_frames;
+                                       current += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames);
+                               }
+                               
+                       } else if ((beat > max_divs) || (next_metric != metrics->end() && dynamic_cast<MeterSection*>(*next_metric))) {
 
                                /* we've arrived at either the end of a bar or
-                                  a new meter marker.
+                                  a new **meter** marker (not tempo marker).
 
                                   its important to move `current' forward by
                                   the actual frames_per_bar, not move it to an
@@ -1509,17 +1631,19 @@ TempoMap::get_points (framepos_t lower, framepos_t upper) const
                                   possible extra fraction from the current
                                */
 
-                               if (beat > ceil (divisions_per_bar)) {
+                               if (beat > max_divs) {
                                        /* next bar goes where the numbers suggest */
-                                       current -=  beat_frames * (ceil(divisions_per_bar)-divisions_per_bar);
-                                       // cerr << "++ next bar from numbers\n";
+                                       current -= beat_frames * (max_divs - divisions_per_bar);
+                                       DEBUG_TRACE (DEBUG::TempoMath, "++ next bar from numbers\n");
                                } else {
-                                       /* next bar goes where the next metric is */
+                                       /* next bar goes where the next meter metric is */
                                        current = limit;
-                                       // cerr << "++ next bar at next metric\n";
+                                       DEBUG_TRACE (DEBUG::TempoMath, "++ next bar at next metric\n");
                                }
+
                                bar++;
                                beat = 1;
+                               bar_adjusted = true;
                        }
                }
 
@@ -1533,26 +1657,33 @@ TempoMap::get_points (framepos_t lower, framepos_t upper) const
                   if there is a next metric, move to it, and continue.
                */
 
-               if (i != metrics->end()) {
+               if (next_metric != metrics->end()) {
 
-                       if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+                       if ((t = dynamic_cast<const TempoSection*>(*next_metric)) != 0) {
                                tempo = t;
-                       } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+                       } else if ((m = dynamic_cast<const MeterSection*>(*next_metric)) != 0) {
                                meter = m;
-                               /* new MeterSection, beat always returns to 1 */
-                               beat = 1;
+
+                               if (!bar_adjusted) {
+                                       /* new MeterSection, beat always returns to 1 */
+                                       beat = 1;
+                               }
                        }
                        
-                       current = (*i)->frame ();
-                       // cerr << "loop around with current @ " << current << endl;
+                       if (reset_current_to_metric_section) {
+                               current = (*next_metric)->frame ();
+                       }
 
+                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("loop around with current @ %1\n", current));
+                       
                        divisions_per_bar = meter->divisions_per_bar ();
                        frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
                        beat_frames = meter->frames_per_division (*tempo, _frame_rate);
 
-                       // cerr << "New metric with beat frames = " << beat_frames <<  " bar = " << frames_per_bar << endl;
-
-                       ++i;
+                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 bar = %2 dpb %3 meter %4 tempo %5\n", 
+                                                                      beat_frames, frames_per_bar, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
+                       
+                       ++next_metric;
                }
 
        } while (1);
@@ -1627,18 +1758,26 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                XMLNodeList nlist;
                XMLNodeConstIterator niter;
                Metrics old_metrics (*metrics);
+               MeterSection* last_meter = 0;
 
                metrics->clear();
 
                nlist = node.children();
-
+               
                for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                        XMLNode* child = *niter;
 
                        if (child->name() == TempoSection::xml_state_node_name) {
 
                                try {
-                                       metrics->push_back (new TempoSection (*child));
+                                       TempoSection* ts = new TempoSection (*child);
+                                       metrics->push_back (ts);
+
+                                       if (ts->bar_offset() < 0.0) {
+                                               if (last_meter) {
+                                                       ts->update_bar_offset_from_bbt (*last_meter);
+                                               } 
+                                       }
                                }
 
                                catch (failed_constructor& err){
@@ -1650,7 +1789,9 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                        } else if (child->name() == MeterSection::xml_state_node_name) {
 
                                try {
-                                       metrics->push_back (new MeterSection (*child));
+                                       MeterSection* ms = new MeterSection (*child);
+                                       metrics->push_back (ms);
+                                       last_meter = ms;
                                }
 
                                catch (failed_constructor& err) {
@@ -1683,11 +1824,11 @@ TempoMap::dump (std::ostream& o) const
        for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
 
                if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                       o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
+                       o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (movable? "
                          << t->movable() << ')' << endl;
                } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
                        o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
-                         << " (move? " << m->movable() << ')' << endl;
+                         << " (movable? " << m->movable() << ')' << endl;
                }
        }
 }
@@ -1731,7 +1872,7 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                }
        }
 
-       timestamp_metrics (false);
+       timestamp_metrics_from_audio_time ();
 
        PropertyChanged (PropertyChange ());
 }
@@ -2310,38 +2451,42 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
  */
 
 int
-MetricSection::compare (MetricSection* other, bool with_bbt) const
+MetricSection::compare (const MetricSection& other) const
 {
-       if (with_bbt) {
-               if (start() == other->start()) {
-                       return 0;
-               } else if (start() < other->start()) {
-                       return -1;
-               } else {
-                       return 1;
-               }
+       if (start() == other.start()) {
+               return 0;
+       } else if (start() < other.start()) {
+               return -1;
        } else {
-               if (frame() == other->frame()) {
-                       return 0;
-               } else if (frame() < other->frame()) {
-                       return -1;
-               } else {
-                       return 1;
-               }
+               return 1;
        }
 
        /* NOTREACHED */
        return 0;
 }
 
+bool
+MetricSection::operator== (const MetricSection& other) const
+{
+       return compare (other) == 0;
+}
+
+bool
+MetricSection::operator!= (const MetricSection& other) const
+{
+       return compare (other) != 0;
+}
+
 std::ostream& 
 operator<< (std::ostream& o, const Meter& m) {
        return o << m.divisions_per_bar() << '/' << m.note_divisor();
 }
+
 std::ostream& 
 operator<< (std::ostream& o, const Tempo& t) {
-       return o << t.beats_per_minute() << " (1/" << t.note_type() << " per minute)" << endl;
+       return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
 }
+
 std::ostream& 
 operator<< (std::ostream& o, const MetricSection& section) {